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 @@
1
+ """Work item management including specs, validation, and CRUD operations."""
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Work Item Creator - Interactive and non-interactive work item creation.
4
+
5
+ Handles user prompts, ID generation, and spec file creation.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import re
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING
13
+
14
+ from solokit.core.error_handlers import log_errors
15
+ from solokit.core.exceptions import (
16
+ ErrorCode,
17
+ ValidationError,
18
+ WorkItemAlreadyExistsError,
19
+ )
20
+ from solokit.core.logging_config import get_logger
21
+ from solokit.core.types import WorkItemStatus, WorkItemType
22
+
23
+ if TYPE_CHECKING:
24
+ from .repository import WorkItemRepository
25
+ from solokit.core.output import get_output
26
+
27
+ logger = get_logger(__name__)
28
+ output = get_output()
29
+
30
+
31
+ class WorkItemCreator:
32
+ """Handles work item creation with interactive and non-interactive modes"""
33
+
34
+ WORK_ITEM_TYPES = WorkItemType.values()
35
+ PRIORITIES = ["critical", "high", "medium", "low"]
36
+
37
+ def __init__(self, repository: WorkItemRepository):
38
+ """Initialize creator with repository
39
+
40
+ Args:
41
+ repository: WorkItemRepository instance for data access
42
+ """
43
+ self.repository = repository
44
+ self.project_root = repository.session_dir.parent
45
+ self.specs_dir = repository.session_dir / "specs"
46
+ self.templates_dir = Path(__file__).parent.parent / "templates"
47
+
48
+ @log_errors()
49
+ def create_from_args(
50
+ self, work_type: str, title: str, priority: str = "high", dependencies: str = ""
51
+ ) -> str:
52
+ """Create work item from command-line arguments (non-interactive)
53
+
54
+ Args:
55
+ work_type: Type of work item (feature, bug, refactor, etc.)
56
+ title: Title of the work item
57
+ priority: Priority level (critical, high, medium, low)
58
+ dependencies: Comma-separated dependency IDs
59
+
60
+ Returns:
61
+ str: The created work item ID
62
+
63
+ Raises:
64
+ ValidationError: If work type is invalid
65
+ WorkItemAlreadyExistsError: If work item with generated ID already exists
66
+ """
67
+ logger.info("Creating work item from args: type=%s, title=%s", work_type, title)
68
+
69
+ # Validate work type
70
+ if work_type not in self.WORK_ITEM_TYPES:
71
+ logger.error("Invalid work item type: %s", work_type)
72
+ raise ValidationError(
73
+ message=f"Invalid work item type '{work_type}'",
74
+ code=ErrorCode.INVALID_WORK_ITEM_TYPE,
75
+ context={"work_type": work_type, "valid_types": self.WORK_ITEM_TYPES},
76
+ remediation=f"Valid types: {', '.join(self.WORK_ITEM_TYPES)}",
77
+ )
78
+
79
+ # Validate priority
80
+ if priority not in self.PRIORITIES:
81
+ logger.warning("Invalid priority '%s', using 'high'", priority)
82
+ logger.warning("Invalid priority '%s', using 'high'", priority)
83
+ priority = "high"
84
+
85
+ # Parse dependencies
86
+ dep_list = []
87
+ if dependencies:
88
+ dep_list = [d.strip() for d in dependencies.split(",") if d.strip()]
89
+ logger.debug("Parsed dependencies: %s", dep_list)
90
+ # Validate dependencies exist
91
+ for dep_id in dep_list:
92
+ if not self.repository.work_item_exists(dep_id):
93
+ logger.warning("Dependency '%s' does not exist", dep_id)
94
+ logger.warning("Warning: Dependency '%s' does not exist", dep_id)
95
+
96
+ # Generate ID
97
+ work_id = self._generate_id(work_type, title)
98
+ logger.debug("Generated work item ID: %s", work_id)
99
+
100
+ # Check for duplicates
101
+ if self.repository.work_item_exists(work_id):
102
+ logger.error("Work item %s already exists", work_id)
103
+ raise WorkItemAlreadyExistsError(work_id)
104
+
105
+ # Create specification file
106
+ spec_file = self._create_spec_file(work_id, work_type, title)
107
+ if not spec_file:
108
+ logger.warning("Could not create specification file for %s", work_id)
109
+ logger.warning("Warning: Could not create specification file")
110
+
111
+ # Add to work_items.json
112
+ self.repository.add_work_item(work_id, work_type, title, priority, dep_list, spec_file)
113
+ logger.info("Work item created: %s (type=%s, priority=%s)", work_id, work_type, priority)
114
+
115
+ # Confirm
116
+ self._print_creation_confirmation(work_id, work_type, priority, dep_list, spec_file)
117
+
118
+ return work_id
119
+
120
+ def _generate_id(self, work_type: str, title: str) -> str:
121
+ """Generate work item ID from type and title
122
+
123
+ Args:
124
+ work_type: Type of work item
125
+ title: Work item title
126
+
127
+ Returns:
128
+ str: Generated work item ID
129
+ """
130
+ # Clean title: lowercase, alphanumeric + underscore only
131
+ clean_title = re.sub(r"[^a-z0-9]+", "_", title.lower())
132
+ clean_title = clean_title.strip("_")
133
+
134
+ # Truncate if too long
135
+ if len(clean_title) > 30:
136
+ clean_title = clean_title[:30]
137
+
138
+ return f"{work_type}_{clean_title}"
139
+
140
+ def _create_spec_file(self, work_id: str, work_type: str, title: str) -> str:
141
+ """Create specification file from template
142
+
143
+ Args:
144
+ work_id: Work item ID
145
+ work_type: Type of work item
146
+ title: Work item title
147
+
148
+ Returns:
149
+ str: Relative path to the created spec file, or empty string if failed
150
+ """
151
+ # Ensure specs directory exists
152
+ self.specs_dir.mkdir(parents=True, exist_ok=True)
153
+
154
+ # Load template
155
+ template_file = self.templates_dir / f"{work_type}_spec.md"
156
+ if not template_file.exists():
157
+ return ""
158
+
159
+ template_content = template_file.read_text()
160
+
161
+ # Replace title placeholder
162
+ if work_type == "feature":
163
+ spec_content = template_content.replace("[Feature Name]", title)
164
+ elif work_type == "bug":
165
+ spec_content = template_content.replace("[Bug Title]", title)
166
+ elif work_type == "refactor":
167
+ spec_content = template_content.replace("[Refactor Title]", title)
168
+ elif work_type == "security":
169
+ spec_content = template_content.replace("[Name]", title)
170
+ elif work_type == "integration_test":
171
+ spec_content = template_content.replace("[Name]", title)
172
+ elif work_type == "deployment":
173
+ spec_content = template_content.replace("[Environment]", title)
174
+ else:
175
+ spec_content = template_content
176
+
177
+ # Save spec file
178
+ spec_path = self.specs_dir / f"{work_id}.md"
179
+ spec_path.write_text(spec_content)
180
+
181
+ # Return relative path from project root
182
+ return f".session/specs/{work_id}.md"
183
+
184
+ def _print_creation_confirmation(
185
+ self,
186
+ work_id: str,
187
+ work_type: str,
188
+ priority: str,
189
+ dependencies: list[str],
190
+ spec_file: str,
191
+ ) -> None:
192
+ """Print creation confirmation message
193
+
194
+ Args:
195
+ work_id: Created work item ID
196
+ work_type: Work item type
197
+ priority: Priority level
198
+ dependencies: List of dependency IDs
199
+ spec_file: Path to spec file
200
+ """
201
+ output.info(f"\n{'=' * 50}")
202
+ output.info("Work item created successfully!")
203
+ output.info("=" * 50)
204
+ output.info(f"\nID: {work_id}")
205
+ output.info(f"Type: {work_type}")
206
+ output.info(f"Priority: {priority}")
207
+ output.info(f"Status: {WorkItemStatus.NOT_STARTED.value}")
208
+ if dependencies:
209
+ output.info(f"Dependencies: {', '.join(dependencies)}")
210
+
211
+ if spec_file:
212
+ output.info(f"\nSpecification saved to: {spec_file}")
213
+
214
+ output.info("\nNext steps:")
215
+ output.info(f"1. Edit specification: {spec_file}")
216
+ output.info("2. Start working: /start")
217
+ output.info("")
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Work Item Deletion - Safe deletion of work items.
4
+
5
+ Handles deletion of work items with dependency checking and interactive confirmation.
6
+ """
7
+
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+ from solokit.core.error_handlers import log_errors
12
+ from solokit.core.exceptions import (
13
+ FileNotFoundError as SolokitFileNotFoundError,
14
+ )
15
+ from solokit.core.exceptions import (
16
+ FileOperationError,
17
+ ValidationError,
18
+ WorkItemNotFoundError,
19
+ )
20
+ from solokit.core.file_ops import load_json, save_json
21
+ from solokit.core.logging_config import get_logger
22
+ from solokit.core.output import get_output
23
+ from solokit.core.types import WorkItemStatus
24
+
25
+ logger = get_logger(__name__)
26
+ output = get_output()
27
+
28
+
29
+ def find_dependents(work_items: dict, work_item_id: str) -> list[str]:
30
+ """
31
+ Find work items that depend on the given work item.
32
+
33
+ Args:
34
+ work_items: Dictionary of all work items
35
+ work_item_id: ID to find dependents for
36
+
37
+ Returns:
38
+ List of work item IDs that depend on this item
39
+ """
40
+ dependents = []
41
+ for wid, item in work_items.items():
42
+ deps = item.get("dependencies", [])
43
+ if work_item_id in deps:
44
+ dependents.append(wid)
45
+ return dependents
46
+
47
+
48
+ @log_errors()
49
+ def delete_work_item(
50
+ work_item_id: str, delete_spec: Optional[bool] = None, project_root: Optional[Path] = None
51
+ ) -> bool:
52
+ """
53
+ Delete a work item from the system.
54
+
55
+ Args:
56
+ work_item_id: ID of work item to delete
57
+ delete_spec: Whether to also delete the spec file (None for interactive prompt)
58
+ project_root: Project root path (defaults to current directory)
59
+
60
+ Returns:
61
+ True if deletion successful, False if user cancels
62
+
63
+ Raises:
64
+ SolokitFileNotFoundError: If work_items.json doesn't exist
65
+ WorkItemNotFoundError: If work item ID doesn't exist
66
+ FileOperationError: If unable to load or save work items file
67
+ ValidationError: If running in non-interactive mode without flags
68
+ """
69
+ # Setup paths
70
+ if project_root is None:
71
+ project_root = Path.cwd()
72
+
73
+ session_dir = project_root / ".session"
74
+ work_items_file = session_dir / "tracking" / "work_items.json"
75
+
76
+ # Check if work items file exists
77
+ if not work_items_file.exists():
78
+ logger.error("Work items file not found")
79
+ raise SolokitFileNotFoundError(file_path=str(work_items_file), file_type="work items")
80
+
81
+ # Load work items
82
+ try:
83
+ work_items_data = load_json(work_items_file)
84
+ except (OSError, ValueError) as e:
85
+ logger.error("Failed to load work items: %s", e)
86
+ raise FileOperationError(
87
+ operation="read", file_path=str(work_items_file), details=str(e), cause=e
88
+ ) from e
89
+
90
+ work_items = work_items_data.get("work_items", {})
91
+
92
+ # Validate work item exists
93
+ if work_item_id not in work_items:
94
+ logger.error("Work item '%s' not found", work_item_id)
95
+ raise WorkItemNotFoundError(work_item_id)
96
+
97
+ item = work_items[work_item_id]
98
+
99
+ # Find dependents
100
+ dependents = find_dependents(work_items, work_item_id)
101
+
102
+ # Show work item details
103
+ output.warning(f"\nThis will permanently delete work item '{work_item_id}'")
104
+ output.info("\nWork item details:")
105
+ output.info(f" Title: {item.get('title', 'N/A')}")
106
+ output.info(f" Type: {item.get('type', 'N/A')}")
107
+ output.info(f" Status: {item.get('status', 'N/A')}")
108
+
109
+ dependencies = item.get("dependencies", [])
110
+ if dependencies:
111
+ output.info(f" Dependencies: {', '.join(dependencies)}")
112
+ else:
113
+ output.info(" Dependencies: none")
114
+
115
+ if dependents:
116
+ output.info(
117
+ f" Dependents: {', '.join(dependents)} ({len(dependents)} item(s) depend on this)"
118
+ )
119
+ else:
120
+ output.info(" Dependents: none")
121
+
122
+ # Require explicit flag (no interactive mode)
123
+ if delete_spec is None:
124
+ logger.error("Must specify --keep-spec or --delete-spec flag")
125
+ raise ValidationError(
126
+ message="Must specify either --keep-spec or --delete-spec flag",
127
+ remediation=(
128
+ "Use command-line flags:\n"
129
+ " sk work-delete <work_item_id> --keep-spec (delete work item only)\n"
130
+ " sk work-delete <work_item_id> --delete-spec (delete work item and spec)"
131
+ ),
132
+ )
133
+
134
+ # Show what will be done
135
+ if delete_spec:
136
+ output.info("\n→ Will delete work item and spec file")
137
+ else:
138
+ output.info("\n→ Will delete work item only (keeping spec file)")
139
+
140
+ # Perform deletion
141
+ logger.info("Deleting work item '%s'", work_item_id)
142
+ del work_items[work_item_id]
143
+
144
+ # Update metadata
145
+ work_items_data["work_items"] = work_items
146
+ if "metadata" not in work_items_data:
147
+ work_items_data["metadata"] = {}
148
+
149
+ work_items_data["metadata"]["total_items"] = len(work_items)
150
+ work_items_data["metadata"]["completed"] = sum(
151
+ 1 for item in work_items.values() if item["status"] == WorkItemStatus.COMPLETED.value
152
+ )
153
+ work_items_data["metadata"]["in_progress"] = sum(
154
+ 1 for item in work_items.values() if item["status"] == WorkItemStatus.IN_PROGRESS.value
155
+ )
156
+ work_items_data["metadata"]["blocked"] = sum(
157
+ 1 for item in work_items.values() if item["status"] == WorkItemStatus.BLOCKED.value
158
+ )
159
+
160
+ # Save work items
161
+ try:
162
+ save_json(work_items_file, work_items_data)
163
+ logger.info("Successfully updated work_items.json")
164
+ output.info(f"✓ Deleted work item '{work_item_id}'")
165
+ except OSError as e:
166
+ logger.error("Failed to save work items: %s", e)
167
+ raise FileOperationError(
168
+ operation="write", file_path=str(work_items_file), details=str(e), cause=e
169
+ ) from e
170
+
171
+ # Delete spec file if requested
172
+ if delete_spec:
173
+ spec_file_path = item.get("spec_file", f".session/specs/{work_item_id}.md")
174
+ spec_path = project_root / spec_file_path
175
+
176
+ if spec_path.exists():
177
+ try:
178
+ spec_path.unlink()
179
+ logger.info("Deleted spec file: %s", spec_file_path)
180
+ output.info(f"✓ Deleted spec file '{spec_file_path}'")
181
+ except (OSError, PermissionError) as e:
182
+ logger.warning("Failed to delete spec file: %s", e)
183
+ output.warning(f"Could not delete spec file: {e}")
184
+ else:
185
+ logger.debug("Spec file not found: %s", spec_file_path)
186
+ output.info(f"Note: Spec file '{spec_file_path}' not found")
187
+
188
+ # Warn about dependents
189
+ if dependents:
190
+ output.warning("\nThe following work items depend on this item:")
191
+ for dep in dependents:
192
+ output.info(f" - {dep}")
193
+ output.info(" Update their dependencies manually if needed.")
194
+
195
+ output.info("\nDeletion successful.")
196
+ logger.info("Work item deletion completed successfully")
197
+ return True
198
+
199
+
200
+ def main() -> int:
201
+ """CLI entry point for work item deletion."""
202
+ import argparse
203
+
204
+ parser = argparse.ArgumentParser(description="Delete a work item")
205
+ parser.add_argument("work_item_id", help="ID of work item to delete")
206
+ parser.add_argument(
207
+ "--keep-spec",
208
+ action="store_true",
209
+ help="Keep the spec file (delete work item only)",
210
+ )
211
+ parser.add_argument(
212
+ "--delete-spec",
213
+ action="store_true",
214
+ help="Delete both work item and spec file",
215
+ )
216
+
217
+ args = parser.parse_args()
218
+
219
+ # Determine delete_spec value
220
+ delete_spec_value = None
221
+ if args.keep_spec and args.delete_spec:
222
+ raise ValidationError(
223
+ message="Cannot specify both --keep-spec and --delete-spec",
224
+ remediation="Choose only one option: --keep-spec OR --delete-spec",
225
+ )
226
+ elif args.keep_spec:
227
+ delete_spec_value = False
228
+ elif args.delete_spec:
229
+ delete_spec_value = True
230
+
231
+ # Perform deletion
232
+ try:
233
+ success = delete_work_item(args.work_item_id, delete_spec=delete_spec_value)
234
+ return 0 if success else 1
235
+ except WorkItemNotFoundError as e:
236
+ output.info(f"❌ Error: {e.message}")
237
+ if e.remediation:
238
+ output.info(f"\n{e.remediation}")
239
+ # Show available work items
240
+ try:
241
+ from pathlib import Path
242
+
243
+ work_items_file = Path.cwd() / ".session" / "tracking" / "work_items.json"
244
+ if work_items_file.exists():
245
+ work_items_data = load_json(work_items_file)
246
+ work_items = work_items_data.get("work_items", {})
247
+ if work_items:
248
+ output.info("\nAvailable work items:")
249
+ for wid in list(work_items.keys())[:5]:
250
+ output.info(f" - {wid}")
251
+ if len(work_items) > 5:
252
+ output.info(f" ... and {len(work_items) - 5} more")
253
+ except Exception: # noqa: BLE001 - This is optional enhancement, don't fail on it
254
+ pass
255
+ return e.exit_code
256
+ except (SolokitFileNotFoundError, FileOperationError, ValidationError) as e:
257
+ output.info(f"❌ Error: {e.message}")
258
+ if e.remediation:
259
+ output.info(f"\n{e.remediation}")
260
+ return e.exit_code
261
+
262
+
263
+ if __name__ == "__main__":
264
+ exit(main())
@@ -0,0 +1,185 @@
1
+ """Fast dependency information retrieval for Claude Code integration.
2
+
3
+ This module provides optimized dependency lookups without reading full spec files.
4
+ Used by /work-new and /work-delete commands to fetch available dependencies.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import sys
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+
15
+ def get_available_dependencies(
16
+ exclude_statuses: list[str] | None = None,
17
+ title_filter: str | None = None,
18
+ max_results: int = 3,
19
+ ) -> list[dict[str, Any]]:
20
+ """Get available work items that can be used as dependencies.
21
+
22
+ Args:
23
+ exclude_statuses: List of statuses to exclude (default: ["completed"])
24
+ title_filter: Optional title to use for smart filtering/relevance
25
+ max_results: Maximum number of results to return (default: 3)
26
+
27
+ Returns:
28
+ List of dependency info dicts with keys: id, type, title, status
29
+
30
+ Raises:
31
+ FileNotFoundError: If work_items.json doesn't exist
32
+ json.JSONDecodeError: If work_items.json is invalid
33
+ """
34
+ if exclude_statuses is None:
35
+ exclude_statuses = ["completed"]
36
+
37
+ # Find .session directory
38
+ session_dir = _find_session_dir()
39
+ if not session_dir:
40
+ print("Error: Not in an Solokit project (no .session directory found)", file=sys.stderr)
41
+ return []
42
+
43
+ # Load work items
44
+ work_items_file = session_dir / "tracking" / "work_items.json"
45
+ if not work_items_file.exists():
46
+ print(f"Error: Work items file not found: {work_items_file}", file=sys.stderr)
47
+ return []
48
+
49
+ try:
50
+ with open(work_items_file) as f:
51
+ data = json.load(f)
52
+ except json.JSONDecodeError as e:
53
+ print(f"Error: Invalid JSON in {work_items_file}: {e}", file=sys.stderr)
54
+ return []
55
+
56
+ # Extract work_items from the data structure
57
+ work_items = data.get("work_items", {})
58
+ if not work_items:
59
+ print("No work items found", file=sys.stderr)
60
+ return []
61
+
62
+ # Filter available dependencies
63
+ available = []
64
+ for work_id, item in work_items.items():
65
+ status = item.get("status", "unknown")
66
+ if status not in exclude_statuses:
67
+ available.append(
68
+ {
69
+ "id": work_id,
70
+ "type": item.get("type", "unknown"),
71
+ "title": item.get("title", "Untitled"),
72
+ "status": status,
73
+ }
74
+ )
75
+
76
+ # Apply smart filtering if title provided
77
+ if title_filter and available:
78
+ available = _filter_by_relevance(available, title_filter)
79
+
80
+ # Limit results
81
+ return available[:max_results]
82
+
83
+
84
+ def _find_session_dir() -> Path | None:
85
+ """Find the .session directory by walking up from current directory."""
86
+ current = Path.cwd()
87
+ while current != current.parent:
88
+ session_dir = current / ".session"
89
+ if session_dir.is_dir():
90
+ return session_dir
91
+ current = current.parent
92
+ return None
93
+
94
+
95
+ def _filter_by_relevance(items: list[dict], title: str) -> list[dict]:
96
+ """Filter and sort items by relevance to the given title.
97
+
98
+ Simple keyword-based relevance scoring:
99
+ - Exact word matches in title get highest score
100
+ - Partial matches get medium score
101
+ - Items with same type get bonus points
102
+
103
+ Args:
104
+ items: List of work item dicts
105
+ title: Title to compare against
106
+
107
+ Returns:
108
+ Sorted list (most relevant first)
109
+ """
110
+ title_lower = title.lower()
111
+ title_words = set(title_lower.split())
112
+
113
+ def relevance_score(item: dict) -> float:
114
+ item_title_lower = item["title"].lower()
115
+ item_words = set(item_title_lower.split())
116
+
117
+ # Exact word matches (high weight)
118
+ word_matches = len(title_words & item_words)
119
+
120
+ # Partial matches (medium weight)
121
+ partial_matches = sum(1 for tw in title_words for iw in item_words if tw in iw or iw in tw)
122
+
123
+ # Calculate score
124
+ score = (word_matches * 3.0) + (partial_matches * 1.5)
125
+
126
+ return score
127
+
128
+ # Score and sort
129
+ scored = [(item, relevance_score(item)) for item in items]
130
+ scored.sort(key=lambda x: x[1], reverse=True)
131
+
132
+ # Return only items with non-zero scores, or all if none match
133
+ relevant = [item for item, score in scored if score > 0]
134
+ return relevant if relevant else items
135
+
136
+
137
+ def main() -> None:
138
+ """CLI entry point for get_dependencies script.
139
+
140
+ Usage:
141
+ python -m solokit.work_items.get_dependencies
142
+ python -m solokit.work_items.get_dependencies --title "My new feature"
143
+ python -m solokit.work_items.get_dependencies --title "Bug fix" --max 5
144
+ python -m solokit.work_items.get_dependencies --exclude-status not_started,in_progress
145
+ """
146
+ import argparse
147
+
148
+ parser = argparse.ArgumentParser(description="Get available dependencies for work items")
149
+ parser.add_argument("--title", help="Optional title for smart filtering by relevance")
150
+ parser.add_argument("--max", type=int, default=3, help="Maximum number of results (default: 3)")
151
+ parser.add_argument(
152
+ "--exclude-status", help="Comma-separated list of statuses to exclude (default: completed)"
153
+ )
154
+
155
+ args = parser.parse_args()
156
+
157
+ # Parse exclude statuses
158
+ exclude = None
159
+ if args.exclude_status:
160
+ exclude = [s.strip() for s in args.exclude_status.split(",")]
161
+
162
+ # Get dependencies
163
+ dependencies = get_available_dependencies(
164
+ exclude_statuses=exclude,
165
+ title_filter=args.title,
166
+ max_results=args.max,
167
+ )
168
+
169
+ # Output results
170
+ if not dependencies:
171
+ print("No available dependencies found")
172
+ sys.exit(1)
173
+
174
+ print(f"Found {len(dependencies)} available dependencies:")
175
+ print()
176
+ for dep in dependencies:
177
+ print(f"ID: {dep['id']}")
178
+ print(f"Type: {dep['type']}")
179
+ print(f"Title: {dep['title']}")
180
+ print(f"Status: {dep['status']}")
181
+ print()
182
+
183
+
184
+ if __name__ == "__main__":
185
+ main()