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/project/tree.py
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Generate and update project tree documentation.
|
|
4
|
+
|
|
5
|
+
Tracks structural changes to the project with reasoning.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import sys
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from solokit.core.command_runner import CommandRunner
|
|
17
|
+
from solokit.core.constants import TREE_GENERATION_TIMEOUT
|
|
18
|
+
from solokit.core.error_handlers import log_errors
|
|
19
|
+
from solokit.core.exceptions import (
|
|
20
|
+
FileOperationError,
|
|
21
|
+
)
|
|
22
|
+
from solokit.core.output import get_output
|
|
23
|
+
|
|
24
|
+
output = get_output()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TreeGenerator:
|
|
28
|
+
"""Generate project tree documentation."""
|
|
29
|
+
|
|
30
|
+
def __init__(self, project_root: Path | None = None):
|
|
31
|
+
"""Initialize TreeGenerator with project root path."""
|
|
32
|
+
self.project_root = project_root or Path.cwd()
|
|
33
|
+
self.tree_file = self.project_root / ".session" / "tracking" / "tree.txt"
|
|
34
|
+
self.updates_file = self.project_root / ".session" / "tracking" / "tree_updates.json"
|
|
35
|
+
self.runner = CommandRunner(
|
|
36
|
+
default_timeout=TREE_GENERATION_TIMEOUT, working_dir=self.project_root
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Items to ignore
|
|
40
|
+
self.ignore_patterns = [
|
|
41
|
+
# Version control
|
|
42
|
+
".git",
|
|
43
|
+
# Python
|
|
44
|
+
"__pycache__",
|
|
45
|
+
"*.pyc",
|
|
46
|
+
".pytest_cache",
|
|
47
|
+
".venv",
|
|
48
|
+
"venv",
|
|
49
|
+
"*.egg-info",
|
|
50
|
+
".mypy_cache",
|
|
51
|
+
".ruff_cache",
|
|
52
|
+
# JavaScript/TypeScript
|
|
53
|
+
"node_modules",
|
|
54
|
+
".next",
|
|
55
|
+
".turbo",
|
|
56
|
+
"out",
|
|
57
|
+
"build",
|
|
58
|
+
"dist",
|
|
59
|
+
".vercel",
|
|
60
|
+
"*.tsbuildinfo",
|
|
61
|
+
# Test coverage
|
|
62
|
+
"coverage",
|
|
63
|
+
".nyc_output",
|
|
64
|
+
# Caches
|
|
65
|
+
".cache",
|
|
66
|
+
# OS
|
|
67
|
+
".DS_Store",
|
|
68
|
+
# Temp/logs
|
|
69
|
+
"*.log",
|
|
70
|
+
"*.tmp",
|
|
71
|
+
"*.backup",
|
|
72
|
+
# Solokit
|
|
73
|
+
".session",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
@log_errors()
|
|
77
|
+
def generate_tree(self) -> str:
|
|
78
|
+
"""Generate tree using tree command, falls back to Python implementation.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
str: Project tree representation
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
FileOperationError: If tree generation fails completely
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
# Build ignore arguments
|
|
88
|
+
ignore_args = []
|
|
89
|
+
for pattern in self.ignore_patterns:
|
|
90
|
+
ignore_args.extend(["-I", pattern])
|
|
91
|
+
|
|
92
|
+
result = self.runner.run(["tree", "-a", "--dirsfirst"] + ignore_args)
|
|
93
|
+
|
|
94
|
+
if result.success:
|
|
95
|
+
return result.stdout
|
|
96
|
+
else:
|
|
97
|
+
# tree command failed, use fallback
|
|
98
|
+
return self._generate_tree_fallback()
|
|
99
|
+
|
|
100
|
+
except (OSError, FileNotFoundError):
|
|
101
|
+
# tree command not available, use fallback
|
|
102
|
+
return self._generate_tree_fallback()
|
|
103
|
+
|
|
104
|
+
def _generate_tree_fallback(self) -> str:
|
|
105
|
+
"""Fallback tree generation without tree command.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
str: Project tree representation
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
FileOperationError: If filesystem operations fail
|
|
112
|
+
"""
|
|
113
|
+
lines = [str(self.project_root.name) + "/"]
|
|
114
|
+
|
|
115
|
+
def should_ignore(path: Path) -> bool:
|
|
116
|
+
for pattern in self.ignore_patterns:
|
|
117
|
+
if pattern.startswith("*"):
|
|
118
|
+
if path.name.endswith(pattern[1:]):
|
|
119
|
+
return True
|
|
120
|
+
elif pattern in path.parts:
|
|
121
|
+
return True
|
|
122
|
+
return False
|
|
123
|
+
|
|
124
|
+
def add_tree(path: Path, prefix: str = "", is_last: bool = True) -> None:
|
|
125
|
+
if should_ignore(path):
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
connector = "└── " if is_last else "├── "
|
|
129
|
+
lines.append(prefix + connector + path.name)
|
|
130
|
+
|
|
131
|
+
if path.is_dir():
|
|
132
|
+
children = sorted(path.iterdir(), key=lambda p: (not p.is_dir(), p.name))
|
|
133
|
+
children = [c for c in children if not should_ignore(c)]
|
|
134
|
+
|
|
135
|
+
for i, child in enumerate(children):
|
|
136
|
+
is_last_child = i == len(children) - 1
|
|
137
|
+
extension = " " if is_last else "│ "
|
|
138
|
+
add_tree(child, prefix + extension, is_last_child)
|
|
139
|
+
|
|
140
|
+
# Generate tree
|
|
141
|
+
try:
|
|
142
|
+
children = sorted(self.project_root.iterdir(), key=lambda p: (not p.is_dir(), p.name))
|
|
143
|
+
children = [c for c in children if not should_ignore(c)]
|
|
144
|
+
|
|
145
|
+
for i, child in enumerate(children):
|
|
146
|
+
add_tree(child, "", i == len(children) - 1)
|
|
147
|
+
|
|
148
|
+
return "\n".join(lines)
|
|
149
|
+
except OSError as e:
|
|
150
|
+
raise FileOperationError(
|
|
151
|
+
operation="read",
|
|
152
|
+
file_path=str(self.project_root),
|
|
153
|
+
details="Failed to generate project tree",
|
|
154
|
+
cause=e,
|
|
155
|
+
) from e
|
|
156
|
+
|
|
157
|
+
def detect_changes(self, old_tree: str, new_tree: str) -> list[dict]:
|
|
158
|
+
"""Detect structural changes between trees."""
|
|
159
|
+
old_lines = set(old_tree.split("\n"))
|
|
160
|
+
new_lines = set(new_tree.split("\n"))
|
|
161
|
+
|
|
162
|
+
added = new_lines - old_lines
|
|
163
|
+
removed = old_lines - new_lines
|
|
164
|
+
|
|
165
|
+
changes = []
|
|
166
|
+
|
|
167
|
+
# Categorize changes
|
|
168
|
+
for line in added:
|
|
169
|
+
if "/" in line or line.strip().endswith("/"):
|
|
170
|
+
changes.append({"type": "directory_added", "path": line.strip()})
|
|
171
|
+
elif line.strip():
|
|
172
|
+
changes.append({"type": "file_added", "path": line.strip()})
|
|
173
|
+
|
|
174
|
+
for line in removed:
|
|
175
|
+
if "/" in line or line.strip().endswith("/"):
|
|
176
|
+
changes.append({"type": "directory_removed", "path": line.strip()})
|
|
177
|
+
elif line.strip():
|
|
178
|
+
changes.append({"type": "file_removed", "path": line.strip()})
|
|
179
|
+
|
|
180
|
+
return changes
|
|
181
|
+
|
|
182
|
+
@log_errors()
|
|
183
|
+
def update_tree(
|
|
184
|
+
self, session_num: int | None = None, non_interactive: bool = False
|
|
185
|
+
) -> list[dict[str, str]]:
|
|
186
|
+
"""Generate/update tree.txt and detect changes.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
session_num: Current session number
|
|
190
|
+
non_interactive: If True, skip interactive reasoning prompts
|
|
191
|
+
|
|
192
|
+
Raises:
|
|
193
|
+
FileOperationError: If reading/writing tree files fails
|
|
194
|
+
"""
|
|
195
|
+
# Generate new tree
|
|
196
|
+
new_tree = self.generate_tree()
|
|
197
|
+
|
|
198
|
+
# Load old tree if exists
|
|
199
|
+
old_tree = ""
|
|
200
|
+
if self.tree_file.exists():
|
|
201
|
+
try:
|
|
202
|
+
old_tree = self.tree_file.read_text()
|
|
203
|
+
except OSError as e:
|
|
204
|
+
raise FileOperationError(
|
|
205
|
+
operation="read",
|
|
206
|
+
file_path=str(self.tree_file),
|
|
207
|
+
details="Failed to read existing tree file",
|
|
208
|
+
cause=e,
|
|
209
|
+
) from e
|
|
210
|
+
|
|
211
|
+
# Detect changes
|
|
212
|
+
changes = self.detect_changes(old_tree, new_tree)
|
|
213
|
+
|
|
214
|
+
# Filter out minor changes (just ordering, etc.)
|
|
215
|
+
significant_changes = [
|
|
216
|
+
c
|
|
217
|
+
for c in changes
|
|
218
|
+
if c["type"] in ["directory_added", "directory_removed"]
|
|
219
|
+
or len(changes) < 20 # If few changes, they're probably significant
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
# Save new tree
|
|
223
|
+
try:
|
|
224
|
+
self.tree_file.parent.mkdir(parents=True, exist_ok=True)
|
|
225
|
+
self.tree_file.write_text(new_tree)
|
|
226
|
+
except OSError as e:
|
|
227
|
+
raise FileOperationError(
|
|
228
|
+
operation="write",
|
|
229
|
+
file_path=str(self.tree_file),
|
|
230
|
+
details="Failed to write tree file",
|
|
231
|
+
cause=e,
|
|
232
|
+
) from e
|
|
233
|
+
|
|
234
|
+
# If significant changes detected, prompt for reasoning (unless non-interactive)
|
|
235
|
+
if significant_changes and session_num:
|
|
236
|
+
output.info(f"\n{'=' * 50}")
|
|
237
|
+
output.info("Structural Changes Detected")
|
|
238
|
+
output.info("=" * 50)
|
|
239
|
+
|
|
240
|
+
for change in significant_changes[:10]: # Show first 10
|
|
241
|
+
output.info(f" {change['type'].upper()}: {change['path']}")
|
|
242
|
+
|
|
243
|
+
if len(significant_changes) > 10:
|
|
244
|
+
output.info(f" ... and {len(significant_changes) - 10} more changes")
|
|
245
|
+
|
|
246
|
+
if non_interactive:
|
|
247
|
+
reasoning = "Automated update during session completion"
|
|
248
|
+
output.info("\n(Non-interactive mode: recording changes without manual reasoning)")
|
|
249
|
+
else:
|
|
250
|
+
output.info("\nPlease provide reasoning for these structural changes:")
|
|
251
|
+
reasoning = input("> ")
|
|
252
|
+
|
|
253
|
+
# Update tree_updates.json
|
|
254
|
+
self._record_tree_update(session_num, significant_changes, reasoning)
|
|
255
|
+
|
|
256
|
+
return changes
|
|
257
|
+
|
|
258
|
+
@log_errors()
|
|
259
|
+
def _record_tree_update(
|
|
260
|
+
self, session_num: int, changes: list[dict[str, Any]], reasoning: str
|
|
261
|
+
) -> None:
|
|
262
|
+
"""Record tree update in tree_updates.json.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
session_num: Current session number
|
|
266
|
+
changes: List of detected changes
|
|
267
|
+
reasoning: User-provided reasoning for changes
|
|
268
|
+
|
|
269
|
+
Raises:
|
|
270
|
+
FileOperationError: If writing tree updates fails
|
|
271
|
+
"""
|
|
272
|
+
updates: dict[str, Any] = {"updates": []}
|
|
273
|
+
|
|
274
|
+
if self.updates_file.exists():
|
|
275
|
+
try:
|
|
276
|
+
updates = json.loads(self.updates_file.read_text())
|
|
277
|
+
except (json.JSONDecodeError, OSError):
|
|
278
|
+
# If tree_updates.json is corrupted or unreadable, start fresh
|
|
279
|
+
# Log warning but don't fail - we can rebuild the history
|
|
280
|
+
updates = {"updates": []}
|
|
281
|
+
|
|
282
|
+
update_entry = {
|
|
283
|
+
"timestamp": datetime.now().isoformat(),
|
|
284
|
+
"session": session_num,
|
|
285
|
+
"changes": changes,
|
|
286
|
+
"reasoning": reasoning,
|
|
287
|
+
"architecture_impact": "", # Could prompt for this too
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
updates["updates"].append(update_entry)
|
|
291
|
+
|
|
292
|
+
try:
|
|
293
|
+
self.updates_file.write_text(json.dumps(updates, indent=2))
|
|
294
|
+
except OSError as e:
|
|
295
|
+
raise FileOperationError(
|
|
296
|
+
operation="write",
|
|
297
|
+
file_path=str(self.updates_file),
|
|
298
|
+
details="Failed to write tree updates",
|
|
299
|
+
cause=e,
|
|
300
|
+
) from e
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def main() -> None:
|
|
304
|
+
"""CLI entry point.
|
|
305
|
+
|
|
306
|
+
Handles exceptions and provides user-friendly error messages.
|
|
307
|
+
"""
|
|
308
|
+
import argparse
|
|
309
|
+
|
|
310
|
+
from solokit.core.exceptions import SolokitError
|
|
311
|
+
from solokit.core.output import get_output
|
|
312
|
+
|
|
313
|
+
output = get_output()
|
|
314
|
+
|
|
315
|
+
parser = argparse.ArgumentParser(description="Generate project tree documentation")
|
|
316
|
+
parser.add_argument("--session", type=int, help="Current session number")
|
|
317
|
+
parser.add_argument("--show-changes", action="store_true", help="Show changes from last run")
|
|
318
|
+
parser.add_argument(
|
|
319
|
+
"--non-interactive",
|
|
320
|
+
action="store_true",
|
|
321
|
+
help="Skip interactive prompts (use automated reasoning)",
|
|
322
|
+
)
|
|
323
|
+
args = parser.parse_args()
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
generator = TreeGenerator()
|
|
327
|
+
|
|
328
|
+
if args.show_changes:
|
|
329
|
+
if generator.updates_file.exists():
|
|
330
|
+
try:
|
|
331
|
+
updates = json.loads(generator.updates_file.read_text())
|
|
332
|
+
output.info("Recent structural changes:")
|
|
333
|
+
for update in updates["updates"][-5:]:
|
|
334
|
+
output.info(f"\nSession {update['session']} ({update['timestamp']})")
|
|
335
|
+
output.info(f"Reasoning: {update['reasoning']}")
|
|
336
|
+
output.info(f"Changes: {len(update['changes'])}")
|
|
337
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
338
|
+
raise FileOperationError(
|
|
339
|
+
operation="read",
|
|
340
|
+
file_path=str(generator.updates_file),
|
|
341
|
+
details="Failed to parse tree updates file",
|
|
342
|
+
cause=e,
|
|
343
|
+
) from e
|
|
344
|
+
else:
|
|
345
|
+
output.info("No tree updates recorded yet")
|
|
346
|
+
else:
|
|
347
|
+
changes = generator.update_tree(
|
|
348
|
+
session_num=args.session, non_interactive=args.non_interactive
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
if changes:
|
|
352
|
+
output.info(f"\n✓ Tree updated with {len(changes)} changes")
|
|
353
|
+
else:
|
|
354
|
+
output.info("\n✓ Tree generated (no changes)")
|
|
355
|
+
|
|
356
|
+
output.info(f"✓ Saved to: {generator.tree_file}")
|
|
357
|
+
|
|
358
|
+
except SolokitError as e:
|
|
359
|
+
# Handle structured Solokit errors with user-friendly output
|
|
360
|
+
output.error(f"\nError: {e.message}")
|
|
361
|
+
if e.remediation:
|
|
362
|
+
output.error(f"Suggestion: {e.remediation}")
|
|
363
|
+
sys.exit(e.exit_code)
|
|
364
|
+
except KeyboardInterrupt:
|
|
365
|
+
output.error("\n\nOperation cancelled by user.")
|
|
366
|
+
sys.exit(130)
|
|
367
|
+
except Exception as e:
|
|
368
|
+
# Unexpected errors
|
|
369
|
+
output.error(f"\nUnexpected error: {e}")
|
|
370
|
+
output.error("Please report this issue.")
|
|
371
|
+
sys.exit(1)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
if __name__ == "__main__":
|
|
375
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Quality gates, validation, and testing infrastructure."""
|