solokit 0.1.1__py3-none-any.whl

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 (323) hide show
  1. solokit/__init__.py +10 -0
  2. solokit/__version__.py +3 -0
  3. solokit/cli.py +374 -0
  4. solokit/core/__init__.py +1 -0
  5. solokit/core/cache.py +102 -0
  6. solokit/core/command_runner.py +278 -0
  7. solokit/core/config.py +453 -0
  8. solokit/core/config_validator.py +204 -0
  9. solokit/core/constants.py +291 -0
  10. solokit/core/error_formatter.py +279 -0
  11. solokit/core/error_handlers.py +346 -0
  12. solokit/core/exceptions.py +1567 -0
  13. solokit/core/file_ops.py +309 -0
  14. solokit/core/logging_config.py +166 -0
  15. solokit/core/output.py +99 -0
  16. solokit/core/performance.py +57 -0
  17. solokit/core/protocols.py +141 -0
  18. solokit/core/types.py +312 -0
  19. solokit/deployment/__init__.py +1 -0
  20. solokit/deployment/executor.py +411 -0
  21. solokit/git/__init__.py +1 -0
  22. solokit/git/integration.py +619 -0
  23. solokit/init/__init__.py +41 -0
  24. solokit/init/claude_commands_installer.py +87 -0
  25. solokit/init/dependency_installer.py +313 -0
  26. solokit/init/docs_structure.py +90 -0
  27. solokit/init/env_generator.py +160 -0
  28. solokit/init/environment_validator.py +334 -0
  29. solokit/init/git_hooks_installer.py +71 -0
  30. solokit/init/git_setup.py +188 -0
  31. solokit/init/gitignore_updater.py +195 -0
  32. solokit/init/initial_commit.py +145 -0
  33. solokit/init/initial_scans.py +109 -0
  34. solokit/init/orchestrator.py +246 -0
  35. solokit/init/readme_generator.py +207 -0
  36. solokit/init/session_structure.py +239 -0
  37. solokit/init/template_installer.py +424 -0
  38. solokit/learning/__init__.py +1 -0
  39. solokit/learning/archiver.py +115 -0
  40. solokit/learning/categorizer.py +126 -0
  41. solokit/learning/curator.py +428 -0
  42. solokit/learning/extractor.py +352 -0
  43. solokit/learning/reporter.py +351 -0
  44. solokit/learning/repository.py +254 -0
  45. solokit/learning/similarity.py +342 -0
  46. solokit/learning/validator.py +144 -0
  47. solokit/project/__init__.py +1 -0
  48. solokit/project/init.py +1162 -0
  49. solokit/project/stack.py +436 -0
  50. solokit/project/sync_plugin.py +438 -0
  51. solokit/project/tree.py +375 -0
  52. solokit/quality/__init__.py +1 -0
  53. solokit/quality/api_validator.py +424 -0
  54. solokit/quality/checkers/__init__.py +25 -0
  55. solokit/quality/checkers/base.py +114 -0
  56. solokit/quality/checkers/context7.py +221 -0
  57. solokit/quality/checkers/custom.py +162 -0
  58. solokit/quality/checkers/deployment.py +323 -0
  59. solokit/quality/checkers/documentation.py +179 -0
  60. solokit/quality/checkers/formatting.py +161 -0
  61. solokit/quality/checkers/integration.py +394 -0
  62. solokit/quality/checkers/linting.py +159 -0
  63. solokit/quality/checkers/security.py +261 -0
  64. solokit/quality/checkers/spec_completeness.py +127 -0
  65. solokit/quality/checkers/tests.py +184 -0
  66. solokit/quality/env_validator.py +306 -0
  67. solokit/quality/gates.py +655 -0
  68. solokit/quality/reporters/__init__.py +10 -0
  69. solokit/quality/reporters/base.py +25 -0
  70. solokit/quality/reporters/console.py +98 -0
  71. solokit/quality/reporters/json_reporter.py +34 -0
  72. solokit/quality/results.py +98 -0
  73. solokit/session/__init__.py +1 -0
  74. solokit/session/briefing/__init__.py +245 -0
  75. solokit/session/briefing/documentation_loader.py +53 -0
  76. solokit/session/briefing/formatter.py +476 -0
  77. solokit/session/briefing/git_context.py +282 -0
  78. solokit/session/briefing/learning_loader.py +212 -0
  79. solokit/session/briefing/milestone_builder.py +78 -0
  80. solokit/session/briefing/orchestrator.py +137 -0
  81. solokit/session/briefing/stack_detector.py +51 -0
  82. solokit/session/briefing/tree_generator.py +52 -0
  83. solokit/session/briefing/work_item_loader.py +209 -0
  84. solokit/session/briefing.py +353 -0
  85. solokit/session/complete.py +1188 -0
  86. solokit/session/status.py +246 -0
  87. solokit/session/validate.py +452 -0
  88. solokit/templates/.claude/commands/end.md +109 -0
  89. solokit/templates/.claude/commands/init.md +159 -0
  90. solokit/templates/.claude/commands/learn-curate.md +88 -0
  91. solokit/templates/.claude/commands/learn-search.md +62 -0
  92. solokit/templates/.claude/commands/learn-show.md +69 -0
  93. solokit/templates/.claude/commands/learn.md +136 -0
  94. solokit/templates/.claude/commands/start.md +114 -0
  95. solokit/templates/.claude/commands/status.md +22 -0
  96. solokit/templates/.claude/commands/validate.md +27 -0
  97. solokit/templates/.claude/commands/work-delete.md +119 -0
  98. solokit/templates/.claude/commands/work-graph.md +139 -0
  99. solokit/templates/.claude/commands/work-list.md +26 -0
  100. solokit/templates/.claude/commands/work-new.md +114 -0
  101. solokit/templates/.claude/commands/work-next.md +25 -0
  102. solokit/templates/.claude/commands/work-show.md +24 -0
  103. solokit/templates/.claude/commands/work-update.md +141 -0
  104. solokit/templates/CHANGELOG.md +17 -0
  105. solokit/templates/WORK_ITEM_TYPES.md +141 -0
  106. solokit/templates/__init__.py +1 -0
  107. solokit/templates/bug_spec.md +217 -0
  108. solokit/templates/config.schema.json +150 -0
  109. solokit/templates/dashboard_refine/base/.gitignore +36 -0
  110. solokit/templates/dashboard_refine/base/app/(dashboard)/layout.tsx +22 -0
  111. solokit/templates/dashboard_refine/base/app/(dashboard)/page.tsx +68 -0
  112. solokit/templates/dashboard_refine/base/app/(dashboard)/users/page.tsx +77 -0
  113. solokit/templates/dashboard_refine/base/app/globals.css +60 -0
  114. solokit/templates/dashboard_refine/base/app/layout.tsx +23 -0
  115. solokit/templates/dashboard_refine/base/app/page.tsx +9 -0
  116. solokit/templates/dashboard_refine/base/components/client-refine-wrapper.tsx +21 -0
  117. solokit/templates/dashboard_refine/base/components/layout/header.tsx +44 -0
  118. solokit/templates/dashboard_refine/base/components/layout/sidebar.tsx +82 -0
  119. solokit/templates/dashboard_refine/base/components/ui/button.tsx +53 -0
  120. solokit/templates/dashboard_refine/base/components/ui/card.tsx +78 -0
  121. solokit/templates/dashboard_refine/base/components/ui/table.tsx +116 -0
  122. solokit/templates/dashboard_refine/base/components.json +16 -0
  123. solokit/templates/dashboard_refine/base/lib/refine.tsx +65 -0
  124. solokit/templates/dashboard_refine/base/lib/utils.ts +13 -0
  125. solokit/templates/dashboard_refine/base/next.config.ts +10 -0
  126. solokit/templates/dashboard_refine/base/package.json.template +40 -0
  127. solokit/templates/dashboard_refine/base/postcss.config.mjs +8 -0
  128. solokit/templates/dashboard_refine/base/providers/refine-provider.tsx +26 -0
  129. solokit/templates/dashboard_refine/base/tailwind.config.ts +57 -0
  130. solokit/templates/dashboard_refine/base/tsconfig.json +27 -0
  131. solokit/templates/dashboard_refine/docker/Dockerfile +57 -0
  132. solokit/templates/dashboard_refine/docker/docker-compose.prod.yml +31 -0
  133. solokit/templates/dashboard_refine/docker/docker-compose.yml +21 -0
  134. solokit/templates/dashboard_refine/tier-1-essential/.eslintrc.json +7 -0
  135. solokit/templates/dashboard_refine/tier-1-essential/jest.config.ts +17 -0
  136. solokit/templates/dashboard_refine/tier-1-essential/jest.setup.ts +1 -0
  137. solokit/templates/dashboard_refine/tier-1-essential/package.json.tier1.template +57 -0
  138. solokit/templates/dashboard_refine/tier-1-essential/tests/setup.ts +26 -0
  139. solokit/templates/dashboard_refine/tier-1-essential/tests/unit/example.test.tsx +73 -0
  140. solokit/templates/dashboard_refine/tier-2-standard/package.json.tier2.template +62 -0
  141. solokit/templates/dashboard_refine/tier-3-comprehensive/eslint.config.mjs +22 -0
  142. solokit/templates/dashboard_refine/tier-3-comprehensive/package.json.tier3.template +79 -0
  143. solokit/templates/dashboard_refine/tier-3-comprehensive/playwright.config.ts +66 -0
  144. solokit/templates/dashboard_refine/tier-3-comprehensive/stryker.conf.json +38 -0
  145. solokit/templates/dashboard_refine/tier-3-comprehensive/tests/e2e/dashboard.spec.ts +88 -0
  146. solokit/templates/dashboard_refine/tier-3-comprehensive/tests/e2e/user-management.spec.ts +102 -0
  147. solokit/templates/dashboard_refine/tier-3-comprehensive/tests/integration/dashboard.test.tsx +90 -0
  148. solokit/templates/dashboard_refine/tier-3-comprehensive/type-coverage.json +16 -0
  149. solokit/templates/dashboard_refine/tier-4-production/instrumentation.ts +9 -0
  150. solokit/templates/dashboard_refine/tier-4-production/k6/dashboard-load-test.js +70 -0
  151. solokit/templates/dashboard_refine/tier-4-production/next.config.ts +46 -0
  152. solokit/templates/dashboard_refine/tier-4-production/package.json.tier4.template +89 -0
  153. solokit/templates/dashboard_refine/tier-4-production/sentry.client.config.ts +26 -0
  154. solokit/templates/dashboard_refine/tier-4-production/sentry.edge.config.ts +11 -0
  155. solokit/templates/dashboard_refine/tier-4-production/sentry.server.config.ts +11 -0
  156. solokit/templates/deployment_spec.md +500 -0
  157. solokit/templates/feature_spec.md +248 -0
  158. solokit/templates/fullstack_nextjs/base/.gitignore +36 -0
  159. solokit/templates/fullstack_nextjs/base/app/api/example/route.ts +65 -0
  160. solokit/templates/fullstack_nextjs/base/app/globals.css +27 -0
  161. solokit/templates/fullstack_nextjs/base/app/layout.tsx +20 -0
  162. solokit/templates/fullstack_nextjs/base/app/page.tsx +32 -0
  163. solokit/templates/fullstack_nextjs/base/components/example-component.tsx +20 -0
  164. solokit/templates/fullstack_nextjs/base/lib/prisma.ts +17 -0
  165. solokit/templates/fullstack_nextjs/base/lib/utils.ts +13 -0
  166. solokit/templates/fullstack_nextjs/base/lib/validations.ts +20 -0
  167. solokit/templates/fullstack_nextjs/base/next.config.ts +7 -0
  168. solokit/templates/fullstack_nextjs/base/package.json.template +32 -0
  169. solokit/templates/fullstack_nextjs/base/postcss.config.mjs +8 -0
  170. solokit/templates/fullstack_nextjs/base/prisma/schema.prisma +21 -0
  171. solokit/templates/fullstack_nextjs/base/tailwind.config.ts +19 -0
  172. solokit/templates/fullstack_nextjs/base/tsconfig.json +27 -0
  173. solokit/templates/fullstack_nextjs/docker/Dockerfile +60 -0
  174. solokit/templates/fullstack_nextjs/docker/docker-compose.prod.yml +57 -0
  175. solokit/templates/fullstack_nextjs/docker/docker-compose.yml +47 -0
  176. solokit/templates/fullstack_nextjs/tier-1-essential/.eslintrc.json +7 -0
  177. solokit/templates/fullstack_nextjs/tier-1-essential/jest.config.ts +17 -0
  178. solokit/templates/fullstack_nextjs/tier-1-essential/jest.setup.ts +1 -0
  179. solokit/templates/fullstack_nextjs/tier-1-essential/package.json.tier1.template +48 -0
  180. solokit/templates/fullstack_nextjs/tier-1-essential/tests/api/example.test.ts +88 -0
  181. solokit/templates/fullstack_nextjs/tier-1-essential/tests/setup.ts +22 -0
  182. solokit/templates/fullstack_nextjs/tier-1-essential/tests/unit/example.test.tsx +22 -0
  183. solokit/templates/fullstack_nextjs/tier-2-standard/package.json.tier2.template +52 -0
  184. solokit/templates/fullstack_nextjs/tier-3-comprehensive/eslint.config.mjs +39 -0
  185. solokit/templates/fullstack_nextjs/tier-3-comprehensive/package.json.tier3.template +68 -0
  186. solokit/templates/fullstack_nextjs/tier-3-comprehensive/playwright.config.ts +66 -0
  187. solokit/templates/fullstack_nextjs/tier-3-comprehensive/stryker.conf.json +33 -0
  188. solokit/templates/fullstack_nextjs/tier-3-comprehensive/tests/e2e/flow.spec.ts +59 -0
  189. solokit/templates/fullstack_nextjs/tier-3-comprehensive/tests/integration/api.test.ts +165 -0
  190. solokit/templates/fullstack_nextjs/tier-3-comprehensive/type-coverage.json +12 -0
  191. solokit/templates/fullstack_nextjs/tier-4-production/instrumentation.ts +9 -0
  192. solokit/templates/fullstack_nextjs/tier-4-production/k6/load-test.js +45 -0
  193. solokit/templates/fullstack_nextjs/tier-4-production/next.config.ts +46 -0
  194. solokit/templates/fullstack_nextjs/tier-4-production/package.json.tier4.template +77 -0
  195. solokit/templates/fullstack_nextjs/tier-4-production/sentry.client.config.ts +26 -0
  196. solokit/templates/fullstack_nextjs/tier-4-production/sentry.edge.config.ts +11 -0
  197. solokit/templates/fullstack_nextjs/tier-4-production/sentry.server.config.ts +11 -0
  198. solokit/templates/git-hooks/prepare-commit-msg +24 -0
  199. solokit/templates/integration_test_spec.md +363 -0
  200. solokit/templates/learnings.json +15 -0
  201. solokit/templates/ml_ai_fastapi/base/.gitignore +104 -0
  202. solokit/templates/ml_ai_fastapi/base/alembic/env.py +96 -0
  203. solokit/templates/ml_ai_fastapi/base/alembic.ini +114 -0
  204. solokit/templates/ml_ai_fastapi/base/pyproject.toml.template +91 -0
  205. solokit/templates/ml_ai_fastapi/base/requirements.txt.template +28 -0
  206. solokit/templates/ml_ai_fastapi/base/src/__init__.py +5 -0
  207. solokit/templates/ml_ai_fastapi/base/src/api/__init__.py +3 -0
  208. solokit/templates/ml_ai_fastapi/base/src/api/dependencies.py +20 -0
  209. solokit/templates/ml_ai_fastapi/base/src/api/routes/__init__.py +3 -0
  210. solokit/templates/ml_ai_fastapi/base/src/api/routes/example.py +134 -0
  211. solokit/templates/ml_ai_fastapi/base/src/api/routes/health.py +66 -0
  212. solokit/templates/ml_ai_fastapi/base/src/core/__init__.py +3 -0
  213. solokit/templates/ml_ai_fastapi/base/src/core/config.py +64 -0
  214. solokit/templates/ml_ai_fastapi/base/src/core/database.py +50 -0
  215. solokit/templates/ml_ai_fastapi/base/src/main.py +64 -0
  216. solokit/templates/ml_ai_fastapi/base/src/models/__init__.py +7 -0
  217. solokit/templates/ml_ai_fastapi/base/src/models/example.py +61 -0
  218. solokit/templates/ml_ai_fastapi/base/src/services/__init__.py +3 -0
  219. solokit/templates/ml_ai_fastapi/base/src/services/example.py +115 -0
  220. solokit/templates/ml_ai_fastapi/docker/Dockerfile +59 -0
  221. solokit/templates/ml_ai_fastapi/docker/docker-compose.prod.yml +112 -0
  222. solokit/templates/ml_ai_fastapi/docker/docker-compose.yml +77 -0
  223. solokit/templates/ml_ai_fastapi/tier-1-essential/pyproject.toml.tier1.template +112 -0
  224. solokit/templates/ml_ai_fastapi/tier-1-essential/pyrightconfig.json +41 -0
  225. solokit/templates/ml_ai_fastapi/tier-1-essential/pytest.ini +69 -0
  226. solokit/templates/ml_ai_fastapi/tier-1-essential/requirements-dev.txt +17 -0
  227. solokit/templates/ml_ai_fastapi/tier-1-essential/ruff.toml +81 -0
  228. solokit/templates/ml_ai_fastapi/tier-1-essential/tests/__init__.py +3 -0
  229. solokit/templates/ml_ai_fastapi/tier-1-essential/tests/conftest.py +72 -0
  230. solokit/templates/ml_ai_fastapi/tier-1-essential/tests/test_main.py +49 -0
  231. solokit/templates/ml_ai_fastapi/tier-1-essential/tests/unit/__init__.py +3 -0
  232. solokit/templates/ml_ai_fastapi/tier-1-essential/tests/unit/test_example.py +113 -0
  233. solokit/templates/ml_ai_fastapi/tier-2-standard/pyproject.toml.tier2.template +130 -0
  234. solokit/templates/ml_ai_fastapi/tier-3-comprehensive/locustfile.py +99 -0
  235. solokit/templates/ml_ai_fastapi/tier-3-comprehensive/mutmut_config.py +53 -0
  236. solokit/templates/ml_ai_fastapi/tier-3-comprehensive/pyproject.toml.tier3.template +150 -0
  237. solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/__init__.py +3 -0
  238. solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/conftest.py +74 -0
  239. solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/test_api.py +131 -0
  240. solokit/templates/ml_ai_fastapi/tier-4-production/pyproject.toml.tier4.template +162 -0
  241. solokit/templates/ml_ai_fastapi/tier-4-production/requirements-prod.txt +25 -0
  242. solokit/templates/ml_ai_fastapi/tier-4-production/src/api/routes/metrics.py +19 -0
  243. solokit/templates/ml_ai_fastapi/tier-4-production/src/core/logging.py +74 -0
  244. solokit/templates/ml_ai_fastapi/tier-4-production/src/core/monitoring.py +68 -0
  245. solokit/templates/ml_ai_fastapi/tier-4-production/src/core/sentry.py +66 -0
  246. solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/__init__.py +3 -0
  247. solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/logging.py +79 -0
  248. solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/tracing.py +60 -0
  249. solokit/templates/refactor_spec.md +287 -0
  250. solokit/templates/saas_t3/base/.gitignore +36 -0
  251. solokit/templates/saas_t3/base/app/api/trpc/[trpc]/route.ts +33 -0
  252. solokit/templates/saas_t3/base/app/globals.css +27 -0
  253. solokit/templates/saas_t3/base/app/layout.tsx +23 -0
  254. solokit/templates/saas_t3/base/app/page.tsx +31 -0
  255. solokit/templates/saas_t3/base/lib/api.tsx +77 -0
  256. solokit/templates/saas_t3/base/lib/utils.ts +13 -0
  257. solokit/templates/saas_t3/base/next.config.ts +7 -0
  258. solokit/templates/saas_t3/base/package.json.template +38 -0
  259. solokit/templates/saas_t3/base/postcss.config.mjs +8 -0
  260. solokit/templates/saas_t3/base/prisma/schema.prisma +20 -0
  261. solokit/templates/saas_t3/base/server/api/root.ts +19 -0
  262. solokit/templates/saas_t3/base/server/api/routers/example.ts +28 -0
  263. solokit/templates/saas_t3/base/server/api/trpc.ts +52 -0
  264. solokit/templates/saas_t3/base/server/db.ts +17 -0
  265. solokit/templates/saas_t3/base/tailwind.config.ts +19 -0
  266. solokit/templates/saas_t3/base/tsconfig.json +27 -0
  267. solokit/templates/saas_t3/docker/Dockerfile +60 -0
  268. solokit/templates/saas_t3/docker/docker-compose.prod.yml +59 -0
  269. solokit/templates/saas_t3/docker/docker-compose.yml +49 -0
  270. solokit/templates/saas_t3/tier-1-essential/.eslintrc.json +7 -0
  271. solokit/templates/saas_t3/tier-1-essential/jest.config.ts +17 -0
  272. solokit/templates/saas_t3/tier-1-essential/jest.setup.ts +1 -0
  273. solokit/templates/saas_t3/tier-1-essential/package.json.tier1.template +54 -0
  274. solokit/templates/saas_t3/tier-1-essential/tests/setup.ts +22 -0
  275. solokit/templates/saas_t3/tier-1-essential/tests/unit/example.test.tsx +24 -0
  276. solokit/templates/saas_t3/tier-2-standard/package.json.tier2.template +58 -0
  277. solokit/templates/saas_t3/tier-3-comprehensive/eslint.config.mjs +39 -0
  278. solokit/templates/saas_t3/tier-3-comprehensive/package.json.tier3.template +74 -0
  279. solokit/templates/saas_t3/tier-3-comprehensive/playwright.config.ts +66 -0
  280. solokit/templates/saas_t3/tier-3-comprehensive/stryker.conf.json +34 -0
  281. solokit/templates/saas_t3/tier-3-comprehensive/tests/e2e/home.spec.ts +41 -0
  282. solokit/templates/saas_t3/tier-3-comprehensive/tests/integration/api.test.ts +44 -0
  283. solokit/templates/saas_t3/tier-3-comprehensive/type-coverage.json +12 -0
  284. solokit/templates/saas_t3/tier-4-production/instrumentation.ts +9 -0
  285. solokit/templates/saas_t3/tier-4-production/k6/load-test.js +51 -0
  286. solokit/templates/saas_t3/tier-4-production/next.config.ts +46 -0
  287. solokit/templates/saas_t3/tier-4-production/package.json.tier4.template +83 -0
  288. solokit/templates/saas_t3/tier-4-production/sentry.client.config.ts +26 -0
  289. solokit/templates/saas_t3/tier-4-production/sentry.edge.config.ts +11 -0
  290. solokit/templates/saas_t3/tier-4-production/sentry.server.config.ts +11 -0
  291. solokit/templates/saas_t3/tier-4-production/vercel.json +37 -0
  292. solokit/templates/security_spec.md +287 -0
  293. solokit/templates/stack-versions.yaml +617 -0
  294. solokit/templates/status_update.json +6 -0
  295. solokit/templates/template-registry.json +257 -0
  296. solokit/templates/work_items.json +11 -0
  297. solokit/testing/__init__.py +1 -0
  298. solokit/testing/integration_runner.py +550 -0
  299. solokit/testing/performance.py +637 -0
  300. solokit/visualization/__init__.py +1 -0
  301. solokit/visualization/dependency_graph.py +788 -0
  302. solokit/work_items/__init__.py +1 -0
  303. solokit/work_items/creator.py +217 -0
  304. solokit/work_items/delete.py +264 -0
  305. solokit/work_items/get_dependencies.py +185 -0
  306. solokit/work_items/get_dependents.py +113 -0
  307. solokit/work_items/get_metadata.py +121 -0
  308. solokit/work_items/get_next_recommendations.py +133 -0
  309. solokit/work_items/manager.py +235 -0
  310. solokit/work_items/milestones.py +137 -0
  311. solokit/work_items/query.py +376 -0
  312. solokit/work_items/repository.py +267 -0
  313. solokit/work_items/scheduler.py +184 -0
  314. solokit/work_items/spec_parser.py +838 -0
  315. solokit/work_items/spec_validator.py +493 -0
  316. solokit/work_items/updater.py +157 -0
  317. solokit/work_items/validator.py +205 -0
  318. solokit-0.1.1.dist-info/METADATA +640 -0
  319. solokit-0.1.1.dist-info/RECORD +323 -0
  320. solokit-0.1.1.dist-info/WHEEL +5 -0
  321. solokit-0.1.1.dist-info/entry_points.txt +2 -0
  322. solokit-0.1.1.dist-info/licenses/LICENSE +21 -0
  323. solokit-0.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,424 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ API contract validation for integration tests.
