@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,266 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ route.py — CLI for skill-router
4
+
5
+ Usage:
6
+ python route.py --task "review my Python class for code quality"
7
+ python route.py --file path/to/file.py --task "review for correctness"
8
+ python route.py --task "design a saga for order processing"
9
+ python route.py --task "decompose my monolith into microservices"
10
+
11
+ Prints the recommended skill(s) with rationale.
12
+ """
13
+
14
+ import argparse
15
+ import os
16
+
17
+ # Skill routing rules
18
+ SKILL_CATALOG = {
19
+ "animation-at-work": {
20
+ "domain": "Web animation, UI motion",
21
+ "languages": [], # language-agnostic but frontend
22
+ "keywords": ["animation", "motion", "transition", "keyframe", "easing", "css animation", "web animation"],
23
+ "anti_keywords": ["backend", "data processing", "api"],
24
+ },
25
+ "clean-code-reviewer": {
26
+ "domain": "Code quality, readability (any language)",
27
+ "languages": ["py", "java", "kt", "js", "ts", "go", "rs", "cpp", "cs"],
28
+ "keywords": ["review", "code quality", "readable", "clean", "refactor", "naming", "smell", "heuristic"],
29
+ "anti_keywords": ["architecture", "system design"],
30
+ },
31
+ "data-intensive-patterns": {
32
+ "domain": "Storage internals, distributed data",
33
+ "languages": [],
34
+ "keywords": ["replication", "partitioning", "consistency", "cap theorem", "storage engine", "lsm", "b-tree", "acid", "base", "linearizability"],
35
+ "anti_keywords": ["ui", "frontend", "domain model"],
36
+ },
37
+ "data-pipelines": {
38
+ "domain": "ETL, data ingestion, orchestration",
39
+ "languages": ["py", "sql"],
40
+ "keywords": ["data pipeline", "etl", "elt", "ingestion", "airflow", "dbt", "spark", "streaming", "batch", "warehouse"],
41
+ "anti_keywords": ["microservice", "ui"],
42
+ },
43
+ "design-patterns": {
44
+ "domain": "GoF OO design patterns",
45
+ "languages": ["java", "py", "kt", "cs"],
46
+ "keywords": ["design pattern", "factory", "singleton", "observer", "strategy", "decorator", "facade", "command", "template method", "composite"],
47
+ "anti_keywords": ["functional", "microservice decomposition"],
48
+ },
49
+ "domain-driven-design": {
50
+ "domain": "Domain modeling, DDD tactical and strategic patterns",
51
+ "languages": [],
52
+ "keywords": ["ddd", "domain model", "aggregate", "value object", "bounded context", "ubiquitous language", "repository", "domain service", "anticorruption", "entity", "anemic"],
53
+ "anti_keywords": ["simple crud", "ui", "code quality"],
54
+ },
55
+ "effective-java": {
56
+ "domain": "Java best practices",
57
+ "languages": ["java"],
58
+ "keywords": ["java", "generics", "enum", "lambda", "stream", "builder pattern", "serialization", "checked exception"],
59
+ "anti_keywords": [],
60
+ },
61
+ "effective-kotlin": {
62
+ "domain": "Kotlin best practices",
63
+ "languages": ["kt"],
64
+ "keywords": ["kotlin", "kotlin best practices", "kotlin safety", "null safety", "kotlin idioms", "effective kotlin", "best practices"],
65
+ "anti_keywords": ["learning kotlin"],
66
+ },
67
+ "effective-python": {
68
+ "domain": "Python idioms and best practices",
69
+ "languages": ["py"],
70
+ "keywords": ["pythonic", "python best practices", "comprehension", "generator", "decorator", "metaclass"],
71
+ "anti_keywords": ["async", "asyncio", "web scraping"],
72
+ },
73
+ "kotlin-in-action": {
74
+ "domain": "Kotlin language features",
75
+ "languages": ["kt"],
76
+ "keywords": ["learn kotlin", "kotlin coroutines", "kotlin lambdas", "kotlin classes", "how does kotlin"],
77
+ "anti_keywords": ["best practices"],
78
+ },
79
+ "lean-startup": {
80
+ "domain": "Startup strategy, MVP, validated learning",
81
+ "languages": [],
82
+ "keywords": ["mvp", "validated learning", "pivot", "build-measure-learn", "lean startup", "hypothesis", "product market fit"],
83
+ "anti_keywords": ["code", "review", "architecture", "api"],
84
+ },
85
+ "microservices-patterns": {
86
+ "domain": "Service decomposition, sagas, CQRS, event sourcing",
87
+ "languages": [],
88
+ "keywords": ["microservice", "services", "saga", "cqrs", "event sourcing", "api gateway", "decompose monolith", "circuit breaker", "distributed transaction", "choreography", "service coordination", "inter-service", "strangle", "strangler"],
89
+ "anti_keywords": ["monolith only", "simple crud"],
90
+ },
91
+ "refactoring-ui": {
92
+ "domain": "UI design, visual hierarchy, typography",
93
+ "languages": ["css", "html", "jsx", "tsx", "svelte"],
94
+ "keywords": ["ui design", "visual hierarchy", "typography", "color", "spacing", "layout", "design system"],
95
+ "anti_keywords": ["backend", "api", "animation"],
96
+ },
97
+ "storytelling-with-data": {
98
+ "domain": "Data visualization, charts, narrative",
99
+ "languages": [],
100
+ "keywords": ["data visualization", "chart", "graph", "dashboard", "storytelling", "declutter", "bar chart", "scatter plot"],
101
+ "anti_keywords": ["backend", "api", "code quality"],
102
+ },
103
+ "system-design-interview": {
104
+ "domain": "System scalability, high-level architecture",
105
+ "languages": [],
106
+ "keywords": ["system design", "scale", "scalability", "rate limiting", "cdn", "load balancer", "cache", "sharding", "high availability"],
107
+ "anti_keywords": ["code review", "domain model"],
108
+ },
109
+ "using-asyncio-python": {
110
+ "domain": "Python asyncio, concurrency",
111
+ "languages": ["py"],
112
+ "keywords": ["asyncio", "async def", "await", "coroutine", "event loop", "aiohttp", "async python", "concurrent python"],
113
+ "anti_keywords": [],
114
+ },
115
+ "web-scraping-python": {
116
+ "domain": "Web scraping, crawling",
117
+ "languages": ["py"],
118
+ "keywords": ["web scraping", "beautifulsoup", "scrapy", "crawl", "parse html", "selenium scraping"],
119
+ "anti_keywords": [],
120
+ },
121
+ }
122
+
123
+ CONFLICT_RULES = [
124
+ ("effective-python", "using-asyncio-python", "using-asyncio-python wins for async topics"),
125
+ ("kotlin-in-action", "effective-kotlin", "effective-kotlin wins for best practice advice"),
126
+ ("clean-code-reviewer", "effective-java", "effective-java wins for Java-specific items; use both for comprehensive review"),
127
+ ]
128
+
129
+
130
+ def detect_language(file_path: str | None) -> str | None:
131
+ """Detect language from file extension."""
132
+ if not file_path:
133
+ return None
134
+ ext = os.path.splitext(file_path)[1].lstrip(".")
135
+ return ext.lower() if ext else None
136
+
137
+
138
+ def score_skill(skill_name: str, skill_info: dict, task: str, language: str | None) -> float:
139
+ """Score a skill's relevance for a task. Higher = more relevant."""
140
+ task_lower = task.lower()
141
+ score = 0.0
142
+
143
+ # Keyword matching
144
+ for keyword in skill_info["keywords"]:
145
+ if keyword in task_lower:
146
+ score += 2.0
147
+
148
+ # Anti-keyword penalty
149
+ for anti_keyword in skill_info["anti_keywords"]:
150
+ if anti_keyword in task_lower:
151
+ score -= 3.0
152
+
153
+ # Language match bonus
154
+ if language and language in skill_info["languages"]:
155
+ score += 3.0
156
+ elif skill_info["languages"] and language and language not in skill_info["languages"]:
157
+ score -= 5.0 # Strong penalty for language mismatch
158
+
159
+ return score
160
+
161
+
162
+ def route(task: str, file_path: str | None = None) -> dict:
163
+ """Route a task to the best skill(s)."""
164
+ language = detect_language(file_path)
165
+ task_lower = task.lower()
166
+
167
+ scores = {}
168
+ keyword_hits = {}
169
+ for skill_name, skill_info in SKILL_CATALOG.items():
170
+ scores[skill_name] = score_skill(skill_name, skill_info, task, language)
171
+ keyword_hits[skill_name] = any(kw in task_lower for kw in skill_info["keywords"])
172
+
173
+ # Sort by score, descending
174
+ ranked = sorted(scores.items(), key=lambda x: x[1], reverse=True)
175
+
176
+ # Require positive score AND at least one keyword hit to prevent
177
+ # language-match-only false positives (e.g. data-pipelines for any .py file)
178
+ candidates = [(name, score) for name, score in ranked if score > 0 and keyword_hits[name]]
179
+
180
+ if not candidates:
181
+ return {
182
+ "primary": None,
183
+ "secondary": None,
184
+ "dont_apply": None,
185
+ "conflict_note": None,
186
+ "language_detected": language,
187
+ "rationale": "No skill matched. Try describing the task with more domain keywords, or browse the skill catalog.",
188
+ }
189
+
190
+ primary_name, _ = candidates[0]
191
+ secondary = candidates[1] if len(candidates) > 1 else None
192
+ dont_apply = candidates[2] if len(candidates) > 2 else None
193
+
194
+ # Apply conflict resolution
195
+ conflict_note = None
196
+ if secondary:
197
+ for skill_a, skill_b, note in CONFLICT_RULES:
198
+ if (primary_name == skill_a and secondary[0] == skill_b) or \
199
+ (primary_name == skill_b and secondary[0] == skill_a):
200
+ conflict_note = note
201
+ break
202
+
203
+ return {
204
+ "primary": primary_name,
205
+ "primary_domain": SKILL_CATALOG[primary_name]["domain"],
206
+ "secondary": secondary[0] if secondary else None,
207
+ "secondary_domain": SKILL_CATALOG[secondary[0]]["domain"] if secondary else None,
208
+ "dont_apply": dont_apply[0] if dont_apply else None,
209
+ "dont_apply_domain": SKILL_CATALOG[dont_apply[0]]["domain"] if dont_apply else None,
210
+ "conflict_note": conflict_note,
211
+ "language_detected": language,
212
+ }
213
+
214
+
215
+ def format_output(result: dict) -> str:
216
+ """Format routing result for CLI output."""
217
+ lines = []
218
+
219
+ if result.get("language_detected"):
220
+ lines.append(f"Detected language: .{result['language_detected']}")
221
+ lines.append("")
222
+
223
+ if not result["primary"]:
224
+ lines.append("No matching skill found.")
225
+ lines.append(result.get("rationale", ""))
226
+ return "\n".join(lines)
227
+
228
+ lines.append(f"**Primary skill:** `{result['primary']}`")
229
+ lines.append(f"**Why:** {result['primary_domain']}")
230
+
231
+ if result["secondary"]:
232
+ lines.append(f"**Secondary (optional):** `{result['secondary']}` — {result['secondary_domain']}")
233
+ else:
234
+ lines.append("**Secondary (optional):** none")
235
+
236
+ if result.get("dont_apply"):
237
+ lines.append(f"**Don't apply:** `{result['dont_apply']}` — {result['dont_apply_domain']} (lower relevance for this task)")
238
+
239
+ if result.get("conflict_note"):
240
+ lines.append(f"**Conflict resolution:** {result['conflict_note']}")
241
+
242
+ return "\n".join(lines)
243
+
244
+
245
+ def main():
246
+ parser = argparse.ArgumentParser(
247
+ description="Route a task to the best @booklib/skills skill",
248
+ formatter_class=argparse.RawDescriptionHelpFormatter,
249
+ epilog="""examples:
250
+ python route.py --task "review my Python class for code quality"
251
+ python route.py --file app/orders.py --task "review for correctness"
252
+ python route.py --task "design a saga for order processing"
253
+ python route.py --task "decompose my monolith into microservices"
254
+ python route.py --task "my bar chart is too cluttered"
255
+ """,
256
+ )
257
+ parser.add_argument("--file", help="Path to the file being reviewed (used for language detection)")
258
+ parser.add_argument("--task", required=True, help="Description of the task or question")
259
+ args = parser.parse_args()
260
+
261
+ result = route(task=args.task, file_path=args.file)
262
+ print(format_output(result))
263
+
264
+
265
+ if __name__ == "__main__":
266
+ main()
@@ -0,0 +1,340 @@
1
+ ---
2
+ name: spring-boot-in-action
3
+ version: "1.0"
4
+ license: MIT
5
+ tags: [java, spring, backend]
6
+ description: >
7
+ Write and review Spring Boot applications using practices from "Spring Boot in Action"
8
+ by Craig Walls. Covers auto-configuration, starter dependencies, externalizing
9
+ configuration with properties and profiles, Spring Security, testing with MockMvc
10
+ and @SpringBootTest, Spring Actuator for production observability, and deployment
11
+ strategies (JAR, WAR, Cloud Foundry). Use when building Spring Boot apps, configuring
12
+ beans, writing integration tests, setting up health checks, or deploying to production.
13
+ Trigger on: "Spring Boot", "Spring", "@SpringBootApplication", "auto-configuration",
14
+ "application.properties", "application.yml", "@RestController", "@Service",
15
+ "@Repository", "SpringBootTest", "Actuator", "starter", ".java files", "Maven", "Gradle".
16
+ ---
17
+
18
+ # Spring Boot in Action Skill
19
+
20
+ Apply the practices from Craig Walls' "Spring Boot in Action" to review existing code and write new Spring Boot applications. This skill operates in two modes: **Review Mode** (analyze code for violations of Spring Boot idioms) and **Write Mode** (produce clean, idiomatic Spring Boot from scratch).
21
+
22
+ The core philosophy: Spring Boot removes boilerplate through **auto-configuration**, **starter dependencies**, and **sensible defaults**. Fight the framework only when necessary — and when you do, prefer `application.properties` over code.
23
+
24
+ ## Reference Files
25
+
26
+ - `practices-catalog.md` — Before/after examples for auto-configuration, starters, properties, profiles, security, testing, Actuator, and deployment
27
+
28
+ ## How to Use This Skill
29
+
30
+ **Before responding**, read `practices-catalog.md` for the topic at hand. For configuration issues read the properties/profiles section. For test code read the testing section. For a full review, read all sections.
31
+
32
+ ---
33
+
34
+ ## Mode 1: Code Review
35
+
36
+ When the user asks you to **review** Spring Boot code, follow this process:
37
+
38
+ ### Step 1: Identify the Layer
39
+ Determine whether the code is a controller, service, repository, configuration class, or test. Review focus shifts by layer.
40
+
41
+ ### Step 2: Analyze the Code
42
+
43
+ Check these areas in order of severity:
44
+
45
+ 1. **Auto-Configuration** (Ch 2, 3): Is auto-configuration being fought manually? Look for `@Bean` definitions that replicate what Spring Boot already provides (DataSource, Jackson, Security, etc.). Remove manual config where auto-config suffices.
46
+
47
+ 2. **Starter Dependencies** (Ch 2): Are dependencies declared individually instead of using starters? `spring-boot-starter-web`, `spring-boot-starter-data-jpa`, `spring-boot-starter-security` etc. bundle correct transitive dependencies and version-manage them.
48
+
49
+ 3. **Externalized Configuration** (Ch 3): Are values hardcoded that belong in `application.properties`? Ports, URLs, credentials, timeouts should all be externalized. Use `@ConfigurationProperties` for type-safe config objects; use `@Value` only for single values.
50
+
51
+ 4. **Profiles** (Ch 3): Is environment-specific config (dev DB vs prod DB) handled with `if` statements or system properties? Use `@Profile` and `application-{profile}.properties` instead.
52
+
53
+ 5. **Security** (Ch 3): Is `WebSecurityConfigurerAdapter` extended when simple property-based config would suffice? Is HTTP Basic enabled in production? Are actuator endpoints exposed without auth?
54
+
55
+ 6. **Testing** (Ch 4):
56
+ - Use `@SpringBootTest` for full integration tests, not raw `new MyService()`
57
+ - Use `@WebMvcTest` for controller-only tests (no full context)
58
+ - Use `@DataJpaTest` for repository tests (in-memory DB, no web layer)
59
+ - Use `MockMvc` for controller assertions without starting a server
60
+ - Use `@MockBean` to replace real beans with mocks in slice tests
61
+ - Avoid `@SpringBootTest(webEnvironment = RANDOM_PORT)` unless testing the full HTTP stack
62
+ - Flag missing negative test cases: if there is no test for "not found" (expecting 404), or no test verifying a POST returns 201 with a Location header, call these out explicitly as missing coverage
63
+
64
+ 7. **Actuator** (Ch 7): Is the application missing health/metrics endpoints? Is `/actuator` fully exposed without security? Are custom health indicators implemented for critical dependencies?
65
+
66
+ 8. **Deployment** (Ch 8): Is `spring.profiles.active` set for production? Is database migration (Flyway/Liquibase) configured? Is the app packaged as a self-contained JAR (preferred) or WAR?
67
+
68
+ 9. **General Idioms**:
69
+ - Constructor injection over field injection (`@Autowired` on fields)
70
+ - `@RestController` = `@Controller` + `@ResponseBody` — use it for REST APIs
71
+ - Return `ResponseEntity<T>` from controllers when status codes matter
72
+ - `Optional<T>` from repository methods, never `null`
73
+
74
+ ### Step 3: Calibrate Before Reporting
75
+
76
+ **Do NOT manufacture issues.** If the code already follows idiomatic Spring Boot practices, say so explicitly and acknowledge what is correct. Do not invent problems to fill a review.
77
+
78
+ **Rule: Only flag what is actually wrong in the code shown. Do not raise issues about code or configuration files that are NOT present in the review.**
79
+ - Missing test files are not an issue in a review of production code unless the prompt asks about test coverage.
80
+ - Missing Flyway/Liquibase is not an issue unless `spring.jpa.hibernate.ddl-auto=create` is used in a production context.
81
+ - `spring.jpa.hibernate.ddl-auto=validate` combined with H2 or any datasource is NOT a conflict — validate is a safe, correct choice.
82
+ - Missing `spring.application.name` is NOT an issue unless service discovery is involved.
83
+
84
+ These patterns are **correct** and must NOT be flagged as issues:
85
+ - `@SpringBootApplication` on the main class — correct (Ch 1)
86
+ - Constructor injection without `@Autowired` — correct; Spring auto-wires single-constructor beans (Ch 2)
87
+ - `ResponseEntity.created(URI.create("/api/resource/" + id)).body(saved)` — correct; relative URIs are standard practice here (Ch 2)
88
+ - `repo.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND))` — correct pattern (Ch 2); at most suggest adding a descriptive message string, do NOT recommend replacing it with a domain exception + `@RestControllerAdvice`
89
+ - `SLF4J` logger via `LoggerFactory.getLogger(...)` — correct (Ch 3)
90
+ - `${ENV_VAR:default}` syntax in `application.properties` — correct externalization (Ch 3)
91
+ - `management.endpoints.web.exposure.include=health,info` — correct Actuator lockdown (Ch 7)
92
+
93
+ **When the code is idiomatic, the only acceptable suggestions are:**
94
+ 1. Add a descriptive message to `ResponseStatusException` if one is missing (minor suggestion only)
95
+ 2. Add `spring-boot-starter-actuator` dependency if the `management.*` properties are present but the starter might not be (minor suggestion only)
96
+ Do not suggest Flyway, `@Valid`, test classes, or `@RestControllerAdvice` unless the code has a concrete problem that requires them.
97
+
98
+ ### Step 4: Report Findings
99
+ For each genuine issue, report:
100
+ - **Chapter reference** (e.g., "Ch 3: Externalized Configuration")
101
+ - **Location** in the code
102
+ - **What's wrong** (the anti-pattern)
103
+ - **How to fix it** (the Spring Boot idiomatic way)
104
+ - **Priority**: Critical (security/bugs), Important (maintainability), Suggestion (polish)
105
+
106
+ ### Step 5: Provide Fixed Code
107
+ Offer a corrected version with comments explaining each change.
108
+
109
+ ---
110
+
111
+ ## Mode 2: Writing New Code
112
+
113
+ When the user asks you to **write** new Spring Boot code, apply these core principles:
114
+
115
+ ### Project Bootstrap (Ch 1, 2)
116
+
117
+ 1. **Start with Spring Initializr** (Ch 1). Use `start.spring.io` or `spring init` CLI. Select starters upfront — don't add raw dependencies manually.
118
+
119
+ 2. **Use starters, not individual dependencies** (Ch 2). `spring-boot-starter-web` includes Tomcat, Spring MVC, Jackson, and logging at compatible versions. Never declare `spring-webmvc` + `jackson-databind` + `tomcat-embed-core` separately.
120
+
121
+ 3. **The main class is the only required boilerplate** (Ch 2):
122
+ ```java
123
+ @SpringBootApplication
124
+ public class MyApp {
125
+ public static void main(String[] args) {
126
+ SpringApplication.run(MyApp.class, args);
127
+ }
128
+ }
129
+ ```
130
+ `@SpringBootApplication` = `@Configuration` + `@EnableAutoConfiguration` + `@ComponentScan`.
131
+
132
+ ### Configuration (Ch 3)
133
+
134
+ 4. **Externalize all environment-specific values** (Ch 3). Nothing deployment-specific belongs in code. Use `application.properties` / `application.yml` for defaults.
135
+
136
+ 5. **Use `@ConfigurationProperties` for grouped config** (Ch 3). Bind a prefix to a POJO — type-safe, IDE-friendly, testable:
137
+ ```java
138
+ @ConfigurationProperties(prefix = "app.mail")
139
+ @Component
140
+ public class MailProperties {
141
+ private String host;
142
+ private int port = 25;
143
+ // getters + setters
144
+ }
145
+ ```
146
+
147
+ 6. **Use profiles for environment differences** (Ch 3). `application-dev.properties` overrides `application.properties` when `spring.profiles.active=dev`. Never use `if (env.equals("production"))` in code.
148
+
149
+ 7. **Override auto-configuration surgically** (Ch 3). Use `spring.*` properties first. Only define a `@Bean` when properties are insufficient. Annotate with `@ConditionalOnMissingBean` if providing a fallback.
150
+
151
+ 8. **Customize error pages declaratively** (Ch 3). Place `error/404.html`, `error/500.html` in `src/main/resources/templates/error/`. No custom `ErrorController` needed for basic cases.
152
+
153
+ ### Security (Ch 3)
154
+
155
+ 9. **Extend `WebSecurityConfigurerAdapter` only for custom rules** (Ch 3). For simple HTTP Basic with custom users, `spring.security.user.name` / `spring.security.user.password` properties suffice.
156
+
157
+ 10. **Always secure Actuator endpoints in production** (Ch 7). Expose only `health` and `info` publicly; require authentication for `env`, `beans`, `mappings`, `shutdown`.
158
+
159
+ ### REST Controllers (Ch 2)
160
+
161
+ 11. **Use `@RestController` for API endpoints** (Ch 2). Eliminates `@ResponseBody` on every method.
162
+
163
+ 12. **Return `ResponseEntity<T>` when HTTP status matters** (Ch 2). `ResponseEntity.ok(body)`, `ResponseEntity.notFound().build()`, `ResponseEntity.status(201).body(created)`.
164
+
165
+ 13. **Use constructor injection, not field injection** (Ch 2). Constructor injection makes dependencies explicit and enables testing without Spring context:
166
+ ```java
167
+ // Prefer this:
168
+ @RestController
169
+ public class BookController {
170
+ private final BookRepository repo;
171
+ public BookController(BookRepository repo) { this.repo = repo; }
172
+ }
173
+ ```
174
+
175
+ 14. **Use `Optional` from repository queries** (Ch 2). `repo.findById(id).orElseThrow(() -> new ResponseStatusException(NOT_FOUND))`.
176
+
177
+ ### Testing (Ch 4)
178
+
179
+ 15. **Match test slice to the layer being tested** (Ch 4):
180
+ - Web layer only → `@WebMvcTest(MyController.class)` + `MockMvc`
181
+ - Repository only → `@DataJpaTest`
182
+ - Full app → `@SpringBootTest`
183
+ - External service → `@MockBean` to replace
184
+
185
+ 16. **Use `MockMvc` for controller assertions without starting a server** (Ch 4):
186
+ ```java
187
+ mockMvc.perform(get("/books/1"))
188
+ .andExpect(status().isOk())
189
+ .andExpect(jsonPath("$.title").value("Spring Boot in Action"));
190
+ ```
191
+
192
+ 17. **Use `@MockBean` to isolate the unit under test** (Ch 4). Replaces the real bean in the Spring context with a Mockito mock — cleaner than manual wiring.
193
+
194
+ 18. **Test security explicitly** (Ch 4). Use `.with(user("admin").roles("ADMIN"))` or `@WithMockUser` to assert secured endpoints reject unauthenticated requests.
195
+
196
+ ### Actuator (Ch 7)
197
+
198
+ 19. **Enable Actuator in every production app** (Ch 7). Add `spring-boot-starter-actuator`. At minimum expose `health` and `info`.
199
+
200
+ 20. **Write custom `HealthIndicator` for critical dependencies** (Ch 7):
201
+ ```java
202
+ @Component
203
+ public class DatabaseHealthIndicator implements HealthIndicator {
204
+ @Override
205
+ public Health health() {
206
+ return canConnect() ? Health.up().build()
207
+ : Health.down().withDetail("reason", "timeout").build();
208
+ }
209
+ }
210
+ ```
211
+
212
+ 21. **Add custom metrics via `MeterRegistry`** (Ch 7). Counter, gauge, timer — gives Prometheus/Grafana visibility into business events.
213
+
214
+ 22. **Restrict Actuator exposure in production** (Ch 7):
215
+ ```properties
216
+ management.endpoints.web.exposure.include=health,info
217
+ management.endpoint.health.show-details=when-authorized
218
+ ```
219
+
220
+ ### Deployment (Ch 8)
221
+
222
+ 23. **Package as an executable JAR by default** (Ch 8). `mvn package` produces a fat JAR with embedded Tomcat. Run with `java -jar app.jar`. No application server needed.
223
+
224
+ 24. **Create a production profile** (Ch 8). `application-production.properties` sets `spring.datasource.url`, disables dev tools, sets log levels to WARN.
225
+
226
+ 25. **Use Flyway or Liquibase for database migrations** (Ch 8). Add `spring-boot-starter-flyway`; place scripts in `classpath:db/migration/V1__init.sql`. Never use `spring.jpa.hibernate.ddl-auto=create` in production.
227
+
228
+ ---
229
+
230
+ ## Starter Cheat Sheet (Ch 2, Appendix B)
231
+
232
+ | Need | Starter |
233
+ |------|---------|
234
+ | REST API | `spring-boot-starter-web` |
235
+ | JPA / Hibernate | `spring-boot-starter-data-jpa` |
236
+ | Security | `spring-boot-starter-security` |
237
+ | Observability | `spring-boot-starter-actuator` |
238
+ | Testing | `spring-boot-starter-test` |
239
+ | Thymeleaf views | `spring-boot-starter-thymeleaf` |
240
+ | Redis cache | `spring-boot-starter-data-redis` |
241
+ | Messaging | `spring-boot-starter-amqp` |
242
+ | DB migration | `flyway-core` |
243
+
244
+ ---
245
+
246
+ ## Code Structure Template
247
+
248
+ ```java
249
+ // Main class (Ch 2)
250
+ @SpringBootApplication
251
+ public class LibraryApp {
252
+ public static void main(String[] args) {
253
+ SpringApplication.run(LibraryApp.class, args);
254
+ }
255
+ }
256
+
257
+ // Entity (Ch 2)
258
+ @Entity
259
+ public class Book {
260
+ @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
261
+ private Long id;
262
+ private String title;
263
+ private String isbn;
264
+ // constructors, getters, setters
265
+ }
266
+
267
+ // Repository (Ch 2)
268
+ public interface BookRepository extends JpaRepository<Book, Long> {
269
+ List<Book> findByTitleContainingIgnoreCase(String title);
270
+ }
271
+
272
+ // Service (Ch 2) — constructor injection
273
+ @Service
274
+ public class BookService {
275
+ private final BookRepository repo;
276
+ public BookService(BookRepository repo) { this.repo = repo; }
277
+
278
+ public Book findById(Long id) {
279
+ return repo.findById(id)
280
+ .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
281
+ }
282
+ }
283
+
284
+ // Controller (Ch 2)
285
+ @RestController
286
+ @RequestMapping("/api/books")
287
+ public class BookController {
288
+ private final BookService service;
289
+ public BookController(BookService service) { this.service = service; }
290
+
291
+ @GetMapping("/{id}")
292
+ public ResponseEntity<Book> getBook(@PathVariable Long id) {
293
+ return ResponseEntity.ok(service.findById(id));
294
+ }
295
+
296
+ @PostMapping
297
+ public ResponseEntity<Book> createBook(@RequestBody Book book) {
298
+ Book saved = service.save(book);
299
+ URI location = URI.create("/api/books/" + saved.getId());
300
+ return ResponseEntity.created(location).body(saved);
301
+ }
302
+ }
303
+
304
+ // application.properties (Ch 3)
305
+ // spring.datasource.url=jdbc:postgresql://localhost/library
306
+ // spring.datasource.username=${DB_USER}
307
+ // spring.datasource.password=${DB_PASS}
308
+ // spring.jpa.hibernate.ddl-auto=validate
309
+ // management.endpoints.web.exposure.include=health,info
310
+
311
+ // application-dev.properties (Ch 3)
312
+ // spring.datasource.url=jdbc:h2:mem:library
313
+ // spring.jpa.hibernate.ddl-auto=create-drop
314
+ // logging.level.org.springframework=DEBUG
315
+ ```
316
+
317
+ ---
318
+
319
+ ## Priority of Practices by Impact
320
+
321
+ ### Critical (Security & Correctness)
322
+ - Ch 3: Never hardcode credentials — use `${ENV_VAR}` in properties
323
+ - Ch 3: Secure Actuator endpoints — `env`, `beans`, `shutdown` must require auth
324
+ - Ch 4: Test secured endpoints explicitly — assert 401/403 on unauthenticated requests
325
+ - Ch 8: Never use `ddl-auto=create` in production — use Flyway/Liquibase
326
+
327
+ ### Important (Idiom & Maintainability)
328
+ - Ch 2: Constructor injection over `@Autowired` field injection
329
+ - Ch 2: `@RestController` over `@Controller` + `@ResponseBody` for APIs
330
+ - Ch 2: `Optional` from repository, never `null`
331
+ - Ch 3: `@ConfigurationProperties` over scattered `@Value` for grouped config
332
+ - Ch 3: Profiles for environment differences — not `if` statements
333
+ - Ch 4: `@WebMvcTest` for controller tests — not full `@SpringBootTest`
334
+ - Ch 7: Custom `HealthIndicator` for each critical dependency
335
+
336
+ ### Suggestions (Polish)
337
+ - Ch 3: Custom error pages in `templates/error/` — no code needed
338
+ - Ch 7: Custom metrics via `MeterRegistry` for business events
339
+ - Ch 8: Production profile disables dev tools, sets WARN log level
340
+ - Ch 2: Use `spring-boot-devtools` in dev for live reload
@@ -0,0 +1,39 @@
1
+ {
2
+ "evals": [
3
+ {
4
+ "id": "eval-01-autoconfig-injection-hardcoding",
5
+ "prompt": "Review this Spring Boot code:\n\n```java\n@Configuration\npublic class AppConfig {\n @Bean\n public DataSource dataSource() {\n DriverManagerDataSource ds = new DriverManagerDataSource();\n ds.setUrl(\"jdbc:postgresql://prod-db.internal/orders\");\n ds.setUsername(\"orders_user\");\n ds.setPassword(\"S3cr3tP@ss\");\n return ds;\n }\n\n @Bean\n public ObjectMapper objectMapper() {\n return new ObjectMapper();\n }\n}\n\n@RestController\npublic class OrderController {\n @Autowired\n private OrderRepository orderRepository;\n\n @Autowired\n private OrderService orderService;\n\n @GetMapping(\"/orders/{id}\")\n public Order getOrder(@PathVariable Long id) {\n return orderRepository.findById(id).orElse(null);\n }\n\n @PostMapping(\"/orders\")\n public Order createOrder(@RequestBody Order order) {\n return orderService.place(order);\n }\n}\n```",
6
+ "expectations": [
7
+ "Flag Ch 2/3: Manual DataSource @Bean fights Spring Boot auto-configuration — delete AppConfig.dataSource() and move connection details to application.properties using spring.datasource.url/username/password",
8
+ "Flag Ch 3: Credentials hardcoded in source code — must be externalized to environment variables: spring.datasource.password=${DB_PASS}",
9
+ "Flag Ch 2: ObjectMapper @Bean is unnecessary — Spring Boot auto-configures Jackson; only define a custom ObjectMapper when you need to change behavior",
10
+ "Flag Ch 2: @Autowired field injection on OrderRepository and OrderService — replace with constructor injection for testability",
11
+ "Flag Ch 2: getOrder returns null (200 with null body) when not found — use Optional.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build()) or throw ResponseStatusException(NOT_FOUND)",
12
+ "Flag Ch 2: createOrder returns 200 — POST that creates a resource should return 201 Created with a Location header; use ResponseEntity.created(uri).body(saved)"
13
+ ]
14
+ },
15
+ {
16
+ "id": "eval-02-testing-antipatterns",
17
+ "prompt": "Review this Spring Boot test code:\n\n```java\n@SpringBootTest\npublic class ProductControllerTest {\n @Autowired\n private ProductController controller;\n\n @Autowired\n private ProductRepository repo;\n\n @Test\n public void testGetProduct() {\n Product p = new Product(null, \"Widget\", 9.99);\n repo.save(p);\n Product result = controller.getProduct(p.getId());\n assertNotNull(result);\n assertEquals(\"Widget\", result.getName());\n }\n\n @Test\n public void testCreateProduct() {\n Product p = new Product(null, \"Gadget\", 19.99);\n Product result = controller.createProduct(p);\n assertNotNull(result.getId());\n }\n\n @Test\n public void testAdminEndpoint() {\n // No auth setup — just calls controller directly\n String result = controller.adminDashboard();\n assertNotNull(result);\n }\n}\n```",
18
+ "expectations": [
19
+ "Flag Ch 4: @SpringBootTest loads the full application context for what are simple controller tests — use @WebMvcTest(ProductController.class) with MockMvc for fast, isolated controller tests",
20
+ "Flag Ch 4: Directly calling controller.getProduct() bypasses HTTP layer — no status code, content-type, or header assertions are possible; use MockMvc.perform(get(...)).andExpect(status().isOk())",
21
+ "Flag Ch 4: testAdminEndpoint calls controller directly with no authentication context — use @WithMockUser(roles='ADMIN') and MockMvc to assert 403 for unauthorized and 200 for authorized access",
22
+ "Flag Ch 4: Tests use a real ProductRepository writing to the database — in a @WebMvcTest test, use @MockBean ProductService to isolate the controller from persistence",
23
+ "Flag Ch 4: No negative test cases — missing test for product not found (expect 404), and no test verifying createProduct returns 201 with a Location header",
24
+ "Provide corrected test class using @WebMvcTest, MockMvc, @MockBean, @WithMockUser, and assertions on HTTP status codes and response JSON"
25
+ ]
26
+ },
27
+ {
28
+ "id": "eval-03-idiomatic-spring-boot",
29
+ "prompt": "Review this Spring Boot code:\n\n```java\n@SpringBootApplication\npublic class LibraryApp {\n public static void main(String[] args) {\n SpringApplication.run(LibraryApp.class, args);\n }\n}\n\n@RestController\n@RequestMapping(\"/api/books\")\npublic class BookController {\n private final BookService service;\n\n public BookController(BookService service) {\n this.service = service;\n }\n\n @GetMapping(\"/{id}\")\n public ResponseEntity<Book> getBook(@PathVariable Long id) {\n return ResponseEntity.ok(service.findById(id));\n }\n\n @PostMapping\n public ResponseEntity<Book> createBook(@RequestBody Book book) {\n Book saved = service.save(book);\n URI location = URI.create(\"/api/books/\" + saved.getId());\n return ResponseEntity.created(location).body(saved);\n }\n}\n\n@Service\npublic class BookService {\n private static final Logger log = LoggerFactory.getLogger(BookService.class);\n private final BookRepository repo;\n\n public BookService(BookRepository repo) { this.repo = repo; }\n\n public Book findById(Long id) {\n return repo.findById(id)\n .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));\n }\n\n public Book save(Book book) { return repo.save(book); }\n}\n```\n\n```properties\nspring.datasource.url=${DB_URL:jdbc:h2:mem:library}\nspring.datasource.username=${DB_USER:sa}\nspring.datasource.password=${DB_PASS:}\nspring.jpa.hibernate.ddl-auto=validate\nmanagement.endpoints.web.exposure.include=health,info\n```",
30
+ "expectations": [
31
+ "Recognize this code is idiomatic Spring Boot — do NOT manufacture issues",
32
+ "Acknowledge correct patterns: @SpringBootApplication (Ch 1), constructor injection in both controller and service (Ch 2), ResponseEntity with correct 200/201 status codes and Location header (Ch 2), Optional.orElseThrow with ResponseStatusException for clean 404 (Ch 2), SLF4J logger (Ch 3), externalized config with env-var defaults (Ch 3), Actuator locked to health+info (Ch 7)",
33
+ "At most note: ResponseStatusException could include a descriptive message like 'Book ' + id + ' not found' for better client error messages",
34
+ "At most suggest: adding spring-boot-starter-actuator dependency if not already present to enable the management.endpoints config",
35
+ "Do NOT flag the absence of @Autowired — constructor injection is the preferred style and Spring auto-wires single constructors without any annotation"
36
+ ]
37
+ }
38
+ ]
39
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "pass_rate": 1,
3
+ "passed": 17,
4
+ "total": 17,
5
+ "baseline_pass_rate": 0.647,
6
+ "baseline_passed": 11,
7
+ "baseline_total": 17,
8
+ "delta": 0.353,
9
+ "model": "default",
10
+ "evals_run": 3,
11
+ "date": "2026-03-28",
12
+ "non_standard_provider": true
13
+ }