@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,42 @@
1
+ export const STOPWORDS = new Set([
2
+ 'a', 'an', 'the', 'in', 'on', 'at', 'to', 'for', 'of', 'and', 'or',
3
+ 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had',
4
+ 'do', 'does', 'did', 'will', 'would', 'shall', 'should', 'may', 'might',
5
+ 'must', 'can', 'could', 'not', 'no', 'nor', 'so', 'yet', 'both', 'with',
6
+ 'about', 'from', 'up', 'down', 'out', 'how', 'what', 'when', 'where',
7
+ 'who', 'why', 'which', 'by', 'as', 'if', 'then', 'than', 'too', 'very',
8
+ 'just', 'more', 'also', 'its', 'it',
9
+ ]);
10
+
11
+ export function extractKeywords(query) {
12
+ const tokens = query
13
+ .toLowerCase()
14
+ .split(/[^a-z0-9]+/)
15
+ .filter(token => token.length > 1 && !STOPWORDS.has(token));
16
+
17
+ return tokens;
18
+ }
19
+
20
+ export function expandQuery(query) {
21
+ const keywords = extractKeywords(query);
22
+
23
+ const expanded = [];
24
+
25
+ if (keywords.length > 0) {
26
+ const keywordJoined = keywords.join(' ');
27
+ const candidates = [
28
+ keywordJoined,
29
+ `best practices for ${keywordJoined}`,
30
+ `how to ${keywordJoined}`,
31
+ ];
32
+ for (const v of candidates) {
33
+ if (v !== query) expanded.push(v);
34
+ }
35
+ }
36
+
37
+ return {
38
+ original: query,
39
+ keywords,
40
+ expanded,
41
+ };
42
+ }
@@ -0,0 +1,40 @@
1
+ import { pipeline } from '@huggingface/transformers';
2
+
3
+ const MODEL = 'Xenova/ms-marco-MiniLM-L-6-v2';
4
+
5
+ export class Reranker {
6
+ constructor() {
7
+ this._pipeline = null;
8
+ }
9
+
10
+ async _ensurePipelineLoaded() {
11
+ if (this._pipeline === null) {
12
+ this._pipeline = await pipeline('text-classification', MODEL);
13
+ }
14
+ }
15
+
16
+ async rerank(query, candidates) {
17
+ // Return empty array early without loading model if candidates is empty
18
+ if (candidates.length === 0) {
19
+ return [];
20
+ }
21
+
22
+ // Load the model on first use
23
+ await this._ensurePipelineLoaded();
24
+
25
+ // Create query-passage pairs
26
+ const pairs = candidates.map(c => [query, c.text]);
27
+
28
+ // Call the cross-encoder model
29
+ const outputs = await this._pipeline(pairs);
30
+
31
+ return candidates
32
+ .map((c, i) => {
33
+ const raw = outputs[i];
34
+ const scores = Array.isArray(raw) ? raw : [raw];
35
+ const best = scores.reduce((a, b) => (a.score > b.score ? a : b));
36
+ return { ...c, score: best.score };
37
+ })
38
+ .sort((a, b) => b.score - a.score);
39
+ }
40
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Reciprocal Rank Fusion (RRF) — merge multiple ranked lists into a single sorted list.
3
+ * Items appearing in multiple lists accumulate scores; deduplication uses `text` as the identity key.
4
+ *
5
+ * Formula: contribution of item at rank r (0-indexed) in list i = weights[i] / (k + r + 1)
6
+ *
7
+ * @param {Array<Array<{score, text, metadata}>>} resultLists - One ranked list per retrieval source
8
+ * @param {Object} options
9
+ * @param {number} [options.k=60] - Smoothing constant (from RRF literature)
10
+ * @param {number[]} [options.weights] - Per-list multipliers (default: all 1)
11
+ * @returns {Array<{score, text, metadata}>} Merged and sorted descending by RRF score
12
+ */
13
+ export function reciprocalRankFusion(resultLists, { k = 60, weights } = {}) {
14
+ // Handle empty input
15
+ if (!resultLists || resultLists.length === 0) {
16
+ return [];
17
+ }
18
+
19
+ // Filter out empty lists and compute default weights
20
+ const nonEmptyLists = resultLists.filter(list => list && list.length > 0);
21
+ if (nonEmptyLists.length === 0) {
22
+ return [];
23
+ }
24
+
25
+ // Use provided weights or default to all 1s
26
+ const finalWeights = weights || Array(resultLists.length).fill(1);
27
+
28
+ // Accumulator: map from text → { text, metadata, score }
29
+ const scoreMap = new Map();
30
+
31
+ // Process each list
32
+ for (let i = 0; i < resultLists.length; i++) {
33
+ const list = resultLists[i];
34
+ const weight = finalWeights[i] !== undefined ? finalWeights[i] : 1;
35
+
36
+ for (let rank = 0; rank < list.length; rank++) {
37
+ const item = list[rank];
38
+ const contribution = weight / (k + rank + 1);
39
+
40
+ if (scoreMap.has(item.text)) {
41
+ // Item already seen; accumulate score
42
+ const existing = scoreMap.get(item.text);
43
+ existing.score += contribution;
44
+ } else {
45
+ // First time seeing this item; store it
46
+ scoreMap.set(item.text, {
47
+ text: item.text,
48
+ metadata: item.metadata,
49
+ score: contribution,
50
+ });
51
+ }
52
+ }
53
+ }
54
+
55
+ // Convert map to sorted array (descending by score)
56
+ const result = Array.from(scoreMap.values()).sort((a, b) => b.score - a.score);
57
+
58
+ return result;
59
+ }
@@ -0,0 +1,151 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { resolveBookLibPaths } from '../paths.js';
4
+
5
+ /**
6
+ * Handles project-wide compliance scanning against the entire BookLib library.
7
+ * Uses audit.json rule sets for fast, accurate static analysis.
8
+ * Falls back to skipping files whose skill has no audit.json.
9
+ */
10
+ export class BookLibScanner {
11
+ constructor() {}
12
+
13
+ /**
14
+ * Scans a directory and generates a 'Wisdom Compliance' heatmap.
15
+ *
16
+ * @param {string} dirPath - The project directory to scan.
17
+ * @param {object} opts
18
+ * @param {string} opts.mode - 'code' (default) or 'docs' (scans .md/.txt files)
19
+ * @returns {string} - A comprehensive project health report.
20
+ */
21
+ scan(dirPath, { mode = 'code' } = {}) {
22
+ const files = this.getFiles(dirPath);
23
+ const reports = [];
24
+ const stats = {
25
+ filesScanned: files.length,
26
+ filesMatched: 0,
27
+ filesAudited: 0,
28
+ violations: 0,
29
+ healthyFiles: 0,
30
+ debtBySkill: {},
31
+ noRulesSkills: new Set(),
32
+ };
33
+
34
+ console.log(`Starting deep scan of ${files.length} files...`);
35
+
36
+ for (const file of files) {
37
+ const skillName = this.detectSkill(file, mode);
38
+ if (!skillName) continue;
39
+
40
+ stats.filesMatched++;
41
+ const auditJsonPath = this._findAuditJson(skillName);
42
+ if (!auditJsonPath) {
43
+ stats.noRulesSkills.add(skillName);
44
+ continue; // skill has no static rules — skip
45
+ }
46
+
47
+ let rules;
48
+ try {
49
+ ({ rules } = JSON.parse(fs.readFileSync(auditJsonPath, 'utf8')));
50
+ } catch { continue; }
51
+
52
+ const lines = fs.readFileSync(file, 'utf8').split('\n');
53
+ let violationCount = 0;
54
+ for (const rule of rules) {
55
+ const regex = new RegExp(rule.pattern, 'g');
56
+ for (const line of lines) {
57
+ if (regex.test(line)) violationCount++;
58
+ }
59
+ }
60
+
61
+ stats.filesAudited++;
62
+ stats.violations += violationCount;
63
+ if (violationCount === 0) stats.healthyFiles++;
64
+ stats.debtBySkill[skillName] = (stats.debtBySkill[skillName] || 0) + violationCount;
65
+ if (violationCount > 0) {
66
+ reports.push({ file, skill: skillName, violationCount });
67
+ }
68
+ }
69
+
70
+ return this.formatDashboard(stats, reports);
71
+ }
72
+
73
+ /**
74
+ * Detects which BookLib skill is most relevant to a file extension.
75
+ */
76
+ detectSkill(filePath, mode = 'code') {
77
+ const ext = path.extname(filePath);
78
+ if (mode === 'docs') {
79
+ if (ext === '.md' || ext === '.mdx' || ext === '.mdc') return 'writing-plans';
80
+ if (ext === '.txt') return 'writing-plans';
81
+ return null;
82
+ }
83
+ if (ext === '.kt' || ext === '.kts') return 'effective-kotlin';
84
+ if (ext === '.ts' || ext === '.tsx') return 'effective-typescript';
85
+ if (ext === '.java') return 'effective-java';
86
+ if (ext === '.py') return 'effective-python';
87
+ if (ext === '.js' || ext === '.mjs' || ext === '.cjs') return 'clean-code-reviewer';
88
+ return null;
89
+ }
90
+
91
+ /**
92
+ * Finds audit.json for a skill — checks bundled skills first, then community cache.
93
+ */
94
+ _findAuditJson(skillName) {
95
+ const { skillsPath, cachePath } = resolveBookLibPaths();
96
+ const bundled = path.join(skillsPath, skillName, 'audit.json');
97
+ if (fs.existsSync(bundled)) return bundled;
98
+ const community = path.join(cachePath, 'skills', skillName, 'audit.json');
99
+ if (fs.existsSync(community)) return community;
100
+ return null;
101
+ }
102
+
103
+ /**
104
+ * Helper to recursively list files, excluding common noise directories.
105
+ */
106
+ getFiles(dir) {
107
+ let results = [];
108
+ const list = fs.readdirSync(dir);
109
+ const ignore = ['.git', 'node_modules', '.booklib', '.claude', '.idea', 'dist', 'build', 'coverage', '.cache'];
110
+
111
+ list.forEach(file => {
112
+ if (ignore.includes(file)) return;
113
+
114
+ const fullPath = path.join(dir, file);
115
+ const stat = fs.statSync(fullPath);
116
+ if (stat && stat.isDirectory()) {
117
+ results = results.concat(this.getFiles(fullPath));
118
+ } else {
119
+ results.push(fullPath);
120
+ }
121
+ });
122
+ return results;
123
+ }
124
+
125
+ formatDashboard(stats, reports) {
126
+ const sortedReports = reports.sort((a, b) => b.violationCount - a.violationCount);
127
+ const topDebt = sortedReports.slice(0, 5);
128
+
129
+ return `
130
+ # 📊 BookLib Wisdom Heatmap
131
+ **Project Health Dashboard**
132
+
133
+ ## 📈 Executive Summary
134
+ - **Files Scanned**: ${stats.filesScanned} (${stats.filesMatched} matched a skill lens, ${stats.filesAudited} audited)
135
+ - **Healthy Files**: ${stats.healthyFiles} / ${stats.filesAudited} audited
136
+ - **Total Violations**: ${stats.violations}
137
+ - **Compliance**: ${((stats.healthyFiles / (stats.filesAudited || 1)) * 100).toFixed(1)}% of audited files violation-free
138
+
139
+ ## 🛠 Architectural Debt by Skill
140
+ ${Object.entries(stats.debtBySkill).map(([skill, count]) => `- **${skill}**: ${count} violations`).join('\n')}
141
+
142
+ ## 🚨 Top 5 Refactoring Priorities (High Debt)
143
+ ${topDebt.map(r => `- \`${path.relative(process.cwd(), r.file)}\` (${r.violationCount} violations | Lens: ${r.skill})`).join('\n')}
144
+
145
+ ---
146
+ > **Note**: This heatmap is based on 'Light Audits' (Static Pattern Detection).
147
+ > Use \`booklib audit <skill> <file>\` for a deep-reasoning review of high-priority files.
148
+ ${stats.noRulesSkills.size > 0 ? `> **No static rules loaded for:** ${[...stats.noRulesSkills].join(', ')} — add an \`audit.json\` to enable pattern checks.` : ''}
149
+ `;
150
+ }
151
+ }
@@ -0,0 +1,139 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+ import { LocalIndex } from 'vectra';
4
+ import { pipeline } from '@huggingface/transformers';
5
+ import { resolveBookLibPaths } from '../paths.js';
6
+ import { BM25Index } from './bm25-index.js';
7
+ import { reciprocalRankFusion } from './rrf.js';
8
+ import { expandQuery } from './query-expander.js';
9
+ import { Reranker } from './reranker.js';
10
+ import { loadEdges, traverseEdges } from './graph.js';
11
+
12
+ const RRF_ORIGINAL_WEIGHT = 2;
13
+ const RRF_EXPANDED_WEIGHT = 1;
14
+ const RERANK_CANDIDATES = 20;
15
+
16
+ export class BookLibSearcher {
17
+ constructor(indexPath) {
18
+ this.indexPath = indexPath ?? resolveBookLibPaths().indexPath;
19
+ this.index = new LocalIndex(this.indexPath);
20
+ this.extractor = null;
21
+ this.reranker = new Reranker();
22
+ }
23
+
24
+ async loadModel() {
25
+ if (!this.extractor) {
26
+ this.extractor = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
27
+ }
28
+ }
29
+
30
+ async getEmbedding(text) {
31
+ await this.loadModel();
32
+ const output = await this.extractor(text, { pooling: 'mean', normalize: true });
33
+ return Array.from(output.data);
34
+ }
35
+
36
+ get bm25Path() {
37
+ return path.join(path.dirname(this.indexPath), 'bm25.json');
38
+ }
39
+
40
+ get graphFile() {
41
+ return path.join(path.dirname(this.indexPath), 'knowledge', 'graph.jsonl');
42
+ }
43
+
44
+ async _vectorSearch(query, topK) {
45
+ const vector = await this.getEmbedding(query);
46
+ const results = await this.index.queryItems(vector, '', topK);
47
+ return results.map(r => ({
48
+ score: r.score,
49
+ text: r.item.metadata.text,
50
+ metadata: { ...r.item.metadata, text: undefined },
51
+ }));
52
+ }
53
+
54
+ async search(query, limit = 5, minScore = 0.5, options = {}) {
55
+ const { useGraph = false } = options;
56
+ if (!(await this.index.isIndexCreated())) {
57
+ throw new Error('Index not found. Please run "booklib index" first.');
58
+ }
59
+
60
+ // Fallback: no BM25 index present — use pure vector search (backwards compatible)
61
+ if (!fs.existsSync(this.bm25Path)) {
62
+ const vector = await this.getEmbedding(query);
63
+ const results = await this.index.queryItems(vector, '', limit);
64
+ const filtered = results
65
+ .filter(r => r.score >= minScore)
66
+ .map(r => ({
67
+ score: r.score,
68
+ text: r.item.metadata.text,
69
+ metadata: { ...r.item.metadata, text: undefined },
70
+ }));
71
+ return addDisplayScores(useGraph ? this._appendGraphResults(filtered) : filtered);
72
+ }
73
+
74
+ const bm25 = BM25Index.load(this.bm25Path);
75
+ const { expanded } = expandQuery(query);
76
+ const allQueries = [query, ...expanded];
77
+
78
+ const resultLists = [];
79
+ const weights = [];
80
+
81
+ for (let i = 0; i < allQueries.length; i++) {
82
+ const q = allQueries[i];
83
+ const w = i === 0 ? RRF_ORIGINAL_WEIGHT : RRF_EXPANDED_WEIGHT;
84
+
85
+ const [vecResults, bm25Results] = await Promise.all([
86
+ this._vectorSearch(q, RERANK_CANDIDATES),
87
+ Promise.resolve(bm25.search(q, RERANK_CANDIDATES)),
88
+ ]);
89
+
90
+ resultLists.push(vecResults, bm25Results);
91
+ weights.push(w, w);
92
+ }
93
+
94
+ const merged = reciprocalRankFusion(resultLists, { weights });
95
+ const candidates = merged.slice(0, RERANK_CANDIDATES);
96
+ const reranked = await this.reranker.rerank(query, candidates);
97
+
98
+ const results = reranked.filter(r => r.score >= minScore).slice(0, limit);
99
+ return addDisplayScores(useGraph ? this._appendGraphResults(results) : results);
100
+ }
101
+
102
+ _appendGraphResults(results) {
103
+ const GRAPH_EDGE_TYPES = new Set(['see-also', 'applies-to', 'extends']);
104
+ let edges;
105
+ try {
106
+ edges = loadEdges({ graphFile: this.graphFile });
107
+ } catch {
108
+ return results;
109
+ }
110
+ if (edges.length === 0) return results;
111
+
112
+ const seenIds = new Set(
113
+ results.map(r => r.metadata?.name ?? r.metadata?.id).filter(Boolean)
114
+ );
115
+ const graphLinked = [];
116
+
117
+ for (const result of results) {
118
+ const nodeId = result.metadata?.name ?? result.metadata?.id;
119
+ if (!nodeId) continue;
120
+
121
+ for (const { id: neighborId, edge } of traverseEdges(nodeId, edges, 1)) {
122
+ if (!GRAPH_EDGE_TYPES.has(edge.type)) continue;
123
+ if (seenIds.has(neighborId)) continue;
124
+ seenIds.add(neighborId);
125
+ graphLinked.push({
126
+ score: 0,
127
+ text: '',
128
+ metadata: { name: neighborId, source: 'graph', edgeType: edge.type },
129
+ });
130
+ }
131
+ }
132
+
133
+ return [...results, ...graphLinked];
134
+ }
135
+ }
136
+
137
+ function addDisplayScores(results) {
138
+ return results.map((r, i) => ({ ...r, displayScore: r.displayScore ?? Math.round(100 / (i + 1)) }));
139
+ }