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,428 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Learning curation orchestrator
4
+
5
+ Coordinates learning curation using specialized modules:
6
+ 1. Categorization - Auto-categorize learnings
7
+ 2. Similarity detection - Merge similar learnings
8
+ 3. Archiving - Archive old learnings
9
+ 4. Extraction - Extract learnings from various sources
10
+ 5. Repository - Data persistence and CRUD
11
+ 6. Reporter - Reports and statistics
12
+ 7. Validator - Learning validation
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import argparse
18
+ from datetime import datetime
19
+ from pathlib import Path
20
+
21
+ from solokit.core.constants import MAX_LEARNING_AGE_SESSIONS
22
+ from solokit.core.error_handlers import log_errors
23
+ from solokit.core.exceptions import FileNotFoundError as SolokitFileNotFoundError
24
+ from solokit.core.logging_config import get_logger
25
+ from solokit.core.output import get_output
26
+ from solokit.learning.archiver import LearningArchiver
27
+ from solokit.learning.categorizer import LearningCategorizer
28
+ from solokit.learning.extractor import LearningExtractor
29
+ from solokit.learning.reporter import LearningReporter
30
+ from solokit.learning.repository import LearningRepository
31
+ from solokit.learning.similarity import LearningSimilarityEngine
32
+ from solokit.learning.validator import LearningValidator
33
+
34
+ logger = get_logger(__name__)
35
+ output = get_output()
36
+
37
+
38
+ class LearningsCurator:
39
+ """Main orchestrator for learning curation - delegates to specialized modules"""
40
+
41
+ def __init__(self, project_root: Path | None = None):
42
+ """
43
+ Initialize LearningsCurator with dependency injection
44
+
45
+ Args:
46
+ project_root: Path to project root directory
47
+ """
48
+ if project_root is None:
49
+ project_root = Path.cwd()
50
+
51
+ self.project_root = project_root
52
+ self.session_dir = project_root / ".session"
53
+
54
+ # Initialize all components
55
+ self.repository = LearningRepository(self.session_dir)
56
+ self.similarity_engine = LearningSimilarityEngine()
57
+ self.categorizer = LearningCategorizer()
58
+ self.archiver = LearningArchiver(self.session_dir)
59
+ self.extractor = LearningExtractor(self.session_dir, self.project_root)
60
+ self.reporter = LearningReporter(self.repository)
61
+ self.validator = LearningValidator()
62
+
63
+ @log_errors()
64
+ def curate(self, dry_run: bool = False) -> None:
65
+ """
66
+ Curate learnings - main orchestration method
67
+
68
+ Orchestrates the entire curation workflow:
69
+ 1. Load existing learnings
70
+ 2. Categorize uncategorized learnings
71
+ 3. Merge similar learnings
72
+ 4. Archive old learnings
73
+ 5. Update metadata
74
+ 6. Save results (unless dry_run)
75
+
76
+ Args:
77
+ dry_run: If True, show changes without saving
78
+
79
+ Raises:
80
+ FileOperationError: If reading/writing learnings file fails
81
+ ValidationError: If learning data is invalid
82
+ """
83
+ logger.info("Starting learning curation (dry_run=%s)", dry_run)
84
+ output.section("Learning Curation")
85
+
86
+ # Load existing learnings
87
+ learnings = self.repository.load_learnings()
88
+ initial_count = self.repository.count_all_learnings(learnings)
89
+ output.info(f"Initial learnings: {initial_count}\n")
90
+
91
+ # Categorize uncategorized learnings
92
+ categorized = self._categorize_learnings(learnings)
93
+ output.info(f"✓ Categorized {categorized} learnings")
94
+
95
+ # Merge similar learnings
96
+ merged = self.similarity_engine.merge_similar_learnings(learnings)
97
+ output.info(f"✓ Merged {merged} duplicate learnings")
98
+
99
+ # Archive old learnings
100
+ archived = self.archiver.archive_old_learnings(learnings)
101
+ output.info(f"✓ Archived {archived} old learnings")
102
+
103
+ # Update metadata
104
+ learnings["last_curated"] = datetime.now().isoformat()
105
+ learnings["curator"] = "session_curator"
106
+ self.repository.update_total_learnings(learnings)
107
+
108
+ final_count = self.repository.count_all_learnings(learnings)
109
+ output.info(f"\nFinal learnings: {final_count}\n")
110
+
111
+ if not dry_run:
112
+ self.repository.save_learnings(learnings)
113
+ output.success("Learnings saved\n")
114
+ else:
115
+ output.info("Dry run - no changes saved\n")
116
+
117
+ def _categorize_learnings(self, learnings: dict) -> int:
118
+ """
119
+ Categorize uncategorized learnings using extractor and categorizer
120
+
121
+ Args:
122
+ learnings: Learnings dictionary
123
+
124
+ Returns:
125
+ Number of learnings categorized
126
+ """
127
+ categorized_count = 0
128
+
129
+ # Extract learnings from session summaries
130
+ # Use the wrapper method for test compatibility
131
+ new_learnings = self._extract_learnings_from_sessions()
132
+
133
+ for learning in new_learnings:
134
+ # Auto-categorize using categorizer
135
+ category = self.categorizer.categorize_learning(learning)
136
+
137
+ # Add to appropriate category
138
+ categories = learnings.setdefault("categories", {})
139
+ if category not in categories:
140
+ categories[category] = []
141
+
142
+ # Check if already exists using similarity engine
143
+ if not self.repository.learning_exists(
144
+ categories[category], learning, self.similarity_engine
145
+ ):
146
+ categories[category].append(learning)
147
+ categorized_count += 1
148
+
149
+ logger.info(f"Categorized {categorized_count} learnings")
150
+ return categorized_count
151
+
152
+ def auto_curate_if_needed(self) -> bool:
153
+ """
154
+ Auto-curate based on configuration and last curation time
155
+
156
+ Returns:
157
+ True if curation was performed, False otherwise
158
+ """
159
+ config = self.repository.get_curation_config()
160
+
161
+ if not config.auto_curate:
162
+ return False
163
+
164
+ learnings = self.repository.load_learnings()
165
+ last_curated = learnings.get("last_curated")
166
+
167
+ if not last_curated:
168
+ # Never curated, do it now
169
+ output.info("Auto-curating (first time)...\n")
170
+ self.curate(dry_run=False)
171
+ return True
172
+
173
+ # Check frequency
174
+ last_date = datetime.fromisoformat(last_curated)
175
+ days_since = (datetime.now() - last_date).days
176
+
177
+ frequency_days = config.frequency
178
+
179
+ if days_since >= frequency_days:
180
+ output.info(f"Auto-curating (last curated {days_since} days ago)...\n")
181
+ self.curate(dry_run=False)
182
+ return True
183
+
184
+ return False
185
+
186
+ # Delegate methods to appropriate components
187
+
188
+ @log_errors()
189
+ def add_learning(
190
+ self,
191
+ content: str,
192
+ category: str,
193
+ session: int | None = None,
194
+ tags: list | None = None,
195
+ context: str | None = None,
196
+ ) -> str:
197
+ """Add a new learning (delegates to repository)"""
198
+ return self.repository.add_learning(content, category, session, tags, context)
199
+
200
+ def add_learning_if_new(self, learning_dict: dict) -> bool:
201
+ """Add learning if it doesn't already exist (delegates to repository)"""
202
+ return self.repository.add_learning_if_new(learning_dict, self.similarity_engine)
203
+
204
+ def search_learnings(self, query: str) -> None:
205
+ """Search learnings by keyword (delegates to reporter)"""
206
+ self.reporter.search_learnings(query)
207
+
208
+ def show_learnings(
209
+ self,
210
+ category: str | None = None,
211
+ tag: str | None = None,
212
+ session: int | None = None,
213
+ date_from: str | None = None,
214
+ date_to: str | None = None,
215
+ include_archived: bool = False,
216
+ ) -> None:
217
+ """Show learnings with optional filters (delegates to reporter)"""
218
+ self.reporter.show_learnings(category, tag, session, date_from, date_to, include_archived)
219
+
220
+ def generate_report(self) -> None:
221
+ """Generate learning summary report (delegates to reporter)"""
222
+ self.reporter.generate_report()
223
+
224
+ def show_statistics(self) -> None:
225
+ """Display learning statistics (delegates to reporter)"""
226
+ self.reporter.show_statistics()
227
+
228
+ def generate_statistics(self) -> dict:
229
+ """Generate learning statistics (delegates to reporter)"""
230
+ return self.reporter.generate_statistics()
231
+
232
+ def show_timeline(self, sessions: int = 10) -> None:
233
+ """Show learning timeline (delegates to reporter)"""
234
+ self.reporter.show_timeline(sessions)
235
+
236
+ @log_errors()
237
+ def extract_from_session_summary(self, session_file: Path) -> list[dict]:
238
+ """Extract learnings from session summary file (delegates to extractor)"""
239
+ return self.extractor.extract_from_session_summary(session_file, self.validator)
240
+
241
+ @log_errors()
242
+ def extract_from_git_commits(
243
+ self, since_session: int = 0, session_id: str | None = None
244
+ ) -> list[dict]:
245
+ """Extract learnings from git commit messages (delegates to extractor)"""
246
+ return self.extractor.extract_from_git_commits(since_session, session_id, self.validator)
247
+
248
+ @log_errors()
249
+ def extract_from_code_comments(
250
+ self, changed_files: list[Path] | None = None, session_id: str | None = None
251
+ ) -> list[dict]:
252
+ """Extract learnings from inline code comments (delegates to extractor)"""
253
+ return self.extractor.extract_from_code_comments(changed_files, session_id, self.validator)
254
+
255
+ def is_valid_learning(self, content: str) -> bool:
256
+ """Check if content is a valid learning (delegates to validator)"""
257
+ return self.validator.is_valid_learning(content)
258
+
259
+ def create_learning_entry(
260
+ self,
261
+ content: str,
262
+ source: str,
263
+ session_id: str | None = None,
264
+ context: str | None = None,
265
+ timestamp: str | None = None,
266
+ learning_id: str | None = None,
267
+ ) -> dict:
268
+ """Create standardized learning entry (delegates to validator)"""
269
+ return self.validator.create_learning_entry(
270
+ content, source, session_id, context, timestamp, learning_id
271
+ )
272
+
273
+ def validate_learning(self, learning: dict) -> bool:
274
+ """Validate learning entry against schema (delegates to validator)"""
275
+ return self.validator.validate_learning(learning)
276
+
277
+ def get_related_learnings(self, learning_id: str, limit: int = 5) -> list[dict]:
278
+ """Get similar learnings (delegates to similarity engine)"""
279
+ learnings = self.repository.load_learnings()
280
+ return self.similarity_engine.get_related_learnings(learnings, learning_id, limit)
281
+
282
+ # ========================================================================
283
+ # Compatibility wrapper methods for tests
284
+ # These delegate to the refactored modules
285
+ # ========================================================================
286
+
287
+ def _count_all_learnings(self, learnings: dict) -> int:
288
+ """Count all learnings (compatibility wrapper for tests)"""
289
+ return self.repository.count_all_learnings(learnings)
290
+
291
+ def _update_total_learnings(self, learnings: dict) -> None:
292
+ """Update total learnings metadata (compatibility wrapper for tests)"""
293
+ self.repository.update_total_learnings(learnings)
294
+
295
+ def _keyword_score(self, text: str, keywords: list[str]) -> int:
296
+ """Calculate keyword score (compatibility wrapper for tests)"""
297
+ return self.categorizer._keyword_score(text, keywords)
298
+
299
+ def _auto_categorize_learning(self, learning: dict) -> str:
300
+ """Auto-categorize learning (compatibility wrapper for tests)"""
301
+ return self.categorizer.categorize_learning(learning)
302
+
303
+ def _are_similar(self, learning_a: dict, learning_b: dict) -> bool:
304
+ """Check if two learnings are similar (compatibility wrapper for tests)"""
305
+ return self.similarity_engine.are_similar(learning_a, learning_b)
306
+
307
+ def _learning_exists(self, cat_learnings: list[dict], new_learning: dict) -> bool:
308
+ """Check if learning exists in category (compatibility wrapper for tests)"""
309
+ return self.repository.learning_exists(cat_learnings, new_learning, self.similarity_engine)
310
+
311
+ def _merge_similar_learnings(self, learnings: dict) -> int:
312
+ """Merge similar learnings (compatibility wrapper for tests)"""
313
+ return self.similarity_engine.merge_similar_learnings(learnings)
314
+
315
+ def _archive_old_learnings(
316
+ self, learnings: dict, max_age_sessions: int = MAX_LEARNING_AGE_SESSIONS
317
+ ) -> int:
318
+ """Archive old learnings (compatibility wrapper for tests)"""
319
+ return self.archiver.archive_old_learnings(learnings, max_age_sessions)
320
+
321
+ def _extract_session_number(self, session_id: str) -> int:
322
+ """Extract session number from session ID (compatibility wrapper for tests)"""
323
+ return self.archiver._extract_session_number(session_id)
324
+
325
+ def _get_current_session_number(self) -> int:
326
+ """Get current session number (compatibility wrapper for tests)"""
327
+ return self.archiver._get_current_session_number()
328
+
329
+ def _extract_learnings_from_sessions(self) -> list[dict]:
330
+ """Extract learnings from session summaries (compatibility wrapper for tests)"""
331
+ return self.extractor.extract_from_sessions()
332
+
333
+ def _load_learnings(self) -> dict:
334
+ """Load learnings from file (compatibility wrapper for tests)"""
335
+ return self.repository.load_learnings()
336
+
337
+
338
+ def main() -> None:
339
+ """Main entry point"""
340
+ parser = argparse.ArgumentParser(description="Learning curation and management")
341
+
342
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
343
+
344
+ # Curate command
345
+ curate_parser = subparsers.add_parser("curate", help="Run curation process")
346
+ curate_parser.add_argument("--dry-run", action="store_true", help="Show changes without saving")
347
+
348
+ # Show learnings command
349
+ show_parser = subparsers.add_parser("show-learnings", help="Show learnings")
350
+ show_parser.add_argument("--category", type=str, help="Filter by category")
351
+ show_parser.add_argument("--tag", type=str, help="Filter by tag")
352
+ show_parser.add_argument("--session", type=int, help="Filter by session number")
353
+
354
+ # Search command
355
+ search_parser = subparsers.add_parser("search", help="Search learnings")
356
+ search_parser.add_argument("query", type=str, help="Search query")
357
+
358
+ # Add learning command
359
+ add_parser = subparsers.add_parser("add-learning", help="Add a new learning")
360
+ add_parser.add_argument("--content", type=str, required=True, help="Learning content")
361
+ add_parser.add_argument(
362
+ "--category",
363
+ type=str,
364
+ required=True,
365
+ choices=[
366
+ "architecture_patterns",
367
+ "gotchas",
368
+ "best_practices",
369
+ "technical_debt",
370
+ "performance_insights",
371
+ "security",
372
+ ],
373
+ help="Learning category",
374
+ )
375
+ add_parser.add_argument("--session", type=int, help="Session number")
376
+ add_parser.add_argument("--tags", type=str, help="Comma-separated tags")
377
+ add_parser.add_argument("--context", type=str, help="Additional context")
378
+
379
+ # Report command (legacy)
380
+ subparsers.add_parser("report", help="Generate summary report")
381
+
382
+ # Statistics command
383
+ subparsers.add_parser("statistics", help="Show learning statistics")
384
+
385
+ # Timeline command
386
+ timeline_parser = subparsers.add_parser("timeline", help="Show learning timeline")
387
+ timeline_parser.add_argument(
388
+ "--sessions", type=int, default=10, help="Number of recent sessions to show"
389
+ )
390
+
391
+ args = parser.parse_args()
392
+
393
+ project_root = Path.cwd()
394
+ session_dir = project_root / ".session"
395
+
396
+ if not session_dir.exists():
397
+ raise SolokitFileNotFoundError(file_path=str(session_dir), file_type="session directory")
398
+
399
+ curator = LearningsCurator(project_root)
400
+
401
+ if args.command == "curate":
402
+ curator.curate(dry_run=args.dry_run)
403
+ elif args.command == "show-learnings":
404
+ curator.show_learnings(category=args.category, tag=args.tag, session=args.session)
405
+ elif args.command == "search":
406
+ curator.search_learnings(args.query)
407
+ elif args.command == "add-learning":
408
+ tags = args.tags.split(",") if args.tags else None
409
+ curator.add_learning(
410
+ content=args.content,
411
+ category=args.category,
412
+ session=args.session,
413
+ tags=tags,
414
+ context=args.context,
415
+ )
416
+ elif args.command == "report":
417
+ curator.generate_report()
418
+ elif args.command == "statistics":
419
+ curator.show_statistics()
420
+ elif args.command == "timeline":
421
+ curator.show_timeline(sessions=args.sessions)
422
+ else:
423
+ # Default to report if no command specified
424
+ curator.generate_report()
425
+
426
+
427
+ if __name__ == "__main__":
428
+ main()