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,334 @@
1
+ """
2
+ Environment Validator Module
3
+
4
+ Validates and optionally auto-updates environment (Python, Node.js) for template installation.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ import shutil
11
+ from pathlib import Path
12
+ from typing import Literal
13
+
14
+ from solokit.core.command_runner import CommandRunner
15
+ from solokit.core.exceptions import ErrorCode, ValidationError
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ MIN_NODE_VERSION = (18, 0, 0)
20
+ MIN_PYTHON_VERSION = (3, 11, 0)
21
+
22
+
23
+ def parse_version(version_str: str) -> tuple[int, int, int]:
24
+ """
25
+ Parse version string into tuple of (major, minor, patch).
26
+
27
+ Args:
28
+ version_str: Version string like "18.0.0" or "v18.0.0" or "3.11.7"
29
+
30
+ Returns:
31
+ Tuple of (major, minor, patch) as integers
32
+
33
+ Raises:
34
+ ValueError: If version string is malformed
35
+ """
36
+ # Remove 'v' prefix if present
37
+ clean_version = version_str.strip().lstrip("v")
38
+
39
+ # Split by '.' and take first 3 parts
40
+ parts = clean_version.split(".")
41
+ if len(parts) < 2:
42
+ raise ValueError(f"Invalid version format: {version_str}")
43
+
44
+ try:
45
+ major = int(parts[0])
46
+ minor = int(parts[1])
47
+ patch = int(parts[2]) if len(parts) > 2 else 0
48
+ return (major, minor, patch)
49
+ except (ValueError, IndexError) as e:
50
+ raise ValueError(f"Invalid version format: {version_str}") from e
51
+
52
+
53
+ def check_node_version() -> tuple[bool, str | None]:
54
+ """
55
+ Check if Node.js is installed and meets minimum version requirement.
56
+
57
+ Returns:
58
+ Tuple of (meets_requirement: bool, current_version: str | None)
59
+ - meets_requirement: True if Node.js >= 18.0.0
60
+ - current_version: Version string or None if not installed
61
+ """
62
+ # Check if node is available
63
+ if not shutil.which("node"):
64
+ return False, None
65
+
66
+ runner = CommandRunner(default_timeout=5)
67
+ result = runner.run(["node", "--version"], check=False)
68
+
69
+ if not result.success:
70
+ return False, None
71
+
72
+ version_str = result.stdout.strip()
73
+ try:
74
+ version = parse_version(version_str)
75
+ meets_req = version >= MIN_NODE_VERSION
76
+ return meets_req, version_str
77
+ except ValueError:
78
+ logger.warning(f"Could not parse Node.js version: {version_str}")
79
+ return False, version_str
80
+
81
+
82
+ def check_python_version(
83
+ specific_binary: str | None = None,
84
+ ) -> tuple[bool, str | None, str | None]:
85
+ """
86
+ Check if Python is installed and meets minimum version requirement.
87
+
88
+ Args:
89
+ specific_binary: Specific Python binary to check (e.g., "python3.11").
90
+ If None, checks "python3" and "python".
91
+
92
+ Returns:
93
+ Tuple of (meets_requirement: bool, current_version: str | None, binary_path: str | None)
94
+ - meets_requirement: True if Python >= 3.11.0
95
+ - current_version: Version string or None if not installed
96
+ - binary_path: Path to the Python binary or None
97
+ """
98
+ binaries_to_check = (
99
+ [specific_binary] if specific_binary else ["python3", "python", "python3.11"]
100
+ )
101
+
102
+ for binary in binaries_to_check:
103
+ binary_path = shutil.which(binary)
104
+ if not binary_path:
105
+ continue
106
+
107
+ runner = CommandRunner(default_timeout=5)
108
+ result = runner.run([binary, "--version"], check=False)
109
+
110
+ if not result.success:
111
+ continue
112
+
113
+ # Python --version outputs to stdout (Python 3.x) or stderr (Python 2.x)
114
+ version_output = result.stdout or result.stderr
115
+ version_str = version_output.strip().replace("Python ", "")
116
+
117
+ try:
118
+ version = parse_version(version_str)
119
+ meets_req = version >= MIN_PYTHON_VERSION
120
+ return meets_req, version_str, binary_path
121
+ except ValueError:
122
+ logger.warning(f"Could not parse Python version from {binary}: {version_str}")
123
+ continue
124
+
125
+ return False, None, None
126
+
127
+
128
+ def attempt_node_install_with_nvm() -> tuple[bool, str]:
129
+ """
130
+ Attempt to install Node.js >= 18.0.0 using nvm if available.
131
+
132
+ Returns:
133
+ Tuple of (success: bool, message: str)
134
+ """
135
+ # Check if nvm is available
136
+ nvm_dir = Path.home() / ".nvm"
137
+ if not nvm_dir.exists():
138
+ return (
139
+ False,
140
+ "nvm not found. Please install Node.js 18+ manually:\n"
141
+ " macOS: brew install node@18\n"
142
+ " Ubuntu: curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -\n"
143
+ " sudo apt-get install -y nodejs",
144
+ )
145
+
146
+ # Try to source nvm and install Node.js 18
147
+ runner = CommandRunner(default_timeout=300)
148
+
149
+ # nvm install command needs to be run in a shell environment
150
+ nvm_script = f"""
151
+ export NVM_DIR="{nvm_dir}"
152
+ [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
153
+ nvm install 18
154
+ nvm use 18
155
+ """
156
+
157
+ result = runner.run(["bash", "-c", nvm_script], check=False)
158
+
159
+ if result.success:
160
+ logger.info("Successfully installed Node.js 18 via nvm")
161
+ return True, "Node.js 18 installed successfully via nvm"
162
+ else:
163
+ return (
164
+ False,
165
+ f"nvm installation failed: {result.stderr}\n\n"
166
+ "Please install Node.js 18+ manually:\n"
167
+ " macOS: brew install node@18\n"
168
+ " Ubuntu: curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -\n"
169
+ " sudo apt-get install -y nodejs",
170
+ )
171
+
172
+
173
+ def attempt_python_install_with_pyenv(version: str = "3.11") -> tuple[bool, str]:
174
+ """
175
+ Attempt to install Python using pyenv if available.
176
+
177
+ Args:
178
+ version: Python version to install (e.g., "3.11")
179
+
180
+ Returns:
181
+ Tuple of (success: bool, message: str)
182
+ """
183
+ # Check if pyenv is available
184
+ if not shutil.which("pyenv"):
185
+ return (
186
+ False,
187
+ f"pyenv not found. Please install Python {version}+ manually:\n"
188
+ f" macOS: brew install python@{version}\n"
189
+ f" Ubuntu: sudo apt install python{version} python{version}-venv",
190
+ )
191
+
192
+ # Try to install Python via pyenv
193
+ runner = CommandRunner(default_timeout=600) # Python builds can take time
194
+
195
+ # Install latest 3.11.x
196
+ result = runner.run(["pyenv", "install", "-s", version], check=False)
197
+
198
+ if result.success:
199
+ logger.info(f"Successfully installed Python {version} via pyenv")
200
+ return True, f"Python {version} installed successfully via pyenv"
201
+ else:
202
+ return (
203
+ False,
204
+ f"pyenv installation failed: {result.stderr}\n\n"
205
+ f"Please install Python {version}+ manually:\n"
206
+ f" macOS: brew install python@{version}\n"
207
+ f" Ubuntu: sudo apt install python{version} python{version}-venv",
208
+ )
209
+
210
+
211
+ def validate_environment(
212
+ stack_type: Literal["saas_t3", "ml_ai_fastapi", "dashboard_refine", "fullstack_nextjs"],
213
+ auto_update: bool = True,
214
+ ) -> dict[str, bool | str | None | list[str]]:
215
+ """
216
+ Validate environment for a specific stack and optionally auto-update.
217
+
218
+ Args:
219
+ stack_type: Type of stack to validate for
220
+ auto_update: If True, attempt to auto-install missing requirements
221
+
222
+ Returns:
223
+ Dictionary with validation results:
224
+ {
225
+ "node_ok": bool,
226
+ "node_version": str | None,
227
+ "python_ok": bool,
228
+ "python_version": str | None,
229
+ "python_binary": str | None,
230
+ "errors": list[str],
231
+ "warnings": list[str]
232
+ }
233
+
234
+ Raises:
235
+ ValidationError: If required environment cannot be satisfied
236
+ """
237
+ errors: list[str] = []
238
+ warnings: list[str] = []
239
+
240
+ result: dict[str, bool | str | None | list[str]] = {
241
+ "node_ok": True,
242
+ "node_version": None,
243
+ "python_ok": True,
244
+ "python_version": None,
245
+ "python_binary": None,
246
+ "errors": errors,
247
+ "warnings": warnings,
248
+ }
249
+
250
+ # Check Node.js for Next.js-based stacks
251
+ if stack_type in ["saas_t3", "dashboard_refine", "fullstack_nextjs"]:
252
+ node_ok, node_version = check_node_version()
253
+ result["node_ok"] = node_ok
254
+ result["node_version"] = node_version
255
+
256
+ if not node_ok:
257
+ if auto_update:
258
+ logger.info("Node.js 18+ not found, attempting to install via nvm...")
259
+ success, message = attempt_node_install_with_nvm()
260
+ if success:
261
+ # Re-check after installation
262
+ node_ok, node_version = check_node_version()
263
+ result["node_ok"] = node_ok
264
+ result["node_version"] = node_version
265
+ if node_ok:
266
+ logger.info(f"Node.js {node_version} is now available")
267
+ else:
268
+ errors.append(
269
+ f"Auto-installation succeeded but Node.js still not detected\n{message}"
270
+ )
271
+ else:
272
+ errors.append(message)
273
+ else:
274
+ errors.append(
275
+ f"Node.js 18+ required but {'not installed' if node_version is None else f'found {node_version}'}\n"
276
+ "Install Node.js 18+ and try again"
277
+ )
278
+
279
+ # Check Python for ML/AI stack
280
+ if stack_type == "ml_ai_fastapi":
281
+ python_ok, python_version, python_binary = check_python_version()
282
+ result["python_ok"] = python_ok
283
+ result["python_version"] = python_version
284
+ result["python_binary"] = python_binary
285
+
286
+ if not python_ok:
287
+ # Try to find python3.11 specifically
288
+ python_ok_311, python_version_311, python_binary_311 = check_python_version(
289
+ "python3.11"
290
+ )
291
+ if python_ok_311:
292
+ result["python_ok"] = True
293
+ result["python_version"] = python_version_311
294
+ result["python_binary"] = python_binary_311
295
+ logger.info(f"Found Python {python_version_311} at {python_binary_311}")
296
+ elif auto_update:
297
+ logger.info("Python 3.11+ not found, attempting to install via pyenv...")
298
+ success, message = attempt_python_install_with_pyenv()
299
+ if success:
300
+ # Re-check after installation
301
+ python_ok, python_version, python_binary = check_python_version("python3.11")
302
+ result["python_ok"] = python_ok
303
+ result["python_version"] = python_version
304
+ result["python_binary"] = python_binary
305
+ if python_ok:
306
+ logger.info(f"Python {python_version} is now available")
307
+ else:
308
+ errors.append(
309
+ f"Auto-installation succeeded but Python 3.11+ still not detected\n{message}"
310
+ )
311
+ else:
312
+ errors.append(message)
313
+ else:
314
+ errors.append(
315
+ f"Python 3.11+ required but {'not installed' if python_version is None else f'found {python_version}'}\n"
316
+ "Install Python 3.11+ and try again"
317
+ )
318
+
319
+ # Raise validation error if there are blocking errors
320
+ if errors:
321
+ error_msg = "Environment validation failed:\n\n" + "\n\n".join(errors)
322
+
323
+ raise ValidationError(
324
+ message=error_msg,
325
+ code=ErrorCode.INVALID_CONFIGURATION,
326
+ context={
327
+ "stack_type": stack_type,
328
+ "node_ok": result["node_ok"],
329
+ "python_ok": result["python_ok"],
330
+ },
331
+ remediation="Install required runtime versions and try again",
332
+ )
333
+
334
+ return result
@@ -0,0 +1,71 @@
1
+ """
2
+ Git Hooks Installer Module
3
+
4
+ Installs git hooks from templates.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ import shutil
11
+ import stat
12
+ from pathlib import Path
13
+
14
+ from solokit.core.exceptions import FileOperationError, NotAGitRepoError, TemplateNotFoundError
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ def install_git_hooks(project_root: Path | None = None) -> list[Path]:
20
+ """
21
+ Install git hooks from templates.
22
+
23
+ Args:
24
+ project_root: Root directory of the project. Defaults to current working directory.
25
+
26
+ Returns:
27
+ List of installed hook paths
28
+
29
+ Raises:
30
+ NotAGitRepoError: If .git/hooks directory doesn't exist (git not initialized).
31
+ TemplateNotFoundError: If hook template file is not found.
32
+ FileOperationError: If hook installation or permission setting fails.
33
+ """
34
+ if project_root is None:
35
+ project_root = Path.cwd()
36
+
37
+ git_hooks_dir = project_root / ".git" / "hooks"
38
+
39
+ # Check if .git/hooks exists
40
+ if not git_hooks_dir.exists():
41
+ raise NotAGitRepoError(str(project_root))
42
+
43
+ # Get template directory
44
+ template_dir = Path(__file__).parent.parent / "templates" / "git-hooks"
45
+
46
+ installed_hooks = []
47
+
48
+ # Install prepare-commit-msg hook
49
+ hook_template = template_dir / "prepare-commit-msg"
50
+ hook_dest = git_hooks_dir / "prepare-commit-msg"
51
+
52
+ if not hook_template.exists():
53
+ raise TemplateNotFoundError(
54
+ template_name="prepare-commit-msg", template_path=str(template_dir)
55
+ )
56
+
57
+ try:
58
+ shutil.copy(hook_template, hook_dest)
59
+ # Make executable (chmod +x)
60
+ hook_dest.chmod(hook_dest.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
61
+ installed_hooks.append(hook_dest)
62
+ logger.info("Installed git prepare-commit-msg hook")
63
+ except Exception as e:
64
+ raise FileOperationError(
65
+ operation="install",
66
+ file_path=str(hook_dest),
67
+ details=f"Failed to copy or set permissions: {str(e)}",
68
+ cause=e,
69
+ )
70
+
71
+ return installed_hooks
@@ -0,0 +1,188 @@
1
+ """
2
+ Git Setup Module
3
+
4
+ Handles git initialization and pre-flight validation checks for template-based init.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ from pathlib import Path
11
+
12
+ from solokit.core.command_runner import CommandRunner
13
+ from solokit.core.constants import GIT_QUICK_TIMEOUT
14
+ from solokit.core.exceptions import ErrorCode, GitError, ValidationError
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ def is_blank_project(project_root: Path | None = None) -> tuple[bool, list[str]]:
20
+ """
21
+ Check if the current directory is blank enough for initialization.
22
+
23
+ A blank project can have:
24
+ - .git directory
25
+ - .gitignore, README.md, LICENSE, .gitattributes
26
+ - docs/ directory
27
+ - Empty directories with .gitkeep
28
+
29
+ Args:
30
+ project_root: Root directory to check. Defaults to current working directory.
31
+
32
+ Returns:
33
+ Tuple of (is_blank: bool, blocking_files: list[str])
34
+ - is_blank: True if project is blank/safe to initialize
35
+ - blocking_files: List of files/directories that block initialization
36
+ """
37
+ if project_root is None:
38
+ project_root = Path.cwd()
39
+
40
+ blocking_files: list[str] = []
41
+
42
+ # Check for existing project files that indicate non-blank project
43
+ blocking_file_patterns = [
44
+ "package.json",
45
+ "package-lock.json",
46
+ "yarn.lock",
47
+ "pnpm-lock.yaml",
48
+ "pyproject.toml",
49
+ "setup.py",
50
+ "requirements.txt",
51
+ "Pipfile",
52
+ "poetry.lock",
53
+ "tsconfig.json",
54
+ ".eslintrc.js",
55
+ ".eslintrc.json",
56
+ ".prettierrc",
57
+ "jest.config.js",
58
+ "vitest.config.ts",
59
+ ".session",
60
+ ]
61
+
62
+ # Check for blocking files
63
+ for file_pattern in blocking_file_patterns:
64
+ file_path = project_root / file_pattern
65
+ if file_path.exists():
66
+ # Add description for better error messages
67
+ if file_pattern == "package.json":
68
+ blocking_files.append("package.json (Node.js project detected)")
69
+ elif file_pattern == "pyproject.toml":
70
+ blocking_files.append("pyproject.toml (Python project detected)")
71
+ elif file_pattern == ".session":
72
+ blocking_files.append(".session/ (Solokit already initialized)")
73
+ else:
74
+ blocking_files.append(file_pattern)
75
+
76
+ # Check for source directories (strong signal of existing project)
77
+ blocking_dir_patterns = [
78
+ "src",
79
+ "app",
80
+ "pages",
81
+ "components",
82
+ "lib",
83
+ "utils",
84
+ "node_modules",
85
+ "venv",
86
+ ".venv",
87
+ "__pycache__",
88
+ ]
89
+
90
+ for dir_pattern in blocking_dir_patterns:
91
+ dir_path = project_root / dir_pattern
92
+ if dir_path.exists() and dir_path.is_dir():
93
+ # Check if directory has actual content (not just .gitkeep)
94
+ try:
95
+ contents = list(dir_path.iterdir())
96
+ if contents and not (len(contents) == 1 and contents[0].name == ".gitkeep"):
97
+ blocking_files.append(f"{dir_pattern}/ directory")
98
+ except PermissionError:
99
+ # Can't read directory - treat as blocking
100
+ blocking_files.append(f"{dir_pattern}/ directory (permission denied)")
101
+
102
+ is_blank = len(blocking_files) == 0
103
+ return is_blank, blocking_files
104
+
105
+
106
+ def check_blank_project_or_exit(project_root: Path | None = None) -> None:
107
+ """
108
+ Check if project is blank, raise exception with helpful error message if not.
109
+
110
+ Args:
111
+ project_root: Root directory to check. Defaults to current working directory.
112
+
113
+ Raises:
114
+ ValidationError: If project is not blank with helpful error message and remediation steps.
115
+ """
116
+ is_blank, blocking_files = is_blank_project(project_root)
117
+
118
+ if not is_blank:
119
+ error_msg = (
120
+ "Cannot initialize: Project directory is not blank.\n\n"
121
+ "Found existing project files:\n"
122
+ + "\n".join(f" - {f}" for f in blocking_files)
123
+ + "\n\n"
124
+ "Solokit initialization must be run in a blank project directory to avoid conflicts.\n\n"
125
+ "Solutions:\n"
126
+ " 1. Create a new directory: mkdir my-project && cd my-project\n"
127
+ " 2. Clone an empty repo: git clone <repo-url> && cd <repo>\n"
128
+ " 3. Clear existing project (CAUTION): Remove conflicting files manually\n"
129
+ )
130
+
131
+ raise ValidationError(
132
+ message=error_msg,
133
+ code=ErrorCode.PROJECT_NOT_BLANK,
134
+ context={"existing_files": blocking_files},
135
+ remediation="Use a blank directory for initialization",
136
+ )
137
+
138
+
139
+ def check_or_init_git(project_root: Path | None = None) -> bool:
140
+ """
141
+ Check if git is initialized, if not initialize it.
142
+
143
+ Args:
144
+ project_root: Root directory of the project. Defaults to current working directory.
145
+
146
+ Returns:
147
+ True if git repository exists or was successfully initialized.
148
+
149
+ Raises:
150
+ GitError: If git initialization or branch configuration fails.
151
+
152
+ Note:
153
+ This function logs success messages but raises exceptions on errors.
154
+ """
155
+ if project_root is None:
156
+ project_root = Path.cwd()
157
+
158
+ git_dir = project_root / ".git"
159
+
160
+ if git_dir.exists():
161
+ logger.info("Git repository already initialized")
162
+ return True
163
+
164
+ runner = CommandRunner(default_timeout=GIT_QUICK_TIMEOUT, working_dir=project_root)
165
+
166
+ # Initialize git
167
+ result = runner.run(["git", "init"], check=True)
168
+ if not result.success:
169
+ raise GitError(
170
+ message="Failed to initialize git repository",
171
+ code=ErrorCode.GIT_COMMAND_FAILED,
172
+ context={"stderr": result.stderr, "command": "git init"},
173
+ remediation="Ensure git is installed and you have write permissions in the directory",
174
+ )
175
+ logger.info("Initialized git repository")
176
+
177
+ # Set default branch to main (modern convention)
178
+ result = runner.run(["git", "branch", "-m", "main"], check=True)
179
+ if not result.success:
180
+ raise GitError(
181
+ message="Failed to set default branch to 'main'",
182
+ code=ErrorCode.GIT_COMMAND_FAILED,
183
+ context={"stderr": result.stderr, "command": "git branch -m main"},
184
+ remediation="Manually run 'git branch -m main' in the repository",
185
+ )
186
+ logger.info("Set default branch to 'main'")
187
+
188
+ return True