@heytherevibin/skillforge 0.2.1
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/CHANGELOG.md +16 -0
- package/CODE_OF_CONDUCT.md +34 -0
- package/CONTRIBUTING.md +38 -0
- package/LICENSE +21 -0
- package/README.md +337 -0
- package/RELEASING.md +93 -0
- package/SECURITY.md +31 -0
- package/STRATEGY.md +26 -0
- package/bin/cli.js +547 -0
- package/lib/packs.js +184 -0
- package/package.json +38 -0
- package/python/app/__init__.py +0 -0
- package/python/app/__pycache__/__init__.cpython-312.pyc +0 -0
- package/python/app/__pycache__/auth.cpython-312.pyc +0 -0
- package/python/app/__pycache__/main.cpython-312.pyc +0 -0
- package/python/app/auth.py +63 -0
- package/python/app/cli.py +78 -0
- package/python/app/db_paths.py +26 -0
- package/python/app/events_cli.py +175 -0
- package/python/app/main.py +647 -0
- package/python/app/materialize.py +138 -0
- package/python/app/mcp_server.py +610 -0
- package/python/app/route_cli.py +117 -0
- package/python/requirements-dev.txt +1 -0
- package/python/requirements.txt +7 -0
- package/python/tests/test_db_paths.py +41 -0
- package/skills/accessibility/SKILL.md +145 -0
- package/skills/agent-architecture-audit/SKILL.md +256 -0
- package/skills/agent-eval/SKILL.md +144 -0
- package/skills/agent-harness-construction/SKILL.md +72 -0
- package/skills/agent-introspection-debugging/SKILL.md +152 -0
- package/skills/agent-payment-x402/SKILL.md +224 -0
- package/skills/agent-sort/SKILL.md +214 -0
- package/skills/agentic-engineering/SKILL.md +62 -0
- package/skills/agentic-os/SKILL.md +386 -0
- package/skills/ai-first-engineering/SKILL.md +50 -0
- package/skills/ai-regression-testing/SKILL.md +384 -0
- package/skills/android-clean-architecture/SKILL.md +338 -0
- package/skills/angular-developer/SKILL.md +153 -0
- package/skills/angular-developer/references/angular-animations.md +160 -0
- package/skills/angular-developer/references/angular-aria.md +410 -0
- package/skills/angular-developer/references/cli.md +86 -0
- package/skills/angular-developer/references/component-harnesses.md +59 -0
- package/skills/angular-developer/references/component-styling.md +91 -0
- package/skills/angular-developer/references/components.md +117 -0
- package/skills/angular-developer/references/creating-services.md +97 -0
- package/skills/angular-developer/references/data-resolvers.md +69 -0
- package/skills/angular-developer/references/define-routes.md +67 -0
- package/skills/angular-developer/references/defining-providers.md +72 -0
- package/skills/angular-developer/references/di-fundamentals.md +120 -0
- package/skills/angular-developer/references/e2e-testing.md +56 -0
- package/skills/angular-developer/references/effects.md +83 -0
- package/skills/angular-developer/references/hierarchical-injectors.md +43 -0
- package/skills/angular-developer/references/host-elements.md +80 -0
- package/skills/angular-developer/references/injection-context.md +63 -0
- package/skills/angular-developer/references/inputs.md +101 -0
- package/skills/angular-developer/references/linked-signal.md +59 -0
- package/skills/angular-developer/references/loading-strategies.md +61 -0
- package/skills/angular-developer/references/mcp.md +108 -0
- package/skills/angular-developer/references/navigate-to-routes.md +69 -0
- package/skills/angular-developer/references/outputs.md +86 -0
- package/skills/angular-developer/references/reactive-forms.md +122 -0
- package/skills/angular-developer/references/rendering-strategies.md +44 -0
- package/skills/angular-developer/references/resource.md +77 -0
- package/skills/angular-developer/references/route-animations.md +56 -0
- package/skills/angular-developer/references/route-guards.md +52 -0
- package/skills/angular-developer/references/router-lifecycle.md +45 -0
- package/skills/angular-developer/references/router-testing.md +87 -0
- package/skills/angular-developer/references/show-routes-with-outlets.md +68 -0
- package/skills/angular-developer/references/signal-forms.md +795 -0
- package/skills/angular-developer/references/signals-overview.md +94 -0
- package/skills/angular-developer/references/tailwind-css.md +69 -0
- package/skills/angular-developer/references/template-driven-forms.md +114 -0
- package/skills/angular-developer/references/testing-fundamentals.md +65 -0
- package/skills/api-connector-builder/SKILL.md +120 -0
- package/skills/api-design/SKILL.md +522 -0
- package/skills/architecture-decision-records/SKILL.md +178 -0
- package/skills/article-writing/SKILL.md +78 -0
- package/skills/automation-audit-ops/SKILL.md +141 -0
- package/skills/autonomous-agent-harness/SKILL.md +272 -0
- package/skills/autonomous-loops/SKILL.md +609 -0
- package/skills/backend-patterns/SKILL.md +560 -0
- package/skills/benchmark/SKILL.md +92 -0
- package/skills/blueprint/SKILL.md +104 -0
- package/skills/browser-qa/SKILL.md +86 -0
- package/skills/bun-runtime/SKILL.md +83 -0
- package/skills/canary-watch/SKILL.md +98 -0
- package/skills/carrier-relationship-management/SKILL.md +211 -0
- package/skills/cisco-ios-patterns/SKILL.md +163 -0
- package/skills/ck/SKILL.md +147 -0
- package/skills/ck/commands/forget.mjs +44 -0
- package/skills/ck/commands/info.mjs +24 -0
- package/skills/ck/commands/init.mjs +143 -0
- package/skills/ck/commands/list.mjs +40 -0
- package/skills/ck/commands/migrate.mjs +202 -0
- package/skills/ck/commands/resume.mjs +36 -0
- package/skills/ck/commands/save.mjs +210 -0
- package/skills/ck/commands/shared.mjs +387 -0
- package/skills/ck/hooks/session-start.mjs +224 -0
- package/skills/claude-devfleet/SKILL.md +103 -0
- package/skills/click-path-audit/SKILL.md +244 -0
- package/skills/clickhouse-io/SKILL.md +438 -0
- package/skills/code-tour/SKILL.md +235 -0
- package/skills/codebase-onboarding/SKILL.md +232 -0
- package/skills/coding-standards/SKILL.md +548 -0
- package/skills/compose-multiplatform-patterns/SKILL.md +298 -0
- package/skills/connections-optimizer/SKILL.md +188 -0
- package/skills/content-engine/SKILL.md +126 -0
- package/skills/content-hash-cache-pattern/SKILL.md +160 -0
- package/skills/context-budget/SKILL.md +134 -0
- package/skills/continuous-agent-loop/SKILL.md +44 -0
- package/skills/continuous-learning/SKILL.md +129 -0
- package/skills/continuous-learning/config.json +18 -0
- package/skills/continuous-learning/evaluate-session.sh +69 -0
- package/skills/continuous-learning-v2/SKILL.md +358 -0
- package/skills/continuous-learning-v2/agents/observer-loop.sh +322 -0
- package/skills/continuous-learning-v2/agents/observer.md +198 -0
- package/skills/continuous-learning-v2/agents/session-guardian.sh +150 -0
- package/skills/continuous-learning-v2/agents/start-observer.sh +248 -0
- package/skills/continuous-learning-v2/config.json +8 -0
- package/skills/continuous-learning-v2/hooks/observe.sh +476 -0
- package/skills/continuous-learning-v2/scripts/detect-project.sh +288 -0
- package/skills/continuous-learning-v2/scripts/instinct-cli.py +1519 -0
- package/skills/continuous-learning-v2/scripts/lib/homunculus-dir.sh +31 -0
- package/skills/continuous-learning-v2/scripts/migrate-homunculus.sh +62 -0
- package/skills/continuous-learning-v2/scripts/test_parse_instinct.py +1018 -0
- package/skills/cost-aware-llm-pipeline/SKILL.md +182 -0
- package/skills/cost-tracking/SKILL.md +147 -0
- package/skills/council/SKILL.md +202 -0
- package/skills/cpp-coding-standards/SKILL.md +722 -0
- package/skills/cpp-testing/SKILL.md +323 -0
- package/skills/crosspost/SKILL.md +110 -0
- package/skills/csharp-testing/SKILL.md +320 -0
- package/skills/customer-billing-ops/SKILL.md +139 -0
- package/skills/customs-trade-compliance/SKILL.md +262 -0
- package/skills/dart-flutter-patterns/SKILL.md +562 -0
- package/skills/dashboard-builder/SKILL.md +108 -0
- package/skills/data-scraper-agent/SKILL.md +764 -0
- package/skills/database-migrations/SKILL.md +428 -0
- package/skills/deep-research/SKILL.md +158 -0
- package/skills/defi-amm-security/SKILL.md +166 -0
- package/skills/deployment-patterns/SKILL.md +426 -0
- package/skills/design-system/SKILL.md +81 -0
- package/skills/django-celery/SKILL.md +456 -0
- package/skills/django-patterns/SKILL.md +733 -0
- package/skills/django-security/SKILL.md +592 -0
- package/skills/django-tdd/SKILL.md +728 -0
- package/skills/django-verification/SKILL.md +468 -0
- package/skills/dmux-workflows/SKILL.md +190 -0
- package/skills/docker-patterns/SKILL.md +363 -0
- package/skills/documentation-lookup/SKILL.md +89 -0
- package/skills/dotnet-patterns/SKILL.md +320 -0
- package/skills/e2e-testing/SKILL.md +325 -0
- package/skills/email-ops/SKILL.md +120 -0
- package/skills/energy-procurement/SKILL.md +227 -0
- package/skills/enterprise-agent-ops/SKILL.md +49 -0
- package/skills/error-handling/SKILL.md +375 -0
- package/skills/eval-harness/SKILL.md +269 -0
- package/skills/evm-token-decimals/SKILL.md +130 -0
- package/skills/exa-search/SKILL.md +106 -0
- package/skills/fal-ai-media/SKILL.md +287 -0
- package/skills/fastapi-patterns/SKILL.md +327 -0
- package/skills/finance-billing-ops/SKILL.md +126 -0
- package/skills/flox-environments/SKILL.md +496 -0
- package/skills/flutter-dart-code-review/SKILL.md +434 -0
- package/skills/foundation-models-on-device/SKILL.md +243 -0
- package/skills/frontend-design-direction/SKILL.md +92 -0
- package/skills/frontend-patterns/SKILL.md +641 -0
- package/skills/frontend-slides/SKILL.md +183 -0
- package/skills/frontend-slides/STYLE_PRESETS.md +330 -0
- package/skills/frontend-slides/animation-patterns.md +122 -0
- package/skills/frontend-slides/html-template.md +419 -0
- package/skills/frontend-slides/scripts/export-pdf.sh +418 -0
- package/skills/frontend-slides/scripts/extract-pptx.py +96 -0
- package/skills/frontend-slides/viewport-base.css +153 -0
- package/skills/fsharp-testing/SKILL.md +279 -0
- package/skills/gan-style-harness/SKILL.md +278 -0
- package/skills/gateguard/SKILL.md +125 -0
- package/skills/git-workflow/SKILL.md +714 -0
- package/skills/github-ops/SKILL.md +143 -0
- package/skills/golang-patterns/SKILL.md +673 -0
- package/skills/golang-testing/SKILL.md +719 -0
- package/skills/google-workspace-ops/SKILL.md +94 -0
- package/skills/healthcare-cdss-patterns/SKILL.md +245 -0
- package/skills/healthcare-emr-patterns/SKILL.md +159 -0
- package/skills/healthcare-eval-harness/SKILL.md +207 -0
- package/skills/healthcare-phi-compliance/SKILL.md +145 -0
- package/skills/hermes-imports/SKILL.md +87 -0
- package/skills/hexagonal-architecture/SKILL.md +275 -0
- package/skills/hipaa-compliance/SKILL.md +78 -0
- package/skills/homelab-network-readiness/SKILL.md +169 -0
- package/skills/homelab-network-setup/SKILL.md +129 -0
- package/skills/homelab-pihole-dns/SKILL.md +274 -0
- package/skills/homelab-vlan-segmentation/SKILL.md +311 -0
- package/skills/homelab-wireguard-vpn/SKILL.md +305 -0
- package/skills/hookify-rules/SKILL.md +128 -0
- package/skills/inventory-demand-planning/SKILL.md +246 -0
- package/skills/investor-materials/SKILL.md +95 -0
- package/skills/investor-outreach/SKILL.md +90 -0
- package/skills/ios-icon-gen/SKILL.md +157 -0
- package/skills/ios-icon-gen/scripts/generate_icons.swift +258 -0
- package/skills/ios-icon-gen/scripts/iconify_gen.sh +235 -0
- package/skills/iterative-retrieval/SKILL.md +209 -0
- package/skills/java-coding-standards/SKILL.md +382 -0
- package/skills/jira-integration/SKILL.md +292 -0
- package/skills/jpa-patterns/SKILL.md +150 -0
- package/skills/knowledge-ops/SKILL.md +153 -0
- package/skills/kotlin-coroutines-flows/SKILL.md +283 -0
- package/skills/kotlin-exposed-patterns/SKILL.md +718 -0
- package/skills/kotlin-ktor-patterns/SKILL.md +688 -0
- package/skills/kotlin-patterns/SKILL.md +710 -0
- package/skills/kotlin-testing/SKILL.md +823 -0
- package/skills/laravel-patterns/SKILL.md +414 -0
- package/skills/laravel-plugin-discovery/SKILL.md +228 -0
- package/skills/laravel-security/SKILL.md +284 -0
- package/skills/laravel-tdd/SKILL.md +282 -0
- package/skills/laravel-verification/SKILL.md +178 -0
- package/skills/lead-intelligence/SKILL.md +320 -0
- package/skills/lead-intelligence/agents/enrichment-agent.md +85 -0
- package/skills/lead-intelligence/agents/mutual-mapper.md +75 -0
- package/skills/lead-intelligence/agents/outreach-drafter.md +98 -0
- package/skills/lead-intelligence/agents/signal-scorer.md +60 -0
- package/skills/liquid-glass-design/SKILL.md +279 -0
- package/skills/llm-trading-agent-security/SKILL.md +146 -0
- package/skills/logistics-exception-management/SKILL.md +221 -0
- package/skills/make-interfaces-feel-better/SKILL.md +151 -0
- package/skills/manim-video/SKILL.md +88 -0
- package/skills/manim-video/assets/network_graph_scene.py +52 -0
- package/skills/market-research/SKILL.md +74 -0
- package/skills/mcp-server-patterns/SKILL.md +68 -0
- package/skills/messages-ops/SKILL.md +103 -0
- package/skills/mle-workflow/SKILL.md +345 -0
- package/skills/motion-advanced/SKILL.md +596 -0
- package/skills/motion-foundations/SKILL.md +299 -0
- package/skills/motion-patterns/SKILL.md +435 -0
- package/skills/motion-ui/SKILL.md +574 -0
- package/skills/mysql-patterns/SKILL.md +411 -0
- package/skills/nanoclaw-repl/SKILL.md +32 -0
- package/skills/nestjs-patterns/SKILL.md +229 -0
- package/skills/netmiko-ssh-automation/SKILL.md +173 -0
- package/skills/network-bgp-diagnostics/SKILL.md +167 -0
- package/skills/network-config-validation/SKILL.md +210 -0
- package/skills/network-interface-health/SKILL.md +152 -0
- package/skills/nextjs-turbopack/SKILL.md +43 -0
- package/skills/nodejs-keccak256/SKILL.md +102 -0
- package/skills/nutrient-document-processing/SKILL.md +166 -0
- package/skills/nuxt4-patterns/SKILL.md +99 -0
- package/skills/openclaw-persona-forge/SKILL.md +288 -0
- package/skills/openclaw-persona-forge/gacha.py +224 -0
- package/skills/openclaw-persona-forge/gacha.sh +5 -0
- package/skills/openclaw-persona-forge/references/avatar-style.md +124 -0
- package/skills/openclaw-persona-forge/references/boundary-rules.md +53 -0
- package/skills/openclaw-persona-forge/references/error-handling.md +53 -0
- package/skills/openclaw-persona-forge/references/identity-tension.md +48 -0
- package/skills/openclaw-persona-forge/references/naming-system.md +39 -0
- package/skills/openclaw-persona-forge/references/output-template.md +166 -0
- package/skills/opensource-pipeline/SKILL.md +254 -0
- package/skills/perl-patterns/SKILL.md +503 -0
- package/skills/perl-security/SKILL.md +502 -0
- package/skills/perl-testing/SKILL.md +474 -0
- package/skills/plan-orchestrate/SKILL.md +253 -0
- package/skills/plankton-code-quality/SKILL.md +236 -0
- package/skills/postgres-patterns/SKILL.md +146 -0
- package/skills/product-capability/SKILL.md +140 -0
- package/skills/product-lens/SKILL.md +91 -0
- package/skills/production-audit/SKILL.md +206 -0
- package/skills/production-scheduling/SKILL.md +237 -0
- package/skills/project-flow-ops/SKILL.md +110 -0
- package/skills/prompt-optimizer/SKILL.md +398 -0
- package/skills/python-patterns/SKILL.md +749 -0
- package/skills/python-testing/SKILL.md +815 -0
- package/skills/pytorch-patterns/SKILL.md +395 -0
- package/skills/quality-nonconformance/SKILL.md +259 -0
- package/skills/quarkus-patterns/SKILL.md +721 -0
- package/skills/quarkus-security/SKILL.md +466 -0
- package/skills/quarkus-tdd/SKILL.md +810 -0
- package/skills/quarkus-verification/SKILL.md +478 -0
- package/skills/ralphinho-rfc-pipeline/SKILL.md +66 -0
- package/skills/redis-patterns/SKILL.md +402 -0
- package/skills/regex-vs-llm-structured-text/SKILL.md +219 -0
- package/skills/remotion-video-creation/SKILL.md +43 -0
- package/skills/remotion-video-creation/rules/3d.md +86 -0
- package/skills/remotion-video-creation/rules/animations.md +29 -0
- package/skills/remotion-video-creation/rules/assets/charts-bar-chart.tsx +173 -0
- package/skills/remotion-video-creation/rules/assets/text-animations-typewriter.tsx +100 -0
- package/skills/remotion-video-creation/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/skills/remotion-video-creation/rules/assets.md +78 -0
- package/skills/remotion-video-creation/rules/audio.md +172 -0
- package/skills/remotion-video-creation/rules/calculate-metadata.md +104 -0
- package/skills/remotion-video-creation/rules/can-decode.md +75 -0
- package/skills/remotion-video-creation/rules/charts.md +58 -0
- package/skills/remotion-video-creation/rules/compositions.md +146 -0
- package/skills/remotion-video-creation/rules/display-captions.md +126 -0
- package/skills/remotion-video-creation/rules/extract-frames.md +229 -0
- package/skills/remotion-video-creation/rules/fonts.md +152 -0
- package/skills/remotion-video-creation/rules/get-audio-duration.md +58 -0
- package/skills/remotion-video-creation/rules/get-video-dimensions.md +68 -0
- package/skills/remotion-video-creation/rules/get-video-duration.md +58 -0
- package/skills/remotion-video-creation/rules/gifs.md +138 -0
- package/skills/remotion-video-creation/rules/images.md +130 -0
- package/skills/remotion-video-creation/rules/import-srt-captions.md +67 -0
- package/skills/remotion-video-creation/rules/lottie.md +67 -0
- package/skills/remotion-video-creation/rules/measuring-dom-nodes.md +34 -0
- package/skills/remotion-video-creation/rules/measuring-text.md +143 -0
- package/skills/remotion-video-creation/rules/sequencing.md +106 -0
- package/skills/remotion-video-creation/rules/tailwind.md +11 -0
- package/skills/remotion-video-creation/rules/text-animations.md +20 -0
- package/skills/remotion-video-creation/rules/timing.md +179 -0
- package/skills/remotion-video-creation/rules/transcribe-captions.md +19 -0
- package/skills/remotion-video-creation/rules/transitions.md +122 -0
- package/skills/remotion-video-creation/rules/trimming.md +52 -0
- package/skills/remotion-video-creation/rules/videos.md +171 -0
- package/skills/repo-scan/SKILL.md +78 -0
- package/skills/research-ops/SKILL.md +111 -0
- package/skills/returns-reverse-logistics/SKILL.md +239 -0
- package/skills/rules-distill/SKILL.md +263 -0
- package/skills/rules-distill/scripts/scan-rules.sh +58 -0
- package/skills/rules-distill/scripts/scan-skills.sh +129 -0
- package/skills/rust-patterns/SKILL.md +498 -0
- package/skills/rust-testing/SKILL.md +499 -0
- package/skills/safety-guard/SKILL.md +74 -0
- package/skills/santa-method/SKILL.md +306 -0
- package/skills/scientific-db-pubmed-database/SKILL.md +175 -0
- package/skills/scientific-db-uspto-database/SKILL.md +177 -0
- package/skills/scientific-pkg-gget/SKILL.md +166 -0
- package/skills/scientific-thinking-literature-review/SKILL.md +192 -0
- package/skills/scientific-thinking-scholar-evaluation/SKILL.md +160 -0
- package/skills/search-first/SKILL.md +181 -0
- package/skills/security-bounty-hunter/SKILL.md +99 -0
- package/skills/security-review/SKILL.md +502 -0
- package/skills/security-review/cloud-infrastructure-security.md +361 -0
- package/skills/seo/SKILL.md +153 -0
- package/skills/skill-comply/SKILL.md +57 -0
- package/skills/skill-comply/fixtures/compliant_trace.jsonl +5 -0
- package/skills/skill-comply/fixtures/noncompliant_trace.jsonl +3 -0
- package/skills/skill-comply/fixtures/tdd_spec.yaml +44 -0
- package/skills/skill-comply/prompts/classifier.md +24 -0
- package/skills/skill-comply/prompts/scenario_generator.md +62 -0
- package/skills/skill-comply/prompts/spec_generator.md +42 -0
- package/skills/skill-comply/pyproject.toml +15 -0
- package/skills/skill-comply/scripts/__init__.py +0 -0
- package/skills/skill-comply/scripts/classifier.py +85 -0
- package/skills/skill-comply/scripts/grader.py +124 -0
- package/skills/skill-comply/scripts/parser.py +107 -0
- package/skills/skill-comply/scripts/report.py +170 -0
- package/skills/skill-comply/scripts/run.py +127 -0
- package/skills/skill-comply/scripts/runner.py +186 -0
- package/skills/skill-comply/scripts/scenario_generator.py +70 -0
- package/skills/skill-comply/scripts/spec_generator.py +72 -0
- package/skills/skill-comply/scripts/utils.py +13 -0
- package/skills/skill-comply/tests/test_grader.py +197 -0
- package/skills/skill-comply/tests/test_parser.py +90 -0
- package/skills/skill-comply/tests/test_runner.py +172 -0
- package/skills/skill-scout/SKILL.md +139 -0
- package/skills/skill-stocktake/SKILL.md +193 -0
- package/skills/skill-stocktake/scripts/quick-diff.sh +87 -0
- package/skills/skill-stocktake/scripts/save-results.sh +56 -0
- package/skills/skill-stocktake/scripts/scan.sh +170 -0
- package/skills/social-graph-ranker/SKILL.md +153 -0
- package/skills/springboot-patterns/SKILL.md +313 -0
- package/skills/springboot-security/SKILL.md +271 -0
- package/skills/springboot-tdd/SKILL.md +157 -0
- package/skills/springboot-verification/SKILL.md +230 -0
- package/skills/strategic-compact/SKILL.md +129 -0
- package/skills/strategic-compact/suggest-compact.sh +54 -0
- package/skills/swift-actor-persistence/SKILL.md +142 -0
- package/skills/swift-concurrency-6-2/SKILL.md +216 -0
- package/skills/swift-protocol-di-testing/SKILL.md +189 -0
- package/skills/swiftui-patterns/SKILL.md +259 -0
- package/skills/tdd-workflow/SKILL.md +462 -0
- package/skills/team-builder/SKILL.md +166 -0
- package/skills/terminal-ops/SKILL.md +108 -0
- package/skills/tinystruct-patterns/SKILL.md +130 -0
- package/skills/tinystruct-patterns/references/architecture.md +77 -0
- package/skills/tinystruct-patterns/references/data-handling.md +35 -0
- package/skills/tinystruct-patterns/references/routing.md +57 -0
- package/skills/tinystruct-patterns/references/system-usage.md +74 -0
- package/skills/tinystruct-patterns/references/testing.md +59 -0
- package/skills/token-budget-advisor/SKILL.md +133 -0
- package/skills/ui-demo/SKILL.md +464 -0
- package/skills/ui-to-vue/SKILL.md +134 -0
- package/skills/unified-notifications-ops/SKILL.md +186 -0
- package/skills/verification-loop/SKILL.md +125 -0
- package/skills/video-editing/SKILL.md +309 -0
- package/skills/videodb/SKILL.md +373 -0
- package/skills/videodb/reference/api-reference.md +550 -0
- package/skills/videodb/reference/capture-reference.md +407 -0
- package/skills/videodb/reference/capture.md +101 -0
- package/skills/videodb/reference/editor.md +443 -0
- package/skills/videodb/reference/generative.md +331 -0
- package/skills/videodb/reference/rtstream-reference.md +564 -0
- package/skills/videodb/reference/rtstream.md +65 -0
- package/skills/videodb/reference/search.md +230 -0
- package/skills/videodb/reference/streaming.md +406 -0
- package/skills/videodb/reference/use-cases.md +118 -0
- package/skills/videodb/scripts/ws_listener.py +282 -0
- package/skills/visa-doc-translate/README.md +86 -0
- package/skills/visa-doc-translate/SKILL.md +117 -0
- package/skills/vite-patterns/SKILL.md +448 -0
- package/skills/windows-desktop-e2e/SKILL.md +787 -0
- package/skills/workspace-surface-audit/SKILL.md +124 -0
- package/skills/x-api/SKILL.md +233 -0
package/lib/packs.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill pack installer.
|
|
3
|
+
*
|
|
4
|
+
* A "pack" is any git repo containing a skillforge.json manifest at the root:
|
|
5
|
+
* {
|
|
6
|
+
* "name": "my-pack",
|
|
7
|
+
* "version": "1.0.0",
|
|
8
|
+
* "skills": ["skill-one", "skill-two"] // folder names relative to repo root
|
|
9
|
+
* }
|
|
10
|
+
*
|
|
11
|
+
* Packs are git-cloned to ~/.skillforge/packs/<source-hash>/ and their
|
|
12
|
+
* declared skill folders are symlinked into ~/.skillforge/skills/.
|
|
13
|
+
* This means uninstall just removes the symlinks + the cached clone,
|
|
14
|
+
* and updates are a single `git pull`.
|
|
15
|
+
*/
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const crypto = require('crypto');
|
|
19
|
+
const { spawnSync } = require('child_process');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
|
|
22
|
+
const CONFIG_DIR = path.join(os.homedir(), '.skillforge');
|
|
23
|
+
const PACKS_DIR = path.join(CONFIG_DIR, 'packs');
|
|
24
|
+
const USER_SKILLS_DIR = path.join(CONFIG_DIR, 'skills');
|
|
25
|
+
const PACK_REGISTRY = path.join(CONFIG_DIR, 'packs.json');
|
|
26
|
+
|
|
27
|
+
function ensureDirs() {
|
|
28
|
+
fs.mkdirSync(PACKS_DIR, { recursive: true });
|
|
29
|
+
fs.mkdirSync(USER_SKILLS_DIR, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function loadRegistry() {
|
|
33
|
+
if (!fs.existsSync(PACK_REGISTRY)) return {};
|
|
34
|
+
try { return JSON.parse(fs.readFileSync(PACK_REGISTRY, 'utf8')); }
|
|
35
|
+
catch { return {}; }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function saveRegistry(reg) {
|
|
39
|
+
fs.writeFileSync(PACK_REGISTRY, JSON.stringify(reg, null, 2));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function normalizeSource(source) {
|
|
43
|
+
// Accept "user/repo", "https://github.com/user/repo", "git@github.com:user/repo.git", or a local path
|
|
44
|
+
if (source.startsWith('http://') || source.startsWith('https://') || source.startsWith('git@')) {
|
|
45
|
+
return { type: 'git', url: source };
|
|
46
|
+
}
|
|
47
|
+
if (source.startsWith('./') || source.startsWith('/') || source.startsWith('~')) {
|
|
48
|
+
return { type: 'local', path: source.replace(/^~/, os.homedir()) };
|
|
49
|
+
}
|
|
50
|
+
// user/repo shorthand → github
|
|
51
|
+
if (/^[\w.-]+\/[\w.-]+$/.test(source)) {
|
|
52
|
+
return { type: 'git', url: `https://github.com/${source}` };
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`Cannot interpret source: ${source}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function sourceKey(src) {
|
|
58
|
+
return crypto.createHash('sha1').update(JSON.stringify(src)).digest('hex').slice(0, 10);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function readManifest(packDir) {
|
|
62
|
+
const mf = path.join(packDir, 'skillforge.json');
|
|
63
|
+
if (!fs.existsSync(mf)) {
|
|
64
|
+
throw new Error(`No skillforge.json found at ${packDir}. A skill pack must have a manifest at its root.`);
|
|
65
|
+
}
|
|
66
|
+
const m = JSON.parse(fs.readFileSync(mf, 'utf8'));
|
|
67
|
+
if (!m.name || !Array.isArray(m.skills)) {
|
|
68
|
+
throw new Error('skillforge.json must have "name" (string) and "skills" (array of folder names)');
|
|
69
|
+
}
|
|
70
|
+
return m;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function validateSkills(packDir, manifest) {
|
|
74
|
+
const errors = [];
|
|
75
|
+
for (const s of manifest.skills) {
|
|
76
|
+
const skillPath = path.join(packDir, s);
|
|
77
|
+
if (!fs.existsSync(skillPath)) errors.push(`Listed skill "${s}" not found in pack`);
|
|
78
|
+
else if (!fs.existsSync(path.join(skillPath, 'SKILL.md'))) errors.push(`"${s}" has no SKILL.md`);
|
|
79
|
+
}
|
|
80
|
+
if (errors.length) throw new Error('Pack validation failed:\n - ' + errors.join('\n - '));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function linkSkills(packDir, manifest, packKey) {
|
|
84
|
+
const linked = [];
|
|
85
|
+
for (const s of manifest.skills) {
|
|
86
|
+
const target = path.join(USER_SKILLS_DIR, s);
|
|
87
|
+
if (fs.existsSync(target)) {
|
|
88
|
+
const stat = fs.lstatSync(target);
|
|
89
|
+
if (stat.isSymbolicLink()) {
|
|
90
|
+
// Existing pack symlink — overwrite (re-install case)
|
|
91
|
+
fs.unlinkSync(target);
|
|
92
|
+
} else {
|
|
93
|
+
throw new Error(`A skill folder named "${s}" already exists in user skills (not from a pack). Remove it first or rename your pack's skill.`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
fs.symlinkSync(path.join(packDir, s), target, 'dir');
|
|
97
|
+
linked.push(s);
|
|
98
|
+
}
|
|
99
|
+
return linked;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function unlinkSkills(skills) {
|
|
103
|
+
const removed = [];
|
|
104
|
+
for (const s of skills) {
|
|
105
|
+
const target = path.join(USER_SKILLS_DIR, s);
|
|
106
|
+
if (fs.existsSync(target)) {
|
|
107
|
+
const stat = fs.lstatSync(target);
|
|
108
|
+
if (stat.isSymbolicLink()) {
|
|
109
|
+
fs.unlinkSync(target);
|
|
110
|
+
removed.push(s);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return removed;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function installPack(source) {
|
|
118
|
+
ensureDirs();
|
|
119
|
+
const src = normalizeSource(source);
|
|
120
|
+
const key = sourceKey(src);
|
|
121
|
+
const packDir = path.join(PACKS_DIR, key);
|
|
122
|
+
|
|
123
|
+
if (src.type === 'git') {
|
|
124
|
+
if (fs.existsSync(packDir)) {
|
|
125
|
+
// Already cloned — update instead
|
|
126
|
+
const r = spawnSync('git', ['-C', packDir, 'pull', '--ff-only', '--quiet'], { stdio: 'inherit' });
|
|
127
|
+
if (r.status !== 0) throw new Error('git pull failed');
|
|
128
|
+
} else {
|
|
129
|
+
const r = spawnSync('git', ['clone', '--depth', '1', '--quiet', src.url, packDir], { stdio: 'inherit' });
|
|
130
|
+
if (r.status !== 0) throw new Error(`git clone failed for ${src.url}`);
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
// Local path — symlink the pack dir itself
|
|
134
|
+
if (fs.existsSync(packDir)) fs.rmSync(packDir, { recursive: true, force: true });
|
|
135
|
+
fs.symlinkSync(src.path, packDir, 'dir');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const manifest = readManifest(packDir);
|
|
139
|
+
validateSkills(packDir, manifest);
|
|
140
|
+
const linked = linkSkills(packDir, manifest, key);
|
|
141
|
+
|
|
142
|
+
const reg = loadRegistry();
|
|
143
|
+
reg[manifest.name] = {
|
|
144
|
+
source,
|
|
145
|
+
key,
|
|
146
|
+
version: manifest.version || 'unknown',
|
|
147
|
+
skills: linked,
|
|
148
|
+
installed_at: new Date().toISOString(),
|
|
149
|
+
};
|
|
150
|
+
saveRegistry(reg);
|
|
151
|
+
return { name: manifest.name, skills: linked, version: manifest.version };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function uninstallPack(name) {
|
|
155
|
+
ensureDirs();
|
|
156
|
+
const reg = loadRegistry();
|
|
157
|
+
const entry = reg[name];
|
|
158
|
+
if (!entry) throw new Error(`No installed pack named "${name}"`);
|
|
159
|
+
const removed = unlinkSkills(entry.skills);
|
|
160
|
+
const packDir = path.join(PACKS_DIR, entry.key);
|
|
161
|
+
if (fs.existsSync(packDir)) {
|
|
162
|
+
const stat = fs.lstatSync(packDir);
|
|
163
|
+
if (stat.isSymbolicLink()) fs.unlinkSync(packDir);
|
|
164
|
+
else fs.rmSync(packDir, { recursive: true, force: true });
|
|
165
|
+
}
|
|
166
|
+
delete reg[name];
|
|
167
|
+
saveRegistry(reg);
|
|
168
|
+
return { name, removed };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function listPacks() {
|
|
172
|
+
ensureDirs();
|
|
173
|
+
const reg = loadRegistry();
|
|
174
|
+
return Object.entries(reg).map(([name, e]) => ({ name, ...e }));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function updatePack(name) {
|
|
178
|
+
const reg = loadRegistry();
|
|
179
|
+
const entry = reg[name];
|
|
180
|
+
if (!entry) throw new Error(`No installed pack named "${name}"`);
|
|
181
|
+
return installPack(entry.source); // re-runs git pull + relinks
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = { installPack, uninstallPack, listPacks, updatePack };
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@heytherevibin/skillforge",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Skill orchestration for Claude: hybrid embedding and router-based routing, MCP and HTTP servers, per-user learning, and a large bundled SKILL.md catalog.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude",
|
|
7
|
+
"skills",
|
|
8
|
+
"agent",
|
|
9
|
+
"orchestrator",
|
|
10
|
+
"anthropic",
|
|
11
|
+
"llm",
|
|
12
|
+
"routing"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"bin": {
|
|
16
|
+
"skillforge": "bin/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"bin/",
|
|
20
|
+
"lib/",
|
|
21
|
+
"python/",
|
|
22
|
+
"skills/",
|
|
23
|
+
"README.md",
|
|
24
|
+
"CHANGELOG.md",
|
|
25
|
+
"STRATEGY.md",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"RELEASING.md",
|
|
28
|
+
"SECURITY.md",
|
|
29
|
+
"CONTRIBUTING.md",
|
|
30
|
+
"CODE_OF_CONDUCT.md"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18.0.0"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"test": "node bin/cli.js --help"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bearer-token auth and per-user namespacing.
|
|
3
|
+
|
|
4
|
+
Single-user mode (default): no token required, all state goes to user_id=''.
|
|
5
|
+
Multi-user mode: set SKILLFORGE_AUTH_TOKENS env var to a JSON map of
|
|
6
|
+
{"token-value": "user-id"}. Requests must send Authorization: Bearer <token>.
|
|
7
|
+
The resolved user_id is then used to scope sessions, weights, and events.
|
|
8
|
+
|
|
9
|
+
This keeps the architecture single-process (one SQLite, one router instance)
|
|
10
|
+
while letting each user have isolated learning state.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
from fastapi import HTTPException, Request
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ---- Token registry ----
|
|
22
|
+
|
|
23
|
+
def _load_tokens() -> dict[str, str]:
|
|
24
|
+
"""Read SKILLFORGE_AUTH_TOKENS env (JSON: {token: user_id})."""
|
|
25
|
+
raw = os.getenv("SKILLFORGE_AUTH_TOKENS", "").strip()
|
|
26
|
+
if not raw:
|
|
27
|
+
return {}
|
|
28
|
+
try:
|
|
29
|
+
m = json.loads(raw)
|
|
30
|
+
if not isinstance(m, dict):
|
|
31
|
+
print("[skillforge] SKILLFORGE_AUTH_TOKENS must be a JSON object")
|
|
32
|
+
return {}
|
|
33
|
+
return {str(k): str(v) for k, v in m.items()}
|
|
34
|
+
except json.JSONDecodeError:
|
|
35
|
+
print("[skillforge] SKILLFORGE_AUTH_TOKENS is not valid JSON, ignoring")
|
|
36
|
+
return {}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
_TOKENS = _load_tokens()
|
|
40
|
+
_AUTH_REQUIRED = bool(_TOKENS)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def auth_enabled() -> bool:
|
|
44
|
+
return _AUTH_REQUIRED
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def resolve_user(request: Request) -> str:
|
|
48
|
+
"""Get user_id from a request.
|
|
49
|
+
|
|
50
|
+
- If auth is not configured (single-user mode): returns ''.
|
|
51
|
+
- If auth is configured: extracts bearer token, returns mapped user_id,
|
|
52
|
+
or raises 401.
|
|
53
|
+
"""
|
|
54
|
+
if not _AUTH_REQUIRED:
|
|
55
|
+
return ""
|
|
56
|
+
header = request.headers.get("authorization", "")
|
|
57
|
+
if not header.lower().startswith("bearer "):
|
|
58
|
+
raise HTTPException(status_code=401, detail="Missing bearer token")
|
|
59
|
+
token = header[7:].strip()
|
|
60
|
+
user_id = _TOKENS.get(token)
|
|
61
|
+
if not user_id:
|
|
62
|
+
raise HTTPException(status_code=401, detail="Invalid token")
|
|
63
|
+
return user_id
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Dev harness: terminal client for POST /chat (requires `skillforge start` + API key)."""
|
|
2
|
+
import argparse
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main():
|
|
11
|
+
ap = argparse.ArgumentParser()
|
|
12
|
+
ap.add_argument("--url", default="http://localhost:8000")
|
|
13
|
+
args = ap.parse_args()
|
|
14
|
+
|
|
15
|
+
session_id = str(uuid.uuid4())
|
|
16
|
+
conversation = []
|
|
17
|
+
print(f"\nskillforge chat — session {session_id[:8]}")
|
|
18
|
+
print("Commands: 'exit' to quit, 'reset' for new session\n")
|
|
19
|
+
|
|
20
|
+
while True:
|
|
21
|
+
try:
|
|
22
|
+
prompt = input("you ▸ ").strip()
|
|
23
|
+
except (EOFError, KeyboardInterrupt):
|
|
24
|
+
print()
|
|
25
|
+
break
|
|
26
|
+
if not prompt:
|
|
27
|
+
continue
|
|
28
|
+
if prompt == "exit":
|
|
29
|
+
break
|
|
30
|
+
if prompt == "reset":
|
|
31
|
+
session_id = str(uuid.uuid4())
|
|
32
|
+
conversation = []
|
|
33
|
+
print(f"[new session: {session_id[:8]}]\n")
|
|
34
|
+
continue
|
|
35
|
+
|
|
36
|
+
full = []
|
|
37
|
+
picked = []
|
|
38
|
+
try:
|
|
39
|
+
with httpx.stream(
|
|
40
|
+
"POST",
|
|
41
|
+
f"{args.url}/chat",
|
|
42
|
+
json={"prompt": prompt, "session_id": session_id, "conversation": conversation},
|
|
43
|
+
timeout=120.0,
|
|
44
|
+
) as r:
|
|
45
|
+
if r.status_code != 200:
|
|
46
|
+
print(f"[error {r.status_code}] {r.read().decode()}")
|
|
47
|
+
continue
|
|
48
|
+
print("claude ▸ ", end="", flush=True)
|
|
49
|
+
for line in r.iter_lines():
|
|
50
|
+
if not line.startswith("data: "):
|
|
51
|
+
continue
|
|
52
|
+
try:
|
|
53
|
+
data = json.loads(line[6:])
|
|
54
|
+
except json.JSONDecodeError:
|
|
55
|
+
continue
|
|
56
|
+
if "delta" in data:
|
|
57
|
+
sys.stdout.write(data["delta"])
|
|
58
|
+
sys.stdout.flush()
|
|
59
|
+
full.append(data["delta"])
|
|
60
|
+
elif "done" in data:
|
|
61
|
+
picked = data.get("picked", [])
|
|
62
|
+
elif "error" in data:
|
|
63
|
+
print(f"\n[stream error] {data['error']}")
|
|
64
|
+
except httpx.HTTPError as e:
|
|
65
|
+
print(f"\n[connection error] {e}")
|
|
66
|
+
print("Is the server running? Try: skillforge start")
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
print()
|
|
70
|
+
if picked:
|
|
71
|
+
print(f" \033[2m↳ skills used: {', '.join(picked)}\033[0m")
|
|
72
|
+
print()
|
|
73
|
+
conversation.append({"role": "user", "content": prompt})
|
|
74
|
+
conversation.append({"role": "assistant", "content": "".join(full)})
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
main()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Orchestrator SQLite path resolution (stdlib only — safe for lightweight tests)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def global_db_path() -> Path:
|
|
10
|
+
return Path(
|
|
11
|
+
os.getenv("SKILLFORGE_DB_PATH", str(Path.home() / ".skillforge" / "data" / "orchestrator.db"))
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def resolve_orchestrator_db(project_root: str | None) -> Path:
|
|
16
|
+
"""SQLite file: ``<project>/.skillforge/orchestrator.db`` when project_root is set, else global path.
|
|
17
|
+
|
|
18
|
+
If ``project_root`` is empty, falls back to env ``SKILLFORGE_PROJECT_ROOT``, then global.
|
|
19
|
+
"""
|
|
20
|
+
pr = (project_root or "").strip()
|
|
21
|
+
if not pr:
|
|
22
|
+
pr = os.getenv("SKILLFORGE_PROJECT_ROOT", "").strip()
|
|
23
|
+
if pr:
|
|
24
|
+
root = Path(pr).expanduser().resolve()
|
|
25
|
+
return root / ".skillforge" / "orchestrator.db"
|
|
26
|
+
return global_db_path()
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Print routing/events from Skillforge SQLite (terminal observability)."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import sqlite3
|
|
7
|
+
import time
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from app.db_paths import resolve_orchestrator_db
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _format_route_line(ts: float, sid: str | None, prev: dict, verbose: bool) -> str:
|
|
14
|
+
tstr = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ts))
|
|
15
|
+
picked = prev.get("picked")
|
|
16
|
+
sid12 = (sid or "-")[:12]
|
|
17
|
+
base = f"{tstr} route session={sid12:12} picked={picked}"
|
|
18
|
+
ms = prev.get("route_ms")
|
|
19
|
+
if ms is not None:
|
|
20
|
+
base += f" {ms}ms"
|
|
21
|
+
if prev.get("rerouted"):
|
|
22
|
+
base += " reroute"
|
|
23
|
+
if verbose:
|
|
24
|
+
prompt = (prev.get("prompt") or "")[:120].replace("\n", " ")
|
|
25
|
+
reason = (prev.get("reasoning") or "")[:100].replace("\n", " ")
|
|
26
|
+
if prompt:
|
|
27
|
+
base += f'\n prompt: {prompt}{"…" if len(str(prev.get("prompt") or "")) > 120 else ""}'
|
|
28
|
+
if reason:
|
|
29
|
+
base += f'\n why: {reason}'
|
|
30
|
+
return base
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _print_row(ts: float, sid: str | None, et: str | None, payload: str | None, verbose: bool) -> None:
|
|
34
|
+
prev = json.loads(payload) if payload else {}
|
|
35
|
+
if et == "route" and isinstance(prev, dict):
|
|
36
|
+
print(_format_route_line(ts, sid, prev, verbose))
|
|
37
|
+
return
|
|
38
|
+
extra = ""
|
|
39
|
+
if et == "feedback" and isinstance(prev, dict):
|
|
40
|
+
extra = f" skill={prev.get('skill')} thumbs={prev.get('thumbs')}"
|
|
41
|
+
tstr = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ts))
|
|
42
|
+
print(f"{tstr} {et or '?'} session={sid or '-':12}{extra}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _print_snapshot(
|
|
46
|
+
con: sqlite3.Connection,
|
|
47
|
+
user_id: str,
|
|
48
|
+
recent_minutes: float,
|
|
49
|
+
*,
|
|
50
|
+
db_path: Path | None = None,
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Usage totals + recently active sessions (routes), for live CLI."""
|
|
53
|
+
since = time.time() - recent_minutes * 60.0
|
|
54
|
+
cur = con.execute(
|
|
55
|
+
"SELECT skill_name, uses, referenced FROM skill_weights WHERE user_id = ? AND uses > 0 "
|
|
56
|
+
"ORDER BY uses DESC LIMIT 12",
|
|
57
|
+
(user_id,),
|
|
58
|
+
)
|
|
59
|
+
usage = cur.fetchall()
|
|
60
|
+
cur = con.execute(
|
|
61
|
+
"""
|
|
62
|
+
SELECT session_id, MAX(ts) AS mt
|
|
63
|
+
FROM events
|
|
64
|
+
WHERE ts >= ? AND event_type = 'route' AND user_id = ?
|
|
65
|
+
GROUP BY session_id
|
|
66
|
+
ORDER BY mt DESC
|
|
67
|
+
LIMIT 15
|
|
68
|
+
""",
|
|
69
|
+
(since, user_id),
|
|
70
|
+
)
|
|
71
|
+
sessions = cur.fetchall()
|
|
72
|
+
line = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
|
73
|
+
print(f"── [{line}] skillforge live ──")
|
|
74
|
+
if db_path is not None:
|
|
75
|
+
print(f" SQLite: {db_path}")
|
|
76
|
+
if usage:
|
|
77
|
+
ubits = [f"{n} uses={u} ref={r}" for n, u, r in usage]
|
|
78
|
+
print(" Top skills:", "; ".join(ubits))
|
|
79
|
+
else:
|
|
80
|
+
print(" Top skills: (no route stats yet for this user_id)")
|
|
81
|
+
if sessions:
|
|
82
|
+
short = [f"{(s or '-')[:10]}…" if s and len(s) > 10 else (s or "-") for s, _ in sessions]
|
|
83
|
+
print(f" Active sessions (routes in last {int(recent_minutes)}m): {len(sessions)} —", ", ".join(short))
|
|
84
|
+
else:
|
|
85
|
+
print(f" Active sessions: none in last {int(recent_minutes)}m")
|
|
86
|
+
print("── new events ──")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def main() -> None:
|
|
90
|
+
ap = argparse.ArgumentParser(
|
|
91
|
+
description="Skillforge event log (SQLite). Use --watch for realtime usage + routes."
|
|
92
|
+
)
|
|
93
|
+
ap.add_argument("--limit", type=int, default=40)
|
|
94
|
+
ap.add_argument("--watch", action="store_true", help="Poll for new rows (default interval 2s)")
|
|
95
|
+
ap.add_argument("--poll", type=float, default=2.0, help="Seconds between polls in --watch mode")
|
|
96
|
+
ap.add_argument(
|
|
97
|
+
"--summary-every",
|
|
98
|
+
type=float,
|
|
99
|
+
default=15.0,
|
|
100
|
+
metavar="SEC",
|
|
101
|
+
help="In --watch mode, re-print usage snapshot every SEC seconds (0 = only at start)",
|
|
102
|
+
)
|
|
103
|
+
ap.add_argument(
|
|
104
|
+
"--recent-minutes",
|
|
105
|
+
type=float,
|
|
106
|
+
default=60.0,
|
|
107
|
+
help="Window for 'active sessions' in snapshots (default 60)",
|
|
108
|
+
)
|
|
109
|
+
ap.add_argument(
|
|
110
|
+
"--user",
|
|
111
|
+
default="",
|
|
112
|
+
help="Logical user id for stats/sessions (matches MCP SKILLFORGE_MCP_USER_ID / HTTP user). Default empty.",
|
|
113
|
+
)
|
|
114
|
+
ap.add_argument(
|
|
115
|
+
"--project-root",
|
|
116
|
+
default="",
|
|
117
|
+
help="Workspace root — reads <root>/.skillforge/orchestrator.db. Default: env SKILLFORGE_PROJECT_ROOT or global DB.",
|
|
118
|
+
)
|
|
119
|
+
ap.add_argument("-v", "--verbose", action="store_true", help="More detail on route lines")
|
|
120
|
+
args = ap.parse_args()
|
|
121
|
+
|
|
122
|
+
pr = (args.project_root or "").strip() or None
|
|
123
|
+
db_path = resolve_orchestrator_db(pr)
|
|
124
|
+
|
|
125
|
+
if not db_path.exists():
|
|
126
|
+
print("No database yet — run skillforge mcp or skillforge start first (or route once with this project_root).")
|
|
127
|
+
print(f" Expected: {db_path}")
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
if args.watch:
|
|
131
|
+
con = sqlite3.connect(str(db_path))
|
|
132
|
+
row = con.execute("SELECT COALESCE(MAX(ts), 0) FROM events").fetchone()
|
|
133
|
+
con.close()
|
|
134
|
+
last_max = float(row[0]) if row else 0.0
|
|
135
|
+
summary_every = args.summary_every
|
|
136
|
+
next_summary = time.monotonic()
|
|
137
|
+
first = True
|
|
138
|
+
try:
|
|
139
|
+
while True:
|
|
140
|
+
now_m = time.monotonic()
|
|
141
|
+
if first or (summary_every > 0 and now_m >= next_summary):
|
|
142
|
+
con = sqlite3.connect(str(db_path))
|
|
143
|
+
_print_snapshot(con, args.user, args.recent_minutes, db_path=db_path)
|
|
144
|
+
con.close()
|
|
145
|
+
first = False
|
|
146
|
+
next_summary = now_m + summary_every
|
|
147
|
+
con = sqlite3.connect(str(db_path))
|
|
148
|
+
cur = con.execute(
|
|
149
|
+
"SELECT ts, session_id, event_type, payload FROM events WHERE ts > ? ORDER BY ts ASC",
|
|
150
|
+
(last_max,),
|
|
151
|
+
)
|
|
152
|
+
rows = cur.fetchall()
|
|
153
|
+
con.close()
|
|
154
|
+
for ts, sid, et, pay in rows:
|
|
155
|
+
_print_row(ts, sid, et, pay, args.verbose)
|
|
156
|
+
last_max = max(last_max, float(ts))
|
|
157
|
+
time.sleep(max(0.5, args.poll))
|
|
158
|
+
except KeyboardInterrupt:
|
|
159
|
+
print()
|
|
160
|
+
else:
|
|
161
|
+
con = sqlite3.connect(str(db_path))
|
|
162
|
+
_print_snapshot(con, args.user, args.recent_minutes, db_path=db_path)
|
|
163
|
+
cur = con.execute(
|
|
164
|
+
"SELECT ts, session_id, event_type, payload FROM events ORDER BY ts DESC LIMIT ?",
|
|
165
|
+
(args.limit,),
|
|
166
|
+
)
|
|
167
|
+
rows = cur.fetchall()
|
|
168
|
+
con.close()
|
|
169
|
+
print("── recent events (newest first) ──")
|
|
170
|
+
for ts, sid, et, pay in rows:
|
|
171
|
+
_print_row(ts, sid, et, pay, args.verbose)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
main()
|