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,493 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Spec File Validation Module
4
+
5
+ Validates work item specification files for completeness and correctness.
6
+ Ensures specs have all required sections and meet quality standards.
7
+
8
+ Part of Phase 5.7.5: Spec File Validation System
9
+ """
10
+
11
+ import re
12
+ from pathlib import Path
13
+ from typing import Any, Optional
14
+
15
+ from solokit.core.error_handlers import log_errors
16
+ from solokit.core.exceptions import (
17
+ FileNotFoundError,
18
+ FileOperationError,
19
+ SpecValidationError,
20
+ )
21
+ from solokit.core.logging_config import get_logger
22
+ from solokit.core.output import get_output
23
+ from solokit.core.types import WorkItemType
24
+ from solokit.work_items.spec_parser import (
25
+ extract_checklist,
26
+ extract_subsection,
27
+ parse_section,
28
+ strip_html_comments,
29
+ )
30
+
31
+ logger = get_logger(__name__)
32
+ output = get_output()
33
+
34
+
35
+ def get_validation_rules(work_item_type: str) -> dict[str, Any]:
36
+ """
37
+ Get validation rules for a specific work item type.
38
+
39
+ Args:
40
+ work_item_type: Type of work item (feature, bug, refactor, security, integration_test, deployment)
41
+
42
+ Returns:
43
+ Dictionary with required_sections, optional_sections, and special_requirements
44
+ """
45
+ rules = {
46
+ WorkItemType.FEATURE.value: {
47
+ "required_sections": [
48
+ "Overview",
49
+ "Rationale",
50
+ "Acceptance Criteria",
51
+ "Implementation Details",
52
+ "Testing Strategy",
53
+ ],
54
+ "optional_sections": [
55
+ "User Story",
56
+ "Documentation Updates",
57
+ "Dependencies",
58
+ "Estimated Effort",
59
+ ],
60
+ "special_requirements": {"acceptance_criteria_min_items": 3},
61
+ },
62
+ WorkItemType.BUG.value: {
63
+ "required_sections": [
64
+ "Description",
65
+ "Steps to Reproduce",
66
+ "Root Cause Analysis",
67
+ "Fix Approach",
68
+ ],
69
+ "optional_sections": [
70
+ "Expected Behavior",
71
+ "Actual Behavior",
72
+ "Impact",
73
+ "Testing Strategy",
74
+ "Prevention",
75
+ "Dependencies",
76
+ "Estimated Effort",
77
+ ],
78
+ "special_requirements": {},
79
+ },
80
+ WorkItemType.REFACTOR.value: {
81
+ "required_sections": [
82
+ "Overview",
83
+ "Current State",
84
+ "Proposed Refactor",
85
+ "Scope",
86
+ ],
87
+ "optional_sections": [
88
+ "Problems with Current Approach",
89
+ "Implementation Plan",
90
+ "Risk Assessment",
91
+ "Success Criteria",
92
+ "Testing Strategy",
93
+ "Dependencies",
94
+ "Estimated Effort",
95
+ ],
96
+ "special_requirements": {},
97
+ },
98
+ WorkItemType.SECURITY.value: {
99
+ "required_sections": [
100
+ "Security Issue",
101
+ "Threat Model",
102
+ "Attack Vector",
103
+ "Mitigation Strategy",
104
+ "Compliance",
105
+ ],
106
+ "optional_sections": [
107
+ "Severity",
108
+ "Affected Components",
109
+ "Security Testing",
110
+ "Post-Deployment",
111
+ "Testing Strategy",
112
+ "Acceptance Criteria",
113
+ "Dependencies",
114
+ "Estimated Effort",
115
+ ],
116
+ "special_requirements": {},
117
+ },
118
+ WorkItemType.INTEGRATION_TEST.value: {
119
+ "required_sections": [
120
+ "Scope",
121
+ "Test Scenarios",
122
+ "Performance Benchmarks",
123
+ "Environment Requirements",
124
+ "Acceptance Criteria",
125
+ ],
126
+ "optional_sections": ["API Contracts", "Dependencies", "Estimated Effort"],
127
+ "special_requirements": {
128
+ "test_scenarios_min": 1,
129
+ "acceptance_criteria_min_items": 3,
130
+ },
131
+ },
132
+ WorkItemType.DEPLOYMENT.value: {
133
+ "required_sections": [
134
+ "Deployment Scope",
135
+ "Deployment Procedure",
136
+ "Rollback Procedure",
137
+ "Smoke Tests",
138
+ "Acceptance Criteria",
139
+ ],
140
+ "optional_sections": [
141
+ "Environment Configuration",
142
+ "Monitoring & Alerting",
143
+ "Post-Deployment Monitoring Period",
144
+ "Dependencies",
145
+ "Estimated Effort",
146
+ ],
147
+ "special_requirements": {
148
+ "deployment_procedure_subsections": [
149
+ "Pre-Deployment Checklist",
150
+ "Deployment Steps",
151
+ "Post-Deployment Steps",
152
+ ],
153
+ "rollback_procedure_subsections": [
154
+ "Rollback Triggers",
155
+ "Rollback Steps",
156
+ ],
157
+ "smoke_tests_min": 1,
158
+ "acceptance_criteria_min_items": 3,
159
+ },
160
+ },
161
+ }
162
+
163
+ return rules.get(
164
+ work_item_type,
165
+ {"required_sections": [], "optional_sections": [], "special_requirements": {}},
166
+ )
167
+
168
+
169
+ def check_required_sections(spec_content: str, work_item_type: str) -> list[str]:
170
+ """
171
+ Check if all required sections are present and non-empty.
172
+
173
+ Args:
174
+ spec_content: Full spec file content
175
+ work_item_type: Type of work item
176
+
177
+ Returns:
178
+ List of error messages (empty if all checks pass)
179
+ """
180
+ errors = []
181
+ rules = get_validation_rules(work_item_type)
182
+ required_sections = rules.get("required_sections", [])
183
+
184
+ # Strip HTML comments before parsing
185
+ clean_content = strip_html_comments(spec_content)
186
+
187
+ for section_name in required_sections:
188
+ section_content = parse_section(clean_content, section_name)
189
+
190
+ if section_content is None:
191
+ errors.append(f"Missing required section: '{section_name}'")
192
+ elif not section_content.strip():
193
+ errors.append(f"Required section '{section_name}' is empty")
194
+
195
+ return errors
196
+
197
+
198
+ def check_acceptance_criteria(spec_content: str, min_items: int = 3) -> Optional[str]:
199
+ """
200
+ Check if Acceptance Criteria section has enough items.
201
+
202
+ Args:
203
+ spec_content: Full spec file content
204
+ min_items: Minimum number of acceptance criteria items required (default: 3)
205
+
206
+ Returns:
207
+ Error message if validation fails, None otherwise
208
+ """
209
+ clean_content = strip_html_comments(spec_content)
210
+ ac_section = parse_section(clean_content, "Acceptance Criteria")
211
+
212
+ if ac_section is None:
213
+ return None # Section doesn't exist, will be caught by check_required_sections
214
+
215
+ checklist = extract_checklist(ac_section)
216
+
217
+ if len(checklist) < min_items:
218
+ return f"Acceptance Criteria must have at least {min_items} items (found {len(checklist)})"
219
+
220
+ return None
221
+
222
+
223
+ def check_test_scenarios(spec_content: str, min_scenarios: int = 1) -> Optional[str]:
224
+ """
225
+ Check if Test Scenarios section has enough scenarios.
226
+
227
+ Args:
228
+ spec_content: Full spec file content
229
+ min_scenarios: Minimum number of test scenarios required (default: 1)
230
+
231
+ Returns:
232
+ Error message if validation fails, None otherwise
233
+ """
234
+ clean_content = strip_html_comments(spec_content)
235
+ scenarios_section = parse_section(clean_content, "Test Scenarios")
236
+
237
+ if scenarios_section is None:
238
+ return None # Will be caught by check_required_sections
239
+
240
+ # Count H3 headings that match "Scenario N:" pattern
241
+ scenario_count = len(re.findall(r"###\s+Scenario\s+\d+:", scenarios_section, re.IGNORECASE))
242
+
243
+ if scenario_count < min_scenarios:
244
+ return f"Test Scenarios must have at least {min_scenarios} scenario(s) (found {scenario_count})"
245
+
246
+ return None
247
+
248
+
249
+ def check_smoke_tests(spec_content: str, min_tests: int = 1) -> Optional[str]:
250
+ """
251
+ Check if Smoke Tests section has enough test cases.
252
+
253
+ Args:
254
+ spec_content: Full spec file content
255
+ min_tests: Minimum number of smoke tests required (default: 1)
256
+
257
+ Returns:
258
+ Error message if validation fails, None otherwise
259
+ """
260
+ clean_content = strip_html_comments(spec_content)
261
+ smoke_tests_section = parse_section(clean_content, "Smoke Tests")
262
+
263
+ if smoke_tests_section is None:
264
+ return None # Will be caught by check_required_sections
265
+
266
+ # Count H3 headings that match "Test N:" pattern
267
+ test_count = len(re.findall(r"###\s+Test\s+\d+:", smoke_tests_section, re.IGNORECASE))
268
+
269
+ if test_count < min_tests:
270
+ return f"Smoke Tests must have at least {min_tests} test(s) (found {test_count})"
271
+
272
+ return None
273
+
274
+
275
+ def check_deployment_subsections(spec_content: str) -> list[str]:
276
+ """
277
+ Check if Deployment Procedure has all required subsections.
278
+
279
+ Args:
280
+ spec_content: Full spec file content
281
+
282
+ Returns:
283
+ List of error messages (empty if all checks pass)
284
+ """
285
+ errors = []
286
+ clean_content = strip_html_comments(spec_content)
287
+ deployment_section = parse_section(clean_content, "Deployment Procedure")
288
+
289
+ if deployment_section is None:
290
+ return [] # Will be caught by check_required_sections
291
+
292
+ required_subsections = [
293
+ "Pre-Deployment Checklist",
294
+ "Deployment Steps",
295
+ "Post-Deployment Steps",
296
+ ]
297
+
298
+ for subsection_name in required_subsections:
299
+ subsection_content = extract_subsection(deployment_section, subsection_name)
300
+ if subsection_content is None:
301
+ errors.append(f"Deployment Procedure missing required subsection: '{subsection_name}'")
302
+ elif not subsection_content.strip():
303
+ errors.append(f"Deployment Procedure subsection '{subsection_name}' is empty")
304
+
305
+ return errors
306
+
307
+
308
+ def check_rollback_subsections(spec_content: str) -> list[str]:
309
+ """
310
+ Check if Rollback Procedure has all required subsections.
311
+
312
+ Args:
313
+ spec_content: Full spec file content
314
+
315
+ Returns:
316
+ List of error messages (empty if all checks pass)
317
+ """
318
+ errors = []
319
+ clean_content = strip_html_comments(spec_content)
320
+ rollback_section = parse_section(clean_content, "Rollback Procedure")
321
+
322
+ if rollback_section is None:
323
+ return [] # Will be caught by check_required_sections
324
+
325
+ required_subsections = ["Rollback Triggers", "Rollback Steps"]
326
+
327
+ for subsection_name in required_subsections:
328
+ subsection_content = extract_subsection(rollback_section, subsection_name)
329
+ if subsection_content is None:
330
+ errors.append(f"Rollback Procedure missing required subsection: '{subsection_name}'")
331
+ elif not subsection_content.strip():
332
+ errors.append(f"Rollback Procedure subsection '{subsection_name}' is empty")
333
+
334
+ return errors
335
+
336
+
337
+ @log_errors()
338
+ def validate_spec_file(work_item_id: str, work_item_type: str) -> None:
339
+ """
340
+ Validate a work item specification file for completeness and correctness.
341
+
342
+ Args:
343
+ work_item_id: ID of the work item
344
+ work_item_type: Type of work item (feature, bug, refactor, security, integration_test, deployment)
345
+
346
+ Raises:
347
+ FileNotFoundError: If spec file doesn't exist
348
+ FileOperationError: If spec file cannot be read
349
+ SpecValidationError: If spec validation fails (contains list of validation errors)
350
+ """
351
+ # Try to load work items to get spec_file path
352
+ # If work_items.json doesn't exist, fallback to default pattern (for backwards compatibility/tests)
353
+ import json
354
+
355
+ work_items_file = Path(".session/tracking/work_items.json")
356
+ spec_file_path = None
357
+
358
+ if work_items_file.exists():
359
+ # Load from work_items.json (preferred method)
360
+ try:
361
+ with open(work_items_file) as f:
362
+ work_items_data = json.load(f)
363
+
364
+ if work_item_id in work_items_data.get("work_items", {}):
365
+ work_item = work_items_data["work_items"][work_item_id]
366
+ spec_file_path = work_item.get("spec_file")
367
+ except (OSError, json.JSONDecodeError):
368
+ # If loading fails, fallback to default pattern
369
+ pass
370
+
371
+ # Fallback to default pattern if not found in work_items.json
372
+ if not spec_file_path:
373
+ spec_file_path = f".session/specs/{work_item_id}.md"
374
+
375
+ spec_path = Path(spec_file_path)
376
+
377
+ if not spec_path.exists():
378
+ raise FileNotFoundError(file_path=str(spec_path), file_type="spec")
379
+
380
+ # Read spec content
381
+ try:
382
+ spec_content = spec_path.read_text(encoding="utf-8")
383
+ except OSError as e:
384
+ raise FileOperationError(
385
+ operation="read", file_path=str(spec_path), details=str(e), cause=e
386
+ )
387
+
388
+ # Collect all errors
389
+ errors = []
390
+
391
+ # Check required sections
392
+ errors.extend(check_required_sections(spec_content, work_item_type))
393
+
394
+ # Get special requirements for this work item type
395
+ rules = get_validation_rules(work_item_type)
396
+ special_requirements = rules.get("special_requirements", {})
397
+
398
+ # Check acceptance criteria (if required)
399
+ if "acceptance_criteria_min_items" in special_requirements:
400
+ min_items = special_requirements["acceptance_criteria_min_items"]
401
+ ac_error = check_acceptance_criteria(spec_content, min_items)
402
+ if ac_error:
403
+ errors.append(ac_error)
404
+
405
+ # Check test scenarios (for integration_test)
406
+ if "test_scenarios_min" in special_requirements:
407
+ min_scenarios = special_requirements["test_scenarios_min"]
408
+ scenarios_error = check_test_scenarios(spec_content, min_scenarios)
409
+ if scenarios_error:
410
+ errors.append(scenarios_error)
411
+
412
+ # Check smoke tests (for deployment)
413
+ if "smoke_tests_min" in special_requirements:
414
+ min_tests = special_requirements["smoke_tests_min"]
415
+ smoke_error = check_smoke_tests(spec_content, min_tests)
416
+ if smoke_error:
417
+ errors.append(smoke_error)
418
+
419
+ # Check deployment subsections (for deployment)
420
+ if "deployment_procedure_subsections" in special_requirements:
421
+ errors.extend(check_deployment_subsections(spec_content))
422
+
423
+ # Check rollback subsections (for deployment)
424
+ if "rollback_procedure_subsections" in special_requirements:
425
+ errors.extend(check_rollback_subsections(spec_content))
426
+
427
+ # Raise SpecValidationError if any validation errors found
428
+ if errors:
429
+ raise SpecValidationError(work_item_id=work_item_id, errors=errors)
430
+
431
+
432
+ def format_validation_report(
433
+ work_item_id: str, work_item_type: str, validation_error: Optional[SpecValidationError] = None
434
+ ) -> str:
435
+ """
436
+ Format validation errors into a human-readable report.
437
+
438
+ Args:
439
+ work_item_id: ID of the work item
440
+ work_item_type: Type of work item
441
+ validation_error: SpecValidationError containing validation errors, None if valid
442
+
443
+ Returns:
444
+ Formatted validation report
445
+ """
446
+ if not validation_error:
447
+ return f"✅ Spec file for '{work_item_id}' ({work_item_type}) is valid"
448
+
449
+ errors = validation_error.context.get("validation_errors", [])
450
+
451
+ report = f"❌ Spec file for '{work_item_id}' ({work_item_type}) has validation errors:\n\n"
452
+
453
+ for i, error in enumerate(errors, 1):
454
+ report += f"{i}. {error}\n"
455
+
456
+ report += "\n💡 Suggestions:\n"
457
+ report += f"- Review the template at templates/{work_item_type}_spec.md\n"
458
+ report += "- Check docs/spec-template-structure.md for section requirements\n"
459
+ report += f"- Edit .session/specs/{work_item_id}.md to add missing sections\n"
460
+
461
+ return report
462
+
463
+
464
+ # CLI interface for testing
465
+ if __name__ == "__main__":
466
+ import sys
467
+
468
+ if len(sys.argv) < 3:
469
+ output.info("Usage: python3 spec_validator.py <work_item_id> <work_item_type>")
470
+ output.info("Example: python3 spec_validator.py feature_websocket_notifications feature")
471
+ sys.exit(1)
472
+
473
+ work_item_id = sys.argv[1]
474
+ work_item_type = sys.argv[2]
475
+
476
+ try:
477
+ logger.info("Validating spec file for %s (%s)", work_item_id, work_item_type)
478
+ validate_spec_file(work_item_id, work_item_type)
479
+ report = format_validation_report(work_item_id, work_item_type)
480
+ output.info(report)
481
+ logger.info("Spec validation successful")
482
+ sys.exit(0)
483
+ except SpecValidationError as e:
484
+ logger.warning("Spec validation failed: %s", e.message)
485
+ report = format_validation_report(work_item_id, work_item_type, e)
486
+ output.info(report)
487
+ sys.exit(e.exit_code)
488
+ except (FileNotFoundError, FileOperationError) as e:
489
+ logger.error("File operation error during validation", exc_info=True)
490
+ output.error(f"Error: {e.message}")
491
+ if e.remediation:
492
+ output.info(f"Remediation: {e.remediation}")
493
+ sys.exit(e.exit_code)
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Work Item Updater - Update operations for work items.
4
+
5
+ Handles field updates with validation, both interactive and non-interactive.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from datetime import datetime
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ from solokit.core.error_handlers import log_errors
14
+ from solokit.core.exceptions import (
15
+ ErrorCode,
16
+ FileOperationError,
17
+ ValidationError,
18
+ WorkItemNotFoundError,
19
+ )
20
+ from solokit.core.logging_config import get_logger
21
+ from solokit.core.types import Priority, WorkItemStatus
22
+
23
+ if TYPE_CHECKING:
24
+ from .repository import WorkItemRepository
25
+ from .validator import WorkItemValidator
26
+ from solokit.core.output import get_output
27
+
28
+ logger = get_logger(__name__)
29
+ output = get_output()
30
+
31
+
32
+ class WorkItemUpdater:
33
+ """Handles work item update operations"""
34
+
35
+ PRIORITIES = Priority.values()
36
+
37
+ def __init__(self, repository: WorkItemRepository, validator: WorkItemValidator | None = None):
38
+ """Initialize updater with repository and optional validator
39
+
40
+ Args:
41
+ repository: WorkItemRepository instance for data access
42
+ validator: Optional WorkItemValidator for validation
43
+ """
44
+ self.repository = repository
45
+ self.validator = validator
46
+
47
+ @log_errors()
48
+ def update(self, work_id: str, **updates: Any) -> None:
49
+ """Update work item fields
50
+
51
+ Args:
52
+ work_id: ID of the work item to update
53
+ **updates: Field updates (status, priority, milestone, add_dependency, remove_dependency)
54
+
55
+ Raises:
56
+ FileOperationError: If work_items.json doesn't exist
57
+ WorkItemNotFoundError: If work item doesn't exist
58
+ ValidationError: If invalid status or priority provided
59
+ """
60
+ items = self.repository.get_all_work_items()
61
+
62
+ if not items:
63
+ raise FileOperationError(
64
+ operation="read",
65
+ file_path=str(self.repository.work_items_file),
66
+ details="No work items found",
67
+ )
68
+
69
+ if work_id not in items:
70
+ raise WorkItemNotFoundError(work_id)
71
+
72
+ item = items[work_id]
73
+ changes = []
74
+
75
+ # Apply updates
76
+ for field, value in updates.items():
77
+ if field == "status":
78
+ if value not in WorkItemStatus.values():
79
+ logger.warning("Invalid status: %s", value)
80
+ raise ValidationError(
81
+ message=f"Invalid status: {value}",
82
+ code=ErrorCode.INVALID_STATUS,
83
+ context={"status": value, "valid_statuses": WorkItemStatus.values()},
84
+ remediation=f"Valid statuses: {', '.join(WorkItemStatus.values())}",
85
+ )
86
+ old_value = item["status"]
87
+ item["status"] = value
88
+ changes.append(f" status: {old_value} → {value}")
89
+
90
+ elif field == "priority":
91
+ if value not in self.PRIORITIES:
92
+ logger.warning("Invalid priority: %s", value)
93
+ raise ValidationError(
94
+ message=f"Invalid priority: {value}",
95
+ code=ErrorCode.INVALID_PRIORITY,
96
+ context={"priority": value, "valid_priorities": self.PRIORITIES},
97
+ remediation=f"Valid priorities: {', '.join(self.PRIORITIES)}",
98
+ )
99
+ old_value = item["priority"]
100
+ item["priority"] = value
101
+ changes.append(f" priority: {old_value} → {value}")
102
+
103
+ elif field == "milestone":
104
+ old_value = item.get("milestone", "(none)")
105
+ item["milestone"] = value
106
+ changes.append(f" milestone: {old_value} → {value}")
107
+
108
+ elif field == "add_dependency":
109
+ # Support comma-separated list of dependencies
110
+ deps = item.get("dependencies", [])
111
+ dep_ids = [d.strip() for d in value.split(",") if d.strip()]
112
+
113
+ for dep_id in dep_ids:
114
+ if dep_id not in deps:
115
+ if self.repository.work_item_exists(dep_id):
116
+ deps.append(dep_id)
117
+ changes.append(f" added dependency: {dep_id}")
118
+ else:
119
+ logger.warning("Dependency '%s' not found", dep_id)
120
+ raise WorkItemNotFoundError(dep_id)
121
+
122
+ item["dependencies"] = deps
123
+
124
+ elif field == "remove_dependency":
125
+ # Support comma-separated list of dependencies
126
+ deps = item.get("dependencies", [])
127
+ dep_ids = [d.strip() for d in value.split(",") if d.strip()]
128
+
129
+ for dep_id in dep_ids:
130
+ if dep_id in deps:
131
+ deps.remove(dep_id)
132
+ changes.append(f" removed dependency: {dep_id}")
133
+
134
+ item["dependencies"] = deps
135
+
136
+ if not changes:
137
+ logger.info("No changes made to %s", work_id)
138
+ raise ValidationError(
139
+ message="No changes made",
140
+ code=ErrorCode.MISSING_REQUIRED_FIELD,
141
+ context={"work_item_id": work_id},
142
+ remediation="Provide valid field updates",
143
+ )
144
+
145
+ # Record update
146
+ item.setdefault("update_history", []).append(
147
+ {"timestamp": datetime.now().isoformat(), "changes": changes}
148
+ )
149
+
150
+ # Save - pass the entire updated item as a dict of updates
151
+ self.repository.update_work_item(work_id, item)
152
+
153
+ # Success - user-facing output
154
+ output.info(f"\nUpdated {work_id}:")
155
+ for change in changes:
156
+ output.info(change)
157
+ output.info("")