@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,173 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ lint.py — Ruff linter tuned to Effective Python items.
4
+ Usage: python lint.py <path>
5
+
6
+ Runs ruff with rules that map directly to Effective Python advice,
7
+ then annotates each violation with the relevant item number and title.
8
+ """
9
+
10
+ import json
11
+ import subprocess
12
+ import sys
13
+ import tempfile
14
+ import textwrap
15
+ from pathlib import Path
16
+
17
+ # Maps ruff rule code prefixes/exact codes -> (Item number, short description)
18
+ RULE_TO_ITEM = {
19
+ "E711": ("Item 2", "PEP 8: use 'is None' / 'is not None', not == None"),
20
+ "E712": ("Item 2", "PEP 8: use 'is True' / 'is False', not == True/False"),
21
+ "B006": ("Item 24", "Avoid mutable default arguments"),
22
+ "B007": ("Item 7", "Unused loop variable — use _ for throwaway"),
23
+ "B008": ("Item 24", "Do not call mutable objects as default arguments"),
24
+ "C400": ("Item 27", "Rewrite map() as a list comprehension"),
25
+ "C401": ("Item 27", "Rewrite set() with a comprehension"),
26
+ "C402": ("Item 27", "Rewrite dict() with a dict comprehension"),
27
+ "C403": ("Item 27", "Rewrite list() with a list comprehension"),
28
+ "C404": ("Item 27", "Rewrite list of tuples as dict comprehension"),
29
+ "C405": ("Item 27", "Rewrite set literal — unnecessary literal call"),
30
+ "C406": ("Item 27", "Rewrite dict literal — unnecessary literal call"),
31
+ "C408": ("Item 27", "Rewrite dict()/list()/tuple() with literal"),
32
+ "C409": ("Item 28", "Unnecessary literal in tuple()"),
33
+ "C410": ("Item 28", "Unnecessary literal in list()"),
34
+ "C411": ("Item 27", "Unnecessary list() call"),
35
+ "C413": ("Item 27", "Unnecessary list/reversed() around sorted()"),
36
+ "C414": ("Item 27", "Unnecessary double cast in comprehension"),
37
+ "C415": ("Item 29", "Unnecessary subscript reversal in comprehension"),
38
+ "C416": ("Item 27", "Unnecessary list comprehension — use list()"),
39
+ "C417": ("Item 27", "Unnecessary map() — use generator/comprehension"),
40
+ "SIM101": ("Item 7", "Merge duplicate isinstance() checks with tuple"),
41
+ "SIM102": ("Item 7", "Collapse nested if into single if"),
42
+ "SIM103": ("Item 7", "Return condition directly, not if/else True/False"),
43
+ "SIM105": ("Item 35", "Use contextlib.suppress instead of try/except/pass"),
44
+ "SIM108": ("Item 7", "Use ternary operator instead of if/else block"),
45
+ "SIM110": ("Item 27", "Use comprehension instead of for-loop with append"),
46
+ "SIM115": ("Item 66", "Use context manager for open()"),
47
+ "SIM117": ("Item 66", "Merge nested with statements"),
48
+ "UP001": ("Item 2", "pyupgrade: use modern Python syntax"),
49
+ "UP003": ("Item 2", "pyupgrade: use type() instead of deprecated form"),
50
+ "UP006": ("Item 90", "Use 'list' instead of 'List' for type hints (3.9+)"),
51
+ "UP007": ("Item 90", "Use 'X | Y' instead of 'Optional[X]' (3.10+)"),
52
+ "UP008": ("Item 90", "Use 'super()' without arguments"),
53
+ "UP009": ("Item 2", "pyupgrade: UTF-8 encoding declaration unnecessary"),
54
+ "UP010": ("Item 2", "pyupgrade: unnecessary __future__ import"),
55
+ "UP032": ("Item 4", "Use f-string instead of .format()"),
56
+ "UP034": ("Item 2", "pyupgrade: extraneous parentheses"),
57
+ }
58
+
59
+ RUFF_CONFIG = textwrap.dedent("""\
60
+ [lint]
61
+ select = [
62
+ "E711", "E712",
63
+ "B006", "B007", "B008",
64
+ "C4",
65
+ "SIM",
66
+ "UP",
67
+ ]
68
+ ignore = []
69
+ """)
70
+
71
+
72
+ def find_item(code: str):
73
+ if code in RULE_TO_ITEM:
74
+ return RULE_TO_ITEM[code]
75
+ # Try prefix match (e.g. SIM, UP, C4xx)
76
+ for prefix_len in (5, 4, 3, 2):
77
+ prefix = code[:prefix_len]
78
+ if prefix in RULE_TO_ITEM:
79
+ return RULE_TO_ITEM[prefix]
80
+ return None
81
+
82
+
83
+ def run_ruff(target: Path, config_path: Path):
84
+ cmd = [
85
+ "ruff", "check",
86
+ "--config", str(config_path),
87
+ "--output-format", "json",
88
+ str(target),
89
+ ]
90
+ try:
91
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
92
+ return result.stdout, result.stderr, result.returncode
93
+ except FileNotFoundError:
94
+ return None, "NOT_FOUND", 1
95
+ except subprocess.TimeoutExpired:
96
+ return None, "TIMEOUT", 1
97
+
98
+
99
+ def main():
100
+ if len(sys.argv) < 2:
101
+ print("Usage: python lint.py <path>")
102
+ sys.exit(1)
103
+
104
+ target = Path(sys.argv[1])
105
+ if not target.exists():
106
+ print(f"Error: path not found: {target}")
107
+ sys.exit(1)
108
+
109
+ with tempfile.NamedTemporaryFile(
110
+ mode="w", suffix=".toml", prefix="ruff_ep_", delete=False
111
+ ) as tmp:
112
+ tmp.write(RUFF_CONFIG)
113
+ config_path = Path(tmp.name)
114
+
115
+ try:
116
+ stdout, stderr, returncode = run_ruff(target, config_path)
117
+ finally:
118
+ config_path.unlink(missing_ok=True)
119
+
120
+ if stdout is None:
121
+ if stderr == "NOT_FOUND":
122
+ print("Error: ruff is not installed.")
123
+ print("Install it with: pip install ruff")
124
+ print("Or globally: pipx install ruff")
125
+ elif stderr == "TIMEOUT":
126
+ print("Error: ruff timed out.")
127
+ else:
128
+ print(f"Error running ruff: {stderr}")
129
+ sys.exit(1)
130
+
131
+ print(f"Effective Python lint report — {target}")
132
+ print("-" * 70)
133
+
134
+ try:
135
+ violations = json.loads(stdout) if stdout.strip() else []
136
+ except json.JSONDecodeError:
137
+ print("Raw ruff output:")
138
+ print(stdout)
139
+ sys.exit(returncode)
140
+
141
+ if not violations:
142
+ print("No violations found. Code aligns well with Effective Python.")
143
+ sys.exit(0)
144
+
145
+ # Group by item
146
+ by_item: dict[str, list] = {}
147
+ for v in violations:
148
+ code = v.get("code", "?")
149
+ mapping = find_item(code)
150
+ item_key = mapping[0] if mapping else "Other"
151
+ by_item.setdefault(item_key, []).append((v, mapping))
152
+
153
+ total = 0
154
+ for item_key in sorted(by_item):
155
+ entries = by_item[item_key]
156
+ item_desc = entries[0][1][1] if entries[0][1] else "ruff violation"
157
+ print(f"\n[{item_key}] {item_desc}")
158
+ for v, _ in entries:
159
+ loc = v.get("location", {})
160
+ row, col = loc.get("row", "?"), loc.get("column", "?")
161
+ filename = Path(v.get("filename", str(target))).name
162
+ message = v.get("message", "")
163
+ code = v.get("code", "?")
164
+ print(f" {filename}:{row}:{col} [{code}] {message}")
165
+ total += 1
166
+
167
+ print(f"\n{'-' * 70}")
168
+ print(f"Total violations: {total}")
169
+ sys.exit(1 if violations else 0)
170
+
171
+
172
+ if __name__ == "__main__":
173
+ main()
@@ -0,0 +1,262 @@
1
+ ---
2
+ name: effective-typescript
3
+ version: "1.0"
4
+ license: MIT
5
+ tags: [typescript, javascript, types]
6
+ description: >
7
+ Review existing TypeScript code and write new TypeScript following the 62 items from
8
+ "Effective TypeScript" by Dan Vanderkam. Use when writing TypeScript, reviewing TypeScript
9
+ code, working with type design, avoiding any, managing type declarations, or migrating
10
+ JavaScript to TypeScript. Trigger on: "TypeScript best practices", "type safety", "any",
11
+ "type assertions", "type design", "strict mode", "TypeScript review", "migrate to TypeScript".
12
+ ---
13
+
14
+ # Effective TypeScript Skill
15
+
16
+ Apply the 62 items from Dan Vanderkam's "Effective TypeScript" to review existing code and write new TypeScript. This skill operates in two modes: **Review Mode** (analyze code for violations) and **Write Mode** (produce idiomatic, well-typed TypeScript from scratch).
17
+
18
+ ## Reference Files
19
+
20
+ This skill includes categorized reference files covering all 62 items:
21
+
22
+ - `ref-01-getting-to-know-ts.md` — Items 1-5: TS/JS relationship, compiler options, code generation, structural typing, any
23
+ - `ref-02-type-system.md` — Items 6-18: editor, sets, type vs value space, declarations vs assertions, object wrappers, excess property checking, generics, readonly, mapped types
24
+ - `ref-03-type-inference.md` — Items 19-27: inferable types, widening, narrowing, objects at once, aliases, async/await, context, functional constructs
25
+ - `ref-04-type-design.md` — Items 28-37: valid states, Postel's Law, documentation, null perimeter, unions of interfaces, string types, branded types
26
+ - `ref-05-working-with-any.md` — Items 38-44: narrowest scope, precise any variants, unsafe assertions, evolving any, unknown, monkey patching, type coverage
27
+ - `ref-06-type-declarations.md` — Items 45-52: devDependencies, three versions, export types, TSDoc, this in callbacks, conditional types, mirror types, testing types
28
+ - `ref-07-writing-running-code.md` — Items 53-57: ECMAScript features, iterating objects, DOM hierarchy, private, source maps
29
+ - `ref-08-migrating.md` — Items 58-62: modern JS, @ts-check, allowJs, module-by-module, noImplicitAny
30
+
31
+ ## How to Use This Skill
32
+
33
+ **Before responding**, read the relevant reference files based on the code's topic. For a general review, read all files. For targeted work (e.g., type design), read the specific reference (e.g., `ref-04-type-design.md`).
34
+
35
+ ---
36
+
37
+ ## Mode 1: Code Review
38
+
39
+ When the user asks you to **review** existing TypeScript code, follow this process:
40
+
41
+ ### Step 1: Read Relevant References
42
+ Determine which chapters apply to the code under review and read those reference files. If unsure, read all of them.
43
+
44
+ ### Step 2: Analyze the Code
45
+ Before listing issues, first ask: **Is this code already applying Effective TypeScript principles?** Look for positive signals:
46
+ - Tagged unions with a discriminant field (Item 28/32)
47
+ - Branded types for nominal typing (Item 37)
48
+ - `unknown` for external data, narrowed before use (Item 42)
49
+ - Type assertions scoped inside well-typed wrapper functions (Item 40)
50
+ - `readonly` on fields/parameters (Item 17)
51
+ - `async`/`await` with typed return types (Item 25)
52
+ - TSDoc comments on public functions (Item 48)
53
+
54
+ **Key rule — Item 40 interaction with Item 9:** A type assertion (`as T`) inside a function that has a fully-typed signature is NOT a violation of Item 9. Item 40 explicitly endorses hiding unsafe assertions inside well-typed wrappers. Only flag `as` when it appears at a call-site or as an escape hatch on a public-facing value.
55
+
56
+ For each relevant item from the book, check whether the code follows or violates the guideline. Focus on:
57
+
58
+ 1. **TypeScript Fundamentals** (Items 1-5): Is `strict` mode enabled? Is `any` used carelessly? Does structural typing cause surprises?
59
+ 2. **Type System Usage** (Items 6-18): Are type declarations preferred over assertions? Are object wrapper types avoided? Are `readonly` and mapped types used appropriately?
60
+ 3. **Type Inference** (Items 19-27): Is inference relied upon where possible? Are `async`/`await` used over callbacks? Are aliases consistent?
61
+ 4. **Type Design** (Items 28-37): Do types represent only valid states? Are string types replaced with literal unions? Are null values pushed to the perimeter?
62
+ 5. **Working with any** (Items 38-44): Is `any` scoped as narrowly as possible? Is `unknown` used for truly unknown values? Are unsafe assertions hidden in well-typed wrappers?
63
+ 6. **Type Declarations** (Items 45-52): Are `@types` in devDependencies? Are public API types exported? Is TSDoc used for comments?
64
+ 7. **Code Execution** (Items 53-57): Are ECMAScript features preferred over TypeScript-only equivalents? Is object iteration done safely?
65
+ 8. **Migration** (Items 58-62): Is modern JavaScript used as a baseline? Is migration done module-by-module?
66
+
67
+ ### Step 3: Calibrate Your Response
68
+
69
+ **If the code is already well-typed:**
70
+ - Open with acknowledgment of what is correct and which Items are applied
71
+ - Only note genuine issues; do not manufacture problems
72
+ - Any remaining observations must be clearly labeled as "optional polish" or "minor suggestion"
73
+ - Do NOT escalate a narrowly scoped assertion inside a well-typed function to Critical/Important
74
+
75
+ **If the code has real issues:**
76
+ For each issue found, report:
77
+ - **Item number and name** (e.g., "Item 9: Prefer Type Declarations to Type Assertions")
78
+ - **Location** in the code
79
+ - **What's wrong** (the anti-pattern)
80
+ - **How to fix it** (the TypeScript-idiomatic way)
81
+ - **Priority**: Critical (bugs/correctness), Important (maintainability), Suggestion (style)
82
+
83
+ ### Step 4: Provide Fixed Code (only when needed)
84
+ If there are real issues, offer a corrected version with comments explaining each change. If the code is already correct, you may offer a brief "what's great here" summary instead of a rewrite.
85
+
86
+ ---
87
+
88
+ ## Mode 2: Writing New Code
89
+
90
+ When the user asks you to **write** new TypeScript code, apply these core practices:
91
+
92
+ <core_principles>
93
+
94
+ ### Always Apply These Core Practices
95
+
96
+ 1. **Enable strict mode** (Item 2). Never write TypeScript without `"strict": true` in tsconfig.json.
97
+
98
+ 2. **Prefer type declarations over assertions** (Item 9). Use `const x: MyType = value` not `const x = value as MyType`.
99
+
100
+ 3. **Avoid object wrapper types** (Item 10). Use `string`, `number`, `boolean` — never `String`, `Number`, `Boolean`.
101
+
102
+ 4. **Use types that represent only valid states** (Item 28). Eliminate impossible states at the type level with tagged unions.
103
+
104
+ 5. **Push null to the perimeter** (Item 31). Don't scatter `T | null` throughout — handle nullability at boundaries.
105
+
106
+ 6. **Prefer unions of interfaces to interfaces of unions** (Item 32). Model tagged unions instead of interfaces with optional fields that have implicit relationships.
107
+
108
+ 7. **Replace plain string types with string literal unions** (Item 33). `type Direction = 'north' | 'south' | 'east' | 'west'` not `string`.
109
+
110
+ 8. **Generate types from APIs and specs, not data** (Item 35). Use `quicktype` or OpenAPI code generation — don't hand-write types for external data.
111
+
112
+ 9. **Use `unknown` instead of `any` for values with unknown type** (Item 42). `unknown` forces callers to narrow before use.
113
+
114
+ 10. **Scope `any` as narrowly as possible** (Item 38). Apply it to a single value, never a whole object or module.
115
+
116
+ 11. **Use `readonly` to prevent mutation bugs** (Item 17). Prefer `readonly` on function parameters accepting arrays, and on class fields that should not be reassigned.
117
+
118
+ 12. **Use `async`/`await` over raw Promises and callbacks** (Item 25). It produces cleaner inferred types and clearer code.
119
+
120
+ 13. **Use type aliases to avoid repeating yourself** (Item 14). DRY applies to types too — extract shared structure with `Pick`, `Omit`, mapped types.
121
+
122
+ 14. **Export all types that appear in public APIs** (Item 47). Don't force users to reconstruct types with `ReturnType<>` or `Parameters<>`.
123
+
124
+ 15. **Use TSDoc for API comments** (Item 48). `/** */` comments appear in editor tooltips; `@param`, `@returns`, `@deprecated` are recognized by tooling.
125
+
126
+ </core_principles>
127
+
128
+ <examples>
129
+
130
+ ### Type Structure Template
131
+
132
+ <example id="1" title="Core type patterns — branded types, tagged unions, unknown, async/await">
133
+
134
+ ```typescript
135
+ // Prefer interfaces for object shapes (extendable); type aliases for unions/intersections
136
+ interface User {
137
+ readonly id: UserId; // Item 17: readonly on fields that shouldn't change
138
+ name: string;
139
+ email: string;
140
+ }
141
+
142
+ // Branded type for nominal typing (Item 37)
143
+ type UserId = string & { readonly __brand: 'UserId' };
144
+
145
+ // Tagged union — only valid states representable (Item 28, 32)
146
+ type RequestState<T> =
147
+ | { status: 'loading' }
148
+ | { status: 'success'; data: T }
149
+ | { status: 'error'; message: string };
150
+
151
+ // unknown, not any, for values from external sources (Item 42)
152
+ function parseResponse(json: string): unknown {
153
+ return JSON.parse(json);
154
+ }
155
+
156
+ // async/await over callbacks (Item 25)
157
+ async function fetchUser(id: UserId): Promise<User> {
158
+ const response = await fetch(`/api/users/${id}`);
159
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
160
+ return response.json() as User; // narrowly scoped assertion inside well-typed function (Item 40)
161
+ }
162
+ ```
163
+
164
+ </example>
165
+
166
+ </examples>
167
+
168
+ <guidelines>
169
+
170
+ ### any Guidelines
171
+ - If `any` is unavoidable, apply it to the smallest possible scope (Item 38)
172
+ - Prefer `unknown` for values received from external sources (Item 42)
173
+ - Hide unsafe assertions inside well-typed wrapper functions (Item 40)
174
+ - Track type coverage with `type-coverage` CLI to prevent regressions (Item 44)
175
+
176
+ </guidelines>
177
+
178
+ ---
179
+
180
+ ## Priority of Items by Impact
181
+
182
+ <core_principles>
183
+
184
+ ### Critical (Correctness & Bugs)
185
+ - Item 2: Enable `strict` mode — `noImplicitAny` and `strictNullChecks` prevent whole classes of bugs
186
+ - Item 9: Prefer declarations to assertions — **but see Item 40**: assertions inside well-typed wrappers are fine
187
+ - Item 28: Types that always represent valid states — impossible states cause runtime errors
188
+ - Item 31: Push null to the perimeter — scattered nullability causes null dereferences
189
+ - Item 42: Use `unknown` instead of `any` — `any` silently disables type checking
190
+
191
+ > **Item 40 exception:** `raw as SomeType` inside a function with a fully-typed signature is explicitly endorsed by Item 40. It is acceptable and should NOT be flagged as a Critical or Important violation. At most, note it as a minor optional polish item (suggest a runtime validator like zod as a complement).
192
+
193
+ </core_principles>
194
+
195
+ <guidelines>
196
+
197
+ ### Important (Maintainability)
198
+ - Item 13: Know the differences between `type` and `interface`
199
+ - Item 14: Use type operations and generics to avoid repetition
200
+ - Item 17: Use `readonly` to prevent mutation bugs
201
+ - Item 25: Use `async`/`await` over callbacks
202
+ - Item 32: Prefer unions of interfaces to interfaces of unions
203
+ - Item 33: Prefer string literal unions over plain `string`
204
+ - Item 47: Export all types that appear in public APIs
205
+ - Item 48: Use TSDoc for API comments
206
+
207
+ ### Suggestions (Polish & Optimization)
208
+ - Item 19: Omit inferable types to reduce clutter
209
+ - Item 35: Generate types from APIs and specs
210
+ - Item 37: Consider brands for nominal typing
211
+ - Item 44: Track type coverage
212
+ - Item 53: Prefer ECMAScript features over TypeScript-only equivalents
213
+
214
+ </guidelines>
215
+
216
+ <anti_patterns>
217
+
218
+ ## Common Anti-Patterns to Flag
219
+
220
+ ### any overuse (Items 38, 42)
221
+ - Return type `Promise<any>` — use `Promise<unknown>` or a concrete typed interface
222
+ - `as any` cast at call-site or on a public-facing value — narrowest-scope rule violated
223
+ - Interface fields typed as `any` (e.g., `result?: any`) — use `unknown` or a typed union
224
+
225
+ ### Interface of unions (Items 28, 32)
226
+ - An interface with `boolean` + optional fields that only make sense together: `{ isLoading: boolean; data?: T; error?: string }` is an interface-of-unions anti-pattern
227
+ - Invalid combinations are representable (e.g., `{ isLoading: true, data: [...] }` or `{ isLoading: false, data: undefined }`)
228
+ - Fix: replace with a tagged union using a `status` discriminant field
229
+ - Non-null assertions (`!`) inside render/display logic are a strong symptom of this pattern
230
+
231
+ ### Plain string parameters (Item 33)
232
+ - Function parameters typed as `string` when only a small, known set of values is valid
233
+ - Fix: `type Theme = 'light' | 'dark'` or similar literal union
234
+
235
+ ### Type assertions at call-sites (Item 9)
236
+ - `value as SomeType` appearing in application code (not inside a well-typed wrapper function)
237
+ - Fix: use a type declaration `const x: SomeType = value` or a validator function that returns the typed value
238
+
239
+ ### Missing strict mode (Item 2)
240
+ - TypeScript files without `"strict": true` in tsconfig — whole classes of bugs (null dereferences, implicit any) go undetected
241
+
242
+ ### Callbacks over async/await (Item 25)
243
+ - `.then()` chains instead of `async`/`await` — harder to read, worse type inference
244
+
245
+ </anti_patterns>
246
+
247
+ <strengths_to_praise>
248
+
249
+ ## Strengths to Recognize in Good Code
250
+
251
+ When reviewing code that applies these patterns correctly, explicitly acknowledge:
252
+
253
+ - **Branded types** (Item 37): `type FooId = string & { readonly __brand: 'FooId' }` — prevents mixing up identifiers of different domain entities
254
+ - **Tagged/discriminated unions** (Item 28, 32): `type Result = { status: 'success'; data: T } | { status: 'error'; message: string }` — only valid states representable
255
+ - **`unknown` for external data** (Item 42): `const raw: unknown = await response.json()` — forces explicit narrowing before use
256
+ - **Assertion inside well-typed wrapper** (Item 40): `as T` inside a function with a fully-typed signature — safe encapsulation of unsafe boundary
257
+ - **`readonly` fields** (Item 17): `readonly id: UserId` on domain entity interfaces — communicates immutability intent
258
+ - **`async`/`await`** (Item 25): cleaner than `.then()` chains, better type inference
259
+ - **TSDoc on public functions** (Item 48): `/** @param ... @returns ... */` — shows up in editor tooltips
260
+ - **String literal unions** (Item 33): `type Status = 'pending' | 'processing' | 'shipped'` instead of `string`
261
+
262
+ </strengths_to_praise>
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "Effective TypeScript Static Checks",
3
+ "rules": [
4
+ {
5
+ "id": "Item 5",
6
+ "pattern": ":\\s*any\\b",
7
+ "message": "Found 'any' type annotation. Replace with specific type or 'unknown' (Item 5/38).",
8
+ "severity": "🔴 Critical"
9
+ },
10
+ {
11
+ "id": "Item 28",
12
+ "pattern": "!\\.",
13
+ "message": "Found non-null assertion (!). Fix the type or use optional chaining (?.) (Item 28/31).",
14
+ "severity": "🔴 Critical"
15
+ },
16
+ {
17
+ "id": "Item 38",
18
+ "pattern": "@ts-ignore|@ts-nocheck",
19
+ "message": "Found @ts-ignore. Fix the underlying type issue (Item 38).",
20
+ "severity": "🔴 Critical"
21
+ },
22
+ {
23
+ "id": "Item 10",
24
+ "pattern": "\\b(String|Number|Boolean)\\b",
25
+ "message": "Found object wrapper type. Use primitive types (string, number, boolean) instead (Item 10).",
26
+ "severity": "🟡 Improvement"
27
+ }
28
+ ]
29
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "evals": [
3
+ {
4
+ "id": "eval-01-any-assertions-string-types",
5
+ "prompt": "Review this TypeScript code:\n\n```typescript\nasync function loadConfig(env: string): Promise<any> {\n const res = await fetch(`/config/${env}`);\n const data = res.json() as any;\n return data;\n}\n\ninterface AppState {\n loading: boolean;\n result?: any;\n error?: string;\n}\n\nfunction applyTheme(theme: string) {\n document.body.className = theme;\n}\n```",
6
+ "expectations": [
7
+ "Flag Item 42: return type should be unknown or a typed interface, not any",
8
+ "Flag Item 9: res.json() as any is a type assertion — prefer a type declaration or typed wrapper",
9
+ "Flag Item 28/32: AppState is an interface of unions — loading:true with result/error present is representable; suggest a tagged union instead",
10
+ "Flag Item 33: theme parameter should be a string literal union (e.g. 'light' | 'dark'), not plain string",
11
+ "Provide a fixed version using unknown, a tagged union, and a literal union type"
12
+ ]
13
+ },
14
+ {
15
+ "id": "eval-02-subtle-interface-of-unions",
16
+ "prompt": "Review this TypeScript code:\n\n```typescript\ninterface FetchState {\n isLoading: boolean;\n data?: User[];\n errorMessage?: string;\n lastUpdated?: Date;\n}\n\nfunction render(state: FetchState) {\n if (state.isLoading) {\n showSpinner();\n } else if (state.errorMessage) {\n showError(state.errorMessage);\n } else {\n showData(state.data!); // non-null assertion\n }\n}\n```",
17
+ "expectations": [
18
+ "Flag Item 32: FetchState is an interface of unions — data and errorMessage have an implicit relationship that the type doesn't enforce",
19
+ "Flag Item 28: states like { isLoading: false, data: [...], errorMessage: 'oops' } are representable but invalid",
20
+ "Flag the non-null assertion (!) on state.data — it's a symptom of the type not representing valid states",
21
+ "Suggest a tagged union: type FetchState = { status: 'loading' } | { status: 'success'; data: User[]; lastUpdated: Date } | { status: 'error'; message: string }",
22
+ "Show how the fixed type makes the render function exhaustive and removes the need for the non-null assertion"
23
+ ]
24
+ },
25
+ {
26
+ "id": "eval-03-already-well-typed",
27
+ "prompt": "Review this TypeScript code:\n\n```typescript\ntype OrderId = string & { readonly __brand: 'OrderId' };\n\ntype OrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled';\n\ntype OrderResult =\n | { status: 'success'; order: Order }\n | { status: 'not_found' }\n | { status: 'error'; message: string };\n\ninterface Order {\n readonly id: OrderId;\n readonly customerId: string;\n status: OrderStatus;\n items: readonly OrderItem[];\n}\n\n/**\n * Fetches an order by ID.\n * @param id - The branded order identifier\n * @returns The order result discriminated union\n */\nasync function fetchOrder(id: OrderId): Promise<OrderResult> {\n const response = await fetch(`/api/orders/${id}`);\n if (response.status === 404) return { status: 'not_found' };\n if (!response.ok) return { status: 'error', message: `HTTP ${response.status}` };\n const raw: unknown = await response.json();\n return { status: 'success', order: raw as Order };\n}\n```",
28
+ "expectations": [
29
+ "Recognize that this code is already applying Effective TypeScript principles correctly",
30
+ "Acknowledge Item 37 (branded OrderId), Item 33 (OrderStatus literal union), Item 28/32 (tagged union OrderResult), Item 17 (readonly fields), Item 42 (unknown from JSON), Item 40 (assertion inside well-typed function), Item 25 (async/await), Item 48 (TSDoc)",
31
+ "Do NOT manufacture issues — the code is well-typed",
32
+ "At most note: raw as Order on the last line is a narrowly scoped assertion (Item 40) — acceptable inside a well-typed wrapper, but mention that a runtime validator (e.g. zod) would catch malformed API responses that TypeScript cannot",
33
+ "At most offer minor suggestions, clearly marked as optional polish"
34
+ ]
35
+ }
36
+ ]
37
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "pass_rate": 1,
3
+ "passed": 15,
4
+ "total": 15,
5
+ "baseline_pass_rate": 0.333,
6
+ "baseline_passed": 5,
7
+ "baseline_total": 15,
8
+ "delta": 0.667,
9
+ "model": "default",
10
+ "evals_run": 3,
11
+ "date": "2026-03-29",
12
+ "non_standard_provider": true
13
+ }
@@ -0,0 +1,70 @@
1
+ # After: Effective TypeScript
2
+
3
+ The same API client rewritten applying Effective TypeScript principles.
4
+
5
+ ```typescript
6
+ // tsconfig.json has "strict": true (Item 2)
7
+
8
+ // Extract repeated shape into a named type (Item 14)
9
+ interface User {
10
+ readonly id: UserId; // Item 17: readonly on identity fields
11
+ name: string;
12
+ email: string;
13
+ role: 'admin' | 'viewer' | 'editor'; // Item 33: literal union, not plain string
14
+ }
15
+
16
+ // Branded type prevents passing a raw string where a UserId is expected (Item 37)
17
+ type UserId = string & { readonly __brand: 'UserId' };
18
+
19
+ // Tagged union — only valid states are representable (Item 28, 32)
20
+ // Impossible: { status: 'loading', data: [...] } or { status: 'success', error: '...' }
21
+ type RequestState<T> =
22
+ | { status: 'loading' }
23
+ | { status: 'success'; data: T }
24
+ | { status: 'error'; message: string };
25
+
26
+ // Return unknown from untrusted sources — callers must narrow (Item 42)
27
+ async function getUser(id: UserId): Promise<User> {
28
+ const response = await fetch(`/api/users/${id}`);
29
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
30
+ const raw: unknown = await response.json();
31
+ // Unsafe assertion scoped inside well-typed function boundary (Item 40)
32
+ return raw as User;
33
+ }
34
+
35
+ // Type declaration, not assertion (Item 9)
36
+ const userIdInput: HTMLInputElement | null = document.getElementById('user-id') as HTMLInputElement | null;
37
+
38
+ // Null pushed to the perimeter — caller handles it once (Item 31)
39
+ function processUser(user: User): string {
40
+ return user.name.toUpperCase(); // safe — name: string, not string | null
41
+ }
42
+
43
+ // String literal union, not plain string (Item 33)
44
+ type Direction = 'north' | 'south' | 'east' | 'west';
45
+ function setDirection(direction: Direction) {
46
+ // TypeScript rejects "sideways" at compile time
47
+ }
48
+
49
+ // async/await over callbacks — types flow naturally (Item 25)
50
+ async function fetchProfile(id: UserId): Promise<User> {
51
+ const response = await fetch(`/api/profiles/${id}`);
52
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
53
+ return response.json() as User;
54
+ }
55
+
56
+ // Named type used in all three functions — DRY (Item 14)
57
+ function renderAdmin(user: User): void {}
58
+ function updateAdmin(user: User): void {}
59
+ function deleteAdmin(user: User): void {}
60
+ ```
61
+
62
+ **Key improvements:**
63
+ - `strict: true` enables `noImplicitAny` and `strictNullChecks` (Item 2)
64
+ - `RequestState<T>` tagged union eliminates impossible states (Items 28, 32)
65
+ - `UserId` branded type prevents mixing up `string` IDs (Item 37)
66
+ - `role` is a literal union, not `string` (Item 33)
67
+ - `unknown` returned from untrusted JSON; unsafe assertion hidden inside typed boundary (Items 40, 42)
68
+ - `async`/`await` replaces callback (Item 25)
69
+ - `User` interface defined once and reused (Item 14)
70
+ - `readonly` on `id` prevents accidental reassignment (Item 17)
@@ -0,0 +1,47 @@
1
+ # Before: Effective TypeScript
2
+
3
+ An API client for a user management service — written without applying Effective TypeScript principles.
4
+
5
+ ```typescript
6
+ // No strict mode, any used freely, type assertions everywhere
7
+
8
+ async function getUser(id: string): Promise<any> {
9
+ const response = await fetch(`/api/users/${id}`);
10
+ const data = await response.json();
11
+ return data;
12
+ }
13
+
14
+ function processUser(user: any) {
15
+ console.log(user.name.toUpperCase()); // no null check, will crash if name is null
16
+ }
17
+
18
+ // Interface of unions — impossible states are representable
19
+ interface RequestState {
20
+ loading: boolean;
21
+ data?: any[]; // present when loading is false AND succeeded
22
+ error?: string; // present when loading is false AND failed
23
+ // What does { loading: false, data: [...], error: "oops" } mean?
24
+ }
25
+
26
+ // Plain string types instead of literal unions
27
+ function setDirection(direction: string) {
28
+ // accepts "north", "sideways", "diagonal", anything
29
+ }
30
+
31
+ // Type assertion instead of declaration
32
+ const userId = document.getElementById('user-id') as HTMLInputElement;
33
+ const value = userId.value as string;
34
+
35
+ // Callback-based async
36
+ function fetchProfile(id: string, callback: (err: Error | null, data: any) => void) {
37
+ fetch(`/api/profiles/${id}`)
38
+ .then(res => res.json())
39
+ .then(data => callback(null, data))
40
+ .catch(err => callback(err, null));
41
+ }
42
+
43
+ // Repeated type shape — DRY violation
44
+ function renderAdmin(user: { id: string; name: string; email: string; role: string }) {}
45
+ function updateAdmin(user: { id: string; name: string; email: string; role: string }) {}
46
+ function deleteAdmin(user: { id: string; name: string; email: string; role: string }) {}
47
+ ```