@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,250 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ audit_css.py — Audit CSS/SCSS/HTML files for Refactoring UI anti-patterns.
4
+ Usage: python audit_css.py <file_or_directory>
5
+
6
+ Detects:
7
+ 1. One-off hex colors (likely not from a design scale)
8
+ 2. Arbitrary pixel values not on a standard spacing scale
9
+ 3. Flat visual hierarchy (too many elements sharing the same font-size)
10
+ 4. Inline styles in HTML (maintainability anti-pattern)
11
+ """
12
+
13
+ import re
14
+ import sys
15
+ from collections import defaultdict
16
+ from pathlib import Path
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # Configuration
20
+ # ---------------------------------------------------------------------------
21
+
22
+ SPACING_SCALE = {4, 8, 10, 12, 14, 16, 18, 20, 24, 28, 32, 36, 40, 48, 56, 64, 80, 96, 112, 128}
23
+ FLAT_HIERARCHY_THRESHOLD = 3 # more than N elements sharing same font-size
24
+ EXTENSIONS = {".css", ".scss", ".html", ".htm"}
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # Regex patterns
28
+ # ---------------------------------------------------------------------------
29
+
30
+ RE_HEX_COLOR = re.compile(r"#([0-9a-fA-F]{3,8})\b")
31
+ RE_PX_VALUE = re.compile(r"\b(\d+)px\b")
32
+ RE_FONT_SIZE_PX = re.compile(r"font-size\s*:\s*(\d+)px", re.IGNORECASE)
33
+ RE_INLINE_STYLE = re.compile(r'\bstyle\s*=\s*["\'][^"\']*["\']', re.IGNORECASE)
34
+ RE_CSS_VAR = re.compile(r"var\(--[^)]+\)")
35
+ RE_SCSS_VAR = re.compile(r"\$[a-zA-Z_][\w-]*")
36
+
37
+ # Pixel properties where arbitrary values matter (spacing/sizing, not borders)
38
+ SPACING_PROPERTIES = re.compile(
39
+ r"(margin|padding|top|right|bottom|left|width|height|gap|"
40
+ r"border-radius|letter-spacing|line-height)\s*:",
41
+ re.IGNORECASE,
42
+ )
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # Helpers
46
+ # ---------------------------------------------------------------------------
47
+
48
+ Issue = dict # {"line": int, "col": int, "code": str, "message": str, "suggestion": str}
49
+
50
+
51
+ def read_lines(path: Path) -> list[str]:
52
+ try:
53
+ return path.read_text(encoding="utf-8", errors="replace").splitlines()
54
+ except OSError as exc:
55
+ print(f"Warning: cannot read {path}: {exc}")
56
+ return []
57
+
58
+
59
+ def is_in_comment(line: str, col: int) -> bool:
60
+ """Rough check: is the match inside a CSS/HTML comment on this line?"""
61
+ before = line[:col]
62
+ return "/*" in before or "<!--" in before or "//" in before
63
+
64
+
65
+ # ---------------------------------------------------------------------------
66
+ # Detectors
67
+ # ---------------------------------------------------------------------------
68
+
69
+ def detect_one_off_hex_colors(lines: list[str], filepath: Path) -> list[Issue]:
70
+ """Flag hex colors that appear only once in the file — likely not from a scale."""
71
+ color_locations: dict[str, list[tuple[int, int]]] = defaultdict(list)
72
+ for lineno, line in enumerate(lines, 1):
73
+ for m in RE_HEX_COLOR.finditer(line):
74
+ # Skip if preceded by var( or $ (already a token)
75
+ prefix = line[max(0, m.start() - 10):m.start()]
76
+ if "var(" in prefix or "$" in prefix[-1:]:
77
+ continue
78
+ normalized = m.group(0).upper()
79
+ color_locations[normalized].append((lineno, m.start()))
80
+
81
+ issues = []
82
+ for color, locations in color_locations.items():
83
+ if len(locations) == 1:
84
+ lineno, col = locations[0]
85
+ issues.append({
86
+ "line": lineno, "col": col + 1,
87
+ "code": "RUI-C01",
88
+ "message": f"One-off color {color} — not reused anywhere in this file",
89
+ "suggestion": f"Extract to a CSS variable: --color-name: {color}; and reference via var(--color-name)",
90
+ })
91
+ return issues
92
+
93
+
94
+ def detect_arbitrary_px_values(lines: list[str], filepath: Path) -> list[Issue]:
95
+ """Flag pixel values on spacing properties that fall outside the spacing scale."""
96
+ issues = []
97
+ in_spacing_context = False
98
+ for lineno, line in enumerate(lines, 1):
99
+ stripped = line.strip()
100
+ if SPACING_PROPERTIES.search(stripped):
101
+ for m in RE_PX_VALUE.finditer(stripped):
102
+ value = int(m.group(1))
103
+ if value == 0 or value in SPACING_SCALE:
104
+ continue
105
+ if is_in_comment(line, m.start()):
106
+ continue
107
+ nearest = min(SPACING_SCALE, key=lambda s: abs(s - value))
108
+ issues.append({
109
+ "line": lineno, "col": m.start() + 1,
110
+ "code": "RUI-S01",
111
+ "message": f"Arbitrary pixel value {value}px not on spacing scale",
112
+ "suggestion": f"Nearest scale value: {nearest}px. Consider using a spacing token.",
113
+ })
114
+ return issues
115
+
116
+
117
+ def detect_flat_hierarchy(lines: list[str], filepath: Path) -> list[Issue]:
118
+ """Flag when many rules share the same font-size — suggests flat visual hierarchy."""
119
+ size_locations: dict[int, list[tuple[int, int]]] = defaultdict(list)
120
+ for lineno, line in enumerate(lines, 1):
121
+ for m in RE_FONT_SIZE_PX.finditer(line):
122
+ size = int(m.group(1))
123
+ size_locations[size].append((lineno, m.start()))
124
+
125
+ issues = []
126
+ for size, locations in size_locations.items():
127
+ if len(locations) > FLAT_HIERARCHY_THRESHOLD:
128
+ # Report at the first occurrence
129
+ lineno, col = locations[0]
130
+ issues.append({
131
+ "line": lineno, "col": col + 1,
132
+ "code": "RUI-H01",
133
+ "message": (
134
+ f"font-size: {size}px used {len(locations)} times — "
135
+ f"indicates flat visual hierarchy"
136
+ ),
137
+ "suggestion": (
138
+ "Refactoring UI: vary font sizes more aggressively to create "
139
+ "clear hierarchy. Use a type scale (e.g. 12, 14, 16, 20, 24, 32, 48px)."
140
+ ),
141
+ })
142
+ return issues
143
+
144
+
145
+ def detect_inline_styles(lines: list[str], filepath: Path) -> list[Issue]:
146
+ """Flag inline style= attributes in HTML files."""
147
+ if filepath.suffix.lower() not in {".html", ".htm"}:
148
+ return []
149
+ issues = []
150
+ for lineno, line in enumerate(lines, 1):
151
+ for m in RE_INLINE_STYLE.finditer(line):
152
+ issues.append({
153
+ "line": lineno, "col": m.start() + 1,
154
+ "code": "RUI-M01",
155
+ "message": f"Inline style attribute — hard to maintain and override",
156
+ "suggestion": "Move styles to a CSS class. Inline styles defeat cascade and make theming impossible.",
157
+ })
158
+ return issues
159
+
160
+
161
+ # ---------------------------------------------------------------------------
162
+ # Scanner
163
+ # ---------------------------------------------------------------------------
164
+
165
+ def scan_file(path: Path) -> list[Issue]:
166
+ lines = read_lines(path)
167
+ if not lines:
168
+ return []
169
+ issues = []
170
+ issues.extend(detect_one_off_hex_colors(lines, path))
171
+ issues.extend(detect_arbitrary_px_values(lines, path))
172
+ issues.extend(detect_flat_hierarchy(lines, path))
173
+ issues.extend(detect_inline_styles(lines, path))
174
+ return sorted(issues, key=lambda i: (i["line"], i["col"]))
175
+
176
+
177
+ def collect_files(target: Path) -> list[Path]:
178
+ if target.is_file():
179
+ return [target] if target.suffix.lower() in EXTENSIONS else []
180
+ return sorted(p for p in target.rglob("*") if p.suffix.lower() in EXTENSIONS)
181
+
182
+
183
+ # ---------------------------------------------------------------------------
184
+ # Report
185
+ # ---------------------------------------------------------------------------
186
+
187
+ def print_report(results: dict[Path, list[Issue]]) -> int:
188
+ total = sum(len(v) for v in results.values())
189
+ files_with_issues = sum(1 for v in results.values() if v)
190
+
191
+ print("=" * 72)
192
+ print("REFACTORING UI CSS AUDIT REPORT")
193
+ print("=" * 72)
194
+
195
+ # Group by code across all files for summary
196
+ by_code: dict[str, list[tuple[Path, Issue]]] = defaultdict(list)
197
+
198
+ for path, issues in results.items():
199
+ if not issues:
200
+ continue
201
+ print(f"\n{path}")
202
+ print("-" * 72)
203
+ for issue in issues:
204
+ print(f" Line {issue['line']:>4}:{issue['col']:<4} [{issue['code']}] {issue['message']}")
205
+ print(f" -> {issue['suggestion']}")
206
+ by_code[issue["code"]].append((path, issue))
207
+
208
+ print("\n" + "=" * 72)
209
+ print("SUMMARY")
210
+ print("=" * 72)
211
+ CODE_LABELS = {
212
+ "RUI-C01": "One-off colors (not from a scale)",
213
+ "RUI-S01": "Arbitrary pixel values",
214
+ "RUI-H01": "Flat visual hierarchy",
215
+ "RUI-M01": "Inline styles in HTML",
216
+ }
217
+ for code, label in CODE_LABELS.items():
218
+ count = len(by_code.get(code, []))
219
+ marker = "[!]" if count else "[OK]"
220
+ print(f" {marker} {label}: {count} issue(s)")
221
+
222
+ print(f"\nFiles scanned : {len(results)}")
223
+ print(f"Files with issues: {files_with_issues}")
224
+ print(f"Total issues : {total}")
225
+ print("=" * 72)
226
+ return total
227
+
228
+
229
+ def main():
230
+ if len(sys.argv) < 2:
231
+ print("Usage: python audit_css.py <file_or_directory>")
232
+ sys.exit(1)
233
+
234
+ target = Path(sys.argv[1])
235
+ if not target.exists():
236
+ print(f"Error: path not found: {target}")
237
+ sys.exit(1)
238
+
239
+ files = collect_files(target)
240
+ if not files:
241
+ print(f"No CSS/SCSS/HTML files found in: {target}")
242
+ sys.exit(0)
243
+
244
+ results = {f: scan_file(f) for f in files}
245
+ total = print_report(results)
246
+ sys.exit(1 if total else 0)
247
+
248
+
249
+ if __name__ == "__main__":
250
+ main()
@@ -0,0 +1,350 @@
1
+ ---
2
+ name: rust-in-action
3
+ version: "1.0"
4
+ license: MIT
5
+ tags: [rust, systems-programming]
6
+ description: >
7
+ Write and review Rust code using systems programming concepts from "Rust in Action"
8
+ by Tim McNamara. Covers language foundations, ownership and borrowing, smart pointers,
9
+ data representation (bits, endianness, floats), memory (stack/heap/virtual), file I/O,
10
+ networking (TCP/HTTP), concurrency (threads/closures), and OS-level programming.
11
+ Use when writing systems-level Rust, working with binary data, raw pointers, file formats,
12
+ network protocols, or low-level memory. Trigger on: "Rust", "systems programming",
13
+ "smart pointers", "endianness", "bit manipulation", "TCP", "raw pointers", "serde",
14
+ "borrow checker", "ownership", "concurrency", "kernel", ".rs files", "cargo".
15
+ ---
16
+
17
+ # Rust in Action Skill
18
+
19
+ Apply the systems programming practices from Tim McNamara's "Rust in Action" to review existing code and write new Rust. This skill operates in two modes: **Review Mode** (analyze code for violations of Rust idioms and systems programming correctness) and **Write Mode** (produce safe, idiomatic, systems-capable Rust from scratch).
20
+
21
+ The key differentiator of this book: Rust is taught through real systems — a CPU simulator, key-value store, NTP client, raw TCP stack, and OS kernel. Practices focus on correctness at the hardware boundary, not just language syntax.
22
+
23
+ ## Reference Files
24
+
25
+ - `practices-catalog.md` — Before/after examples for ownership, smart pointers, bit ops, I/O, networking, concurrency, error wrapping, and state machines
26
+
27
+ ## How to Use This Skill
28
+
29
+ **Before responding**, read `practices-catalog.md` for the topic at hand. For ownership/borrowing issues read the ownership section. For systems/binary data read the data section. For a full review, read all sections.
30
+
31
+ ---
32
+
33
+ ## Mode 1: Code Review
34
+
35
+ When the user asks you to **review** Rust code, follow this process:
36
+
37
+ ### Step 1: Identify the Domain
38
+ Determine whether the code is application-level, systems-level (binary data, I/O, networking, memory), or concurrent. The review focus shifts accordingly.
39
+
40
+ ### Step 2: Analyze the Code
41
+
42
+ **Critical rule**: Only flag genuine issues. If a pattern is idiomatic Rust, acknowledge it as correct. Do not manufacture problems where none exist. When code is well-written, say so and offer only minor suggestions. See the "Idiomatic Patterns — Do NOT Flag as Issues" section for patterns that must never be flagged.
43
+
44
+ <core_principles>
45
+ Check these areas in order of severity:
46
+
47
+ 1. **Ownership & Borrowing** (Ch 4): Unnecessary `.clone()`? Value moved when a borrow would suffice? Use references where full ownership is not required.
48
+ 2. **Smart Pointer Choice** (Ch 6): Is the right pointer type used? `Box<T>` for heap, `Rc<T>` for single-thread shared, `Arc<T>` for multi-thread shared, `RefCell<T>` for interior mutability (single-thread), `Mutex<T>` for interior mutability (multi-thread). `Cow<T>` when data is usually read but occasionally mutated.
49
+ 3. **Error Handling** (Ch 3, 8): `.unwrap()` or `.expect()` where `?` belongs? For library code, define a custom error type that wraps downstream errors via `From` impl. Never leak internal error types across the public API boundary.
50
+ 4. **Binary Data & Endianness** (Ch 5, 7): Are integer byte representations explicit? Use `to_le_bytes()` / `from_le_bytes()` / `to_be_bytes()`. Validate with checksums when writing binary formats. Use `serde` + `bincode` for structured serialization.
51
+ 5. **Memory** (Ch 6): Is `unsafe` minimized? Raw pointer use must be bounded by a safe abstraction. Stack vs heap allocation: prefer stack; use `Box` only when size is unknown at compile time or you need heap lifetime.
52
+ 6. **File & I/O** (Ch 7): Use `BufReader`/`BufWriter` for large files. Handle `ENOENT`, `EPERM`, `ENOSPC` distinctly — don't collapse I/O errors to strings. Use `std::fs::Path` for type-safe path handling.
53
+ 7. **Networking** (Ch 8): TCP state is implicit in OS — model explicit state machines with enums. Use trait objects (`Box<dyn Trait>`) only when heterogeneous runtime dispatch is needed. Prefer `impl Trait` for static dispatch.
54
+ 8. **Concurrency** (Ch 10): Closures passed to threads must be `'static` or use `move`. Shared mutable state needs `Arc<Mutex<T>>`. Use channels for message passing over shared state. Thread pool patterns over spawning one thread per task.
55
+ 9. **Time** (Ch 9): Don't use `std::time::SystemTime` for elapsed measurement — it can go backwards. Use `std::time::Instant` for durations. For network time, NTP requires epoch conversion (NTP epoch: 1900 vs Unix: 1970 — offset 70 years = 2_208_988_800 seconds).
56
+ 10. **Idioms**: Iterator adapters over manual loops. `for item in &collection` not `for i in 0..collection.len()`. `if let`/`while let` for single-variant matching. Exhaustive `match` — no silent wildcard arms.
57
+ </core_principles>
58
+
59
+ ### Step 3: Report Findings
60
+ For each issue, report:
61
+ - **Chapter reference** (e.g., "Ch 6: Smart Pointers")
62
+ - **Location** in the code
63
+ - **What's wrong** (the anti-pattern)
64
+ - **How to fix it** (the idiomatic / systems-correct approach)
65
+ - **Priority**: Critical (safety/UB/data corruption), Important (idiom/correctness), Suggestion (polish)
66
+
67
+ ### Step 4: Provide Fixed Code
68
+ Offer a corrected version with comments explaining each change.
69
+
70
+ ---
71
+
72
+ ## Mode 2: Writing New Code
73
+
74
+ When the user asks you to **write** new Rust code, apply these core principles:
75
+
76
+ <core_principles>
77
+ ### Language Foundations (Ch 2)
78
+
79
+ 1. **Use cargo, not rustc directly** (Ch 2). `cargo new`, `cargo build`, `cargo test`, `cargo doc`. Add third-party crates via `Cargo.toml` — never manually link.
80
+
81
+ 2. **Prefer integer types that match the domain** (Ch 2). Use `u8` for bytes, `u16`/`u32`/`u64` for protocol fields sized to spec, `i64` for timestamps. Avoid default `usize` for domain values.
82
+
83
+ 3. **Use `loop` for retry/event loops; `while` for condition-driven; `for` for iteration** (Ch 2). Never use `loop { if cond { break } }` where `while cond {}` is clearer.
84
+
85
+ ### Compound Types & Traits (Ch 3)
86
+
87
+ 4. **Model domain state with enums, not stringly-typed flags** (Ch 3). Enums with data (`enum Packet { Ack(u32), Data(Vec<u8>) }`) replace boolean + optional pairs and make invalid states unrepresentable.
88
+
89
+ 5. **Implement `new()` as the canonical constructor** (Ch 3). `impl MyStruct { pub fn new(...) -> Self { ... } }`. Use `Default` for zero-value construction.
90
+
91
+ 6. **Implement `std::fmt::Display` for user-facing output, `Debug` via derive** (Ch 3). Derive `Debug`; hand-implement `Display`. Never use `{:?}` in user-facing messages.
92
+
93
+ 7. **Use `pub(crate)` to limit visibility to the crate; keep internals private** (Ch 3). Public API surface should be minimal and intentional.
94
+
95
+ 8. **Document public items with `///` rustdoc comments** (Ch 3). Include examples in doc comments — `cargo test` runs them.
96
+
97
+ ### Ownership, Borrowing & Smart Pointers (Ch 4, 6)
98
+
99
+ 9. **Use references where full ownership is not required** (Ch 4). Pass `&T` for read, `&mut T` for write. Only transfer ownership when the callee must own (e.g., storing in a struct).
100
+
101
+ 10. **Choose smart pointers by use case** (Ch 6):
102
+ - `Box<T>` — heap allocation, single owner, unknown size at compile time
103
+ - `Rc<T>` — shared ownership, single-threaded
104
+ - `Arc<T>` — shared ownership, multi-threaded
105
+ - `Cell<T>` — interior mutability for `Copy` types, single-threaded
106
+ - `RefCell<T>` — interior mutability for non-`Copy`, single-threaded, runtime borrow checks
107
+ - `Cow<'a, T>` — clone-on-write, avoids allocation when data is only read
108
+ - `Arc<Mutex<T>>` — shared mutable state across threads
109
+
110
+ 11. **Never use `Rc` across thread boundaries** (Ch 6). The compiler enforces this — `Rc` is not `Send`. Use `Arc` instead.
111
+
112
+ 12. **Minimize `unsafe` blocks; wrap them in safe abstractions** (Ch 6). Raw pointers (`*const T`, `*mut T`) must be bounded within a module or function that upholds safety invariants. Document the safety contract with `// SAFETY:` comments.
113
+
114
+ ### Data Representation (Ch 5)
115
+
116
+ 13. **Be explicit about endianness in binary protocols** (Ch 5, 7). Use `u32::to_le_bytes()`, `u32::from_be_bytes()` etc. Never assume native endianness when writing to disk or network.
117
+
118
+ 14. **Use bit operations to inspect and build packed data** (Ch 5). AND (`&`) to isolate bits, OR (`|`) to set bits, shift (`<<`, `>>`) to position. Use named constants for masks: `const SIGN_BIT: u32 = 0x8000_0000`.
119
+
120
+ 15. **Validate binary data with checksums** (Ch 7). For key-value stores and file formats, store a CRC or hash alongside data. Verify on read before trusting.
121
+
122
+ ### Files & Storage (Ch 7)
123
+
124
+ 16. **Use `BufReader`/`BufWriter` for file I/O** (Ch 7). Raw `File::read()` makes a syscall per call. `BufReader` batches reads into user-space buffer.
125
+
126
+ 17. **Use `serde` + `bincode` for binary serialization** (Ch 7). Add `#[derive(Serialize, Deserialize)]`; let `bincode::serialize`/`deserialize` handle encoding. Use `serde_json` for human-readable formats.
127
+
128
+ 18. **Use `std::path::Path` and `PathBuf` for file paths** (Ch 7). Never build paths with string concatenation. Use `path.join()`, `path.extension()`, `path.file_name()`.
129
+
130
+ ### Networking (Ch 8)
131
+
132
+ 19. **Model protocol state explicitly with enums** (Ch 8). A TCP connection has states (SYN_SENT, ESTABLISHED, CLOSE_WAIT, etc.). Encode them as enum variants — the compiler enforces valid transitions.
133
+
134
+ 20. **Wrap library errors in a domain error type** (Ch 8). When a function calls multiple libraries (network + I/O + parse), define an enum that wraps each. Implement `From<LibError> for DomainError` so `?` converts automatically.
135
+
136
+ 21. **Use trait objects only for heterogeneous runtime dispatch** (Ch 8). `Vec<Box<dyn Animal>>` is correct when you have a mixed collection. For a single concrete type, `impl Trait` is zero-cost.
137
+
138
+ ### Concurrency (Ch 10)
139
+
140
+ 22. **Use `move` closures when passing to threads** (Ch 10). `thread::spawn(move || { ... })` transfers ownership of captured variables into the thread. This is required when the closure outlives the current stack frame.
141
+
142
+ 23. **Use `Arc::clone()` explicitly, not `.clone()` on a value** (Ch 10). `Arc::clone(&ptr)` is idiomatic — it's cheap (increments a reference count). Avoid `.clone()` on the inner value.
143
+
144
+ 24. **Use channels for work distribution; `Arc<Mutex<T>>` for shared state** (Ch 10). Channels (`std::sync::mpsc`) are simpler and safer. Use shared state only when channels don't fit (e.g., result collection).
145
+
146
+ 25. **Use thread pools over raw `thread::spawn` per task** (Ch 10). Spawning one thread per request doesn't scale. Use `rayon`, `tokio`, or a manual pool with a bounded queue.
147
+
148
+ ### Time (Ch 9)
149
+
150
+ 26. **Use `Instant` for elapsed time, `SystemTime` for wall clock** (Ch 9). `SystemTime` can go backwards (NTP adjustments, leap seconds). `Instant` is monotonic.
151
+
152
+ 27. **Apply the NTP epoch offset when working with network time** (Ch 9). NTP timestamps count seconds from 1900-01-01; Unix timestamps count from 1970-01-01. Offset: `2_208_988_800u64` seconds.
153
+ </core_principles>
154
+
155
+ ---
156
+
157
+ ## Smart Pointer Selection Guide (Ch 6)
158
+
159
+ ```
160
+ Is the data shared across threads?
161
+ ├── Yes → Arc<T> (read-only) or Arc<Mutex<T>> (mutable)
162
+ └── No
163
+ ├── Shared (multiple owners, single thread)?
164
+ │ └── Rc<T> (read-only) or Rc<RefCell<T>> (mutable)
165
+ └── Single owner
166
+ ├── Size unknown at compile time / recursive type?
167
+ │ └── Box<T>
168
+ ├── Usually read, occasionally cloned/modified?
169
+ │ └── Cow<'a, T>
170
+ └── Interior mutability needed?
171
+ ├── Copy type → Cell<T>
172
+ └── Non-Copy → RefCell<T>
173
+ ```
174
+
175
+ ---
176
+
177
+ ## Code Structure Templates
178
+
179
+ <examples>
180
+ <example id="1" title="Binary Protocol Field (Ch 5, 7)">
181
+ ```rust
182
+ /// Parse a 4-byte big-endian u32 from a byte buffer at offset.
183
+ fn read_u32_be(buf: &[u8], offset: usize) -> Result<u32, ParseError> {
184
+ buf.get(offset..offset + 4)
185
+ .ok_or(ParseError::UnexpectedEof)
186
+ .map(|b| u32::from_be_bytes(b.try_into().unwrap()))
187
+ }
188
+
189
+ const FLAGS_MASK: u8 = 0b0000_1111; // isolate lower 4 bits
190
+ fn extract_flags(byte: u8) -> u8 {
191
+ byte & FLAGS_MASK
192
+ }
193
+ ```
194
+ </example>
195
+
196
+ <example id="2" title="Library Error Type (Ch 8)">
197
+ ```rust
198
+ #[derive(Debug)]
199
+ pub enum AppError {
200
+ Io(std::io::Error),
201
+ Network(std::net::AddrParseError),
202
+ Parse(String),
203
+ }
204
+
205
+ impl std::fmt::Display for AppError {
206
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207
+ match self {
208
+ AppError::Io(e) => write!(f, "I/O error: {e}"),
209
+ AppError::Network(e) => write!(f, "network error: {e}"),
210
+ AppError::Parse(msg) => write!(f, "parse error: {msg}"),
211
+ }
212
+ }
213
+ }
214
+
215
+ impl std::error::Error for AppError {}
216
+ impl From<std::io::Error> for AppError {
217
+ fn from(e: std::io::Error) -> Self { AppError::Io(e) }
218
+ }
219
+ impl From<std::net::AddrParseError> for AppError {
220
+ fn from(e: std::net::AddrParseError) -> Self { AppError::Network(e) }
221
+ }
222
+ ```
223
+ </example>
224
+
225
+ <example id="3" title="State Machine with Enum (Ch 8)">
226
+ ```rust
227
+ #[derive(Debug, Clone, PartialEq)]
228
+ enum ConnectionState {
229
+ Idle,
230
+ Connecting { addr: std::net::SocketAddr },
231
+ Connected { stream: std::net::TcpStream },
232
+ Closed,
233
+ }
234
+
235
+ impl ConnectionState {
236
+ fn connect(addr: std::net::SocketAddr) -> Result<Self, AppError> {
237
+ let stream = std::net::TcpStream::connect(addr)?;
238
+ Ok(ConnectionState::Connected { stream })
239
+ }
240
+ }
241
+ ```
242
+ </example>
243
+
244
+ <example id="4" title="Thread Pool Pattern (Ch 10)">
245
+ ```rust
246
+ use std::sync::{Arc, Mutex};
247
+ use std::sync::mpsc;
248
+ use std::thread;
249
+
250
+ type Job = Box<dyn FnOnce() + Send + 'static>;
251
+
252
+ struct ThreadPool {
253
+ sender: mpsc::Sender<Job>,
254
+ }
255
+
256
+ impl ThreadPool {
257
+ fn new(size: usize) -> Self {
258
+ let (sender, receiver) = mpsc::channel::<Job>();
259
+ let receiver = Arc::new(Mutex::new(receiver));
260
+
261
+ for _ in 0..size {
262
+ let rx = Arc::clone(&receiver);
263
+ thread::spawn(move || loop {
264
+ let job = rx.lock().expect("mutex poisoned").recv();
265
+ match job {
266
+ Ok(f) => f(),
267
+ Err(_) => break, // channel closed
268
+ }
269
+ });
270
+ }
271
+ ThreadPool { sender }
272
+ }
273
+
274
+ fn execute(&self, f: impl FnOnce() + Send + 'static) {
275
+ self.sender.send(Box::new(f)).expect("thread pool closed");
276
+ }
277
+ }
278
+ ```
279
+ </example>
280
+ </examples>
281
+
282
+ ---
283
+
284
+ <strengths_to_praise>
285
+ ## Idiomatic Patterns — Do NOT Flag as Issues
286
+
287
+ When reviewing code, recognize these patterns as **correct and idiomatic**. Do not manufacture issues from them:
288
+
289
+ - **`Arc<Mutex<T>>`** — the standard pattern for shared mutable state across threads (Ch 6, 10). This is correct and idiomatic. Never call it redundant, a design error, or suggest removing/replacing it when it is the appropriate choice. When a `Store` or similar struct wraps shared mutable data accessed by multiple threads, `Arc<Mutex<Vec<u8>>>` is exactly right — acknowledge it as such.
290
+ - **`BufReader<File>`** — explicitly recommended for batched I/O (Ch 7). Never call it overhead-adding or harmful.
291
+ - **`AtomicU64` with `Ordering::Relaxed`** — appropriate for counters where exact cross-thread ordering is not required, e.g. telemetry, stats (Ch 10). Not a bug.
292
+ - **`&Path` parameters** — correct; prefer `&Path` over `&str` or `String` for file paths (Ch 7).
293
+ - **Custom error enums with `Display` + `Error` + `From` impls** — the canonical library error pattern (Ch 3, 8). Praise it, don't critique it.
294
+ - **`.expect("mutex poisoned")` with a descriptive reason** — correct idiom for panicking on a poisoned mutex (Ch 10).
295
+ - **`Arc::clone(&ptr)` idiom** — correct; makes cheap refcount increment explicit (Ch 10).
296
+ - **`move` closures for threads** — required and correct (Ch 10).
297
+
298
+ If code is already correct, say so. Only flag real issues. If the only things left to say are minor suggestions, label them as suggestions, not bugs or important issues.
299
+ </strengths_to_praise>
300
+
301
+ ---
302
+
303
+ <anti_patterns>
304
+ ## Priority of Practices by Impact
305
+
306
+ ### Critical (Safety, Correctness, UB)
307
+ - Ch 4: Borrow, don't clone — never move when a reference suffices
308
+ - Ch 6: Choose the right smart pointer — `Rc` is not thread-safe; don't share across threads
309
+ - Ch 6: Wrap `unsafe` in safe abstractions — document safety contracts with `// SAFETY:`
310
+ - Ch 5/7: Explicit endianness — wrong byte order silently corrupts binary data
311
+ - Ch 9: Use `Instant` for elapsed time — `SystemTime` can go backwards
312
+
313
+ ### Important (Idiom & Maintainability)
314
+ - Ch 3: Domain enums over stringly-typed state — invalid states should not compile
315
+ - Ch 8: Wrap downstream errors in a domain error type with `From` impls
316
+ - Ch 8: `impl Trait` over `dyn Trait` when types are homogeneous
317
+ - Ch 10: `move` closures for threads — required when closure outlives the stack frame
318
+ - Ch 10: `Arc::clone()` idiom — makes cheap pointer clone explicit
319
+
320
+ ### Suggestions (Systems Polish)
321
+ - Ch 2: Size integer types to the protocol spec — `u8` for bytes, `u16` for ports
322
+ - Ch 5: Named bit-mask constants — `const SIGN_BIT: u32 = 0x8000_0000`
323
+ - Ch 7: `BufReader`/`BufWriter` for all file I/O — syscall batching
324
+ - Ch 7: Checksums on binary writes — detect corruption on read
325
+ - Ch 9: NTP epoch offset constant — `const NTP_UNIX_OFFSET: u64 = 2_208_988_800`
326
+
327
+ ### Anti-Patterns to Always Flag
328
+ - **`static mut`** — a data race waiting to happen in concurrent code; replace with `Arc<Mutex<T>>` or an atomic type (Ch 6, 10)
329
+ - **`from_ne_bytes` / `to_ne_bytes` in protocols** — native endianness is host-dependent; always use `from_le_bytes`/`from_be_bytes` (Ch 5, 7)
330
+ - **`.unwrap()` in library or I/O code** — panics on error; use `?` with `Result` propagation (Ch 3, 8)
331
+ - **`Box<Vec<T>>`** — `Vec<T>` already heap-allocates; wrapping it in `Box` adds a pointless double-indirection; return `Vec<T>` directly (Ch 6)
332
+ - **`Rc<T>` passed to `thread::spawn`** — `Rc` is not `Send`; use `Arc<T>` for shared ownership across threads (Ch 6)
333
+ - **Indexing slices without bounds checks** — `bytes[0..4]` panics on short input; use `bytes.get(0..4).ok_or(...)` (Ch 5, 7)
334
+ - **Not returning `JoinHandle` from thread-spawning functions** — callers cannot join threads or detect panics; return `thread::JoinHandle<()>` (Ch 10)
335
+ </anti_patterns>
336
+
337
+ <guidelines>
338
+ ## Review Guidelines Summary
339
+
340
+ When reviewing Rust code:
341
+ 1. Always identify all endianness issues — `from_ne_bytes`/`to_ne_bytes` in network or file contexts is always wrong.
342
+ 2. Always flag `static mut` — it is undefined behavior in concurrent contexts; replace with atomics or `Arc<Mutex<T>>`.
343
+ 3. Always flag `.unwrap()` in non-test I/O or library code — use `?` and `Result`.
344
+ 4. Flag `Box<Vec<T>>` as redundant double-indirection.
345
+ 5. Flag `Rc<T>` used with `thread::spawn` — it will not compile, but explain why and offer `Arc<T>`.
346
+ 6. Flag unchecked slice indexing — suggest `.get(range).ok_or(...)`.
347
+ 7. Flag thread-spawning functions that discard the `JoinHandle`.
348
+ 8. Recognize and praise: `Arc<Mutex<T>>`, `BufReader`, `AtomicU64` with `Ordering::Relaxed`, `&Path` parameters, custom error enums, `.expect("mutex poisoned")`, `Arc::clone(&ptr)`, `move` closures.
349
+ 9. Always provide corrected code with comments explaining each change.
350
+ </guidelines>
@@ -0,0 +1,38 @@
1
+ {
2
+ "evals": [
3
+ {
4
+ "id": "eval-01-endianness-unwrap-static-mut",
5
+ "prompt": "Review this Rust code:\n\n```rust\nstatic mut PACKET_COUNT: u32 = 0;\n\nfn parse_header(bytes: &[u8]) -> u32 {\n let id = u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);\n unsafe { PACKET_COUNT += 1; }\n id\n}\n\nfn read_data(path: &str) -> Vec<u8> {\n std::fs::read(path).unwrap()\n}\n\nfn write_id(id: u32) -> Vec<u8> {\n id.to_ne_bytes().to_vec()\n}\n```",
6
+ "expectations": [
7
+ "Flag Ch 5/7: from_ne_bytes uses native endianness — silently produces wrong results on big-endian hosts; use from_le_bytes or from_be_bytes to match the protocol spec",
8
+ "Flag Ch 6/10: static mut PACKET_COUNT is a data race in concurrent code — replace with Arc<Mutex<u32>> or an atomic (std::sync::atomic::AtomicU32)",
9
+ "Flag Ch 3: .unwrap() in read_data will panic on I/O error — return Result<Vec<u8>, std::io::Error> and use ?",
10
+ "Flag Ch 7: to_ne_bytes in write_id — same endianness problem as parse_header; must match the parse side",
11
+ "Note that parse_header does no bounds checking before indexing bytes[0..3] — should use bytes.get(0..4).ok_or(...) to avoid a panic on short input",
12
+ "Provide corrected versions using from_le_bytes/to_le_bytes, AtomicU32 or Arc<Mutex<u32>>, and Result"
13
+ ]
14
+ },
15
+ {
16
+ "id": "eval-02-smart-pointer-choice",
17
+ "prompt": "Review this Rust code:\n\n```rust\nuse std::rc::Rc;\nuse std::thread;\n\nstruct Cache {\n data: Rc<Vec<String>>,\n}\n\nimpl Cache {\n fn spawn_reader(&self) {\n let d = Rc::clone(&self.data);\n thread::spawn(move || {\n println!(\"{:?}\", d);\n });\n }\n}\n\nfn make_buffer(large: bool) -> Box<Vec<u8>> {\n if large {\n Box::new(vec![0u8; 1024 * 1024])\n } else {\n Box::new(vec![0u8; 64])\n }\n}\n```",
18
+ "expectations": [
19
+ "Flag Ch 6: Rc is not Send — sharing Rc across thread boundaries is a compile error; replace Rc<Vec<String>> with Arc<Vec<String>>",
20
+ "Flag Ch 6: Box<Vec<u8>> is redundant — Vec<u8> already heap-allocates; Box<Vec<T>> is a double-indirection with no benefit; return Vec<u8> directly",
21
+ "Note that Arc::clone(&self.data) is the correct idiom for incrementing an Arc refcount — it makes the cheap clone explicit",
22
+ "Note that spawn_reader returns no JoinHandle — callers cannot join the thread or detect panics; suggest returning thread::JoinHandle<()>",
23
+ "Provide corrected versions using Arc<Vec<String>>, returning Vec<u8> directly, and returning JoinHandle from spawn_reader"
24
+ ]
25
+ },
26
+ {
27
+ "id": "eval-03-idiomatic-systems-rust",
28
+ "prompt": "Review this Rust code:\n\n```rust\nuse std::sync::{Arc, Mutex};\nuse std::sync::atomic::{AtomicU64, Ordering};\nuse std::path::Path;\nuse std::io::{BufReader, Read};\nuse std::fs::File;\n\n#[derive(Debug)]\npub enum StoreError {\n Io(std::io::Error),\n Corrupt { offset: usize },\n}\n\nimpl std::fmt::Display for StoreError {\n fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n match self {\n StoreError::Io(e) => write!(f, \"io: {e}\"),\n StoreError::Corrupt { offset } => write!(f, \"corrupt at byte {offset}\"),\n }\n }\n}\n\nimpl std::error::Error for StoreError {}\nimpl From<std::io::Error> for StoreError {\n fn from(e: std::io::Error) -> Self { StoreError::Io(e) }\n}\n\nstruct Store {\n reads: AtomicU64,\n data: Arc<Mutex<Vec<u8>>>,\n}\n\nimpl Store {\n fn new() -> Self {\n Store { reads: AtomicU64::new(0), data: Arc::new(Mutex::new(vec![])) }\n }\n\n fn load(&self, path: &Path) -> Result<(), StoreError> {\n let f = File::open(path)?;\n let mut reader = BufReader::new(f);\n let mut buf = vec![];\n reader.read_to_end(&mut buf)?;\n *self.data.lock().expect(\"mutex poisoned\") = buf;\n Ok(())\n }\n\n fn read(&self, offset: usize, len: usize) -> Result<Vec<u8>, StoreError> {\n self.reads.fetch_add(1, Ordering::Relaxed);\n let data = self.data.lock().expect(\"mutex poisoned\");\n data.get(offset..offset + len)\n .map(|s| s.to_vec())\n .ok_or(StoreError::Corrupt { offset })\n }\n}\n```",
29
+ "expectations": [
30
+ "Recognize this code is idiomatic systems Rust — do NOT manufacture issues",
31
+ "Acknowledge: StoreError with Display + Error + From<io::Error> for ? propagation (Ch 3, 8); BufReader for batched I/O (Ch 7); &Path parameter (Ch 7); Arc<Mutex<T>> for shared mutable data (Ch 6, 10); AtomicU64 for lock-free counter (Ch 10); .expect('mutex poisoned') with reason (Ch 10)",
32
+ "At most suggest: reads counter could use Ordering::SeqCst if cross-thread visibility of exact count matters; or note that Relaxed is fine for approximate telemetry",
33
+ "At most suggest: Default could be derived or implemented for Store alongside new(), enabling Store::default() construction",
34
+ "Do NOT flag AtomicU64 with Relaxed ordering as a bug — it is appropriate for a read counter where exact ordering across threads is not required"
35
+ ]
36
+ }
37
+ ]
38
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "pass_rate": 1,
3
+ "passed": 16,
4
+ "total": 16,
5
+ "baseline_pass_rate": 0.438,
6
+ "baseline_passed": 7,
7
+ "baseline_total": 16,
8
+ "delta": 0.563,
9
+ "model": "default",
10
+ "evals_run": 3,
11
+ "date": "2026-03-29",
12
+ "non_standard_provider": true
13
+ }