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,221 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Context7 library verification checker.
|
|
4
|
+
|
|
5
|
+
Verifies important libraries via Context7 MCP integration by parsing
|
|
6
|
+
libraries from stack.txt and querying Context7 for security and version status.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import time
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Union, cast
|
|
14
|
+
|
|
15
|
+
from solokit.core.command_runner import CommandRunner
|
|
16
|
+
from solokit.core.constants import QUALITY_CHECK_LONG_TIMEOUT
|
|
17
|
+
from solokit.core.exceptions import FileOperationError
|
|
18
|
+
from solokit.core.logging_config import get_logger
|
|
19
|
+
from solokit.quality.checkers.base import CheckResult, QualityChecker
|
|
20
|
+
|
|
21
|
+
logger = get_logger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Context7Checker(QualityChecker):
|
|
25
|
+
"""Context7 library verification for important dependencies."""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
config: dict[str, Any],
|
|
30
|
+
project_root: Path | None = None,
|
|
31
|
+
runner: CommandRunner | None = None,
|
|
32
|
+
):
|
|
33
|
+
"""Initialize Context7 checker.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
config: Context7 configuration
|
|
37
|
+
project_root: Project root directory
|
|
38
|
+
runner: Optional CommandRunner instance (for testing)
|
|
39
|
+
"""
|
|
40
|
+
super().__init__(config, project_root)
|
|
41
|
+
self.runner = (
|
|
42
|
+
runner
|
|
43
|
+
if runner is not None
|
|
44
|
+
else CommandRunner(default_timeout=QUALITY_CHECK_LONG_TIMEOUT)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def name(self) -> str:
|
|
48
|
+
"""Return checker name."""
|
|
49
|
+
return "context7"
|
|
50
|
+
|
|
51
|
+
def is_enabled(self) -> bool:
|
|
52
|
+
"""Check if Context7 verification is enabled."""
|
|
53
|
+
return bool(self.config.get("enabled", False))
|
|
54
|
+
|
|
55
|
+
def run(self) -> CheckResult:
|
|
56
|
+
"""Run Context7 library verification."""
|
|
57
|
+
start_time = time.time()
|
|
58
|
+
|
|
59
|
+
if not self.is_enabled():
|
|
60
|
+
return self._create_skipped_result(reason="not enabled")
|
|
61
|
+
|
|
62
|
+
# Get stack.txt path (relative to project root or .session/tracking)
|
|
63
|
+
stack_file = self.project_root / ".session" / "tracking" / "stack.txt"
|
|
64
|
+
if not stack_file.exists():
|
|
65
|
+
return self._create_skipped_result(reason="no stack.txt")
|
|
66
|
+
|
|
67
|
+
logger.info("Running Context7 library verification")
|
|
68
|
+
|
|
69
|
+
# Parse libraries from stack.txt
|
|
70
|
+
try:
|
|
71
|
+
libraries = self._parse_libraries_from_stack(stack_file)
|
|
72
|
+
except FileOperationError as e:
|
|
73
|
+
execution_time = time.time() - start_time
|
|
74
|
+
return CheckResult(
|
|
75
|
+
checker_name=self.name(),
|
|
76
|
+
passed=False,
|
|
77
|
+
status="failed",
|
|
78
|
+
errors=[{"message": f"Failed to read stack.txt: {e}"}],
|
|
79
|
+
warnings=[],
|
|
80
|
+
info={"reason": "stack file read error"},
|
|
81
|
+
execution_time=execution_time,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Verify libraries
|
|
85
|
+
results: dict[str, Any] = {"libraries": [], "verified": 0, "failed": 0}
|
|
86
|
+
|
|
87
|
+
for lib in libraries:
|
|
88
|
+
# Check if library should be verified
|
|
89
|
+
if not self._should_verify_library(lib):
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
# Query Context7 MCP
|
|
93
|
+
verified = self._query_context7(lib)
|
|
94
|
+
|
|
95
|
+
results["libraries"].append(
|
|
96
|
+
{
|
|
97
|
+
"name": lib["name"],
|
|
98
|
+
"version": lib.get("version", "unknown"),
|
|
99
|
+
"verified": verified,
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if verified:
|
|
104
|
+
results["verified"] += 1
|
|
105
|
+
else:
|
|
106
|
+
results["failed"] += 1
|
|
107
|
+
|
|
108
|
+
passed = results["failed"] == 0
|
|
109
|
+
execution_time = time.time() - start_time
|
|
110
|
+
|
|
111
|
+
errors = []
|
|
112
|
+
if not passed:
|
|
113
|
+
for lib in results["libraries"]:
|
|
114
|
+
if not lib["verified"]:
|
|
115
|
+
errors.append(
|
|
116
|
+
{
|
|
117
|
+
"message": f"Library verification failed: {lib['name']} {lib['version']}",
|
|
118
|
+
"library": lib["name"],
|
|
119
|
+
"version": lib["version"],
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return CheckResult(
|
|
124
|
+
checker_name=self.name(),
|
|
125
|
+
passed=passed,
|
|
126
|
+
status="passed" if passed else "failed",
|
|
127
|
+
errors=cast(list[Union[dict[str, Any], str]], errors),
|
|
128
|
+
warnings=[],
|
|
129
|
+
info={
|
|
130
|
+
"total_libraries": len(results["libraries"]),
|
|
131
|
+
"verified": results["verified"],
|
|
132
|
+
"failed": results["failed"],
|
|
133
|
+
"libraries": results["libraries"],
|
|
134
|
+
},
|
|
135
|
+
execution_time=execution_time,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def _parse_libraries_from_stack(self, stack_file: Path) -> list[dict[str, str]]:
|
|
139
|
+
"""
|
|
140
|
+
Parse libraries from stack.txt.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
stack_file: Path to stack.txt file
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
List of library dictionaries with 'name' and 'version' keys
|
|
147
|
+
|
|
148
|
+
Raises:
|
|
149
|
+
FileOperationError: If stack file cannot be read
|
|
150
|
+
"""
|
|
151
|
+
libraries = []
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
with open(stack_file) as f:
|
|
155
|
+
content = f.read()
|
|
156
|
+
|
|
157
|
+
# Parse libraries - expecting format like "Python 3.x" or "pytest (testing)"
|
|
158
|
+
lines = content.split("\n")
|
|
159
|
+
for line in lines:
|
|
160
|
+
line = line.strip()
|
|
161
|
+
if not line or line.startswith("#"):
|
|
162
|
+
continue
|
|
163
|
+
|
|
164
|
+
# Extract library name and version
|
|
165
|
+
parts = line.split()
|
|
166
|
+
if len(parts) >= 1:
|
|
167
|
+
name = parts[0]
|
|
168
|
+
version = parts[1] if len(parts) > 1 else "unknown"
|
|
169
|
+
libraries.append({"name": name, "version": version})
|
|
170
|
+
|
|
171
|
+
except OSError as e:
|
|
172
|
+
raise FileOperationError(
|
|
173
|
+
operation="read",
|
|
174
|
+
file_path=str(stack_file),
|
|
175
|
+
details="Failed to read stack.txt file",
|
|
176
|
+
cause=e,
|
|
177
|
+
) from e
|
|
178
|
+
|
|
179
|
+
return libraries
|
|
180
|
+
|
|
181
|
+
def _should_verify_library(self, lib: dict[str, str]) -> bool:
|
|
182
|
+
"""Check if library should be verified via Context7.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
lib: Library dictionary with 'name' and 'version' keys
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
True if library should be verified, False otherwise
|
|
189
|
+
"""
|
|
190
|
+
# Check if library is in important list (if configured)
|
|
191
|
+
important_libs = self.config.get("important_libraries", [])
|
|
192
|
+
if important_libs:
|
|
193
|
+
return lib["name"] in important_libs
|
|
194
|
+
|
|
195
|
+
# By default, verify all libraries
|
|
196
|
+
return True
|
|
197
|
+
|
|
198
|
+
def _query_context7(self, lib: dict[str, str]) -> bool:
|
|
199
|
+
"""Query Context7 MCP for library verification (stub).
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
lib: Library dictionary with 'name' and 'version' keys
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
True if library is current/secure, False otherwise
|
|
206
|
+
|
|
207
|
+
Note:
|
|
208
|
+
This is currently a stub implementation. When fully implemented, this should:
|
|
209
|
+
1. Connect to Context7 MCP server
|
|
210
|
+
2. Query library version and security status
|
|
211
|
+
3. Return True if library is current/secure, False otherwise
|
|
212
|
+
|
|
213
|
+
Returns True by default to allow framework operation.
|
|
214
|
+
"""
|
|
215
|
+
# NOTE: Future integration - Context7 MCP for library verification
|
|
216
|
+
# When implemented, this should:
|
|
217
|
+
# 1. Connect to Context7 MCP server
|
|
218
|
+
# 2. Query library version and security status
|
|
219
|
+
# 3. Return True if library is current/secure, False otherwise
|
|
220
|
+
# Returns True by default to allow framework operation
|
|
221
|
+
return True
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Custom validation checker.
|
|
4
|
+
|
|
5
|
+
Runs user-defined validation rules.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import time
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Union, cast
|
|
13
|
+
|
|
14
|
+
from solokit.core.command_runner import CommandRunner
|
|
15
|
+
from solokit.core.constants import QUALITY_CHECK_LONG_TIMEOUT, QUALITY_CHECK_STANDARD_TIMEOUT
|
|
16
|
+
from solokit.core.logging_config import get_logger
|
|
17
|
+
from solokit.quality.checkers.base import CheckResult, QualityChecker
|
|
18
|
+
|
|
19
|
+
logger = get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CustomValidationChecker(QualityChecker):
|
|
23
|
+
"""Custom validation rules execution."""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
config: dict[str, Any],
|
|
28
|
+
project_root: Path | None = None,
|
|
29
|
+
work_item: dict[str, Any] | None = None,
|
|
30
|
+
runner: CommandRunner | None = None,
|
|
31
|
+
):
|
|
32
|
+
"""Initialize custom validation checker.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
config: Custom validations configuration
|
|
36
|
+
project_root: Project root directory
|
|
37
|
+
work_item: Work item dictionary (for work-item-specific rules)
|
|
38
|
+
runner: Optional CommandRunner instance (for testing)
|
|
39
|
+
"""
|
|
40
|
+
super().__init__(config, project_root)
|
|
41
|
+
self.runner = (
|
|
42
|
+
runner
|
|
43
|
+
if runner is not None
|
|
44
|
+
else CommandRunner(default_timeout=QUALITY_CHECK_LONG_TIMEOUT)
|
|
45
|
+
)
|
|
46
|
+
self.work_item = work_item or {}
|
|
47
|
+
|
|
48
|
+
def name(self) -> str:
|
|
49
|
+
"""Return checker name."""
|
|
50
|
+
return "custom_validations"
|
|
51
|
+
|
|
52
|
+
def is_enabled(self) -> bool:
|
|
53
|
+
"""Check if custom validations are enabled."""
|
|
54
|
+
# Custom validations are enabled if there are any rules defined
|
|
55
|
+
work_item_rules = self.work_item.get("validation_rules", [])
|
|
56
|
+
project_rules = self.config.get("rules", [])
|
|
57
|
+
return len(work_item_rules) > 0 or len(project_rules) > 0
|
|
58
|
+
|
|
59
|
+
def run(self) -> CheckResult:
|
|
60
|
+
"""Run custom validation rules."""
|
|
61
|
+
start_time = time.time()
|
|
62
|
+
|
|
63
|
+
# Get rules from both work item and project config
|
|
64
|
+
work_item_rules = self.work_item.get("validation_rules", [])
|
|
65
|
+
project_rules = self.config.get("rules", [])
|
|
66
|
+
all_rules = work_item_rules + project_rules
|
|
67
|
+
|
|
68
|
+
if not all_rules:
|
|
69
|
+
return self._create_skipped_result(reason="no custom rules defined")
|
|
70
|
+
|
|
71
|
+
logger.info(f"Running {len(all_rules)} custom validation rules")
|
|
72
|
+
|
|
73
|
+
validations = []
|
|
74
|
+
passed = True
|
|
75
|
+
|
|
76
|
+
for rule in all_rules:
|
|
77
|
+
rule_type = rule.get("type")
|
|
78
|
+
required = rule.get("required", False)
|
|
79
|
+
|
|
80
|
+
# Execute rule based on type
|
|
81
|
+
if rule_type == "command":
|
|
82
|
+
rule_passed = self._run_command_validation(rule)
|
|
83
|
+
elif rule_type == "file_exists":
|
|
84
|
+
rule_passed = self._check_file_exists(rule)
|
|
85
|
+
elif rule_type == "grep":
|
|
86
|
+
rule_passed = self._run_grep_validation(rule)
|
|
87
|
+
else:
|
|
88
|
+
logger.warning(f"Unknown validation rule type: {rule_type}")
|
|
89
|
+
rule_passed = True
|
|
90
|
+
|
|
91
|
+
validations.append(
|
|
92
|
+
{
|
|
93
|
+
"name": rule.get("name", "unknown"),
|
|
94
|
+
"passed": rule_passed,
|
|
95
|
+
"required": required,
|
|
96
|
+
"type": rule_type,
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if not rule_passed and required:
|
|
101
|
+
passed = False
|
|
102
|
+
|
|
103
|
+
execution_time = time.time() - start_time
|
|
104
|
+
|
|
105
|
+
errors = []
|
|
106
|
+
for validation in validations:
|
|
107
|
+
if not validation["passed"] and validation["required"]:
|
|
108
|
+
errors.append({"message": f"Required validation failed: {validation['name']}"})
|
|
109
|
+
|
|
110
|
+
warnings = []
|
|
111
|
+
for validation in validations:
|
|
112
|
+
if not validation["passed"] and not validation["required"]:
|
|
113
|
+
warnings.append({"message": f"Optional validation failed: {validation['name']}"})
|
|
114
|
+
|
|
115
|
+
return CheckResult(
|
|
116
|
+
checker_name=self.name(),
|
|
117
|
+
passed=passed,
|
|
118
|
+
status="passed" if passed else "failed",
|
|
119
|
+
errors=cast(list[Union[dict[str, Any], str]], errors),
|
|
120
|
+
warnings=cast(list[Union[dict[str, Any], str]], warnings),
|
|
121
|
+
info={"validations": validations},
|
|
122
|
+
execution_time=execution_time,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def _run_command_validation(self, rule: dict[str, Any]) -> bool:
|
|
126
|
+
"""Run command validation rule."""
|
|
127
|
+
command = rule.get("command")
|
|
128
|
+
if not command:
|
|
129
|
+
logger.warning("Command validation missing 'command' field")
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
logger.debug(f"Running command validation: {command}")
|
|
133
|
+
result = self.runner.run(command.split(), timeout=QUALITY_CHECK_LONG_TIMEOUT)
|
|
134
|
+
return result.success
|
|
135
|
+
|
|
136
|
+
def _check_file_exists(self, rule: dict[str, Any]) -> bool:
|
|
137
|
+
"""Check if file exists at path."""
|
|
138
|
+
file_path = rule.get("path")
|
|
139
|
+
if not file_path:
|
|
140
|
+
logger.warning("File exists validation missing 'path' field")
|
|
141
|
+
return True
|
|
142
|
+
|
|
143
|
+
path = self.project_root / file_path
|
|
144
|
+
exists = path.exists()
|
|
145
|
+
logger.debug(f"Checking file exists: {file_path} -> {exists}")
|
|
146
|
+
return bool(exists)
|
|
147
|
+
|
|
148
|
+
def _run_grep_validation(self, rule: dict[str, Any]) -> bool:
|
|
149
|
+
"""Run grep validation rule."""
|
|
150
|
+
pattern = rule.get("pattern")
|
|
151
|
+
files = rule.get("files", ".")
|
|
152
|
+
|
|
153
|
+
if not pattern:
|
|
154
|
+
logger.warning("Grep validation missing 'pattern' field")
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
logger.debug(f"Running grep validation: pattern={pattern}, files={files}")
|
|
158
|
+
result = self.runner.run(
|
|
159
|
+
["grep", "-r", pattern, files], timeout=QUALITY_CHECK_STANDARD_TIMEOUT
|
|
160
|
+
)
|
|
161
|
+
# grep returns 0 if pattern found
|
|
162
|
+
return result.success
|