4
+
5
+ Supports:
6
+ - OpenAPI/Swagger specification validation
7
+ - Breaking change detection
8
+ - Contract testing
9
+ - Version compatibility checking
10
+ """
11
+
12
+ import json
13
+ import logging
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ import yaml
18
+
19
+ from solokit.core.error_handlers import convert_file_errors, log_errors
20
+ from solokit.core.exceptions import (
21
+ BreakingChangeError,
22
+ FileOperationError,
23
+ InvalidOpenAPISpecError,
24
+ SchemaValidationError,
25
+ WorkItemNotFoundError,
26
+ )
27
+ from solokit.core.exceptions import (
28
+ FileNotFoundError as SolokitFileNotFoundError,
29
+ )
30
+ from solokit.core.file_ops import load_json
31
+ from solokit.core.output import get_output
32
+
33
+ output = get_output()
34
+ logger = logging.getLogger(__name__)
35
+
36
+
37
+ class APIContractValidator:
38
+ """Validate API contracts for integration tests."""
39
+
40
+ def __init__(self, work_item: dict):
41
+ """
42
+ Initialize API contract validator.
43
+
44
+ Args:
45
+ work_item: Integration test work item with contract specifications
46
+ """
47
+ self.work_item = work_item
48
+ self.contracts = work_item.get("api_contracts", [])
49
+ self.results: dict[str, Any] = {
50
+ "contracts_validated": 0,
51
+ "breaking_changes": [],
52
+ "warnings": [],
53
+ "passed": False,
54
+ }
55
+
56
+ @log_errors()
57
+ def validate_contracts(self) -> tuple[bool, dict[str, Any]]:
58
+ """
59
+ Validate all API contracts.
60
+
61
+ Returns:
62
+ (passed: bool, results: dict)
63
+
64
+ Raises:
65
+ SchemaValidationError: If contract file validation fails
66
+ InvalidOpenAPISpecError: If OpenAPI/Swagger spec is invalid
67
+ BreakingChangeError: If breaking changes detected and not allowed
68
+ FileNotFoundError: If contract file not found
69
+ """
70
+ logger.info(f"Validating {len(self.contracts)} API contracts...")
71
+
72
+ all_passed = True
73
+
74
+ for contract in self.contracts:
75
+ contract_file = contract.get("contract_file")
76
+ if not contract_file:
77
+ continue
78
+
79
+ # Validate contract file exists and is valid
80
+ try:
81
+ self._validate_contract_file(contract_file)
82
+ except (
83
+ SchemaValidationError,
84
+ InvalidOpenAPISpecError,
85
+ SolokitFileNotFoundError,
86
+ ) as e:
87
+ logger.error(f"Contract validation failed for {contract_file}: {e.message}")
88
+ all_passed = False
89
+ continue
90
+
91
+ # Check for breaking changes if previous version exists
92
+ previous_version = contract.get("previous_version")
93
+ if previous_version:
94
+ try:
95
+ breaking_changes = self._detect_breaking_changes(
96
+ contract_file, previous_version
97
+ )
98
+ if breaking_changes:
99
+ self.results["breaking_changes"].extend(breaking_changes)
100
+ if not contract.get("allow_breaking_changes", False):
101
+ all_passed = False
102
+ except BreakingChangeError as e:
103
+ logger.error(f"Breaking change detection failed: {e.message}")
104
+ all_passed = False
105
+ continue
106
+
107
+ self.results["contracts_validated"] += 1
108
+
109
+ self.results["passed"] = all_passed
110
+ return all_passed, self.results
111
+
112
+ @log_errors()
113
+ def _validate_contract_file(self, contract_file: str) -> None:
114
+ """
115
+ Validate OpenAPI/Swagger contract file.
116
+
117
+ Args:
118
+ contract_file: Path to contract file
119
+
120
+ Raises:
121
+ FileNotFoundError: If contract file not found
122
+ SchemaValidationError: If contract parsing fails
123
+ InvalidOpenAPISpecError: If not a valid OpenAPI/Swagger spec
124
+ """
125
+ contract_path = Path(contract_file)
126
+
127
+ if not contract_path.exists():
128
+ raise SolokitFileNotFoundError(file_path=contract_file, file_type="API contract")
129
+
130
+ # Load contract
131
+ try:
132
+ if contract_file.endswith(".yaml") or contract_file.endswith(".yml"):
133
+ with open(contract_path) as f:
134
+ spec = yaml.safe_load(f)
135
+ else:
136
+ with open(contract_path) as f:
137
+ spec = json.load(f)
138
+ except (json.JSONDecodeError, yaml.YAMLError) as e:
139
+ raise SchemaValidationError(
140
+ contract_file=contract_file,
141
+ details=f"Failed to parse contract file: {e}",
142
+ ) from e
143
+ except OSError as e:
144
+ raise FileOperationError(
145
+ operation="read", file_path=contract_file, details=str(e), cause=e
146
+ ) from e
147
+
148
+ # Validate OpenAPI structure
149
+ if "openapi" not in spec and "swagger" not in spec:
150
+ raise InvalidOpenAPISpecError(
151
+ contract_file=contract_file,
152
+ details="Missing 'openapi' or 'swagger' field",
153
+ )
154
+
155
+ # Validate required fields
156
+ if "paths" not in spec:
157
+ raise SchemaValidationError(
158
+ contract_file=contract_file, details="Missing 'paths' field"
159
+ )
160
+
161
+ logger.info(f"Contract valid: {contract_file}")
162
+
163
+ @log_errors()
164
+ def _detect_breaking_changes(self, current_file: str, previous_file: str) -> list[dict]:
165
+ """
166
+ Detect breaking changes between contract versions.
167
+
168
+ Args:
169
+ current_file: Path to current contract
170
+ previous_file: Path to previous contract version
171
+
172
+ Returns:
173
+ List of breaking changes
174
+
175
+ Raises:
176
+ FileNotFoundError: If contract file not found
177
+ SchemaValidationError: If contract parsing fails
178
+ BreakingChangeError: If breaking changes detected and not allowed
179
+ """
180
+ breaking_changes = []
181
+
182
+ # Load both versions
183
+ try:
184
+ current_spec = self._load_spec(current_file)
185
+ previous_spec = self._load_spec(previous_file)
186
+ except (SolokitFileNotFoundError, SchemaValidationError, FileOperationError) as e:
187
+ logger.error(f"Failed to load contract specs: {e.message}")
188
+ return [{"type": "load_error", "message": str(e)}]
189
+
190
+ # Check for removed endpoints
191
+ previous_paths = set(previous_spec.get("paths", {}).keys())
192
+ current_paths = set(current_spec.get("paths", {}).keys())
193
+
194
+ removed_paths = previous_paths - current_paths
195
+ for path in removed_paths:
196
+ breaking_changes.append(
197
+ {
198
+ "type": "removed_endpoint",
199
+ "path": path,
200
+ "severity": "high",
201
+ "message": f"Endpoint removed: {path}",
202
+ }
203
+ )
204
+
205
+ # Check for modified endpoints
206
+ for path in previous_paths & current_paths:
207
+ endpoint_changes = self._check_endpoint_changes(
208
+ path, previous_spec["paths"][path], current_spec["paths"][path]
209
+ )
210
+ breaking_changes.extend(endpoint_changes)
211
+
212
+ if breaking_changes:
213
+ logger.warning(f"{len(breaking_changes)} breaking changes detected")
214
+ for change in breaking_changes:
215
+ logger.warning(f" - {change['type']}: {change['message']}")
216
+ else:
217
+ logger.info("No breaking changes detected")
218
+
219
+ return breaking_changes
220
+
221
+ @convert_file_errors
222
+ def _load_spec(self, file_path: str) -> dict[str, Any]:
223
+ """
224
+ Load OpenAPI/Swagger spec from file.
225
+
226
+ Args:
227
+ file_path: Path to spec file
228
+
229
+ Returns:
230
+ Parsed spec dictionary
231
+
232
+ Raises:
233
+ FileNotFoundError: If file not found
234
+ SchemaValidationError: If parsing fails
235
+ """
236
+ path = Path(file_path)
237
+
238
+ if not path.exists():
239
+ raise SolokitFileNotFoundError(file_path=file_path, file_type="API contract")
240
+
241
+ try:
242
+ if file_path.endswith(".yaml") or file_path.endswith(".yml"):
243
+ with open(path) as f:
244
+ return yaml.safe_load(f) # type: ignore[no-any-return]
245
+ else:
246
+ with open(path) as f:
247
+ return json.load(f) # type: ignore[no-any-return]
248
+ except (json.JSONDecodeError, yaml.YAMLError) as e:
249
+ raise SchemaValidationError(
250
+ contract_file=file_path, details=f"Failed to parse contract file: {e}"
251
+ ) from e
252
+
253
+ def _check_endpoint_changes(self, path: str, previous: dict, current: dict) -> list[dict]:
254
+ """
255
+ Check for breaking changes in a specific endpoint.
256
+
257
+ Args:
258
+ path: Endpoint path
259
+ previous: Previous endpoint definition
260
+ current: Current endpoint definition
261
+
262
+ Returns:
263
+ List of breaking changes
264
+ """
265
+ changes = []
266
+
267
+ # Check HTTP methods
268
+ previous_methods = set(previous.keys())
269
+ current_methods = set(current.keys())
270
+
271
+ removed_methods = previous_methods - current_methods
272
+ for method in removed_methods:
273
+ changes.append(
274
+ {
275
+ "type": "removed_method",
276
+ "path": path,
277
+ "method": method.upper(),
278
+ "severity": "high",
279
+ "message": f"HTTP method removed: {method.upper()} {path}",
280
+ }
281
+ )
282
+
283
+ # Check parameters for common methods
284
+ for method in previous_methods & current_methods:
285
+ if method in ["get", "post", "put", "patch", "delete"]:
286
+ param_changes = self._check_parameter_changes(
287
+ path, method, previous.get(method, {}), current.get(method, {})
288
+ )
289
+ changes.extend(param_changes)
290
+
291
+ return changes
292
+
293
+ def _check_parameter_changes(
294
+ self, path: str, method: str, previous: dict, current: dict
295
+ ) -> list[dict]:
296
+ """
297
+ Check for breaking changes in endpoint parameters.
298
+
299
+ Args:
300
+ path: Endpoint path
301
+ method: HTTP method
302
+ previous: Previous endpoint definition
303
+ current: Current endpoint definition
304
+
305
+ Returns:
306
+ List of breaking changes
307
+ """
308
+ changes = []
309
+
310
+ previous_params = {p["name"]: p for p in previous.get("parameters", [])}
311
+ current_params = {p["name"]: p for p in current.get("parameters", [])}
312
+
313
+ # Check for removed required parameters
314
+ for param_name, param in previous_params.items():
315
+ if param.get("required", False):
316
+ if param_name not in current_params:
317
+ changes.append(
318
+ {
319
+ "type": "removed_required_parameter",
320
+ "path": path,
321
+ "method": method.upper(),
322
+ "parameter": param_name,
323
+ "severity": "high",
324
+ "message": f"Required parameter removed: {param_name} from {method.upper()} {path}",
325
+ }
326
+ )
327
+
328
+ # Check for newly required parameters (breaking change)
329
+ for param_name, param in current_params.items():
330
+ if param.get("required", False):
331
+ if param_name not in previous_params:
332
+ changes.append(
333
+ {
334
+ "type": "added_required_parameter",
335
+ "path": path,
336
+ "method": method.upper(),
337
+ "parameter": param_name,
338
+ "severity": "high",
339
+ "message": f"New required parameter: {param_name} in {method.upper()} {path}",
340
+ }
341
+ )
342
+ elif not previous_params[param_name].get("required", False):
343
+ changes.append(
344
+ {
345
+ "type": "parameter_now_required",
346
+ "path": path,
347
+ "method": method.upper(),
348
+ "parameter": param_name,
349
+ "severity": "high",
350
+ "message": f"Parameter became required: {param_name} in {method.upper()} {path}",
351
+ }
352
+ )
353
+
354
+ return changes
355
+
356
+ def generate_report(self) -> str:
357
+ """
358
+ Generate API contract validation report.
359
+
360
+ Returns:
361
+ Formatted report string
362
+ """
363
+ report = f"""
364
+ API Contract Validation Report
365
+ {"=" * 80}
366
+
367
+ Contracts Validated: {self.results["contracts_validated"]}
368
+
369
+ Breaking Changes: {len(self.results["breaking_changes"])}
370
+ """
371
+
372
+ if self.results["breaking_changes"]:
373
+ report += "\nBreaking Changes Detected:\n"
374
+ for change in self.results["breaking_changes"]:
375
+ report += f" • [{change['severity'].upper()}] {change['message']}\n"
376
+
377
+ if self.results["warnings"]:
378
+ report += "\nWarnings:\n"
379
+ for warning in self.results["warnings"]:
380
+ report += f" • {warning}\n"
381
+
382
+ report += f"\nStatus: {'PASSED' if self.results['passed'] else 'FAILED'}\n"
383
+
384
+ return report
385
+
386
+
387
+ @log_errors()
388
+ def main() -> None:
389
+ """
390
+ CLI entry point.
391
+
392
+ Raises:
393
+ WorkItemNotFoundError: If work item not found
394
+ Various validation errors from APIContractValidator
395
+ """
396
+ import sys
397
+
398
+ if len(sys.argv) < 2:
399
+ logger.error("Usage: python api_contract_validator.py <work_item_id>")
400
+ sys.exit(1)
401
+
402
+ work_item_id = sys.argv[1]
403
+
404
+ # Load work item
405
+ work_items_file = Path(".session/tracking/work_items.json")
406
+ data = load_json(work_items_file)
407
+ work_item = data["work_items"].get(work_item_id)
408
+
409
+ if not work_item:
410
+ raise WorkItemNotFoundError(work_item_id)
411
+
412
+ # Validate contracts
413
+ validator = APIContractValidator(work_item)
414
+ try:
415
+ passed, results = validator.validate_contracts()
416
+ output.info(validator.generate_report())
417
+ sys.exit(0 if passed else 1)
418
+ except Exception as e:
419
+ logger.error(f"Validation failed: {e}")
420
+ raise
421
+
422
+
423
+ if __name__ == "__main__":
424
+ main()
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env python3
2
+ """Quality checker modules."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from solokit.quality.checkers.base import CheckResult, QualityChecker
7
+ from solokit.quality.checkers.custom import CustomValidationChecker
8
+ from solokit.quality.checkers.documentation import DocumentationChecker
9
+ from solokit.quality.checkers.formatting import FormattingChecker
10
+ from solokit.quality.checkers.linting import LintingChecker
11
+ from solokit.quality.checkers.security import SecurityChecker
12
+ from solokit.quality.checkers.spec_completeness import SpecCompletenessChecker
13
+ from solokit.quality.checkers.tests import ExecutionChecker
14
+
15
+ __all__ = [
16
+ "CheckResult",
17
+ "QualityChecker",
18
+ "CustomValidationChecker",
19
+ "DocumentationChecker",
20
+ "FormattingChecker",
21
+ "LintingChecker",
22
+ "SecurityChecker",
23
+ "SpecCompletenessChecker",
24
+ "ExecutionChecker",
25
+ ]
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Base classes for quality checkers.
4
+
5
+ Defines the abstract interface for all quality checkers and the result structure.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from abc import ABC, abstractmethod
11
+ from dataclasses import dataclass, field
12
+ from pathlib import Path
13
+ from typing import Any, Union
14
+
15
+
16
+ @dataclass
17
+ class CheckResult:
18
+ """Result from a quality check execution.
19
+
20
+ Attributes:
21
+ checker_name: Name of the checker that produced this result
22
+ passed: Whether the check passed overall
23
+ status: Status string (e.g., "passed", "failed", "skipped")
24
+ errors: List of error messages or error dictionaries
25
+ warnings: List of warning messages or warning dictionaries
26
+ info: Additional information about the check execution
27
+ execution_time: Time taken to run the check in seconds
28
+ """
29
+
30
+ checker_name: str
31
+ passed: bool
32
+ status: str
33
+ errors: list[Union[dict[str, Any], str]] = field(default_factory=list) # noqa: UP007
34
+ warnings: list[Union[dict[str, Any], str]] = field(default_factory=list) # noqa: UP007
35
+ info: dict[str, Any] = field(default_factory=dict)
36
+ execution_time: float = 0.0
37
+
38
+ @property
39
+ def details(self) -> dict[str, Any]:
40
+ """Return check result as details dict for backward compatibility.
41
+
42
+ Returns:
43
+ Dict containing status, passed, and info merged together
44
+ """
45
+ result_dict: dict[str, Any] = {"status": self.status, "passed": self.passed}
46
+ result_dict.update(self.info)
47
+ if self.errors:
48
+ result_dict["errors"] = self.errors
49
+ if self.warnings:
50
+ result_dict["warnings"] = self.warnings
51
+ return result_dict
52
+
53
+
54
+ class QualityChecker(ABC):
55
+ """Abstract base class for quality checkers.
56
+
57
+ All quality checkers must inherit from this class and implement
58
+ the abstract methods. This provides a consistent interface for
59
+ the orchestrator to execute different types of quality checks.
60
+ """
61
+
62
+ def __init__(self, config: dict[str, Any], project_root: Path | None = None):
63
+ """Initialize the quality checker.
64
+
65
+ Args:
66
+ config: Configuration dictionary for this checker
67
+ project_root: Root directory of the project (defaults to current directory)
68
+ """
69
+ self.config = config
70
+ self.project_root = project_root or Path.cwd()
71
+
72
+ @abstractmethod
73
+ def name(self) -> str:
74
+ """Return the name of this checker.
75
+
76
+ Returns:
77
+ A unique identifier for this checker (e.g., "bandit", "tests")
78
+ """
79
+ pass
80
+
81
+ @abstractmethod
82
+ def is_enabled(self) -> bool:
83
+ """Check if this checker is enabled in the configuration.
84
+
85
+ Returns:
86
+ True if the checker should run, False to skip
87
+ """
88
+ pass
89
+
90
+ @abstractmethod
91
+ def run(self) -> CheckResult:
92
+ """Execute the quality check.
93
+
94
+ Returns:
95
+ CheckResult containing the outcome of the check
96
+ """
97
+ pass
98
+
99
+ def _create_skipped_result(self, reason: str = "disabled") -> CheckResult:
100
+ """Helper to create a skipped result.
101
+
102
+ Args:
103
+ reason: Reason why the check was skipped
104
+
105
+ Returns:
106
+ CheckResult marked as skipped
107
+ """
108
+ return CheckResult(
109
+ checker_name=self.name(),
110
+ passed=True,
111
+ status="skipped",
112
+ info={"reason": reason},
113
+ execution_time=0.0,
114
+ )