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,346 @@
1
+ """
2
+ Error handling decorators and utilities.
3
+
4
+ Provides decorators for common error handling patterns like retry,
5
+ timeout, and logging. Separates error handling concerns from business logic.
6
+
7
+ Usage:
8
+ from solokit.core.error_handlers import with_retry, log_errors
9
+
10
+ @log_errors()
11
+ @with_retry(max_attempts=3)
12
+ def load_config(path: str) -> dict:
13
+ return json.load(open(path))
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import functools
19
+ import logging
20
+ import subprocess
21
+ import time
22
+ from typing import Any, Callable, Literal, TypeVar
23
+
24
+ from solokit.core.exceptions import ErrorCode, GitError, SolokitError, SubprocessError, SystemError
25
+ from solokit.core.exceptions import TimeoutError as SolokitTimeoutError
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ T = TypeVar("T")
30
+
31
+
32
+ def with_timeout(
33
+ seconds: int, operation_name: str
34
+ ) -> Callable[[Callable[..., T]], Callable[..., T]]:
35
+ """
36
+ Decorator to add timeout to function execution.
37
+
38
+ Note: This uses signal.alarm which only works on Unix systems.
39
+ On Windows, the timeout is not enforced but the function still runs.
40
+
41
+ Args:
42
+ seconds: Timeout in seconds
43
+ operation_name: Name of operation for error message
44
+
45
+ Returns:
46
+ Decorator function
47
+
48
+ Raises:
49
+ TimeoutError: If function exceeds timeout
50
+
51
+ Example:
52
+ >>> @with_timeout(seconds=5, operation_name="fetch data")
53
+ ... def fetch_data():
54
+ ... time.sleep(10) # Will timeout
55
+ """
56
+
57
+ def decorator(func: Callable[..., T]) -> Callable[..., T]:
58
+ @functools.wraps(func)
59
+ def wrapper(*args: Any, **kwargs: Any) -> T:
60
+ try:
61
+ import signal
62
+
63
+ def timeout_handler(signum: int, frame: Any) -> None:
64
+ raise SolokitTimeoutError(operation=operation_name, timeout_seconds=seconds)
65
+
66
+ # Set up timeout signal (Unix only)
67
+ old_handler = signal.signal(signal.SIGALRM, timeout_handler)
68
+ signal.alarm(seconds)
69
+
70
+ try:
71
+ return func(*args, **kwargs)
72
+ finally:
73
+ signal.alarm(0)
74
+ signal.signal(signal.SIGALRM, old_handler)
75
+ except AttributeError:
76
+ # Windows doesn't have SIGALRM, just run without timeout
77
+ logger.warning(f"Timeout not supported on this platform for {operation_name}")
78
+ return func(*args, **kwargs)
79
+
80
+ return wrapper
81
+
82
+ return decorator
83
+
84
+
85
+ def with_retry(
86
+ max_attempts: int = 3,
87
+ delay_seconds: float = 1.0,
88
+ backoff_multiplier: float = 2.0,
89
+ exceptions: tuple[type[Exception], ...] = (Exception,),
90
+ ) -> Callable[[Callable[..., T]], Callable[..., T]]:
91
+ """
92
+ Decorator to retry function on failure.
93
+
94
+ Args:
95
+ max_attempts: Maximum number of attempts
96
+ delay_seconds: Initial delay between attempts
97
+ backoff_multiplier: Multiply delay by this after each attempt
98
+ exceptions: Tuple of exceptions to catch and retry
99
+
100
+ Returns:
101
+ Decorator function
102
+
103
+ Example:
104
+ >>> @with_retry(max_attempts=3, delay_seconds=2.0)
105
+ ... def load_file(path: Path) -> dict:
106
+ ... return json.load(open(path))
107
+ """
108
+
109
+ def decorator(func: Callable[..., T]) -> Callable[..., T]:
110
+ @functools.wraps(func)
111
+ def wrapper(*args: Any, **kwargs: Any) -> T:
112
+ delay = delay_seconds
113
+ last_exception = None
114
+
115
+ for attempt in range(1, max_attempts + 1):
116
+ try:
117
+ return func(*args, **kwargs)
118
+ except exceptions as e:
119
+ last_exception = e
120
+ if attempt < max_attempts:
121
+ logger.warning(
122
+ f"Attempt {attempt}/{max_attempts} failed for {func.__name__}: {e}. "
123
+ f"Retrying in {delay}s..."
124
+ )
125
+ time.sleep(delay)
126
+ delay *= backoff_multiplier
127
+ else:
128
+ logger.error(f"All {max_attempts} attempts failed for {func.__name__}")
129
+
130
+ # Re-raise the last exception
131
+ if last_exception:
132
+ raise last_exception
133
+ # This should never happen, but mypy needs it
134
+ raise RuntimeError(f"{func.__name__} completed without returning or raising")
135
+
136
+ return wrapper
137
+
138
+ return decorator
139
+
140
+
141
+ def log_errors(
142
+ logger_instance: logging.Logger | None = None,
143
+ ) -> Callable[[Callable[..., T]], Callable[..., T]]:
144
+ """
145
+ Decorator to log exceptions with structured data.
146
+
147
+ Args:
148
+ logger_instance: Logger to use (defaults to function's module logger)
149
+
150
+ Returns:
151
+ Decorator function
152
+
153
+ Example:
154
+ >>> @log_errors()
155
+ ... def process_work_item(item_id: str):
156
+ ... # Business logic that may raise SolokitError
157
+ ... pass
158
+ """
159
+
160
+ def decorator(func: Callable[..., T]) -> Callable[..., T]:
161
+ @functools.wraps(func)
162
+ def wrapper(*args: Any, **kwargs: Any) -> T:
163
+ log = logger_instance or logging.getLogger(func.__module__)
164
+
165
+ try:
166
+ return func(*args, **kwargs)
167
+ except SolokitError as e:
168
+ # Log structured error data
169
+ log.error(
170
+ f"{func.__name__} failed: {e.message}",
171
+ extra={
172
+ "error_code": e.code.value,
173
+ "error_category": e.category.value,
174
+ "context": e.context,
175
+ "function": func.__name__,
176
+ },
177
+ )
178
+ raise
179
+ except Exception as e: # noqa: BLE001 - Logging decorator catches all for observability
180
+ # Log unexpected errors
181
+ log.exception(
182
+ f"{func.__name__} failed with unexpected error: {e}",
183
+ extra={"function": func.__name__},
184
+ )
185
+ raise
186
+
187
+ return wrapper
188
+
189
+ return decorator
190
+
191
+
192
+ def convert_subprocess_errors(func: Callable[..., T]) -> Callable[..., T]:
193
+ """
194
+ Decorator to convert subprocess exceptions to SolokitError.
195
+
196
+ Converts FileNotFoundError and subprocess exceptions to structured errors.
197
+
198
+ Example:
199
+ >>> @convert_subprocess_errors
200
+ ... def run_git_command(args: list[str]) -> str:
201
+ ... result = subprocess.run(["git"] + args, check=True, capture_output=True, text=True)
202
+ ... return result.stdout
203
+ """
204
+
205
+ @functools.wraps(func)
206
+ def wrapper(*args: Any, **kwargs: Any) -> T:
207
+ try:
208
+ return func(*args, **kwargs)
209
+ except subprocess.TimeoutExpired as e:
210
+ raise SolokitTimeoutError(
211
+ operation=f"subprocess: {' '.join(e.cmd) if isinstance(e.cmd, list) else e.cmd}",
212
+ timeout_seconds=int(e.timeout),
213
+ context={"stdout": e.stdout, "stderr": e.stderr},
214
+ ) from e
215
+ except FileNotFoundError as e:
216
+ # Command not found (e.g., git not installed)
217
+ cmd_name = e.filename or "unknown command"
218
+ raise GitError(
219
+ message=f"Command not found: {cmd_name}",
220
+ code=ErrorCode.GIT_NOT_FOUND,
221
+ remediation=f"Install {cmd_name} or ensure it's in your PATH",
222
+ cause=e,
223
+ ) from e
224
+ except subprocess.CalledProcessError as e:
225
+ raise SubprocessError(
226
+ command=" ".join(e.cmd) if isinstance(e.cmd, list) else str(e.cmd),
227
+ returncode=e.returncode,
228
+ stderr=e.stderr.decode() if isinstance(e.stderr, bytes) else e.stderr,
229
+ stdout=e.stdout.decode() if isinstance(e.stdout, bytes) else e.stdout,
230
+ ) from e
231
+
232
+ return wrapper
233
+
234
+
235
+ def convert_file_errors(func: Callable[..., T]) -> Callable[..., T]:
236
+ """
237
+ Decorator to convert file operation exceptions to SolokitError.
238
+
239
+ Converts IOError, OSError, FileNotFoundError, etc. to structured errors.
240
+
241
+ Example:
242
+ >>> @convert_file_errors
243
+ ... def read_config(path: str) -> dict:
244
+ ... with open(path) as f:
245
+ ... return json.load(f)
246
+ """
247
+
248
+ @functools.wraps(func)
249
+ def wrapper(*args: Any, **kwargs: Any) -> T:
250
+ import builtins
251
+
252
+ from solokit.core.exceptions import FileNotFoundError as SolokitFileNotFoundError
253
+
254
+ try:
255
+ return func(*args, **kwargs)
256
+ except builtins.FileNotFoundError as e:
257
+ # Catch FileNotFoundError first (it's a subclass of OSError)
258
+ file_path = getattr(e, "filename", "unknown")
259
+ raise SolokitFileNotFoundError(file_path=file_path) from e
260
+ except OSError as e:
261
+ # Extract file path from exception if available
262
+ file_path = getattr(e, "filename", "unknown")
263
+ raise SystemError(
264
+ message=f"File operation failed: {e}",
265
+ code=ErrorCode.FILE_OPERATION_FAILED,
266
+ context={"file_path": file_path, "error": str(e)},
267
+ cause=e,
268
+ ) from e
269
+
270
+ return wrapper
271
+
272
+
273
+ class ErrorContext:
274
+ """
275
+ Context manager for handling errors with cleanup.
276
+
277
+ Example:
278
+ >>> with ErrorContext("processing work item", work_item_id=item_id):
279
+ ... # Do work that may raise errors
280
+ ... process_item(item_id)
281
+ """
282
+
283
+ def __init__(
284
+ self, operation: str, cleanup: Callable[[], None] | None = None, **context_data: Any
285
+ ) -> None:
286
+ self.operation = operation
287
+ self.cleanup = cleanup
288
+ self.context_data = context_data
289
+
290
+ def __enter__(self) -> ErrorContext:
291
+ return self
292
+
293
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> Literal[False]:
294
+ # Run cleanup regardless of success/failure
295
+ if self.cleanup:
296
+ try:
297
+ self.cleanup()
298
+ except Exception as e: # noqa: BLE001 - Cleanup must not fail the main operation
299
+ logger.error(f"Cleanup failed for {self.operation}: {e}")
300
+
301
+ # Add context to SolokitError if present
302
+ if exc_type and issubclass(exc_type, SolokitError):
303
+ exc_val.context.update(self.context_data)
304
+
305
+ # Don't suppress exception
306
+ return False
307
+
308
+
309
+ def safe_execute(
310
+ func: Callable[..., T],
311
+ *args: Any,
312
+ default: T | None = None,
313
+ log_errors: bool = True,
314
+ **kwargs: Any,
315
+ ) -> T | None:
316
+ """
317
+ Execute function and return default value on error instead of raising.
318
+
319
+ Useful for optional operations where failure should not stop execution.
320
+
321
+ Args:
322
+ func: Function to execute
323
+ *args: Positional arguments for func
324
+ default: Value to return on error
325
+ log_errors: Whether to log errors
326
+ **kwargs: Keyword arguments for func
327
+
328
+ Returns:
329
+ Function result or default value
330
+
331
+ Example:
332
+ >>> result = safe_execute(
333
+ ... load_optional_config,
334
+ ... "/path/to/config.json",
335
+ ... default={}
336
+ ... )
337
+ """
338
+ try:
339
+ return func(*args, **kwargs)
340
+ except Exception as e: # noqa: BLE001 - safe_execute is designed to catch all errors
341
+ if log_errors:
342
+ logger.warning(
343
+ f"Optional operation failed: {func.__name__}: {e}",
344
+ extra={"function": func.__name__, "error": str(e)},
345
+ )
346
+ return default