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,267 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Work Item Repository - Data access and persistence layer.
4
+
5
+ Handles CRUD operations for work items and milestones in work_items.json.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from datetime import datetime
11
+ from pathlib import Path
12
+ from typing import Any, cast
13
+
14
+ from solokit.core.cache import FileCache
15
+ from solokit.core.file_ops import load_json, save_json
16
+ from solokit.core.logging_config import get_logger
17
+ from solokit.core.performance import measure_time
18
+ from solokit.core.types import WorkItemStatus
19
+
20
+ logger = get_logger(__name__)
21
+
22
+
23
+ class WorkItemRepository:
24
+ """Repository for work item data access and persistence with caching"""
25
+
26
+ def __init__(self, session_dir: Path):
27
+ """Initialize repository with session directory
28
+
29
+ Args:
30
+ session_dir: Path to .session directory
31
+ """
32
+ self.session_dir = session_dir
33
+ self.work_items_file = session_dir / "tracking" / "work_items.json"
34
+ self._file_cache = FileCache()
35
+
36
+ @measure_time("load_work_items")
37
+ def load_all(self) -> dict[str, Any]:
38
+ """Load all work items and milestones from work_items.json with caching
39
+
40
+ Returns:
41
+ dict: Complete work items data including work_items and milestones
42
+ """
43
+ if not self.work_items_file.exists():
44
+ return {"work_items": {}, "milestones": {}}
45
+
46
+ return cast(dict[str, Any], self._file_cache.load_json(self.work_items_file, load_json))
47
+
48
+ def save_all(self, data: dict[str, Any]) -> None:
49
+ """Save all work items and milestones to work_items.json
50
+
51
+ Args:
52
+ data: Complete work items data to save
53
+ """
54
+ # Update metadata counters before saving
55
+ self._update_metadata(data)
56
+ save_json(self.work_items_file, data)
57
+ # Invalidate cache after write
58
+ self._file_cache.invalidate(self.work_items_file)
59
+
60
+ def get_work_item(self, work_id: str) -> dict[str, Any] | None:
61
+ """Get a single work item by ID
62
+
63
+ Args:
64
+ work_id: Work item ID
65
+
66
+ Returns:
67
+ dict: Work item data, or None if not found
68
+ """
69
+ data = self.load_all()
70
+ work_items = data.get("work_items", {})
71
+ result = work_items.get(work_id)
72
+ return result if result is None else dict(result)
73
+
74
+ def get_all_work_items(self) -> dict[str, Any]:
75
+ """Get all work items
76
+
77
+ Returns:
78
+ dict: All work items keyed by ID
79
+ """
80
+ data = self.load_all()
81
+ return dict(data.get("work_items", {}))
82
+
83
+ def work_item_exists(self, work_id: str) -> bool:
84
+ """Check if work item exists
85
+
86
+ Args:
87
+ work_id: Work item ID
88
+
89
+ Returns:
90
+ bool: True if work item exists
91
+ """
92
+ return self.get_work_item(work_id) is not None
93
+
94
+ def add_work_item(
95
+ self,
96
+ work_id: str,
97
+ work_type: str,
98
+ title: str,
99
+ priority: str,
100
+ dependencies: list[str],
101
+ spec_file: str = "",
102
+ ) -> None:
103
+ """Add a new work item to tracking
104
+
105
+ Args:
106
+ work_id: Unique work item ID
107
+ work_type: Type of work item
108
+ title: Work item title
109
+ priority: Priority level
110
+ dependencies: List of dependency IDs
111
+ spec_file: Relative path to spec file
112
+ """
113
+ data = self.load_all()
114
+
115
+ work_item = {
116
+ "id": work_id,
117
+ "type": work_type,
118
+ "title": title,
119
+ "status": WorkItemStatus.NOT_STARTED.value,
120
+ "priority": priority,
121
+ "dependencies": dependencies,
122
+ "milestone": "",
123
+ "spec_file": spec_file,
124
+ "created_at": datetime.now().isoformat(),
125
+ "sessions": [],
126
+ }
127
+
128
+ data.setdefault("work_items", {})[work_id] = work_item
129
+ self.save_all(data)
130
+ logger.info("Added work item: %s", work_id)
131
+
132
+ def update_work_item(self, work_id: str, updates: dict[str, Any]) -> None:
133
+ """Update a work item with new field values
134
+
135
+ Args:
136
+ work_id: Work item ID
137
+ updates: Dictionary of field updates
138
+ """
139
+ data = self.load_all()
140
+ items = data.get("work_items", {})
141
+
142
+ if work_id not in items:
143
+ return
144
+
145
+ item = items[work_id]
146
+
147
+ # Apply updates
148
+ for field, value in updates.items():
149
+ if field == "add_dependency":
150
+ deps = item.get("dependencies", [])
151
+ if value not in deps:
152
+ deps.append(value)
153
+ item["dependencies"] = deps
154
+ elif field == "remove_dependency":
155
+ deps = item.get("dependencies", [])
156
+ if value in deps:
157
+ deps.remove(value)
158
+ item["dependencies"] = deps
159
+ else:
160
+ item[field] = value
161
+
162
+ data["work_items"][work_id] = item
163
+ self.save_all(data)
164
+ logger.debug("Updated work item: %s", work_id)
165
+
166
+ def delete_work_item(self, work_id: str) -> bool:
167
+ """Delete a work item
168
+
169
+ Args:
170
+ work_id: Work item ID
171
+
172
+ Returns:
173
+ bool: True if deleted, False if not found
174
+ """
175
+ data = self.load_all()
176
+ items = data.get("work_items", {})
177
+
178
+ if work_id not in items:
179
+ return False
180
+
181
+ del items[work_id]
182
+ data["work_items"] = items
183
+ self.save_all(data)
184
+ logger.info("Deleted work item: %s", work_id)
185
+ return True
186
+
187
+ def get_milestone(self, name: str) -> dict[str, Any] | None:
188
+ """Get a milestone by name
189
+
190
+ Args:
191
+ name: Milestone name
192
+
193
+ Returns:
194
+ dict: Milestone data, or None if not found
195
+ """
196
+ data = self.load_all()
197
+ milestones = data.get("milestones", {})
198
+ result = milestones.get(name)
199
+ return result if result is None else dict(result)
200
+
201
+ def get_all_milestones(self) -> dict[str, Any]:
202
+ """Get all milestones
203
+
204
+ Returns:
205
+ dict: All milestones keyed by name
206
+ """
207
+ data = self.load_all()
208
+ return dict(data.get("milestones", {}))
209
+
210
+ def milestone_exists(self, name: str) -> bool:
211
+ """Check if milestone exists
212
+
213
+ Args:
214
+ name: Milestone name
215
+
216
+ Returns:
217
+ bool: True if milestone exists
218
+ """
219
+ return self.get_milestone(name) is not None
220
+
221
+ def add_milestone(
222
+ self, name: str, title: str, description: str, target_date: str | None = None
223
+ ) -> None:
224
+ """Add a new milestone
225
+
226
+ Args:
227
+ name: Unique milestone name
228
+ title: Milestone title
229
+ description: Milestone description
230
+ target_date: Optional target completion date
231
+ """
232
+ data = self.load_all()
233
+
234
+ milestone = {
235
+ "name": name,
236
+ "title": title,
237
+ "description": description,
238
+ "target_date": target_date or "",
239
+ "status": "not_started",
240
+ "created_at": datetime.now().isoformat(),
241
+ }
242
+
243
+ data.setdefault("milestones", {})[name] = milestone
244
+ self.save_all(data)
245
+ logger.info("Added milestone: %s", name)
246
+
247
+ def _update_metadata(self, data: dict[str, Any]) -> None:
248
+ """Update metadata counters
249
+
250
+ Args:
251
+ data: Work items data to update metadata for
252
+ """
253
+ if "metadata" not in data:
254
+ data["metadata"] = {}
255
+
256
+ work_items = data.get("work_items", {})
257
+ data["metadata"]["total_items"] = len(work_items)
258
+ data["metadata"]["completed"] = sum(
259
+ 1 for item in work_items.values() if item["status"] == WorkItemStatus.COMPLETED.value
260
+ )
261
+ data["metadata"]["in_progress"] = sum(
262
+ 1 for item in work_items.values() if item["status"] == WorkItemStatus.IN_PROGRESS.value
263
+ )
264
+ data["metadata"]["blocked"] = sum(
265
+ 1 for item in work_items.values() if item["status"] == WorkItemStatus.BLOCKED.value
266
+ )
267
+ data["metadata"]["last_updated"] = datetime.now().isoformat()
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Work Item Scheduler - Work queue and next item selection.
4
+
5
+ Handles finding the next work item to start based on dependencies and priority.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import TYPE_CHECKING, Any
11
+
12
+ from solokit.core.logging_config import get_logger
13
+ from solokit.core.types import Priority, WorkItemStatus
14
+
15
+ if TYPE_CHECKING:
16
+ from .repository import WorkItemRepository
17
+ from solokit.core.output import get_output
18
+
19
+ logger = get_logger(__name__)
20
+ output = get_output()
21
+
22
+
23
+ class WorkItemScheduler:
24
+ """Handles work item scheduling and queue management"""
25
+
26
+ def __init__(self, repository: WorkItemRepository):
27
+ """Initialize scheduler with repository
28
+
29
+ Args:
30
+ repository: WorkItemRepository instance for data access
31
+ """
32
+ self.repository = repository
33
+
34
+ def get_next(self) -> dict[str, Any] | None:
35
+ """Find next work item to start based on dependencies and priority
36
+
37
+ Returns:
38
+ dict: Next work item to start, or None if none available
39
+ """
40
+ items = self.repository.get_all_work_items()
41
+
42
+ if not items:
43
+ output.info("No work items found.")
44
+ return None
45
+
46
+ # Filter to not_started items
47
+ not_started = {
48
+ wid: item
49
+ for wid, item in items.items()
50
+ if item["status"] == WorkItemStatus.NOT_STARTED.value
51
+ }
52
+
53
+ if not not_started:
54
+ output.info("No work items available to start.")
55
+ output.info("All items are either in progress or completed.")
56
+ return None
57
+
58
+ # Check dependencies and categorize
59
+ ready_items = []
60
+ blocked_items = []
61
+
62
+ for work_id, item in not_started.items():
63
+ is_blocked = self._is_blocked(item, items)
64
+ if is_blocked:
65
+ # Find what's blocking
66
+ blocking = [
67
+ dep_id
68
+ for dep_id in item.get("dependencies", [])
69
+ if items.get(dep_id, {}).get("status") != WorkItemStatus.COMPLETED.value
70
+ ]
71
+ blocked_items.append((work_id, item, blocking))
72
+ else:
73
+ ready_items.append((work_id, item))
74
+
75
+ if not ready_items:
76
+ output.info("No work items ready to start. All have unmet dependencies.\n")
77
+ output.info("Blocked items:")
78
+ for work_id, item, blocking in blocked_items:
79
+ output.info(f" 🔴 {work_id} - Blocked by: {', '.join(blocking)}")
80
+ return None
81
+
82
+ # Sort ready items by priority
83
+ priority_order = {
84
+ Priority.CRITICAL.value: 0,
85
+ Priority.HIGH.value: 1,
86
+ Priority.MEDIUM.value: 2,
87
+ Priority.LOW.value: 3,
88
+ }
89
+ ready_items.sort(key=lambda x: priority_order.get(x[1]["priority"], 99))
90
+
91
+ # Get top item
92
+ next_id, next_item = ready_items[0]
93
+
94
+ # Display
95
+ self._display_next_item(next_id, next_item, ready_items, blocked_items, items)
96
+
97
+ return next_item # type: ignore[no-any-return]
98
+
99
+ def _is_blocked(self, item: dict, all_items: dict) -> bool:
100
+ """Check if work item is blocked by dependencies
101
+
102
+ Args:
103
+ item: Work item to check
104
+ all_items: All work items
105
+
106
+ Returns:
107
+ bool: True if blocked
108
+ """
109
+ dependencies = item.get("dependencies", [])
110
+ if not dependencies:
111
+ return False
112
+
113
+ for dep_id in dependencies:
114
+ if dep_id not in all_items:
115
+ continue
116
+ if all_items[dep_id]["status"] != WorkItemStatus.COMPLETED.value:
117
+ return True
118
+
119
+ return False
120
+
121
+ def _display_next_item(
122
+ self,
123
+ next_id: str,
124
+ next_item: dict,
125
+ ready_items: list,
126
+ blocked_items: list,
127
+ all_items: dict,
128
+ ) -> None:
129
+ """Display the next recommended work item
130
+
131
+ Args:
132
+ next_id: ID of next item
133
+ next_item: Next item data
134
+ ready_items: List of ready items
135
+ blocked_items: List of blocked items
136
+ all_items: All work items
137
+ """
138
+ output.info("\nNext Recommended Work Item:")
139
+ output.info("=" * 80)
140
+ output.info("")
141
+
142
+ priority_emoji = {
143
+ Priority.CRITICAL.value: "🔴",
144
+ Priority.HIGH.value: "🟠",
145
+ Priority.MEDIUM.value: "🟡",
146
+ Priority.LOW.value: "🟢",
147
+ }
148
+
149
+ emoji = priority_emoji.get(next_item["priority"], "")
150
+ output.info(f"{emoji} {next_item['priority'].upper()}: {next_item['title']}")
151
+ output.info(f"ID: {next_id}")
152
+ output.info(f"Type: {next_item['type']}")
153
+ output.info(f"Priority: {next_item['priority']}")
154
+ output.info("Ready to start: Yes ✓")
155
+ output.info("")
156
+
157
+ # Dependencies
158
+ deps = next_item.get("dependencies", [])
159
+ if deps:
160
+ output.info("Dependencies: All satisfied")
161
+ for dep_id in deps:
162
+ output.info(f" ✓ {dep_id} (completed)")
163
+ else:
164
+ output.info("Dependencies: None")
165
+ output.info("")
166
+
167
+ # Estimated effort
168
+ estimated = next_item.get("estimated_effort", "Unknown")
169
+ output.info(f"Estimated effort: {estimated}")
170
+ output.info("")
171
+
172
+ output.info("To start: /start")
173
+ output.info("")
174
+
175
+ # Show other items
176
+ if len(ready_items) > 1 or blocked_items:
177
+ output.info("Other items waiting:")
178
+ for work_id, item in ready_items[1:3]: # Show next 2 ready items
179
+ emoji = priority_emoji.get(item["priority"], "")
180
+ output.info(f" {emoji} {work_id} - Ready ({item['priority']} priority)")
181
+
182
+ for work_id, item, blocking in blocked_items[:2]: # Show 2 blocked items
183
+ output.info(f" 🔴 {work_id} - Blocked by: {', '.join(blocking[:2])}")
184
+ output.info("")