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,1162 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Deterministic Solokit initialization - transforms any project into working Solokit project.
4
+ Philosophy: Don't check and warn - CREATE and FIX.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ import shutil
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ from solokit.core.command_runner import CommandRunner
16
+ from solokit.core.constants import GIT_QUICK_TIMEOUT, GIT_STANDARD_TIMEOUT
17
+ from solokit.core.exceptions import (
18
+ DirectoryNotEmptyError,
19
+ ErrorCode,
20
+ FileOperationError,
21
+ GitError,
22
+ NotAGitRepoError,
23
+ TemplateNotFoundError,
24
+ ValidationError,
25
+ )
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ # ============================================================================
31
+ # LEGACY INIT FUNCTIONS
32
+ # These functions are kept for backward compatibility with init_project()
33
+ # New template-based init uses modules in src/solokit/init/ instead
34
+ # ============================================================================
35
+
36
+
37
+ def check_or_init_git(project_root: Path | None = None) -> bool:
38
+ """
39
+ Check if git is initialized, if not initialize it.
40
+
41
+ Args:
42
+ project_root: Root directory of the project. Defaults to current working directory.
43
+
44
+ Returns:
45
+ True if git repository exists or was successfully initialized.
46
+
47
+ Raises:
48
+ GitError: If git initialization or branch configuration fails.
49
+
50
+ Note:
51
+ This function prints success messages but does not print errors - it raises exceptions instead.
52
+ """
53
+ if project_root is None:
54
+ project_root = Path.cwd()
55
+
56
+ git_dir = project_root / ".git"
57
+
58
+ if git_dir.exists():
59
+ logger.info("Git repository already initialized")
60
+ return True
61
+
62
+ runner = CommandRunner(default_timeout=GIT_QUICK_TIMEOUT, working_dir=project_root)
63
+
64
+ # Initialize git
65
+ result = runner.run(["git", "init"], check=True)
66
+ if not result.success:
67
+ raise GitError(
68
+ message="Failed to initialize git repository",
69
+ code=ErrorCode.GIT_COMMAND_FAILED,
70
+ context={"stderr": result.stderr, "command": "git init"},
71
+ remediation="Ensure git is installed and you have write permissions in the directory",
72
+ )
73
+ logger.info("Initialized git repository")
74
+
75
+ # Set default branch to main (modern convention)
76
+ result = runner.run(["git", "branch", "-m", "main"], check=True)
77
+ if not result.success:
78
+ raise GitError(
79
+ message="Failed to set default branch to 'main'",
80
+ code=ErrorCode.GIT_COMMAND_FAILED,
81
+ context={"stderr": result.stderr, "command": "git branch -m main"},
82
+ remediation="Manually run 'git branch -m main' in the repository",
83
+ )
84
+ logger.info("Set default branch to 'main'")
85
+
86
+ return True
87
+
88
+
89
+ def install_git_hooks(project_root: Path | None = None) -> bool:
90
+ """
91
+ Install git hooks from templates.
92
+
93
+ Args:
94
+ project_root: Root directory of the project. Defaults to current working directory.
95
+
96
+ Returns:
97
+ True if git hooks were successfully installed.
98
+
99
+ Raises:
100
+ NotAGitRepoError: If .git/hooks directory doesn't exist (git not initialized).
101
+ TemplateNotFoundError: If hook template file is not found.
102
+ FileOperationError: If hook installation or permission setting fails.
103
+ """
104
+ if project_root is None:
105
+ project_root = Path.cwd()
106
+
107
+ git_hooks_dir = project_root / ".git" / "hooks"
108
+
109
+ # Check if .git/hooks exists
110
+ if not git_hooks_dir.exists():
111
+ raise NotAGitRepoError(str(project_root))
112
+
113
+ # Get template directory
114
+ template_dir = Path(__file__).parent.parent / "templates" / "git-hooks"
115
+
116
+ # Install prepare-commit-msg hook
117
+ hook_template = template_dir / "prepare-commit-msg"
118
+ hook_dest = git_hooks_dir / "prepare-commit-msg"
119
+
120
+ if not hook_template.exists():
121
+ raise TemplateNotFoundError(
122
+ template_name="prepare-commit-msg", template_path=str(template_dir)
123
+ )
124
+
125
+ try:
126
+ shutil.copy(hook_template, hook_dest)
127
+ # Make executable (chmod +x)
128
+ import stat
129
+
130
+ hook_dest.chmod(hook_dest.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
131
+ logger.info("Installed git prepare-commit-msg hook")
132
+ return True
133
+ except Exception as e:
134
+ raise FileOperationError(
135
+ operation="install",
136
+ file_path=str(hook_dest),
137
+ details=f"Failed to copy or set permissions: {str(e)}",
138
+ cause=e,
139
+ )
140
+
141
+
142
+ def detect_project_type() -> str:
143
+ """Detect project type from existing files."""
144
+ if Path("package.json").exists():
145
+ if Path("tsconfig.json").exists():
146
+ return "typescript"
147
+ return "javascript"
148
+ elif Path("pyproject.toml").exists() or Path("setup.py").exists():
149
+ return "python"
150
+ else:
151
+ # No project files found - ask user
152
+ logger.info("\nNo project files detected. What type of project is this?")
153
+ logger.info("1. TypeScript")
154
+ logger.info("2. JavaScript")
155
+ logger.info("3. Python")
156
+
157
+ if not sys.stdin.isatty():
158
+ # Non-interactive mode - default to TypeScript
159
+ logger.info("Non-interactive mode: defaulting to TypeScript")
160
+ return "typescript"
161
+
162
+ choice = input("Enter choice (1-3): ").strip()
163
+ return {"1": "typescript", "2": "javascript", "3": "python"}.get(choice, "typescript")
164
+
165
+
166
+ def ensure_package_manager_file(project_type: str) -> None:
167
+ """
168
+ Create or update package manager file with required dependencies.
169
+
170
+ Args:
171
+ project_type: Type of project (typescript, javascript, or python).
172
+
173
+ Raises:
174
+ ValidationError: If project_type is invalid.
175
+ TemplateNotFoundError: If required template file is not found.
176
+ FileOperationError: If file read/write operations fail.
177
+
178
+ Note:
179
+ Prints informational messages about created/updated files but raises exceptions on errors.
180
+ """
181
+ if project_type not in ["typescript", "javascript", "python"]:
182
+ raise ValidationError(
183
+ message=f"Invalid project type: {project_type}",
184
+ code=ErrorCode.INVALID_WORK_ITEM_TYPE,
185
+ context={"project_type": project_type},
186
+ remediation="Use 'typescript', 'javascript', or 'python'",
187
+ )
188
+
189
+ template_dir = Path(__file__).parent.parent / "templates"
190
+
191
+ if project_type in ["typescript", "javascript"]:
192
+ package_json = Path("package.json")
193
+
194
+ if not package_json.exists():
195
+ logger.info("Creating package.json...")
196
+ # Get project name from directory
197
+ project_name = Path.cwd().name
198
+ project_desc = f"A {project_type} project with Session-Driven Development"
199
+
200
+ # Load template and replace placeholders
201
+ template_path = template_dir / "package.json.template"
202
+ if not template_path.exists():
203
+ raise TemplateNotFoundError(
204
+ template_name="package.json.template", template_path=str(template_dir)
205
+ )
206
+
207
+ try:
208
+ template_content = template_path.read_text()
209
+ content = template_content.replace("{project_name}", project_name)
210
+ content = content.replace("{project_description}", project_desc)
211
+ package_json.write_text(content)
212
+ logger.info(f"Created package.json for {project_name}")
213
+ except Exception as e:
214
+ raise FileOperationError(
215
+ operation="create",
216
+ file_path=str(package_json),
217
+ details=f"Failed to create package.json from template: {str(e)}",
218
+ cause=e,
219
+ )
220
+ else:
221
+ logger.info("Found package.json")
222
+ # Ensure required scripts and devDependencies exist
223
+ try:
224
+ with open(package_json) as f:
225
+ data = json.load(f)
226
+ except json.JSONDecodeError as e:
227
+ raise FileOperationError(
228
+ operation="parse",
229
+ file_path=str(package_json),
230
+ details=f"Invalid JSON in package.json: {str(e)}",
231
+ cause=e,
232
+ )
233
+
234
+ # Ensure scripts
235
+ required_scripts = {
236
+ "test": "jest",
237
+ "lint": "eslint src tests --ext .ts,.tsx,.js,.jsx"
238
+ if project_type == "typescript"
239
+ else "eslint src tests --ext .js,.jsx",
240
+ "format": 'prettier --write "src/**/*" "tests/**/*"',
241
+ }
242
+ if project_type == "typescript":
243
+ required_scripts["build"] = "tsc"
244
+
245
+ if "scripts" not in data:
246
+ data["scripts"] = {}
247
+
248
+ scripts_modified = False
249
+ for script, cmd in required_scripts.items():
250
+ if script not in data["scripts"]:
251
+ data["scripts"][script] = cmd
252
+ logger.info(f"Added script: {script}")
253
+ scripts_modified = True
254
+
255
+ # Ensure devDependencies
256
+ if "devDependencies" not in data:
257
+ data["devDependencies"] = {}
258
+
259
+ # Common dependencies for all JS/TS projects
260
+ required_deps = {
261
+ "jest": "^29.5.0",
262
+ "prettier": "^3.0.0",
263
+ "eslint": "^8.40.0",
264
+ "@types/jest": "^29.5.0",
265
+ "@types/node": "^20.0.0",
266
+ }
267
+
268
+ # TypeScript-specific dependencies
269
+ if project_type == "typescript":
270
+ required_deps.update(
271
+ {
272
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
273
+ "@typescript-eslint/parser": "^6.0.0",
274
+ "ts-jest": "^29.1.0",
275
+ "typescript": "^5.0.0",
276
+ }
277
+ )
278
+
279
+ deps_modified = False
280
+ for pkg, version in required_deps.items():
281
+ if pkg not in data["devDependencies"]:
282
+ data["devDependencies"][pkg] = version
283
+ logger.info(f"Added devDependency: {pkg}")
284
+ deps_modified = True
285
+
286
+ # Save back only if modified
287
+ if scripts_modified or deps_modified:
288
+ try:
289
+ with open(package_json, "w") as f:
290
+ json.dump(data, f, indent=2)
291
+ if deps_modified:
292
+ logger.info("Run 'npm install' to install new dependencies")
293
+ except Exception as e:
294
+ raise FileOperationError(
295
+ operation="write",
296
+ file_path=str(package_json),
297
+ details=f"Failed to save package.json: {str(e)}",
298
+ cause=e,
299
+ )
300
+
301
+ elif project_type == "python":
302
+ pyproject = Path("pyproject.toml")
303
+
304
+ if not pyproject.exists():
305
+ logger.info("Creating pyproject.toml...")
306
+ project_name = Path.cwd().name.replace("-", "_")
307
+ project_desc = "A Python project with Session-Driven Development"
308
+
309
+ template_path = template_dir / "pyproject.toml.template"
310
+ if not template_path.exists():
311
+ raise TemplateNotFoundError(
312
+ template_name="pyproject.toml.template", template_path=str(template_dir)
313
+ )
314
+
315
+ try:
316
+ template_content = template_path.read_text()
317
+ content = template_content.replace("{project_name}", project_name)
318
+ content = template_content.replace("{project_description}", project_desc)
319
+ pyproject.write_text(content)
320
+ logger.info(f"Created pyproject.toml for {project_name}")
321
+ except Exception as e:
322
+ raise FileOperationError(
323
+ operation="create",
324
+ file_path=str(pyproject),
325
+ details=f"Failed to create pyproject.toml from template: {str(e)}",
326
+ cause=e,
327
+ )
328
+ else:
329
+ logger.info("Found pyproject.toml")
330
+ # Check if it has dev dependencies section
331
+ try:
332
+ content = pyproject.read_text()
333
+ if "[project.optional-dependencies]" not in content and "dev" not in content:
334
+ logger.info(
335
+ "Note: Add [project.optional-dependencies] section with pytest, pytest-cov, ruff"
336
+ )
337
+ logger.info("Or install manually: pip install pytest pytest-cov ruff")
338
+ except Exception as e:
339
+ raise FileOperationError(
340
+ operation="read",
341
+ file_path=str(pyproject),
342
+ details=f"Failed to read pyproject.toml: {str(e)}",
343
+ cause=e,
344
+ )
345
+
346
+
347
+ def ensure_config_files(project_type: str) -> None:
348
+ """
349
+ Create all required config files from templates.
350
+
351
+ Args:
352
+ project_type: Type of project (typescript, javascript, or python).
353
+
354
+ Raises:
355
+ FileOperationError: If file copy operation fails.
356
+
357
+ Note:
358
+ Prints informational messages about created/found files.
359
+ Missing templates are silently skipped (no error raised).
360
+ """
361
+ template_dir = Path(__file__).parent.parent / "templates"
362
+
363
+ # Common configs
364
+ configs_to_create = [
365
+ ("CHANGELOG.md", "CHANGELOG.md"),
366
+ ]
367
+
368
+ if project_type in ["typescript", "javascript"]:
369
+ configs_to_create.extend(
370
+ [
371
+ (".eslintrc.json", ".eslintrc.json"),
372
+ (".prettierrc.json", ".prettierrc.json"),
373
+ (".prettierignore", ".prettierignore"),
374
+ ]
375
+ )
376
+
377
+ # Use correct jest config based on project type
378
+ if project_type == "typescript":
379
+ configs_to_create.append(("jest.config.js", "jest.config.js"))
380
+ configs_to_create.append(("tsconfig.json", "tsconfig.json"))
381
+ else: # javascript
382
+ configs_to_create.append(("jest.config.js.javascript", "jest.config.js"))
383
+
384
+ for template_name, dest_name in configs_to_create:
385
+ dest_path = Path(dest_name)
386
+ if not dest_path.exists():
387
+ template_path = template_dir / template_name
388
+ if template_path.exists():
389
+ try:
390
+ shutil.copy(template_path, dest_path)
391
+ logger.info(f"Created {dest_name}")
392
+ except Exception as e:
393
+ raise FileOperationError(
394
+ operation="copy",
395
+ file_path=str(dest_path),
396
+ details=f"Failed to copy config file: {str(e)}",
397
+ cause=e,
398
+ )
399
+ else:
400
+ logger.info(f"Found {dest_name}")
401
+
402
+
403
+ def install_dependencies(project_type: str) -> None:
404
+ """
405
+ Install project dependencies.
406
+
407
+ Args:
408
+ project_type: Type of project (typescript, javascript, or python).
409
+
410
+ Raises:
411
+ CommandExecutionError: If dependency installation command fails critically.
412
+
413
+ Note:
414
+ Prints informational messages. Some failures are logged as warnings rather than exceptions
415
+ to allow the initialization to continue (user can manually install dependencies later).
416
+ """
417
+ if project_type in ["typescript", "javascript"]:
418
+ # Always run npm install to ensure new devDependencies are installed
419
+ logger.info("\nInstalling npm dependencies...")
420
+ try:
421
+ runner = CommandRunner(default_timeout=300)
422
+ result = runner.run(["npm", "install"], check=True)
423
+ if result.success:
424
+ logger.info("Dependencies installed")
425
+ else:
426
+ logger.warning("npm install failed - you may need to run it manually")
427
+ except Exception as e:
428
+ logger.warning(f"npm install failed: {e} - you may need to run it manually")
429
+
430
+ elif project_type == "python":
431
+ # Check if we're in a venv, if not create one
432
+ if not (Path("venv").exists() or Path(".venv").exists()):
433
+ logger.info("\nCreating Python virtual environment...")
434
+ try:
435
+ runner = CommandRunner(default_timeout=60)
436
+ result = runner.run([sys.executable, "-m", "venv", "venv"], check=True)
437
+ if result.success:
438
+ logger.info("Created venv/")
439
+ logger.info(
440
+ "Activate with: source venv/bin/activate (Unix) or venv\\Scripts\\activate (Windows)"
441
+ )
442
+ else:
443
+ logger.warning("venv creation failed")
444
+ return
445
+ except Exception as e:
446
+ logger.warning(f"venv creation failed: {e}")
447
+ return
448
+
449
+ # Try to install dev dependencies
450
+ logger.info("\nInstalling Python dependencies...")
451
+ pip_cmd = "venv/bin/pip" if Path("venv").exists() else ".venv/bin/pip"
452
+ if Path(pip_cmd).exists():
453
+ try:
454
+ runner = CommandRunner(default_timeout=300)
455
+ result = runner.run([pip_cmd, "install", "-e", ".[dev]"], check=True)
456
+ if result.success:
457
+ logger.info("Dependencies installed")
458
+ else:
459
+ logger.warning(
460
+ "pip install failed - you may need to activate venv and install manually"
461
+ )
462
+ except Exception as e:
463
+ logger.warning(
464
+ f"pip install failed: {e} - you may need to activate venv and install manually"
465
+ )
466
+ else:
467
+ logger.warning("Please activate virtual environment and run: pip install -e .[dev]")
468
+
469
+
470
+ def create_smoke_tests(project_type: str) -> None:
471
+ """
472
+ Create initial smoke tests that validate Solokit setup.
473
+
474
+ Args:
475
+ project_type: Type of project (typescript, javascript, or python).
476
+
477
+ Raises:
478
+ FileOperationError: If test directory creation or file copy fails.
479
+
480
+ Note:
481
+ Prints informational messages about created/found test files.
482
+ Missing templates are silently skipped (no error raised).
483
+ """
484
+ template_dir = Path(__file__).parent.parent / "templates" / "tests"
485
+ test_dir = Path("tests")
486
+
487
+ try:
488
+ test_dir.mkdir(exist_ok=True)
489
+ except Exception as e:
490
+ raise FileOperationError(
491
+ operation="create",
492
+ file_path=str(test_dir),
493
+ details=f"Failed to create tests directory: {str(e)}",
494
+ cause=e,
495
+ )
496
+
497
+ if project_type == "typescript":
498
+ test_file = test_dir / "solokit-setup.test.ts"
499
+ template_name = "solokit-setup.test.ts"
500
+ if not test_file.exists():
501
+ template_file = template_dir / template_name
502
+ if template_file.exists():
503
+ try:
504
+ shutil.copy(template_file, test_file)
505
+ logger.info(f"Created smoke tests: {test_file}")
506
+ except Exception as e:
507
+ raise FileOperationError(
508
+ operation="copy",
509
+ file_path=str(test_file),
510
+ details=f"Failed to copy smoke test template: {str(e)}",
511
+ cause=e,
512
+ )
513
+ else:
514
+ logger.info(f"Found {test_file}")
515
+
516
+ elif project_type == "javascript":
517
+ test_file = test_dir / "solokit-setup.test.js"
518
+ template_name = "solokit-setup.test.js"
519
+ if not test_file.exists():
520
+ template_file = template_dir / template_name
521
+ if template_file.exists():
522
+ try:
523
+ shutil.copy(template_file, test_file)
524
+ logger.info(f"Created smoke tests: {test_file}")
525
+ except Exception as e:
526
+ raise FileOperationError(
527
+ operation="copy",
528
+ file_path=str(test_file),
529
+ details=f"Failed to copy smoke test template: {str(e)}",
530
+ cause=e,
531
+ )
532
+ else:
533
+ logger.info(f"Found {test_file}")
534
+
535
+ elif project_type == "python":
536
+ test_file = test_dir / "test_sdd_setup.py"
537
+ if not test_file.exists():
538
+ template_file = template_dir / "test_sdd_setup.py"
539
+ if template_file.exists():
540
+ try:
541
+ shutil.copy(template_file, test_file)
542
+ logger.info(f"Created smoke tests: {test_file}")
543
+ except Exception as e:
544
+ raise FileOperationError(
545
+ operation="copy",
546
+ file_path=str(test_file),
547
+ details=f"Failed to copy smoke test template: {str(e)}",
548
+ cause=e,
549
+ )
550
+ else:
551
+ logger.info(f"Found {test_file}")
552
+
553
+
554
+ def create_session_structure() -> None:
555
+ """
556
+ Create .session directory structure.
557
+
558
+ Raises:
559
+ FileOperationError: If directory creation fails.
560
+
561
+ Note:
562
+ Prints informational messages about created directories.
563
+ """
564
+ session_dir = Path(".session")
565
+
566
+ logger.info("\nCreating .session/ structure...")
567
+
568
+ # Create directories
569
+ try:
570
+ (session_dir / "tracking").mkdir(parents=True)
571
+ (session_dir / "briefings").mkdir(parents=True)
572
+ (session_dir / "history").mkdir(parents=True)
573
+ (session_dir / "specs").mkdir(parents=True)
574
+ except Exception as e:
575
+ raise FileOperationError(
576
+ operation="create",
577
+ file_path=str(session_dir),
578
+ details=f"Failed to create .session directory structure: {str(e)}",
579
+ cause=e,
580
+ )
581
+
582
+ logger.info("Created .session/tracking/")
583
+ logger.info("Created .session/briefings/")
584
+ logger.info("Created .session/history/")
585
+ logger.info("Created .session/specs/")
586
+
587
+
588
+ def initialize_tracking_files() -> None:
589
+ """
590
+ Initialize tracking files from templates.
591
+
592
+ Raises:
593
+ FileOperationError: If file operations fail.
594
+ TemplateNotFoundError: If required template files are missing.
595
+
596
+ Note:
597
+ Creates tracking files, config.json, and schema files in .session directory.
598
+ """
599
+ session_dir = Path(".session")
600
+ template_dir = Path(__file__).parent.parent / "templates"
601
+
602
+ logger.info("\nInitializing tracking files...")
603
+
604
+ # Copy templates
605
+ tracking_files = [
606
+ ("work_items.json", "tracking/work_items.json"),
607
+ ("learnings.json", "tracking/learnings.json"),
608
+ ("status_update.json", "tracking/status_update.json"),
609
+ ]
610
+
611
+ for src, dst in tracking_files:
612
+ src_path = template_dir / src
613
+ dst_path = session_dir / dst
614
+ if src_path.exists():
615
+ try:
616
+ shutil.copy(src_path, dst_path)
617
+ logger.info(f"Created {dst}")
618
+ except Exception as e:
619
+ raise FileOperationError(
620
+ operation="copy",
621
+ file_path=str(dst_path),
622
+ details=f"Failed to copy tracking file template: {str(e)}",
623
+ cause=e,
624
+ )
625
+
626
+ # Create empty files for stack and tree tracking
627
+ try:
628
+ (session_dir / "tracking" / "stack_updates.json").write_text(
629
+ json.dumps({"updates": []}, indent=2)
630
+ )
631
+ logger.info("Created stack_updates.json")
632
+
633
+ (session_dir / "tracking" / "tree_updates.json").write_text(
634
+ json.dumps({"updates": []}, indent=2)
635
+ )
636
+ logger.info("Created tree_updates.json")
637
+ except Exception as e:
638
+ raise FileOperationError(
639
+ operation="create",
640
+ file_path=str(session_dir / "tracking"),
641
+ details=f"Failed to create tracking files: {str(e)}",
642
+ cause=e,
643
+ )
644
+
645
+ # Create config.json with default settings
646
+ config_data = {
647
+ "curation": {
648
+ "auto_curate": True,
649
+ "frequency": 5,
650
+ "dry_run": False,
651
+ "similarity_threshold": 0.7,
652
+ "categories": [
653
+ "architecture_patterns",
654
+ "gotchas",
655
+ "best_practices",
656
+ "technical_debt",
657
+ "performance_insights",
658
+ "security",
659
+ ],
660
+ },
661
+ "quality_gates": {
662
+ "test_execution": {
663
+ "enabled": True,
664
+ "required": True,
665
+ "coverage_threshold": 80,
666
+ "commands": {
667
+ "python": "pytest --cov --cov-report=json",
668
+ "javascript": "npm test -- --coverage",
669
+ "typescript": "npm test -- --coverage",
670
+ },
671
+ },
672
+ "linting": {
673
+ "enabled": True,
674
+ "required": True,
675
+ "auto_fix": True,
676
+ "commands": {
677
+ "python": "ruff check .",
678
+ "javascript": "npx eslint . --ext .js,.jsx",
679
+ "typescript": "npx eslint . --ext .ts,.tsx",
680
+ },
681
+ },
682
+ "formatting": {
683
+ "enabled": True,
684
+ "required": True,
685
+ "auto_fix": True,
686
+ "commands": {
687
+ "python": "ruff format .",
688
+ "javascript": "npx prettier .",
689
+ "typescript": "npx prettier .",
690
+ },
691
+ },
692
+ "security": {"enabled": True, "required": True, "fail_on": "high"},
693
+ "documentation": {
694
+ "enabled": True,
695
+ "required": True,
696
+ "check_changelog": True,
697
+ "check_docstrings": True,
698
+ "check_readme": False,
699
+ },
700
+ "context7": {
701
+ "enabled": False,
702
+ "required": True,
703
+ "important_libraries": [],
704
+ },
705
+ "custom_validations": {"rules": []},
706
+ },
707
+ "integration_tests": {
708
+ "enabled": True,
709
+ "docker_compose_file": "docker-compose.integration.yml",
710
+ "environment_validation": True,
711
+ "health_check_timeout": 300,
712
+ "test_data_fixtures": True,
713
+ "parallel_execution": True,
714
+ "performance_benchmarks": {
715
+ "enabled": True,
716
+ "required": True,
717
+ "regression_threshold": 0.10,
718
+ "baseline_storage": ".session/tracking/performance_baselines.json",
719
+ "load_test_tool": "wrk",
720
+ "metrics": ["response_time", "throughput", "resource_usage"],
721
+ },
722
+ "api_contracts": {
723
+ "enabled": True,
724
+ "required": True,
725
+ "contract_format": "openapi",
726
+ "breaking_change_detection": True,
727
+ "version_storage": ".session/tracking/api_contracts/",
728
+ "fail_on_breaking_changes": True,
729
+ },
730
+ "documentation": {
731
+ "architecture_diagrams": True,
732
+ "sequence_diagrams": True,
733
+ "contract_documentation": True,
734
+ "performance_baseline_docs": True,
735
+ },
736
+ },
737
+ "git_workflow": {
738
+ "mode": "pr",
739
+ "auto_push": True,
740
+ "auto_create_pr": True,
741
+ "delete_branch_after_merge": True,
742
+ "pr_title_template": "{type}: {title}",
743
+ "pr_body_template": "## Summary\n\n{description}\n\n## Work Item\n- ID: {work_item_id}\n- Type: {type}\n- Session: {session_num}\n\n## Changes\n{commit_messages}\n\nšŸ¤– Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>",
744
+ },
745
+ }
746
+ try:
747
+ (session_dir / "config.json").write_text(json.dumps(config_data, indent=2))
748
+ logger.info("Created config.json")
749
+ except Exception as e:
750
+ raise FileOperationError(
751
+ operation="create",
752
+ file_path=str(session_dir / "config.json"),
753
+ details=f"Failed to create config.json: {str(e)}",
754
+ cause=e,
755
+ )
756
+
757
+ # Copy config schema file
758
+ schema_source = Path(__file__).parent.parent / "templates" / "config.schema.json"
759
+ schema_dest = session_dir / "config.schema.json"
760
+
761
+ if schema_source.exists() and not schema_dest.exists():
762
+ try:
763
+ shutil.copy(schema_source, schema_dest)
764
+ logger.info("Created config.schema.json")
765
+ except Exception as e:
766
+ raise FileOperationError(
767
+ operation="copy",
768
+ file_path=str(schema_dest),
769
+ details=f"Failed to copy config schema: {str(e)}",
770
+ cause=e,
771
+ )
772
+
773
+
774
+ def run_initial_scans() -> None:
775
+ """
776
+ Run initial stack and tree scans with FIXED path resolution (Bug #12).
777
+
778
+ Raises:
779
+ None - failures are logged as warnings to allow initialization to continue.
780
+
781
+ Note:
782
+ Uses absolute paths to stack.py and tree.py scripts.
783
+ Failures don't block initialization - user can generate these later.
784
+ """
785
+ logger.info("\nGenerating project context...")
786
+
787
+ # Get Solokit installation directory
788
+ script_dir = Path(__file__).parent
789
+ runner = CommandRunner(default_timeout=GIT_STANDARD_TIMEOUT)
790
+
791
+ # Run stack.py with absolute path
792
+ try:
793
+ result = runner.run(["python", str(script_dir / "stack.py")], check=True)
794
+ if result.success:
795
+ logger.info("Generated stack.txt")
796
+ else:
797
+ logger.warning("Could not generate stack.txt")
798
+ if result.stderr:
799
+ logger.warning(f"Error: {result.stderr.strip()}")
800
+ except Exception as e:
801
+ logger.warning(f"Stack generation failed: {e}")
802
+
803
+ # Run tree.py with absolute path
804
+ try:
805
+ result = runner.run(["python", str(script_dir / "tree.py")], check=True)
806
+ if result.success:
807
+ logger.info("Generated tree.txt")
808
+ else:
809
+ logger.warning("Could not generate tree.txt")
810
+ if result.stderr:
811
+ logger.warning(f"Error: {result.stderr.strip()}")
812
+ except Exception as e:
813
+ logger.warning(f"Tree generation failed: {e}")
814
+
815
+
816
+ def ensure_gitignore_entries() -> None:
817
+ """
818
+ Add .session patterns and OS-specific files to .gitignore.
819
+
820
+ Raises:
821
+ FileOperationError: If .gitignore read/write operations fail.
822
+
823
+ Note:
824
+ Prints informational messages about updated .gitignore.
825
+ """
826
+ gitignore = Path(".gitignore")
827
+
828
+ required_entries = [
829
+ ".session/briefings/",
830
+ ".session/history/",
831
+ "coverage/",
832
+ "coverage.json",
833
+ "node_modules/",
834
+ "dist/",
835
+ "venv/",
836
+ ".venv/",
837
+ "*.pyc",
838
+ "__pycache__/",
839
+ ]
840
+
841
+ # OS-specific files
842
+ os_specific_entries = [
843
+ ".DS_Store # macOS",
844
+ ".DS_Store? # macOS",
845
+ "._* # macOS resource forks",
846
+ ".Spotlight-V100 # macOS",
847
+ ".Trashes # macOS",
848
+ "Thumbs.db # Windows",
849
+ "ehthumbs.db # Windows",
850
+ "Desktop.ini # Windows",
851
+ "$RECYCLE.BIN/ # Windows",
852
+ "*~ # Linux backup files",
853
+ ]
854
+
855
+ try:
856
+ existing_content = gitignore.read_text() if gitignore.exists() else ""
857
+ except Exception as e:
858
+ raise FileOperationError(
859
+ operation="read",
860
+ file_path=str(gitignore),
861
+ details=f"Failed to read .gitignore: {str(e)}",
862
+ cause=e,
863
+ )
864
+
865
+ entries_to_add = []
866
+ for entry in required_entries:
867
+ if entry not in existing_content:
868
+ entries_to_add.append(entry)
869
+
870
+ # First pass: check which patterns need to be added
871
+ os_patterns_needed = []
872
+ for entry in os_specific_entries:
873
+ # Skip comment-only lines in first pass
874
+ pattern = entry.split("#")[0].strip()
875
+ if pattern and pattern not in existing_content:
876
+ os_patterns_needed.append(entry)
877
+
878
+ # If we need to add any OS patterns, include the header
879
+ os_entries_to_add = []
880
+ if os_patterns_needed:
881
+ # Add the section header first
882
+ os_entries_to_add.append("# OS-specific files")
883
+ # Then add all the patterns
884
+ os_entries_to_add.extend(os_patterns_needed)
885
+
886
+ if entries_to_add or os_entries_to_add:
887
+ logger.info("\nUpdating .gitignore...")
888
+ try:
889
+ with open(gitignore, "a") as f:
890
+ if existing_content and not existing_content.endswith("\n"):
891
+ f.write("\n")
892
+
893
+ if entries_to_add:
894
+ f.write("\n# Solokit-related patterns\n")
895
+ for entry in entries_to_add:
896
+ f.write(f"{entry}\n")
897
+
898
+ if os_entries_to_add:
899
+ f.write("\n")
900
+ for entry in os_entries_to_add:
901
+ f.write(f"{entry}\n")
902
+
903
+ total_added = len(entries_to_add) + len(
904
+ [e for e in os_entries_to_add if not e.startswith("#")]
905
+ )
906
+ logger.info(f"Added {total_added} entries to .gitignore")
907
+ except Exception as e:
908
+ raise FileOperationError(
909
+ operation="write",
910
+ file_path=str(gitignore),
911
+ details=f"Failed to update .gitignore: {str(e)}",
912
+ cause=e,
913
+ )
914
+ else:
915
+ logger.info(".gitignore already up to date")
916
+
917
+
918
+ def create_initial_commit(project_root: Path | None = None) -> bool:
919
+ """
920
+ Create initial commit after project initialization.
921
+
922
+ Args:
923
+ project_root: Root directory of the project. Defaults to current working directory.
924
+
925
+ Returns:
926
+ True if initial commit was created or already exists.
927
+
928
+ Raises:
929
+ GitError: If git add or commit operations fail critically.
930
+
931
+ Note:
932
+ Prints informational messages. Some failures are logged as warnings to allow
933
+ the initialization to complete (user can commit manually later).
934
+ """
935
+ if project_root is None:
936
+ project_root = Path.cwd()
937
+
938
+ runner = CommandRunner(default_timeout=GIT_STANDARD_TIMEOUT, working_dir=project_root)
939
+
940
+ try:
941
+ # Check if repository has any commits by trying to count them
942
+ # This will fail gracefully if no commits exist yet
943
+ result = runner.run(["git", "rev-list", "--count", "--all"], check=False)
944
+
945
+ if result.success and result.stdout.strip() and int(result.stdout.strip()) > 0:
946
+ logger.info("Git repository already has commits, skipping initial commit")
947
+ return True
948
+
949
+ except Exception:
950
+ # If command fails (e.g., no commits yet), continue to create initial commit
951
+ pass
952
+
953
+ try:
954
+ # Stage all initialized files
955
+ result = runner.run(["git", "add", "-A"], check=True)
956
+ if not result.success:
957
+ logger.warning(f"Git add failed: {result.stderr}")
958
+ logger.warning("You may need to commit manually before starting sessions")
959
+ return False
960
+
961
+ # Create initial commit
962
+ commit_message = """chore: Initialize project with Session-Driven Development
963
+
964
+ Project initialized with Solokit framework including:
965
+ - Project structure and configuration files
966
+ - Quality gates and testing setup
967
+ - Session tracking infrastructure
968
+ - Documentation templates
969
+
970
+ šŸ¤– Generated with [Claude Code](https://claude.com/claude-code)
971
+
972
+ Co-Authored-By: Claude <noreply@anthropic.com>"""
973
+
974
+ result = runner.run(["git", "commit", "-m", commit_message], check=True)
975
+ if not result.success:
976
+ logger.warning(f"Git commit failed: {result.stderr}")
977
+ logger.warning("You may need to commit manually before starting sessions")
978
+ return False
979
+
980
+ logger.info("Created initial commit on main branch")
981
+ return True
982
+
983
+ except Exception as e:
984
+ logger.warning(f"Failed to create initial commit: {e}")
985
+ logger.warning("You may need to commit manually before starting sessions")
986
+ return False
987
+
988
+
989
+ def init_project() -> int:
990
+ """
991
+ LEGACY: Basic initialization function without templates.
992
+
993
+ This function is deprecated in favor of template-based initialization.
994
+ Use `sk init --template=<template> --tier=<tier> --coverage=<coverage>` instead.
995
+
996
+ Returns:
997
+ 0 on success, 1 if already initialized, or raises exception on critical errors.
998
+
999
+ Raises:
1000
+ DirectoryNotEmptyError: If .session directory already exists.
1001
+ GitError: If git operations fail critically.
1002
+ FileOperationError: If file operations fail critically.
1003
+ ValidationError: If configuration or validation fails.
1004
+
1005
+ Note:
1006
+ Some non-critical failures (like dependency installation) are logged as warnings
1007
+ and don't stop the initialization process. The user can fix these manually.
1008
+ All output is now through logger instead of print().
1009
+ """
1010
+ logger.warning("āš ļø Using legacy initialization mode")
1011
+ logger.warning("āš ļø Consider using template-based init for better experience")
1012
+ logger.warning("")
1013
+ logger.info("šŸš€ Initializing Session-Driven Development...\n")
1014
+
1015
+ # 1. Check if already initialized
1016
+ if Path(".session").exists():
1017
+ raise DirectoryNotEmptyError(".session")
1018
+
1019
+ # 2. Check or initialize git repository
1020
+ check_or_init_git()
1021
+
1022
+ # 3. Install git hooks
1023
+ install_git_hooks()
1024
+ logger.info("")
1025
+
1026
+ # 4. Detect project type
1027
+ project_type = detect_project_type()
1028
+ logger.info(f"\nšŸ“¦ Project type: {project_type}\n")
1029
+
1030
+ # 5. Ensure package manager file (create/update)
1031
+ ensure_package_manager_file(project_type)
1032
+
1033
+ # 6. Ensure all config files (create from templates)
1034
+ logger.info("")
1035
+ ensure_config_files(project_type)
1036
+
1037
+ # 7. Install dependencies
1038
+ logger.info("")
1039
+ install_dependencies(project_type)
1040
+
1041
+ # 8. Create smoke tests
1042
+ logger.info("")
1043
+ create_smoke_tests(project_type)
1044
+
1045
+ # 9. Create .session structure
1046
+ create_session_structure()
1047
+
1048
+ # 10. Initialize tracking files
1049
+ initialize_tracking_files()
1050
+
1051
+ # 11. Generate project context (stack/tree)
1052
+ run_initial_scans()
1053
+
1054
+ # 12. Update .gitignore
1055
+ logger.info("")
1056
+ ensure_gitignore_entries()
1057
+
1058
+ # 13. Create initial commit
1059
+ logger.info("")
1060
+ create_initial_commit()
1061
+
1062
+ # Success summary
1063
+ logger.info("\n" + "=" * 60)
1064
+ logger.info("āœ… Solokit Initialized Successfully!")
1065
+ logger.info("=" * 60)
1066
+
1067
+ logger.info("\nšŸ“¦ What was created/updated:")
1068
+ logger.info(" āœ“ Git repository initialized with initial commit")
1069
+ logger.info(" āœ“ Git hooks (prepare-commit-msg with CHANGELOG/LEARNING reminders)")
1070
+ logger.info(" āœ“ Config files (.eslintrc, .prettierrc, jest.config, etc.)")
1071
+ logger.info(" āœ“ Dependencies installed")
1072
+ logger.info(" āœ“ Smoke tests created")
1073
+ logger.info(" āœ“ .session/ structure with tracking files")
1074
+ logger.info(" āœ“ Project context (stack.txt, tree.txt)")
1075
+ logger.info(" āœ“ .gitignore updated")
1076
+
1077
+ logger.info("\nšŸš€ Next Step:")
1078
+ logger.info(" /sk:work-new")
1079
+ logger.info("")
1080
+
1081
+ return 0
1082
+
1083
+
1084
+ # ============================================================================
1085
+ # NEW TEMPLATE-BASED INIT (PRIMARY ENTRY POINT)
1086
+ # ============================================================================
1087
+
1088
+
1089
+ def main() -> int:
1090
+ """
1091
+ Main entry point for init command with template-based initialization.
1092
+
1093
+ Handles CLI argument parsing and routes to appropriate init function:
1094
+ - With arguments (--template, --tier, etc.): Template-based init
1095
+ - Without arguments (legacy): Basic init (deprecated)
1096
+
1097
+ Returns:
1098
+ 0 on success, non-zero on failure
1099
+ """
1100
+ import argparse
1101
+
1102
+ parser = argparse.ArgumentParser(description="Initialize Session-Driven Development project")
1103
+ parser.add_argument(
1104
+ "--template",
1105
+ choices=["saas_t3", "ml_ai_fastapi", "dashboard_refine", "fullstack_nextjs"],
1106
+ help="Template to use for initialization",
1107
+ )
1108
+ parser.add_argument(
1109
+ "--tier",
1110
+ choices=[
1111
+ "tier-1-essential",
1112
+ "tier-2-standard",
1113
+ "tier-3-comprehensive",
1114
+ "tier-4-production",
1115
+ ],
1116
+ help="Quality gates tier",
1117
+ )
1118
+ parser.add_argument(
1119
+ "--coverage",
1120
+ type=int,
1121
+ help="Test coverage target percentage (e.g., 60, 80, 90)",
1122
+ )
1123
+ parser.add_argument(
1124
+ "--options",
1125
+ help="Comma-separated list of additional options (ci_cd,docker,pre_commit,env_templates)",
1126
+ )
1127
+
1128
+ args = parser.parse_args()
1129
+
1130
+ # Check if template-based init is requested
1131
+ if args.template:
1132
+ # Template-based initialization (new flow)
1133
+ from solokit.init.orchestrator import run_template_based_init
1134
+
1135
+ # Validate required arguments
1136
+ if not args.tier:
1137
+ logger.error("--tier is required when using --template")
1138
+ return 1
1139
+ if not args.coverage:
1140
+ logger.error("--coverage is required when using --template")
1141
+ return 1
1142
+
1143
+ # Parse additional options
1144
+ additional_options = []
1145
+ if args.options:
1146
+ additional_options = [opt.strip() for opt in args.options.split(",")]
1147
+
1148
+ # Run template-based init
1149
+ return run_template_based_init(
1150
+ template_id=args.template,
1151
+ tier=args.tier,
1152
+ coverage_target=args.coverage,
1153
+ additional_options=additional_options,
1154
+ )
1155
+ else:
1156
+ # Legacy init (basic initialization without templates)
1157
+ # The deprecation warning is shown in init_project() itself
1158
+ return init_project()
1159
+
1160
+
1161
+ if __name__ == "__main__":
1162
+ exit(main())