@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
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ck — Context Keeper v2
|
|
3
|
+
* shared.mjs — common utilities for all command scripts
|
|
4
|
+
*
|
|
5
|
+
* No external dependencies. Node.js stdlib only.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
9
|
+
import { resolve } from 'path';
|
|
10
|
+
import { homedir } from 'os';
|
|
11
|
+
import { spawnSync } from 'child_process';
|
|
12
|
+
import { randomBytes } from 'crypto';
|
|
13
|
+
|
|
14
|
+
// ─── Paths ────────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export const CK_HOME = resolve(homedir(), '.claude', 'ck');
|
|
17
|
+
export const CONTEXTS_DIR = resolve(CK_HOME, 'contexts');
|
|
18
|
+
export const PROJECTS_FILE = resolve(CK_HOME, 'projects.json');
|
|
19
|
+
export const CURRENT_SESSION = resolve(CK_HOME, 'current-session.json');
|
|
20
|
+
export const SKILL_FILE = resolve(homedir(), '.claude', 'skills', 'ck', 'SKILL.md');
|
|
21
|
+
|
|
22
|
+
// ─── JSON I/O ─────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
export function readJson(filePath) {
|
|
25
|
+
try {
|
|
26
|
+
if (!existsSync(filePath)) return null;
|
|
27
|
+
return JSON.parse(readFileSync(filePath, 'utf8'));
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function writeJson(filePath, data) {
|
|
34
|
+
const dir = resolve(filePath, '..');
|
|
35
|
+
mkdirSync(dir, { recursive: true });
|
|
36
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function readProjects() {
|
|
40
|
+
return readJson(PROJECTS_FILE) || {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function writeProjects(projects) {
|
|
44
|
+
writeJson(PROJECTS_FILE, projects);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ─── Context I/O ──────────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
export function contextPath(contextDir) {
|
|
50
|
+
return resolve(CONTEXTS_DIR, contextDir, 'context.json');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function contextMdPath(contextDir) {
|
|
54
|
+
return resolve(CONTEXTS_DIR, contextDir, 'CONTEXT.md');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function loadContext(contextDir) {
|
|
58
|
+
return readJson(contextPath(contextDir));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function saveContext(contextDir, data) {
|
|
62
|
+
const dir = resolve(CONTEXTS_DIR, contextDir);
|
|
63
|
+
mkdirSync(dir, { recursive: true });
|
|
64
|
+
writeJson(contextPath(contextDir), data);
|
|
65
|
+
writeFileSync(contextMdPath(contextDir), renderContextMd(data), 'utf8');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Resolve which project to operate on.
|
|
70
|
+
* @param {string|undefined} arg — undefined = cwd match, number string = alphabetical index, else name search
|
|
71
|
+
* @param {string} cwd
|
|
72
|
+
* @returns {{ name, contextDir, projectPath, context } | null}
|
|
73
|
+
*/
|
|
74
|
+
export function resolveContext(arg, cwd) {
|
|
75
|
+
const projects = readProjects();
|
|
76
|
+
const entries = Object.entries(projects); // [path, {name, contextDir, lastUpdated}]
|
|
77
|
+
|
|
78
|
+
if (!arg) {
|
|
79
|
+
// Match by cwd
|
|
80
|
+
const entry = projects[cwd];
|
|
81
|
+
if (!entry) return null;
|
|
82
|
+
const context = loadContext(entry.contextDir);
|
|
83
|
+
if (!context) return null;
|
|
84
|
+
return { name: entry.name, contextDir: entry.contextDir, projectPath: cwd, context };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Collect all contexts sorted alphabetically by contextDir
|
|
88
|
+
const sorted = entries
|
|
89
|
+
.map(([path, info]) => ({ path, ...info }))
|
|
90
|
+
.sort((a, b) => a.contextDir.localeCompare(b.contextDir));
|
|
91
|
+
|
|
92
|
+
const asNumber = parseInt(arg, 10);
|
|
93
|
+
if (!isNaN(asNumber) && String(asNumber) === arg) {
|
|
94
|
+
// Number-based lookup (1-indexed)
|
|
95
|
+
const item = sorted[asNumber - 1];
|
|
96
|
+
if (!item) return null;
|
|
97
|
+
const context = loadContext(item.contextDir);
|
|
98
|
+
if (!context) return null;
|
|
99
|
+
return { name: item.name, contextDir: item.contextDir, projectPath: item.path, context };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Name-based lookup: exact > prefix > substring (case-insensitive)
|
|
103
|
+
const lower = arg.toLowerCase();
|
|
104
|
+
let match =
|
|
105
|
+
sorted.find(e => e.name.toLowerCase() === lower) ||
|
|
106
|
+
sorted.find(e => e.name.toLowerCase().startsWith(lower)) ||
|
|
107
|
+
sorted.find(e => e.name.toLowerCase().includes(lower));
|
|
108
|
+
|
|
109
|
+
if (!match) return null;
|
|
110
|
+
const context = loadContext(match.contextDir);
|
|
111
|
+
if (!context) return null;
|
|
112
|
+
return { name: match.name, contextDir: match.contextDir, projectPath: match.path, context };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ─── Date helpers ─────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
export function today() {
|
|
118
|
+
return new Date().toISOString().slice(0, 10);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function daysAgoLabel(dateStr) {
|
|
122
|
+
if (!dateStr) return 'unknown';
|
|
123
|
+
const diff = Math.floor((Date.now() - new Date(dateStr)) / 86_400_000);
|
|
124
|
+
if (diff === 0) return 'Today';
|
|
125
|
+
if (diff === 1) return '1 day ago';
|
|
126
|
+
return `${diff} days ago`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function stalenessIcon(dateStr) {
|
|
130
|
+
if (!dateStr) return '○';
|
|
131
|
+
const diff = Math.floor((Date.now() - new Date(dateStr)) / 86_400_000);
|
|
132
|
+
if (diff < 1) return '●';
|
|
133
|
+
if (diff <= 5) return '◐';
|
|
134
|
+
return '○';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ─── ID generation ────────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
export function shortId() {
|
|
140
|
+
return randomBytes(4).toString('hex');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── Git helpers ──────────────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
function runGit(args, cwd) {
|
|
146
|
+
try {
|
|
147
|
+
const result = spawnSync('git', ['-C', cwd, ...args], {
|
|
148
|
+
timeout: 3000,
|
|
149
|
+
stdio: 'pipe',
|
|
150
|
+
encoding: 'utf8',
|
|
151
|
+
});
|
|
152
|
+
if (result.status !== 0) return null;
|
|
153
|
+
return result.stdout.trim();
|
|
154
|
+
} catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function gitLogSince(projectPath, sinceDate) {
|
|
160
|
+
if (!sinceDate) return null;
|
|
161
|
+
return runGit(['log', '--oneline', `--since=${sinceDate}`], projectPath);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function gitSummary(projectPath, sinceDate) {
|
|
165
|
+
const log = gitLogSince(projectPath, sinceDate);
|
|
166
|
+
if (!log) return null;
|
|
167
|
+
const commits = log.split('\n').filter(Boolean).length;
|
|
168
|
+
if (commits === 0) return null;
|
|
169
|
+
|
|
170
|
+
// Count unique files changed: use a separate runGit call to avoid nested shell substitution
|
|
171
|
+
const countStr = runGit(['rev-list', '--count', 'HEAD', `--since=${sinceDate}`], projectPath);
|
|
172
|
+
const revCount = countStr ? parseInt(countStr, 10) : commits;
|
|
173
|
+
const diff = runGit(['diff', '--shortstat', `HEAD~${Math.min(revCount, 50)}..HEAD`], projectPath);
|
|
174
|
+
|
|
175
|
+
if (diff) {
|
|
176
|
+
const filesMatch = diff.match(/(\d+) file/);
|
|
177
|
+
const files = filesMatch ? parseInt(filesMatch[1]) : '?';
|
|
178
|
+
return `${commits} commit${commits !== 1 ? 's' : ''}, ${files} file${files !== 1 ? 's' : ''} changed`;
|
|
179
|
+
}
|
|
180
|
+
return `${commits} commit${commits !== 1 ? 's' : ''}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ─── Native memory path encoding ──────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
export function encodeProjectPath(absolutePath) {
|
|
186
|
+
// "/Users/sree/dev/app" -> "-Users-sree-dev-app"
|
|
187
|
+
return absolutePath.replace(/\//g, '-');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function nativeMemoryDir(absolutePath) {
|
|
191
|
+
const encoded = encodeProjectPath(absolutePath);
|
|
192
|
+
return resolve(homedir(), '.claude', 'projects', encoded, 'memory');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ─── Rendering ────────────────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
/** Render the human-readable CONTEXT.md from context.json */
|
|
198
|
+
export function renderContextMd(ctx) {
|
|
199
|
+
const latest = ctx.sessions?.[ctx.sessions.length - 1] || null;
|
|
200
|
+
const lines = [
|
|
201
|
+
`<!-- Generated by ck v2 — edit context.json instead -->`,
|
|
202
|
+
`# Project: ${ctx.displayName ?? ctx.name}`,
|
|
203
|
+
`> Path: ${ctx.path}`,
|
|
204
|
+
];
|
|
205
|
+
if (ctx.repo) lines.push(`> Repo: ${ctx.repo}`);
|
|
206
|
+
const sessionCount = ctx.sessions?.length || 0;
|
|
207
|
+
lines.push(`> Last Session: ${ctx.sessions?.[sessionCount - 1]?.date || 'never'} | Sessions: ${sessionCount}`);
|
|
208
|
+
lines.push(``);
|
|
209
|
+
lines.push(`## What This Is`);
|
|
210
|
+
lines.push(ctx.description || '_Not set._');
|
|
211
|
+
lines.push(``);
|
|
212
|
+
lines.push(`## Tech Stack`);
|
|
213
|
+
lines.push(Array.isArray(ctx.stack) ? ctx.stack.join(', ') : (ctx.stack || '_Not set._'));
|
|
214
|
+
lines.push(``);
|
|
215
|
+
lines.push(`## Current Goal`);
|
|
216
|
+
lines.push(ctx.goal || '_Not set._');
|
|
217
|
+
lines.push(``);
|
|
218
|
+
lines.push(`## Where I Left Off`);
|
|
219
|
+
lines.push(latest?.leftOff || '_Not yet recorded. Run /ck:save after your first session._');
|
|
220
|
+
lines.push(``);
|
|
221
|
+
lines.push(`## Next Steps`);
|
|
222
|
+
if (latest?.nextSteps?.length) {
|
|
223
|
+
latest.nextSteps.forEach((s, i) => lines.push(`${i + 1}. ${s}`));
|
|
224
|
+
} else {
|
|
225
|
+
lines.push(`_Not yet recorded._`);
|
|
226
|
+
}
|
|
227
|
+
lines.push(``);
|
|
228
|
+
lines.push(`## Blockers`);
|
|
229
|
+
if (latest?.blockers?.length) {
|
|
230
|
+
latest.blockers.forEach(b => lines.push(`- ${b}`));
|
|
231
|
+
} else {
|
|
232
|
+
lines.push(`- None`);
|
|
233
|
+
}
|
|
234
|
+
lines.push(``);
|
|
235
|
+
lines.push(`## Do Not Do`);
|
|
236
|
+
if (ctx.constraints?.length) {
|
|
237
|
+
ctx.constraints.forEach(c => lines.push(`- ${c}`));
|
|
238
|
+
} else {
|
|
239
|
+
lines.push(`- None specified`);
|
|
240
|
+
}
|
|
241
|
+
lines.push(``);
|
|
242
|
+
|
|
243
|
+
// All decisions across sessions
|
|
244
|
+
const allDecisions = (ctx.sessions || []).flatMap(s =>
|
|
245
|
+
(s.decisions || []).map(d => ({ ...d, date: s.date }))
|
|
246
|
+
);
|
|
247
|
+
lines.push(`## Decisions Made`);
|
|
248
|
+
lines.push(`| Decision | Why | Date |`);
|
|
249
|
+
lines.push(`|----------|-----|------|`);
|
|
250
|
+
if (allDecisions.length) {
|
|
251
|
+
allDecisions.forEach(d => lines.push(`| ${d.what} | ${d.why || ''} | ${d.date || ''} |`));
|
|
252
|
+
} else {
|
|
253
|
+
lines.push(`| _(none yet)_ | | |`);
|
|
254
|
+
}
|
|
255
|
+
lines.push(``);
|
|
256
|
+
|
|
257
|
+
// Session history (most recent first)
|
|
258
|
+
if (ctx.sessions?.length > 1) {
|
|
259
|
+
lines.push(`## Session History`);
|
|
260
|
+
const reversed = [...ctx.sessions].reverse();
|
|
261
|
+
reversed.forEach(s => {
|
|
262
|
+
lines.push(`### ${s.date} — ${s.summary || 'Session'}`);
|
|
263
|
+
if (s.gitActivity) lines.push(`_${s.gitActivity}_`);
|
|
264
|
+
if (s.leftOff) lines.push(`**Left off:** ${s.leftOff}`);
|
|
265
|
+
});
|
|
266
|
+
lines.push(``);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return lines.join('\n');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Render the bordered briefing box used by /ck:resume */
|
|
273
|
+
export function renderBriefingBox(ctx, _meta = {}) {
|
|
274
|
+
const latest = ctx.sessions?.[ctx.sessions.length - 1] || {};
|
|
275
|
+
const W = 57;
|
|
276
|
+
const pad = (str, w) => {
|
|
277
|
+
const s = String(str || '');
|
|
278
|
+
return s.length > w ? s.slice(0, w - 1) + '…' : s.padEnd(w);
|
|
279
|
+
};
|
|
280
|
+
const row = (label, value) => `│ ${label} → ${pad(value, W - label.length - 7)}│`;
|
|
281
|
+
|
|
282
|
+
const when = daysAgoLabel(ctx.sessions?.[ctx.sessions.length - 1]?.date);
|
|
283
|
+
const sessions = ctx.sessions?.length || 0;
|
|
284
|
+
const shortSessId = latest.id?.slice(0, 8) || null;
|
|
285
|
+
|
|
286
|
+
const lines = [
|
|
287
|
+
`┌${'─'.repeat(W)}┐`,
|
|
288
|
+
`│ RESUMING: ${pad(ctx.displayName ?? ctx.name, W - 12)}│`,
|
|
289
|
+
`│ Last session: ${pad(`${when} | Sessions: ${sessions}`, W - 16)}│`,
|
|
290
|
+
];
|
|
291
|
+
if (shortSessId) lines.push(`│ Session ID: ${pad(shortSessId, W - 14)}│`);
|
|
292
|
+
lines.push(`├${'─'.repeat(W)}┤`);
|
|
293
|
+
lines.push(row('WHAT IT IS', ctx.description || '—'));
|
|
294
|
+
lines.push(row('STACK ', Array.isArray(ctx.stack) ? ctx.stack.join(', ') : (ctx.stack || '—')));
|
|
295
|
+
lines.push(row('PATH ', ctx.path));
|
|
296
|
+
if (ctx.repo) lines.push(row('REPO ', ctx.repo));
|
|
297
|
+
lines.push(row('GOAL ', ctx.goal || '—'));
|
|
298
|
+
lines.push(`├${'─'.repeat(W)}┤`);
|
|
299
|
+
lines.push(`│ WHERE I LEFT OFF${' '.repeat(W - 18)}│`);
|
|
300
|
+
const leftOffLines = (latest.leftOff || '—').split('\n').filter(Boolean);
|
|
301
|
+
leftOffLines.forEach(l => lines.push(`│ • ${pad(l, W - 7)}│`));
|
|
302
|
+
lines.push(`├${'─'.repeat(W)}┤`);
|
|
303
|
+
lines.push(`│ NEXT STEPS${' '.repeat(W - 12)}│`);
|
|
304
|
+
const steps = latest.nextSteps || [];
|
|
305
|
+
if (steps.length) {
|
|
306
|
+
steps.forEach((s, i) => lines.push(`│ ${i + 1}. ${pad(s, W - 8)}│`));
|
|
307
|
+
} else {
|
|
308
|
+
lines.push(`│ —${' '.repeat(W - 5)}│`);
|
|
309
|
+
}
|
|
310
|
+
const blockers = latest.blockers?.length ? latest.blockers.join(', ') : 'None';
|
|
311
|
+
lines.push(`│ BLOCKERS → ${pad(blockers, W - 13)}│`);
|
|
312
|
+
if (latest.gitActivity) {
|
|
313
|
+
lines.push(`│ GIT → ${pad(latest.gitActivity, W - 13)}│`);
|
|
314
|
+
}
|
|
315
|
+
lines.push(`└${'─'.repeat(W)}┘`);
|
|
316
|
+
return lines.join('\n');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** Render compact info block used by /ck:info */
|
|
320
|
+
export function renderInfoBlock(ctx) {
|
|
321
|
+
const latest = ctx.sessions?.[ctx.sessions.length - 1] || {};
|
|
322
|
+
const sep = '─'.repeat(44);
|
|
323
|
+
const lines = [
|
|
324
|
+
`ck: ${ctx.displayName ?? ctx.name}`,
|
|
325
|
+
sep,
|
|
326
|
+
];
|
|
327
|
+
lines.push(`PATH ${ctx.path}`);
|
|
328
|
+
if (ctx.repo) lines.push(`REPO ${ctx.repo}`);
|
|
329
|
+
if (latest.id) lines.push(`SESSION ${latest.id.slice(0, 8)}`);
|
|
330
|
+
lines.push(`GOAL ${ctx.goal || '—'}`);
|
|
331
|
+
lines.push(sep);
|
|
332
|
+
lines.push(`WHERE I LEFT OFF`);
|
|
333
|
+
(latest.leftOff || '—').split('\n').filter(Boolean).forEach(l => lines.push(` • ${l}`));
|
|
334
|
+
lines.push(`NEXT STEPS`);
|
|
335
|
+
(latest.nextSteps || []).forEach((s, i) => lines.push(` ${i + 1}. ${s}`));
|
|
336
|
+
if (!latest.nextSteps?.length) lines.push(` —`);
|
|
337
|
+
lines.push(`BLOCKERS`);
|
|
338
|
+
if (latest.blockers?.length) {
|
|
339
|
+
latest.blockers.forEach(b => lines.push(` • ${b}`));
|
|
340
|
+
} else {
|
|
341
|
+
lines.push(` • None`);
|
|
342
|
+
}
|
|
343
|
+
return lines.join('\n');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** Render ASCII list table used by /ck:list */
|
|
347
|
+
export function renderListTable(entries, cwd, _todayStr) {
|
|
348
|
+
// entries: [{name, contextDir, path, context, lastUpdated}]
|
|
349
|
+
// Sorted alphabetically by contextDir before calling
|
|
350
|
+
const rows = entries.map((e, i) => {
|
|
351
|
+
const isHere = e.path === cwd;
|
|
352
|
+
const latest = e.context?.sessions?.[e.context.sessions.length - 1] || {};
|
|
353
|
+
const when = daysAgoLabel(latest.date);
|
|
354
|
+
const icon = stalenessIcon(latest.date);
|
|
355
|
+
const statusLabel = icon === '●' ? '● Active' : icon === '◐' ? '◐ Warm' : '○ Stale';
|
|
356
|
+
const sessId = latest.id ? latest.id.slice(0, 8) : '—';
|
|
357
|
+
const summary = (latest.summary || '—').slice(0, 34);
|
|
358
|
+
const displayName = ((e.context?.displayName ?? e.name) + (isHere ? ' <-' : '')).slice(0, 18);
|
|
359
|
+
return {
|
|
360
|
+
num: String(i + 1),
|
|
361
|
+
name: displayName,
|
|
362
|
+
status: statusLabel,
|
|
363
|
+
when: when.slice(0, 10),
|
|
364
|
+
sessId,
|
|
365
|
+
summary,
|
|
366
|
+
};
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const cols = {
|
|
370
|
+
num: Math.max(1, ...rows.map(r => r.num.length)),
|
|
371
|
+
name: Math.max(7, ...rows.map(r => r.name.length)),
|
|
372
|
+
status: Math.max(6, ...rows.map(r => r.status.length)),
|
|
373
|
+
when: Math.max(9, ...rows.map(r => r.when.length)),
|
|
374
|
+
sessId: Math.max(7, ...rows.map(r => r.sessId.length)),
|
|
375
|
+
summary: Math.max(12, ...rows.map(r => r.summary.length)),
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const hr = `+${'-'.repeat(cols.num + 2)}+${'-'.repeat(cols.name + 2)}+${'-'.repeat(cols.status + 2)}+${'-'.repeat(cols.when + 2)}+${'-'.repeat(cols.sessId + 2)}+${'-'.repeat(cols.summary + 2)}+`;
|
|
379
|
+
const cell = (val, width) => ` ${val.padEnd(width)} `;
|
|
380
|
+
const headerRow = `|${cell('#', cols.num)}|${cell('Project', cols.name)}|${cell('Status', cols.status)}|${cell('Last Seen', cols.when)}|${cell('Session', cols.sessId)}|${cell('Last Summary', cols.summary)}|`;
|
|
381
|
+
|
|
382
|
+
const dataRows = rows.map(r =>
|
|
383
|
+
`|${cell(r.num, cols.num)}|${cell(r.name, cols.name)}|${cell(r.status, cols.status)}|${cell(r.when, cols.when)}|${cell(r.sessId, cols.sessId)}|${cell(r.summary, cols.summary)}|`
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
return [hr, headerRow, hr, ...dataRows, hr].join('\n');
|
|
387
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ck — Context Keeper v2
|
|
4
|
+
* session-start.mjs — inject compact project context on session start.
|
|
5
|
+
*
|
|
6
|
+
* Injects ~100 tokens (not ~2,500 like v1).
|
|
7
|
+
* SKILL.md is injected separately (still small at ~50 lines).
|
|
8
|
+
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - Compact 5-line summary for registered projects
|
|
11
|
+
* - Unsaved session detection → "Last session wasn't saved. Run /ck:save."
|
|
12
|
+
* - Git activity since last session
|
|
13
|
+
* - Goal mismatch detection vs CLAUDE.md
|
|
14
|
+
* - Mini portfolio for unregistered directories
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
18
|
+
import { resolve } from 'path';
|
|
19
|
+
import { homedir } from 'os';
|
|
20
|
+
import { spawnSync } from 'child_process';
|
|
21
|
+
|
|
22
|
+
const CK_HOME = resolve(homedir(), '.claude', 'ck');
|
|
23
|
+
const PROJECTS_FILE = resolve(CK_HOME, 'projects.json');
|
|
24
|
+
const CURRENT_SESSION = resolve(CK_HOME, 'current-session.json');
|
|
25
|
+
const SKILL_FILE = resolve(homedir(), '.claude', 'skills', 'ck', 'SKILL.md');
|
|
26
|
+
|
|
27
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
function readJson(p) {
|
|
30
|
+
try { return JSON.parse(readFileSync(p, 'utf8')); } catch { return null; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function daysAgo(dateStr) {
|
|
34
|
+
if (!dateStr) return 'unknown';
|
|
35
|
+
const diff = Math.floor((Date.now() - new Date(dateStr)) / 86_400_000);
|
|
36
|
+
if (diff === 0) return 'today';
|
|
37
|
+
if (diff === 1) return '1 day ago';
|
|
38
|
+
return `${diff} days ago`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function stalenessIcon(dateStr) {
|
|
42
|
+
if (!dateStr) return '○';
|
|
43
|
+
const diff = Math.floor((Date.now() - new Date(dateStr)) / 86_400_000);
|
|
44
|
+
return diff < 1 ? '●' : diff <= 5 ? '◐' : '○';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function gitLogSince(projectPath, sinceDate) {
|
|
48
|
+
if (!sinceDate || !existsSync(resolve(projectPath, '.git'))) return null;
|
|
49
|
+
try {
|
|
50
|
+
const result = spawnSync(
|
|
51
|
+
'git',
|
|
52
|
+
['-C', projectPath, 'log', '--oneline', `--since=${sinceDate}`],
|
|
53
|
+
{ timeout: 3000, stdio: 'pipe', encoding: 'utf8' },
|
|
54
|
+
);
|
|
55
|
+
if (result.status !== 0) return null;
|
|
56
|
+
const output = result.stdout.trim();
|
|
57
|
+
const commits = output.split('\n').filter(Boolean).length;
|
|
58
|
+
return commits > 0 ? `${commits} commit${commits !== 1 ? 's' : ''} since last session` : null;
|
|
59
|
+
} catch { return null; }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function extractClaudeMdGoal(projectPath) {
|
|
63
|
+
const p = resolve(projectPath, 'CLAUDE.md');
|
|
64
|
+
if (!existsSync(p)) return null;
|
|
65
|
+
try {
|
|
66
|
+
const md = readFileSync(p, 'utf8');
|
|
67
|
+
const m = md.match(/## Current Goal\n([\s\S]*?)(?=\n## |$)/);
|
|
68
|
+
return m ? m[1].trim().split('\n')[0].trim() : null;
|
|
69
|
+
} catch { return null; }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─── Session ID from stdin ────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
function readSessionId() {
|
|
75
|
+
try {
|
|
76
|
+
const raw = readFileSync(0, 'utf8');
|
|
77
|
+
return JSON.parse(raw).session_id || null;
|
|
78
|
+
} catch { return null; }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
function main() {
|
|
84
|
+
const cwd = process.env.PWD || process.cwd();
|
|
85
|
+
const sessionId = readSessionId();
|
|
86
|
+
|
|
87
|
+
// Load skill (always inject — now only ~50 lines)
|
|
88
|
+
const skill = existsSync(SKILL_FILE) ? readFileSync(SKILL_FILE, 'utf8') : '';
|
|
89
|
+
|
|
90
|
+
const projects = readJson(PROJECTS_FILE) || {};
|
|
91
|
+
const entry = projects[cwd];
|
|
92
|
+
|
|
93
|
+
// Read previous session BEFORE overwriting current-session.json
|
|
94
|
+
const prevSession = readJson(CURRENT_SESSION);
|
|
95
|
+
|
|
96
|
+
// Write current-session.json
|
|
97
|
+
try {
|
|
98
|
+
writeFileSync(CURRENT_SESSION, JSON.stringify({
|
|
99
|
+
sessionId,
|
|
100
|
+
projectPath: cwd,
|
|
101
|
+
projectName: entry?.name || null,
|
|
102
|
+
startedAt: new Date().toISOString(),
|
|
103
|
+
}, null, 2), 'utf8');
|
|
104
|
+
} catch { /* non-fatal */ }
|
|
105
|
+
|
|
106
|
+
const parts = [];
|
|
107
|
+
if (skill) parts.push(skill);
|
|
108
|
+
|
|
109
|
+
// ── REGISTERED PROJECT ────────────────────────────────────────────────────
|
|
110
|
+
if (entry?.contextDir) {
|
|
111
|
+
const contextFile = resolve(CK_HOME, 'contexts', entry.contextDir, 'context.json');
|
|
112
|
+
const context = readJson(contextFile);
|
|
113
|
+
|
|
114
|
+
if (context) {
|
|
115
|
+
const latest = context.sessions?.[context.sessions.length - 1] || {};
|
|
116
|
+
const sessionDate = latest.date || context.createdAt;
|
|
117
|
+
const sessionCount = context.sessions?.length || 0;
|
|
118
|
+
const displayName = context.displayName ?? context.name;
|
|
119
|
+
|
|
120
|
+
// ── Compact summary block (~100 tokens) ──────────────────────────────
|
|
121
|
+
const summaryLines = [
|
|
122
|
+
`ck: ${displayName} | ${daysAgo(sessionDate)} | ${sessionCount} session${sessionCount !== 1 ? 's' : ''}`,
|
|
123
|
+
`Goal: ${context.goal || '—'}`,
|
|
124
|
+
latest.leftOff ? `Left off: ${latest.leftOff.split('\n')[0]}` : null,
|
|
125
|
+
latest.nextSteps?.length ? `Next: ${latest.nextSteps.slice(0, 2).join(' · ')}` : null,
|
|
126
|
+
].filter(Boolean);
|
|
127
|
+
|
|
128
|
+
// ── Unsaved session detection ─────────────────────────────────────────
|
|
129
|
+
if (prevSession?.sessionId && prevSession.sessionId !== sessionId) {
|
|
130
|
+
// Check if previous session ID exists in sessions array
|
|
131
|
+
const alreadySaved = context.sessions?.some(s => s.id === prevSession.sessionId);
|
|
132
|
+
if (!alreadySaved) {
|
|
133
|
+
summaryLines.push(`WARNING Last session wasn't saved — run /ck:save to capture it`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── Git activity ──────────────────────────────────────────────────────
|
|
138
|
+
const gitLine = gitLogSince(cwd, sessionDate);
|
|
139
|
+
if (gitLine) summaryLines.push(`Git: ${gitLine}`);
|
|
140
|
+
|
|
141
|
+
// ── Goal mismatch detection ───────────────────────────────────────────
|
|
142
|
+
const claudeMdGoal = extractClaudeMdGoal(cwd);
|
|
143
|
+
if (claudeMdGoal && context.goal &&
|
|
144
|
+
claudeMdGoal.toLowerCase().trim() !== context.goal.toLowerCase().trim()) {
|
|
145
|
+
summaryLines.push(`WARNING Goal mismatch — ck: "${context.goal.slice(0, 40)}" · CLAUDE.md: "${claudeMdGoal.slice(0, 40)}"`);
|
|
146
|
+
summaryLines.push(` Run /ck:save with updated goal to sync`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
parts.push([
|
|
150
|
+
`---`,
|
|
151
|
+
`## ck: ${displayName}`,
|
|
152
|
+
``,
|
|
153
|
+
summaryLines.join('\n'),
|
|
154
|
+
].join('\n'));
|
|
155
|
+
|
|
156
|
+
// Instruct Claude to display compact briefing at session start
|
|
157
|
+
parts.push([
|
|
158
|
+
`---`,
|
|
159
|
+
`## ck: SESSION START`,
|
|
160
|
+
``,
|
|
161
|
+
`IMPORTANT: Display the following as your FIRST message, verbatim:`,
|
|
162
|
+
``,
|
|
163
|
+
'```',
|
|
164
|
+
summaryLines.join('\n'),
|
|
165
|
+
'```',
|
|
166
|
+
``,
|
|
167
|
+
`After the block, add one line: "Ready — what are we working on?"`,
|
|
168
|
+
`If you see WARNING lines above, mention them briefly after the block.`,
|
|
169
|
+
].join('\n'));
|
|
170
|
+
|
|
171
|
+
return parts;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ── NOT IN A REGISTERED PROJECT ────────────────────────────────────────────
|
|
176
|
+
const entries = Object.entries(projects);
|
|
177
|
+
if (entries.length === 0) return parts;
|
|
178
|
+
|
|
179
|
+
// Load and sort by most recent
|
|
180
|
+
const recent = entries
|
|
181
|
+
.map(([path, info]) => {
|
|
182
|
+
const ctx = readJson(resolve(CK_HOME, 'contexts', info.contextDir, 'context.json'));
|
|
183
|
+
const latest = ctx?.sessions?.[ctx.sessions.length - 1] || {};
|
|
184
|
+
return { name: info.name, path, lastDate: latest.date || '', summary: latest.summary || '—', ctx };
|
|
185
|
+
})
|
|
186
|
+
.sort((a, b) => (b.lastDate > a.lastDate ? 1 : -1))
|
|
187
|
+
.slice(0, 3);
|
|
188
|
+
|
|
189
|
+
const miniRows = recent.map(p => {
|
|
190
|
+
const icon = stalenessIcon(p.lastDate);
|
|
191
|
+
const when = daysAgo(p.lastDate);
|
|
192
|
+
const name = p.name.padEnd(16).slice(0, 16);
|
|
193
|
+
const whenStr = when.padEnd(12).slice(0, 12);
|
|
194
|
+
const summary = p.summary.slice(0, 32);
|
|
195
|
+
return ` ${name} ${icon} ${whenStr} ${summary}`;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const miniStatus = [
|
|
199
|
+
`ck — recent projects:`,
|
|
200
|
+
` ${'PROJECT'.padEnd(16)} S ${'LAST SEEN'.padEnd(12)} LAST SESSION`,
|
|
201
|
+
` ${'─'.repeat(68)}`,
|
|
202
|
+
...miniRows,
|
|
203
|
+
``,
|
|
204
|
+
`Run /ck:list · /ck:resume <name> · /ck:init to register this folder`,
|
|
205
|
+
].join('\n');
|
|
206
|
+
|
|
207
|
+
parts.push([
|
|
208
|
+
`---`,
|
|
209
|
+
`## ck: SESSION START`,
|
|
210
|
+
``,
|
|
211
|
+
`IMPORTANT: Display the following as your FIRST message, verbatim:`,
|
|
212
|
+
``,
|
|
213
|
+
'```',
|
|
214
|
+
miniStatus,
|
|
215
|
+
'```',
|
|
216
|
+
].join('\n'));
|
|
217
|
+
|
|
218
|
+
return parts;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const parts = main();
|
|
222
|
+
if (parts.length > 0) {
|
|
223
|
+
console.log(JSON.stringify({ additionalContext: parts.join('\n\n---\n\n') }));
|
|
224
|
+
}
|