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
solokit/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Session-Driven Development (Solokit) - AI-Augmented Development Workflow System.
|
|
2
|
+
|
|
3
|
+
Solokit is a structured workflow system for AI-augmented software development that organizes
|
|
4
|
+
work into discrete sessions, each focused on completing a single work item with
|
|
5
|
+
comprehensive context, quality gates, and learning capture.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from solokit.__version__ import __version__
|
|
9
|
+
|
|
10
|
+
__all__ = ["__version__"]
|
solokit/__version__.py
ADDED
solokit/cli.py
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Solokit CLI Entry Point
|
|
4
|
+
|
|
5
|
+
Universal interface for all Session-Driven Development commands.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
solokit <command> [args...]
|
|
9
|
+
|
|
10
|
+
Examples:
|
|
11
|
+
sk work-list
|
|
12
|
+
sk work-list --status not_started
|
|
13
|
+
sk work-show feature_user_auth
|
|
14
|
+
sk start
|
|
15
|
+
sk learn-search "authentication"
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from solokit.core.error_formatter import ErrorFormatter
|
|
25
|
+
|
|
26
|
+
# Import error handling infrastructure
|
|
27
|
+
from solokit.core.exceptions import (
|
|
28
|
+
ErrorCode,
|
|
29
|
+
SolokitError,
|
|
30
|
+
SystemError,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Import logging configuration
|
|
34
|
+
from solokit.core.logging_config import get_logger, setup_logging
|
|
35
|
+
from solokit.core.output import get_output
|
|
36
|
+
|
|
37
|
+
logger = get_logger(__name__)
|
|
38
|
+
output = get_output()
|
|
39
|
+
|
|
40
|
+
# Command routing table
|
|
41
|
+
# Format: 'command-name': (module_path, class_name, function_name, needs_argparse)
|
|
42
|
+
# - module_path: Python import path
|
|
43
|
+
# - class_name: Class to instantiate (None for standalone functions)
|
|
44
|
+
# - function_name: Method or function to call
|
|
45
|
+
# - needs_argparse: True if script has its own argparse handling
|
|
46
|
+
COMMANDS = {
|
|
47
|
+
# Work Item Management (WorkItemManager class)
|
|
48
|
+
"work-list": (
|
|
49
|
+
"solokit.work_items.manager",
|
|
50
|
+
"WorkItemManager",
|
|
51
|
+
"list_work_items",
|
|
52
|
+
False,
|
|
53
|
+
),
|
|
54
|
+
"work-next": (
|
|
55
|
+
"solokit.work_items.manager",
|
|
56
|
+
"WorkItemManager",
|
|
57
|
+
"get_next_work_item",
|
|
58
|
+
False,
|
|
59
|
+
),
|
|
60
|
+
"work-show": (
|
|
61
|
+
"solokit.work_items.manager",
|
|
62
|
+
"WorkItemManager",
|
|
63
|
+
"show_work_item",
|
|
64
|
+
False,
|
|
65
|
+
),
|
|
66
|
+
"work-update": (
|
|
67
|
+
"solokit.work_items.manager",
|
|
68
|
+
"WorkItemManager",
|
|
69
|
+
"update_work_item",
|
|
70
|
+
False,
|
|
71
|
+
),
|
|
72
|
+
"work-new": (
|
|
73
|
+
"solokit.work_items.manager",
|
|
74
|
+
"WorkItemManager",
|
|
75
|
+
"create_work_item_from_args",
|
|
76
|
+
False,
|
|
77
|
+
),
|
|
78
|
+
"work-delete": ("solokit.work_items.delete", None, "main", True),
|
|
79
|
+
# Dependency Graph (uses argparse in main)
|
|
80
|
+
"work-graph": ("solokit.visualization.dependency_graph", None, "main", True),
|
|
81
|
+
# Session Management (standalone main functions)
|
|
82
|
+
"start": ("solokit.session.briefing", None, "main", True),
|
|
83
|
+
"end": ("solokit.session.complete", None, "main", True),
|
|
84
|
+
"status": ("solokit.session.status", None, "get_session_status", False),
|
|
85
|
+
"validate": ("solokit.session.validate", None, "main", True),
|
|
86
|
+
# Learning System (uses argparse in main)
|
|
87
|
+
"learn": ("solokit.learning.curator", None, "main", True),
|
|
88
|
+
"learn-show": ("solokit.learning.curator", None, "main", True),
|
|
89
|
+
"learn-search": ("solokit.learning.curator", None, "main", True),
|
|
90
|
+
"learn-curate": ("solokit.learning.curator", None, "main", True),
|
|
91
|
+
# Project Initialization
|
|
92
|
+
"init": ("solokit.project.init", None, "main", True),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def parse_work_list_args(args: list[str]) -> argparse.Namespace:
|
|
97
|
+
"""Parse arguments for work-list command."""
|
|
98
|
+
parser = argparse.ArgumentParser(description="List work items")
|
|
99
|
+
parser.add_argument("--status", help="Filter by status")
|
|
100
|
+
parser.add_argument("--type", help="Filter by type")
|
|
101
|
+
parser.add_argument("--milestone", help="Filter by milestone")
|
|
102
|
+
return parser.parse_args(args)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def parse_work_show_args(args: list[str]) -> argparse.Namespace:
|
|
106
|
+
"""Parse arguments for work-show command."""
|
|
107
|
+
parser = argparse.ArgumentParser(description="Show work item details")
|
|
108
|
+
parser.add_argument("work_id", help="Work item ID")
|
|
109
|
+
return parser.parse_args(args)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def parse_work_new_args(args: list[str]) -> argparse.Namespace:
|
|
113
|
+
"""Parse arguments for work-new command."""
|
|
114
|
+
parser = argparse.ArgumentParser(description="Create a new work item")
|
|
115
|
+
parser.add_argument(
|
|
116
|
+
"--type",
|
|
117
|
+
"-t",
|
|
118
|
+
required=True,
|
|
119
|
+
help="Work item type (feature, bug, refactor, security, integration_test, deployment)",
|
|
120
|
+
)
|
|
121
|
+
parser.add_argument("--title", "-T", required=True, help="Work item title")
|
|
122
|
+
parser.add_argument(
|
|
123
|
+
"--priority",
|
|
124
|
+
"-p",
|
|
125
|
+
required=True,
|
|
126
|
+
help="Priority (critical, high, medium, low)",
|
|
127
|
+
)
|
|
128
|
+
parser.add_argument("--dependencies", "-d", default="", help="Comma-separated dependency IDs")
|
|
129
|
+
return parser.parse_args(args)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def parse_work_update_args(args: list[str]) -> argparse.Namespace:
|
|
133
|
+
"""Parse arguments for work-update command."""
|
|
134
|
+
parser = argparse.ArgumentParser(description="Update work item fields")
|
|
135
|
+
parser.add_argument("work_id", help="Work item ID")
|
|
136
|
+
parser.add_argument(
|
|
137
|
+
"--status", help="Update status (not_started/in_progress/blocked/completed)"
|
|
138
|
+
)
|
|
139
|
+
parser.add_argument("--priority", help="Update priority (critical/high/medium/low)")
|
|
140
|
+
parser.add_argument("--milestone", help="Update milestone")
|
|
141
|
+
parser.add_argument("--add-dependency", help="Add dependency by ID")
|
|
142
|
+
parser.add_argument("--remove-dependency", help="Remove dependency by ID")
|
|
143
|
+
return parser.parse_args(args)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def route_command(command_name: str, args: list[str]) -> int:
|
|
147
|
+
"""
|
|
148
|
+
Route command to appropriate script/function.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
command_name: Name of the command (e.g., 'work-list')
|
|
152
|
+
args: List of command-line arguments
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Exit code (0 for success, non-zero for error)
|
|
156
|
+
|
|
157
|
+
Raises:
|
|
158
|
+
SystemError: If command is unknown or execution fails
|
|
159
|
+
"""
|
|
160
|
+
if command_name not in COMMANDS:
|
|
161
|
+
available = ", ".join(sorted(COMMANDS.keys()))
|
|
162
|
+
raise SystemError(
|
|
163
|
+
message=f"Unknown command '{command_name}'",
|
|
164
|
+
code=ErrorCode.INVALID_COMMAND,
|
|
165
|
+
context={"command": command_name, "available_commands": list(COMMANDS.keys())},
|
|
166
|
+
remediation=f"Available commands: {available}",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
module_path, class_name, function_name, needs_argparse = COMMANDS[command_name]
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
# Import the module
|
|
173
|
+
module = __import__(module_path, fromlist=[class_name or function_name])
|
|
174
|
+
|
|
175
|
+
# Handle different command types
|
|
176
|
+
if needs_argparse:
|
|
177
|
+
# Scripts with argparse: set sys.argv and call main()
|
|
178
|
+
# The script's own argparse will handle arguments
|
|
179
|
+
if command_name in ["learn", "learn-show", "learn-search", "learn-curate"]:
|
|
180
|
+
# Learning commands need special handling for subcommands
|
|
181
|
+
if command_name == "learn":
|
|
182
|
+
sys.argv = ["learning_curator.py", "add-learning"] + args
|
|
183
|
+
elif command_name == "learn-show":
|
|
184
|
+
sys.argv = ["learning_curator.py", "show-learnings"] + args
|
|
185
|
+
elif command_name == "learn-search":
|
|
186
|
+
sys.argv = ["learning_curator.py", "search"] + args
|
|
187
|
+
elif command_name == "learn-curate":
|
|
188
|
+
sys.argv = ["learning_curator.py", "curate"] + args
|
|
189
|
+
else:
|
|
190
|
+
# Other argparse commands (work-graph, start, end, validate)
|
|
191
|
+
sys.argv = [command_name] + args
|
|
192
|
+
|
|
193
|
+
func = getattr(module, function_name)
|
|
194
|
+
result = func()
|
|
195
|
+
return int(result) if result is not None else 0
|
|
196
|
+
|
|
197
|
+
elif class_name:
|
|
198
|
+
# Class-based commands: instantiate class and call method
|
|
199
|
+
cls = getattr(module, class_name)
|
|
200
|
+
instance = cls()
|
|
201
|
+
method = getattr(instance, function_name)
|
|
202
|
+
|
|
203
|
+
# Special argument handling for specific commands
|
|
204
|
+
if command_name == "work-list":
|
|
205
|
+
parsed = parse_work_list_args(args)
|
|
206
|
+
result = method(
|
|
207
|
+
status_filter=parsed.status,
|
|
208
|
+
type_filter=parsed.type,
|
|
209
|
+
milestone_filter=parsed.milestone,
|
|
210
|
+
)
|
|
211
|
+
elif command_name == "work-show":
|
|
212
|
+
parsed = parse_work_show_args(args)
|
|
213
|
+
result = method(parsed.work_id)
|
|
214
|
+
elif command_name == "work-next":
|
|
215
|
+
result = method()
|
|
216
|
+
elif command_name == "work-new":
|
|
217
|
+
# Parse arguments (all required)
|
|
218
|
+
parsed = parse_work_new_args(args)
|
|
219
|
+
result = method(
|
|
220
|
+
work_type=parsed.type,
|
|
221
|
+
title=parsed.title,
|
|
222
|
+
priority=parsed.priority,
|
|
223
|
+
dependencies=parsed.dependencies,
|
|
224
|
+
)
|
|
225
|
+
elif command_name == "work-update":
|
|
226
|
+
# Parse arguments
|
|
227
|
+
parsed = parse_work_update_args(args)
|
|
228
|
+
|
|
229
|
+
# Build kwargs from provided flags
|
|
230
|
+
kwargs = {}
|
|
231
|
+
if parsed.status:
|
|
232
|
+
kwargs["status"] = parsed.status
|
|
233
|
+
if parsed.priority:
|
|
234
|
+
kwargs["priority"] = parsed.priority
|
|
235
|
+
if parsed.milestone:
|
|
236
|
+
kwargs["milestone"] = parsed.milestone
|
|
237
|
+
if parsed.add_dependency:
|
|
238
|
+
kwargs["add_dependency"] = parsed.add_dependency
|
|
239
|
+
if parsed.remove_dependency:
|
|
240
|
+
kwargs["remove_dependency"] = parsed.remove_dependency
|
|
241
|
+
|
|
242
|
+
result = method(parsed.work_id, **kwargs)
|
|
243
|
+
else:
|
|
244
|
+
result = method()
|
|
245
|
+
|
|
246
|
+
# Handle different return types
|
|
247
|
+
if result is None:
|
|
248
|
+
return 0
|
|
249
|
+
elif isinstance(result, bool):
|
|
250
|
+
return 0 if result else 1
|
|
251
|
+
elif isinstance(result, int):
|
|
252
|
+
return result
|
|
253
|
+
else:
|
|
254
|
+
return 0
|
|
255
|
+
|
|
256
|
+
else:
|
|
257
|
+
# Standalone function commands
|
|
258
|
+
func = getattr(module, function_name)
|
|
259
|
+
result = func()
|
|
260
|
+
return int(result) if result is not None else 0
|
|
261
|
+
|
|
262
|
+
except ModuleNotFoundError as e:
|
|
263
|
+
raise SystemError(
|
|
264
|
+
message=f"Could not import module '{module_path}'",
|
|
265
|
+
code=ErrorCode.MODULE_NOT_FOUND,
|
|
266
|
+
context={"module_path": module_path, "command": command_name},
|
|
267
|
+
remediation="Check that the command is properly installed",
|
|
268
|
+
cause=e,
|
|
269
|
+
) from e
|
|
270
|
+
except AttributeError as e:
|
|
271
|
+
raise SystemError(
|
|
272
|
+
message=f"Could not find function '{function_name}' in module '{module_path}'",
|
|
273
|
+
code=ErrorCode.FUNCTION_NOT_FOUND,
|
|
274
|
+
context={"function": function_name, "module": module_path, "command": command_name},
|
|
275
|
+
remediation="This appears to be an internal error - please report it",
|
|
276
|
+
cause=e,
|
|
277
|
+
) from e
|
|
278
|
+
except SolokitError:
|
|
279
|
+
# Re-raise SolokitError exceptions to be caught by main()
|
|
280
|
+
raise
|
|
281
|
+
except Exception as e:
|
|
282
|
+
# Wrap unexpected exceptions
|
|
283
|
+
raise SystemError(
|
|
284
|
+
message=f"Unexpected error executing command '{command_name}'",
|
|
285
|
+
code=ErrorCode.COMMAND_FAILED,
|
|
286
|
+
context={"command": command_name},
|
|
287
|
+
cause=e,
|
|
288
|
+
) from e
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def main() -> int:
|
|
292
|
+
"""
|
|
293
|
+
Main entry point for CLI with centralized error handling.
|
|
294
|
+
|
|
295
|
+
This function implements the standard error handling pattern:
|
|
296
|
+
- Parse arguments
|
|
297
|
+
- Route to command handlers
|
|
298
|
+
- Catch all exceptions in centralized handler
|
|
299
|
+
- Format errors using ErrorFormatter
|
|
300
|
+
- Return appropriate exit codes
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
Exit code (0 for success, non-zero for errors)
|
|
304
|
+
"""
|
|
305
|
+
try:
|
|
306
|
+
# Parse global flags first
|
|
307
|
+
parser = argparse.ArgumentParser(
|
|
308
|
+
description="Session-Driven Development CLI",
|
|
309
|
+
add_help=False, # Don't show help yet, let commands handle it
|
|
310
|
+
)
|
|
311
|
+
parser.add_argument(
|
|
312
|
+
"--verbose",
|
|
313
|
+
"-v",
|
|
314
|
+
action="store_true",
|
|
315
|
+
help="Enable verbose (DEBUG) logging",
|
|
316
|
+
)
|
|
317
|
+
parser.add_argument(
|
|
318
|
+
"--log-file",
|
|
319
|
+
type=str,
|
|
320
|
+
help="Write logs to file",
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# Parse known args (global flags) and leave rest for command routing
|
|
324
|
+
args, remaining = parser.parse_known_args()
|
|
325
|
+
|
|
326
|
+
# Setup logging based on global flags
|
|
327
|
+
log_level = "DEBUG" if args.verbose else "INFO"
|
|
328
|
+
log_file = Path(args.log_file) if args.log_file else None
|
|
329
|
+
setup_logging(level=log_level, log_file=log_file)
|
|
330
|
+
|
|
331
|
+
# Check if command is provided
|
|
332
|
+
if len(remaining) < 1:
|
|
333
|
+
output.error("Usage: solokit [--verbose] [--log-file FILE] <command> [args...]")
|
|
334
|
+
output.error("\nGlobal flags:")
|
|
335
|
+
output.error(" --verbose, -v Enable verbose (DEBUG) logging")
|
|
336
|
+
output.error(" --log-file FILE Write logs to file")
|
|
337
|
+
output.error("\nAvailable commands:")
|
|
338
|
+
output.error(" Work Items:")
|
|
339
|
+
output.error(
|
|
340
|
+
" work-list, work-next, work-show, work-update, work-new, work-delete, work-graph"
|
|
341
|
+
)
|
|
342
|
+
output.error(" Sessions:")
|
|
343
|
+
output.error(" start, end, status, validate")
|
|
344
|
+
output.error(" Learnings:")
|
|
345
|
+
output.error(" learn, learn-show, learn-search, learn-curate")
|
|
346
|
+
output.error(" Initialization:")
|
|
347
|
+
output.error(" init")
|
|
348
|
+
return 1
|
|
349
|
+
|
|
350
|
+
command = remaining[0]
|
|
351
|
+
command_args = remaining[1:]
|
|
352
|
+
|
|
353
|
+
# Route command - will raise exceptions on error
|
|
354
|
+
exit_code = route_command(command, command_args)
|
|
355
|
+
return exit_code
|
|
356
|
+
|
|
357
|
+
except SolokitError as e:
|
|
358
|
+
# Structured Solokit errors with proper formatting
|
|
359
|
+
ErrorFormatter.print_error(e, verbose=args.verbose if "args" in locals() else False)
|
|
360
|
+
return e.exit_code
|
|
361
|
+
|
|
362
|
+
except KeyboardInterrupt:
|
|
363
|
+
# User cancelled operation
|
|
364
|
+
output.error("\n\nOperation cancelled by user")
|
|
365
|
+
return 130
|
|
366
|
+
|
|
367
|
+
except Exception as e:
|
|
368
|
+
# Unexpected errors - show full details
|
|
369
|
+
ErrorFormatter.print_error(e, verbose=True)
|
|
370
|
+
return 1
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
if __name__ == "__main__":
|
|
374
|
+
sys.exit(main())
|
solokit/core/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Core functionality for Solokit including file operations, logging, and configuration."""
|
solokit/core/cache.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Caching layer for frequently accessed data"""
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Callable, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Cache:
|
|
10
|
+
"""Thread-safe cache with TTL"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, default_ttl: int = 300):
|
|
13
|
+
"""Initialize cache with default TTL (seconds)"""
|
|
14
|
+
self.default_ttl = default_ttl
|
|
15
|
+
self._cache: dict[str, tuple[Any, datetime]] = {}
|
|
16
|
+
self._lock = threading.Lock()
|
|
17
|
+
|
|
18
|
+
def get(self, key: str) -> Optional[Any]:
|
|
19
|
+
"""Get value from cache"""
|
|
20
|
+
with self._lock:
|
|
21
|
+
if key in self._cache:
|
|
22
|
+
value, expires_at = self._cache[key]
|
|
23
|
+
if datetime.now() < expires_at:
|
|
24
|
+
return value
|
|
25
|
+
else:
|
|
26
|
+
# Expired
|
|
27
|
+
del self._cache[key]
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
def set(self, key: str, value: Any, ttl: Optional[int] = None) -> None:
|
|
31
|
+
"""Set value in cache with TTL"""
|
|
32
|
+
ttl = ttl or self.default_ttl
|
|
33
|
+
expires_at = datetime.now() + timedelta(seconds=ttl)
|
|
34
|
+
with self._lock:
|
|
35
|
+
self._cache[key] = (value, expires_at)
|
|
36
|
+
|
|
37
|
+
def invalidate(self, key: str) -> None:
|
|
38
|
+
"""Invalidate cache entry"""
|
|
39
|
+
with self._lock:
|
|
40
|
+
if key in self._cache:
|
|
41
|
+
del self._cache[key]
|
|
42
|
+
|
|
43
|
+
def clear(self) -> None:
|
|
44
|
+
"""Clear all cache"""
|
|
45
|
+
with self._lock:
|
|
46
|
+
self._cache.clear()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Global cache instance
|
|
50
|
+
_global_cache = Cache(default_ttl=60) # 1 minute default
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_cache() -> Cache:
|
|
54
|
+
"""Get global cache instance"""
|
|
55
|
+
return _global_cache
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class FileCache:
|
|
59
|
+
"""Cache for JSON files with modification tracking"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, cache: Optional[Cache] = None):
|
|
62
|
+
"""Initialize file cache"""
|
|
63
|
+
self.cache = cache or get_cache()
|
|
64
|
+
|
|
65
|
+
def load_json(self, file_path: Path, loader_func: Callable[[Path], Any]) -> Any:
|
|
66
|
+
"""
|
|
67
|
+
Load JSON with caching
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
file_path: Path to JSON file
|
|
71
|
+
loader_func: Function to load file if not cached
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Loaded data (from cache or file)
|
|
75
|
+
"""
|
|
76
|
+
cache_key = f"file:{file_path}"
|
|
77
|
+
|
|
78
|
+
# Check modification time
|
|
79
|
+
if file_path.exists():
|
|
80
|
+
mtime = file_path.stat().st_mtime
|
|
81
|
+
mtime_key = f"{cache_key}:mtime"
|
|
82
|
+
|
|
83
|
+
# Check cache
|
|
84
|
+
cached_mtime = self.cache.get(mtime_key)
|
|
85
|
+
if cached_mtime == mtime:
|
|
86
|
+
cached_data = self.cache.get(cache_key)
|
|
87
|
+
if cached_data is not None:
|
|
88
|
+
return cached_data
|
|
89
|
+
|
|
90
|
+
# Load and cache
|
|
91
|
+
data = loader_func(file_path)
|
|
92
|
+
self.cache.set(cache_key, data, ttl=300) # 5 min TTL
|
|
93
|
+
self.cache.set(mtime_key, mtime, ttl=300)
|
|
94
|
+
return data
|
|
95
|
+
else:
|
|
96
|
+
return loader_func(file_path)
|
|
97
|
+
|
|
98
|
+
def invalidate(self, file_path: Path) -> None:
|
|
99
|
+
"""Invalidate cache for file"""
|
|
100
|
+
cache_key = f"file:{file_path}"
|
|
101
|
+
self.cache.invalidate(cache_key)
|
|
102
|
+
self.cache.invalidate(f"{cache_key}:mtime")
|