@booklib/core 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (374) hide show
  1. package/.cursor/rules/booklib-standards.mdc +40 -0
  2. package/.gemini/context.md +372 -0
  3. package/AGENTS.md +166 -0
  4. package/CHANGELOG.md +226 -0
  5. package/CLAUDE.md +81 -0
  6. package/CODE_OF_CONDUCT.md +31 -0
  7. package/CONTRIBUTING.md +304 -0
  8. package/LICENSE +21 -0
  9. package/PLAN.md +28 -0
  10. package/README.ja.md +198 -0
  11. package/README.ko.md +198 -0
  12. package/README.md +503 -0
  13. package/README.pt-BR.md +198 -0
  14. package/README.uk.md +241 -0
  15. package/README.zh-CN.md +198 -0
  16. package/SECURITY.md +9 -0
  17. package/agents/architecture-reviewer.md +136 -0
  18. package/agents/booklib-reviewer.md +90 -0
  19. package/agents/data-reviewer.md +107 -0
  20. package/agents/jvm-reviewer.md +146 -0
  21. package/agents/python-reviewer.md +128 -0
  22. package/agents/rust-reviewer.md +115 -0
  23. package/agents/ts-reviewer.md +110 -0
  24. package/agents/ui-reviewer.md +117 -0
  25. package/assets/logo.svg +36 -0
  26. package/bin/booklib-mcp.js +304 -0
  27. package/bin/booklib.js +1705 -0
  28. package/bin/skills.cjs +1292 -0
  29. package/booklib-router.mdc +36 -0
  30. package/booklib.config.json +19 -0
  31. package/commands/animation-at-work.md +10 -0
  32. package/commands/clean-code-reviewer.md +10 -0
  33. package/commands/data-intensive-patterns.md +10 -0
  34. package/commands/data-pipelines.md +10 -0
  35. package/commands/design-patterns.md +10 -0
  36. package/commands/domain-driven-design.md +10 -0
  37. package/commands/effective-java.md +10 -0
  38. package/commands/effective-kotlin.md +10 -0
  39. package/commands/effective-python.md +10 -0
  40. package/commands/effective-typescript.md +10 -0
  41. package/commands/kotlin-in-action.md +10 -0
  42. package/commands/lean-startup.md +10 -0
  43. package/commands/microservices-patterns.md +10 -0
  44. package/commands/programming-with-rust.md +10 -0
  45. package/commands/refactoring-ui.md +10 -0
  46. package/commands/rust-in-action.md +10 -0
  47. package/commands/skill-router.md +10 -0
  48. package/commands/spring-boot-in-action.md +10 -0
  49. package/commands/storytelling-with-data.md +10 -0
  50. package/commands/system-design-interview.md +10 -0
  51. package/commands/using-asyncio-python.md +10 -0
  52. package/commands/web-scraping-python.md +10 -0
  53. package/community/registry.json +1616 -0
  54. package/hooks/hooks.json +23 -0
  55. package/hooks/posttooluse-capture.mjs +67 -0
  56. package/hooks/suggest.js +153 -0
  57. package/lib/agent-behaviors.js +40 -0
  58. package/lib/agent-detector.js +96 -0
  59. package/lib/config-loader.js +39 -0
  60. package/lib/conflict-resolver.js +148 -0
  61. package/lib/context-builder.js +574 -0
  62. package/lib/discovery-engine.js +298 -0
  63. package/lib/doctor/hook-installer.js +83 -0
  64. package/lib/doctor/usage-tracker.js +87 -0
  65. package/lib/engine/ai-features.js +253 -0
  66. package/lib/engine/auditor.js +103 -0
  67. package/lib/engine/bm25-index.js +178 -0
  68. package/lib/engine/capture.js +120 -0
  69. package/lib/engine/corrections.js +198 -0
  70. package/lib/engine/doctor.js +195 -0
  71. package/lib/engine/graph-injector.js +137 -0
  72. package/lib/engine/graph.js +161 -0
  73. package/lib/engine/handoff.js +405 -0
  74. package/lib/engine/indexer.js +242 -0
  75. package/lib/engine/parser.js +53 -0
  76. package/lib/engine/query-expander.js +42 -0
  77. package/lib/engine/reranker.js +40 -0
  78. package/lib/engine/rrf.js +59 -0
  79. package/lib/engine/scanner.js +151 -0
  80. package/lib/engine/searcher.js +139 -0
  81. package/lib/engine/session-coordinator.js +306 -0
  82. package/lib/engine/session-manager.js +429 -0
  83. package/lib/engine/synthesizer.js +70 -0
  84. package/lib/installer.js +70 -0
  85. package/lib/instinct-block.js +33 -0
  86. package/lib/mcp-config-writer.js +88 -0
  87. package/lib/paths.js +57 -0
  88. package/lib/profiles/design.md +19 -0
  89. package/lib/profiles/general.md +16 -0
  90. package/lib/profiles/research-analysis.md +22 -0
  91. package/lib/profiles/software-development.md +23 -0
  92. package/lib/profiles/writing-content.md +19 -0
  93. package/lib/project-initializer.js +916 -0
  94. package/lib/registry/skills.js +102 -0
  95. package/lib/registry-searcher.js +99 -0
  96. package/lib/rules/rules-manager.js +169 -0
  97. package/lib/skill-fetcher.js +333 -0
  98. package/lib/well-known-builder.js +70 -0
  99. package/lib/wizard/index.js +404 -0
  100. package/lib/wizard/integration-detector.js +41 -0
  101. package/lib/wizard/project-detector.js +100 -0
  102. package/lib/wizard/prompt.js +156 -0
  103. package/lib/wizard/registry-embeddings.js +107 -0
  104. package/lib/wizard/skill-recommender.js +69 -0
  105. package/llms-full.txt +254 -0
  106. package/llms.txt +70 -0
  107. package/package.json +45 -0
  108. package/research-reports/2026-04-01-current-architecture.md +160 -0
  109. package/research-reports/IDEAS.md +93 -0
  110. package/rules/common/clean-code.md +42 -0
  111. package/rules/java/effective-java.md +42 -0
  112. package/rules/kotlin/effective-kotlin.md +37 -0
  113. package/rules/python/effective-python.md +38 -0
  114. package/rules/rust/rust.md +37 -0
  115. package/rules/typescript/effective-typescript.md +42 -0
  116. package/scripts/gen-llms-full.mjs +36 -0
  117. package/scripts/gen-og.mjs +142 -0
  118. package/scripts/validate-frontmatter.js +25 -0
  119. package/skills/animation-at-work/SKILL.md +270 -0
  120. package/skills/animation-at-work/assets/example_asset.txt +1 -0
  121. package/skills/animation-at-work/evals/evals.json +44 -0
  122. package/skills/animation-at-work/evals/results.json +13 -0
  123. package/skills/animation-at-work/examples/after.md +64 -0
  124. package/skills/animation-at-work/examples/before.md +35 -0
  125. package/skills/animation-at-work/references/api_reference.md +369 -0
  126. package/skills/animation-at-work/references/review-checklist.md +79 -0
  127. package/skills/animation-at-work/scripts/audit_animations.py +295 -0
  128. package/skills/animation-at-work/scripts/example.py +1 -0
  129. package/skills/clean-code-reviewer/SKILL.md +444 -0
  130. package/skills/clean-code-reviewer/audit.json +35 -0
  131. package/skills/clean-code-reviewer/evals/evals.json +185 -0
  132. package/skills/clean-code-reviewer/evals/results.json +13 -0
  133. package/skills/clean-code-reviewer/examples/after.md +48 -0
  134. package/skills/clean-code-reviewer/examples/before.md +33 -0
  135. package/skills/clean-code-reviewer/references/api_reference.md +158 -0
  136. package/skills/clean-code-reviewer/references/practices-catalog.md +282 -0
  137. package/skills/clean-code-reviewer/references/review-checklist.md +254 -0
  138. package/skills/clean-code-reviewer/scripts/pre-review.py +206 -0
  139. package/skills/data-intensive-patterns/SKILL.md +267 -0
  140. package/skills/data-intensive-patterns/assets/example_asset.txt +1 -0
  141. package/skills/data-intensive-patterns/evals/evals.json +54 -0
  142. package/skills/data-intensive-patterns/evals/results.json +13 -0
  143. package/skills/data-intensive-patterns/examples/after.md +61 -0
  144. package/skills/data-intensive-patterns/examples/before.md +38 -0
  145. package/skills/data-intensive-patterns/references/api_reference.md +34 -0
  146. package/skills/data-intensive-patterns/references/patterns-catalog.md +551 -0
  147. package/skills/data-intensive-patterns/references/review-checklist.md +193 -0
  148. package/skills/data-intensive-patterns/scripts/adr.py +213 -0
  149. package/skills/data-intensive-patterns/scripts/example.py +1 -0
  150. package/skills/data-pipelines/SKILL.md +259 -0
  151. package/skills/data-pipelines/assets/example_asset.txt +1 -0
  152. package/skills/data-pipelines/evals/evals.json +45 -0
  153. package/skills/data-pipelines/evals/results.json +13 -0
  154. package/skills/data-pipelines/examples/after.md +97 -0
  155. package/skills/data-pipelines/examples/before.md +37 -0
  156. package/skills/data-pipelines/references/api_reference.md +301 -0
  157. package/skills/data-pipelines/references/review-checklist.md +181 -0
  158. package/skills/data-pipelines/scripts/example.py +1 -0
  159. package/skills/data-pipelines/scripts/new_pipeline.py +444 -0
  160. package/skills/design-patterns/SKILL.md +271 -0
  161. package/skills/design-patterns/assets/example_asset.txt +1 -0
  162. package/skills/design-patterns/evals/evals.json +46 -0
  163. package/skills/design-patterns/evals/results.json +13 -0
  164. package/skills/design-patterns/examples/after.md +52 -0
  165. package/skills/design-patterns/examples/before.md +29 -0
  166. package/skills/design-patterns/references/api_reference.md +1 -0
  167. package/skills/design-patterns/references/patterns-catalog.md +726 -0
  168. package/skills/design-patterns/references/review-checklist.md +173 -0
  169. package/skills/design-patterns/scripts/example.py +1 -0
  170. package/skills/design-patterns/scripts/scaffold.py +807 -0
  171. package/skills/domain-driven-design/SKILL.md +142 -0
  172. package/skills/domain-driven-design/assets/example_asset.txt +1 -0
  173. package/skills/domain-driven-design/evals/evals.json +48 -0
  174. package/skills/domain-driven-design/evals/results.json +13 -0
  175. package/skills/domain-driven-design/examples/after.md +80 -0
  176. package/skills/domain-driven-design/examples/before.md +43 -0
  177. package/skills/domain-driven-design/references/api_reference.md +1 -0
  178. package/skills/domain-driven-design/references/patterns-catalog.md +545 -0
  179. package/skills/domain-driven-design/references/review-checklist.md +158 -0
  180. package/skills/domain-driven-design/scripts/example.py +1 -0
  181. package/skills/domain-driven-design/scripts/scaffold.py +421 -0
  182. package/skills/effective-java/SKILL.md +227 -0
  183. package/skills/effective-java/assets/example_asset.txt +1 -0
  184. package/skills/effective-java/evals/evals.json +46 -0
  185. package/skills/effective-java/evals/results.json +13 -0
  186. package/skills/effective-java/examples/after.md +83 -0
  187. package/skills/effective-java/examples/before.md +37 -0
  188. package/skills/effective-java/references/api_reference.md +1 -0
  189. package/skills/effective-java/references/items-catalog.md +955 -0
  190. package/skills/effective-java/references/review-checklist.md +216 -0
  191. package/skills/effective-java/scripts/checkstyle_setup.py +211 -0
  192. package/skills/effective-java/scripts/example.py +1 -0
  193. package/skills/effective-kotlin/SKILL.md +271 -0
  194. package/skills/effective-kotlin/assets/example_asset.txt +1 -0
  195. package/skills/effective-kotlin/audit.json +29 -0
  196. package/skills/effective-kotlin/evals/evals.json +45 -0
  197. package/skills/effective-kotlin/evals/results.json +13 -0
  198. package/skills/effective-kotlin/examples/after.md +36 -0
  199. package/skills/effective-kotlin/examples/before.md +38 -0
  200. package/skills/effective-kotlin/references/api_reference.md +1 -0
  201. package/skills/effective-kotlin/references/practices-catalog.md +1228 -0
  202. package/skills/effective-kotlin/references/review-checklist.md +126 -0
  203. package/skills/effective-kotlin/scripts/example.py +1 -0
  204. package/skills/effective-python/SKILL.md +441 -0
  205. package/skills/effective-python/evals/evals.json +44 -0
  206. package/skills/effective-python/evals/results.json +13 -0
  207. package/skills/effective-python/examples/after.md +56 -0
  208. package/skills/effective-python/examples/before.md +40 -0
  209. package/skills/effective-python/ref-01-pythonic-thinking.md +202 -0
  210. package/skills/effective-python/ref-02-lists-and-dicts.md +146 -0
  211. package/skills/effective-python/ref-03-functions.md +186 -0
  212. package/skills/effective-python/ref-04-comprehensions-generators.md +211 -0
  213. package/skills/effective-python/ref-05-classes-interfaces.md +188 -0
  214. package/skills/effective-python/ref-06-metaclasses-attributes.md +209 -0
  215. package/skills/effective-python/ref-07-concurrency.md +213 -0
  216. package/skills/effective-python/ref-08-robustness-performance.md +248 -0
  217. package/skills/effective-python/ref-09-testing-debugging.md +253 -0
  218. package/skills/effective-python/ref-10-collaboration.md +175 -0
  219. package/skills/effective-python/references/api_reference.md +218 -0
  220. package/skills/effective-python/references/practices-catalog.md +483 -0
  221. package/skills/effective-python/references/review-checklist.md +190 -0
  222. package/skills/effective-python/scripts/lint.py +173 -0
  223. package/skills/effective-typescript/SKILL.md +262 -0
  224. package/skills/effective-typescript/audit.json +29 -0
  225. package/skills/effective-typescript/evals/evals.json +37 -0
  226. package/skills/effective-typescript/evals/results.json +13 -0
  227. package/skills/effective-typescript/examples/after.md +70 -0
  228. package/skills/effective-typescript/examples/before.md +47 -0
  229. package/skills/effective-typescript/references/api_reference.md +118 -0
  230. package/skills/effective-typescript/references/practices-catalog.md +371 -0
  231. package/skills/effective-typescript/scripts/review.py +169 -0
  232. package/skills/kotlin-in-action/SKILL.md +261 -0
  233. package/skills/kotlin-in-action/assets/example_asset.txt +1 -0
  234. package/skills/kotlin-in-action/evals/evals.json +43 -0
  235. package/skills/kotlin-in-action/evals/results.json +13 -0
  236. package/skills/kotlin-in-action/examples/after.md +53 -0
  237. package/skills/kotlin-in-action/examples/before.md +39 -0
  238. package/skills/kotlin-in-action/references/api_reference.md +1 -0
  239. package/skills/kotlin-in-action/references/practices-catalog.md +436 -0
  240. package/skills/kotlin-in-action/references/review-checklist.md +204 -0
  241. package/skills/kotlin-in-action/scripts/example.py +1 -0
  242. package/skills/kotlin-in-action/scripts/setup_detekt.py +224 -0
  243. package/skills/lean-startup/SKILL.md +160 -0
  244. package/skills/lean-startup/assets/example_asset.txt +1 -0
  245. package/skills/lean-startup/evals/evals.json +43 -0
  246. package/skills/lean-startup/evals/results.json +13 -0
  247. package/skills/lean-startup/examples/after.md +80 -0
  248. package/skills/lean-startup/examples/before.md +34 -0
  249. package/skills/lean-startup/references/api_reference.md +319 -0
  250. package/skills/lean-startup/references/review-checklist.md +137 -0
  251. package/skills/lean-startup/scripts/example.py +1 -0
  252. package/skills/lean-startup/scripts/new_experiment.py +286 -0
  253. package/skills/microservices-patterns/SKILL.md +384 -0
  254. package/skills/microservices-patterns/evals/evals.json +45 -0
  255. package/skills/microservices-patterns/evals/results.json +13 -0
  256. package/skills/microservices-patterns/examples/after.md +69 -0
  257. package/skills/microservices-patterns/examples/before.md +40 -0
  258. package/skills/microservices-patterns/references/patterns-catalog.md +391 -0
  259. package/skills/microservices-patterns/references/review-checklist.md +169 -0
  260. package/skills/microservices-patterns/scripts/new_service.py +583 -0
  261. package/skills/programming-with-rust/SKILL.md +209 -0
  262. package/skills/programming-with-rust/evals/evals.json +37 -0
  263. package/skills/programming-with-rust/evals/results.json +13 -0
  264. package/skills/programming-with-rust/examples/after.md +107 -0
  265. package/skills/programming-with-rust/examples/before.md +59 -0
  266. package/skills/programming-with-rust/references/api_reference.md +152 -0
  267. package/skills/programming-with-rust/references/practices-catalog.md +335 -0
  268. package/skills/programming-with-rust/scripts/review.py +142 -0
  269. package/skills/refactoring-ui/SKILL.md +362 -0
  270. package/skills/refactoring-ui/assets/example_asset.txt +1 -0
  271. package/skills/refactoring-ui/evals/evals.json +45 -0
  272. package/skills/refactoring-ui/evals/results.json +13 -0
  273. package/skills/refactoring-ui/examples/after.md +85 -0
  274. package/skills/refactoring-ui/examples/before.md +58 -0
  275. package/skills/refactoring-ui/references/api_reference.md +355 -0
  276. package/skills/refactoring-ui/references/review-checklist.md +114 -0
  277. package/skills/refactoring-ui/scripts/audit_css.py +250 -0
  278. package/skills/refactoring-ui/scripts/example.py +1 -0
  279. package/skills/rust-in-action/SKILL.md +350 -0
  280. package/skills/rust-in-action/evals/evals.json +38 -0
  281. package/skills/rust-in-action/evals/results.json +13 -0
  282. package/skills/rust-in-action/examples/after.md +156 -0
  283. package/skills/rust-in-action/examples/before.md +56 -0
  284. package/skills/rust-in-action/references/practices-catalog.md +346 -0
  285. package/skills/rust-in-action/scripts/review.py +147 -0
  286. package/skills/skill-router/SKILL.md +186 -0
  287. package/skills/skill-router/evals/evals.json +38 -0
  288. package/skills/skill-router/evals/results.json +13 -0
  289. package/skills/skill-router/examples/after.md +63 -0
  290. package/skills/skill-router/examples/before.md +39 -0
  291. package/skills/skill-router/references/api_reference.md +24 -0
  292. package/skills/skill-router/references/routing-heuristics.md +89 -0
  293. package/skills/skill-router/references/skill-catalog.md +174 -0
  294. package/skills/skill-router/scripts/route.py +266 -0
  295. package/skills/spring-boot-in-action/SKILL.md +340 -0
  296. package/skills/spring-boot-in-action/evals/evals.json +39 -0
  297. package/skills/spring-boot-in-action/evals/results.json +13 -0
  298. package/skills/spring-boot-in-action/examples/after.md +185 -0
  299. package/skills/spring-boot-in-action/examples/before.md +84 -0
  300. package/skills/spring-boot-in-action/references/practices-catalog.md +403 -0
  301. package/skills/spring-boot-in-action/scripts/review.py +184 -0
  302. package/skills/storytelling-with-data/SKILL.md +241 -0
  303. package/skills/storytelling-with-data/assets/example_asset.txt +1 -0
  304. package/skills/storytelling-with-data/evals/evals.json +47 -0
  305. package/skills/storytelling-with-data/evals/results.json +13 -0
  306. package/skills/storytelling-with-data/examples/after.md +50 -0
  307. package/skills/storytelling-with-data/examples/before.md +33 -0
  308. package/skills/storytelling-with-data/references/api_reference.md +379 -0
  309. package/skills/storytelling-with-data/references/review-checklist.md +111 -0
  310. package/skills/storytelling-with-data/scripts/chart_review.py +301 -0
  311. package/skills/storytelling-with-data/scripts/example.py +1 -0
  312. package/skills/system-design-interview/SKILL.md +233 -0
  313. package/skills/system-design-interview/assets/example_asset.txt +1 -0
  314. package/skills/system-design-interview/evals/evals.json +46 -0
  315. package/skills/system-design-interview/evals/results.json +13 -0
  316. package/skills/system-design-interview/examples/after.md +94 -0
  317. package/skills/system-design-interview/examples/before.md +27 -0
  318. package/skills/system-design-interview/references/api_reference.md +582 -0
  319. package/skills/system-design-interview/references/review-checklist.md +201 -0
  320. package/skills/system-design-interview/scripts/example.py +1 -0
  321. package/skills/system-design-interview/scripts/new_design.py +421 -0
  322. package/skills/using-asyncio-python/SKILL.md +290 -0
  323. package/skills/using-asyncio-python/assets/example_asset.txt +1 -0
  324. package/skills/using-asyncio-python/evals/evals.json +43 -0
  325. package/skills/using-asyncio-python/evals/results.json +13 -0
  326. package/skills/using-asyncio-python/examples/after.md +68 -0
  327. package/skills/using-asyncio-python/examples/before.md +39 -0
  328. package/skills/using-asyncio-python/references/api_reference.md +267 -0
  329. package/skills/using-asyncio-python/references/review-checklist.md +149 -0
  330. package/skills/using-asyncio-python/scripts/check_blocking.py +270 -0
  331. package/skills/using-asyncio-python/scripts/example.py +1 -0
  332. package/skills/web-scraping-python/SKILL.md +280 -0
  333. package/skills/web-scraping-python/assets/example_asset.txt +1 -0
  334. package/skills/web-scraping-python/evals/evals.json +46 -0
  335. package/skills/web-scraping-python/evals/results.json +13 -0
  336. package/skills/web-scraping-python/examples/after.md +109 -0
  337. package/skills/web-scraping-python/examples/before.md +40 -0
  338. package/skills/web-scraping-python/references/api_reference.md +393 -0
  339. package/skills/web-scraping-python/references/review-checklist.md +163 -0
  340. package/skills/web-scraping-python/scripts/example.py +1 -0
  341. package/skills/web-scraping-python/scripts/new_scraper.py +231 -0
  342. package/skills/writing-plans/audit.json +34 -0
  343. package/tests/agent-detector.test.js +83 -0
  344. package/tests/corrections.test.js +245 -0
  345. package/tests/doctor/hook-installer.test.js +72 -0
  346. package/tests/doctor/usage-tracker.test.js +140 -0
  347. package/tests/engine/benchmark-eval.test.js +31 -0
  348. package/tests/engine/bm25-index.test.js +85 -0
  349. package/tests/engine/capture-command.test.js +35 -0
  350. package/tests/engine/capture.test.js +17 -0
  351. package/tests/engine/graph-augmented-search.test.js +107 -0
  352. package/tests/engine/graph-injector.test.js +44 -0
  353. package/tests/engine/graph.test.js +216 -0
  354. package/tests/engine/hybrid-searcher.test.js +74 -0
  355. package/tests/engine/indexer-bm25.test.js +37 -0
  356. package/tests/engine/mcp-tools.test.js +73 -0
  357. package/tests/engine/project-initializer-mcp.test.js +99 -0
  358. package/tests/engine/query-expander.test.js +36 -0
  359. package/tests/engine/reranker.test.js +51 -0
  360. package/tests/engine/rrf.test.js +49 -0
  361. package/tests/engine/srag-prefix.test.js +47 -0
  362. package/tests/instinct-block.test.js +23 -0
  363. package/tests/mcp-config-writer.test.js +60 -0
  364. package/tests/project-initializer-new-agents.test.js +48 -0
  365. package/tests/rules/rules-manager.test.js +230 -0
  366. package/tests/well-known-builder.test.js +40 -0
  367. package/tests/wizard/integration-detector.test.js +31 -0
  368. package/tests/wizard/project-detector.test.js +51 -0
  369. package/tests/wizard/prompt-session.test.js +61 -0
  370. package/tests/wizard/prompt.test.js +16 -0
  371. package/tests/wizard/registry-embeddings.test.js +35 -0
  372. package/tests/wizard/skill-recommender.test.js +34 -0
  373. package/tests/wizard/slot-count.test.js +25 -0
  374. package/vercel.json +21 -0
