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
solokit/core/config.py ADDED
@@ -0,0 +1,453 @@
1
+ """Centralized configuration management with caching and validation.
2
+
3
+ This module provides a singleton ConfigManager that loads, validates, and caches
4
+ configuration from .session/config.json. It replaces the duplicated configuration
5
+ loading logic scattered across multiple modules.
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ from dataclasses import dataclass, field
11
+ from pathlib import Path
12
+ from typing import Optional
13
+
14
+ from solokit.core.exceptions import (
15
+ ConfigurationError,
16
+ ConfigValidationError,
17
+ ErrorCode,
18
+ )
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ @dataclass
24
+ class ExecutionConfig:
25
+ """Test execution configuration."""
26
+
27
+ enabled: bool = True
28
+ required: bool = True
29
+ coverage_threshold: int = 80
30
+ commands: dict[str, str] = field(
31
+ default_factory=lambda: {
32
+ "python": "pytest --cov=src/solokit --cov-report=json",
33
+ "javascript": "npm test -- --coverage",
34
+ "typescript": "npm test -- --coverage",
35
+ }
36
+ )
37
+
38
+
39
+ @dataclass
40
+ class LintingConfig:
41
+ """Linting configuration."""
42
+
43
+ enabled: bool = True
44
+ required: bool = False
45
+ auto_fix: bool = True
46
+ commands: dict[str, str] = field(
47
+ default_factory=lambda: {
48
+ "python": "ruff check .",
49
+ "javascript": "npx eslint .",
50
+ "typescript": "npx eslint .",
51
+ }
52
+ )
53
+
54
+
55
+ @dataclass
56
+ class FormattingConfig:
57
+ """Formatting configuration."""
58
+
59
+ enabled: bool = True
60
+ required: bool = False
61
+ auto_fix: bool = True
62
+ commands: dict[str, str] = field(
63
+ default_factory=lambda: {
64
+ "python": "ruff format .",
65
+ "javascript": "npx prettier .",
66
+ "typescript": "npx prettier .",
67
+ }
68
+ )
69
+
70
+
71
+ @dataclass
72
+ class SecurityConfig:
73
+ """Security scanning configuration."""
74
+
75
+ enabled: bool = True
76
+ required: bool = True
77
+ fail_on: str = "high" # critical, high, medium, low
78
+
79
+
80
+ @dataclass
81
+ class DocumentationConfig:
82
+ """Documentation validation configuration."""
83
+
84
+ enabled: bool = True
85
+ required: bool = False
86
+ check_changelog: bool = True
87
+ check_docstrings: bool = True
88
+ check_readme: bool = False
89
+
90
+
91
+ @dataclass
92
+ class SpecCompletenessConfig:
93
+ """Spec completeness validation configuration."""
94
+
95
+ enabled: bool = True
96
+ required: bool = True
97
+
98
+
99
+ @dataclass
100
+ class Context7Config:
101
+ """Context7 library verification configuration."""
102
+
103
+ enabled: bool = False
104
+ important_libraries: list[str] = field(default_factory=list)
105
+
106
+
107
+ @dataclass
108
+ class IntegrationConfig:
109
+ """Integration test validation configuration."""
110
+
111
+ enabled: bool = True
112
+ documentation: dict[str, bool] = field(
113
+ default_factory=lambda: {
114
+ "enabled": True,
115
+ "architecture_diagrams": True,
116
+ "sequence_diagrams": True,
117
+ "contract_documentation": True,
118
+ "performance_baseline_docs": True,
119
+ }
120
+ )
121
+
122
+
123
+ @dataclass
124
+ class DeploymentConfig:
125
+ """Deployment quality gates configuration."""
126
+
127
+ enabled: bool = True
128
+ integration_tests: dict[str, bool] = field(default_factory=lambda: {"enabled": True})
129
+ security_scans: dict[str, bool] = field(default_factory=lambda: {"enabled": True})
130
+
131
+
132
+ @dataclass
133
+ class QualityGatesConfig:
134
+ """Quality gates configuration."""
135
+
136
+ test_execution: ExecutionConfig = field(default_factory=ExecutionConfig)
137
+ linting: LintingConfig = field(default_factory=LintingConfig)
138
+ formatting: FormattingConfig = field(default_factory=FormattingConfig)
139
+ security: SecurityConfig = field(default_factory=SecurityConfig)
140
+ documentation: DocumentationConfig = field(default_factory=DocumentationConfig)
141
+ spec_completeness: SpecCompletenessConfig = field(default_factory=SpecCompletenessConfig)
142
+ context7: Context7Config = field(default_factory=Context7Config)
143
+ integration: IntegrationConfig = field(default_factory=IntegrationConfig)
144
+ deployment: DeploymentConfig = field(default_factory=DeploymentConfig)
145
+
146
+
147
+ @dataclass
148
+ class GitWorkflowConfig:
149
+ """Git workflow configuration."""
150
+
151
+ mode: str = "pr"
152
+ auto_push: bool = True
153
+ auto_create_pr: bool = True
154
+ delete_branch_after_merge: bool = True
155
+ pr_title_template: str = "{type}: {title}"
156
+ pr_body_template: str = "## Work Item: {work_item_id}\n\n{description}\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)"
157
+
158
+
159
+ @dataclass
160
+ class CurationConfig:
161
+ """Learning curation configuration."""
162
+
163
+ auto_curate: bool = False
164
+ frequency: int = 5
165
+ dry_run: bool = False
166
+ similarity_threshold: float = 0.7
167
+
168
+
169
+ @dataclass
170
+ class SolokitConfig:
171
+ """Main Solokit configuration."""
172
+
173
+ quality_gates: QualityGatesConfig = field(default_factory=QualityGatesConfig)
174
+ git_workflow: GitWorkflowConfig = field(default_factory=GitWorkflowConfig)
175
+ curation: CurationConfig = field(default_factory=CurationConfig)
176
+
177
+
178
+ class ConfigManager:
179
+ """Centralized configuration management with caching and validation.
180
+
181
+ This class implements the Singleton pattern to ensure a single source of truth
182
+ for configuration across the entire application. It loads configuration from
183
+ .session/config.json, validates it, and caches it for performance.
184
+
185
+ Example:
186
+ >>> config_mgr = get_config_manager()
187
+ >>> config_mgr.load_config(Path(".session/config.json"))
188
+ >>> quality_config = config_mgr.quality_gates
189
+ >>> print(quality_config.test_execution.coverage_threshold)
190
+ 80
191
+ """
192
+
193
+ _instance: Optional["ConfigManager"] = None
194
+ _config: Optional[SolokitConfig] = None
195
+ _config_path: Optional[Path] = None
196
+
197
+ def __new__(cls) -> "ConfigManager":
198
+ """Ensure only one instance of ConfigManager exists."""
199
+ if cls._instance is None:
200
+ cls._instance = super().__new__(cls)
201
+ return cls._instance
202
+
203
+ def __init__(self) -> None:
204
+ """Initialize with default configuration if not already initialized."""
205
+ if self._config is None:
206
+ self._config = SolokitConfig()
207
+
208
+ def load_config(self, config_path: Path, force_reload: bool = False) -> None:
209
+ """Load configuration from file.
210
+
211
+ Args:
212
+ config_path: Path to config.json file
213
+ force_reload: Force reload even if already cached
214
+
215
+ Raises:
216
+ ConfigurationError: If JSON is invalid or file cannot be read
217
+ ConfigValidationError: If configuration structure is invalid
218
+
219
+ Note:
220
+ If the config file doesn't exist, default configuration is used.
221
+ """
222
+ # Skip if already loaded and not forcing reload
223
+ if not force_reload and self._config_path == config_path:
224
+ logger.debug("Config already loaded from %s, using cache", config_path)
225
+ return
226
+
227
+ self._config_path = config_path
228
+
229
+ if not config_path.exists():
230
+ logger.info("Config file not found at %s, using defaults", config_path)
231
+ self._config = SolokitConfig()
232
+ return
233
+
234
+ try:
235
+ with open(config_path, encoding="utf-8") as f:
236
+ data = json.load(f)
237
+
238
+ # Parse config sections with defaults
239
+ quality_gates_dict = data.get("quality_gates", {})
240
+
241
+ # Handle integration_tests at top level (for backward compatibility)
242
+ # Merge it into quality_gates if it exists
243
+ if "integration_tests" in data:
244
+ if "integration" not in quality_gates_dict:
245
+ quality_gates_dict["integration"] = data["integration_tests"]
246
+
247
+ quality_gates_data = self._parse_quality_gates(quality_gates_dict)
248
+ git_workflow_data = data.get("git_workflow", {})
249
+ curation_data = data.get("curation", {})
250
+
251
+ # Filter curation_data to only include valid CurationConfig fields
252
+ # This maintains backward compatibility with old configs
253
+ valid_curation_fields = {
254
+ "auto_curate",
255
+ "frequency",
256
+ "dry_run",
257
+ "similarity_threshold",
258
+ }
259
+ filtered_curation_data = (
260
+ {k: v for k, v in curation_data.items() if k in valid_curation_fields}
261
+ if curation_data
262
+ else {}
263
+ )
264
+
265
+ # Create config with parsed data
266
+ self._config = SolokitConfig(
267
+ quality_gates=quality_gates_data,
268
+ git_workflow=(
269
+ GitWorkflowConfig(**git_workflow_data)
270
+ if git_workflow_data
271
+ else GitWorkflowConfig()
272
+ ),
273
+ curation=(
274
+ CurationConfig(**filtered_curation_data)
275
+ if filtered_curation_data
276
+ else CurationConfig()
277
+ ),
278
+ )
279
+
280
+ logger.info("Loaded configuration from %s", config_path)
281
+
282
+ except json.JSONDecodeError as e:
283
+ raise ConfigurationError(
284
+ message=f"Invalid JSON in configuration file: {config_path}",
285
+ code=ErrorCode.INVALID_JSON,
286
+ context={"config_path": str(config_path), "error": str(e)},
287
+ remediation="Check the JSON syntax in your config file. Ensure all quotes, brackets, and commas are properly placed.",
288
+ cause=e,
289
+ )
290
+ except TypeError as e:
291
+ raise ConfigValidationError(
292
+ config_path=str(config_path),
293
+ errors=[f"Invalid configuration structure: {str(e)}"],
294
+ )
295
+ except PermissionError as e:
296
+ raise ConfigurationError(
297
+ message=f"Permission denied reading configuration file: {config_path}",
298
+ code=ErrorCode.FILE_OPERATION_FAILED,
299
+ context={"config_path": str(config_path)},
300
+ remediation="Check file permissions and ensure you have read access to the config file.",
301
+ cause=e,
302
+ )
303
+ except OSError as e:
304
+ raise ConfigurationError(
305
+ message=f"Error reading configuration file: {config_path}",
306
+ code=ErrorCode.FILE_OPERATION_FAILED,
307
+ context={"config_path": str(config_path), "error": str(e)},
308
+ remediation="Ensure the config file is not corrupted and the file system is accessible.",
309
+ cause=e,
310
+ )
311
+
312
+ def _parse_quality_gates(self, data: dict) -> QualityGatesConfig:
313
+ """Parse quality gates configuration with nested structures.
314
+
315
+ Args:
316
+ data: Raw quality gates configuration dict
317
+
318
+ Returns:
319
+ Validated QualityGatesConfig with defaults for missing values
320
+
321
+ Raises:
322
+ ConfigValidationError: If quality_gates structure is invalid
323
+ """
324
+ try:
325
+ # Parse nested configs with field filtering
326
+ # Helper to filter only valid fields for a dataclass
327
+ def filter_fields(data: dict, config_class: type) -> dict:
328
+ """Filter dict to only include fields that exist in the dataclass."""
329
+ if not data:
330
+ return {}
331
+ import dataclasses
332
+
333
+ valid_fields = {f.name for f in dataclasses.fields(config_class)}
334
+ return {k: v for k, v in data.items() if k in valid_fields}
335
+
336
+ test_exec_data = filter_fields(data.get("test_execution", {}), ExecutionConfig)
337
+ linting_data = filter_fields(data.get("linting", {}), LintingConfig)
338
+ formatting_data = filter_fields(data.get("formatting", {}), FormattingConfig)
339
+ security_data = filter_fields(data.get("security", {}), SecurityConfig)
340
+ documentation_data = filter_fields(data.get("documentation", {}), DocumentationConfig)
341
+ spec_completeness_data = filter_fields(
342
+ data.get("spec_completeness", {}), SpecCompletenessConfig
343
+ )
344
+ context7_data = filter_fields(data.get("context7", {}), Context7Config)
345
+ integration_data = filter_fields(data.get("integration", {}), IntegrationConfig)
346
+ deployment_data = filter_fields(data.get("deployment", {}), DeploymentConfig)
347
+
348
+ return QualityGatesConfig(
349
+ test_execution=(
350
+ ExecutionConfig(**test_exec_data) if test_exec_data else ExecutionConfig()
351
+ ),
352
+ linting=(LintingConfig(**linting_data) if linting_data else LintingConfig()),
353
+ formatting=(
354
+ FormattingConfig(**formatting_data) if formatting_data else FormattingConfig()
355
+ ),
356
+ security=(SecurityConfig(**security_data) if security_data else SecurityConfig()),
357
+ documentation=(
358
+ DocumentationConfig(**documentation_data)
359
+ if documentation_data
360
+ else DocumentationConfig()
361
+ ),
362
+ spec_completeness=(
363
+ SpecCompletenessConfig(**spec_completeness_data)
364
+ if spec_completeness_data
365
+ else SpecCompletenessConfig()
366
+ ),
367
+ context7=(Context7Config(**context7_data) if context7_data else Context7Config()),
368
+ integration=(
369
+ IntegrationConfig(**integration_data)
370
+ if integration_data
371
+ else IntegrationConfig()
372
+ ),
373
+ deployment=(
374
+ DeploymentConfig(**deployment_data) if deployment_data else DeploymentConfig()
375
+ ),
376
+ )
377
+ except TypeError as e:
378
+ # Collect validation errors
379
+ errors = [f"Invalid quality_gates structure: {str(e)}"]
380
+ raise ConfigValidationError(
381
+ config_path=str(self._config_path) if self._config_path else "unknown",
382
+ errors=errors,
383
+ )
384
+
385
+ @property
386
+ def quality_gates(self) -> QualityGatesConfig:
387
+ """Get quality gates configuration.
388
+
389
+ Returns:
390
+ Quality gates configuration with all sub-configurations
391
+ """
392
+ assert self._config is not None, "Config not initialized"
393
+ return self._config.quality_gates
394
+
395
+ @property
396
+ def git_workflow(self) -> GitWorkflowConfig:
397
+ """Get git workflow configuration.
398
+
399
+ Returns:
400
+ Git workflow configuration
401
+ """
402
+ assert self._config is not None, "Config not initialized"
403
+ return self._config.git_workflow
404
+
405
+ @property
406
+ def curation(self) -> CurationConfig:
407
+ """Get curation configuration.
408
+
409
+ Returns:
410
+ Learning curation configuration
411
+ """
412
+ assert self._config is not None, "Config not initialized"
413
+ return self._config.curation
414
+
415
+ def get_config(self) -> SolokitConfig:
416
+ """Get full configuration.
417
+
418
+ Returns:
419
+ Complete Solokit configuration
420
+ """
421
+ assert self._config is not None, "Config not initialized"
422
+ return self._config
423
+
424
+ def invalidate_cache(self) -> None:
425
+ """Invalidate cached configuration.
426
+
427
+ Forces next load_config() call to reload from disk.
428
+ Useful for testing and when config file changes.
429
+ """
430
+ self._config_path = None
431
+ logger.debug("Configuration cache invalidated")
432
+
433
+
434
+ # Global instance
435
+ _config_manager: Optional[ConfigManager] = None
436
+
437
+
438
+ def get_config_manager() -> ConfigManager:
439
+ """Get global ConfigManager instance.
440
+
441
+ Returns:
442
+ Singleton ConfigManager instance
443
+
444
+ Example:
445
+ >>> config = get_config_manager()
446
+ >>> config.load_config(Path(".session/config.json"))
447
+ >>> print(config.quality_gates.test_execution.enabled)
448
+ True
449
+ """
450
+ global _config_manager
451
+ if _config_manager is None:
452
+ _config_manager = ConfigManager()
453
+ return _config_manager
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env python3
2
+ """Configuration validation using JSON schema."""
3
+
4
+ import json
5
+ import logging
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from solokit.core.error_handlers import log_errors
10
+ from solokit.core.exceptions import (
11
+ ConfigurationError,
12
+ ConfigValidationError,
13
+ ErrorCode,
14
+ ValidationError,
15
+ )
16
+ from solokit.core.exceptions import (
17
+ FileNotFoundError as SolokitFileNotFoundError,
18
+ )
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ @log_errors()
24
+ def validate_config(config_path: Path, schema_path: Path) -> dict[str, Any]:
25
+ """
26
+ Validate configuration against JSON schema.
27
+
28
+ Args:
29
+ config_path: Path to config.json
30
+ schema_path: Path to config.schema.json
31
+
32
+ Returns:
33
+ Validated configuration dictionary
34
+
35
+ Raises:
36
+ FileNotFoundError: If config file doesn't exist
37
+ ValidationError: If config JSON is malformed
38
+ ConfigurationError: If schema is invalid or malformed
39
+ ConfigValidationError: If config fails schema validation
40
+ """
41
+ try:
42
+ import jsonschema
43
+ except ImportError:
44
+ # If jsonschema not installed, skip validation but warn
45
+ logger.warning("jsonschema not installed, skipping validation")
46
+ # Still try to load and return config
47
+ if not config_path.exists():
48
+ raise SolokitFileNotFoundError(str(config_path), file_type="config")
49
+ try:
50
+ with open(config_path) as f:
51
+ config: dict[str, Any] = json.load(f)
52
+ return config
53
+ except json.JSONDecodeError as e:
54
+ raise ValidationError(
55
+ message=f"Invalid JSON in config file: {config_path}",
56
+ code=ErrorCode.INVALID_JSON,
57
+ context={"file_path": str(config_path), "error": str(e)},
58
+ remediation="Fix JSON syntax errors in config file",
59
+ cause=e,
60
+ ) from e
61
+
62
+ # Load config
63
+ if not config_path.exists():
64
+ raise SolokitFileNotFoundError(str(config_path), file_type="config")
65
+
66
+ try:
67
+ with open(config_path) as f:
68
+ config = json.load(f)
69
+ except json.JSONDecodeError as e:
70
+ raise ValidationError(
71
+ message=f"Invalid JSON in config file: {config_path}",
72
+ code=ErrorCode.INVALID_JSON,
73
+ context={"file_path": str(config_path), "error": str(e)},
74
+ remediation="Fix JSON syntax errors in config file",
75
+ cause=e,
76
+ ) from e
77
+
78
+ # Load schema
79
+ if not schema_path.exists():
80
+ # Schema missing is a warning, not an error - allow validation to be skipped
81
+ logger.warning(f"Schema file not found: {schema_path}, skipping validation")
82
+ config_dict: dict[str, Any] = config
83
+ return config_dict
84
+
85
+ try:
86
+ with open(schema_path) as f:
87
+ schema = json.load(f)
88
+ except json.JSONDecodeError as e:
89
+ raise ConfigurationError(
90
+ message=f"Invalid JSON in schema file: {schema_path}",
91
+ code=ErrorCode.INVALID_CONFIG_VALUE,
92
+ context={"file_path": str(schema_path), "error": str(e)},
93
+ remediation="Fix JSON syntax errors in schema file",
94
+ cause=e,
95
+ ) from e
96
+
97
+ # Validate
98
+ try:
99
+ jsonschema.validate(instance=config, schema=schema)
100
+ validated_config: dict[str, Any] = config
101
+ return validated_config
102
+ except jsonschema.ValidationError as e:
103
+ error_msg = _format_validation_error(e)
104
+ raise ConfigValidationError(config_path=str(config_path), errors=[error_msg]) from e
105
+ except jsonschema.SchemaError as e:
106
+ raise ConfigurationError(
107
+ message=f"Invalid schema structure: {schema_path}",
108
+ code=ErrorCode.INVALID_CONFIG_VALUE,
109
+ context={"file_path": str(schema_path), "error": e.message},
110
+ remediation="Fix schema structure errors in schema file",
111
+ cause=e,
112
+ ) from e
113
+
114
+
115
+ def _format_validation_error(error: Any) -> str:
116
+ """Format validation error for user-friendly display."""
117
+ path = " -> ".join(str(p) for p in error.path) if error.path else "root"
118
+ return f"Validation error at '{path}': {error.message}"
119
+
120
+
121
+ @log_errors()
122
+ def load_and_validate_config(config_path: Path, schema_path: Path) -> dict[str, Any]:
123
+ """
124
+ Load and validate configuration.
125
+
126
+ This is a convenience function that wraps validate_config.
127
+ Use validate_config directly for better error handling.
128
+
129
+ Args:
130
+ config_path: Path to config.json
131
+ schema_path: Path to config.schema.json
132
+
133
+ Returns:
134
+ Loaded and validated configuration
135
+
136
+ Raises:
137
+ FileNotFoundError: If config file doesn't exist
138
+ ValidationError: If config JSON is malformed
139
+ ConfigurationError: If schema is invalid or malformed
140
+ ConfigValidationError: If config fails schema validation
141
+ """
142
+ # validate_config now loads and validates in one step
143
+ return validate_config(config_path, schema_path)
144
+
145
+
146
+ def main() -> None:
147
+ """
148
+ CLI entry point for manual validation.
149
+
150
+ Exits with 0 on success, non-zero on error.
151
+
152
+ Raises:
153
+ SystemExit: Always exits with status code
154
+ """
155
+ import sys
156
+
157
+ from solokit.core.exceptions import SolokitError
158
+ from solokit.core.output import get_output
159
+
160
+ output = get_output()
161
+
162
+ if len(sys.argv) < 2:
163
+ output.info("Usage: config_validator.py <config_path> [schema_path]")
164
+ output.info("\nValidate Solokit configuration against JSON schema.")
165
+ output.info("\nExample:")
166
+ output.info(" python3 config_validator.py .session/config.json")
167
+ output.info(
168
+ " python3 config_validator.py .session/config.json .session/config.schema.json"
169
+ )
170
+ sys.exit(1)
171
+
172
+ config_path = Path(sys.argv[1])
173
+
174
+ # Default schema path
175
+ if len(sys.argv) >= 3:
176
+ schema_path = Path(sys.argv[2])
177
+ else:
178
+ # Assume schema is in same directory as config
179
+ schema_path = config_path.parent / "config.schema.json"
180
+
181
+ output.info(f"Validating: {config_path}")
182
+ output.info(f"Against schema: {schema_path}\n")
183
+
184
+ try:
185
+ validate_config(config_path, schema_path)
186
+ output.success("Configuration is valid!")
187
+ sys.exit(0)
188
+ except SolokitError as e:
189
+ output.info("✗ Configuration validation failed!\n")
190
+ output.info(f"Error: {e.message}")
191
+ if e.context:
192
+ output.info(f"Context: {e.context}")
193
+ if e.remediation:
194
+ output.info(f"\nRemediation: {e.remediation}")
195
+ output.info("\nSee docs/configuration.md for configuration reference.")
196
+ sys.exit(e.exit_code)
197
+ except Exception as e:
198
+ output.info("✗ Unexpected error during validation!\n")
199
+ output.info(f"Error: {e}")
200
+ sys.exit(1)
201
+
202
+
203
+ if __name__ == "__main__":
204
+ main()