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,550 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Integration test execution framework.
4
+
5
+ Supports:
6
+ - Multi-service orchestration
7
+ - Test environment setup/teardown
8
+ - Test data management
9
+ - Parallel test execution
10
+ - Result aggregation
11
+
12
+ Updated in Phase 5.7.3 to use spec_parser for reading test specifications.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import logging
19
+ import sys
20
+ import time
21
+ from datetime import datetime
22
+ from pathlib import Path
23
+ from typing import Any
24
+
25
+ from solokit.core.command_runner import CommandRunner
26
+ from solokit.core.constants import (
27
+ CLEANUP_TIMEOUT,
28
+ DOCKER_COMMAND_TIMEOUT,
29
+ DOCKER_COMPOSE_TIMEOUT,
30
+ FIXTURE_SETUP_TIMEOUT,
31
+ INTEGRATION_TEST_TIMEOUT,
32
+ )
33
+ from solokit.core.exceptions import (
34
+ EnvironmentSetupError,
35
+ FileNotFoundError,
36
+ IntegrationExecutionError,
37
+ TimeoutError,
38
+ ValidationError,
39
+ )
40
+ from solokit.core.output import get_output
41
+ from solokit.work_items import spec_parser
42
+
43
+ output = get_output()
44
+ logger = logging.getLogger(__name__)
45
+
46
+
47
+ class IntegrationTestRunner:
48
+ """Execute integration tests with multi-service orchestration."""
49
+
50
+ def __init__(self, work_item: dict):
51
+ """
52
+ Initialize integration test runner.
53
+
54
+ Args:
55
+ work_item: Integration test work item (must have 'id' field)
56
+
57
+ Raises:
58
+ ValidationError: If work item missing 'id' field or spec parsing fails
59
+ FileNotFoundError: If spec file not found
60
+ """
61
+ self.work_item = work_item
62
+ work_id = work_item.get("id")
63
+
64
+ if not work_id:
65
+ raise ValidationError(
66
+ message="Work item must have 'id' field",
67
+ context={"work_item": work_item},
68
+ remediation="Ensure work item dict contains 'id' key",
69
+ )
70
+
71
+ # Parse spec file to get test scenarios and environment requirements
72
+ # Pass full work_item dict to support custom spec filenames
73
+ try:
74
+ parsed_spec = spec_parser.parse_spec_file(work_item)
75
+ except FileNotFoundError:
76
+ # Re-raise as-is (already correct exception type)
77
+ raise
78
+ except Exception as e:
79
+ raise ValidationError(
80
+ message=f"Failed to parse spec file for {work_id}",
81
+ context={"work_item_id": work_id, "error": str(e)},
82
+ remediation=f"Check .session/specs/{work_id}.md for valid format",
83
+ cause=e,
84
+ )
85
+
86
+ # Extract test scenarios from parsed spec
87
+ self.test_scenarios = parsed_spec.get("test_scenarios", [])
88
+
89
+ # Parse environment requirements from spec content
90
+ # The environment_requirements section contains service names and configuration
91
+ env_req_text = parsed_spec.get("environment_requirements", "")
92
+ self.env_requirements = self._parse_environment_requirements(env_req_text)
93
+
94
+ self.results: dict[str, Any] = {
95
+ "scenarios": [],
96
+ "start_time": None,
97
+ "end_time": None,
98
+ "total_duration": 0.0,
99
+ "passed": 0,
100
+ "failed": 0,
101
+ "skipped": 0,
102
+ }
103
+
104
+ # Initialize CommandRunner
105
+ self.runner = CommandRunner(default_timeout=INTEGRATION_TEST_TIMEOUT)
106
+
107
+ def _parse_environment_requirements(self, env_text: str) -> dict:
108
+ """
109
+ Parse environment requirements from spec text.
110
+
111
+ Args:
112
+ env_text: Environment requirements section content
113
+
114
+ Returns:
115
+ Dict with 'services_required' and 'compose_file' keys
116
+ """
117
+ if not env_text:
118
+ return {
119
+ "services_required": [],
120
+ "compose_file": "docker-compose.integration.yml",
121
+ }
122
+
123
+ # Extract service names (look for lines with service names)
124
+ services = []
125
+ compose_file = "docker-compose.integration.yml"
126
+
127
+ for line in env_text.split("\n"):
128
+ line = line.strip()
129
+ # Look for service names (simple heuristic: lines with common service names)
130
+ if any(
131
+ s in line.lower()
132
+ for s in [
133
+ "postgresql",
134
+ "postgres",
135
+ "redis",
136
+ "mongodb",
137
+ "mysql",
138
+ "nginx",
139
+ "kafka",
140
+ ]
141
+ ):
142
+ # Extract service name and version if present
143
+ parts = line.split()
144
+ if parts:
145
+ services.append(parts[0].strip("-*•"))
146
+ # Look for compose file reference
147
+ if "docker-compose" in line.lower() or "compose" in line.lower():
148
+ # Try to extract filename
149
+ words = line.split()
150
+ for word in words:
151
+ if "docker-compose" in word or word.endswith(".yml") or word.endswith(".yaml"):
152
+ compose_file = word.strip("`\"':")
153
+
154
+ return {"services_required": services, "compose_file": compose_file}
155
+
156
+ def setup_environment(self) -> None:
157
+ """
158
+ Set up integration test environment.
159
+
160
+ Raises:
161
+ FileNotFoundError: If Docker Compose file not found
162
+ EnvironmentSetupError: If service startup or health check fails
163
+ TimeoutError: If service startup times out
164
+ """
165
+ logger.info("Setting up integration test environment...")
166
+
167
+ # Check if Docker Compose file exists
168
+ compose_file = self.env_requirements.get("compose_file", "docker-compose.integration.yml")
169
+ if not Path(compose_file).exists():
170
+ raise FileNotFoundError(file_path=compose_file, file_type="Docker Compose")
171
+
172
+ # Start services
173
+ result = self.runner.run(
174
+ ["docker-compose", "-f", compose_file, "up", "-d"],
175
+ timeout=DOCKER_COMPOSE_TIMEOUT,
176
+ )
177
+
178
+ if not result.success:
179
+ if result.timed_out:
180
+ raise TimeoutError(
181
+ operation="docker-compose startup",
182
+ timeout_seconds=180,
183
+ context={"compose_file": compose_file},
184
+ )
185
+ raise EnvironmentSetupError(
186
+ component="docker-compose",
187
+ details=result.stderr or "Failed to start services",
188
+ context={"compose_file": compose_file, "stderr": result.stderr},
189
+ )
190
+
191
+ logger.info(f"✓ Services started from {compose_file}")
192
+
193
+ # Wait for services to be healthy
194
+ services = self.env_requirements.get("services_required", [])
195
+ for service in services:
196
+ self._wait_for_service(service)
197
+
198
+ logger.info(f"✓ All {len(services)} services are healthy")
199
+
200
+ # Load test data
201
+ self._load_test_data()
202
+
203
+ logger.info("✓ Integration test environment ready")
204
+
205
+ def _wait_for_service(self, service: str, timeout: int = 60) -> None:
206
+ """
207
+ Wait for service to be healthy.
208
+
209
+ Args:
210
+ service: Service name
211
+ timeout: Maximum wait time in seconds
212
+
213
+ Raises:
214
+ TimeoutError: If service doesn't become healthy within timeout
215
+ EnvironmentSetupError: If service health check fails
216
+ """
217
+ start_time = time.time()
218
+
219
+ while time.time() - start_time < timeout:
220
+ result = self.runner.run(
221
+ ["docker-compose", "ps", "-q", service], timeout=DOCKER_COMMAND_TIMEOUT
222
+ )
223
+
224
+ if result.success and result.stdout.strip():
225
+ # Check health status
226
+ health_result = self.runner.run(
227
+ [
228
+ "docker",
229
+ "inspect",
230
+ "--format='{{.State.Health.Status}}'",
231
+ result.stdout.strip(),
232
+ ],
233
+ timeout=DOCKER_COMMAND_TIMEOUT,
234
+ )
235
+
236
+ if health_result.success and "healthy" in health_result.stdout:
237
+ return
238
+
239
+ time.sleep(2)
240
+
241
+ # Timeout - service didn't become healthy
242
+ raise TimeoutError(
243
+ operation=f"waiting for service '{service}' to become healthy",
244
+ timeout_seconds=timeout,
245
+ context={"service": service},
246
+ )
247
+
248
+ def _load_test_data(self) -> None:
249
+ """
250
+ Load test data fixtures.
251
+
252
+ Raises:
253
+ FileNotFoundError: If fixture file not found
254
+ EnvironmentSetupError: If fixture loading fails
255
+ """
256
+ fixtures = self.env_requirements.get("test_data_fixtures", [])
257
+
258
+ for fixture in fixtures:
259
+ fixture_path = Path(fixture)
260
+ if not fixture_path.exists():
261
+ logger.warning(f"Fixture not found: {fixture}")
262
+ continue
263
+
264
+ # Execute fixture loading script
265
+ result = self.runner.run(
266
+ ["python", str(fixture_path)], timeout=FIXTURE_SETUP_TIMEOUT, check=True
267
+ )
268
+ if result.success:
269
+ logger.info(f"✓ Loaded fixture: {fixture}")
270
+ else:
271
+ raise EnvironmentSetupError(
272
+ component="test data fixture",
273
+ details=f"Failed to load fixture: {fixture}",
274
+ context={"fixture": fixture, "stderr": result.stderr},
275
+ )
276
+
277
+ def run_tests(self, language: str | None = None) -> dict[str, Any]:
278
+ """
279
+ Execute all integration test scenarios.
280
+
281
+ Args:
282
+ language: Project language (python, javascript, typescript)
283
+
284
+ Returns:
285
+ dict: Test results with passed/failed counts and duration
286
+
287
+ Raises:
288
+ ValidationError: If unsupported language
289
+ IntegrationExecutionError: If tests fail to execute
290
+ """
291
+ self.results["start_time"] = datetime.now().isoformat()
292
+
293
+ logger.info(f"\nRunning {len(self.test_scenarios)} integration test scenarios...\n")
294
+
295
+ # Detect language if not provided
296
+ if language is None:
297
+ language = self._detect_language()
298
+
299
+ # Run scenarios based on language
300
+ if language == "python":
301
+ self._run_pytest()
302
+ elif language in ["javascript", "typescript"]:
303
+ self._run_jest()
304
+ else:
305
+ raise ValidationError(
306
+ message=f"Unsupported language: {language}",
307
+ context={"language": language},
308
+ remediation="Supported languages: python, javascript, typescript",
309
+ )
310
+
311
+ self.results["end_time"] = datetime.now().isoformat()
312
+
313
+ # Calculate duration
314
+ start = datetime.fromisoformat(self.results["start_time"])
315
+ end = datetime.fromisoformat(self.results["end_time"])
316
+ self.results["total_duration"] = (end - start).total_seconds()
317
+
318
+ return self.results
319
+
320
+ def _run_pytest(self) -> None:
321
+ """
322
+ Run integration tests using pytest.
323
+
324
+ Raises:
325
+ TimeoutError: If tests timeout
326
+ IntegrationExecutionError: If tests fail
327
+ """
328
+ test_dir = self.work_item.get("test_directory", "tests/integration")
329
+
330
+ result = self.runner.run(
331
+ [
332
+ "pytest",
333
+ test_dir,
334
+ "-v",
335
+ "--tb=short",
336
+ "--json-report",
337
+ "--json-report-file=integration-test-results.json",
338
+ ],
339
+ timeout=INTEGRATION_TEST_TIMEOUT,
340
+ )
341
+
342
+ # Parse results
343
+ results_file = Path("integration-test-results.json")
344
+ if results_file.exists():
345
+ with open(results_file) as f:
346
+ test_data = json.load(f)
347
+
348
+ self.results["passed"] = test_data.get("summary", {}).get("passed", 0)
349
+ self.results["failed"] = test_data.get("summary", {}).get("failed", 0)
350
+ self.results["skipped"] = test_data.get("summary", {}).get("skipped", 0)
351
+ self.results["tests"] = test_data.get("tests", [])
352
+
353
+ if result.timed_out:
354
+ raise TimeoutError(
355
+ operation="pytest execution",
356
+ timeout_seconds=600,
357
+ context={"test_directory": test_dir},
358
+ )
359
+
360
+ if not result.success:
361
+ raise IntegrationExecutionError(
362
+ test_framework="pytest",
363
+ details=f"{self.results.get('failed', 0)} tests failed",
364
+ context={
365
+ "test_directory": test_dir,
366
+ "passed": self.results.get("passed", 0),
367
+ "failed": self.results.get("failed", 0),
368
+ "skipped": self.results.get("skipped", 0),
369
+ "stderr": result.stderr,
370
+ },
371
+ )
372
+
373
+ def _run_jest(self) -> None:
374
+ """
375
+ Run integration tests using Jest.
376
+
377
+ Raises:
378
+ TimeoutError: If tests timeout
379
+ IntegrationExecutionError: If tests fail
380
+ """
381
+ result = self.runner.run(
382
+ [
383
+ "npm",
384
+ "test",
385
+ "--",
386
+ "--testPathPattern=integration",
387
+ "--json",
388
+ "--outputFile=integration-test-results.json",
389
+ ],
390
+ timeout=INTEGRATION_TEST_TIMEOUT,
391
+ )
392
+
393
+ # Parse results
394
+ results_file = Path("integration-test-results.json")
395
+ if results_file.exists():
396
+ with open(results_file) as f:
397
+ test_data = json.load(f)
398
+
399
+ self.results["passed"] = test_data.get("numPassedTests", 0)
400
+ self.results["failed"] = test_data.get("numFailedTests", 0)
401
+ self.results["skipped"] = test_data.get("numPendingTests", 0)
402
+
403
+ if result.timed_out:
404
+ raise TimeoutError(
405
+ operation="jest execution",
406
+ timeout_seconds=600,
407
+ context={"test_pattern": "integration"},
408
+ )
409
+
410
+ if not result.success:
411
+ raise IntegrationExecutionError(
412
+ test_framework="jest",
413
+ details=f"{self.results.get('failed', 0)} tests failed",
414
+ context={
415
+ "passed": self.results.get("passed", 0),
416
+ "failed": self.results.get("failed", 0),
417
+ "skipped": self.results.get("skipped", 0),
418
+ "stderr": result.stderr,
419
+ },
420
+ )
421
+
422
+ def _detect_language(self) -> str:
423
+ """Detect project language."""
424
+ if Path("pyproject.toml").exists() or Path("setup.py").exists():
425
+ return "python"
426
+ elif Path("package.json").exists():
427
+ if Path("tsconfig.json").exists():
428
+ return "typescript"
429
+ return "javascript"
430
+ return "python"
431
+
432
+ def teardown_environment(self) -> None:
433
+ """
434
+ Tear down integration test environment.
435
+
436
+ Raises:
437
+ EnvironmentSetupError: If teardown fails
438
+ TimeoutError: If teardown times out
439
+ """
440
+ logger.info("\nTearing down integration test environment...")
441
+
442
+ compose_file = self.env_requirements.get("compose_file", "docker-compose.integration.yml")
443
+
444
+ # Stop and remove services
445
+ result = self.runner.run(
446
+ ["docker-compose", "-f", compose_file, "down", "-v"],
447
+ timeout=CLEANUP_TIMEOUT,
448
+ )
449
+
450
+ if not result.success:
451
+ if result.timed_out:
452
+ raise TimeoutError(
453
+ operation="docker-compose teardown",
454
+ timeout_seconds=60,
455
+ context={"compose_file": compose_file},
456
+ )
457
+ raise EnvironmentSetupError(
458
+ component="docker-compose teardown",
459
+ details=result.stderr or "Failed to tear down services",
460
+ context={"compose_file": compose_file, "stderr": result.stderr},
461
+ )
462
+
463
+ logger.info("✓ Services stopped and removed")
464
+ logger.info("✓ Volumes cleaned up")
465
+
466
+ def generate_report(self) -> str:
467
+ """Generate integration test report."""
468
+ report = f"""
469
+ Integration Test Report
470
+ {"=" * 80}
471
+
472
+ Work Item: {self.work_item.get("id", "N/A")}
473
+ Test Name: {self.work_item.get("title", "N/A")}
474
+
475
+ Duration: {self.results["total_duration"]:.2f} seconds
476
+
477
+ Results:
478
+ ✓ Passed: {self.results["passed"]}
479
+ ✗ Failed: {self.results["failed"]}
480
+ ○ Skipped: {self.results["skipped"]}
481
+
482
+ Status: {"PASSED" if self.results["failed"] == 0 else "FAILED"}
483
+ """
484
+ return report
485
+
486
+
487
+ def main() -> None:
488
+ """
489
+ CLI entry point.
490
+
491
+ Raises:
492
+ ValidationError: If arguments invalid
493
+ WorkItemNotFoundError: If work item not found
494
+ Various exceptions from runner methods
495
+ """
496
+ from solokit.core.exceptions import WorkItemNotFoundError
497
+ from solokit.core.file_ops import load_json
498
+
499
+ if len(sys.argv) < 2:
500
+ raise ValidationError(
501
+ message="Missing required argument: work_item_id",
502
+ context={"usage": "python integration_test_runner.py <work_item_id>"},
503
+ remediation="Provide work item ID as command-line argument",
504
+ )
505
+
506
+ work_item_id = sys.argv[1]
507
+
508
+ # Load work item
509
+ work_items_file = Path(".session/tracking/work_items.json")
510
+ data = load_json(work_items_file)
511
+ work_item = data["work_items"].get(work_item_id)
512
+
513
+ if not work_item:
514
+ raise WorkItemNotFoundError(work_item_id)
515
+
516
+ # Run integration tests
517
+ runner = IntegrationTestRunner(work_item)
518
+
519
+ try:
520
+ # Setup
521
+ runner.setup_environment()
522
+
523
+ # Execute tests
524
+ results = runner.run_tests()
525
+
526
+ # Print report
527
+ output.info(runner.generate_report())
528
+
529
+ # Teardown
530
+ runner.teardown_environment()
531
+
532
+ # Exit with code 1 if tests failed
533
+ if results.get("failed", 0) > 0:
534
+ sys.exit(1)
535
+ else:
536
+ sys.exit(0)
537
+
538
+ except Exception:
539
+ # Attempt teardown on any failure
540
+ try:
541
+ runner.teardown_environment()
542
+ except Exception as teardown_error:
543
+ logger.warning(f"Teardown failed: {teardown_error}")
544
+
545
+ # Re-raise the original exception for proper error handling
546
+ raise
547
+
548
+
549
+ if __name__ == "__main__":
550
+ main()