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.
- solokit/__init__.py +10 -0
- solokit/__version__.py +3 -0
- solokit/cli.py +374 -0
- solokit/core/__init__.py +1 -0
- solokit/core/cache.py +102 -0
- solokit/core/command_runner.py +278 -0
- solokit/core/config.py +453 -0
- solokit/core/config_validator.py +204 -0
- solokit/core/constants.py +291 -0
- solokit/core/error_formatter.py +279 -0
- solokit/core/error_handlers.py +346 -0
- solokit/core/exceptions.py +1567 -0
- solokit/core/file_ops.py +309 -0
- solokit/core/logging_config.py +166 -0
- solokit/core/output.py +99 -0
- solokit/core/performance.py +57 -0
- solokit/core/protocols.py +141 -0
- solokit/core/types.py +312 -0
- solokit/deployment/__init__.py +1 -0
- solokit/deployment/executor.py +411 -0
- solokit/git/__init__.py +1 -0
- solokit/git/integration.py +619 -0
- solokit/init/__init__.py +41 -0
- solokit/init/claude_commands_installer.py +87 -0
- solokit/init/dependency_installer.py +313 -0
- solokit/init/docs_structure.py +90 -0
- solokit/init/env_generator.py +160 -0
- solokit/init/environment_validator.py +334 -0
- solokit/init/git_hooks_installer.py +71 -0
- solokit/init/git_setup.py +188 -0
- solokit/init/gitignore_updater.py +195 -0
- solokit/init/initial_commit.py +145 -0
- solokit/init/initial_scans.py +109 -0
- solokit/init/orchestrator.py +246 -0
- solokit/init/readme_generator.py +207 -0
- solokit/init/session_structure.py +239 -0
- solokit/init/template_installer.py +424 -0
- solokit/learning/__init__.py +1 -0
- solokit/learning/archiver.py +115 -0
- solokit/learning/categorizer.py +126 -0
- solokit/learning/curator.py +428 -0
- solokit/learning/extractor.py +352 -0
- solokit/learning/reporter.py +351 -0
- solokit/learning/repository.py +254 -0
- solokit/learning/similarity.py +342 -0
- solokit/learning/validator.py +144 -0
- solokit/project/__init__.py +1 -0
- solokit/project/init.py +1162 -0
- solokit/project/stack.py +436 -0
- solokit/project/sync_plugin.py +438 -0
- solokit/project/tree.py +375 -0
- solokit/quality/__init__.py +1 -0
- solokit/quality/api_validator.py +424 -0
- solokit/quality/checkers/__init__.py +25 -0
- solokit/quality/checkers/base.py +114 -0
- solokit/quality/checkers/context7.py +221 -0
- solokit/quality/checkers/custom.py +162 -0
- solokit/quality/checkers/deployment.py +323 -0
- solokit/quality/checkers/documentation.py +179 -0
- solokit/quality/checkers/formatting.py +161 -0
- solokit/quality/checkers/integration.py +394 -0
- solokit/quality/checkers/linting.py +159 -0
- solokit/quality/checkers/security.py +261 -0
- solokit/quality/checkers/spec_completeness.py +127 -0
- solokit/quality/checkers/tests.py +184 -0
- solokit/quality/env_validator.py +306 -0
- solokit/quality/gates.py +655 -0
- solokit/quality/reporters/__init__.py +10 -0
- solokit/quality/reporters/base.py +25 -0
- solokit/quality/reporters/console.py +98 -0
- solokit/quality/reporters/json_reporter.py +34 -0
- solokit/quality/results.py +98 -0
- solokit/session/__init__.py +1 -0
- solokit/session/briefing/__init__.py +245 -0
- solokit/session/briefing/documentation_loader.py +53 -0
- solokit/session/briefing/formatter.py +476 -0
- solokit/session/briefing/git_context.py +282 -0
- solokit/session/briefing/learning_loader.py +212 -0
- solokit/session/briefing/milestone_builder.py +78 -0
- solokit/session/briefing/orchestrator.py +137 -0
- solokit/session/briefing/stack_detector.py +51 -0
- solokit/session/briefing/tree_generator.py +52 -0
- solokit/session/briefing/work_item_loader.py +209 -0
- solokit/session/briefing.py +353 -0
- solokit/session/complete.py +1188 -0
- solokit/session/status.py +246 -0
- solokit/session/validate.py +452 -0
- solokit/templates/.claude/commands/end.md +109 -0
- solokit/templates/.claude/commands/init.md +159 -0
- solokit/templates/.claude/commands/learn-curate.md +88 -0
- solokit/templates/.claude/commands/learn-search.md +62 -0
- solokit/templates/.claude/commands/learn-show.md +69 -0
- solokit/templates/.claude/commands/learn.md +136 -0
- solokit/templates/.claude/commands/start.md +114 -0
- solokit/templates/.claude/commands/status.md +22 -0
- solokit/templates/.claude/commands/validate.md +27 -0
- solokit/templates/.claude/commands/work-delete.md +119 -0
- solokit/templates/.claude/commands/work-graph.md +139 -0
- solokit/templates/.claude/commands/work-list.md +26 -0
- solokit/templates/.claude/commands/work-new.md +114 -0
- solokit/templates/.claude/commands/work-next.md +25 -0
- solokit/templates/.claude/commands/work-show.md +24 -0
- solokit/templates/.claude/commands/work-update.md +141 -0
- solokit/templates/CHANGELOG.md +17 -0
- solokit/templates/WORK_ITEM_TYPES.md +141 -0
- solokit/templates/__init__.py +1 -0
- solokit/templates/bug_spec.md +217 -0
- solokit/templates/config.schema.json +150 -0
- solokit/templates/dashboard_refine/base/.gitignore +36 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/layout.tsx +22 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/page.tsx +68 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/users/page.tsx +77 -0
- solokit/templates/dashboard_refine/base/app/globals.css +60 -0
- solokit/templates/dashboard_refine/base/app/layout.tsx +23 -0
- solokit/templates/dashboard_refine/base/app/page.tsx +9 -0
- solokit/templates/dashboard_refine/base/components/client-refine-wrapper.tsx +21 -0
- solokit/templates/dashboard_refine/base/components/layout/header.tsx +44 -0
- solokit/templates/dashboard_refine/base/components/layout/sidebar.tsx +82 -0
- solokit/templates/dashboard_refine/base/components/ui/button.tsx +53 -0
- solokit/templates/dashboard_refine/base/components/ui/card.tsx +78 -0
- solokit/templates/dashboard_refine/base/components/ui/table.tsx +116 -0
- solokit/templates/dashboard_refine/base/components.json +16 -0
- solokit/templates/dashboard_refine/base/lib/refine.tsx +65 -0
- solokit/templates/dashboard_refine/base/lib/utils.ts +13 -0
- solokit/templates/dashboard_refine/base/next.config.ts +10 -0
- solokit/templates/dashboard_refine/base/package.json.template +40 -0
- solokit/templates/dashboard_refine/base/postcss.config.mjs +8 -0
- solokit/templates/dashboard_refine/base/providers/refine-provider.tsx +26 -0
- solokit/templates/dashboard_refine/base/tailwind.config.ts +57 -0
- solokit/templates/dashboard_refine/base/tsconfig.json +27 -0
- solokit/templates/dashboard_refine/docker/Dockerfile +57 -0
- solokit/templates/dashboard_refine/docker/docker-compose.prod.yml +31 -0
- solokit/templates/dashboard_refine/docker/docker-compose.yml +21 -0
- solokit/templates/dashboard_refine/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/dashboard_refine/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/dashboard_refine/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/dashboard_refine/tier-1-essential/package.json.tier1.template +57 -0
- solokit/templates/dashboard_refine/tier-1-essential/tests/setup.ts +26 -0
- solokit/templates/dashboard_refine/tier-1-essential/tests/unit/example.test.tsx +73 -0
- solokit/templates/dashboard_refine/tier-2-standard/package.json.tier2.template +62 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/eslint.config.mjs +22 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/package.json.tier3.template +79 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/stryker.conf.json +38 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/e2e/dashboard.spec.ts +88 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/e2e/user-management.spec.ts +102 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/integration/dashboard.test.tsx +90 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/type-coverage.json +16 -0
- solokit/templates/dashboard_refine/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/dashboard_refine/tier-4-production/k6/dashboard-load-test.js +70 -0
- solokit/templates/dashboard_refine/tier-4-production/next.config.ts +46 -0
- solokit/templates/dashboard_refine/tier-4-production/package.json.tier4.template +89 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/deployment_spec.md +500 -0
- solokit/templates/feature_spec.md +248 -0
- solokit/templates/fullstack_nextjs/base/.gitignore +36 -0
- solokit/templates/fullstack_nextjs/base/app/api/example/route.ts +65 -0
- solokit/templates/fullstack_nextjs/base/app/globals.css +27 -0
- solokit/templates/fullstack_nextjs/base/app/layout.tsx +20 -0
- solokit/templates/fullstack_nextjs/base/app/page.tsx +32 -0
- solokit/templates/fullstack_nextjs/base/components/example-component.tsx +20 -0
- solokit/templates/fullstack_nextjs/base/lib/prisma.ts +17 -0
- solokit/templates/fullstack_nextjs/base/lib/utils.ts +13 -0
- solokit/templates/fullstack_nextjs/base/lib/validations.ts +20 -0
- solokit/templates/fullstack_nextjs/base/next.config.ts +7 -0
- solokit/templates/fullstack_nextjs/base/package.json.template +32 -0
- solokit/templates/fullstack_nextjs/base/postcss.config.mjs +8 -0
- solokit/templates/fullstack_nextjs/base/prisma/schema.prisma +21 -0
- solokit/templates/fullstack_nextjs/base/tailwind.config.ts +19 -0
- solokit/templates/fullstack_nextjs/base/tsconfig.json +27 -0
- solokit/templates/fullstack_nextjs/docker/Dockerfile +60 -0
- solokit/templates/fullstack_nextjs/docker/docker-compose.prod.yml +57 -0
- solokit/templates/fullstack_nextjs/docker/docker-compose.yml +47 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/package.json.tier1.template +48 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/api/example.test.ts +88 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/setup.ts +22 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/unit/example.test.tsx +22 -0
- solokit/templates/fullstack_nextjs/tier-2-standard/package.json.tier2.template +52 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/eslint.config.mjs +39 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/package.json.tier3.template +68 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/stryker.conf.json +33 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/tests/e2e/flow.spec.ts +59 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/tests/integration/api.test.ts +165 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/type-coverage.json +12 -0
- solokit/templates/fullstack_nextjs/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/fullstack_nextjs/tier-4-production/k6/load-test.js +45 -0
- solokit/templates/fullstack_nextjs/tier-4-production/next.config.ts +46 -0
- solokit/templates/fullstack_nextjs/tier-4-production/package.json.tier4.template +77 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/git-hooks/prepare-commit-msg +24 -0
- solokit/templates/integration_test_spec.md +363 -0
- solokit/templates/learnings.json +15 -0
- solokit/templates/ml_ai_fastapi/base/.gitignore +104 -0
- solokit/templates/ml_ai_fastapi/base/alembic/env.py +96 -0
- solokit/templates/ml_ai_fastapi/base/alembic.ini +114 -0
- solokit/templates/ml_ai_fastapi/base/pyproject.toml.template +91 -0
- solokit/templates/ml_ai_fastapi/base/requirements.txt.template +28 -0
- solokit/templates/ml_ai_fastapi/base/src/__init__.py +5 -0
- solokit/templates/ml_ai_fastapi/base/src/api/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/api/dependencies.py +20 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/example.py +134 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/health.py +66 -0
- solokit/templates/ml_ai_fastapi/base/src/core/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/core/config.py +64 -0
- solokit/templates/ml_ai_fastapi/base/src/core/database.py +50 -0
- solokit/templates/ml_ai_fastapi/base/src/main.py +64 -0
- solokit/templates/ml_ai_fastapi/base/src/models/__init__.py +7 -0
- solokit/templates/ml_ai_fastapi/base/src/models/example.py +61 -0
- solokit/templates/ml_ai_fastapi/base/src/services/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/services/example.py +115 -0
- solokit/templates/ml_ai_fastapi/docker/Dockerfile +59 -0
- solokit/templates/ml_ai_fastapi/docker/docker-compose.prod.yml +112 -0
- solokit/templates/ml_ai_fastapi/docker/docker-compose.yml +77 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pyproject.toml.tier1.template +112 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pyrightconfig.json +41 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pytest.ini +69 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/requirements-dev.txt +17 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/ruff.toml +81 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/conftest.py +72 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/test_main.py +49 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/unit/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/unit/test_example.py +113 -0
- solokit/templates/ml_ai_fastapi/tier-2-standard/pyproject.toml.tier2.template +130 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/locustfile.py +99 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/mutmut_config.py +53 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/pyproject.toml.tier3.template +150 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/conftest.py +74 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/test_api.py +131 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/pyproject.toml.tier4.template +162 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/requirements-prod.txt +25 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/api/routes/metrics.py +19 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/logging.py +74 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/monitoring.py +68 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/sentry.py +66 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/logging.py +79 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/tracing.py +60 -0
- solokit/templates/refactor_spec.md +287 -0
- solokit/templates/saas_t3/base/.gitignore +36 -0
- solokit/templates/saas_t3/base/app/api/trpc/[trpc]/route.ts +33 -0
- solokit/templates/saas_t3/base/app/globals.css +27 -0
- solokit/templates/saas_t3/base/app/layout.tsx +23 -0
- solokit/templates/saas_t3/base/app/page.tsx +31 -0
- solokit/templates/saas_t3/base/lib/api.tsx +77 -0
- solokit/templates/saas_t3/base/lib/utils.ts +13 -0
- solokit/templates/saas_t3/base/next.config.ts +7 -0
- solokit/templates/saas_t3/base/package.json.template +38 -0
- solokit/templates/saas_t3/base/postcss.config.mjs +8 -0
- solokit/templates/saas_t3/base/prisma/schema.prisma +20 -0
- solokit/templates/saas_t3/base/server/api/root.ts +19 -0
- solokit/templates/saas_t3/base/server/api/routers/example.ts +28 -0
- solokit/templates/saas_t3/base/server/api/trpc.ts +52 -0
- solokit/templates/saas_t3/base/server/db.ts +17 -0
- solokit/templates/saas_t3/base/tailwind.config.ts +19 -0
- solokit/templates/saas_t3/base/tsconfig.json +27 -0
- solokit/templates/saas_t3/docker/Dockerfile +60 -0
- solokit/templates/saas_t3/docker/docker-compose.prod.yml +59 -0
- solokit/templates/saas_t3/docker/docker-compose.yml +49 -0
- solokit/templates/saas_t3/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/saas_t3/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/saas_t3/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/saas_t3/tier-1-essential/package.json.tier1.template +54 -0
- solokit/templates/saas_t3/tier-1-essential/tests/setup.ts +22 -0
- solokit/templates/saas_t3/tier-1-essential/tests/unit/example.test.tsx +24 -0
- solokit/templates/saas_t3/tier-2-standard/package.json.tier2.template +58 -0
- solokit/templates/saas_t3/tier-3-comprehensive/eslint.config.mjs +39 -0
- solokit/templates/saas_t3/tier-3-comprehensive/package.json.tier3.template +74 -0
- solokit/templates/saas_t3/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/saas_t3/tier-3-comprehensive/stryker.conf.json +34 -0
- solokit/templates/saas_t3/tier-3-comprehensive/tests/e2e/home.spec.ts +41 -0
- solokit/templates/saas_t3/tier-3-comprehensive/tests/integration/api.test.ts +44 -0
- solokit/templates/saas_t3/tier-3-comprehensive/type-coverage.json +12 -0
- solokit/templates/saas_t3/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/saas_t3/tier-4-production/k6/load-test.js +51 -0
- solokit/templates/saas_t3/tier-4-production/next.config.ts +46 -0
- solokit/templates/saas_t3/tier-4-production/package.json.tier4.template +83 -0
- solokit/templates/saas_t3/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/saas_t3/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/saas_t3/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/saas_t3/tier-4-production/vercel.json +37 -0
- solokit/templates/security_spec.md +287 -0
- solokit/templates/stack-versions.yaml +617 -0
- solokit/templates/status_update.json +6 -0
- solokit/templates/template-registry.json +257 -0
- solokit/templates/work_items.json +11 -0
- solokit/testing/__init__.py +1 -0
- solokit/testing/integration_runner.py +550 -0
- solokit/testing/performance.py +637 -0
- solokit/visualization/__init__.py +1 -0
- solokit/visualization/dependency_graph.py +788 -0
- solokit/work_items/__init__.py +1 -0
- solokit/work_items/creator.py +217 -0
- solokit/work_items/delete.py +264 -0
- solokit/work_items/get_dependencies.py +185 -0
- solokit/work_items/get_dependents.py +113 -0
- solokit/work_items/get_metadata.py +121 -0
- solokit/work_items/get_next_recommendations.py +133 -0
- solokit/work_items/manager.py +235 -0
- solokit/work_items/milestones.py +137 -0
- solokit/work_items/query.py +376 -0
- solokit/work_items/repository.py +267 -0
- solokit/work_items/scheduler.py +184 -0
- solokit/work_items/spec_parser.py +838 -0
- solokit/work_items/spec_validator.py +493 -0
- solokit/work_items/updater.py +157 -0
- solokit/work_items/validator.py +205 -0
- solokit-0.1.1.dist-info/METADATA +640 -0
- solokit-0.1.1.dist-info/RECORD +323 -0
- solokit-0.1.1.dist-info/WHEEL +5 -0
- solokit-0.1.1.dist-info/entry_points.txt +2 -0
- solokit-0.1.1.dist-info/licenses/LICENSE +21 -0
- solokit-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Fast dependent lookup for Claude Code integration.
|
|
2
|
+
|
|
3
|
+
This module provides optimized lookup of work items that depend on a given work item.
|
|
4
|
+
Used by /work-delete command to check if deleting a work item will affect others.
|
|
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_dependents(work_item_id: str) -> list[dict[str, Any]]:
|
|
16
|
+
"""Get list of work items that depend on the given work item.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
work_item_id: ID of the work item to check for dependents
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
List of dependent work items with keys: id, type, title, status
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
FileNotFoundError: If work_items.json doesn't exist
|
|
26
|
+
json.JSONDecodeError: If work_items.json is invalid
|
|
27
|
+
"""
|
|
28
|
+
# Find .session directory
|
|
29
|
+
session_dir = _find_session_dir()
|
|
30
|
+
if not session_dir:
|
|
31
|
+
print("Error: Not in an Solokit project (no .session directory found)", file=sys.stderr)
|
|
32
|
+
return []
|
|
33
|
+
|
|
34
|
+
# Load work items
|
|
35
|
+
work_items_file = session_dir / "tracking" / "work_items.json"
|
|
36
|
+
if not work_items_file.exists():
|
|
37
|
+
print(f"Error: Work items file not found: {work_items_file}", file=sys.stderr)
|
|
38
|
+
return []
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
with open(work_items_file) as f:
|
|
42
|
+
data = json.load(f)
|
|
43
|
+
except json.JSONDecodeError as e:
|
|
44
|
+
print(f"Error: Invalid JSON in {work_items_file}: {e}", file=sys.stderr)
|
|
45
|
+
return []
|
|
46
|
+
|
|
47
|
+
# Extract work_items from the data structure
|
|
48
|
+
work_items = data.get("work_items", {})
|
|
49
|
+
if not work_items:
|
|
50
|
+
print("No work items found", file=sys.stderr)
|
|
51
|
+
return []
|
|
52
|
+
|
|
53
|
+
# Find dependents (work items that have this work_item_id in their dependencies)
|
|
54
|
+
dependents = []
|
|
55
|
+
for item_id, item in work_items.items():
|
|
56
|
+
dependencies = item.get("dependencies", [])
|
|
57
|
+
if work_item_id in dependencies:
|
|
58
|
+
dependents.append(
|
|
59
|
+
{
|
|
60
|
+
"id": item_id,
|
|
61
|
+
"type": item.get("type", "unknown"),
|
|
62
|
+
"title": item.get("title", "Untitled"),
|
|
63
|
+
"status": item.get("status", "unknown"),
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return dependents
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _find_session_dir() -> Path | None:
|
|
71
|
+
"""Find the .session directory by walking up from current directory."""
|
|
72
|
+
current = Path.cwd()
|
|
73
|
+
while current != current.parent:
|
|
74
|
+
session_dir = current / ".session"
|
|
75
|
+
if session_dir.is_dir():
|
|
76
|
+
return session_dir
|
|
77
|
+
current = current.parent
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def main() -> None:
|
|
82
|
+
"""CLI entry point for get_dependents script.
|
|
83
|
+
|
|
84
|
+
Usage:
|
|
85
|
+
python -m solokit.work_items.get_dependents <work_item_id>
|
|
86
|
+
"""
|
|
87
|
+
import argparse
|
|
88
|
+
|
|
89
|
+
parser = argparse.ArgumentParser(description="Get work items that depend on a given work item")
|
|
90
|
+
parser.add_argument("work_item_id", help="ID of the work item to check for dependents")
|
|
91
|
+
|
|
92
|
+
args = parser.parse_args()
|
|
93
|
+
|
|
94
|
+
# Get dependents
|
|
95
|
+
dependents = get_dependents(args.work_item_id)
|
|
96
|
+
|
|
97
|
+
# Output results
|
|
98
|
+
if not dependents:
|
|
99
|
+
print(f"No work items depend on '{args.work_item_id}'")
|
|
100
|
+
sys.exit(0)
|
|
101
|
+
|
|
102
|
+
print(f"Found {len(dependents)} work item(s) that depend on '{args.work_item_id}':")
|
|
103
|
+
print()
|
|
104
|
+
for dep in dependents:
|
|
105
|
+
print(f"ID: {dep['id']}")
|
|
106
|
+
print(f"Type: {dep['type']}")
|
|
107
|
+
print(f"Title: {dep['title']}")
|
|
108
|
+
print(f"Status: {dep['status']}")
|
|
109
|
+
print()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
main()
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Get work item metadata without loading full spec.
|
|
4
|
+
|
|
5
|
+
Lightweight utility to fetch just the metadata fields for a work item,
|
|
6
|
+
avoiding the overhead of loading and displaying full specifications.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_work_item_metadata(
|
|
17
|
+
work_item_id: str, include_dependency_details: bool = False
|
|
18
|
+
) -> dict | None:
|
|
19
|
+
"""Get work item metadata without loading spec file.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
work_item_id: ID of the work item
|
|
23
|
+
include_dependency_details: If True, include full details (type, title) for each dependency
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
dict: Work item metadata (id, type, title, status, priority, dependencies, milestone)
|
|
27
|
+
If include_dependency_details=True, also includes 'dependency_details' list
|
|
28
|
+
None: If work item doesn't exist
|
|
29
|
+
"""
|
|
30
|
+
# Find work_items.json
|
|
31
|
+
session_dir = Path.cwd() / ".session"
|
|
32
|
+
work_items_file = session_dir / "tracking" / "work_items.json"
|
|
33
|
+
|
|
34
|
+
if not work_items_file.exists():
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
# Load work items
|
|
38
|
+
with open(work_items_file) as f:
|
|
39
|
+
data = json.load(f)
|
|
40
|
+
|
|
41
|
+
work_items = data.get("work_items", {})
|
|
42
|
+
|
|
43
|
+
if work_item_id not in work_items:
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
item = work_items[work_item_id]
|
|
47
|
+
|
|
48
|
+
# Build metadata
|
|
49
|
+
metadata = {
|
|
50
|
+
"id": item["id"],
|
|
51
|
+
"type": item["type"],
|
|
52
|
+
"title": item["title"],
|
|
53
|
+
"status": item["status"],
|
|
54
|
+
"priority": item["priority"],
|
|
55
|
+
"dependencies": item.get("dependencies", []),
|
|
56
|
+
"milestone": item.get("milestone", ""),
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Optionally include dependency details (fetch all in one pass)
|
|
60
|
+
if include_dependency_details and metadata["dependencies"]:
|
|
61
|
+
dep_details = []
|
|
62
|
+
for dep_id in metadata["dependencies"]:
|
|
63
|
+
if dep_id in work_items:
|
|
64
|
+
dep_item = work_items[dep_id]
|
|
65
|
+
dep_details.append(
|
|
66
|
+
{
|
|
67
|
+
"id": dep_id,
|
|
68
|
+
"type": dep_item["type"],
|
|
69
|
+
"title": dep_item["title"],
|
|
70
|
+
"status": dep_item.get("status", "unknown"),
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
metadata["dependency_details"] = dep_details
|
|
74
|
+
|
|
75
|
+
return metadata
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def main() -> int:
|
|
79
|
+
"""CLI entry point."""
|
|
80
|
+
if len(sys.argv) < 2:
|
|
81
|
+
print(
|
|
82
|
+
"Usage: python -m solokit.work_items.get_metadata <work_item_id> [--with-deps]",
|
|
83
|
+
file=sys.stderr,
|
|
84
|
+
)
|
|
85
|
+
sys.exit(1)
|
|
86
|
+
|
|
87
|
+
work_item_id = sys.argv[1]
|
|
88
|
+
include_deps = "--with-deps" in sys.argv
|
|
89
|
+
|
|
90
|
+
metadata = get_work_item_metadata(work_item_id, include_dependency_details=include_deps)
|
|
91
|
+
|
|
92
|
+
if metadata is None:
|
|
93
|
+
print(f"Error: Work item '{work_item_id}' not found", file=sys.stderr)
|
|
94
|
+
sys.exit(1)
|
|
95
|
+
|
|
96
|
+
# Print in a clean, parseable format
|
|
97
|
+
print(f"ID: {metadata['id']}")
|
|
98
|
+
print(f"Type: {metadata['type']}")
|
|
99
|
+
print(f"Title: {metadata['title']}")
|
|
100
|
+
print(f"Status: {metadata['status']}")
|
|
101
|
+
print(f"Priority: {metadata['priority']}")
|
|
102
|
+
print(f"Milestone: {metadata['milestone'] or '(none)'}")
|
|
103
|
+
|
|
104
|
+
if metadata["dependencies"]:
|
|
105
|
+
if "dependency_details" in metadata:
|
|
106
|
+
# Print with full details
|
|
107
|
+
deps_str = []
|
|
108
|
+
for dep in metadata["dependency_details"]:
|
|
109
|
+
deps_str.append(f"{dep['id']} ([{dep['type']}] {dep['title']} - {dep['status']})")
|
|
110
|
+
print("Dependencies:\n " + "\n ".join(deps_str))
|
|
111
|
+
else:
|
|
112
|
+
# Print just IDs
|
|
113
|
+
print(f"Dependencies: {', '.join(metadata['dependencies'])}")
|
|
114
|
+
else:
|
|
115
|
+
print("Dependencies: (none)")
|
|
116
|
+
|
|
117
|
+
return 0
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if __name__ == "__main__":
|
|
121
|
+
sys.exit(main())
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Get next recommended work items for interactive selection.
|
|
4
|
+
|
|
5
|
+
This script returns the top 4 ready-to-start work items based on:
|
|
6
|
+
- Dependencies are satisfied (not blocked)
|
|
7
|
+
- Priority (critical > high > medium > low)
|
|
8
|
+
- Status is not_started
|
|
9
|
+
|
|
10
|
+
Output format (one per line):
|
|
11
|
+
work_item_id | type | title | priority
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
python -m solokit.work_items.get_next_recommendations [--limit N]
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_ready_work_items(limit: int = 4) -> list[dict[str, Any]]:
|
|
26
|
+
"""Get list of ready-to-start work items sorted by priority.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
limit: Maximum number of items to return (default 4)
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
list: Ready work items with id, type, title, priority
|
|
33
|
+
"""
|
|
34
|
+
# Find work_items.json
|
|
35
|
+
work_items_file = Path(".session/tracking/work_items.json")
|
|
36
|
+
if not work_items_file.exists():
|
|
37
|
+
print("Error: .session/tracking/work_items.json not found", file=sys.stderr)
|
|
38
|
+
return []
|
|
39
|
+
|
|
40
|
+
# Load work items
|
|
41
|
+
try:
|
|
42
|
+
with open(work_items_file) as f:
|
|
43
|
+
data = json.load(f)
|
|
44
|
+
except json.JSONDecodeError as e:
|
|
45
|
+
print(f"Error: Invalid JSON in {work_items_file}: {e}", file=sys.stderr)
|
|
46
|
+
return []
|
|
47
|
+
|
|
48
|
+
# Extract work_items from data structure
|
|
49
|
+
work_items = data.get("work_items", {})
|
|
50
|
+
if not work_items:
|
|
51
|
+
print("No work items found", file=sys.stderr)
|
|
52
|
+
return []
|
|
53
|
+
|
|
54
|
+
# Filter to not_started items
|
|
55
|
+
not_started = {
|
|
56
|
+
wid: item for wid, item in work_items.items() if item.get("status") == "not_started"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if not not_started:
|
|
60
|
+
print("No work items available to start", file=sys.stderr)
|
|
61
|
+
return []
|
|
62
|
+
|
|
63
|
+
# Check dependencies and filter to ready items
|
|
64
|
+
ready_items = []
|
|
65
|
+
|
|
66
|
+
for work_id, item in not_started.items():
|
|
67
|
+
dependencies = item.get("dependencies", [])
|
|
68
|
+
|
|
69
|
+
# Check if all dependencies are completed
|
|
70
|
+
is_ready = True
|
|
71
|
+
if dependencies:
|
|
72
|
+
for dep_id in dependencies:
|
|
73
|
+
dep_item = work_items.get(dep_id)
|
|
74
|
+
if not dep_item or dep_item.get("status") != "completed":
|
|
75
|
+
is_ready = False
|
|
76
|
+
break
|
|
77
|
+
|
|
78
|
+
if is_ready:
|
|
79
|
+
ready_items.append(
|
|
80
|
+
{
|
|
81
|
+
"id": work_id,
|
|
82
|
+
"type": item.get("type", "unknown"),
|
|
83
|
+
"title": item.get("title", "Untitled"),
|
|
84
|
+
"priority": item.get("priority", "medium"),
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if not ready_items:
|
|
89
|
+
print("No work items ready to start. All have unmet dependencies.", file=sys.stderr)
|
|
90
|
+
return []
|
|
91
|
+
|
|
92
|
+
# Sort by priority (critical > high > medium > low)
|
|
93
|
+
priority_order = {
|
|
94
|
+
"critical": 0,
|
|
95
|
+
"high": 1,
|
|
96
|
+
"medium": 2,
|
|
97
|
+
"low": 3,
|
|
98
|
+
}
|
|
99
|
+
ready_items.sort(key=lambda x: priority_order.get(x["priority"], 99))
|
|
100
|
+
|
|
101
|
+
# Return top N items
|
|
102
|
+
return ready_items[:limit]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def main() -> int:
|
|
106
|
+
"""Main entry point for script."""
|
|
107
|
+
import argparse
|
|
108
|
+
|
|
109
|
+
parser = argparse.ArgumentParser(
|
|
110
|
+
description="Get next recommended work items for interactive selection"
|
|
111
|
+
)
|
|
112
|
+
parser.add_argument(
|
|
113
|
+
"--limit",
|
|
114
|
+
type=int,
|
|
115
|
+
default=4,
|
|
116
|
+
help="Maximum number of recommendations to return (default: 4)",
|
|
117
|
+
)
|
|
118
|
+
args = parser.parse_args()
|
|
119
|
+
|
|
120
|
+
ready_items = get_ready_work_items(limit=args.limit)
|
|
121
|
+
|
|
122
|
+
if not ready_items:
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
|
|
125
|
+
# Output format: work_item_id | type | title | priority
|
|
126
|
+
for item in ready_items:
|
|
127
|
+
print(f"{item['id']} | {item['type']} | {item['title']} | {item['priority']}")
|
|
128
|
+
|
|
129
|
+
return 0
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
if __name__ == "__main__":
|
|
133
|
+
sys.exit(main())
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Work Item Manager - Main orchestrator for work item operations.
|
|
4
|
+
|
|
5
|
+
Delegates to specialized modules for different concerns:
|
|
6
|
+
- creator: Work item creation and prompts
|
|
7
|
+
- repository: Data access and persistence
|
|
8
|
+
- validator: Validation logic
|
|
9
|
+
- query: Listing, filtering, and display
|
|
10
|
+
- updater: Update operations
|
|
11
|
+
- scheduler: Work queue and next item selection
|
|
12
|
+
- milestones: Milestone management
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from solokit.core.constants import get_session_dir, get_specs_dir
|
|
21
|
+
from solokit.core.types import Priority, WorkItemType
|
|
22
|
+
|
|
23
|
+
from .creator import WorkItemCreator
|
|
24
|
+
from .milestones import MilestoneManager
|
|
25
|
+
from .query import WorkItemQuery
|
|
26
|
+
from .repository import WorkItemRepository
|
|
27
|
+
from .scheduler import WorkItemScheduler
|
|
28
|
+
from .updater import WorkItemUpdater
|
|
29
|
+
from .validator import WorkItemValidator
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class WorkItemManager:
|
|
33
|
+
"""Main orchestrator for work item operations"""
|
|
34
|
+
|
|
35
|
+
WORK_ITEM_TYPES = WorkItemType.values()
|
|
36
|
+
PRIORITIES = Priority.values()
|
|
37
|
+
|
|
38
|
+
def __init__(self, project_root: Path | None = None):
|
|
39
|
+
"""Initialize manager with dependency injection
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
project_root: Optional project root path (defaults to current directory)
|
|
43
|
+
"""
|
|
44
|
+
self.project_root = project_root or Path.cwd()
|
|
45
|
+
self.session_dir = get_session_dir(self.project_root)
|
|
46
|
+
|
|
47
|
+
# Initialize components
|
|
48
|
+
self.repository = WorkItemRepository(self.session_dir)
|
|
49
|
+
self.creator = WorkItemCreator(self.repository)
|
|
50
|
+
self.validator = WorkItemValidator()
|
|
51
|
+
self.query = WorkItemQuery(self.repository)
|
|
52
|
+
self.updater = WorkItemUpdater(self.repository, self.validator)
|
|
53
|
+
self.scheduler = WorkItemScheduler(self.repository)
|
|
54
|
+
self.milestones = MilestoneManager(self.repository)
|
|
55
|
+
|
|
56
|
+
# Legacy compatibility - maintain these paths for backward compatibility
|
|
57
|
+
self.work_items_file = self.repository.work_items_file
|
|
58
|
+
self.specs_dir = get_specs_dir(self.project_root)
|
|
59
|
+
self.templates_dir = Path(__file__).parent.parent / "templates"
|
|
60
|
+
|
|
61
|
+
# Delegate creation methods
|
|
62
|
+
def create_work_item_from_args(
|
|
63
|
+
self, work_type: str, title: str, priority: str = "high", dependencies: str = ""
|
|
64
|
+
) -> str:
|
|
65
|
+
"""Create work item from command-line arguments
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
work_type: Type of work item (feature, bug, refactor, etc.)
|
|
69
|
+
title: Title of the work item
|
|
70
|
+
priority: Priority level (critical, high, medium, low)
|
|
71
|
+
dependencies: Comma-separated dependency IDs
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
str: The created work item ID
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
ValidationError: If work type is invalid
|
|
78
|
+
WorkItemAlreadyExistsError: If work item with generated ID already exists
|
|
79
|
+
FileOperationError: If spec file creation or tracking update fails
|
|
80
|
+
"""
|
|
81
|
+
return self.creator.create_from_args(work_type, title, priority, dependencies)
|
|
82
|
+
|
|
83
|
+
# Delegate query methods
|
|
84
|
+
def list_work_items(
|
|
85
|
+
self,
|
|
86
|
+
status_filter: str | None = None,
|
|
87
|
+
type_filter: str | None = None,
|
|
88
|
+
milestone_filter: str | None = None,
|
|
89
|
+
) -> dict:
|
|
90
|
+
"""List work items with optional filters
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
status_filter: Optional status filter
|
|
94
|
+
type_filter: Optional type filter
|
|
95
|
+
milestone_filter: Optional milestone filter
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
dict: Dictionary with 'items' list and 'count'
|
|
99
|
+
"""
|
|
100
|
+
return self.query.list_items(status_filter, type_filter, milestone_filter)
|
|
101
|
+
|
|
102
|
+
def show_work_item(self, work_id: str) -> dict[str, Any]:
|
|
103
|
+
"""Show detailed information about a work item
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
work_id: ID of the work item to display
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
dict: The work item data
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
FileOperationError: If work_items.json doesn't exist
|
|
113
|
+
WorkItemNotFoundError: If work item doesn't exist
|
|
114
|
+
"""
|
|
115
|
+
return self.query.show_item(work_id)
|
|
116
|
+
|
|
117
|
+
# Delegate update methods
|
|
118
|
+
def update_work_item(self, work_id: str, **updates: Any) -> None:
|
|
119
|
+
"""Update a work item
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
work_id: ID of the work item to update
|
|
123
|
+
**updates: Field updates (status, priority, milestone, add_dependency, remove_dependency)
|
|
124
|
+
|
|
125
|
+
Raises:
|
|
126
|
+
FileOperationError: If work_items.json doesn't exist
|
|
127
|
+
WorkItemNotFoundError: If work item doesn't exist
|
|
128
|
+
ValidationError: If invalid status or priority provided
|
|
129
|
+
"""
|
|
130
|
+
return self.updater.update(work_id, **updates)
|
|
131
|
+
|
|
132
|
+
# Delegate scheduler methods
|
|
133
|
+
def get_next_work_item(self) -> dict[str, Any] | None:
|
|
134
|
+
"""Get next recommended work item based on dependencies and priority
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
dict: Next work item to start, or None if none available
|
|
138
|
+
"""
|
|
139
|
+
return self.scheduler.get_next()
|
|
140
|
+
|
|
141
|
+
# Delegate validation methods
|
|
142
|
+
def validate_integration_test(self, work_item: dict) -> None:
|
|
143
|
+
"""Validate integration test work item
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
work_item: Work item dictionary to validate
|
|
147
|
+
|
|
148
|
+
Raises:
|
|
149
|
+
FileOperationError: If spec file not found
|
|
150
|
+
ValidationError: If spec validation fails (with validation errors in context)
|
|
151
|
+
"""
|
|
152
|
+
return self.validator.validate_integration_test(work_item)
|
|
153
|
+
|
|
154
|
+
def validate_deployment(self, work_item: dict) -> None:
|
|
155
|
+
"""Validate deployment work item
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
work_item: Work item dictionary to validate
|
|
159
|
+
|
|
160
|
+
Raises:
|
|
161
|
+
FileOperationError: If spec file not found
|
|
162
|
+
ValidationError: If spec validation fails (with validation errors in context)
|
|
163
|
+
"""
|
|
164
|
+
return self.validator.validate_deployment(work_item)
|
|
165
|
+
|
|
166
|
+
# Delegate milestone methods
|
|
167
|
+
def create_milestone(
|
|
168
|
+
self, name: str, title: str, description: str, target_date: str | None = None
|
|
169
|
+
) -> None:
|
|
170
|
+
"""Create a new milestone
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
name: Milestone name (unique identifier)
|
|
174
|
+
title: Milestone title
|
|
175
|
+
description: Milestone description
|
|
176
|
+
target_date: Optional target completion date
|
|
177
|
+
|
|
178
|
+
Raises:
|
|
179
|
+
ValidationError: If milestone with this name already exists
|
|
180
|
+
FileOperationError: If saving milestone fails
|
|
181
|
+
"""
|
|
182
|
+
return self.milestones.create(name, title, description, target_date)
|
|
183
|
+
|
|
184
|
+
def get_milestone_progress(self, milestone_name: str) -> dict:
|
|
185
|
+
"""Get milestone progress
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
milestone_name: Name of the milestone
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
dict: Progress statistics including total, completed, in_progress, not_started, percent
|
|
192
|
+
"""
|
|
193
|
+
return self.milestones.get_progress(milestone_name)
|
|
194
|
+
|
|
195
|
+
def list_milestones(self) -> None:
|
|
196
|
+
"""List all milestones with progress"""
|
|
197
|
+
return self.milestones.list_all()
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def main() -> int:
|
|
201
|
+
"""CLI entry point."""
|
|
202
|
+
import argparse
|
|
203
|
+
|
|
204
|
+
parser = argparse.ArgumentParser(description="Work Item Manager")
|
|
205
|
+
parser.add_argument(
|
|
206
|
+
"--type",
|
|
207
|
+
help="Work item type (feature, bug, refactor, security, integration_test, deployment)",
|
|
208
|
+
)
|
|
209
|
+
parser.add_argument("--title", help="Work item title")
|
|
210
|
+
parser.add_argument("--priority", default="high", help="Priority (critical, high, medium, low)")
|
|
211
|
+
parser.add_argument("--dependencies", default="", help="Comma-separated dependency IDs")
|
|
212
|
+
|
|
213
|
+
args = parser.parse_args()
|
|
214
|
+
|
|
215
|
+
manager = WorkItemManager()
|
|
216
|
+
|
|
217
|
+
# Require type and title arguments (no interactive mode)
|
|
218
|
+
if not args.type or not args.title:
|
|
219
|
+
parser.error("Both --type and --title are required")
|
|
220
|
+
|
|
221
|
+
work_id = manager.create_work_item_from_args(
|
|
222
|
+
work_type=args.type,
|
|
223
|
+
title=args.title,
|
|
224
|
+
priority=args.priority,
|
|
225
|
+
dependencies=args.dependencies,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
if work_id:
|
|
229
|
+
return 0
|
|
230
|
+
else:
|
|
231
|
+
return 1
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
if __name__ == "__main__":
|
|
235
|
+
exit(main())
|