@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.
Files changed (402) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/CODE_OF_CONDUCT.md +34 -0
  3. package/CONTRIBUTING.md +38 -0
  4. package/LICENSE +21 -0
  5. package/README.md +337 -0
  6. package/RELEASING.md +93 -0
  7. package/SECURITY.md +31 -0
  8. package/STRATEGY.md +26 -0
  9. package/bin/cli.js +547 -0
  10. package/lib/packs.js +184 -0
  11. package/package.json +38 -0
  12. package/python/app/__init__.py +0 -0
  13. package/python/app/__pycache__/__init__.cpython-312.pyc +0 -0
  14. package/python/app/__pycache__/auth.cpython-312.pyc +0 -0
  15. package/python/app/__pycache__/main.cpython-312.pyc +0 -0
  16. package/python/app/auth.py +63 -0
  17. package/python/app/cli.py +78 -0
  18. package/python/app/db_paths.py +26 -0
  19. package/python/app/events_cli.py +175 -0
  20. package/python/app/main.py +647 -0
  21. package/python/app/materialize.py +138 -0
  22. package/python/app/mcp_server.py +610 -0
  23. package/python/app/route_cli.py +117 -0
  24. package/python/requirements-dev.txt +1 -0
  25. package/python/requirements.txt +7 -0
  26. package/python/tests/test_db_paths.py +41 -0
  27. package/skills/accessibility/SKILL.md +145 -0
  28. package/skills/agent-architecture-audit/SKILL.md +256 -0
  29. package/skills/agent-eval/SKILL.md +144 -0
  30. package/skills/agent-harness-construction/SKILL.md +72 -0
  31. package/skills/agent-introspection-debugging/SKILL.md +152 -0
  32. package/skills/agent-payment-x402/SKILL.md +224 -0
  33. package/skills/agent-sort/SKILL.md +214 -0
  34. package/skills/agentic-engineering/SKILL.md +62 -0
  35. package/skills/agentic-os/SKILL.md +386 -0
  36. package/skills/ai-first-engineering/SKILL.md +50 -0
  37. package/skills/ai-regression-testing/SKILL.md +384 -0
  38. package/skills/android-clean-architecture/SKILL.md +338 -0
  39. package/skills/angular-developer/SKILL.md +153 -0
  40. package/skills/angular-developer/references/angular-animations.md +160 -0
  41. package/skills/angular-developer/references/angular-aria.md +410 -0
  42. package/skills/angular-developer/references/cli.md +86 -0
  43. package/skills/angular-developer/references/component-harnesses.md +59 -0
  44. package/skills/angular-developer/references/component-styling.md +91 -0
  45. package/skills/angular-developer/references/components.md +117 -0
  46. package/skills/angular-developer/references/creating-services.md +97 -0
  47. package/skills/angular-developer/references/data-resolvers.md +69 -0
  48. package/skills/angular-developer/references/define-routes.md +67 -0
  49. package/skills/angular-developer/references/defining-providers.md +72 -0
  50. package/skills/angular-developer/references/di-fundamentals.md +120 -0
  51. package/skills/angular-developer/references/e2e-testing.md +56 -0
  52. package/skills/angular-developer/references/effects.md +83 -0
  53. package/skills/angular-developer/references/hierarchical-injectors.md +43 -0
  54. package/skills/angular-developer/references/host-elements.md +80 -0
  55. package/skills/angular-developer/references/injection-context.md +63 -0
  56. package/skills/angular-developer/references/inputs.md +101 -0
  57. package/skills/angular-developer/references/linked-signal.md +59 -0
  58. package/skills/angular-developer/references/loading-strategies.md +61 -0
  59. package/skills/angular-developer/references/mcp.md +108 -0
  60. package/skills/angular-developer/references/navigate-to-routes.md +69 -0
  61. package/skills/angular-developer/references/outputs.md +86 -0
  62. package/skills/angular-developer/references/reactive-forms.md +122 -0
  63. package/skills/angular-developer/references/rendering-strategies.md +44 -0
  64. package/skills/angular-developer/references/resource.md +77 -0
  65. package/skills/angular-developer/references/route-animations.md +56 -0
  66. package/skills/angular-developer/references/route-guards.md +52 -0
  67. package/skills/angular-developer/references/router-lifecycle.md +45 -0
  68. package/skills/angular-developer/references/router-testing.md +87 -0
  69. package/skills/angular-developer/references/show-routes-with-outlets.md +68 -0
  70. package/skills/angular-developer/references/signal-forms.md +795 -0
  71. package/skills/angular-developer/references/signals-overview.md +94 -0
  72. package/skills/angular-developer/references/tailwind-css.md +69 -0
  73. package/skills/angular-developer/references/template-driven-forms.md +114 -0
  74. package/skills/angular-developer/references/testing-fundamentals.md +65 -0
  75. package/skills/api-connector-builder/SKILL.md +120 -0
  76. package/skills/api-design/SKILL.md +522 -0
  77. package/skills/architecture-decision-records/SKILL.md +178 -0
  78. package/skills/article-writing/SKILL.md +78 -0
  79. package/skills/automation-audit-ops/SKILL.md +141 -0
  80. package/skills/autonomous-agent-harness/SKILL.md +272 -0
  81. package/skills/autonomous-loops/SKILL.md +609 -0
  82. package/skills/backend-patterns/SKILL.md +560 -0
  83. package/skills/benchmark/SKILL.md +92 -0
  84. package/skills/blueprint/SKILL.md +104 -0
  85. package/skills/browser-qa/SKILL.md +86 -0
  86. package/skills/bun-runtime/SKILL.md +83 -0
  87. package/skills/canary-watch/SKILL.md +98 -0
  88. package/skills/carrier-relationship-management/SKILL.md +211 -0
  89. package/skills/cisco-ios-patterns/SKILL.md +163 -0
  90. package/skills/ck/SKILL.md +147 -0
  91. package/skills/ck/commands/forget.mjs +44 -0
  92. package/skills/ck/commands/info.mjs +24 -0
  93. package/skills/ck/commands/init.mjs +143 -0
  94. package/skills/ck/commands/list.mjs +40 -0
  95. package/skills/ck/commands/migrate.mjs +202 -0
  96. package/skills/ck/commands/resume.mjs +36 -0
  97. package/skills/ck/commands/save.mjs +210 -0
  98. package/skills/ck/commands/shared.mjs +387 -0
  99. package/skills/ck/hooks/session-start.mjs +224 -0
  100. package/skills/claude-devfleet/SKILL.md +103 -0
  101. package/skills/click-path-audit/SKILL.md +244 -0
  102. package/skills/clickhouse-io/SKILL.md +438 -0
  103. package/skills/code-tour/SKILL.md +235 -0
  104. package/skills/codebase-onboarding/SKILL.md +232 -0
  105. package/skills/coding-standards/SKILL.md +548 -0
  106. package/skills/compose-multiplatform-patterns/SKILL.md +298 -0
  107. package/skills/connections-optimizer/SKILL.md +188 -0
  108. package/skills/content-engine/SKILL.md +126 -0
  109. package/skills/content-hash-cache-pattern/SKILL.md +160 -0
  110. package/skills/context-budget/SKILL.md +134 -0
  111. package/skills/continuous-agent-loop/SKILL.md +44 -0
  112. package/skills/continuous-learning/SKILL.md +129 -0
  113. package/skills/continuous-learning/config.json +18 -0
  114. package/skills/continuous-learning/evaluate-session.sh +69 -0
  115. package/skills/continuous-learning-v2/SKILL.md +358 -0
  116. package/skills/continuous-learning-v2/agents/observer-loop.sh +322 -0
  117. package/skills/continuous-learning-v2/agents/observer.md +198 -0
  118. package/skills/continuous-learning-v2/agents/session-guardian.sh +150 -0
  119. package/skills/continuous-learning-v2/agents/start-observer.sh +248 -0
  120. package/skills/continuous-learning-v2/config.json +8 -0
  121. package/skills/continuous-learning-v2/hooks/observe.sh +476 -0
  122. package/skills/continuous-learning-v2/scripts/detect-project.sh +288 -0
  123. package/skills/continuous-learning-v2/scripts/instinct-cli.py +1519 -0
  124. package/skills/continuous-learning-v2/scripts/lib/homunculus-dir.sh +31 -0
  125. package/skills/continuous-learning-v2/scripts/migrate-homunculus.sh +62 -0
  126. package/skills/continuous-learning-v2/scripts/test_parse_instinct.py +1018 -0
  127. package/skills/cost-aware-llm-pipeline/SKILL.md +182 -0
  128. package/skills/cost-tracking/SKILL.md +147 -0
  129. package/skills/council/SKILL.md +202 -0
  130. package/skills/cpp-coding-standards/SKILL.md +722 -0
  131. package/skills/cpp-testing/SKILL.md +323 -0
  132. package/skills/crosspost/SKILL.md +110 -0
  133. package/skills/csharp-testing/SKILL.md +320 -0
  134. package/skills/customer-billing-ops/SKILL.md +139 -0
  135. package/skills/customs-trade-compliance/SKILL.md +262 -0
  136. package/skills/dart-flutter-patterns/SKILL.md +562 -0
  137. package/skills/dashboard-builder/SKILL.md +108 -0
  138. package/skills/data-scraper-agent/SKILL.md +764 -0
  139. package/skills/database-migrations/SKILL.md +428 -0
  140. package/skills/deep-research/SKILL.md +158 -0
  141. package/skills/defi-amm-security/SKILL.md +166 -0
  142. package/skills/deployment-patterns/SKILL.md +426 -0
  143. package/skills/design-system/SKILL.md +81 -0
  144. package/skills/django-celery/SKILL.md +456 -0
  145. package/skills/django-patterns/SKILL.md +733 -0
  146. package/skills/django-security/SKILL.md +592 -0
  147. package/skills/django-tdd/SKILL.md +728 -0
  148. package/skills/django-verification/SKILL.md +468 -0
  149. package/skills/dmux-workflows/SKILL.md +190 -0
  150. package/skills/docker-patterns/SKILL.md +363 -0
  151. package/skills/documentation-lookup/SKILL.md +89 -0
  152. package/skills/dotnet-patterns/SKILL.md +320 -0
  153. package/skills/e2e-testing/SKILL.md +325 -0
  154. package/skills/email-ops/SKILL.md +120 -0
  155. package/skills/energy-procurement/SKILL.md +227 -0
  156. package/skills/enterprise-agent-ops/SKILL.md +49 -0
  157. package/skills/error-handling/SKILL.md +375 -0
  158. package/skills/eval-harness/SKILL.md +269 -0
  159. package/skills/evm-token-decimals/SKILL.md +130 -0
  160. package/skills/exa-search/SKILL.md +106 -0
  161. package/skills/fal-ai-media/SKILL.md +287 -0
  162. package/skills/fastapi-patterns/SKILL.md +327 -0
  163. package/skills/finance-billing-ops/SKILL.md +126 -0
  164. package/skills/flox-environments/SKILL.md +496 -0
  165. package/skills/flutter-dart-code-review/SKILL.md +434 -0
  166. package/skills/foundation-models-on-device/SKILL.md +243 -0
  167. package/skills/frontend-design-direction/SKILL.md +92 -0
  168. package/skills/frontend-patterns/SKILL.md +641 -0
  169. package/skills/frontend-slides/SKILL.md +183 -0
  170. package/skills/frontend-slides/STYLE_PRESETS.md +330 -0
  171. package/skills/frontend-slides/animation-patterns.md +122 -0
  172. package/skills/frontend-slides/html-template.md +419 -0
  173. package/skills/frontend-slides/scripts/export-pdf.sh +418 -0
  174. package/skills/frontend-slides/scripts/extract-pptx.py +96 -0
  175. package/skills/frontend-slides/viewport-base.css +153 -0
  176. package/skills/fsharp-testing/SKILL.md +279 -0
  177. package/skills/gan-style-harness/SKILL.md +278 -0
  178. package/skills/gateguard/SKILL.md +125 -0
  179. package/skills/git-workflow/SKILL.md +714 -0
  180. package/skills/github-ops/SKILL.md +143 -0
  181. package/skills/golang-patterns/SKILL.md +673 -0
  182. package/skills/golang-testing/SKILL.md +719 -0
  183. package/skills/google-workspace-ops/SKILL.md +94 -0
  184. package/skills/healthcare-cdss-patterns/SKILL.md +245 -0
  185. package/skills/healthcare-emr-patterns/SKILL.md +159 -0
  186. package/skills/healthcare-eval-harness/SKILL.md +207 -0
  187. package/skills/healthcare-phi-compliance/SKILL.md +145 -0
  188. package/skills/hermes-imports/SKILL.md +87 -0
  189. package/skills/hexagonal-architecture/SKILL.md +275 -0
  190. package/skills/hipaa-compliance/SKILL.md +78 -0
  191. package/skills/homelab-network-readiness/SKILL.md +169 -0
  192. package/skills/homelab-network-setup/SKILL.md +129 -0
  193. package/skills/homelab-pihole-dns/SKILL.md +274 -0
  194. package/skills/homelab-vlan-segmentation/SKILL.md +311 -0
  195. package/skills/homelab-wireguard-vpn/SKILL.md +305 -0
  196. package/skills/hookify-rules/SKILL.md +128 -0
  197. package/skills/inventory-demand-planning/SKILL.md +246 -0
  198. package/skills/investor-materials/SKILL.md +95 -0
  199. package/skills/investor-outreach/SKILL.md +90 -0
  200. package/skills/ios-icon-gen/SKILL.md +157 -0
  201. package/skills/ios-icon-gen/scripts/generate_icons.swift +258 -0
  202. package/skills/ios-icon-gen/scripts/iconify_gen.sh +235 -0
  203. package/skills/iterative-retrieval/SKILL.md +209 -0
  204. package/skills/java-coding-standards/SKILL.md +382 -0
  205. package/skills/jira-integration/SKILL.md +292 -0
  206. package/skills/jpa-patterns/SKILL.md +150 -0
  207. package/skills/knowledge-ops/SKILL.md +153 -0
  208. package/skills/kotlin-coroutines-flows/SKILL.md +283 -0
  209. package/skills/kotlin-exposed-patterns/SKILL.md +718 -0
  210. package/skills/kotlin-ktor-patterns/SKILL.md +688 -0
  211. package/skills/kotlin-patterns/SKILL.md +710 -0
  212. package/skills/kotlin-testing/SKILL.md +823 -0
  213. package/skills/laravel-patterns/SKILL.md +414 -0
  214. package/skills/laravel-plugin-discovery/SKILL.md +228 -0
  215. package/skills/laravel-security/SKILL.md +284 -0
  216. package/skills/laravel-tdd/SKILL.md +282 -0
  217. package/skills/laravel-verification/SKILL.md +178 -0
  218. package/skills/lead-intelligence/SKILL.md +320 -0
  219. package/skills/lead-intelligence/agents/enrichment-agent.md +85 -0
  220. package/skills/lead-intelligence/agents/mutual-mapper.md +75 -0
  221. package/skills/lead-intelligence/agents/outreach-drafter.md +98 -0
  222. package/skills/lead-intelligence/agents/signal-scorer.md +60 -0
  223. package/skills/liquid-glass-design/SKILL.md +279 -0
  224. package/skills/llm-trading-agent-security/SKILL.md +146 -0
  225. package/skills/logistics-exception-management/SKILL.md +221 -0
  226. package/skills/make-interfaces-feel-better/SKILL.md +151 -0
  227. package/skills/manim-video/SKILL.md +88 -0
  228. package/skills/manim-video/assets/network_graph_scene.py +52 -0
  229. package/skills/market-research/SKILL.md +74 -0
  230. package/skills/mcp-server-patterns/SKILL.md +68 -0
  231. package/skills/messages-ops/SKILL.md +103 -0
  232. package/skills/mle-workflow/SKILL.md +345 -0
  233. package/skills/motion-advanced/SKILL.md +596 -0
  234. package/skills/motion-foundations/SKILL.md +299 -0
  235. package/skills/motion-patterns/SKILL.md +435 -0
  236. package/skills/motion-ui/SKILL.md +574 -0
  237. package/skills/mysql-patterns/SKILL.md +411 -0
  238. package/skills/nanoclaw-repl/SKILL.md +32 -0
  239. package/skills/nestjs-patterns/SKILL.md +229 -0
  240. package/skills/netmiko-ssh-automation/SKILL.md +173 -0
  241. package/skills/network-bgp-diagnostics/SKILL.md +167 -0
  242. package/skills/network-config-validation/SKILL.md +210 -0
  243. package/skills/network-interface-health/SKILL.md +152 -0
  244. package/skills/nextjs-turbopack/SKILL.md +43 -0
  245. package/skills/nodejs-keccak256/SKILL.md +102 -0
  246. package/skills/nutrient-document-processing/SKILL.md +166 -0
  247. package/skills/nuxt4-patterns/SKILL.md +99 -0
  248. package/skills/openclaw-persona-forge/SKILL.md +288 -0
  249. package/skills/openclaw-persona-forge/gacha.py +224 -0
  250. package/skills/openclaw-persona-forge/gacha.sh +5 -0
  251. package/skills/openclaw-persona-forge/references/avatar-style.md +124 -0
  252. package/skills/openclaw-persona-forge/references/boundary-rules.md +53 -0
  253. package/skills/openclaw-persona-forge/references/error-handling.md +53 -0
  254. package/skills/openclaw-persona-forge/references/identity-tension.md +48 -0
  255. package/skills/openclaw-persona-forge/references/naming-system.md +39 -0
  256. package/skills/openclaw-persona-forge/references/output-template.md +166 -0
  257. package/skills/opensource-pipeline/SKILL.md +254 -0
  258. package/skills/perl-patterns/SKILL.md +503 -0
  259. package/skills/perl-security/SKILL.md +502 -0
  260. package/skills/perl-testing/SKILL.md +474 -0
  261. package/skills/plan-orchestrate/SKILL.md +253 -0
  262. package/skills/plankton-code-quality/SKILL.md +236 -0
  263. package/skills/postgres-patterns/SKILL.md +146 -0
  264. package/skills/product-capability/SKILL.md +140 -0
  265. package/skills/product-lens/SKILL.md +91 -0
  266. package/skills/production-audit/SKILL.md +206 -0
  267. package/skills/production-scheduling/SKILL.md +237 -0
  268. package/skills/project-flow-ops/SKILL.md +110 -0
  269. package/skills/prompt-optimizer/SKILL.md +398 -0
  270. package/skills/python-patterns/SKILL.md +749 -0
  271. package/skills/python-testing/SKILL.md +815 -0
  272. package/skills/pytorch-patterns/SKILL.md +395 -0
  273. package/skills/quality-nonconformance/SKILL.md +259 -0
  274. package/skills/quarkus-patterns/SKILL.md +721 -0
  275. package/skills/quarkus-security/SKILL.md +466 -0
  276. package/skills/quarkus-tdd/SKILL.md +810 -0
  277. package/skills/quarkus-verification/SKILL.md +478 -0
  278. package/skills/ralphinho-rfc-pipeline/SKILL.md +66 -0
  279. package/skills/redis-patterns/SKILL.md +402 -0
  280. package/skills/regex-vs-llm-structured-text/SKILL.md +219 -0
  281. package/skills/remotion-video-creation/SKILL.md +43 -0
  282. package/skills/remotion-video-creation/rules/3d.md +86 -0
  283. package/skills/remotion-video-creation/rules/animations.md +29 -0
  284. package/skills/remotion-video-creation/rules/assets/charts-bar-chart.tsx +173 -0
  285. package/skills/remotion-video-creation/rules/assets/text-animations-typewriter.tsx +100 -0
  286. package/skills/remotion-video-creation/rules/assets/text-animations-word-highlight.tsx +108 -0
  287. package/skills/remotion-video-creation/rules/assets.md +78 -0
  288. package/skills/remotion-video-creation/rules/audio.md +172 -0
  289. package/skills/remotion-video-creation/rules/calculate-metadata.md +104 -0
  290. package/skills/remotion-video-creation/rules/can-decode.md +75 -0
  291. package/skills/remotion-video-creation/rules/charts.md +58 -0
  292. package/skills/remotion-video-creation/rules/compositions.md +146 -0
  293. package/skills/remotion-video-creation/rules/display-captions.md +126 -0
  294. package/skills/remotion-video-creation/rules/extract-frames.md +229 -0
  295. package/skills/remotion-video-creation/rules/fonts.md +152 -0
  296. package/skills/remotion-video-creation/rules/get-audio-duration.md +58 -0
  297. package/skills/remotion-video-creation/rules/get-video-dimensions.md +68 -0
  298. package/skills/remotion-video-creation/rules/get-video-duration.md +58 -0
  299. package/skills/remotion-video-creation/rules/gifs.md +138 -0
  300. package/skills/remotion-video-creation/rules/images.md +130 -0
  301. package/skills/remotion-video-creation/rules/import-srt-captions.md +67 -0
  302. package/skills/remotion-video-creation/rules/lottie.md +67 -0
  303. package/skills/remotion-video-creation/rules/measuring-dom-nodes.md +34 -0
  304. package/skills/remotion-video-creation/rules/measuring-text.md +143 -0
  305. package/skills/remotion-video-creation/rules/sequencing.md +106 -0
  306. package/skills/remotion-video-creation/rules/tailwind.md +11 -0
  307. package/skills/remotion-video-creation/rules/text-animations.md +20 -0
  308. package/skills/remotion-video-creation/rules/timing.md +179 -0
  309. package/skills/remotion-video-creation/rules/transcribe-captions.md +19 -0
  310. package/skills/remotion-video-creation/rules/transitions.md +122 -0
  311. package/skills/remotion-video-creation/rules/trimming.md +52 -0
  312. package/skills/remotion-video-creation/rules/videos.md +171 -0
  313. package/skills/repo-scan/SKILL.md +78 -0
  314. package/skills/research-ops/SKILL.md +111 -0
  315. package/skills/returns-reverse-logistics/SKILL.md +239 -0
  316. package/skills/rules-distill/SKILL.md +263 -0
  317. package/skills/rules-distill/scripts/scan-rules.sh +58 -0
  318. package/skills/rules-distill/scripts/scan-skills.sh +129 -0
  319. package/skills/rust-patterns/SKILL.md +498 -0
  320. package/skills/rust-testing/SKILL.md +499 -0
  321. package/skills/safety-guard/SKILL.md +74 -0
  322. package/skills/santa-method/SKILL.md +306 -0
  323. package/skills/scientific-db-pubmed-database/SKILL.md +175 -0
  324. package/skills/scientific-db-uspto-database/SKILL.md +177 -0
  325. package/skills/scientific-pkg-gget/SKILL.md +166 -0
  326. package/skills/scientific-thinking-literature-review/SKILL.md +192 -0
  327. package/skills/scientific-thinking-scholar-evaluation/SKILL.md +160 -0
  328. package/skills/search-first/SKILL.md +181 -0
  329. package/skills/security-bounty-hunter/SKILL.md +99 -0
  330. package/skills/security-review/SKILL.md +502 -0
  331. package/skills/security-review/cloud-infrastructure-security.md +361 -0
  332. package/skills/seo/SKILL.md +153 -0
  333. package/skills/skill-comply/SKILL.md +57 -0
  334. package/skills/skill-comply/fixtures/compliant_trace.jsonl +5 -0
  335. package/skills/skill-comply/fixtures/noncompliant_trace.jsonl +3 -0
  336. package/skills/skill-comply/fixtures/tdd_spec.yaml +44 -0
  337. package/skills/skill-comply/prompts/classifier.md +24 -0
  338. package/skills/skill-comply/prompts/scenario_generator.md +62 -0
  339. package/skills/skill-comply/prompts/spec_generator.md +42 -0
  340. package/skills/skill-comply/pyproject.toml +15 -0
  341. package/skills/skill-comply/scripts/__init__.py +0 -0
  342. package/skills/skill-comply/scripts/classifier.py +85 -0
  343. package/skills/skill-comply/scripts/grader.py +124 -0
  344. package/skills/skill-comply/scripts/parser.py +107 -0
  345. package/skills/skill-comply/scripts/report.py +170 -0
  346. package/skills/skill-comply/scripts/run.py +127 -0
  347. package/skills/skill-comply/scripts/runner.py +186 -0
  348. package/skills/skill-comply/scripts/scenario_generator.py +70 -0
  349. package/skills/skill-comply/scripts/spec_generator.py +72 -0
  350. package/skills/skill-comply/scripts/utils.py +13 -0
  351. package/skills/skill-comply/tests/test_grader.py +197 -0
  352. package/skills/skill-comply/tests/test_parser.py +90 -0
  353. package/skills/skill-comply/tests/test_runner.py +172 -0
  354. package/skills/skill-scout/SKILL.md +139 -0
  355. package/skills/skill-stocktake/SKILL.md +193 -0
  356. package/skills/skill-stocktake/scripts/quick-diff.sh +87 -0
  357. package/skills/skill-stocktake/scripts/save-results.sh +56 -0
  358. package/skills/skill-stocktake/scripts/scan.sh +170 -0
  359. package/skills/social-graph-ranker/SKILL.md +153 -0
  360. package/skills/springboot-patterns/SKILL.md +313 -0
  361. package/skills/springboot-security/SKILL.md +271 -0
  362. package/skills/springboot-tdd/SKILL.md +157 -0
  363. package/skills/springboot-verification/SKILL.md +230 -0
  364. package/skills/strategic-compact/SKILL.md +129 -0
  365. package/skills/strategic-compact/suggest-compact.sh +54 -0
  366. package/skills/swift-actor-persistence/SKILL.md +142 -0
  367. package/skills/swift-concurrency-6-2/SKILL.md +216 -0
  368. package/skills/swift-protocol-di-testing/SKILL.md +189 -0
  369. package/skills/swiftui-patterns/SKILL.md +259 -0
  370. package/skills/tdd-workflow/SKILL.md +462 -0
  371. package/skills/team-builder/SKILL.md +166 -0
  372. package/skills/terminal-ops/SKILL.md +108 -0
  373. package/skills/tinystruct-patterns/SKILL.md +130 -0
  374. package/skills/tinystruct-patterns/references/architecture.md +77 -0
  375. package/skills/tinystruct-patterns/references/data-handling.md +35 -0
  376. package/skills/tinystruct-patterns/references/routing.md +57 -0
  377. package/skills/tinystruct-patterns/references/system-usage.md +74 -0
  378. package/skills/tinystruct-patterns/references/testing.md +59 -0
  379. package/skills/token-budget-advisor/SKILL.md +133 -0
  380. package/skills/ui-demo/SKILL.md +464 -0
  381. package/skills/ui-to-vue/SKILL.md +134 -0
  382. package/skills/unified-notifications-ops/SKILL.md +186 -0
  383. package/skills/verification-loop/SKILL.md +125 -0
  384. package/skills/video-editing/SKILL.md +309 -0
  385. package/skills/videodb/SKILL.md +373 -0
  386. package/skills/videodb/reference/api-reference.md +550 -0
  387. package/skills/videodb/reference/capture-reference.md +407 -0
  388. package/skills/videodb/reference/capture.md +101 -0
  389. package/skills/videodb/reference/editor.md +443 -0
  390. package/skills/videodb/reference/generative.md +331 -0
  391. package/skills/videodb/reference/rtstream-reference.md +564 -0
  392. package/skills/videodb/reference/rtstream.md +65 -0
  393. package/skills/videodb/reference/search.md +230 -0
  394. package/skills/videodb/reference/streaming.md +406 -0
  395. package/skills/videodb/reference/use-cases.md +118 -0
  396. package/skills/videodb/scripts/ws_listener.py +282 -0
  397. package/skills/visa-doc-translate/README.md +86 -0
  398. package/skills/visa-doc-translate/SKILL.md +117 -0
  399. package/skills/vite-patterns/SKILL.md +448 -0
  400. package/skills/windows-desktop-e2e/SKILL.md +787 -0
  401. package/skills/workspace-surface-audit/SKILL.md +124 -0
  402. package/skills/x-api/SKILL.md +233 -0
