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,438 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Sync Solokit main repository to claude-plugins marketplace repository.
4
+
5
+ This script automates the process of syncing files from the main Solokit repo
6
+ to the claude-plugins marketplace repo, handling transformations and preserving
7
+ plugin-specific files.
8
+
9
+ Note: This script is automatically executed by GitHub Actions on every push to main.
10
+ See .github/workflows/sync-plugin.yml for the automation workflow.
11
+
12
+ Usage:
13
+ python src/solokit/project/sync_plugin.py [--main-repo PATH] [--plugin-repo PATH] [--dry-run]
14
+
15
+ Arguments:
16
+ --main-repo PATH Path to main Solokit repository (default: current directory)
17
+ --plugin-repo PATH Path to claude-plugins repository (required)
18
+ --dry-run Show what would be synced without making changes
19
+ """
20
+
21
+ import argparse
22
+ import json
23
+ import shutil
24
+ import sys
25
+ from pathlib import Path
26
+
27
+ from solokit.core.exceptions import (
28
+ ErrorCode,
29
+ FileOperationError,
30
+ ValidationError,
31
+ )
32
+ from solokit.core.exceptions import (
33
+ FileNotFoundError as SolokitFileNotFoundError,
34
+ )
35
+ from solokit.core.logging_config import get_logger
36
+
37
+ logger = get_logger(__name__)
38
+
39
+
40
+ class PluginSyncer:
41
+ """Handles syncing from main Solokit repo to claude-plugins marketplace."""
42
+
43
+ # Define file mappings: (source_path, dest_path, is_directory)
44
+ FILE_MAPPINGS = [
45
+ ("src/solokit", "solokit/src/solokit", True),
46
+ (".claude/commands", "solokit/commands", True),
47
+ ("pyproject.toml", "solokit/pyproject.toml", False),
48
+ ]
49
+
50
+ # Files to preserve in plugin repo (never overwrite)
51
+ # These files are maintained separately in the plugin marketplace repo
52
+ PRESERVE_FILES = [
53
+ ".claude-plugin/plugin.json", # Only update version field
54
+ "README.md", # Plugin has its own marketplace README
55
+ "CONTRIBUTING.md", # Directs contributors to main Solokit repo
56
+ "LICENSE", # Static license file for marketplace
57
+ "SECURITY.md", # Static security policy for marketplace
58
+ ".git", # Git metadata
59
+ ".github", # Plugin repo's own workflows
60
+ ]
61
+
62
+ def __init__(self, main_repo: Path, plugin_repo: Path, dry_run: bool = False):
63
+ """
64
+ Initialize the syncer.
65
+
66
+ Args:
67
+ main_repo: Path to main Solokit repository
68
+ plugin_repo: Path to claude-plugins repository
69
+ dry_run: If True, show what would be done without making changes
70
+ """
71
+ self.main_repo = main_repo.resolve()
72
+ self.plugin_repo = plugin_repo.resolve()
73
+ self.dry_run = dry_run
74
+ self.changes: list[str] = []
75
+
76
+ def validate_repos(self) -> None:
77
+ """
78
+ Validate that both repositories exist and have expected structure.
79
+
80
+ Raises:
81
+ SolokitFileNotFoundError: If repository path doesn't exist
82
+ ValidationError: If repository is missing expected files/directories
83
+ """
84
+ # Check main repo
85
+ if not self.main_repo.exists():
86
+ raise SolokitFileNotFoundError(
87
+ file_path=str(self.main_repo), file_type="main repository"
88
+ )
89
+
90
+ main_markers = ["src/solokit/cli.py", "pyproject.toml", ".claude/commands"]
91
+ for marker in main_markers:
92
+ if not (self.main_repo / marker).exists():
93
+ raise ValidationError(
94
+ message=f"Main repository missing expected file/directory: {marker}",
95
+ code=ErrorCode.CONFIG_VALIDATION_FAILED,
96
+ context={
97
+ "repository": str(self.main_repo),
98
+ "missing_marker": marker,
99
+ "marker_type": "file" if "." in marker else "directory",
100
+ },
101
+ remediation=f"Ensure {self.main_repo} is a valid Solokit repository",
102
+ )
103
+
104
+ # Check plugin repo
105
+ if not self.plugin_repo.exists():
106
+ raise SolokitFileNotFoundError(
107
+ file_path=str(self.plugin_repo), file_type="plugin repository"
108
+ )
109
+
110
+ plugin_markers = ["solokit", "solokit/.claude-plugin/plugin.json"]
111
+ for marker in plugin_markers:
112
+ if not (self.plugin_repo / marker).exists():
113
+ raise ValidationError(
114
+ message=f"Plugin repository missing expected file/directory: {marker}",
115
+ code=ErrorCode.CONFIG_VALIDATION_FAILED,
116
+ context={
117
+ "repository": str(self.plugin_repo),
118
+ "missing_marker": marker,
119
+ "marker_type": "file" if "plugin.json" in marker else "directory",
120
+ },
121
+ remediation=f"Ensure {self.plugin_repo} is a valid claude-plugins repository",
122
+ )
123
+
124
+ logger.info("Repository validation passed")
125
+
126
+ def get_version_from_main(self) -> str:
127
+ """
128
+ Extract version from pyproject.toml in main repo.
129
+
130
+ Returns:
131
+ Version string (e.g., "0.5.7")
132
+
133
+ Raises:
134
+ SolokitFileNotFoundError: If pyproject.toml doesn't exist
135
+ ValidationError: If version field not found in pyproject.toml
136
+ FileOperationError: If file cannot be read
137
+ """
138
+ pyproject_path = self.main_repo / "pyproject.toml"
139
+ if not pyproject_path.exists():
140
+ raise SolokitFileNotFoundError(
141
+ file_path=str(pyproject_path), file_type="pyproject.toml"
142
+ )
143
+
144
+ try:
145
+ with open(pyproject_path) as f:
146
+ for line in f:
147
+ if line.strip().startswith("version"):
148
+ # Extract version from line like: version = "0.5.7"
149
+ version = line.split("=")[1].strip().strip('"')
150
+ return version
151
+ except OSError as e:
152
+ raise FileOperationError(
153
+ operation="read", file_path=str(pyproject_path), details=str(e), cause=e
154
+ )
155
+
156
+ raise ValidationError(
157
+ message="Version not found in pyproject.toml",
158
+ code=ErrorCode.MISSING_REQUIRED_FIELD,
159
+ context={"file_path": str(pyproject_path), "expected_field": "version"},
160
+ remediation="Ensure pyproject.toml contains a 'version = \"x.y.z\"' line",
161
+ )
162
+
163
+ def update_plugin_version(self, version: str) -> None:
164
+ """
165
+ Update version in plugin.json.
166
+
167
+ Args:
168
+ version: Version string to set (e.g., "0.5.7")
169
+
170
+ Raises:
171
+ FileOperationError: If plugin.json cannot be read, parsed, or written
172
+ """
173
+ plugin_json_path = self.plugin_repo / "solokit" / ".claude-plugin" / "plugin.json"
174
+
175
+ if self.dry_run:
176
+ logger.info(f"[DRY RUN] Would update plugin.json version to {version}")
177
+ self.changes.append(f"Update plugin.json version to {version}")
178
+ return
179
+
180
+ try:
181
+ with open(plugin_json_path) as f:
182
+ plugin_data = json.load(f)
183
+ except json.JSONDecodeError as e:
184
+ raise FileOperationError(
185
+ operation="parse",
186
+ file_path=str(plugin_json_path),
187
+ details=f"Invalid JSON: {e}",
188
+ cause=e,
189
+ )
190
+ except OSError as e:
191
+ raise FileOperationError(
192
+ operation="read", file_path=str(plugin_json_path), details=str(e), cause=e
193
+ )
194
+
195
+ old_version = plugin_data.get("version", "unknown")
196
+ plugin_data["version"] = version
197
+
198
+ try:
199
+ with open(plugin_json_path, "w") as f:
200
+ json.dump(plugin_data, f, indent=2)
201
+ f.write("\n") # Add trailing newline
202
+ except OSError as e:
203
+ raise FileOperationError(
204
+ operation="write", file_path=str(plugin_json_path), details=str(e), cause=e
205
+ )
206
+
207
+ change_msg = f"Updated plugin.json version: {old_version} → {version}"
208
+ logger.info(change_msg)
209
+ self.changes.append(change_msg)
210
+
211
+ def sync_file(self, src: Path, dest: Path) -> None:
212
+ """
213
+ Sync a single file from source to destination.
214
+
215
+ Args:
216
+ src: Source file path
217
+ dest: Destination file path
218
+
219
+ Raises:
220
+ FileOperationError: If file copy operation fails
221
+ """
222
+ if self.dry_run:
223
+ logger.info(
224
+ f"[DRY RUN] Would copy: {src.relative_to(self.main_repo)} → {dest.relative_to(self.plugin_repo)}"
225
+ )
226
+ self.changes.append(
227
+ f"Copy {src.relative_to(self.main_repo)} → {dest.relative_to(self.plugin_repo)}"
228
+ )
229
+ return
230
+
231
+ try:
232
+ # Create parent directory if needed
233
+ dest.parent.mkdir(parents=True, exist_ok=True)
234
+
235
+ # Copy file
236
+ shutil.copy2(src, dest)
237
+ except (OSError, shutil.Error) as e:
238
+ raise FileOperationError(
239
+ operation="copy",
240
+ file_path=str(src),
241
+ details=f"Failed to copy to {dest}: {e}",
242
+ cause=e,
243
+ )
244
+
245
+ logger.info(
246
+ f"Copied: {src.relative_to(self.main_repo)} → {dest.relative_to(self.plugin_repo)}"
247
+ )
248
+ self.changes.append(f"Copied {src.relative_to(self.main_repo)}")
249
+
250
+ def sync_directory(self, src: Path, dest: Path) -> None:
251
+ """
252
+ Sync a directory from source to destination.
253
+
254
+ Args:
255
+ src: Source directory path
256
+ dest: Destination directory path
257
+
258
+ Raises:
259
+ FileOperationError: If directory sync operation fails
260
+ """
261
+ if self.dry_run:
262
+ logger.info(
263
+ f"[DRY RUN] Would sync directory: {src.relative_to(self.main_repo)} → {dest.relative_to(self.plugin_repo)}"
264
+ )
265
+
266
+ # Count files to sync
267
+ file_count = sum(1 for _ in src.rglob("*") if _.is_file())
268
+ self.changes.append(
269
+ f"Sync directory {src.relative_to(self.main_repo)} ({file_count} files)"
270
+ )
271
+ return
272
+
273
+ try:
274
+ # Remove existing destination if it exists
275
+ if dest.exists():
276
+ shutil.rmtree(dest)
277
+
278
+ # Copy entire directory tree
279
+ shutil.copytree(src, dest)
280
+ except (OSError, shutil.Error) as e:
281
+ raise FileOperationError(
282
+ operation="sync_directory",
283
+ file_path=str(src),
284
+ details=f"Failed to sync directory to {dest}: {e}",
285
+ cause=e,
286
+ )
287
+
288
+ # Count files synced
289
+ file_count = sum(1 for _ in dest.rglob("*") if _.is_file())
290
+ logger.info(f"Synced directory: {src.relative_to(self.main_repo)} ({file_count} files)")
291
+ self.changes.append(
292
+ f"Synced directory {src.relative_to(self.main_repo)} ({file_count} files)"
293
+ )
294
+
295
+ def sync_all_files(self) -> None:
296
+ """
297
+ Sync all files according to FILE_MAPPINGS.
298
+
299
+ Raises:
300
+ FileOperationError: If any file/directory sync operation fails
301
+ """
302
+ logger.info("Syncing files...")
303
+
304
+ for src_rel, dest_rel, is_directory in self.FILE_MAPPINGS:
305
+ src = self.main_repo / src_rel
306
+ dest = self.plugin_repo / dest_rel
307
+
308
+ if not src.exists():
309
+ logger.warning(f"Source not found (skipping): {src_rel}")
310
+ continue
311
+
312
+ if is_directory:
313
+ self.sync_directory(src, dest)
314
+ else:
315
+ self.sync_file(src, dest)
316
+
317
+ def generate_summary(self) -> str:
318
+ """Generate a summary of all changes made."""
319
+ summary_lines = [
320
+ "# Plugin Sync Summary",
321
+ "",
322
+ f"Main repo: {self.main_repo}",
323
+ f"Plugin repo: {self.plugin_repo}",
324
+ f"Dry run: {self.dry_run}",
325
+ "",
326
+ "## Changes:",
327
+ ]
328
+
329
+ if self.changes:
330
+ for change in self.changes:
331
+ summary_lines.append(f"- {change}")
332
+ else:
333
+ summary_lines.append("- No changes made")
334
+
335
+ return "\n".join(summary_lines)
336
+
337
+ def sync(self) -> None:
338
+ """
339
+ Execute the sync process.
340
+
341
+ Raises:
342
+ SolokitFileNotFoundError: If repository paths don't exist
343
+ ValidationError: If repositories are missing expected files
344
+ FileOperationError: If file operations fail
345
+ """
346
+ logger.info("Starting plugin sync...")
347
+
348
+ # Validate repositories
349
+ self.validate_repos()
350
+
351
+ # Get version from main repo
352
+ version = self.get_version_from_main()
353
+ logger.info(f"Main repo version: {version}")
354
+
355
+ # Update plugin version
356
+ self.update_plugin_version(version)
357
+
358
+ # Sync all files
359
+ self.sync_all_files()
360
+
361
+ # Print summary
362
+ logger.info("=" * 60)
363
+ logger.info(self.generate_summary())
364
+ logger.info("=" * 60)
365
+
366
+ if self.dry_run:
367
+ logger.warning("This was a DRY RUN - no changes were made")
368
+ else:
369
+ logger.info("Sync completed successfully!")
370
+
371
+
372
+ def main() -> None:
373
+ """
374
+ Main entry point.
375
+
376
+ Exit codes:
377
+ 0: Success
378
+ 1: General error
379
+ 2: Validation error
380
+ 3: File not found
381
+ 4: Configuration error
382
+ 5: System/file operation error
383
+ """
384
+ parser = argparse.ArgumentParser(
385
+ description="Sync Solokit main repository to claude-plugins marketplace repository"
386
+ )
387
+ parser.add_argument(
388
+ "--main-repo",
389
+ type=Path,
390
+ default=Path.cwd(),
391
+ help="Path to main Solokit repository (default: current directory)",
392
+ )
393
+ parser.add_argument(
394
+ "--plugin-repo",
395
+ type=Path,
396
+ required=True,
397
+ help="Path to claude-plugins repository",
398
+ )
399
+ parser.add_argument(
400
+ "--dry-run",
401
+ action="store_true",
402
+ help="Show what would be synced without making changes",
403
+ )
404
+
405
+ args = parser.parse_args()
406
+
407
+ # Create syncer and run
408
+ syncer = PluginSyncer(
409
+ main_repo=args.main_repo,
410
+ plugin_repo=args.plugin_repo,
411
+ dry_run=args.dry_run,
412
+ )
413
+
414
+ try:
415
+ syncer.sync()
416
+ sys.exit(0)
417
+ except SolokitFileNotFoundError as e:
418
+ logger.error(f"File not found: {e.message}")
419
+ if e.remediation:
420
+ logger.error(f"Remediation: {e.remediation}")
421
+ sys.exit(e.exit_code)
422
+ except ValidationError as e:
423
+ logger.error(f"Validation error: {e.message}")
424
+ if e.remediation:
425
+ logger.error(f"Remediation: {e.remediation}")
426
+ sys.exit(e.exit_code)
427
+ except FileOperationError as e:
428
+ logger.error(f"File operation error: {e.message}")
429
+ if e.remediation:
430
+ logger.error(f"Remediation: {e.remediation}")
431
+ sys.exit(e.exit_code)
432
+ except Exception as e:
433
+ logger.error(f"Unexpected error: {e}")
434
+ sys.exit(1)
435
+
436
+
437
+ if __name__ == "__main__":
438
+ main()