@softspark/ai-toolkit 1.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 (327) hide show
  1. package/AGENTS.md +412 -0
  2. package/CHANGELOG.md +68 -0
  3. package/LICENSE +21 -0
  4. package/README.md +632 -0
  5. package/action.yml +53 -0
  6. package/app/.claude-plugin/plugin.json +44 -0
  7. package/app/ARCHITECTURE.md +306 -0
  8. package/app/CLAUDE.md.template +23 -0
  9. package/app/agents/ai-engineer.md +128 -0
  10. package/app/agents/backend-specialist.md +193 -0
  11. package/app/agents/business-intelligence.md +54 -0
  12. package/app/agents/chaos-monkey.md +67 -0
  13. package/app/agents/chief-of-staff.md +51 -0
  14. package/app/agents/code-archaeologist.md +127 -0
  15. package/app/agents/code-reviewer.md +184 -0
  16. package/app/agents/command-expert.md +131 -0
  17. package/app/agents/data-analyst.md +205 -0
  18. package/app/agents/data-scientist.md +151 -0
  19. package/app/agents/database-architect.md +317 -0
  20. package/app/agents/debugger.md +238 -0
  21. package/app/agents/devops-implementer.md +194 -0
  22. package/app/agents/documenter.md +364 -0
  23. package/app/agents/explorer-agent.md +145 -0
  24. package/app/agents/fact-checker.md +172 -0
  25. package/app/agents/frontend-specialist.md +209 -0
  26. package/app/agents/game-developer.md +216 -0
  27. package/app/agents/incident-responder.md +226 -0
  28. package/app/agents/infrastructure-architect.md +127 -0
  29. package/app/agents/infrastructure-validator.md +247 -0
  30. package/app/agents/llm-ops-engineer.md +237 -0
  31. package/app/agents/mcp-expert.md +228 -0
  32. package/app/agents/mcp-server-architect.md +195 -0
  33. package/app/agents/mcp-testing-engineer.md +292 -0
  34. package/app/agents/meta-architect.md +58 -0
  35. package/app/agents/ml-engineer.md +136 -0
  36. package/app/agents/mobile-developer.md +190 -0
  37. package/app/agents/night-watchman.md +55 -0
  38. package/app/agents/nlp-engineer.md +154 -0
  39. package/app/agents/orchestrator.md +437 -0
  40. package/app/agents/performance-optimizer.md +254 -0
  41. package/app/agents/predictive-analyst.md +57 -0
  42. package/app/agents/product-manager.md +194 -0
  43. package/app/agents/project-planner.md +287 -0
  44. package/app/agents/prompt-engineer.md +103 -0
  45. package/app/agents/qa-automation-engineer.md +182 -0
  46. package/app/agents/rag-engineer.md +201 -0
  47. package/app/agents/research-synthesizer.md +138 -0
  48. package/app/agents/search-specialist.md +101 -0
  49. package/app/agents/security-architect.md +62 -0
  50. package/app/agents/security-auditor.md +293 -0
  51. package/app/agents/seo-specialist.md +111 -0
  52. package/app/agents/system-governor.md +57 -0
  53. package/app/agents/tech-lead.md +62 -0
  54. package/app/agents/technical-researcher.md +103 -0
  55. package/app/agents/test-engineer.md +264 -0
  56. package/app/constitution.md +38 -0
  57. package/app/hooks/_profile-check.sh +11 -0
  58. package/app/hooks/guard-destructive.sh +74 -0
  59. package/app/hooks/guard-path.sh +73 -0
  60. package/app/hooks/post-tool-use.sh +35 -0
  61. package/app/hooks/pre-compact.sh +31 -0
  62. package/app/hooks/quality-check.sh +22 -0
  63. package/app/hooks/quality-gate.sh +49 -0
  64. package/app/hooks/save-session.sh +24 -0
  65. package/app/hooks/session-end.sh +37 -0
  66. package/app/hooks/session-start.sh +29 -0
  67. package/app/hooks/subagent-start.sh +16 -0
  68. package/app/hooks/subagent-stop.sh +16 -0
  69. package/app/hooks/track-usage.sh +50 -0
  70. package/app/hooks/user-prompt-submit.sh +25 -0
  71. package/app/hooks.json +178 -0
  72. package/app/mcp-defaults.json +23 -0
  73. package/app/output-styles/golden-rules.md +43 -0
  74. package/app/plugins/README.md +19 -0
  75. package/app/plugins/csharp-pack/README.md +11 -0
  76. package/app/plugins/csharp-pack/plugin.json +18 -0
  77. package/app/plugins/enterprise-pack/README.md +16 -0
  78. package/app/plugins/enterprise-pack/hooks/output-style.sh +6 -0
  79. package/app/plugins/enterprise-pack/hooks/status-line.sh +8 -0
  80. package/app/plugins/enterprise-pack/plugin.json +24 -0
  81. package/app/plugins/frontend-pack/README.md +14 -0
  82. package/app/plugins/frontend-pack/plugin.json +22 -0
  83. package/app/plugins/java-pack/README.md +11 -0
  84. package/app/plugins/java-pack/plugin.json +18 -0
  85. package/app/plugins/kotlin-pack/README.md +11 -0
  86. package/app/plugins/kotlin-pack/plugin.json +18 -0
  87. package/app/plugins/memory-pack/README.md +24 -0
  88. package/app/plugins/memory-pack/hooks/observation-capture.sh +67 -0
  89. package/app/plugins/memory-pack/hooks/session-summary.sh +71 -0
  90. package/app/plugins/memory-pack/plugin.json +22 -0
  91. package/app/plugins/memory-pack/scripts/init_db.py +81 -0
  92. package/app/plugins/memory-pack/scripts/strip_private.py +22 -0
  93. package/app/plugins/memory-pack/skills/mem-search/SKILL.md +70 -0
  94. package/app/plugins/research-pack/README.md +14 -0
  95. package/app/plugins/research-pack/plugin.json +22 -0
  96. package/app/plugins/ruby-pack/README.md +11 -0
  97. package/app/plugins/ruby-pack/plugin.json +18 -0
  98. package/app/plugins/rust-pack/README.md +11 -0
  99. package/app/plugins/rust-pack/plugin.json +18 -0
  100. package/app/plugins/security-pack/README.md +15 -0
  101. package/app/plugins/security-pack/plugin.json +23 -0
  102. package/app/plugins/swift-pack/README.md +11 -0
  103. package/app/plugins/swift-pack/plugin.json +18 -0
  104. package/app/rules/claude-toolkit-rules.md +21 -0
  105. package/app/rules/git-conventions.md +5 -0
  106. package/app/rules/quality-gates.md +10 -0
  107. package/app/skills/_lib/__init__.py +1 -0
  108. package/app/skills/_lib/detect_utils.py +150 -0
  109. package/app/skills/agent-creator/SKILL.md +82 -0
  110. package/app/skills/analyze/SKILL.md +92 -0
  111. package/app/skills/analyze/scripts/complexity.py +165 -0
  112. package/app/skills/api-patterns/SKILL.md +305 -0
  113. package/app/skills/app-builder/SKILL.md +187 -0
  114. package/app/skills/architecture-audit/SKILL.md +141 -0
  115. package/app/skills/architecture-decision/SKILL.md +55 -0
  116. package/app/skills/architecture-decision/templates/adr-template.md +36 -0
  117. package/app/skills/biz-scan/SKILL.md +30 -0
  118. package/app/skills/briefing/SKILL.md +27 -0
  119. package/app/skills/build/SKILL.md +97 -0
  120. package/app/skills/build/scripts/detect-build.py +151 -0
  121. package/app/skills/chaos/SKILL.md +32 -0
  122. package/app/skills/ci/SKILL.md +77 -0
  123. package/app/skills/ci/scripts/ci-detect.py +135 -0
  124. package/app/skills/ci/templates/github-actions-node.yml +38 -0
  125. package/app/skills/ci/templates/github-actions-python.yml +42 -0
  126. package/app/skills/ci-cd-patterns/SKILL.md +299 -0
  127. package/app/skills/clean-code/SKILL.md +110 -0
  128. package/app/skills/clean-code/reference/dart.md +18 -0
  129. package/app/skills/clean-code/reference/go.md +23 -0
  130. package/app/skills/clean-code/reference/php.md +32 -0
  131. package/app/skills/clean-code/reference/python.md +180 -0
  132. package/app/skills/clean-code/reference/typescript.md +26 -0
  133. package/app/skills/command-creator/SKILL.md +83 -0
  134. package/app/skills/commit/SKILL.md +98 -0
  135. package/app/skills/commit/scripts/pre-commit-check.py +87 -0
  136. package/app/skills/commit/templates/conventional-commit.md +52 -0
  137. package/app/skills/csharp-patterns/SKILL.md +450 -0
  138. package/app/skills/database-patterns/SKILL.md +297 -0
  139. package/app/skills/debug/SKILL.md +154 -0
  140. package/app/skills/debug/scripts/error-parser.py +187 -0
  141. package/app/skills/debugging-tactics/SKILL.md +136 -0
  142. package/app/skills/deploy/SKILL.md +130 -0
  143. package/app/skills/deploy/scripts/pre_deploy_check.py +171 -0
  144. package/app/skills/deploy/templates/deployment-checklist.md +31 -0
  145. package/app/skills/design-an-interface/SKILL.md +105 -0
  146. package/app/skills/design-engineering/SKILL.md +260 -0
  147. package/app/skills/docker-devops/SKILL.md +303 -0
  148. package/app/skills/docs/SKILL.md +145 -0
  149. package/app/skills/docs/scripts/doc-inventory.py +176 -0
  150. package/app/skills/docs/templates/adr-template.md +36 -0
  151. package/app/skills/docs/templates/readme-template.md +67 -0
  152. package/app/skills/documentation-standards/SKILL.md +191 -0
  153. package/app/skills/ecommerce-patterns/SKILL.md +209 -0
  154. package/app/skills/evaluate/SKILL.md +132 -0
  155. package/app/skills/evolve/SKILL.md +27 -0
  156. package/app/skills/explain/SKILL.md +54 -0
  157. package/app/skills/explain/scripts/dependency-graph.py +215 -0
  158. package/app/skills/explore/SKILL.md +112 -0
  159. package/app/skills/explore/scripts/visualize.py +117 -0
  160. package/app/skills/fix/SKILL.md +78 -0
  161. package/app/skills/fix/scripts/error-classifier.py +191 -0
  162. package/app/skills/flutter-patterns/SKILL.md +254 -0
  163. package/app/skills/git-mastery/SKILL.md +70 -0
  164. package/app/skills/grill-me/SKILL.md +38 -0
  165. package/app/skills/health/SKILL.md +91 -0
  166. package/app/skills/health/scripts/health_check.py +162 -0
  167. package/app/skills/hive-mind/SKILL.md +56 -0
  168. package/app/skills/hook-creator/SKILL.md +107 -0
  169. package/app/skills/index/SKILL.md +74 -0
  170. package/app/skills/instinct-review/SKILL.md +77 -0
  171. package/app/skills/java-patterns/SKILL.md +442 -0
  172. package/app/skills/kotlin-patterns/SKILL.md +446 -0
  173. package/app/skills/lint/SKILL.md +103 -0
  174. package/app/skills/lint/scripts/detect-linters.py +112 -0
  175. package/app/skills/mcp-patterns/SKILL.md +270 -0
  176. package/app/skills/mem-search/SKILL.md +70 -0
  177. package/app/skills/migrate/SKILL.md +90 -0
  178. package/app/skills/migrate/scripts/migration-status.py +195 -0
  179. package/app/skills/migration-patterns/SKILL.md +260 -0
  180. package/app/skills/night-watch/SKILL.md +28 -0
  181. package/app/skills/observability-patterns/SKILL.md +203 -0
  182. package/app/skills/onboard/SKILL.md +76 -0
  183. package/app/skills/orchestrate/SKILL.md +86 -0
  184. package/app/skills/panic/SKILL.md +30 -0
  185. package/app/skills/performance-profiling/SKILL.md +59 -0
  186. package/app/skills/plan/SKILL.md +110 -0
  187. package/app/skills/plan/templates/plan-template.md +40 -0
  188. package/app/skills/plan-writing/SKILL.md +201 -0
  189. package/app/skills/plugin-creator/SKILL.md +78 -0
  190. package/app/skills/pr/SKILL.md +129 -0
  191. package/app/skills/pr/scripts/pr-summary.py +175 -0
  192. package/app/skills/prd-to-issues/SKILL.md +108 -0
  193. package/app/skills/prd-to-plan/SKILL.md +120 -0
  194. package/app/skills/predict/SKILL.md +30 -0
  195. package/app/skills/qa-session/SKILL.md +110 -0
  196. package/app/skills/rag-patterns/SKILL.md +203 -0
  197. package/app/skills/refactor/SKILL.md +124 -0
  198. package/app/skills/refactor/scripts/refactor-scan.py +210 -0
  199. package/app/skills/refactor-plan/SKILL.md +112 -0
  200. package/app/skills/repeat/SKILL.md +149 -0
  201. package/app/skills/research-mastery/SKILL.md +56 -0
  202. package/app/skills/review/SKILL.md +141 -0
  203. package/app/skills/review/scripts/diff-analyzer.py +170 -0
  204. package/app/skills/rollback/SKILL.md +87 -0
  205. package/app/skills/rollback/scripts/rollback_info.py +149 -0
  206. package/app/skills/ruby-patterns/SKILL.md +454 -0
  207. package/app/skills/rust-patterns/SKILL.md +446 -0
  208. package/app/skills/search/SKILL.md +64 -0
  209. package/app/skills/security-patterns/SKILL.md +91 -0
  210. package/app/skills/security-patterns/reference/authentication.md +37 -0
  211. package/app/skills/security-patterns/reference/authorization.md +22 -0
  212. package/app/skills/security-patterns/reference/input-validation.md +30 -0
  213. package/app/skills/security-patterns/reference/oauth-csrf-audit.md +131 -0
  214. package/app/skills/skill-creator/SKILL.md +154 -0
  215. package/app/skills/skill-creator/templates/dashboard/index.html +130 -0
  216. package/app/skills/skill-creator/templates/reasoning-engine/assets/example.json +12 -0
  217. package/app/skills/skill-creator/templates/reasoning-engine/search.py +110 -0
  218. package/app/skills/subagent-development/SKILL.md +225 -0
  219. package/app/skills/subagent-development/reference/code-quality-reviewer-prompt.md +145 -0
  220. package/app/skills/subagent-development/reference/implementer-prompt.md +118 -0
  221. package/app/skills/subagent-development/reference/spec-reviewer-prompt.md +100 -0
  222. package/app/skills/swarm/SKILL.md +81 -0
  223. package/app/skills/swift-patterns/SKILL.md +500 -0
  224. package/app/skills/tdd/SKILL.md +174 -0
  225. package/app/skills/tdd/reference/deep-modules.md +32 -0
  226. package/app/skills/tdd/reference/interface-design.md +32 -0
  227. package/app/skills/tdd/reference/mocking.md +52 -0
  228. package/app/skills/tdd/reference/refactoring.md +10 -0
  229. package/app/skills/tdd/reference/tests.md +59 -0
  230. package/app/skills/teams/SKILL.md +101 -0
  231. package/app/skills/test/SKILL.md +107 -0
  232. package/app/skills/test/scripts/detect-runner.py +113 -0
  233. package/app/skills/testing-patterns/SKILL.md +73 -0
  234. package/app/skills/testing-patterns/reference/flutter-testing.md +33 -0
  235. package/app/skills/testing-patterns/reference/go-testing.md +52 -0
  236. package/app/skills/testing-patterns/reference/php-phpunit.md +39 -0
  237. package/app/skills/testing-patterns/reference/python-pytest.md +228 -0
  238. package/app/skills/testing-patterns/reference/typescript-vitest.md +50 -0
  239. package/app/skills/triage-issue/SKILL.md +120 -0
  240. package/app/skills/typescript-patterns/SKILL.md +256 -0
  241. package/app/skills/ubiquitous-language/SKILL.md +74 -0
  242. package/app/skills/verification-before-completion/SKILL.md +108 -0
  243. package/app/skills/workflow/SKILL.md +250 -0
  244. package/app/skills/write-a-prd/SKILL.md +129 -0
  245. package/app/skills/write-a-prd/reference/visual-companion.md +78 -0
  246. package/app/skills/write-a-prd/scripts/frame-template.html +111 -0
  247. package/app/skills/write-a-prd/scripts/visual-server.cjs +79 -0
  248. package/app/templates/skill/generator/SKILL.md.template +40 -0
  249. package/app/templates/skill/knowledge/SKILL.md.template +52 -0
  250. package/app/templates/skill/linter/SKILL.md.template +34 -0
  251. package/app/templates/skill/reviewer/SKILL.md.template +51 -0
  252. package/app/templates/skill/workflow/SKILL.md.template +49 -0
  253. package/benchmarks/README.md +111 -0
  254. package/benchmarks/ecosystem-dashboard.json +148 -0
  255. package/benchmarks/ecosystem-harvest.json +148 -0
  256. package/benchmarks/results.json +38 -0
  257. package/benchmarks/run.py +351 -0
  258. package/bin/ai-toolkit.js +345 -0
  259. package/kb/best-practices/README.md +11 -0
  260. package/kb/howto/README.md +11 -0
  261. package/kb/procedures/maintenance-sop.md +306 -0
  262. package/kb/reference/agents-catalog.md +124 -0
  263. package/kb/reference/anti-pattern-registry-format.md +221 -0
  264. package/kb/reference/architecture-overview.md +232 -0
  265. package/kb/reference/benchmark-config.md +62 -0
  266. package/kb/reference/ci-integration.md +66 -0
  267. package/kb/reference/claude-ecosystem-benchmark-snapshot.md +80 -0
  268. package/kb/reference/claude-ecosystem-expansion-foundations.md +102 -0
  269. package/kb/reference/commands-catalog.md +21 -0
  270. package/kb/reference/distribution-model.md +63 -0
  271. package/kb/reference/global-install-model.md +56 -0
  272. package/kb/reference/hierarchical-override-pattern.md +200 -0
  273. package/kb/reference/hooks-catalog.md +306 -0
  274. package/kb/reference/integrations.md +88 -0
  275. package/kb/reference/language-packs.md +52 -0
  276. package/kb/reference/merge-friendly-install-model.md +58 -0
  277. package/kb/reference/plugin-pack-conventions.md +151 -0
  278. package/kb/reference/quick-wins-implementation-summary.md +70 -0
  279. package/kb/reference/skill-templates.md +50 -0
  280. package/kb/reference/skills-catalog.md +215 -0
  281. package/kb/reference/skills-unification.md +57 -0
  282. package/kb/reference/stats.md +69 -0
  283. package/kb/reference/sync.md +76 -0
  284. package/kb/troubleshooting/README.md +11 -0
  285. package/llms-full.txt +3068 -0
  286. package/llms.txt +39 -0
  287. package/package.json +75 -0
  288. package/scripts/_common.py +160 -0
  289. package/scripts/add_rule.py +50 -0
  290. package/scripts/benchmark_config.py +127 -0
  291. package/scripts/benchmark_ecosystem.py +288 -0
  292. package/scripts/check_deps.py +260 -0
  293. package/scripts/create_skill.py +118 -0
  294. package/scripts/doctor.py +504 -0
  295. package/scripts/eject.py +113 -0
  296. package/scripts/emission.py +256 -0
  297. package/scripts/evaluate_skills.py +260 -0
  298. package/scripts/frontmatter.py +58 -0
  299. package/scripts/generate_agents_md.py +91 -0
  300. package/scripts/generate_aider_conf.py +51 -0
  301. package/scripts/generate_cline.py +35 -0
  302. package/scripts/generate_copilot.py +30 -0
  303. package/scripts/generate_cursor_rules.py +35 -0
  304. package/scripts/generate_gemini.py +28 -0
  305. package/scripts/generate_llms_txt.py +164 -0
  306. package/scripts/generate_roo_modes.py +80 -0
  307. package/scripts/generate_windsurf.py +35 -0
  308. package/scripts/generator_base.py +140 -0
  309. package/scripts/harvest_ecosystem.py +50 -0
  310. package/scripts/inject_rule_cli.py +101 -0
  311. package/scripts/inject_section_cli.py +47 -0
  312. package/scripts/injection.py +180 -0
  313. package/scripts/install.py +236 -0
  314. package/scripts/install_git_hooks.py +71 -0
  315. package/scripts/install_steps/__init__.py +5 -0
  316. package/scripts/install_steps/ai_tools.py +261 -0
  317. package/scripts/install_steps/hooks.py +90 -0
  318. package/scripts/install_steps/markers.py +79 -0
  319. package/scripts/install_steps/symlinks.py +87 -0
  320. package/scripts/merge-hooks.py +192 -0
  321. package/scripts/plugin.py +642 -0
  322. package/scripts/plugin_schema.py +138 -0
  323. package/scripts/remove_rule.py +58 -0
  324. package/scripts/stats.py +81 -0
  325. package/scripts/sync.py +215 -0
  326. package/scripts/uninstall.py +292 -0
  327. package/scripts/validate.py +700 -0
