@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,23 @@
1
+ {
2
+ "UserPromptSubmit": [
3
+ {
4
+ "hooks": [
5
+ {
6
+ "type": "command",
7
+ "command": "node \"$HOME/.claude/booklib-suggest.js\""
8
+ }
9
+ ]
10
+ }
11
+ ],
12
+ "PostToolUse": [
13
+ {
14
+ "matcher": "WebFetch|WebSearch",
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "node \"${BOOKLIB_ROOT}/hooks/posttooluse-capture.mjs\""
19
+ }
20
+ ]
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ // PostToolUse hook — suggests saving knowledge after WebFetch or WebSearch.
3
+ // Reads tool info from stdin (JSON), writes a hint to stdout when relevant.
4
+
5
+ "use strict";
6
+
7
+ process.exitCode = 0;
8
+
9
+ let input = '';
10
+ process.stdin.setEncoding('utf8');
11
+ process.stdin.on('data', chunk => { input += chunk; });
12
+ process.stdin.on('end', () => {
13
+ let toolName = '';
14
+ let toolInput = {};
15
+
16
+ try {
17
+ const parsed = JSON.parse(input);
18
+ toolName = parsed.tool_name ?? parsed.toolName ?? '';
19
+ toolInput = parsed.tool_input ?? parsed.toolInput ?? {};
20
+ } catch {
21
+ process.exit(0);
22
+ }
23
+
24
+ const captureTools = ['WebFetch', 'WebSearch', 'web_fetch', 'web_search'];
25
+ if (!captureTools.includes(toolName)) process.exit(0);
26
+
27
+ const isSearch = ['WebSearch', 'web_search'].includes(toolName);
28
+ const url = toolInput.url ?? '';
29
+ const query = toolInput.query ?? toolInput.input ?? '';
30
+
31
+ let suggestedTitle;
32
+ let sourceDesc;
33
+
34
+ if (isSearch && query) {
35
+ suggestedTitle = query.slice(0, 60);
36
+ sourceDesc = `search: "${query}"`;
37
+ } else if (url) {
38
+ try {
39
+ const u = new URL(url);
40
+ const lastSegment = u.pathname.split('/').filter(Boolean).pop() ?? '';
41
+ const readable = lastSegment.replace(/[-_]/g, ' ').replace(/\.\w+$/, '').trim();
42
+ suggestedTitle = (readable ? `${readable} (${u.hostname})` : u.hostname).slice(0, 60);
43
+ } catch {
44
+ suggestedTitle = url.slice(0, 60);
45
+ }
46
+ sourceDesc = url;
47
+ } else {
48
+ process.stdout.write('\n[booklib] To save what you found: booklib note "<title>"\n');
49
+ process.exit(0);
50
+ }
51
+
52
+ // Strip shell metacharacters so the displayed command is always safe to copy-paste
53
+ const safeTitle = suggestedTitle.replace(/["$`\\]/g, '');
54
+
55
+ const hint = [
56
+ '',
57
+ `[booklib] Knowledge capture — ${sourceDesc}`,
58
+ ` Save what you found:`,
59
+ ` echo "paste key findings here" | booklib note "${safeTitle}"`,
60
+ ` Or create a research template:`,
61
+ ` booklib research "${safeTitle}"`,
62
+ '',
63
+ ].join('\n');
64
+
65
+ process.stdout.write(hint);
66
+ process.exit(0);
67
+ });
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * booklib-suggest.js
4
+ * Claude Code UserPromptSubmit hook — suggests a relevant booklib skill
5
+ * when the prompt contains a review intent AND a language/domain signal.
6
+ *
7
+ * Install: copy (or symlink) this file to ~/.claude/booklib-suggest.js
8
+ * Hook config (hooks.json):
9
+ * { "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "node \"$HOME/.claude/booklib-suggest.js\"" }] }] }
10
+ */
11
+
12
+ "use strict";
13
+
14
+ process.exitCode = 0;
15
+
16
+ const REVIEW_KEYWORDS = [
17
+ "review", "check", "improve", "refactor", "fix", "audit",
18
+ "analyse", "analyze", "critique", "lint",
19
+ ];
20
+
21
+ const LANGUAGE_SIGNALS = {
22
+ python_async: [".py", "asyncio", "async def", "await "],
23
+ python_scraping: [".py", "python", "beautifulsoup", "scrapy", "requests.get", "web scraping"],
24
+ python: [".py", "python", "def ", "async def", "import ", "asyncio", "beautifulsoup", "scrapy"],
25
+ typescript: [".ts", ".tsx", ".js", "typescript", "interface ", "type ", "const ", "function "],
26
+ java: [".java", "java", "class ", "@override", "public static"],
27
+ kotlin: [".kt", "kotlin", "fun ", "val ", "var ", "data class"],
28
+ rust: [".rs", "rust", "fn ", "impl ", "struct ", "enum ", "let mut"],
29
+ ui_animation: [".css", ".scss", "animation", "transition", "@keyframes", "styled", "tailwind"],
30
+ ui: [".css", ".scss", "animation", "transition", "@keyframes", "styled", "tailwind"],
31
+ data: ["pipeline", "etl", "dataframe", "schema", "migration", "replication"],
32
+ architecture: ["microservice", "aggregate", "bounded context", "saga", "event sourcing"],
33
+ };
34
+
35
+ const SKILL_MAP = {
36
+ python_async: { skill: "/using-asyncio-python", reason: "Async patterns and asyncio best practices in Python" },
37
+ python_scraping: { skill: "/web-scraping-python", reason: "Web scraping techniques and patterns with Python" },
38
+ python: { skill: "/effective-python", reason: "Pythonic idioms and best practices" },
39
+ typescript: { skill: "/effective-typescript", reason: "TypeScript type safety and idiomatic patterns" },
40
+ java: { skill: "/effective-java", reason: "Effective Java patterns and API design" },
41
+ kotlin: { skill: "/effective-kotlin", reason: "Idiomatic Kotlin and best practices" },
42
+ rust: { skill: "/programming-with-rust", reason: "Rust ownership, safety, and idiomatic patterns" },
43
+ ui_animation: { skill: "/animation-at-work", reason: "Web animation principles and best practices" },
44
+ ui: { skill: "/refactoring-ui", reason: "UI design principles and visual hierarchy" },
45
+ data: { skill: "/data-pipelines", reason: "Data pipeline design and ETL best practices" },
46
+ architecture: { skill: "/skill-router", reason: "Routes to the right skill for architecture concerns" },
47
+ };
48
+
49
+ function extractPrompt(raw) {
50
+ if (!raw || raw.trim() === "") return "";
51
+ try {
52
+ const parsed = JSON.parse(raw);
53
+ // Claude Code may send { prompt: "..." } or { message: "..." }
54
+ if (parsed && typeof parsed === "object") {
55
+ return String(parsed.prompt || parsed.message || parsed.text || "");
56
+ }
57
+ } catch (_) {
58
+ // Not JSON — treat as raw prompt text
59
+ }
60
+ return raw;
61
+ }
62
+
63
+ function hasReviewIntent(text) {
64
+ const lower = text.toLowerCase();
65
+ return REVIEW_KEYWORDS.some((kw) => lower.includes(kw));
66
+ }
67
+
68
+ /**
69
+ * Returns the most specific language key that matches, or null.
70
+ * Order matters: more specific keys (python_async, python_scraping) are checked first.
71
+ */
72
+ function detectLanguage(text) {
73
+ const lower = text.toLowerCase();
74
+
75
+ const orderedKeys = [
76
+ "python_async",
77
+ "python_scraping",
78
+ "python",
79
+ "typescript",
80
+ "java",
81
+ "kotlin",
82
+ "rust",
83
+ "ui_animation",
84
+ "ui",
85
+ "data",
86
+ "architecture",
87
+ ];
88
+
89
+ for (const key of orderedKeys) {
90
+ const signals = LANGUAGE_SIGNALS[key];
91
+ // For compound keys, require at least 2 signals to avoid false positives
92
+ // (e.g. python_async needs both a python marker AND an async marker)
93
+ if (key === "python_async") {
94
+ const hasPython = [".py", "python", "def ", "import "].some((s) => lower.includes(s));
95
+ const hasAsync = ["asyncio", "async def", "await "].some((s) => lower.includes(s));
96
+ if (hasPython && hasAsync) return key;
97
+ continue;
98
+ }
99
+ if (key === "python_scraping") {
100
+ const hasPython = [".py", "python", "def ", "import "].some((s) => lower.includes(s));
101
+ const hasScraping = ["beautifulsoup", "scrapy", "web scraping", "requests.get"].some((s) => lower.includes(s));
102
+ if (hasPython && hasScraping) return key;
103
+ continue;
104
+ }
105
+ if (signals.some((s) => lower.includes(s.toLowerCase()))) {
106
+ return key;
107
+ }
108
+ }
109
+
110
+ return null;
111
+ }
112
+
113
+ function main() {
114
+ let raw = "";
115
+ try {
116
+ // Read all of stdin synchronously
117
+ const fd = require("fs").openSync("/dev/stdin", "r");
118
+ const chunks = [];
119
+ const buf = Buffer.alloc(4096);
120
+ let bytesRead;
121
+ while ((bytesRead = require("fs").readSync(fd, buf, 0, buf.length, null)) > 0) {
122
+ chunks.push(buf.slice(0, bytesRead));
123
+ }
124
+ require("fs").closeSync(fd);
125
+ raw = Buffer.concat(chunks).toString("utf8");
126
+ } catch (_) {
127
+ // stdin unavailable or empty — exit silently
128
+ process.exit(0);
129
+ }
130
+
131
+ const prompt = extractPrompt(raw);
132
+ if (!prompt) process.exit(0);
133
+
134
+ if (!hasReviewIntent(prompt)) process.exit(0);
135
+
136
+ const langKey = detectLanguage(prompt);
137
+
138
+ if (!langKey) {
139
+ // Review intent but no specific language — suggest clean-code-reviewer
140
+ process.stdout.write(
141
+ "💡 booklib: try /clean-code-reviewer — General clean code review principles\n"
142
+ );
143
+ process.exit(0);
144
+ }
145
+
146
+ const match = SKILL_MAP[langKey];
147
+ if (!match) process.exit(0);
148
+
149
+ process.stdout.write(`💡 booklib: try ${match.skill} — ${match.reason}\n`);
150
+ process.exit(0);
151
+ }
152
+
153
+ main();
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Returns the agent behaviors markdown block for a given tool target.
3
+ * @param {string} target - Tool target (claude, cursor, copilot, etc.)
4
+ * @returns {string} Markdown block
5
+ */
6
+ export function renderBehaviors(target) {
7
+ const canRunCLI = !['cursor', 'copilot'].includes(target);
8
+
9
+ if (canRunCLI) {
10
+ return `### Code Review
11
+ When reviewing code or suggesting changes, query BookLib for relevant principles:
12
+ \`booklib search "topic relevant to the code"\`
13
+ Cite the skill and principle in your review.
14
+
15
+ ### Knowledge Capture
16
+ When the user discovers a useful pattern or asks to remember something:
17
+ \`booklib capture --title "insight title" --type insight --tags relevant,tags\`
18
+
19
+ ### Search Before Answering
20
+ When asked about best practices, patterns, or "how should I do X":
21
+ First run: \`booklib search "the question"\`
22
+ Use results to ground your answer in the project's active skills.
23
+
24
+ ### Deep Audit
25
+ When asked to thoroughly review a file against a specific skill:
26
+ \`booklib audit <skill-name> <file-path>\`
27
+
28
+ ### Health Check
29
+ When code quality issues accumulate or skills seem irrelevant:
30
+ \`booklib doctor\``;
31
+ }
32
+
33
+ return `### Code Quality Guidance
34
+ When reviewing or generating code, apply the principles from the active skills listed below.
35
+ Focus on the patterns and anti-patterns described in each skill's documentation.
36
+
37
+ ### What These Skills Cover
38
+ Each skill represents distilled knowledge from a specific book or guide.
39
+ When making design decisions, consider which skill's principles are most relevant.`;
40
+ }
@@ -0,0 +1,96 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { execSync } from 'node:child_process';
5
+
6
+ /**
7
+ * Detects which AI coding agents are present in the project or on $PATH.
8
+ * Always returns at least ['claude'].
9
+ */
10
+ export class AgentDetector {
11
+ /**
12
+ * @param {object} opts
13
+ * @param {string} [opts.cwd] - project root to check for config dirs/files
14
+ * @param {boolean} [opts.checkPath] - whether to check $PATH (default true)
15
+ */
16
+ constructor({ cwd = process.cwd(), checkPath = true, home } = {}) {
17
+ this.cwd = cwd;
18
+ this.checkPath = checkPath;
19
+ this.home = home ?? os.homedir();
20
+ }
21
+
22
+ detect() {
23
+ const found = new Set(['claude']); // always present
24
+
25
+ const DIR_SIGNALS = {
26
+ cursor: ['.cursor'],
27
+ 'roo-code': ['.roo'],
28
+ openhands: ['.openhands'],
29
+ junie: ['.junie'],
30
+ goose: ['.goose'],
31
+ letta: ['.letta'],
32
+ windsurf: ['.windsurf'],
33
+ gemini: ['.gemini'],
34
+ };
35
+
36
+ const FILE_SIGNALS = {
37
+ opencode: ['opencode.toml'],
38
+ copilot: ['.github/copilot-instructions.md'],
39
+ };
40
+
41
+ const EXTENSION_SIGNALS = {
42
+ copilot: ['github.copilot', 'github.copilot-chat'],
43
+ };
44
+
45
+ const PATH_SIGNALS = {
46
+ cursor: 'cursor',
47
+ codex: 'codex',
48
+ windsurf: 'windsurf',
49
+ gemini: 'gemini',
50
+ goose: 'goose',
51
+ opencode: 'opencode',
52
+ };
53
+
54
+ for (const [agent, dirs] of Object.entries(DIR_SIGNALS)) {
55
+ if (dirs.some(dir => fs.existsSync(path.join(this.cwd, dir)))) {
56
+ found.add(agent);
57
+ }
58
+ }
59
+
60
+ for (const [agent, files] of Object.entries(FILE_SIGNALS)) {
61
+ if (files.some(file => fs.existsSync(path.join(this.cwd, file)))) {
62
+ found.add(agent);
63
+ }
64
+ }
65
+
66
+ // VS Code extension detection
67
+ const extDir = path.join(this.home, '.vscode', 'extensions');
68
+ if (fs.existsSync(extDir)) {
69
+ let entries;
70
+ try { entries = fs.readdirSync(extDir); } catch { entries = []; }
71
+ for (const [agent, prefixes] of Object.entries(EXTENSION_SIGNALS)) {
72
+ if (prefixes.some(p => entries.some(e => e.startsWith(p)))) {
73
+ found.add(agent);
74
+ }
75
+ }
76
+ }
77
+
78
+ if (this.checkPath) {
79
+ for (const [agent, bin] of Object.entries(PATH_SIGNALS)) {
80
+ if (this._inPath(bin)) found.add(agent);
81
+ }
82
+ }
83
+
84
+ return [...found];
85
+ }
86
+
87
+ _inPath(bin) {
88
+ try {
89
+ const cmd = process.platform === 'win32' ? `where ${bin}` : `which ${bin}`;
90
+ execSync(cmd, { stdio: 'ignore' });
91
+ return true;
92
+ } catch {
93
+ return false;
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,39 @@
1
+ import fs from 'fs';
2
+ import { resolveBookLibPaths } from './paths.js';
3
+
4
+ const DEFAULT_CONFIG = {
5
+ sources: [
6
+ { type: 'registry', trusted: true }
7
+ ],
8
+ discovery: {
9
+ ttlHours: 24
10
+ },
11
+ search: {
12
+ minScore: 0.3,
13
+ registryFallbackThreshold: 0.4
14
+ }
15
+ };
16
+
17
+ /**
18
+ * Loads booklib.config.json from project-local or global user dir.
19
+ * Returns DEFAULT_CONFIG if no config file is found.
20
+ * User config is shallow-merged with defaults so missing keys always have values.
21
+ */
22
+ export function loadConfig(projectCwd) {
23
+ const { configPath } = resolveBookLibPaths(projectCwd);
24
+
25
+ let userConfig = {};
26
+ if (fs.existsSync(configPath)) {
27
+ try {
28
+ userConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
29
+ } catch {
30
+ // malformed config — fall back to defaults silently
31
+ }
32
+ }
33
+
34
+ return {
35
+ sources: userConfig.sources ?? DEFAULT_CONFIG.sources,
36
+ discovery: { ...DEFAULT_CONFIG.discovery, ...(userConfig.discovery ?? {}) },
37
+ search: { ...DEFAULT_CONFIG.search, ...(userConfig.search ?? {}) },
38
+ };
39
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Resolves conflicts between skill suggestions or indexed chunks.
3
+ *
4
+ * Resolution rules (applied per topic group):
5
+ * specificity delta >= 2 OR score delta >= 0.2 → auto-resolve (higher wins, rationale attached)
6
+ * otherwise → escalate (add to conflicts list)
7
+ *
8
+ * Usage:
9
+ * const resolver = new ConflictResolver(registryArray);
10
+ * const { winners, suppressed, conflicts } = resolver.resolveSkills(suggestions);
11
+ * const { winners, suppressed, conflicts } = resolver.resolveChunks(searchResults);
12
+ */
13
+ export class ConflictResolver {
14
+ /**
15
+ * @param {Array<{name: string, specificity?: number, topic?: string, stars?: number}>} registry
16
+ */
17
+ constructor(registry = []) {
18
+ this._byName = new Map(registry.map(s => [s.name, s]));
19
+ }
20
+
21
+ /**
22
+ * Resolves conflicts between registry-level skill suggestions.
23
+ * @param {object[]} skills — each must have .name
24
+ */
25
+ resolveSkills(skills) {
26
+ return this._resolve(
27
+ skills,
28
+ s => s.name,
29
+ s => this._meta(s.name, s)
30
+ );
31
+ }
32
+
33
+ /**
34
+ * Resolves conflicts between semantic-search chunks.
35
+ * @param {Array<{score: number, text: string, metadata: object}>} chunks
36
+ */
37
+ resolveChunks(chunks) {
38
+ // Deduplicate by skill name — keep highest-scored chunk per skill
39
+ const bySkill = new Map();
40
+ for (const c of chunks) {
41
+ const raw = c.metadata?.name ?? c.metadata?.filePath ?? 'unknown';
42
+ // Normalise to skill name: take first path segment (handles reference file paths like
43
+ // "clean-code-reviewer/references/review-checklist.md" → "clean-code-reviewer")
44
+ const skillName = raw.includes('/') ? raw.split('/')[0] : raw;
45
+ const existing = bySkill.get(skillName);
46
+ if (!existing || (c.score ?? 0) > (existing.score ?? 0)) {
47
+ bySkill.set(skillName, c);
48
+ }
49
+ }
50
+ const deduped = [...bySkill.values()];
51
+ return this._resolve(
52
+ deduped,
53
+ c => c.metadata?.name ?? c.metadata?.filePath ?? 'unknown',
54
+ c => this._meta(c.metadata?.name, c)
55
+ );
56
+ }
57
+
58
+ // ── Private ────────────────────────────────────────────────────────────────
59
+
60
+ /** Look up registry metadata, falling back to inline values or defaults. */
61
+ _meta(name, item = {}) {
62
+ const reg = this._byName.get(name);
63
+ return {
64
+ specificity: reg?.specificity ?? item?.specificity ?? 5,
65
+ topic: reg?.topic ?? item?.topic ?? name ?? 'unknown',
66
+ stars: reg?.stars ?? item?.stars ?? 0,
67
+ };
68
+ }
69
+
70
+ _resolve(items, getName, getMeta) {
71
+ // Group by topic
72
+ const byTopic = new Map();
73
+ for (const item of items) {
74
+ const { topic } = getMeta(item);
75
+ if (!byTopic.has(topic)) byTopic.set(topic, []);
76
+ byTopic.get(topic).push(item);
77
+ }
78
+
79
+ const winners = [];
80
+ const suppressed = [];
81
+ const conflicts = [];
82
+
83
+ for (const [, candidates] of byTopic) {
84
+ if (candidates.length === 1) {
85
+ winners.push({ ...candidates[0], _decision: 'auto', _rationale: null });
86
+ continue;
87
+ }
88
+
89
+ // Sort: specificity desc → score desc → stars desc
90
+ const sorted = [...candidates].sort((a, b) => {
91
+ const ma = getMeta(a), mb = getMeta(b);
92
+ if (mb.specificity !== ma.specificity) return mb.specificity - ma.specificity;
93
+ if ((b.score ?? 0) !== (a.score ?? 0)) return (b.score ?? 0) - (a.score ?? 0);
94
+ return (mb.stars ?? 0) - (ma.stars ?? 0);
95
+ });
96
+
97
+ const best = sorted[0];
98
+ const bestMeta = getMeta(best);
99
+ let bestAdded = false;
100
+
101
+ for (const runner of sorted.slice(1)) {
102
+ const runnerMeta = getMeta(runner);
103
+ const dSpec = bestMeta.specificity - runnerMeta.specificity;
104
+ const dScore = (best.score ?? 0) - (runner.score ?? 0);
105
+
106
+ if (dSpec >= 2 || dScore >= 0.2) {
107
+ // Clear winner — auto-resolve silently
108
+ const reason = dSpec >= 2
109
+ ? `more specific (${bestMeta.specificity} vs ${runnerMeta.specificity})`
110
+ : `higher relevance (${(best.score ?? 0).toFixed(2)} vs ${(runner.score ?? 0).toFixed(2)})`;
111
+
112
+ if (!bestAdded) {
113
+ winners.push({
114
+ ...best,
115
+ _decision: 'auto',
116
+ _rationale: `chosen over \`${getName(runner)}\` — ${reason}`,
117
+ });
118
+ bestAdded = true;
119
+ }
120
+ suppressed.push({ ...runner, _decision: 'suppressed', _rationale: `\`${getName(best)}\` preferred — ${reason}` });
121
+ } else {
122
+ // Genuine conflict — escalate to human
123
+ if (!conflicts.find(c => c.options.some(o => o.name === getName(best)))) {
124
+ conflicts.push({
125
+ topic: bestMeta.topic,
126
+ options: sorted.map(s => ({
127
+ name: getName(s),
128
+ specificity: getMeta(s).specificity,
129
+ score: s.score ?? null,
130
+ })),
131
+ message:
132
+ `\`${getName(best)}\` vs \`${getName(runner)}\` — both equally applicable` +
133
+ ` (specificity ${bestMeta.specificity} vs ${runnerMeta.specificity}).` +
134
+ ` Which should guide this decision?`,
135
+ });
136
+ }
137
+ }
138
+ }
139
+
140
+ // Add best as winner if no conflict was raised for it
141
+ if (!bestAdded && !conflicts.some(c => c.options.some(o => o.name === getName(best)))) {
142
+ winners.push({ ...best, _decision: 'auto', _rationale: null });
143
+ }
144
+ }
145
+
146
+ return { winners, suppressed, conflicts };
147
+ }
148
+ }