@@ -0,0 +1,610 @@
1
+ """
2
+ MCP server for skillforge.
3
+
4
+ Exposes skill routing as MCP tools so MCP-aware clients (Claude Desktop,
5
+ Claude Code, Cursor, etc.) can use the orchestrator without running the
6
+ HTTP server.
7
+
8
+ Tools exposed:
9
+ route_skills / skillforge_bootstrap — routing (+ optional project materialize).
10
+ materialize_project — .cursor/rules, docs/SKILLFORGE-PRD.md, CLAUDE.md block.
11
+ list_skills, skill_feedback, skill_referenced, disable_skill.
12
+
13
+ Run as: python -m app.mcp_server
14
+ Speaks MCP over stdio (the protocol's standard transport for local servers).
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import asyncio
19
+ import json
20
+ import os
21
+ import sqlite3
22
+ import sys
23
+ import time
24
+ from pathlib import Path
25
+
26
+ from app.db_paths import resolve_orchestrator_db
27
+ from app.main import (
28
+ build_router_and_skills,
29
+ init_db,
30
+ load_all_skills,
31
+ log_event,
32
+ run_route_turn,
33
+ set_skill_disabled,
34
+ skill_catalog_manifest,
35
+ update_skill_stat,
36
+ Router,
37
+ )
38
+ from app.materialize import materialize_project_files
39
+
40
+
41
+ def _env_truthy(name: str, default: str = "1") -> bool:
42
+ return os.getenv(name, default).strip().lower() not in ("0", "false", "no", "")
43
+
44
+
45
+ def _hot_reload_enabled() -> bool:
46
+ return _env_truthy("SKILLFORGE_SKILL_HOT_RELOAD", "1")
47
+
48
+
49
+ def _watch_interval_sec() -> float:
50
+ raw = os.getenv("SKILLFORGE_WATCH_SKILLS_INTERVAL", "30")
51
+ try:
52
+ return float(raw)
53
+ except ValueError:
54
+ return 30.0
55
+
56
+
57
+ def _mcp_tools_list_changed_capability() -> bool:
58
+ """Advertise listChanged only when we run a background poll that may emit notifications."""
59
+ return (
60
+ _hot_reload_enabled()
61
+ and _env_truthy("SKILLFORGE_MCP_LIST_CHANGED", "1")
62
+ and _watch_interval_sec() > 0
63
+ )
64
+
65
+
66
+ class MCPServer:
67
+ """Minimal MCP server speaking JSON-RPC 2.0 over stdio.
68
+
69
+ Implements just the methods needed for a tool server:
70
+ - initialize
71
+ - tools/list
72
+ - tools/call
73
+ - notifications/initialized (no-op)
74
+ """
75
+
76
+ def __init__(self):
77
+ self.skills = None
78
+ self.router = None
79
+ self.initialized = False
80
+ self._catalog_manifest: tuple[tuple[str, int], ...] | None = None
81
+ self._reload_lock: asyncio.Lock | None = None
82
+ self._db_cache: dict[str, sqlite3.Connection] = {}
83
+
84
+ def _mcp_user_id(self, args: dict) -> str:
85
+ """Per-tool user namespace for weights/sessions/events (aligned with HTTP bearer user id)."""
86
+ raw = (
87
+ args.get("user_id")
88
+ or os.getenv("SKILLFORGE_MCP_USER_ID", "")
89
+ or ""
90
+ )
91
+ return str(raw).strip()
92
+
93
+ def _project_root_from_args(self, args: dict) -> str | None:
94
+ raw = args.get("project_root")
95
+ if raw is not None and str(raw).strip():
96
+ return str(raw).strip()
97
+ env = os.getenv("SKILLFORGE_PROJECT_ROOT", "").strip()
98
+ return env or None
99
+
100
+ def _get_con(self, args: dict):
101
+ path = resolve_orchestrator_db(self._project_root_from_args(args))
102
+ key = str(path)
103
+ if key not in self._db_cache:
104
+ self._db_cache[key] = init_db(path)
105
+ return self._db_cache[key]
106
+
107
+ async def setup(self):
108
+ if self._reload_lock is None:
109
+ self._reload_lock = asyncio.Lock()
110
+
111
+ self.router, self.skills = await asyncio.to_thread(
112
+ build_router_and_skills, log=True, log_prefix="[skillforge-mcp]"
113
+ )
114
+ self._catalog_manifest = skill_catalog_manifest()
115
+ if _hot_reload_enabled():
116
+ interval = _watch_interval_sec()
117
+ if interval > 0:
118
+ asyncio.create_task(self._watch_skills_poll_loop(interval))
119
+
120
+ def _reload_catalog_sync(self):
121
+ skills = load_all_skills()
122
+ embed_model = self.router.embed_model
123
+ anthropic = self.router.anthropic
124
+ self.router = Router(skills, embed_model, anthropic)
125
+ self.skills = {s.name: s for s in skills}
126
+ print(f"[skillforge-mcp] Hot-reloaded {len(skills)} skills", file=sys.stderr)
127
+
128
+ def _emit_tools_list_changed(self):
129
+ if not _mcp_tools_list_changed_capability():
130
+ return
131
+ note = {
132
+ "jsonrpc": "2.0",
133
+ "method": "notifications/tools/list_changed",
134
+ "params": {},
135
+ }
136
+ sys.stdout.write(json.dumps(note) + "\n")
137
+ sys.stdout.flush()
138
+
139
+ async def _reload_if_stale(self, *, emit_notification: bool) -> bool:
140
+ if not _hot_reload_enabled() or self.router is None:
141
+ return False
142
+ m = skill_catalog_manifest()
143
+ if m == self._catalog_manifest:
144
+ return False
145
+ if self._reload_lock is None:
146
+ self._reload_lock = asyncio.Lock()
147
+ async with self._reload_lock:
148
+ m2 = skill_catalog_manifest()
149
+ if m2 == self._catalog_manifest:
150
+ return False
151
+ await asyncio.to_thread(self._reload_catalog_sync)
152
+ self._catalog_manifest = skill_catalog_manifest()
153
+ if emit_notification:
154
+ self._emit_tools_list_changed()
155
+ return True
156
+
157
+ async def _watch_skills_poll_loop(self, interval: float):
158
+ while True:
159
+ await asyncio.sleep(interval)
160
+ try:
161
+ if not _hot_reload_enabled():
162
+ return
163
+ await self._reload_if_stale(emit_notification=True)
164
+ except Exception as e:
165
+ print(f"[skillforge-mcp] watch skills: {e}", file=sys.stderr)
166
+
167
+ # ---- MCP handlers ----
168
+
169
+ def handle_initialize(self, params):
170
+ caps: dict = {"tools": {}}
171
+ if _mcp_tools_list_changed_capability():
172
+ caps["tools"]["listChanged"] = True
173
+ return {
174
+ "protocolVersion": "2024-11-05",
175
+ "capabilities": caps,
176
+ "serverInfo": {"name": "skillforge", "version": "0.2.1"},
177
+ }
178
+
179
+ def handle_tools_list(self, params):
180
+ return {
181
+ "tools": [
182
+ {
183
+ "name": "route_skills",
184
+ "description": (
185
+ "Route the user's prompt to the most relevant skills from the catalog "
186
+ "and return their full SKILL.md bodies. The client should inject the "
187
+ "returned content into the LLM's context. Returns up to 7 skills. "
188
+ "Pass project_root (workspace path) for per-repo SQLite in .skillforge/ "
189
+ "and learning; else use env SKILLFORGE_PROJECT_ROOT or global data dir. "
190
+ "Optional session_id for reroute stats; optional user_id for multi-user."
191
+ ),
192
+ "inputSchema": {
193
+ "type": "object",
194
+ "properties": {
195
+ "prompt": {"type": "string", "description": "The user's prompt or task description"},
196
+ "project_root": {
197
+ "type": "string",
198
+ "description": "Repo/workspace root — stores orchestrator state in .skillforge/",
199
+ },
200
+ "conversation": {
201
+ "type": "array",
202
+ "description": "Optional recent messages for context",
203
+ "items": {"type": "object"},
204
+ },
205
+ "session_id": {
206
+ "type": "string",
207
+ "description": "Stable id for this chat; reuse across turns for reroute detection",
208
+ },
209
+ "user_id": {
210
+ "type": "string",
211
+ "description": "Logical user id for weights/sessions/events (same as HTTP user id string)",
212
+ },
213
+ },
214
+ "required": ["prompt"],
215
+ },
216
+ },
217
+ {
218
+ "name": "list_skills",
219
+ "description": (
220
+ "List all available skills with descriptions and usage stats. "
221
+ "Optional project_root (or SKILLFORGE_PROJECT_ROOT) selects per-repo DB; else global."
222
+ ),
223
+ "inputSchema": {
224
+ "type": "object",
225
+ "properties": {
226
+ "project_root": {"type": "string"},
227
+ "user_id": {"type": "string"},
228
+ },
229
+ },
230
+ },
231
+ {
232
+ "name": "skill_feedback",
233
+ "description": (
234
+ "Report thumbs up (+1) or down (-1) for a skill, feeding the learning loop. "
235
+ "Use after observing whether a routed skill actually helped."
236
+ ),
237
+ "inputSchema": {
238
+ "type": "object",
239
+ "properties": {
240
+ "skill_name": {"type": "string"},
241
+ "thumbs": {"type": "integer", "enum": [-1, 1]},
242
+ "project_root": {"type": "string"},
243
+ "user_id": {"type": "string"},
244
+ "session_id": {"type": "string", "description": "Optional; stored in events when set"},
245
+ },
246
+ "required": ["skill_name", "thumbs"],
247
+ },
248
+ },
249
+ {
250
+ "name": "disable_skill",
251
+ "description": "Disable (or re-enable) a skill from being routed to.",
252
+ "inputSchema": {
253
+ "type": "object",
254
+ "properties": {
255
+ "skill_name": {"type": "string"},
256
+ "disabled": {"type": "boolean"},
257
+ "project_root": {"type": "string"},
258
+ "user_id": {"type": "string"},
259
+ },
260
+ "required": ["skill_name", "disabled"],
261
+ },
262
+ },
263
+ {
264
+ "name": "skill_referenced",
265
+ "description": (
266
+ "Record that a routed skill was used in the model output (updates referenced count + weight). "
267
+ "Call when the assistant clearly applied a skill returned by route_skills."
268
+ ),
269
+ "inputSchema": {
270
+ "type": "object",
271
+ "properties": {
272
+ "skill_name": {"type": "string"},
273
+ "project_root": {"type": "string"},
274
+ "user_id": {"type": "string"},
275
+ },
276
+ "required": ["skill_name"],
277
+ },
278
+ },
279
+ {
280
+ "name": "materialize_project",
281
+ "description": (
282
+ "Write project-local Skillforge files: .cursor/rules/skillforge.mdc, "
283
+ "docs/SKILLFORGE-PRD.md, and a CLAUDE.md section. "
284
+ "Pass project_root (workspace path) and skill_names from route_skills. "
285
+ "Hosts must supply project_root; MCP does not infer cwd."
286
+ ),
287
+ "inputSchema": {
288
+ "type": "object",
289
+ "properties": {
290
+ "project_root": {"type": "string", "description": "Absolute or relative path to the repo root"},
291
+ "skill_names": {
292
+ "type": "array",
293
+ "items": {"type": "string"},
294
+ "description": "Skill names from the last route_skills result",
295
+ },
296
+ "merge": {
297
+ "type": "boolean",
298
+ "description": "If false and .cursor/rules/skillforge.mdc exists, skip overwriting that file",
299
+ "default": True,
300
+ },
301
+ },
302
+ "required": ["project_root", "skill_names"],
303
+ },
304
+ },
305
+ {
306
+ "name": "skillforge_bootstrap",
307
+ "description": (
308
+ "One-shot: route_skills for the prompt, then materialize_project into project_root. "
309
+ "Same args as route_skills plus project_root and optional merge."
310
+ ),
311
+ "inputSchema": {
312
+ "type": "object",
313
+ "properties": {
314
+ "prompt": {"type": "string"},
315
+ "project_root": {"type": "string"},
316
+ "conversation": {"type": "array", "items": {"type": "object"}},
317
+ "session_id": {"type": "string"},
318
+ "user_id": {"type": "string"},
319
+ "merge": {"type": "boolean", "default": True},
320
+ },
321
+ "required": ["prompt", "project_root"],
322
+ },
323
+ },
324
+ ]
325
+ }
326
+
327
+ async def handle_tools_call(self, params):
328
+ name = params.get("name")
329
+ args = params.get("arguments", {})
330
+
331
+ if name == "route_skills":
332
+ return await self._tool_route_skills(args)
333
+ if name == "list_skills":
334
+ return self._tool_list_skills(args)
335
+ if name == "skill_feedback":
336
+ return self._tool_skill_feedback(args)
337
+ if name == "disable_skill":
338
+ return self._tool_disable_skill(args)
339
+ if name == "skill_referenced":
340
+ return self._tool_skill_referenced(args)
341
+ if name == "materialize_project":
342
+ return self._tool_materialize_project(args)
343
+ if name == "skillforge_bootstrap":
344
+ return await self._tool_skillforge_bootstrap(args)
345
+ raise ValueError(f"Unknown tool: {name}")
346
+
347
+ async def _tool_route_skills(self, args):
348
+ prompt = args.get("prompt", "")
349
+ conversation = args.get("conversation", [])
350
+ session_id = args.get("session_id") or None
351
+ user_id = self._mcp_user_id(args)
352
+ if not prompt.strip():
353
+ return {"content": [{"type": "text", "text": "No prompt provided."}]}
354
+ con = self._get_con(args)
355
+ result = await run_route_turn(
356
+ con,
357
+ self.router,
358
+ prompt,
359
+ conversation,
360
+ user_id=user_id,
361
+ session_id=session_id,
362
+ )
363
+ picked_names = result["picked_names"]
364
+ reasoning = result["reasoning"]
365
+ pr = self._project_root_from_args(args)
366
+ db_path = resolve_orchestrator_db(pr)
367
+ if pr:
368
+ try:
369
+ d = Path(pr).expanduser().resolve() / ".skillforge"
370
+ d.mkdir(parents=True, exist_ok=True)
371
+ snap = {
372
+ "ts": time.time(),
373
+ "session_id": result["session_id"],
374
+ "picked": picked_names,
375
+ "reasoning": reasoning,
376
+ "route_ms": round(result["route_ms"], 1),
377
+ "user_id": user_id,
378
+ }
379
+ (d / "last_route.json").write_text(json.dumps(snap, indent=2), encoding="utf-8")
380
+ except OSError:
381
+ pass
382
+
383
+ # Build response: a header explaining what was loaded, then the skill bodies
384
+ blocks = [
385
+ f"# Skillforge — routed {len(picked_names)} skill(s)",
386
+ f"_DB:_ `{db_path}`",
387
+ f"_Reasoning: {reasoning}_" if reasoning else "",
388
+ "",
389
+ ]
390
+ for n in picked_names:
391
+ s = self.skills.get(n)
392
+ if s:
393
+ blocks.append(f"---\n## Skill: {s.name}\n\n{s.body}\n")
394
+ if not picked_names:
395
+ blocks.append("_No skills matched this prompt closely enough to load._")
396
+ return {
397
+ "content": [{"type": "text", "text": "\n".join(b for b in blocks if b is not None)}],
398
+ "_meta": {
399
+ "picked": picked_names,
400
+ "reasoning": reasoning,
401
+ "session_id": result["session_id"],
402
+ "user_id": user_id,
403
+ "rerouted": result["rerouted"],
404
+ "change_pct": round(result["change"] * 100, 1),
405
+ "route_ms": round(result["route_ms"], 1),
406
+ "orchestrator_db": str(db_path),
407
+ },
408
+ }
409
+
410
+ def _tool_list_skills(self, args):
411
+ user_id = self._mcp_user_id(args)
412
+ con = self._get_con(args)
413
+ out = []
414
+ for name, s in sorted(self.skills.items()):
415
+ cur = con.execute(
416
+ "SELECT uses, referenced, thumbs_up, thumbs_down, disabled FROM skill_weights "
417
+ "WHERE user_id = ? AND skill_name = ?",
418
+ (user_id, name),
419
+ )
420
+ row = cur.fetchone()
421
+ uses, ref, up, down, disabled = row if row else (0, 0, 0, 0, 0)
422
+ out.append({
423
+ "name": name,
424
+ "description": s.description[:200],
425
+ "source": s.source,
426
+ "uses": uses,
427
+ "thumbs": up - down,
428
+ "disabled": bool(disabled),
429
+ })
430
+ # Format as readable text for MCP clients
431
+ lines = [f"# Skills ({len(out)} total)\n"]
432
+ for sk in out:
433
+ flag = " [DISABLED]" if sk["disabled"] else ""
434
+ lines.append(f"**{sk['name']}**{flag} ({sk['source']}, used {sk['uses']}x): {sk['description']}")
435
+ return {"content": [{"type": "text", "text": "\n".join(lines)}]}
436
+
437
+ def _tool_skill_feedback(self, args):
438
+ name = args.get("skill_name")
439
+ thumbs = args.get("thumbs", 0)
440
+ user_id = self._mcp_user_id(args)
441
+ session_id = args.get("session_id") or ""
442
+ con = self._get_con(args)
443
+ if name not in self.skills:
444
+ return {"content": [{"type": "text", "text": f"Unknown skill: {name}"}], "isError": True}
445
+ field = "thumbs_up" if thumbs > 0 else "thumbs_down"
446
+ update_skill_stat(con, name, field, 1, user_id=user_id)
447
+ if session_id:
448
+ log_event(
449
+ con,
450
+ session_id,
451
+ "feedback",
452
+ {"skill": name, "thumbs": thumbs},
453
+ user_id=user_id,
454
+ )
455
+ return {"content": [{"type": "text", "text": f"Recorded {'👍' if thumbs > 0 else '👎'} for {name}"}]}
456
+
457
+ def _tool_disable_skill(self, args):
458
+ name = args.get("skill_name")
459
+ disabled = args.get("disabled", False)
460
+ user_id = self._mcp_user_id(args)
461
+ con = self._get_con(args)
462
+ if name not in self.skills:
463
+ return {"content": [{"type": "text", "text": f"Unknown skill: {name}"}], "isError": True}
464
+ set_skill_disabled(con, name, disabled, user_id=user_id)
465
+ return {"content": [{"type": "text", "text": f"{'Disabled' if disabled else 'Enabled'} {name}"}]}
466
+
467
+ def _tool_skill_referenced(self, args):
468
+ name = args.get("skill_name")
469
+ user_id = self._mcp_user_id(args)
470
+ con = self._get_con(args)
471
+ if name not in self.skills:
472
+ return {"content": [{"type": "text", "text": f"Unknown skill: {name}"}], "isError": True}
473
+ update_skill_stat(con, name, "referenced", 1, user_id=user_id)
474
+ return {"content": [{"type": "text", "text": f"Recorded reference for {name}"}]}
475
+
476
+ def _tool_materialize_project(self, args):
477
+ root = (args.get("project_root") or "").strip()
478
+ names_raw = args.get("skill_names") or []
479
+ merge = args.get("merge", True)
480
+ if not root:
481
+ return {
482
+ "content": [{"type": "text", "text": "project_root is required."}],
483
+ "isError": True,
484
+ }
485
+ if not isinstance(names_raw, list):
486
+ names_raw = []
487
+ valid = [n for n in names_raw if isinstance(n, str) and n in self.skills]
488
+ desc = {n: self.skills[n].description for n in valid}
489
+ try:
490
+ out = materialize_project_files(root, valid, desc, merge=bool(merge))
491
+ except ValueError as e:
492
+ return {"content": [{"type": "text", "text": str(e)}], "isError": True}
493
+ lines = ["# Skillforge — materialized project files", "", "Written:", *[f"- {p}" for p in out["written"]], ""]
494
+ return {"content": [{"type": "text", "text": "\n".join(lines)}], "_meta": out}
495
+
496
+ async def _tool_skillforge_bootstrap(self, args):
497
+ prompt = args.get("prompt", "")
498
+ root = (args.get("project_root") or "").strip()
499
+ conversation = args.get("conversation", [])
500
+ session_id = args.get("session_id") or None
501
+ user_id = self._mcp_user_id(args)
502
+ merge = args.get("merge", True)
503
+ if not prompt.strip():
504
+ return {"content": [{"type": "text", "text": "No prompt provided."}], "isError": True}
505
+ if not root:
506
+ return {
507
+ "content": [{"type": "text", "text": "project_root is required."}],
508
+ "isError": True,
509
+ }
510
+ route = await self._tool_route_skills(
511
+ {
512
+ "prompt": prompt,
513
+ "project_root": root,
514
+ "conversation": conversation,
515
+ "session_id": session_id,
516
+ "user_id": user_id,
517
+ }
518
+ )
519
+ if route.get("isError"):
520
+ return route
521
+ picked = (route.get("_meta") or {}).get("picked") or []
522
+ mat = self._tool_materialize_project(
523
+ {"project_root": root, "skill_names": picked, "merge": merge}
524
+ )
525
+ if mat.get("isError"):
526
+ return mat
527
+ body = [
528
+ route["content"][0]["text"],
529
+ "---",
530
+ mat["content"][0]["text"],
531
+ ]
532
+ return {
533
+ "content": [{"type": "text", "text": "\n".join(body)}],
534
+ "_meta": {
535
+ "route": route.get("_meta"),
536
+ "materialize": mat.get("_meta"),
537
+ },
538
+ }
539
+
540
+ # ---- JSON-RPC dispatcher ----
541
+
542
+ async def dispatch(self, request):
543
+ method = request.get("method")
544
+ params = request.get("params", {})
545
+ req_id = request.get("id")
546
+
547
+ try:
548
+ if method == "initialize":
549
+ result = self.handle_initialize(params)
550
+ elif method == "notifications/initialized":
551
+ # Notification, no response expected. Now finish heavy setup.
552
+ if not self.initialized:
553
+ self.initialized = True
554
+ await self.setup()
555
+ return None
556
+ elif method == "tools/list":
557
+ if not self.initialized:
558
+ await self.setup()
559
+ self.initialized = True
560
+ await self._reload_if_stale(emit_notification=False)
561
+ result = self.handle_tools_list(params)
562
+ elif method == "tools/call":
563
+ if not self.initialized:
564
+ await self.setup()
565
+ self.initialized = True
566
+ await self._reload_if_stale(emit_notification=False)
567
+ result = await self.handle_tools_call(params)
568
+ else:
569
+ if req_id is None:
570
+ return None
571
+ return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32601, "message": f"Method not found: {method}"}}
572
+
573
+ if req_id is None:
574
+ return None # notification
575
+ return {"jsonrpc": "2.0", "id": req_id, "result": result}
576
+ except Exception as e:
577
+ if req_id is None:
578
+ return None
579
+ return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32603, "message": str(e)}}
580
+
581
+
582
+ async def main():
583
+ server = MCPServer()
584
+ loop = asyncio.get_event_loop()
585
+ reader = asyncio.StreamReader()
586
+ protocol = asyncio.StreamReaderProtocol(reader)
587
+ await loop.connect_read_pipe(lambda: protocol, sys.stdin)
588
+
589
+ while True:
590
+ try:
591
+ line = await reader.readline()
592
+ except Exception:
593
+ break
594
+ if not line:
595
+ break
596
+ try:
597
+ request = json.loads(line.decode().strip())
598
+ except json.JSONDecodeError:
599
+ continue
600
+ response = await server.dispatch(request)
601
+ if response is not None:
602
+ sys.stdout.write(json.dumps(response) + "\n")
603
+ sys.stdout.flush()
604
+
605
+
606
+ if __name__ == "__main__":
607
+ try:
608
+ asyncio.run(main())
609
+ except KeyboardInterrupt:
610
+ pass