@@ -0,0 +1,446 @@
1
+ ---
2
+ name: kotlin-patterns
3
+ description: "Loaded when user asks about Kotlin development patterns"
4
+ effort: medium
5
+ user-invocable: false
6
+ ---
7
+
8
+ # Kotlin Patterns Skill
9
+
10
+ ## Project Structure
11
+
12
+ ### Gradle KTS Multi-Module Layout
13
+ ```
14
+ project-root/
15
+ ├── build.gradle.kts
16
+ ├── settings.gradle.kts
17
+ ├── gradle/libs.versions.toml
18
+ ├── app/
19
+ │ ├── build.gradle.kts
20
+ │ └── src/{main,test}/kotlin/com/example/app/
21
+ ├── domain/
22
+ │ └── src/main/kotlin/com/example/domain/
23
+ │ ├── model/
24
+ │ ├── repository/
25
+ │ └── usecase/
26
+ └── infrastructure/
27
+ └── src/main/kotlin/com/example/infra/
28
+ ```
29
+
30
+ ### settings.gradle.kts
31
+ ```kotlin
32
+ rootProject.name = "my-project"
33
+ dependencyResolutionManagement {
34
+ versionCatalogs { create("libs") { from(files("gradle/libs.versions.toml")) } }
35
+ }
36
+ include(":app", ":domain", ":infrastructure")
37
+ ```
38
+
39
+ ### Module build.gradle.kts
40
+ ```kotlin
41
+ plugins {
42
+ alias(libs.plugins.kotlin.jvm)
43
+ alias(libs.plugins.kotlin.serialization)
44
+ }
45
+ dependencies {
46
+ implementation(project(":domain"))
47
+ implementation(libs.kotlinx.coroutines.core)
48
+ testImplementation(libs.bundles.testing)
49
+ }
50
+ kotlin { jvmToolchain(21) }
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Idioms / Code Style
56
+
57
+ ### Data Classes + Value Classes
58
+ ```kotlin
59
+ data class User(
60
+ val id: UserId,
61
+ val name: String,
62
+ val email: String,
63
+ val role: Role = Role.USER,
64
+ ) {
65
+ init {
66
+ require(name.isNotBlank()) { "Name must not be blank" }
67
+ require(email.contains("@")) { "Invalid email format" }
68
+ }
69
+ }
70
+
71
+ @JvmInline
72
+ value class UserId(val value: String) // Zero-overhead type-safe ID
73
+ enum class Role { ADMIN, USER, GUEST }
74
+ ```
75
+
76
+ ### Sealed Interfaces for Domain Modeling
77
+ ```kotlin
78
+ sealed interface PaymentResult {
79
+ data class Success(val transactionId: String, val amount: Money) : PaymentResult
80
+ data class Declined(val reason: String) : PaymentResult
81
+ data class Error(val exception: Throwable) : PaymentResult
82
+ }
83
+
84
+ // Exhaustive when -- compiler enforces all branches
85
+ fun handlePayment(result: PaymentResult): String = when (result) {
86
+ is PaymentResult.Success -> "Paid: ${result.amount}"
87
+ is PaymentResult.Declined -> "Declined: ${result.reason}"
88
+ is PaymentResult.Error -> "Error: ${result.exception.message}"
89
+ }
90
+ ```
91
+
92
+ ### Extension Functions
93
+ ```kotlin
94
+ fun String.toSlug(): String =
95
+ lowercase().replace(Regex("[^a-z0-9\\s-]"), "").replace(Regex("\\s+"), "-").trim('-')
96
+
97
+ // Scoped extensions -- visible only inside containing class
98
+ class OrderService {
99
+ private fun Order.totalWithTax(): Money = total * (1 + taxRate)
100
+ }
101
+ ```
102
+
103
+ ### Null Safety
104
+ ```kotlin
105
+ fun getDisplayName(user: User?): String =
106
+ user?.name?.takeIf { it.isNotBlank() } ?: "Anonymous"
107
+
108
+ fun processEmail(email: String?) { email?.let { sendWelcomeEmail(it) } }
109
+
110
+ fun loadConfig(path: String?): Config {
111
+ val resolved = requireNotNull(path) { "Config path must not be null" }
112
+ return parseConfig(resolved)
113
+ }
114
+ ```
115
+
116
+ ### Scope Functions
117
+
118
+ | Function | Ref | Returns | Use case |
119
+ |----------|-----|---------|----------|
120
+ | `let` | `it` | Lambda result | Null check + transform |
121
+ | `run` | `this` | Lambda result | Config + compute |
122
+ | `apply` | `this` | Object itself | Object initialization |
123
+ | `also` | `it` | Object itself | Side effects |
124
+
125
+ ```kotlin
126
+ val conn = Connection().apply { host = "localhost"; port = 5432 }
127
+ fun createUser(req: CreateUserRequest): User =
128
+ userRepository.save(req.toUser()).also { logger.info("Created: ${it.id}") }
129
+ ```
130
+
131
+ ### Type-Safe Builders (DSL)
132
+ ```kotlin
133
+ fun html(block: HtmlBuilder.() -> Unit): String = HtmlBuilder().apply(block).build()
134
+
135
+ val page = html {
136
+ head { title("My Page") }
137
+ body { p("Hello, world!") }
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Error Handling
144
+
145
+ ### Result<T> and runCatching
146
+ ```kotlin
147
+ fun findUser(id: UserId): Result<User> = runCatching {
148
+ userRepository.findById(id) ?: throw UserNotFoundException(id)
149
+ }
150
+
151
+ fun getUserDisplayName(id: UserId): String =
152
+ findUser(id).map { it.name }.recover { "Unknown User" }.getOrThrow()
153
+
154
+ fun handleLookup(id: UserId): Response = findUser(id).fold(
155
+ onSuccess = { Response.ok(it) },
156
+ onFailure = { Response.notFound(it.message) },
157
+ )
158
+ ```
159
+
160
+ ### Sealed Class Error Hierarchy
161
+ ```kotlin
162
+ sealed class DomainError(override val message: String) : Exception(message) {
163
+ data class NotFound(val resource: String, val id: String) : DomainError("$resource not found: $id")
164
+ data class Validation(val field: String, val reason: String) : DomainError("Invalid $field: $reason")
165
+ data class Conflict(val detail: String) : DomainError("Conflict: $detail")
166
+ }
167
+
168
+ fun handleError(error: DomainError): Response = when (error) {
169
+ is DomainError.NotFound -> Response.status(404).body(error.message)
170
+ is DomainError.Validation -> Response.status(422).body(error.message)
171
+ is DomainError.Conflict -> Response.status(409).body(error.message)
172
+ }
173
+ ```
174
+
175
+ ### Preconditions
176
+ ```kotlin
177
+ fun transferMoney(from: Account, to: Account, amount: Money) {
178
+ require(amount.value > 0) { "Transfer amount must be positive" }
179
+ require(from.id != to.id) { "Cannot transfer to same account" }
180
+ check(from.balance >= amount) { "Insufficient funds: ${from.balance}" }
181
+ }
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Testing Patterns
187
+
188
+ ### JUnit 5 + MockK
189
+ ```kotlin
190
+ class UserServiceTest {
191
+ private val repository = mockk<UserRepository>()
192
+ private val notifier = mockk<NotificationService>(relaxed = true)
193
+ private val service = UserService(repository, notifier)
194
+
195
+ @Test
196
+ fun `creates user and sends welcome notification`() {
197
+ val expected = User(UserId("1"), "Alice", "alice@test.com")
198
+ every { repository.save(any()) } returns expected
199
+
200
+ val result = service.createUser(CreateUserRequest("Alice", "alice@test.com"))
201
+
202
+ assertThat(result).isEqualTo(expected)
203
+ verify(exactly = 1) { notifier.sendWelcome(expected) }
204
+ }
205
+
206
+ @Test
207
+ fun `throws on duplicate email`() {
208
+ every { repository.save(any()) } throws DomainError.Conflict("Email exists")
209
+ assertThrows<DomainError.Conflict> {
210
+ service.createUser(CreateUserRequest("Bob", "dup@test.com"))
211
+ }
212
+ }
213
+ }
214
+ ```
215
+
216
+ ### Kotest + Property-Based Testing
217
+ ```kotlin
218
+ class MoneySpec : FunSpec({
219
+ test("addition is commutative") {
220
+ checkAll(Arb.positiveLong(), Arb.positiveLong()) { a, b ->
221
+ Money(a) + Money(b) shouldBe Money(b) + Money(a)
222
+ }
223
+ }
224
+ test("cannot create negative money") {
225
+ shouldThrow<IllegalArgumentException> { Money(-1) }
226
+ }
227
+ })
228
+ ```
229
+
230
+ ### assertSoftly + Parameterized Tests
231
+ ```kotlin
232
+ @Test
233
+ fun `user has correct defaults`() {
234
+ val user = User.create("Alice", "alice@test.com")
235
+ assertSoftly(user) {
236
+ name shouldBe "Alice"
237
+ role shouldBe Role.USER
238
+ isActive shouldBe true
239
+ }
240
+ }
241
+
242
+ @ParameterizedTest
243
+ @CsvSource("alice@test.com, true", "not-an-email, false", "'', false")
244
+ fun `validates email format`(input: String, expected: Boolean) {
245
+ assertThat(isValidEmail(input)).isEqualTo(expected)
246
+ }
247
+ ```
248
+
249
+ ---
250
+
251
+ ## Common Frameworks
252
+
253
+ ### Spring Boot with Kotlin
254
+ ```kotlin
255
+ @RestController
256
+ @RequestMapping("/api/v1/users")
257
+ class UserController(private val userService: UserService) {
258
+ @GetMapping("/{id}")
259
+ fun getUser(@PathVariable id: String): ResponseEntity<UserDto> =
260
+ userService.findById(UserId(id))?.let { ResponseEntity.ok(it.toDto()) }
261
+ ?: ResponseEntity.notFound().build()
262
+
263
+ @PostMapping
264
+ fun createUser(@Valid @RequestBody req: CreateUserRequest): ResponseEntity<UserDto> {
265
+ val user = userService.create(req)
266
+ return ResponseEntity.created(URI("/api/v1/users/${user.id.value}")).body(user.toDto())
267
+ }
268
+ }
269
+ ```
270
+
271
+ ### Ktor
272
+ ```kotlin
273
+ fun Application.configureRouting() {
274
+ routing {
275
+ route("/api/v1/users") {
276
+ get { call.respond(userService.listAll()) }
277
+ get("/{id}") {
278
+ val id = call.parameters["id"] ?: return@get call.respond(HttpStatusCode.BadRequest)
279
+ val user = userService.findById(UserId(id)) ?: return@get call.respond(HttpStatusCode.NotFound)
280
+ call.respond(user)
281
+ }
282
+ post {
283
+ val req = call.receive<CreateUserRequest>()
284
+ call.respond(HttpStatusCode.Created, userService.create(req))
285
+ }
286
+ }
287
+ }
288
+ }
289
+
290
+ fun Application.configurePlugins() {
291
+ install(ContentNegotiation) { json() }
292
+ install(StatusPages) {
293
+ exception<DomainError.NotFound> { call, e -> call.respond(HttpStatusCode.NotFound, e.message) }
294
+ }
295
+ }
296
+ ```
297
+
298
+ ### Exposed (SQL DSL)
299
+ ```kotlin
300
+ object Users : Table("users") {
301
+ val id = varchar("id", 36)
302
+ val name = varchar("name", 255)
303
+ val email = varchar("email", 255).uniqueIndex()
304
+ val role = enumerationByName<Role>("role", 20)
305
+ override val primaryKey = PrimaryKey(id)
306
+ }
307
+
308
+ suspend fun findByRole(role: Role): List<User> = newSuspendedTransaction(Dispatchers.IO) {
309
+ Users.selectAll().where { Users.role eq role }.map { it.toUser() }
310
+ }
311
+ ```
312
+
313
+ ### kotlinx.serialization
314
+ ```kotlin
315
+ @Serializable
316
+ data class ApiResponse<T>(val data: T, val meta: Meta? = null)
317
+
318
+ @Serializable
319
+ data class Meta(val page: Int, val totalPages: Int, val totalItems: Long)
320
+ ```
321
+
322
+ ### Koin (DI)
323
+ ```kotlin
324
+ val appModule = module {
325
+ singleOf(::UserRepository)
326
+ singleOf(::UserService)
327
+ factoryOf(::CreateUserUseCase)
328
+ }
329
+ fun Application.configureKoin() { install(Koin) { modules(appModule) } }
330
+ ```
331
+
332
+ ---
333
+
334
+ ## Performance Tips
335
+
336
+ ### Inline Functions
337
+ ```kotlin
338
+ inline fun <T> measureTimeAndReturn(block: () -> T): Pair<T, Duration> {
339
+ val start = System.nanoTime()
340
+ val result = block()
341
+ return result to Duration.ofNanos(System.nanoTime() - start)
342
+ }
343
+ // crossinline: prevents non-local returns
344
+ inline fun transaction(crossinline block: () -> Unit) {
345
+ begin(); try { block() } catch (e: Exception) { rollback(); throw e }; commit()
346
+ }
347
+ ```
348
+
349
+ ### Value Classes
350
+ ```kotlin
351
+ @JvmInline value class Email(val value: String) {
352
+ init { require(value.contains("@")) }
353
+ }
354
+ @JvmInline value class Meters(val value: Double)
355
+ // Compiles to raw String/Double -- zero object allocation
356
+ ```
357
+
358
+ ### Sequences vs Lists
359
+ ```kotlin
360
+ // Bad: 3 intermediate lists
361
+ users.filter { it.isActive }.map { it.name }.take(10)
362
+ // Good: lazy, single pass, stops after 10
363
+ users.asSequence().filter { it.isActive }.map { it.name }.take(10).toList()
364
+ // Rule: sequences for 3+ chained ops on 1000+ elements
365
+ ```
366
+
367
+ ### Coroutines
368
+ ```kotlin
369
+ suspend fun loadDashboard(userId: String): Dashboard = coroutineScope {
370
+ val profile = async { userService.getProfile(userId) }
371
+ val orders = async { orderService.getRecent(userId) }
372
+ val notifs = async { notificationService.getUnread(userId) }
373
+ Dashboard(profile.await(), orders.await(), notifs.await())
374
+ }
375
+
376
+ fun observeOrders(): Flow<Order> =
377
+ orderRepository.observe().map { it.toDomain() }.catch { emit(Order.EMPTY) }.flowOn(Dispatchers.IO)
378
+ ```
379
+
380
+ ### Avoid Reflection
381
+ ```kotlin
382
+ // Bad: Gson uses reflection -- slow, no compile-time safety
383
+ val json = Gson().toJson(user)
384
+ // Good: kotlinx.serialization uses compile-time codegen
385
+ @Serializable data class User(val id: String, val name: String)
386
+ val json = Json.encodeToString(user)
387
+ ```
388
+
389
+ ---
390
+
391
+ ## Build / Package Management
392
+
393
+ ### Version Catalog (gradle/libs.versions.toml)
394
+ ```toml
395
+ [versions]
396
+ kotlin = "2.1.0"
397
+ coroutines = "1.10.1"
398
+ ktor = "3.0.3"
399
+ kotest = "5.9.1"
400
+ mockk = "1.13.14"
401
+
402
+ [libraries]
403
+ kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
404
+ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.7.3" }
405
+ ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }
406
+ mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
407
+ kotest-runner = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" }
408
+ kotest-assertions = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" }
409
+
410
+ [bundles]
411
+ testing = ["mockk", "kotest-runner", "kotest-assertions"]
412
+
413
+ [plugins]
414
+ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
415
+ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
416
+ ```
417
+
418
+ ### Kotlin Multiplatform
419
+ ```kotlin
420
+ kotlin {
421
+ jvm(); iosArm64(); iosSimulatorArm64(); js(IR) { browser() }
422
+ sourceSets {
423
+ commonMain.dependencies {
424
+ implementation(libs.kotlinx.coroutines.core)
425
+ implementation(libs.kotlinx.serialization.json)
426
+ }
427
+ commonTest.dependencies { implementation(kotlin("test")) }
428
+ jvmMain.dependencies { implementation(libs.ktor.server.core) }
429
+ }
430
+ }
431
+ ```
432
+
433
+ ---
434
+
435
+ ## Anti-Patterns to Avoid
436
+
437
+ | Anti-Pattern | Problem | Solution |
438
+ |--------------|---------|----------|
439
+ | `!!` everywhere | NPE at runtime | Safe calls, elvis, `requireNotNull` |
440
+ | `var` by default | Mutability bugs | Default to `val` |
441
+ | Catching `Exception` | Swallows `CancellationException` | Catch specific types |
442
+ | Mutable data class props | Breaks hashCode/equals | Use `val` in data classes |
443
+ | Stringly-typed IDs | Mix up userId/orderId | Value classes |
444
+ | Blocking in coroutine | Thread starvation | `withContext(Dispatchers.IO)` |
445
+ | Ignoring `Result` failures | Silent errors | Always handle both paths |
446
+ | God object / util class | No cohesion | Extension functions |
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: lint
3
+ description: "Lint code with auto-detected tools and fix suggestions"
4
+ effort: low
5
+ disable-model-invocation: true
6
+ argument-hint: "[path]"
7
+ allowed-tools: Bash, Read
8
+ ---
9
+
10
+ # Lint Runner
11
+
12
+ $ARGUMENTS
13
+
14
+ Run linting and type checking based on detected project type.
15
+
16
+ ## Project context
17
+
18
+ - Project files: !`ls pyproject.toml package.json composer.json pubspec.yaml go.mod 2>/dev/null`
19
+
20
+ ## Auto-Detection
21
+
22
+ Run the bundled script to detect available linters:
23
+
24
+ ```bash
25
+ python3 ${CLAUDE_SKILL_DIR}/scripts/detect-linters.py .
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ```
31
+ /lint [path]
32
+ ```
33
+
34
+ ## Commands by Project Type
35
+
36
+ | Project Type | Lint Command | Type Check |
37
+ |--------------|--------------|------------|
38
+ | **Python** | `ruff check .` | `mypy .` |
39
+ | **TypeScript/Node** | `npx eslint .` | `npx tsc --noEmit` |
40
+ | **PHP** | `./vendor/bin/phpstan analyse` | - |
41
+ | **Go** | `golangci-lint run` | - |
42
+ | **Rust** | `cargo clippy` | - |
43
+ | **Flutter/Dart** | `dart analyze` | - |
44
+
45
+ ## Python Projects
46
+
47
+ ```bash
48
+ # Linting
49
+ ruff check .
50
+
51
+ # Type checking
52
+ mypy .
53
+
54
+ # Auto-fix
55
+ ruff check --fix .
56
+ ruff format .
57
+ ```
58
+
59
+ ## TypeScript/Node Projects
60
+
61
+ ```bash
62
+ # Linting
63
+ npx eslint .
64
+
65
+ # Type checking
66
+ npx tsc --noEmit
67
+
68
+ # Auto-fix
69
+ npx eslint --fix .
70
+ ```
71
+
72
+ ## PHP Projects
73
+
74
+ ```bash
75
+ # Static analysis
76
+ ./vendor/bin/phpstan analyse
77
+
78
+ # Code style
79
+ ./vendor/bin/phpcs
80
+ ./vendor/bin/phpcbf # auto-fix
81
+ ```
82
+
83
+ ## Docker Execution (if applicable)
84
+
85
+ ```bash
86
+ # Generic pattern - replace {container} with your app container
87
+ docker exec {container} make lint
88
+ docker exec {container} make typecheck
89
+ ```
90
+
91
+ ## Quality Gates
92
+
93
+ - Linting: 0 errors
94
+ - Type checking: 0 errors (for new code)
95
+
96
+ ## Common Issues
97
+
98
+ | Error | Fix |
99
+ |-------|-----|
100
+ | Missing type hints | Add type annotations |
101
+ | Unused imports | Remove or use `# noqa: F401` |
102
+ | Line too long | Break line or disable for that line |
103
+ | Import order | Let linter fix with `--fix` |
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env python3
2
+ """Detect available linters and build combined lint/format commands for a project.
3
+
4
+ Scans a project directory for Python, Node.js, PHP, Go, and Dart
5
+ configuration files, checks which linters are installed or configured,
6
+ and returns a JSON object with individual linter entries, a combined
7
+ check command, and available format commands.
8
+
9
+ Usage::
10
+
11
+ python3 detect-linters.py [project_directory]
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import sys
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
20
+ from _lib.detect_utils import is_installed, read_json, read_text, run_detector
21
+
22
+
23
+ def detect(project_dir: Path) -> dict[str, Any]:
24
+ """Detect linters configured or installed for the project."""
25
+ linters: list[dict[str, Any]] = []
26
+ format_commands: list[str] = []
27
+
28
+ # Python: pyproject.toml
29
+ pyproject = read_text(project_dir / "pyproject.toml")
30
+ if pyproject or (project_dir / "setup.py").exists():
31
+ py_linters: dict[str, dict[str, str | None]] = {
32
+ "ruff": {"check": "ruff check .", "fix": "ruff check --fix .", "format": "ruff format ."},
33
+ "flake8": {"check": "flake8 .", "fix": None, "format": None},
34
+ "pylint": {"check": "pylint src/", "fix": None, "format": None},
35
+ "mypy": {"check": "mypy .", "fix": None, "format": None},
36
+ "black": {"check": "black --check .", "fix": None, "format": "black ."},
37
+ }
38
+ for name, cmds in py_linters.items():
39
+ configured = pyproject and name in pyproject if pyproject else False
40
+ installed = is_installed(name)
41
+ if configured or installed:
42
+ linters.append({"name": name, "language": "python", "installed": installed,
43
+ "configured": configured, "check_command": cmds["check"],
44
+ "fix_command": cmds["fix"]})
45
+ if cmds.get("format"):
46
+ format_commands.append(cmds["format"]) # type: ignore[arg-type]
47
+
48
+ # Node.js: package.json
49
+ pkg = read_json(project_dir / "package.json")
50
+ if pkg:
51
+ dev_deps = {**pkg.get("devDependencies", {}), **pkg.get("dependencies", {})}
52
+ node_linters: dict[str, dict[str, str | None]] = {
53
+ "eslint": {"check": "npx eslint .", "fix": "npx eslint --fix ."},
54
+ "prettier": {"check": "npx prettier --check .", "format": "npx prettier --write ."},
55
+ "tsc": {"check": "npx tsc --noEmit", "fix": None},
56
+ }
57
+ for name, cmds in node_linters.items():
58
+ in_deps = name in dev_deps or (name == "tsc" and "typescript" in dev_deps)
59
+ installed = is_installed(f"npx {name}") if name != "tsc" else is_installed("npx tsc")
60
+ if in_deps:
61
+ linters.append({"name": name, "language": "javascript/typescript",
62
+ "installed": True, "configured": in_deps,
63
+ "check_command": cmds["check"], "fix_command": cmds.get("fix")})
64
+ if cmds.get("format"):
65
+ format_commands.append(cmds["format"]) # type: ignore[arg-type]
66
+
67
+ # PHP: composer.json
68
+ composer = read_json(project_dir / "composer.json")
69
+ if composer:
70
+ require_dev: dict[str, str] = composer.get("require-dev", {})
71
+ php_linters: dict[str, dict[str, str]] = {
72
+ "phpstan": {"check": "./vendor/bin/phpstan analyse", "pkg": "phpstan/phpstan"},
73
+ "psalm": {"check": "./vendor/bin/psalm", "pkg": "vimeo/psalm"},
74
+ "phpcs": {"check": "./vendor/bin/phpcs", "pkg": "squizlabs/php_codesniffer"},
75
+ }
76
+ for name, info in php_linters.items():
77
+ in_deps = any(info["pkg"] in k for k in require_dev)
78
+ if in_deps:
79
+ linters.append({"name": name, "language": "php", "installed": True,
80
+ "configured": True, "check_command": info["check"], "fix_command": None})
81
+
82
+ # Go: .golangci.yml
83
+ if (project_dir / "go.mod").exists():
84
+ go_lint: dict[str, str | None] = {"check": "golangci-lint run", "fix": None}
85
+ configured = (project_dir / ".golangci.yml").exists() or (project_dir / ".golangci.yaml").exists()
86
+ installed = is_installed("golangci-lint")
87
+ if configured or installed:
88
+ linters.append({"name": "golangci-lint", "language": "go", "installed": installed,
89
+ "configured": configured, "check_command": go_lint["check"], "fix_command": None})
90
+ linters.append({"name": "go-vet", "language": "go", "installed": True,
91
+ "configured": True, "check_command": "go vet ./...", "fix_command": None})
92
+
93
+ # Dart: pubspec.yaml
94
+ if (project_dir / "pubspec.yaml").exists():
95
+ linters.append({"name": "dart-analyze", "language": "dart", "installed": is_installed("dart"),
96
+ "configured": True, "check_command": "dart analyze", "fix_command": "dart fix --apply"})
97
+
98
+ # Build combined command
99
+ check_parts = [l["check_command"] for l in linters if l.get("installed") and l.get("check_command")]
100
+ combined = " && ".join(check_parts) if check_parts else None
101
+
102
+ return {
103
+ "linters": linters,
104
+ "combined_command": combined,
105
+ "format_commands": format_commands,
106
+ "linter_count": len(linters),
107
+ "installed_count": sum(1 for l in linters if l.get("installed")),
108
+ }
109
+
110
+
111
+ if __name__ == "__main__":
112
+ run_detector(detect)