@@ -0,0 +1,333 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+ import https from 'https';
5
+ import { execSync } from 'child_process';
6
+ import { fileURLToPath } from 'url';
7
+ import { resolveBookLibPaths } from './paths.js';
8
+ import { BookLibIndexer } from './engine/indexer.js';
9
+
10
+ /**
11
+ * Thrown when a skill source is not trusted and no `onPrompt` handler is provided
12
+ * (or the user declined the prompt).
13
+ */
14
+ export class RequiresConfirmationError extends Error {
15
+ constructor(skill) {
16
+ super(`Skill "${skill.name}" requires user confirmation before indexing (untrusted source).`);
17
+ this.skill = skill;
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Fetches a skill from its source (npm or GitHub raw URL) and indexes it locally.
23
+ *
24
+ * Usage:
25
+ * const fetcher = new SkillFetcher();
26
+ * await fetcher.fetch(skillDescriptor, {
27
+ * onPrompt: async (skill) => confirm(`Index "${skill.name}" from ${skill.source.type}?`),
28
+ * });
29
+ */
30
+ export class SkillFetcher {
31
+ constructor(options = {}) {
32
+ this.paths = resolveBookLibPaths(options.projectCwd);
33
+ this.indexer = new BookLibIndexer(this.paths.indexPath);
34
+ }
35
+
36
+ /**
37
+ * Fetches and indexes a single skill.
38
+ *
39
+ * @param {object} skill - Skill descriptor (from SKILL_REGISTRY or DiscoveryEngine)
40
+ * @param {object} opts
41
+ * @param {Function} [opts.onPrompt] - async (skill) => boolean; called for untrusted sources.
42
+ * If omitted and source is untrusted, throws RequiresConfirmationError.
43
+ */
44
+ async fetch(skill, { onPrompt } = {}) {
45
+ if (!skill.trusted) {
46
+ if (typeof onPrompt !== 'function') {
47
+ throw new RequiresConfirmationError(skill);
48
+ }
49
+ const confirmed = await onPrompt(skill);
50
+ if (!confirmed) {
51
+ throw new RequiresConfirmationError(skill);
52
+ }
53
+ }
54
+
55
+ const content = await this._fetchContent(skill);
56
+ const skillDir = this._skillDir(skill);
57
+
58
+ fs.mkdirSync(skillDir, { recursive: true });
59
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), content);
60
+
61
+ // Sync to ~/.claude/skills/ so Claude Code's native Skill tool can use it
62
+ this._syncToClaudeSkills(skill, skillDir);
63
+
64
+ console.log(`Indexing ${skill.name}...`);
65
+ await this.indexer.indexDirectory(skillDir);
66
+ console.log(`${skill.name} is ready.`);
67
+ }
68
+
69
+ /**
70
+ * Returns true if the skill is already fetched locally.
71
+ */
72
+ isCached(skill) {
73
+ return fs.existsSync(path.join(this._skillDir(skill), 'SKILL.md'));
74
+ }
75
+
76
+ // ── Private helpers ────────────────────────────────────────────────────────
77
+
78
+ _skillDir(skill) {
79
+ const folderName = skill.name.replace(/[@/]/g, '_');
80
+ return path.join(this.paths.cachePath, 'skills', folderName);
81
+ }
82
+
83
+ /**
84
+ * Writes a clean SKILL.md to ~/.claude/skills/<name>/ so Claude Code's
85
+ * native Skill tool (and obra/superpowers, ruflo) can load it directly.
86
+ *
87
+ * Strips BookLib's <framework> indexing wrapper — orchestrators expect
88
+ * plain frontmatter + content, not BookLib's internal format.
89
+ * Safe to call multiple times — overwrites only BookLib-managed files.
90
+ */
91
+ _syncToClaudeSkills(skill, skillDir) {
92
+ try {
93
+ const claudeSkillsDir = path.join(os.homedir(), '.claude', 'skills');
94
+ const slugName = skill.name.replace(/["']/g, '').replace(/\s+/g, '-').toLowerCase();
95
+ const destDir = path.join(claudeSkillsDir, slugName);
96
+ const destFile = path.join(destDir, 'SKILL.md');
97
+ const srcFile = path.join(skillDir, 'SKILL.md');
98
+
99
+ if (!fs.existsSync(srcFile)) return;
100
+
101
+ // Don't overwrite user-managed directories (not created by us)
102
+ const markerFile = path.join(destDir, '.booklib');
103
+ if (fs.existsSync(destDir) && !fs.existsSync(markerFile)) return;
104
+
105
+ fs.mkdirSync(destDir, { recursive: true });
106
+
107
+ // Strip BookLib's <framework> wrapper for a clean Skill-tool-compatible file
108
+ const raw = fs.readFileSync(srcFile, 'utf8');
109
+ const clean = this._stripFrameworkWrapper(raw, skill);
110
+ fs.writeFileSync(destFile, clean);
111
+ fs.writeFileSync(markerFile, ''); // marker so we know we own this dir
112
+ } catch {
113
+ // Non-fatal — BookLib still works without the sync
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Removes the ~/.claude/skills/<name> directory written by _syncToClaudeSkills.
119
+ * Only removes BookLib-managed directories (those containing a .booklib marker).
120
+ */
121
+ desyncFromClaudeSkills(skill) {
122
+ try {
123
+ const claudeSkillsDir = path.join(os.homedir(), '.claude', 'skills');
124
+ const slugName = skill.name.replace(/["']/g, '').replace(/\s+/g, '-').toLowerCase();
125
+ const destDir = path.join(claudeSkillsDir, slugName);
126
+ if (!fs.existsSync(path.join(destDir, '.booklib'))) return; // not ours
127
+ fs.rmSync(destDir, { recursive: true, force: true });
128
+ } catch { /* non-fatal */ }
129
+ }
130
+
131
+ /**
132
+ * Extracts clean content from BookLib's internal <framework>-wrapped format.
133
+ * Produces a minimal frontmatter + raw content file suitable for the Skill tool.
134
+ */
135
+ _stripFrameworkWrapper(content, skill) {
136
+ const desc = skill.description ? `description: ${skill.description.replace(/\n/g, ' ').slice(0, 200)}\n` : '';
137
+ const frontmatter = `---\nname: ${skill.name}\n${desc}---\n\n`;
138
+
139
+ // Extract content between <framework>...</framework> if present
140
+ const match = content.match(/<framework>([\s\S]*?)<\/framework>/);
141
+ if (match) return frontmatter + match[1].trim() + '\n';
142
+
143
+ // Already clean (native SKILL.md from github-skills-dir sources) — strip our frontmatter and re-emit
144
+ const withoutHeader = content.replace(/^---[\s\S]*?---\s*\n(#[^\n]+\n(>[^\n]*\n)?)?/, '').trim();
145
+ return frontmatter + withoutHeader + '\n';
146
+ }
147
+
148
+ async _fetchContent(skill) {
149
+ const { source } = skill;
150
+
151
+ let raw;
152
+ if (source.type === 'github') {
153
+ raw = await this._fetchUrl(source.url);
154
+ } else if (source.type === 'npm') {
155
+ raw = await this._fetchFromNpm(source.package, source.path ?? 'SKILL.md');
156
+ } else {
157
+ throw new Error(`Unsupported source type: ${source.type}`);
158
+ }
159
+
160
+ // If content already has BookLib XML structure, return as-is.
161
+ // Otherwise apply Universal Adapter: wrap in <framework> so the parser
162
+ // produces structured chunks instead of one giant blob.
163
+ if (raw.includes('<framework>') || raw.includes('<core_principles>')) {
164
+ return raw;
165
+ }
166
+
167
+ const descFm = skill.description ? `description: ${skill.description.replace(/\n/g, ' ').slice(0, 200)}\n` : '';
168
+ const descBody = skill.description ? `\n> ${skill.description}\n` : '';
169
+ return `---
170
+ name: ${skill.name}
171
+ ${descFm}source: ${source.url ?? source.package}
172
+ stars: ${skill.stars ?? 0}
173
+ ---
174
+
175
+ # ${skill.name}
176
+ ${descBody}
177
+ <framework>
178
+ ${raw}
179
+ </framework>
180
+ `;
181
+ }
182
+
183
+ /**
184
+ * Extracts a file from an npm package tarball without installing node_modules.
185
+ * Uses `npm pack --dry-run` to get the tarball, then `tar` to extract one file.
186
+ */
187
+ _fetchFromNpm(packageName, filePath) {
188
+ return new Promise((resolve, reject) => {
189
+ try {
190
+ const tmpDir = fs.mkdtempSync(path.join(this.paths.cachePath, 'npm-tmp-'));
191
+ const tarball = execSync(`npm pack ${packageName} --quiet`, { cwd: tmpDir }).toString().trim();
192
+ const tarballPath = path.join(tmpDir, tarball);
193
+ const extracted = execSync(
194
+ `tar -xOf "${tarballPath}" "package/${filePath}" 2>/dev/null || tar -xOf "${tarballPath}" "${filePath}"`,
195
+ { cwd: tmpDir }
196
+ ).toString();
197
+ fs.rmSync(tmpDir, { recursive: true, force: true });
198
+ resolve(extracted);
199
+ } catch (err) {
200
+ reject(new Error(`Failed to extract ${filePath} from npm package ${packageName}: ${err.message}`));
201
+ }
202
+ });
203
+ }
204
+
205
+ _fetchUrl(url) {
206
+ return new Promise((resolve, reject) => {
207
+ const options = { headers: { 'User-Agent': 'booklib-fetcher/1.0' } };
208
+ https.get(url, options, res => {
209
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
210
+ return resolve(this._fetchUrl(res.headers.location));
211
+ }
212
+ if (res.statusCode !== 200) {
213
+ return reject(new Error(`HTTP ${res.statusCode} fetching ${url}`));
214
+ }
215
+ let data = '';
216
+ res.on('data', chunk => (data += chunk));
217
+ res.on('end', () => resolve(data));
218
+ }).on('error', reject);
219
+ });
220
+ }
221
+ }
222
+
223
+ // ── Three-tier skill install ─────────────────────────────────────────────────
224
+
225
+ const PACKAGE_ROOT = path.resolve(fileURLToPath(import.meta.url), '..', '..');
226
+
227
+ /**
228
+ * Installs a skill using three-tier lookup:
229
+ * 1. Already installed in ~/.claude/skills/<name>/ with .booklib marker → 'already-installed'
230
+ * 2. Bundled in <packageRoot>/skills/<name>/SKILL.md → install via installBundledSkill → 'installed'
231
+ * 3. Cached in ~/.booklib/skills/<name>/SKILL.md → copy to ~/.claude/skills/ → 'installed'
232
+ * 4. None found → 'not-found'
233
+ *
234
+ * @param {string} skillName - skill directory name
235
+ * @returns {'already-installed' | 'installed' | 'not-found'}
236
+ */
237
+ export function installSkill(skillName) {
238
+ const claudeDir = path.join(os.homedir(), '.claude', 'skills', skillName);
239
+
240
+ // Already installed with BookLib marker
241
+ if (fs.existsSync(path.join(claudeDir, '.booklib'))) return 'already-installed';
242
+
243
+ // Try bundled
244
+ const bundledPath = path.join(PACKAGE_ROOT, 'skills', skillName, 'SKILL.md');
245
+ if (fs.existsSync(bundledPath)) {
246
+ installBundledSkill(skillName);
247
+ return 'installed';
248
+ }
249
+
250
+ // Try cache
251
+ const { cachePath } = resolveBookLibPaths();
252
+ const cachedPath = path.join(cachePath, 'skills', skillName, 'SKILL.md');
253
+ if (fs.existsSync(cachedPath)) {
254
+ fs.mkdirSync(claudeDir, { recursive: true });
255
+ fs.copyFileSync(cachedPath, path.join(claudeDir, 'SKILL.md'));
256
+ fs.writeFileSync(path.join(claudeDir, '.booklib'), '');
257
+ return 'installed';
258
+ }
259
+
260
+ return 'not-found';
261
+ }
262
+
263
+ // ── Slot management ───────────────────────────────────────────────────────────
264
+
265
+ /**
266
+ * Counts ALL skill directories in claudeSkillsDir (BookLib-managed or not).
267
+ * Used for health-check warnings because Claude loads every skill at startup.
268
+ *
269
+ * @param {string} [claudeSkillsDir] - override for testing
270
+ */
271
+ export function countAllSlots(claudeSkillsDir) {
272
+ const dir = claudeSkillsDir ?? path.join(os.homedir(), '.claude', 'skills');
273
+ if (!fs.existsSync(dir)) return 0;
274
+ try {
275
+ return fs.readdirSync(dir, { withFileTypes: true })
276
+ .filter(d => d.isDirectory())
277
+ .length;
278
+ } catch { return 0; }
279
+ }
280
+
281
+ /**
282
+ * Counts BookLib-managed skill slots in claudeSkillsDir.
283
+ * Only counts dirs containing a `.booklib` marker (written by _syncToClaudeSkills).
284
+ *
285
+ * @param {string} [claudeSkillsDir] - override for testing
286
+ */
287
+ export function countInstalledSlots(claudeSkillsDir) {
288
+ const dir = claudeSkillsDir ?? path.join(os.homedir(), '.claude', 'skills');
289
+ if (!fs.existsSync(dir)) return 0;
290
+ try {
291
+ return fs.readdirSync(dir, { withFileTypes: true })
292
+ .filter(d => d.isDirectory() && fs.existsSync(path.join(dir, d.name, '.booklib')))
293
+ .length;
294
+ } catch { return 0; }
295
+ }
296
+
297
+ /**
298
+ * Returns names of all BookLib-managed installed skills.
299
+ *
300
+ * @param {string} [claudeSkillsDir] - override for testing
301
+ */
302
+ export function listInstalledSkillNames(claudeSkillsDir) {
303
+ const dir = claudeSkillsDir ?? path.join(os.homedir(), '.claude', 'skills');
304
+ if (!fs.existsSync(dir)) return [];
305
+ try {
306
+ return fs.readdirSync(dir, { withFileTypes: true })
307
+ .filter(d => d.isDirectory() && fs.existsSync(path.join(dir, d.name, '.booklib')))
308
+ .map(d => d.name);
309
+ } catch { return []; }
310
+ }
311
+
312
+ /**
313
+ * Installs a bundled skill (from the package's skills/ dir) to ~/.claude/skills/.
314
+ * No-ops if the destination exists and is not BookLib-managed.
315
+ *
316
+ * @param {string} skillName - directory name under skills/
317
+ * @param {string} [claudeSkillsDir] - override for testing
318
+ */
319
+ export function installBundledSkill(skillName, claudeSkillsDir) {
320
+ const packageRoot = path.resolve(fileURLToPath(import.meta.url), '..', '..');
321
+ const srcFile = path.join(packageRoot, 'skills', skillName, 'SKILL.md');
322
+ if (!fs.existsSync(srcFile)) throw new Error(`Bundled skill not found: ${skillName}`);
323
+
324
+ const destBase = claudeSkillsDir ?? path.join(os.homedir(), '.claude', 'skills');
325
+ const destDir = path.join(destBase, skillName);
326
+ const marker = path.join(destDir, '.booklib');
327
+
328
+ if (fs.existsSync(destDir) && !fs.existsSync(marker)) return; // user-managed, skip
329
+
330
+ fs.mkdirSync(destDir, { recursive: true });
331
+ fs.copyFileSync(srcFile, path.join(destDir, 'SKILL.md'));
332
+ fs.writeFileSync(marker, '');
333
+ }
@@ -0,0 +1,70 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import matter from 'gray-matter';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const PACKAGE_ROOT = path.resolve(fileURLToPath(import.meta.url), '..', '..');
7
+ const BUNDLED_SKILLS_DIR = path.join(PACKAGE_ROOT, 'skills');
8
+ const DEFAULT_OUT = path.join(PACKAGE_ROOT, 'docs', '.well-known', 'skills', 'default');
9
+
10
+ export class WellKnownBuilder {
11
+ constructor({ outDir = DEFAULT_OUT } = {}) {
12
+ this.outDir = outDir;
13
+ }
14
+
15
+ async build() {
16
+ const skills = this._readBundledSkills();
17
+ const content = this._render(skills);
18
+ fs.mkdirSync(this.outDir, { recursive: true });
19
+ const outPath = path.join(this.outDir, 'skill.md');
20
+ fs.writeFileSync(outPath, content);
21
+ return outPath;
22
+ }
23
+
24
+ _readBundledSkills() {
25
+ return fs.readdirSync(BUNDLED_SKILLS_DIR)
26
+ .map(name => {
27
+ const file = path.join(BUNDLED_SKILLS_DIR, name, 'SKILL.md');
28
+ if (!fs.existsSync(file)) return null;
29
+ const { data } = matter(fs.readFileSync(file, 'utf8'));
30
+ return { name: data.name ?? name, description: data.description ?? '', tags: data.tags ?? [] };
31
+ })
32
+ .filter(Boolean);
33
+ }
34
+
35
+ _render(skills) {
36
+ const list = skills
37
+ .map(s => `- **${s.name}**: ${String(s.description).replace(/\s+/g, ' ').slice(0, 120)}`)
38
+ .join('\n');
39
+
40
+ return `---
41
+ name: booklib-skills
42
+ description: >
43
+ BookLib — curated skills from canonical programming books. Covers Kotlin,
44
+ Python, Java, TypeScript, Rust, architecture, DDD, data-intensive systems,
45
+ UI design, and more. Install individual skills via npx skillsadd booklib-ai/booklib/<name>.
46
+ version: "1.0"
47
+ license: MIT
48
+ tags: [books, knowledge, all-languages, architecture, best-practices]
49
+ ---
50
+
51
+ # BookLib Skills
52
+
53
+ Book knowledge distilled into structured AI skills. Install any skill with:
54
+
55
+ \`\`\`
56
+ npx skillsadd booklib-ai/booklib/<skill-name>
57
+ \`\`\`
58
+
59
+ ## Available Skills
60
+
61
+ ${list}
62
+
63
+ ## Install Everything
64
+
65
+ \`\`\`bash
66
+ npm install -g @booklib/skills && booklib init
67
+ \`\`\`
68
+ `;
69
+ }
70
+ }