claude-flow-novice 2.15.2 → 2.15.4
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/.claude/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
- package/.claude/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
- package/.claude/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
- package/.claude/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
- package/.claude/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
- package/.claude/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
- package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
- package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
- package/.claude/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
- package/.claude/commands/cfn-loop-cli.md +16 -2
- package/.claude/commands/switch-api.md +31 -10
- package/.claude/hooks/cfn-BACKUP_USAGE.md +243 -243
- package/.claude/hooks/cfn-invoke-security-validation.sh +69 -69
- package/.claude/hooks/cfn-lint-sql-injection.sh +61 -0
- package/.claude/hooks/cfn-post-edit-cfn-retrospective.sh +109 -78
- package/.claude/hooks/cfn-post-edit.config.json +44 -44
- package/.claude/hooks/cfn-pre-edit-security-warning.sh +40 -0
- package/.claude/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
- package/.claude/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
- package/.claude/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
- package/.claude/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
- package/.claude/skills/cfn-hybrid-routing/check-dependencies.sh +51 -51
- package/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
- package/.claude/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +184 -23
- package/.claude/skills/cfn-loop-orchestration/security_utils.sh +24 -0
- package/.claude/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
- package/.claude/skills/cfn-loop-validation/orchestrate-cfn-loop.sh +252 -252
- package/.claude/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
- package/.claude/skills/cfn-redis-coordination/agent-log.sh +4 -0
- package/.claude/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
- package/.claude/skills/cfn-redis-coordination/agent-recovery.sh +74 -74
- package/.claude/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
- package/.claude/skills/cfn-redis-coordination/get-context.sh +145 -112
- package/.claude/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
- package/.claude/skills/cfn-redis-coordination/invoke-waiting-mode.sh +3 -0
- package/.claude/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
- package/.claude/skills/cfn-redis-coordination/redis-functions.sh +33 -0
- package/.claude/skills/cfn-redis-coordination/report-completion.sh +24 -31
- package/.claude/skills/cfn-redis-coordination/store-context.sh +4 -0
- package/.claude/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
- package/.claude/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
- package/.claude/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
- package/.claude/skills/cfn-transparency-middleware/middleware-config.sh +28 -28
- package/.claude/skills/cfn-transparency-middleware/performance-benchmark.sh +78 -78
- package/.claude/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
- package/.claude/skills/cfn-transparency-middleware/test-integration.sh +161 -161
- package/.claude/skills/cfn-transparency-middleware/test-transparency-skill.sh +367 -367
- package/.claude/skills/cfn-transparency-middleware/tests/input-validation.sh +107 -92
- package/.claude/skills/cfn-transparency-middleware/wrap-agent.sh +131 -131
- package/README.md +116 -475
- package/claude-assets/agents/cfn-dev-team/README.md +103 -0
- package/claude-assets/agents/cfn-dev-team/architecture/goal-planner.md +1 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +77 -15
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +355 -6
- package/claude-assets/agents/cfn-dev-team/coordinators/consensus-builder.md +82 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +82 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +77 -15
- package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +99 -12
- package/claude-assets/agents/cfn-dev-team/dev-ops/github-commit-agent.md +1 -1
- package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +97 -0
- package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +20 -1
- package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +97 -0
- package/claude-assets/agents/cfn-dev-team/developers/backend-developer.md +110 -13
- package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +106 -15
- package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +115 -11
- package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +94 -7
- package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +87 -9
- package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +85 -7
- package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +160 -28
- package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +101 -19
- package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +108 -14
- package/claude-assets/agents/cfn-dev-team/reviewers/{reviewer.md → code-reviewer.md} +95 -8
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +107 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +98 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +95 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +136 -9
- package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +108 -1
- package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +107 -13
- package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +737 -0
- package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +828 -0
- package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +106 -7
- package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +77 -0
- package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +684 -0
- package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +110 -1
- package/claude-assets/agents/cfn-dev-team/testers/tester.md +94 -7
- package/claude-assets/agents/cfn-dev-team/utility/code-booster.md +1 -3
- package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +87 -13
- package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +103 -7
- package/claude-assets/agents/cfn-dev-team/utility/researcher.md +1 -3
- package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +94 -7
- package/claude-assets/agents/docker-coordinators/cfn-docker-v3-coordinator.md +46 -0
- package/claude-assets/agents/project-only-agents/npm-package-specialist.md +1 -1
- package/claude-assets/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
- package/claude-assets/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
- package/claude-assets/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
- package/claude-assets/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
- package/claude-assets/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
- package/claude-assets/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
- package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
- package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
- package/claude-assets/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
- package/claude-assets/commands/cfn-loop-cli.md +16 -2
- package/claude-assets/commands/switch-api.md +31 -10
- package/claude-assets/hooks/cfn-BACKUP_USAGE.md +243 -243
- package/claude-assets/hooks/cfn-invoke-security-validation.sh +69 -69
- package/claude-assets/hooks/cfn-lint-sql-injection.sh +61 -0
- package/claude-assets/hooks/cfn-post-edit-cfn-retrospective.sh +109 -78
- package/claude-assets/hooks/cfn-post-edit.config.json +44 -44
- package/claude-assets/hooks/cfn-post-execution/memory-cleanup.sh +19 -19
- package/claude-assets/hooks/cfn-pre-edit-security-warning.sh +40 -0
- package/claude-assets/hooks/cfn-pre-execution/memory-check.sh +19 -19
- package/claude-assets/hooks/detect-hardcoded-credentials.sh +212 -0
- package/claude-assets/skills/SKILL_TEMPLATE.md +774 -0
- package/claude-assets/skills/agent-lifecycle/execute-lifecycle-hook.sh +543 -572
- package/claude-assets/skills/agent-lifecycle/simple-audit.sh +57 -30
- package/claude-assets/skills/agent-template-generator/SKILL.md +440 -0
- package/claude-assets/skills/agent-template-generator/generate-agent.sh +405 -0
- package/claude-assets/skills/agent-validation-linter/SKILL.md +589 -0
- package/claude-assets/skills/agent-validation-linter/lint-agents.sh +271 -0
- package/claude-assets/skills/bootstrap/bash-fundamentals.md +786 -0
- package/claude-assets/skills/bootstrap/database-connection.md +464 -0
- package/claude-assets/skills/bootstrap/error-handling.md +580 -0
- package/claude-assets/skills/bootstrap/file-operations.md +699 -0
- package/claude-assets/skills/bootstrap/skill-loader.md +616 -0
- package/claude-assets/skills/bootstrap/sqlite-params.sh +287 -0
- package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
- package/claude-assets/skills/cfn-automatic-memory-persistence/persist-agent-output.sh +48 -48
- package/claude-assets/skills/cfn-automatic-memory-persistence/query-agent-history.sh +34 -34
- package/claude-assets/skills/cfn-automatic-memory-persistence/test-memory-persistence.sh +17 -16
- package/claude-assets/skills/cfn-deliverable-validation/confidence-calculator.sh +261 -261
- package/claude-assets/skills/cfn-deployment/SKILL.md +293 -0
- package/claude-assets/skills/cfn-deployment/execute.sh +21 -0
- package/claude-assets/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
- package/claude-assets/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
- package/claude-assets/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
- package/claude-assets/skills/cfn-environment-sanitization/sanitize-environment.sh +38 -0
- package/claude-assets/skills/cfn-error-batching-strategy/lib/core-functions.sh +47 -47
- package/claude-assets/skills/cfn-expert-update/update-expert.sh +345 -345
- package/claude-assets/skills/cfn-file-operations/SKILL.md +290 -0
- package/claude-assets/skills/cfn-file-operations/execute.sh +129 -0
- package/claude-assets/skills/cfn-file-operations/lib/atomic-write.sh +294 -0
- package/claude-assets/skills/cfn-file-operations/lib/lock.sh +361 -0
- package/claude-assets/skills/cfn-file-operations/test.sh +369 -0
- package/claude-assets/skills/cfn-hybrid-routing/check-dependencies.sh +51 -51
- package/claude-assets/skills/cfn-intervention-detector/detect-intervention.sh +110 -110
- package/claude-assets/skills/cfn-intervention-orchestrator/execute-intervention.sh +58 -58
- package/claude-assets/skills/cfn-log-operations/SKILL.md +308 -0
- package/claude-assets/skills/cfn-log-operations/execute.sh +420 -0
- package/claude-assets/skills/cfn-log-operations/lib/rotate.sh +406 -0
- package/claude-assets/skills/cfn-log-operations/lib/search.sh +448 -0
- package/claude-assets/skills/cfn-log-operations/test.sh +394 -0
- package/claude-assets/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
- package/claude-assets/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +184 -23
- package/claude-assets/skills/cfn-loop-orchestration/security_utils.sh +24 -0
- package/claude-assets/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
- package/claude-assets/skills/cfn-loop-validation/orchestrate-cfn-loop.sh +252 -252
- package/claude-assets/skills/cfn-loop2-output-processing/process-validator-output.sh +275 -275
- package/claude-assets/skills/cfn-memory-management/check-memory.sh +159 -159
- package/claude-assets/skills/cfn-memory-management/cleanup-memory.sh +196 -196
- package/claude-assets/skills/cfn-node-heap-sizer/task-mode-heap-limiter.sh +325 -325
- package/claude-assets/skills/cfn-parameterized-queries/SKILL.md +339 -0
- package/claude-assets/skills/cfn-playbook/query-playbook.sh +19 -15
- package/claude-assets/skills/cfn-playbook/update-playbook.sh +25 -14
- package/claude-assets/skills/cfn-playbook-auto-update/auto-update-playbook.sh +85 -85
- package/claude-assets/skills/cfn-process-instrumentation/instrument-process.sh +44 -0
- package/claude-assets/skills/cfn-promotion/SKILL.md +305 -0
- package/claude-assets/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-log.sh +4 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-recovery.sh +74 -74
- package/claude-assets/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
- package/claude-assets/skills/cfn-redis-coordination/get-context.sh +145 -112
- package/claude-assets/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
- package/claude-assets/skills/cfn-redis-coordination/invoke-waiting-mode.sh +3 -0
- package/claude-assets/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
- package/claude-assets/skills/cfn-redis-coordination/redis-functions.sh +33 -0
- package/claude-assets/skills/cfn-redis-coordination/report-completion.sh +24 -31
- package/claude-assets/skills/cfn-redis-coordination/store-context.sh +4 -0
- package/claude-assets/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
- package/claude-assets/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
- package/claude-assets/skills/cfn-scope-simplifier/simplify-scope.sh +67 -67
- package/claude-assets/skills/cfn-skill-loader/SKILL.md +466 -0
- package/claude-assets/skills/cfn-skill-loader/execute.sh +344 -0
- package/claude-assets/skills/cfn-specialist-injection/recommend-specialist.sh +56 -56
- package/claude-assets/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
- package/claude-assets/skills/cfn-standardized-error-handling/capture-agent-error.sh +86 -86
- package/claude-assets/skills/cfn-standardized-error-handling/test-error-handling.sh +165 -165
- package/claude-assets/skills/cfn-task-audit/get-audit-data.sh +42 -21
- package/claude-assets/skills/cfn-task-audit/store-task-audit.sh +17 -10
- package/claude-assets/skills/cfn-task-config-init/initialize-config.sh +264 -264
- package/claude-assets/skills/cfn-task-decomposition/task-decomposer.sh +278 -278
- package/claude-assets/skills/cfn-test-runner/detect-regressions.sh +17 -14
- package/claude-assets/skills/cfn-test-runner/detect-regressions.sh.backup-1763392821 +55 -0
- package/claude-assets/skills/cfn-test-runner/store-benchmarks.sh +17 -19
- package/claude-assets/skills/cfn-transparency-middleware/middleware-config.sh +28 -28
- package/claude-assets/skills/cfn-transparency-middleware/performance-benchmark.sh +78 -78
- package/claude-assets/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
- package/claude-assets/skills/cfn-transparency-middleware/test-integration.sh +161 -161
- package/claude-assets/skills/cfn-transparency-middleware/test-transparency-skill.sh +367 -367
- package/claude-assets/skills/cfn-transparency-middleware/tests/input-validation.sh +107 -92
- package/claude-assets/skills/cfn-transparency-middleware/wrap-agent.sh +131 -131
- package/claude-assets/skills/cfn-utilities/SKILL.md +237 -0
- package/claude-assets/skills/cfn-utilities/execute.sh +32 -0
- package/claude-assets/skills/cfn-utilities/lib/errors.sh +56 -0
- package/claude-assets/skills/cfn-utilities/lib/file-ops.sh +164 -0
- package/claude-assets/skills/cfn-utilities/lib/logging.sh +77 -0
- package/claude-assets/skills/cfn-utilities/lib/retry.sh +127 -0
- package/claude-assets/skills/cfn-utilities/test.sh +317 -0
- package/claude-assets/skills/docker-build/SKILL.md +96 -203
- package/claude-assets/skills/docker-build/build.sh +73 -73
- package/claude-assets/skills/integration/agent-handoff.sh +492 -0
- package/claude-assets/skills/integration/file-operations.sh +414 -0
- package/claude-assets/skills/json-validation/SKILL.md +431 -0
- package/claude-assets/skills/json-validation/test-validate-success-criteria.sh +421 -0
- package/claude-assets/skills/json-validation/validate-success-criteria.sh +197 -0
- package/claude-assets/skills/redis-coordination/validate-parameters.sh +34 -0
- package/claude-assets/skills/workflow-codification/APPROVAL_WORKFLOW.md +806 -0
- package/claude-assets/skills/workflow-codification/COST_TRACKING.md +637 -0
- package/claude-assets/skills/workflow-codification/DEPLOY_QUICK_REFERENCE.md +106 -0
- package/claude-assets/skills/workflow-codification/EDGE_CASE_TRACKING.md +404 -0
- package/claude-assets/skills/workflow-codification/PROPAGATE_UPDATE_QUICK_REFERENCE.md +366 -0
- package/claude-assets/skills/workflow-codification/README_PHASE4.md +457 -0
- package/claude-assets/skills/workflow-codification/SKILL.md +110 -0
- package/claude-assets/skills/workflow-codification/analyze-patterns.sh +899 -0
- package/claude-assets/skills/workflow-codification/approval-workflow.sh +514 -0
- package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh +481 -0
- package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh.backup-1763392820 +512 -0
- package/claude-assets/skills/workflow-codification/generate-skill-update.sh +525 -0
- package/claude-assets/skills/workflow-codification/lib/security-utils.sh +204 -0
- package/claude-assets/skills/workflow-codification/propagate-skill-update.sh +648 -0
- package/claude-assets/skills/workflow-codification/propagate-skill-update.sh.backup-1763392820 +664 -0
- package/claude-assets/skills/workflow-codification/review-skill.sh +643 -0
- package/claude-assets/skills/workflow-codification/templates/email-notification.txt +114 -0
- package/claude-assets/skills/workflow-codification/templates/slack-notification.md +85 -0
- package/claude-assets/skills/workflow-codification/test-integration.sh +296 -0
- package/claude-assets/skills/workflow-codification/test-metadata-update.sh +350 -0
- package/claude-assets/skills/workflow-codification/track-cost-savings.sh +486 -0
- package/claude-assets/skills/workflow-codification/track-cost-savings.sh.backup-1763392821 +445 -0
- package/claude-assets/skills/workflow-codification/track-edge-case.sh +290 -0
- package/claude-assets/skills/workflow-codification/workflow-codification.db +0 -0
- package/dist/ace/ace-curator.js +10 -2
- package/dist/ace/ace-curator.js.map +1 -1
- package/dist/ace/ace-generator.js +4 -0
- package/dist/ace/ace-generator.js.map +1 -1
- package/dist/ace/ace-reflector.js +1 -1
- package/dist/ace/ace-reflector.js.map +1 -1
- package/dist/ace/context-injection.js +24 -2
- package/dist/ace/context-injection.js.map +1 -1
- package/dist/agents/agent-loader.js +146 -165
- package/dist/agents/agent-loader.js.map +1 -1
- package/dist/agents/task-agent-integration.js +1 -1
- package/dist/agents/task-agent-integration.js.map +1 -1
- package/dist/api/health-endpoints.js +390 -0
- package/dist/api/health-endpoints.js.map +1 -0
- package/dist/cli/agent-executor.js +4 -1
- package/dist/cli/agent-executor.js.map +1 -1
- package/dist/cli/agent-prompt-builder.js +89 -1
- package/dist/cli/agent-prompt-builder.js.map +1 -1
- package/dist/cli/agent-spawn.js +130 -37
- package/dist/cli/agent-spawn.js.map +1 -1
- package/dist/cli/config-manager.js +91 -109
- package/dist/cli/config-manager.js.map +1 -1
- package/dist/cli/skill-cache-validator.js +412 -0
- package/dist/cli/skill-cache-validator.js.map +1 -0
- package/dist/cli/skill-cli.js +991 -0
- package/dist/cli/skill-cli.js.map +1 -0
- package/dist/cli/skill-execution-logger.js +284 -0
- package/dist/cli/skill-execution-logger.js.map +1 -0
- package/dist/cli/skill-loader.js +457 -0
- package/dist/cli/skill-loader.js.map +1 -0
- package/dist/coordination/event-bus.js +2 -2
- package/dist/coordination/event-bus.js.map +1 -1
- package/dist/coordination/fleet-manager.js +1 -1
- package/dist/coordination/fleet-manager.js.map +1 -1
- package/dist/coordination/index.js +23 -9
- package/dist/coordination/index.js.map +1 -1
- package/dist/coordination/types/fleet-manager.types.js.map +1 -1
- package/dist/db/migration-manager.js +483 -0
- package/dist/db/migration-manager.js.map +1 -0
- package/dist/db/skills-query.js +535 -0
- package/dist/db/skills-query.js.map +1 -0
- package/dist/integration/DatabaseHandoff.js +507 -0
- package/dist/integration/DatabaseHandoff.js.map +1 -0
- package/dist/integration/StandardAdapter.js +291 -0
- package/dist/integration/StandardAdapter.js.map +1 -0
- package/dist/jobs/edge-case-analyzer.js +367 -0
- package/dist/jobs/edge-case-analyzer.js.map +1 -0
- package/dist/jobs/promotion-sla-enforcer.js +288 -0
- package/dist/jobs/promotion-sla-enforcer.js.map +1 -0
- package/dist/lib/agent-output-parser.js +518 -0
- package/dist/lib/agent-output-parser.js.map +1 -0
- package/dist/lib/agent-output-validator.js +950 -0
- package/dist/lib/agent-output-validator.js.map +1 -0
- package/dist/lib/agent-workspace.js +281 -0
- package/dist/lib/agent-workspace.js.map +1 -0
- package/dist/lib/artifact-registry.js +443 -0
- package/dist/lib/artifact-registry.js.map +1 -0
- package/dist/lib/atomic-file-writer.js +377 -0
- package/dist/lib/atomic-file-writer.js.map +1 -0
- package/dist/lib/backup-manager.js +779 -0
- package/dist/lib/backup-manager.js.map +1 -0
- package/dist/lib/checkpoint-manager.js +837 -0
- package/dist/lib/checkpoint-manager.js.map +1 -0
- package/dist/lib/circuit-breaker.js +340 -0
- package/dist/lib/circuit-breaker.js.map +1 -0
- package/dist/lib/completion-signal-handler.js +243 -0
- package/dist/lib/completion-signal-handler.js.map +1 -0
- package/dist/lib/config-manager.js +312 -0
- package/dist/lib/config-manager.js.map +1 -0
- package/dist/lib/config-migrator.js +386 -0
- package/dist/lib/config-migrator.js.map +1 -0
- package/dist/lib/config-validator.js +687 -0
- package/dist/lib/config-validator.js.map +1 -0
- package/dist/lib/correlation-cache.js +311 -0
- package/dist/lib/correlation-cache.js.map +1 -0
- package/dist/lib/correlation.js +263 -0
- package/dist/lib/correlation.js.map +1 -0
- package/dist/lib/database-service/connection-pool-manager.js +520 -0
- package/dist/lib/database-service/connection-pool-manager.js.map +1 -0
- package/dist/lib/database-service/correlation.js +329 -0
- package/dist/lib/database-service/correlation.js.map +1 -0
- package/dist/lib/database-service/errors.js +120 -0
- package/dist/lib/database-service/errors.js.map +1 -0
- package/dist/lib/database-service/index.js +168 -0
- package/dist/lib/database-service/index.js.map +1 -0
- package/dist/lib/database-service/postgres-adapter.js +526 -0
- package/dist/lib/database-service/postgres-adapter.js.map +1 -0
- package/dist/lib/database-service/redis-adapter.js +360 -0
- package/dist/lib/database-service/redis-adapter.js.map +1 -0
- package/dist/lib/database-service/sqlite-adapter.js +544 -0
- package/dist/lib/database-service/sqlite-adapter.js.map +1 -0
- package/dist/lib/database-service/transaction-manager.js +773 -0
- package/dist/lib/database-service/transaction-manager.js.map +1 -0
- package/dist/lib/database-service/types.js +23 -0
- package/dist/lib/database-service/types.js.map +1 -0
- package/dist/lib/deadlock-resolver.js +292 -0
- package/dist/lib/deadlock-resolver.js.map +1 -0
- package/dist/lib/distributed-lock.js +451 -0
- package/dist/lib/distributed-lock.js.map +1 -0
- package/dist/lib/edge-case-deduplicator.js +227 -0
- package/dist/lib/edge-case-deduplicator.js.map +1 -0
- package/dist/lib/encryption-manager.js +322 -0
- package/dist/lib/encryption-manager.js.map +1 -0
- package/dist/lib/error-aggregator.js +234 -0
- package/dist/lib/error-aggregator.js.map +1 -0
- package/dist/lib/errors.js +287 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/file-lock-manager.js +578 -0
- package/dist/lib/file-lock-manager.js.map +1 -0
- package/dist/lib/file-operations.js +367 -0
- package/dist/lib/file-operations.js.map +1 -0
- package/dist/lib/idempotent-write.js +237 -0
- package/dist/lib/idempotent-write.js.map +1 -0
- package/dist/lib/integration-schema-validator.js +522 -0
- package/dist/lib/integration-schema-validator.js.map +1 -0
- package/dist/lib/lock-health-monitor.js +298 -0
- package/dist/lib/lock-health-monitor.js.map +1 -0
- package/dist/lib/log-shipper.js +422 -0
- package/dist/lib/log-shipper.js.map +1 -0
- package/dist/lib/logging.js +146 -0
- package/dist/lib/logging.js.map +1 -0
- package/dist/lib/message-deduplicator.js +439 -0
- package/dist/lib/message-deduplicator.js.map +1 -0
- package/dist/lib/multi-system-query.js +604 -0
- package/dist/lib/multi-system-query.js.map +1 -0
- package/dist/lib/orphan-detector.js +332 -0
- package/dist/lib/orphan-detector.js.map +1 -0
- package/dist/lib/password-generator.js +166 -0
- package/dist/lib/password-generator.js.map +1 -0
- package/dist/lib/path-validator.js +429 -0
- package/dist/lib/path-validator.js.map +1 -0
- package/dist/lib/query-translator.js +905 -0
- package/dist/lib/query-translator.js.map +1 -0
- package/dist/lib/queue-recovery.js +469 -0
- package/dist/lib/queue-recovery.js.map +1 -0
- package/dist/lib/redis-queue-manager.js +512 -0
- package/dist/lib/redis-queue-manager.js.map +1 -0
- package/dist/lib/reflection-archiver.js +272 -0
- package/dist/lib/reflection-archiver.js.map +1 -0
- package/dist/lib/retry-manager.js +453 -0
- package/dist/lib/retry-manager.js.map +1 -0
- package/dist/lib/retry.js +262 -0
- package/dist/lib/retry.js.map +1 -0
- package/dist/lib/schema-transform.js +695 -0
- package/dist/lib/schema-transform.js.map +1 -0
- package/dist/lib/schema-validator.js +491 -0
- package/dist/lib/schema-validator.js.map +1 -0
- package/dist/lib/skill-cache.js +297 -0
- package/dist/lib/skill-cache.js.map +1 -0
- package/dist/lib/skill-content-manager.js +337 -0
- package/dist/lib/skill-content-manager.js.map +1 -0
- package/dist/lib/skill-frontmatter-parser.js +237 -0
- package/dist/lib/skill-frontmatter-parser.js.map +1 -0
- package/dist/lib/skill-git-integration.js +275 -0
- package/dist/lib/skill-git-integration.js.map +1 -0
- package/dist/lib/skill-markdown-validator.js +396 -0
- package/dist/lib/skill-markdown-validator.js.map +1 -0
- package/dist/lib/skill-output-parser.js +312 -0
- package/dist/lib/skill-output-parser.js.map +1 -0
- package/dist/lib/unified-query-api.js +467 -0
- package/dist/lib/unified-query-api.js.map +1 -0
- package/dist/middleware/auth-middleware.js +350 -0
- package/dist/middleware/auth-middleware.js.map +1 -0
- package/dist/middleware/schema-validation.js +347 -0
- package/dist/middleware/schema-validation.js.map +1 -0
- package/dist/providers/anthropic-provider.js +1 -1
- package/dist/providers/anthropic-provider.js.map +1 -1
- package/dist/providers/provider-factory.js +2 -2
- package/dist/providers/provider-factory.js.map +1 -1
- package/dist/services/edge-case-analyzer.js +321 -0
- package/dist/services/edge-case-analyzer.js.map +1 -0
- package/dist/services/edge-case-deduplicator.js +266 -0
- package/dist/services/edge-case-deduplicator.js.map +1 -0
- package/dist/services/edge-case-detector.js +337 -0
- package/dist/services/edge-case-detector.js.map +1 -0
- package/dist/services/edge-case-tracker.js +547 -0
- package/dist/services/edge-case-tracker.js.map +1 -0
- package/dist/services/health-check-system.js +586 -0
- package/dist/services/health-check-system.js.map +1 -0
- package/dist/services/metrics-logger.js +412 -0
- package/dist/services/metrics-logger.js.map +1 -0
- package/dist/services/patch-generator.js +378 -0
- package/dist/services/patch-generator.js.map +1 -0
- package/dist/services/patch-validator.js +337 -0
- package/dist/services/patch-validator.js.map +1 -0
- package/dist/services/performance-monitor.js +811 -0
- package/dist/services/performance-monitor.js.map +1 -0
- package/dist/services/promotion-pipeline.js +918 -0
- package/dist/services/promotion-pipeline.js.map +1 -0
- package/dist/services/promotion-validator.js +394 -0
- package/dist/services/promotion-validator.js.map +1 -0
- package/dist/services/reflection-logger.js +388 -0
- package/dist/services/reflection-logger.js.map +1 -0
- package/dist/services/skill-deployment.js +472 -0
- package/dist/services/skill-deployment.js.map +1 -0
- package/dist/services/skill-loader.js +427 -0
- package/dist/services/skill-loader.js.map +1 -0
- package/dist/services/skill-promotion.js +372 -0
- package/dist/services/skill-promotion.js.map +1 -0
- package/dist/services/skill-validator.js +454 -0
- package/dist/services/skill-validator.js.map +1 -0
- package/dist/services/skill-versioning.js +244 -0
- package/dist/services/skill-versioning.js.map +1 -0
- package/dist/services/workspace-supervisor.js +597 -0
- package/dist/services/workspace-supervisor.js.map +1 -0
- package/dist/types/agent-output.js +44 -0
- package/dist/types/agent-output.js.map +1 -0
- package/dist/types/config.js +28 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/edge-case.js +45 -0
- package/dist/types/edge-case.js.map +1 -0
- package/package.json +201 -176
- package/readme/README.md +19 -4
- package/scripts/artifact-cleanup.sh +392 -0
- package/scripts/backup-cleanup.sh +627 -0
- package/scripts/cleanup-workspaces.sh +412 -0
- package/scripts/cleanup-yaml-configs.sh +141 -0
- package/scripts/deploy-approved-skills.sh +263 -0
- package/scripts/deploy-production.sh +355 -355
- package/scripts/docker-playwright-fix.sh +311 -311
- package/scripts/docker-rebuild-all-agents.sh +127 -127
- package/scripts/health-check.sh +447 -0
- package/scripts/log-aggregator.sh +554 -0
- package/scripts/log-monitor.sh +629 -0
- package/scripts/manage-agent-workspaces.sh +434 -0
- package/scripts/memory-leak-prevention.sh +305 -305
- package/scripts/migrate-artifacts.sh +563 -0
- package/scripts/migrate-schema.sh +533 -0
- package/scripts/migrate-yaml-to-json.sh +465 -0
- package/scripts/promote-staged-skills.sh +423 -0
- package/scripts/run-marketing-tests.sh +42 -42
- package/scripts/update_paths.sh +46 -46
- package/scripts/verify-no-secrets.sh +88 -35
- package/.claude/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
- package/.claude/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
- package/.claude/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
- package/.claude/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
- package/README.md.backup_before_replace +0 -781
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
- package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/skill-content-manager.ts"],"sourcesContent":["/**\r\n * Skill Content Manager\r\n *\r\n * Manages standardized skill content storage, organization, and versioning\r\n * Enforces directory structure, validates required files, tracks versions\r\n *\r\n * @module skill-content-manager\r\n * @version 1.0.0\r\n */\r\n\r\nimport { join, basename, dirname } from 'path';\r\nimport { stat, readdir, access, chmod, readFile, writeFile, mkdir } from 'fs/promises';\r\nimport { constants } from 'fs';\r\nimport { StandardError } from './errors.js';\r\nimport {\r\n SkillFrontmatter,\r\n ParsedSkillDocument,\r\n parseFrontmatter,\r\n validateFrontmatter,\r\n parseAndValidate,\r\n updateFrontmatter,\r\n createSkillDocument\r\n} from './skill-frontmatter-parser.js';\r\nimport {\r\n calculateFileHash,\r\n getCommitMetadata,\r\n getVersionHistory,\r\n commitFile,\r\n hasUncommittedChanges,\r\n verifyContentIntegrity,\r\n getFileCreationDate,\r\n getFileModificationDate,\r\n GitCommitMetadata,\r\n VersionHistoryEntry\r\n} from './skill-git-integration.js';\r\n\r\n/**\r\n * Required files in skill directory\r\n */\r\nexport const REQUIRED_SKILL_FILES = [\r\n 'SKILL.md',\r\n 'execute.sh',\r\n 'test.sh',\r\n 'validate.sh',\r\n 'package.json'\r\n] as const;\r\n\r\n/**\r\n * Skill directory structure validation result\r\n */\r\nexport interface SkillStructureValidation {\r\n valid: boolean;\r\n skillName: string;\r\n skillPath: string;\r\n missingFiles: string[];\r\n invalidPermissions: string[];\r\n errors: string[];\r\n warnings: string[];\r\n}\r\n\r\n/**\r\n * Complete skill metadata including git info\r\n */\r\nexport interface SkillMetadata extends SkillFrontmatter {\r\n skillPath: string;\r\n contentHash: string;\r\n gitMetadata?: GitCommitMetadata;\r\n versionHistory?: VersionHistoryEntry[];\r\n hasUncommittedChanges?: boolean;\r\n fileCreated?: string;\r\n fileModified?: string;\r\n}\r\n\r\n/**\r\n * Skill update options\r\n */\r\nexport interface SkillUpdateOptions {\r\n autoCommit?: boolean;\r\n commitMessage?: string;\r\n updateTimestamp?: boolean;\r\n validateStructure?: boolean;\r\n}\r\n\r\n/**\r\n * Skill content manager error\r\n */\r\nexport class SkillContentError extends StandardError {\r\n constructor(message: string, public readonly context?: Record<string, unknown>) {\r\n super('SKILL_CONTENT_ERROR', message, context);\r\n this.name = 'SkillContentError';\r\n }\r\n}\r\n\r\n/**\r\n * Validate skill directory structure\r\n *\r\n * @param skillPath - Path to skill directory\r\n * @returns Validation result with details\r\n */\r\nexport async function validateSkillStructure(\r\n skillPath: string\r\n): Promise<SkillStructureValidation> {\r\n const skillName = basename(skillPath);\r\n const missingFiles: string[] = [];\r\n const invalidPermissions: string[] = [];\r\n const errors: string[] = [];\r\n const warnings: string[] = [];\r\n\r\n try {\r\n // Check if directory exists\r\n const dirStat = await stat(skillPath);\r\n if (!dirStat.isDirectory()) {\r\n errors.push(`${skillPath} is not a directory`);\r\n return {\r\n valid: false,\r\n skillName,\r\n skillPath,\r\n missingFiles,\r\n invalidPermissions,\r\n errors,\r\n warnings\r\n };\r\n }\r\n } catch (error) {\r\n errors.push(`Skill directory does not exist: ${skillPath}`);\r\n return {\r\n valid: false,\r\n skillName,\r\n skillPath,\r\n missingFiles,\r\n invalidPermissions,\r\n errors,\r\n warnings\r\n };\r\n }\r\n\r\n // Check required files\r\n for (const fileName of REQUIRED_SKILL_FILES) {\r\n const filePath = join(skillPath, fileName);\r\n\r\n try {\r\n await access(filePath, constants.F_OK);\r\n\r\n // Check execute permission for .sh files\r\n if (fileName.endsWith('.sh')) {\r\n try {\r\n await access(filePath, constants.X_OK);\r\n } catch {\r\n invalidPermissions.push(fileName);\r\n errors.push(`${fileName} is not executable`);\r\n }\r\n }\r\n } catch {\r\n missingFiles.push(fileName);\r\n errors.push(`Required file missing: ${fileName}`);\r\n }\r\n }\r\n\r\n // Validate SKILL.md frontmatter if exists\r\n const skillMdPath = join(skillPath, 'SKILL.md');\r\n try {\r\n await access(skillMdPath, constants.F_OK);\r\n const content = await readFile(skillMdPath, 'utf-8');\r\n\r\n try {\r\n const parsed = parseFrontmatter(content);\r\n const validation = validateFrontmatter(parsed.frontmatter);\r\n\r\n if (!validation.valid) {\r\n errors.push(...validation.errors);\r\n }\r\n\r\n warnings.push(...validation.warnings);\r\n } catch (error) {\r\n errors.push(`SKILL.md frontmatter error: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n } catch {\r\n // Already reported as missing file\r\n }\r\n\r\n return {\r\n valid: errors.length === 0,\r\n skillName,\r\n skillPath,\r\n missingFiles,\r\n invalidPermissions,\r\n errors,\r\n warnings\r\n };\r\n}\r\n\r\n/**\r\n * Fix skill file permissions\r\n *\r\n * @param skillPath - Path to skill directory\r\n * @returns Array of files that were fixed\r\n */\r\nexport async function fixSkillPermissions(skillPath: string): Promise<string[]> {\r\n const fixed: string[] = [];\r\n\r\n for (const fileName of REQUIRED_SKILL_FILES) {\r\n if (fileName.endsWith('.sh')) {\r\n const filePath = join(skillPath, fileName);\r\n\r\n try {\r\n await access(filePath, constants.F_OK);\r\n await chmod(filePath, 0o755); // rwxr-xr-x\r\n fixed.push(fileName);\r\n } catch {\r\n // File doesn't exist, skip\r\n }\r\n }\r\n }\r\n\r\n return fixed;\r\n}\r\n\r\n/**\r\n * Load skill metadata with git integration\r\n *\r\n * @param skillPath - Path to skill directory\r\n * @param includeHistory - Include version history (default: false)\r\n * @returns Complete skill metadata\r\n */\r\nexport async function loadSkillMetadata(\r\n skillPath: string,\r\n includeHistory: boolean = false\r\n): Promise<SkillMetadata> {\r\n const skillMdPath = join(skillPath, 'SKILL.md');\r\n\r\n try {\r\n // Read and parse SKILL.md\r\n const content = await readFile(skillMdPath, 'utf-8');\r\n const parsed = parseAndValidate(content);\r\n\r\n // Calculate content hash\r\n const contentHash = calculateFileHash(skillMdPath);\r\n\r\n // Get git metadata (graceful fallback if not in git repo)\r\n let gitMetadata: GitCommitMetadata | undefined;\r\n let versionHistory: VersionHistoryEntry[] | undefined;\r\n let hasChanges: boolean | undefined;\r\n let fileCreated: string | undefined;\r\n let fileModified: string | undefined;\r\n\r\n try {\r\n gitMetadata = await getCommitMetadata(skillMdPath);\r\n hasChanges = await hasUncommittedChanges(skillMdPath);\r\n fileCreated = await getFileCreationDate(skillMdPath);\r\n fileModified = await getFileModificationDate(skillMdPath);\r\n\r\n if (includeHistory) {\r\n versionHistory = await getVersionHistory(skillMdPath);\r\n }\r\n } catch {\r\n // Not in git repo or no git history - continue without git data\r\n }\r\n\r\n return {\r\n ...parsed.frontmatter,\r\n skillPath,\r\n contentHash: await contentHash,\r\n gitMetadata,\r\n versionHistory,\r\n hasUncommittedChanges: hasChanges,\r\n fileCreated,\r\n fileModified\r\n };\r\n } catch (error) {\r\n throw new SkillContentError(\r\n `Failed to load skill metadata from ${skillPath}`,\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Update skill frontmatter\r\n *\r\n * @param skillPath - Path to skill directory\r\n * @param updates - Partial frontmatter updates\r\n * @param options - Update options\r\n * @returns Updated skill metadata\r\n */\r\nexport async function updateSkillFrontmatter(\r\n skillPath: string,\r\n updates: Partial<SkillFrontmatter>,\r\n options: SkillUpdateOptions = {}\r\n): Promise<SkillMetadata> {\r\n const {\r\n autoCommit = false,\r\n commitMessage,\r\n updateTimestamp = true,\r\n validateStructure = true\r\n } = options;\r\n\r\n const skillMdPath = join(skillPath, 'SKILL.md');\r\n\r\n try {\r\n // Validate structure first if requested\r\n if (validateStructure) {\r\n const validation = await validateSkillStructure(skillPath);\r\n if (!validation.valid) {\r\n throw new SkillContentError('Skill structure validation failed', {\r\n errors: validation.errors\r\n });\r\n }\r\n }\r\n\r\n // Read current content\r\n const currentContent = await readFile(skillMdPath, 'utf-8');\r\n\r\n // Update frontmatter\r\n const updatedContent = updateFrontmatter(currentContent, updates);\r\n\r\n // Write updated content\r\n await writeFile(skillMdPath, updatedContent, 'utf-8');\r\n\r\n // Auto-commit if requested\r\n if (autoCommit) {\r\n const message = commitMessage || `Update ${basename(skillPath)} frontmatter`;\r\n await commitFile(skillMdPath, message);\r\n }\r\n\r\n // Return updated metadata\r\n return await loadSkillMetadata(skillPath);\r\n } catch (error) {\r\n throw new SkillContentError(\r\n `Failed to update skill frontmatter for ${skillPath}`,\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Create new skill directory with standard structure\r\n *\r\n * @param parentDir - Parent directory for skills\r\n * @param skillName - Name of skill to create\r\n * @param frontmatter - Initial frontmatter\r\n * @param content - Initial SKILL.md content\r\n * @returns Created skill metadata\r\n */\r\nexport async function createSkill(\r\n parentDir: string,\r\n skillName: string,\r\n frontmatter: SkillFrontmatter,\r\n content: string = ''\r\n): Promise<SkillMetadata> {\r\n const skillPath = join(parentDir, skillName);\r\n\r\n try {\r\n // Create skill directory\r\n await mkdir(skillPath, { recursive: true });\r\n\r\n // Create SKILL.md\r\n const skillMdPath = join(skillPath, 'SKILL.md');\r\n const skillDocument = createSkillDocument(frontmatter, content);\r\n await writeFile(skillMdPath, skillDocument, 'utf-8');\r\n\r\n // Create placeholder files\r\n const executeSh = `#!/bin/bash\r\n# ${frontmatter.name} - Execution Script\r\n# Version: ${frontmatter.version}\r\n\r\nset -euo pipefail\r\n\r\necho \"Executing ${frontmatter.name}...\"\r\n# Add implementation here\r\n`;\r\n\r\n const testSh = `#!/bin/bash\r\n# ${frontmatter.name} - Test Script\r\n# Version: ${frontmatter.version}\r\n\r\nset -euo pipefail\r\n\r\necho \"Testing ${frontmatter.name}...\"\r\n# Add tests here\r\n`;\r\n\r\n const validateSh = `#!/bin/bash\r\n# ${frontmatter.name} - Validation Script\r\n# Version: ${frontmatter.version}\r\n\r\nset -euo pipefail\r\n\r\necho \"Validating ${frontmatter.name}...\"\r\n# Add validation here\r\n`;\r\n\r\n const packageJson = {\r\n name: skillName,\r\n version: frontmatter.version,\r\n description: frontmatter.description,\r\n scripts: {\r\n execute: './execute.sh',\r\n test: './test.sh',\r\n validate: './validate.sh'\r\n }\r\n };\r\n\r\n await writeFile(join(skillPath, 'execute.sh'), executeSh, 'utf-8');\r\n await writeFile(join(skillPath, 'test.sh'), testSh, 'utf-8');\r\n await writeFile(join(skillPath, 'validate.sh'), validateSh, 'utf-8');\r\n await writeFile(join(skillPath, 'package.json'), JSON.stringify(packageJson, null, 2), 'utf-8');\r\n\r\n // Fix permissions\r\n await fixSkillPermissions(skillPath);\r\n\r\n // Return metadata\r\n return await loadSkillMetadata(skillPath);\r\n } catch (error) {\r\n throw new SkillContentError(\r\n `Failed to create skill ${skillName}`,\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Scan directory for all skills\r\n *\r\n * @param skillsDir - Path to skills directory\r\n * @returns Array of skill paths\r\n */\r\nexport async function scanSkills(skillsDir: string): Promise<string[]> {\r\n try {\r\n const entries = await readdir(skillsDir, { withFileTypes: true });\r\n const skillPaths: string[] = [];\r\n\r\n for (const entry of entries) {\r\n if (entry.isDirectory()) {\r\n const skillPath = join(skillsDir, entry.name);\r\n const skillMdPath = join(skillPath, 'SKILL.md');\r\n\r\n try {\r\n await access(skillMdPath, constants.F_OK);\r\n skillPaths.push(skillPath);\r\n } catch {\r\n // Not a valid skill directory, skip\r\n }\r\n }\r\n }\r\n\r\n return skillPaths.sort();\r\n } catch (error) {\r\n throw new SkillContentError(\r\n `Failed to scan skills directory ${skillsDir}`,\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Verify skill content integrity\r\n *\r\n * @param skillPath - Path to skill directory\r\n * @param expectedHash - Expected content hash\r\n * @returns True if content matches expected hash\r\n */\r\nexport async function verifySkillIntegrity(\r\n skillPath: string,\r\n expectedHash: string\r\n): Promise<boolean> {\r\n const skillMdPath = join(skillPath, 'SKILL.md');\r\n return await verifyContentIntegrity(skillMdPath, expectedHash);\r\n}\r\n"],"names":["join","basename","stat","readdir","access","chmod","readFile","writeFile","mkdir","constants","StandardError","parseFrontmatter","validateFrontmatter","parseAndValidate","updateFrontmatter","createSkillDocument","calculateFileHash","getCommitMetadata","getVersionHistory","commitFile","hasUncommittedChanges","verifyContentIntegrity","getFileCreationDate","getFileModificationDate","REQUIRED_SKILL_FILES","SkillContentError","message","context","name","validateSkillStructure","skillPath","skillName","missingFiles","invalidPermissions","errors","warnings","dirStat","isDirectory","push","valid","error","fileName","filePath","F_OK","endsWith","X_OK","skillMdPath","content","parsed","validation","frontmatter","Error","String","length","fixSkillPermissions","fixed","loadSkillMetadata","includeHistory","contentHash","gitMetadata","versionHistory","hasChanges","fileCreated","fileModified","updateSkillFrontmatter","updates","options","autoCommit","commitMessage","updateTimestamp","validateStructure","currentContent","updatedContent","createSkill","parentDir","recursive","skillDocument","executeSh","version","testSh","validateSh","packageJson","description","scripts","execute","test","validate","JSON","stringify","scanSkills","skillsDir","entries","withFileTypes","skillPaths","entry","sort","verifySkillIntegrity","expectedHash"],"mappings":"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"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Frontmatter Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses and validates YAML frontmatter in SKILL.md files
|
|
5
|
+
* Supports semantic versioning and comprehensive metadata tracking
|
|
6
|
+
*
|
|
7
|
+
* @module skill-frontmatter-parser
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
*/ import * as yaml from 'js-yaml';
|
|
10
|
+
import { StandardError } from './errors.js';
|
|
11
|
+
/**
|
|
12
|
+
* Frontmatter parser error
|
|
13
|
+
*/ export class FrontmatterParseError extends StandardError {
|
|
14
|
+
context;
|
|
15
|
+
constructor(message, context){
|
|
16
|
+
super('FRONTMATTER_PARSE_ERROR', message, context), this.context = context;
|
|
17
|
+
this.name = 'FrontmatterParseError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Frontmatter validation error
|
|
22
|
+
*/ export class FrontmatterValidationError extends StandardError {
|
|
23
|
+
errors;
|
|
24
|
+
constructor(message, errors){
|
|
25
|
+
super('FRONTMATTER_VALIDATION_ERROR', message, {
|
|
26
|
+
errors
|
|
27
|
+
}), this.errors = errors;
|
|
28
|
+
this.name = 'FrontmatterValidationError';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Semantic version regex pattern
|
|
33
|
+
*/ const SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
|
34
|
+
/**
|
|
35
|
+
* Valid skill status values
|
|
36
|
+
*/ const VALID_STATUSES = [
|
|
37
|
+
'draft',
|
|
38
|
+
'approved',
|
|
39
|
+
'staging',
|
|
40
|
+
'deployed',
|
|
41
|
+
'deprecated'
|
|
42
|
+
];
|
|
43
|
+
/**
|
|
44
|
+
* Parse SKILL.md content and extract frontmatter
|
|
45
|
+
*
|
|
46
|
+
* @param content - Raw SKILL.md file content
|
|
47
|
+
* @returns Parsed document with frontmatter and content
|
|
48
|
+
* @throws FrontmatterParseError if parsing fails
|
|
49
|
+
*/ export function parseFrontmatter(content) {
|
|
50
|
+
// Extract frontmatter block
|
|
51
|
+
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
|
|
52
|
+
if (!frontmatterMatch) {
|
|
53
|
+
throw new FrontmatterParseError('No frontmatter block found. SKILL.md must start with YAML frontmatter enclosed in ---');
|
|
54
|
+
}
|
|
55
|
+
const [, frontmatterYaml, markdownContent] = frontmatterMatch;
|
|
56
|
+
// Parse YAML
|
|
57
|
+
let frontmatter;
|
|
58
|
+
try {
|
|
59
|
+
frontmatter = yaml.load(frontmatterYaml);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
throw new FrontmatterParseError('Failed to parse YAML frontmatter', {
|
|
62
|
+
error: error instanceof Error ? error.message : String(error),
|
|
63
|
+
yaml: frontmatterYaml.substring(0, 200)
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// Validate structure
|
|
67
|
+
if (!frontmatter || typeof frontmatter !== 'object') {
|
|
68
|
+
throw new FrontmatterParseError('Frontmatter must be a valid YAML object');
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
frontmatter: frontmatter,
|
|
72
|
+
content: markdownContent.trim(),
|
|
73
|
+
raw: content
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Validate frontmatter against schema
|
|
78
|
+
*
|
|
79
|
+
* @param frontmatter - Frontmatter object to validate
|
|
80
|
+
* @returns Validation result with errors and warnings
|
|
81
|
+
*/ export function validateFrontmatter(frontmatter) {
|
|
82
|
+
const errors = [];
|
|
83
|
+
const warnings = [];
|
|
84
|
+
// Required fields
|
|
85
|
+
if (!frontmatter.name || typeof frontmatter.name !== 'string') {
|
|
86
|
+
errors.push('Field "name" is required and must be a string');
|
|
87
|
+
}
|
|
88
|
+
if (!frontmatter.version || typeof frontmatter.version !== 'string') {
|
|
89
|
+
errors.push('Field "version" is required and must be a string');
|
|
90
|
+
} else if (!SEMVER_PATTERN.test(frontmatter.version)) {
|
|
91
|
+
errors.push(`Field "version" must be valid semantic version (e.g., 1.0.0), got: ${frontmatter.version}`);
|
|
92
|
+
}
|
|
93
|
+
if (!Array.isArray(frontmatter.tags)) {
|
|
94
|
+
errors.push('Field "tags" is required and must be an array');
|
|
95
|
+
} else if (frontmatter.tags.length === 0) {
|
|
96
|
+
warnings.push('Field "tags" is empty, consider adding tags for categorization');
|
|
97
|
+
} else if (!frontmatter.tags.every((tag)=>typeof tag === 'string')) {
|
|
98
|
+
errors.push('All tags must be strings');
|
|
99
|
+
}
|
|
100
|
+
if (!frontmatter.status) {
|
|
101
|
+
errors.push('Field "status" is required');
|
|
102
|
+
} else if (!VALID_STATUSES.includes(frontmatter.status)) {
|
|
103
|
+
errors.push(`Field "status" must be one of: ${VALID_STATUSES.join(', ')}, got: ${frontmatter.status}`);
|
|
104
|
+
}
|
|
105
|
+
if (!frontmatter.author || typeof frontmatter.author !== 'string') {
|
|
106
|
+
errors.push('Field "author" is required and must be a string');
|
|
107
|
+
}
|
|
108
|
+
if (!frontmatter.description || typeof frontmatter.description !== 'string') {
|
|
109
|
+
errors.push('Field "description" is required and must be a string');
|
|
110
|
+
} else if (frontmatter.description.length < 10) {
|
|
111
|
+
warnings.push('Field "description" is very short, consider adding more detail');
|
|
112
|
+
}
|
|
113
|
+
// Optional fields validation
|
|
114
|
+
if (frontmatter.dependencies !== undefined) {
|
|
115
|
+
if (!Array.isArray(frontmatter.dependencies)) {
|
|
116
|
+
errors.push('Field "dependencies" must be an array if provided');
|
|
117
|
+
} else if (!frontmatter.dependencies.every((dep)=>typeof dep === 'string')) {
|
|
118
|
+
errors.push('All dependencies must be strings');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (frontmatter.created !== undefined && typeof frontmatter.created !== 'string') {
|
|
122
|
+
errors.push('Field "created" must be a string (ISO date) if provided');
|
|
123
|
+
}
|
|
124
|
+
if (frontmatter.updated !== undefined && typeof frontmatter.updated !== 'string') {
|
|
125
|
+
errors.push('Field "updated" must be a string (ISO date) if provided');
|
|
126
|
+
}
|
|
127
|
+
// Date validation
|
|
128
|
+
if (frontmatter.created) {
|
|
129
|
+
const createdDate = new Date(frontmatter.created);
|
|
130
|
+
if (isNaN(createdDate.getTime())) {
|
|
131
|
+
errors.push(`Field "created" is not a valid date: ${frontmatter.created}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (frontmatter.updated) {
|
|
135
|
+
const updatedDate = new Date(frontmatter.updated);
|
|
136
|
+
if (isNaN(updatedDate.getTime())) {
|
|
137
|
+
errors.push(`Field "updated" is not a valid date: ${frontmatter.updated}`);
|
|
138
|
+
}
|
|
139
|
+
// Check if updated is after created
|
|
140
|
+
if (frontmatter.created) {
|
|
141
|
+
const createdDate = new Date(frontmatter.created);
|
|
142
|
+
const updatedDate = new Date(frontmatter.updated);
|
|
143
|
+
if (updatedDate < createdDate) {
|
|
144
|
+
errors.push('Field "updated" cannot be before "created"');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
valid: errors.length === 0,
|
|
150
|
+
errors,
|
|
151
|
+
warnings
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Parse and validate SKILL.md content
|
|
156
|
+
*
|
|
157
|
+
* @param content - Raw SKILL.md file content
|
|
158
|
+
* @returns Parsed and validated document
|
|
159
|
+
* @throws FrontmatterValidationError if validation fails
|
|
160
|
+
*/ export function parseAndValidate(content) {
|
|
161
|
+
const parsed = parseFrontmatter(content);
|
|
162
|
+
const validation = validateFrontmatter(parsed.frontmatter);
|
|
163
|
+
if (!validation.valid) {
|
|
164
|
+
throw new FrontmatterValidationError('Frontmatter validation failed', validation.errors);
|
|
165
|
+
}
|
|
166
|
+
return parsed;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Serialize frontmatter to YAML string
|
|
170
|
+
*
|
|
171
|
+
* @param frontmatter - Frontmatter object to serialize
|
|
172
|
+
* @returns YAML string representation
|
|
173
|
+
*/ export function serializeFrontmatter(frontmatter) {
|
|
174
|
+
return yaml.dump(frontmatter, {
|
|
175
|
+
indent: 2,
|
|
176
|
+
lineWidth: 100,
|
|
177
|
+
noRefs: true,
|
|
178
|
+
sortKeys: false
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Update frontmatter in SKILL.md content
|
|
183
|
+
*
|
|
184
|
+
* @param content - Original SKILL.md content
|
|
185
|
+
* @param updates - Partial frontmatter updates
|
|
186
|
+
* @returns Updated SKILL.md content
|
|
187
|
+
*/ export function updateFrontmatter(content, updates) {
|
|
188
|
+
const parsed = parseFrontmatter(content);
|
|
189
|
+
const updated = {
|
|
190
|
+
...parsed.frontmatter,
|
|
191
|
+
...updates
|
|
192
|
+
};
|
|
193
|
+
// Update timestamp
|
|
194
|
+
updated.updated = new Date().toISOString().split('T')[0];
|
|
195
|
+
const yamlString = serializeFrontmatter(updated);
|
|
196
|
+
return `---\n${yamlString}---\n${parsed.content}`;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Create new SKILL.md content with frontmatter
|
|
200
|
+
*
|
|
201
|
+
* @param frontmatter - Frontmatter object
|
|
202
|
+
* @param content - Markdown content
|
|
203
|
+
* @returns Complete SKILL.md content
|
|
204
|
+
*/ export function createSkillDocument(frontmatter, content) {
|
|
205
|
+
// Validate before creating
|
|
206
|
+
const validation = validateFrontmatter(frontmatter);
|
|
207
|
+
if (!validation.valid) {
|
|
208
|
+
throw new FrontmatterValidationError('Cannot create skill document with invalid frontmatter', validation.errors);
|
|
209
|
+
}
|
|
210
|
+
const yamlString = serializeFrontmatter(frontmatter);
|
|
211
|
+
return `---\n${yamlString}---\n${content}`;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Extract frontmatter summary for display
|
|
215
|
+
*
|
|
216
|
+
* @param frontmatter - Frontmatter object
|
|
217
|
+
* @returns Human-readable summary
|
|
218
|
+
*/ export function getFrontmatterSummary(frontmatter) {
|
|
219
|
+
return `${frontmatter.name} v${frontmatter.version} [${frontmatter.status}]`;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Compare two versions using semantic versioning
|
|
223
|
+
*
|
|
224
|
+
* @param version1 - First version string
|
|
225
|
+
* @param version2 - Second version string
|
|
226
|
+
* @returns -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
227
|
+
*/ export function compareVersions(version1, version2) {
|
|
228
|
+
const v1Parts = version1.split('.').map(Number);
|
|
229
|
+
const v2Parts = version2.split('.').map(Number);
|
|
230
|
+
for(let i = 0; i < 3; i++){
|
|
231
|
+
if (v1Parts[i] > v2Parts[i]) return 1;
|
|
232
|
+
if (v1Parts[i] < v2Parts[i]) return -1;
|
|
233
|
+
}
|
|
234
|
+
return 0;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
//# sourceMappingURL=skill-frontmatter-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/skill-frontmatter-parser.ts"],"sourcesContent":["/**\r\n * Skill Frontmatter Parser\r\n *\r\n * Parses and validates YAML frontmatter in SKILL.md files\r\n * Supports semantic versioning and comprehensive metadata tracking\r\n *\r\n * @module skill-frontmatter-parser\r\n * @version 1.0.0\r\n */\r\n\r\nimport * as yaml from 'js-yaml';\r\nimport { StandardError } from './errors.js';\r\n\r\n/**\r\n * Skill status lifecycle stages\r\n */\r\nexport type SkillStatus = 'draft' | 'approved' | 'staging' | 'deployed' | 'deprecated';\r\n\r\n/**\r\n * Skill frontmatter schema\r\n */\r\nexport interface SkillFrontmatter {\r\n name: string;\r\n version: string;\r\n tags: string[];\r\n status: SkillStatus;\r\n author: string;\r\n description: string;\r\n dependencies?: string[];\r\n created?: string;\r\n updated?: string;\r\n\r\n // Optional extended metadata\r\n complexity?: 'Low' | 'Medium' | 'High';\r\n keywords?: string[];\r\n triggers?: string[];\r\n performance_targets?: Record<string, number | string>;\r\n}\r\n\r\n/**\r\n * Parsed frontmatter with content\r\n */\r\nexport interface ParsedSkillDocument {\r\n frontmatter: SkillFrontmatter;\r\n content: string;\r\n raw: string;\r\n}\r\n\r\n/**\r\n * Frontmatter validation result\r\n */\r\nexport interface ValidationResult {\r\n valid: boolean;\r\n errors: string[];\r\n warnings: string[];\r\n}\r\n\r\n/**\r\n * Frontmatter parser error\r\n */\r\nexport class FrontmatterParseError extends StandardError {\r\n constructor(message: string, public readonly context?: Record<string, unknown>) {\r\n super('FRONTMATTER_PARSE_ERROR', message, context);\r\n this.name = 'FrontmatterParseError';\r\n }\r\n}\r\n\r\n/**\r\n * Frontmatter validation error\r\n */\r\nexport class FrontmatterValidationError extends StandardError {\r\n constructor(message: string, public readonly errors: string[]) {\r\n super('FRONTMATTER_VALIDATION_ERROR', message, { errors });\r\n this.name = 'FrontmatterValidationError';\r\n }\r\n}\r\n\r\n/**\r\n * Semantic version regex pattern\r\n */\r\nconst SEMVER_PATTERN = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$/;\r\n\r\n/**\r\n * Valid skill status values\r\n */\r\nconst VALID_STATUSES: SkillStatus[] = ['draft', 'approved', 'staging', 'deployed', 'deprecated'];\r\n\r\n/**\r\n * Parse SKILL.md content and extract frontmatter\r\n *\r\n * @param content - Raw SKILL.md file content\r\n * @returns Parsed document with frontmatter and content\r\n * @throws FrontmatterParseError if parsing fails\r\n */\r\nexport function parseFrontmatter(content: string): ParsedSkillDocument {\r\n // Extract frontmatter block\r\n const frontmatterMatch = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n([\\s\\S]*)$/);\r\n\r\n if (!frontmatterMatch) {\r\n throw new FrontmatterParseError('No frontmatter block found. SKILL.md must start with YAML frontmatter enclosed in ---');\r\n }\r\n\r\n const [, frontmatterYaml, markdownContent] = frontmatterMatch;\r\n\r\n // Parse YAML\r\n let frontmatter: unknown;\r\n try {\r\n frontmatter = yaml.load(frontmatterYaml);\r\n } catch (error) {\r\n throw new FrontmatterParseError(\r\n 'Failed to parse YAML frontmatter',\r\n {\r\n error: error instanceof Error ? error.message : String(error),\r\n yaml: frontmatterYaml.substring(0, 200)\r\n }\r\n );\r\n }\r\n\r\n // Validate structure\r\n if (!frontmatter || typeof frontmatter !== 'object') {\r\n throw new FrontmatterParseError('Frontmatter must be a valid YAML object');\r\n }\r\n\r\n return {\r\n frontmatter: frontmatter as SkillFrontmatter,\r\n content: markdownContent.trim(),\r\n raw: content\r\n };\r\n}\r\n\r\n/**\r\n * Validate frontmatter against schema\r\n *\r\n * @param frontmatter - Frontmatter object to validate\r\n * @returns Validation result with errors and warnings\r\n */\r\nexport function validateFrontmatter(frontmatter: SkillFrontmatter): ValidationResult {\r\n const errors: string[] = [];\r\n const warnings: string[] = [];\r\n\r\n // Required fields\r\n if (!frontmatter.name || typeof frontmatter.name !== 'string') {\r\n errors.push('Field \"name\" is required and must be a string');\r\n }\r\n\r\n if (!frontmatter.version || typeof frontmatter.version !== 'string') {\r\n errors.push('Field \"version\" is required and must be a string');\r\n } else if (!SEMVER_PATTERN.test(frontmatter.version)) {\r\n errors.push(`Field \"version\" must be valid semantic version (e.g., 1.0.0), got: ${frontmatter.version}`);\r\n }\r\n\r\n if (!Array.isArray(frontmatter.tags)) {\r\n errors.push('Field \"tags\" is required and must be an array');\r\n } else if (frontmatter.tags.length === 0) {\r\n warnings.push('Field \"tags\" is empty, consider adding tags for categorization');\r\n } else if (!frontmatter.tags.every(tag => typeof tag === 'string')) {\r\n errors.push('All tags must be strings');\r\n }\r\n\r\n if (!frontmatter.status) {\r\n errors.push('Field \"status\" is required');\r\n } else if (!VALID_STATUSES.includes(frontmatter.status)) {\r\n errors.push(`Field \"status\" must be one of: ${VALID_STATUSES.join(', ')}, got: ${frontmatter.status}`);\r\n }\r\n\r\n if (!frontmatter.author || typeof frontmatter.author !== 'string') {\r\n errors.push('Field \"author\" is required and must be a string');\r\n }\r\n\r\n if (!frontmatter.description || typeof frontmatter.description !== 'string') {\r\n errors.push('Field \"description\" is required and must be a string');\r\n } else if (frontmatter.description.length < 10) {\r\n warnings.push('Field \"description\" is very short, consider adding more detail');\r\n }\r\n\r\n // Optional fields validation\r\n if (frontmatter.dependencies !== undefined) {\r\n if (!Array.isArray(frontmatter.dependencies)) {\r\n errors.push('Field \"dependencies\" must be an array if provided');\r\n } else if (!frontmatter.dependencies.every(dep => typeof dep === 'string')) {\r\n errors.push('All dependencies must be strings');\r\n }\r\n }\r\n\r\n if (frontmatter.created !== undefined && typeof frontmatter.created !== 'string') {\r\n errors.push('Field \"created\" must be a string (ISO date) if provided');\r\n }\r\n\r\n if (frontmatter.updated !== undefined && typeof frontmatter.updated !== 'string') {\r\n errors.push('Field \"updated\" must be a string (ISO date) if provided');\r\n }\r\n\r\n // Date validation\r\n if (frontmatter.created) {\r\n const createdDate = new Date(frontmatter.created);\r\n if (isNaN(createdDate.getTime())) {\r\n errors.push(`Field \"created\" is not a valid date: ${frontmatter.created}`);\r\n }\r\n }\r\n\r\n if (frontmatter.updated) {\r\n const updatedDate = new Date(frontmatter.updated);\r\n if (isNaN(updatedDate.getTime())) {\r\n errors.push(`Field \"updated\" is not a valid date: ${frontmatter.updated}`);\r\n }\r\n\r\n // Check if updated is after created\r\n if (frontmatter.created) {\r\n const createdDate = new Date(frontmatter.created);\r\n const updatedDate = new Date(frontmatter.updated);\r\n if (updatedDate < createdDate) {\r\n errors.push('Field \"updated\" cannot be before \"created\"');\r\n }\r\n }\r\n }\r\n\r\n return {\r\n valid: errors.length === 0,\r\n errors,\r\n warnings\r\n };\r\n}\r\n\r\n/**\r\n * Parse and validate SKILL.md content\r\n *\r\n * @param content - Raw SKILL.md file content\r\n * @returns Parsed and validated document\r\n * @throws FrontmatterValidationError if validation fails\r\n */\r\nexport function parseAndValidate(content: string): ParsedSkillDocument {\r\n const parsed = parseFrontmatter(content);\r\n const validation = validateFrontmatter(parsed.frontmatter);\r\n\r\n if (!validation.valid) {\r\n throw new FrontmatterValidationError(\r\n 'Frontmatter validation failed',\r\n validation.errors\r\n );\r\n }\r\n\r\n return parsed;\r\n}\r\n\r\n/**\r\n * Serialize frontmatter to YAML string\r\n *\r\n * @param frontmatter - Frontmatter object to serialize\r\n * @returns YAML string representation\r\n */\r\nexport function serializeFrontmatter(frontmatter: SkillFrontmatter): string {\r\n return yaml.dump(frontmatter, {\r\n indent: 2,\r\n lineWidth: 100,\r\n noRefs: true,\r\n sortKeys: false\r\n });\r\n}\r\n\r\n/**\r\n * Update frontmatter in SKILL.md content\r\n *\r\n * @param content - Original SKILL.md content\r\n * @param updates - Partial frontmatter updates\r\n * @returns Updated SKILL.md content\r\n */\r\nexport function updateFrontmatter(\r\n content: string,\r\n updates: Partial<SkillFrontmatter>\r\n): string {\r\n const parsed = parseFrontmatter(content);\r\n const updated = { ...parsed.frontmatter, ...updates };\r\n\r\n // Update timestamp\r\n updated.updated = new Date().toISOString().split('T')[0];\r\n\r\n const yamlString = serializeFrontmatter(updated);\r\n return `---\\n${yamlString}---\\n${parsed.content}`;\r\n}\r\n\r\n/**\r\n * Create new SKILL.md content with frontmatter\r\n *\r\n * @param frontmatter - Frontmatter object\r\n * @param content - Markdown content\r\n * @returns Complete SKILL.md content\r\n */\r\nexport function createSkillDocument(\r\n frontmatter: SkillFrontmatter,\r\n content: string\r\n): string {\r\n // Validate before creating\r\n const validation = validateFrontmatter(frontmatter);\r\n if (!validation.valid) {\r\n throw new FrontmatterValidationError(\r\n 'Cannot create skill document with invalid frontmatter',\r\n validation.errors\r\n );\r\n }\r\n\r\n const yamlString = serializeFrontmatter(frontmatter);\r\n return `---\\n${yamlString}---\\n${content}`;\r\n}\r\n\r\n/**\r\n * Extract frontmatter summary for display\r\n *\r\n * @param frontmatter - Frontmatter object\r\n * @returns Human-readable summary\r\n */\r\nexport function getFrontmatterSummary(frontmatter: SkillFrontmatter): string {\r\n return `${frontmatter.name} v${frontmatter.version} [${frontmatter.status}]`;\r\n}\r\n\r\n/**\r\n * Compare two versions using semantic versioning\r\n *\r\n * @param version1 - First version string\r\n * @param version2 - Second version string\r\n * @returns -1 if v1 < v2, 0 if equal, 1 if v1 > v2\r\n */\r\nexport function compareVersions(version1: string, version2: string): number {\r\n const v1Parts = version1.split('.').map(Number);\r\n const v2Parts = version2.split('.').map(Number);\r\n\r\n for (let i = 0; i < 3; i++) {\r\n if (v1Parts[i] > v2Parts[i]) return 1;\r\n if (v1Parts[i] < v2Parts[i]) return -1;\r\n }\r\n\r\n return 0;\r\n}\r\n"],"names":["yaml","StandardError","FrontmatterParseError","message","context","name","FrontmatterValidationError","errors","SEMVER_PATTERN","VALID_STATUSES","parseFrontmatter","content","frontmatterMatch","match","frontmatterYaml","markdownContent","frontmatter","load","error","Error","String","substring","trim","raw","validateFrontmatter","warnings","push","version","test","Array","isArray","tags","length","every","tag","status","includes","join","author","description","dependencies","undefined","dep","created","updated","createdDate","Date","isNaN","getTime","updatedDate","valid","parseAndValidate","parsed","validation","serializeFrontmatter","dump","indent","lineWidth","noRefs","sortKeys","updateFrontmatter","updates","toISOString","split","yamlString","createSkillDocument","getFrontmatterSummary","compareVersions","version1","version2","v1Parts","map","Number","v2Parts","i"],"mappings":"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"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Git Integration
|
|
3
|
+
*
|
|
4
|
+
* Manages version tracking, content hashing, and git metadata for skills
|
|
5
|
+
* Provides atomic updates with rollback capabilities
|
|
6
|
+
*
|
|
7
|
+
* @module skill-git-integration
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
*/ import { exec } from 'child_process';
|
|
10
|
+
import { promisify } from 'util';
|
|
11
|
+
import { createHash } from 'crypto';
|
|
12
|
+
import { readFile } from 'fs/promises';
|
|
13
|
+
import { StandardError } from './errors.js';
|
|
14
|
+
const execAsync = promisify(exec);
|
|
15
|
+
/**
|
|
16
|
+
* Git integration error
|
|
17
|
+
*/ export class GitIntegrationError extends StandardError {
|
|
18
|
+
context;
|
|
19
|
+
constructor(message, context){
|
|
20
|
+
super('GIT_INTEGRATION_ERROR', message, context), this.context = context;
|
|
21
|
+
this.name = 'GitIntegrationError';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Calculate SHA256 hash of file content
|
|
26
|
+
*
|
|
27
|
+
* @param content - File content string
|
|
28
|
+
* @returns SHA256 hash in hexadecimal
|
|
29
|
+
*/ export function calculateContentHash(content) {
|
|
30
|
+
return createHash('sha256').update(content).digest('hex');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Calculate SHA256 hash of file
|
|
34
|
+
*
|
|
35
|
+
* @param filePath - Path to file
|
|
36
|
+
* @returns SHA256 hash in hexadecimal
|
|
37
|
+
*/ export async function calculateFileHash(filePath) {
|
|
38
|
+
try {
|
|
39
|
+
const content = await readFile(filePath, 'utf-8');
|
|
40
|
+
return calculateContentHash(content);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new GitIntegrationError(`Failed to calculate file hash for ${filePath}`, {
|
|
43
|
+
error: error instanceof Error ? error.message : String(error)
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Check if a path is in a git repository
|
|
49
|
+
*
|
|
50
|
+
* @param filePath - Path to check
|
|
51
|
+
* @returns True if in git repository
|
|
52
|
+
*/ export async function isGitRepository(filePath) {
|
|
53
|
+
try {
|
|
54
|
+
await execAsync('git rev-parse --is-inside-work-tree', {
|
|
55
|
+
cwd: filePath
|
|
56
|
+
});
|
|
57
|
+
return true;
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get git commit metadata for a file
|
|
64
|
+
*
|
|
65
|
+
* @param filePath - Path to file
|
|
66
|
+
* @param commitRef - Git commit reference (default: HEAD)
|
|
67
|
+
* @returns Commit metadata
|
|
68
|
+
*/ export async function getCommitMetadata(filePath, commitRef = 'HEAD') {
|
|
69
|
+
try {
|
|
70
|
+
const { stdout } = await execAsync(`git log -1 --format="%H%n%an%n%ae%n%ai%n%s" ${commitRef} -- "${filePath}"`);
|
|
71
|
+
const [hash, author, email, date, message] = stdout.trim().split('\n');
|
|
72
|
+
if (!hash) {
|
|
73
|
+
throw new GitIntegrationError('No git history found for file', {
|
|
74
|
+
filePath
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
hash,
|
|
79
|
+
author,
|
|
80
|
+
email,
|
|
81
|
+
date,
|
|
82
|
+
message: message || 'No commit message'
|
|
83
|
+
};
|
|
84
|
+
} catch (error) {
|
|
85
|
+
throw new GitIntegrationError(`Failed to get git metadata for ${filePath}`, {
|
|
86
|
+
error: error instanceof Error ? error.message : String(error)
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get version history for a skill file
|
|
92
|
+
*
|
|
93
|
+
* @param filePath - Path to SKILL.md file
|
|
94
|
+
* @param limit - Maximum number of entries (default: 10)
|
|
95
|
+
* @returns Array of version history entries
|
|
96
|
+
*/ export async function getVersionHistory(filePath, limit = 10) {
|
|
97
|
+
try {
|
|
98
|
+
// Get commit history
|
|
99
|
+
const { stdout } = await execAsync(`git log -${limit} --format="%H" -- "${filePath}"`);
|
|
100
|
+
const commits = stdout.trim().split('\n').filter(Boolean);
|
|
101
|
+
const history = [];
|
|
102
|
+
for (const commitHash of commits){
|
|
103
|
+
try {
|
|
104
|
+
// Get file content at this commit
|
|
105
|
+
const { stdout: content } = await execAsync(`git show ${commitHash}:"${filePath}"`);
|
|
106
|
+
// Get commit metadata
|
|
107
|
+
const metadata = await getCommitMetadata(filePath, commitHash);
|
|
108
|
+
// Calculate content hash
|
|
109
|
+
const contentHash = calculateContentHash(content);
|
|
110
|
+
// Extract version from content (if available in frontmatter)
|
|
111
|
+
const versionMatch = content.match(/version:\s*["']?([0-9.]+)["']?/);
|
|
112
|
+
const version = versionMatch ? versionMatch[1] : 'unknown';
|
|
113
|
+
history.push({
|
|
114
|
+
version,
|
|
115
|
+
commit: metadata,
|
|
116
|
+
contentHash,
|
|
117
|
+
timestamp: metadata.date,
|
|
118
|
+
filePath
|
|
119
|
+
});
|
|
120
|
+
} catch (error) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return history;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
throw new GitIntegrationError(`Failed to get version history for ${filePath}`, {
|
|
127
|
+
error: error instanceof Error ? error.message : String(error)
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Stage and commit a file with atomic operation
|
|
133
|
+
*
|
|
134
|
+
* @param filePath - Path to file to commit
|
|
135
|
+
* @param message - Commit message
|
|
136
|
+
* @returns Git operation result
|
|
137
|
+
*/ export async function commitFile(filePath, message) {
|
|
138
|
+
try {
|
|
139
|
+
// Stage file
|
|
140
|
+
await execAsync(`git add "${filePath}"`);
|
|
141
|
+
// Commit
|
|
142
|
+
const { stdout } = await execAsync(`git commit -m "${message}"`);
|
|
143
|
+
// Get commit hash
|
|
144
|
+
const { stdout: hashOutput } = await execAsync('git rev-parse HEAD');
|
|
145
|
+
const commitHash = hashOutput.trim();
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
commitHash
|
|
149
|
+
};
|
|
150
|
+
} catch (error) {
|
|
151
|
+
return {
|
|
152
|
+
success: false,
|
|
153
|
+
error: error instanceof Error ? error.message : String(error)
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Rollback file to previous commit
|
|
159
|
+
*
|
|
160
|
+
* @param filePath - Path to file to rollback
|
|
161
|
+
* @param commitRef - Git commit reference to rollback to (default: HEAD~1)
|
|
162
|
+
* @returns Git operation result
|
|
163
|
+
*/ export async function rollbackFile(filePath, commitRef = 'HEAD~1') {
|
|
164
|
+
try {
|
|
165
|
+
// Checkout file from previous commit
|
|
166
|
+
await execAsync(`git checkout ${commitRef} -- "${filePath}"`);
|
|
167
|
+
// Commit the rollback
|
|
168
|
+
const message = `Rollback ${filePath} to ${commitRef}`;
|
|
169
|
+
return await commitFile(filePath, message);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
return {
|
|
172
|
+
success: false,
|
|
173
|
+
error: error instanceof Error ? error.message : String(error)
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Check if file has uncommitted changes
|
|
179
|
+
*
|
|
180
|
+
* @param filePath - Path to file
|
|
181
|
+
* @returns True if file has uncommitted changes
|
|
182
|
+
*/ export async function hasUncommittedChanges(filePath) {
|
|
183
|
+
try {
|
|
184
|
+
const { stdout } = await execAsync(`git status --porcelain "${filePath}"`);
|
|
185
|
+
return stdout.trim().length > 0;
|
|
186
|
+
} catch (error) {
|
|
187
|
+
throw new GitIntegrationError(`Failed to check git status for ${filePath}`, {
|
|
188
|
+
error: error instanceof Error ? error.message : String(error)
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get current git branch
|
|
194
|
+
*
|
|
195
|
+
* @returns Current branch name
|
|
196
|
+
*/ export async function getCurrentBranch() {
|
|
197
|
+
try {
|
|
198
|
+
const { stdout } = await execAsync('git rev-parse --abbrev-ref HEAD');
|
|
199
|
+
return stdout.trim();
|
|
200
|
+
} catch (error) {
|
|
201
|
+
throw new GitIntegrationError('Failed to get current branch', {
|
|
202
|
+
error: error instanceof Error ? error.message : String(error)
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Get file diff between two commits
|
|
208
|
+
*
|
|
209
|
+
* @param filePath - Path to file
|
|
210
|
+
* @param commitRef1 - First commit reference
|
|
211
|
+
* @param commitRef2 - Second commit reference (default: HEAD)
|
|
212
|
+
* @returns Diff output
|
|
213
|
+
*/ export async function getFileDiff(filePath, commitRef1, commitRef2 = 'HEAD') {
|
|
214
|
+
try {
|
|
215
|
+
const { stdout } = await execAsync(`git diff ${commitRef1} ${commitRef2} -- "${filePath}"`);
|
|
216
|
+
return stdout;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
throw new GitIntegrationError(`Failed to get diff for ${filePath}`, {
|
|
219
|
+
error: error instanceof Error ? error.message : String(error)
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Verify content integrity using git hash
|
|
225
|
+
*
|
|
226
|
+
* @param filePath - Path to file
|
|
227
|
+
* @param expectedHash - Expected content hash
|
|
228
|
+
* @returns True if content matches expected hash
|
|
229
|
+
*/ export async function verifyContentIntegrity(filePath, expectedHash) {
|
|
230
|
+
try {
|
|
231
|
+
const actualHash = await calculateFileHash(filePath);
|
|
232
|
+
return actualHash === expectedHash;
|
|
233
|
+
} catch (error) {
|
|
234
|
+
throw new GitIntegrationError(`Failed to verify content integrity for ${filePath}`, {
|
|
235
|
+
error: error instanceof Error ? error.message : String(error)
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Get file creation date from git history
|
|
241
|
+
*
|
|
242
|
+
* @param filePath - Path to file
|
|
243
|
+
* @returns ISO date string of first commit
|
|
244
|
+
*/ export async function getFileCreationDate(filePath) {
|
|
245
|
+
try {
|
|
246
|
+
const { stdout } = await execAsync(`git log --diff-filter=A --follow --format="%ai" -- "${filePath}" | tail -1`);
|
|
247
|
+
if (!stdout.trim()) {
|
|
248
|
+
throw new GitIntegrationError('No creation date found in git history', {
|
|
249
|
+
filePath
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return new Date(stdout.trim()).toISOString();
|
|
253
|
+
} catch (error) {
|
|
254
|
+
throw new GitIntegrationError(`Failed to get creation date for ${filePath}`, {
|
|
255
|
+
error: error instanceof Error ? error.message : String(error)
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Get file last modification date from git history
|
|
261
|
+
*
|
|
262
|
+
* @param filePath - Path to file
|
|
263
|
+
* @returns ISO date string of last commit
|
|
264
|
+
*/ export async function getFileModificationDate(filePath) {
|
|
265
|
+
try {
|
|
266
|
+
const metadata = await getCommitMetadata(filePath);
|
|
267
|
+
return new Date(metadata.date).toISOString();
|
|
268
|
+
} catch (error) {
|
|
269
|
+
throw new GitIntegrationError(`Failed to get modification date for ${filePath}`, {
|
|
270
|
+
error: error instanceof Error ? error.message : String(error)
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
//# sourceMappingURL=skill-git-integration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/skill-git-integration.ts"],"sourcesContent":["/**\r\n * Skill Git Integration\r\n *\r\n * Manages version tracking, content hashing, and git metadata for skills\r\n * Provides atomic updates with rollback capabilities\r\n *\r\n * @module skill-git-integration\r\n * @version 1.0.0\r\n */\r\n\r\nimport { exec } from 'child_process';\r\nimport { promisify } from 'util';\r\nimport { createHash } from 'crypto';\r\nimport { readFile } from 'fs/promises';\r\nimport { StandardError } from './errors.js';\r\n\r\nconst execAsync = promisify(exec);\r\n\r\n/**\r\n * Git commit metadata\r\n */\r\nexport interface GitCommitMetadata {\r\n hash: string;\r\n author: string;\r\n email: string;\r\n date: string;\r\n message: string;\r\n}\r\n\r\n/**\r\n * Version history entry\r\n */\r\nexport interface VersionHistoryEntry {\r\n version: string;\r\n commit: GitCommitMetadata;\r\n contentHash: string;\r\n timestamp: string;\r\n filePath: string;\r\n}\r\n\r\n/**\r\n * Git operation result\r\n */\r\nexport interface GitOperationResult {\r\n success: boolean;\r\n commitHash?: string;\r\n error?: string;\r\n}\r\n\r\n/**\r\n * Git integration error\r\n */\r\nexport class GitIntegrationError extends StandardError {\r\n constructor(message: string, public readonly context?: Record<string, unknown>) {\r\n super('GIT_INTEGRATION_ERROR', message, context);\r\n this.name = 'GitIntegrationError';\r\n }\r\n}\r\n\r\n/**\r\n * Calculate SHA256 hash of file content\r\n *\r\n * @param content - File content string\r\n * @returns SHA256 hash in hexadecimal\r\n */\r\nexport function calculateContentHash(content: string): string {\r\n return createHash('sha256').update(content).digest('hex');\r\n}\r\n\r\n/**\r\n * Calculate SHA256 hash of file\r\n *\r\n * @param filePath - Path to file\r\n * @returns SHA256 hash in hexadecimal\r\n */\r\nexport async function calculateFileHash(filePath: string): Promise<string> {\r\n try {\r\n const content = await readFile(filePath, 'utf-8');\r\n return calculateContentHash(content);\r\n } catch (error) {\r\n throw new GitIntegrationError(\r\n `Failed to calculate file hash for ${filePath}`,\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Check if a path is in a git repository\r\n *\r\n * @param filePath - Path to check\r\n * @returns True if in git repository\r\n */\r\nexport async function isGitRepository(filePath: string): Promise<boolean> {\r\n try {\r\n await execAsync('git rev-parse --is-inside-work-tree', {\r\n cwd: filePath\r\n });\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Get git commit metadata for a file\r\n *\r\n * @param filePath - Path to file\r\n * @param commitRef - Git commit reference (default: HEAD)\r\n * @returns Commit metadata\r\n */\r\nexport async function getCommitMetadata(\r\n filePath: string,\r\n commitRef: string = 'HEAD'\r\n): Promise<GitCommitMetadata> {\r\n try {\r\n const { stdout } = await execAsync(\r\n `git log -1 --format=\"%H%n%an%n%ae%n%ai%n%s\" ${commitRef} -- \"${filePath}\"`\r\n );\r\n\r\n const [hash, author, email, date, message] = stdout.trim().split('\\n');\r\n\r\n if (!hash) {\r\n throw new GitIntegrationError('No git history found for file', { filePath });\r\n }\r\n\r\n return {\r\n hash,\r\n author,\r\n email,\r\n date,\r\n message: message || 'No commit message'\r\n };\r\n } catch (error) {\r\n throw new GitIntegrationError(\r\n `Failed to get git metadata for ${filePath}`,\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Get version history for a skill file\r\n *\r\n * @param filePath - Path to SKILL.md file\r\n * @param limit - Maximum number of entries (default: 10)\r\n * @returns Array of version history entries\r\n */\r\nexport async function getVersionHistory(\r\n filePath: string,\r\n limit: number = 10\r\n): Promise<VersionHistoryEntry[]> {\r\n try {\r\n // Get commit history\r\n const { stdout } = await execAsync(\r\n `git log -${limit} --format=\"%H\" -- \"${filePath}\"`\r\n );\r\n\r\n const commits = stdout.trim().split('\\n').filter(Boolean);\r\n const history: VersionHistoryEntry[] = [];\r\n\r\n for (const commitHash of commits) {\r\n try {\r\n // Get file content at this commit\r\n const { stdout: content } = await execAsync(\r\n `git show ${commitHash}:\"${filePath}\"`\r\n );\r\n\r\n // Get commit metadata\r\n const metadata = await getCommitMetadata(filePath, commitHash);\r\n\r\n // Calculate content hash\r\n const contentHash = calculateContentHash(content);\r\n\r\n // Extract version from content (if available in frontmatter)\r\n const versionMatch = content.match(/version:\\s*[\"']?([0-9.]+)[\"']?/);\r\n const version = versionMatch ? versionMatch[1] : 'unknown';\r\n\r\n history.push({\r\n version,\r\n commit: metadata,\r\n contentHash,\r\n timestamp: metadata.date,\r\n filePath\r\n });\r\n } catch (error) {\r\n // Skip commits where file doesn't exist\r\n continue;\r\n }\r\n }\r\n\r\n return history;\r\n } catch (error) {\r\n throw new GitIntegrationError(\r\n `Failed to get version history for ${filePath}`,\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Stage and commit a file with atomic operation\r\n *\r\n * @param filePath - Path to file to commit\r\n * @param message - Commit message\r\n * @returns Git operation result\r\n */\r\nexport async function commitFile(\r\n filePath: string,\r\n message: string\r\n): Promise<GitOperationResult> {\r\n try {\r\n // Stage file\r\n await execAsync(`git add \"${filePath}\"`);\r\n\r\n // Commit\r\n const { stdout } = await execAsync(`git commit -m \"${message}\"`);\r\n\r\n // Get commit hash\r\n const { stdout: hashOutput } = await execAsync('git rev-parse HEAD');\r\n const commitHash = hashOutput.trim();\r\n\r\n return {\r\n success: true,\r\n commitHash\r\n };\r\n } catch (error) {\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : String(error)\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Rollback file to previous commit\r\n *\r\n * @param filePath - Path to file to rollback\r\n * @param commitRef - Git commit reference to rollback to (default: HEAD~1)\r\n * @returns Git operation result\r\n */\r\nexport async function rollbackFile(\r\n filePath: string,\r\n commitRef: string = 'HEAD~1'\r\n): Promise<GitOperationResult> {\r\n try {\r\n // Checkout file from previous commit\r\n await execAsync(`git checkout ${commitRef} -- \"${filePath}\"`);\r\n\r\n // Commit the rollback\r\n const message = `Rollback ${filePath} to ${commitRef}`;\r\n return await commitFile(filePath, message);\r\n } catch (error) {\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : String(error)\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Check if file has uncommitted changes\r\n *\r\n * @param filePath - Path to file\r\n * @returns True if file has uncommitted changes\r\n */\r\nexport async function hasUncommittedChanges(filePath: string): Promise<boolean> {\r\n try {\r\n const { stdout } = await execAsync(`git status --porcelain \"${filePath}\"`);\r\n return stdout.trim().length > 0;\r\n } catch (error) {\r\n throw new GitIntegrationError(\r\n `Failed to check git status for ${filePath}`,\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Get current git branch\r\n *\r\n * @returns Current branch name\r\n */\r\nexport async function getCurrentBranch(): Promise<string> {\r\n try {\r\n const { stdout } = await execAsync('git rev-parse --abbrev-ref HEAD');\r\n return stdout.trim();\r\n } catch (error) {\r\n throw new GitIntegrationError(\r\n 'Failed to get current branch',\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Get file diff between two commits\r\n *\r\n * @param filePath - Path to file\r\n * @param commitRef1 - First commit reference\r\n * @param commitRef2 - Second commit reference (default: HEAD)\r\n * @returns Diff output\r\n */\r\nexport async function getFileDiff(\r\n filePath: string,\r\n commitRef1: string,\r\n commitRef2: string = 'HEAD'\r\n): Promise<string> {\r\n try {\r\n const { stdout } = await execAsync(\r\n `git diff ${commitRef1} ${commitRef2} -- \"${filePath}\"`\r\n );\r\n return stdout;\r\n } catch (error) {\r\n throw new GitIntegrationError(\r\n `Failed to get diff for ${filePath}`,\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Verify content integrity using git hash\r\n *\r\n * @param filePath - Path to file\r\n * @param expectedHash - Expected content hash\r\n * @returns True if content matches expected hash\r\n */\r\nexport async function verifyContentIntegrity(\r\n filePath: string,\r\n expectedHash: string\r\n): Promise<boolean> {\r\n try {\r\n const actualHash = await calculateFileHash(filePath);\r\n return actualHash === expectedHash;\r\n } catch (error) {\r\n throw new GitIntegrationError(\r\n `Failed to verify content integrity for ${filePath}`,\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Get file creation date from git history\r\n *\r\n * @param filePath - Path to file\r\n * @returns ISO date string of first commit\r\n */\r\nexport async function getFileCreationDate(filePath: string): Promise<string> {\r\n try {\r\n const { stdout } = await execAsync(\r\n `git log --diff-filter=A --follow --format=\"%ai\" -- \"${filePath}\" | tail -1`\r\n );\r\n\r\n if (!stdout.trim()) {\r\n throw new GitIntegrationError('No creation date found in git history', { filePath });\r\n }\r\n\r\n return new Date(stdout.trim()).toISOString();\r\n } catch (error) {\r\n throw new GitIntegrationError(\r\n `Failed to get creation date for ${filePath}`,\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Get file last modification date from git history\r\n *\r\n * @param filePath - Path to file\r\n * @returns ISO date string of last commit\r\n */\r\nexport async function getFileModificationDate(filePath: string): Promise<string> {\r\n try {\r\n const metadata = await getCommitMetadata(filePath);\r\n return new Date(metadata.date).toISOString();\r\n } catch (error) {\r\n throw new GitIntegrationError(\r\n `Failed to get modification date for ${filePath}`,\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n}\r\n"],"names":["exec","promisify","createHash","readFile","StandardError","execAsync","GitIntegrationError","message","context","name","calculateContentHash","content","update","digest","calculateFileHash","filePath","error","Error","String","isGitRepository","cwd","getCommitMetadata","commitRef","stdout","hash","author","email","date","trim","split","getVersionHistory","limit","commits","filter","Boolean","history","commitHash","metadata","contentHash","versionMatch","match","version","push","commit","timestamp","commitFile","hashOutput","success","rollbackFile","hasUncommittedChanges","length","getCurrentBranch","getFileDiff","commitRef1","commitRef2","verifyContentIntegrity","expectedHash","actualHash","getFileCreationDate","Date","toISOString","getFileModificationDate"],"mappings":"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"}
|