specsmith 0.12.0.dev493__tar.gz → 0.12.0.dev496__tar.gz
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.
- {specsmith-0.12.0.dev493/src/specsmith.egg-info → specsmith-0.12.0.dev496}/PKG-INFO +1 -1
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/pyproject.toml +1 -1
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/auditor.py +46 -4
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/cli.py +243 -40
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/mcp_server.py +159 -11
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496/src/specsmith.egg-info}/PKG-INFO +1 -1
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_auditor.py +89 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_mcp_server.py +177 -3
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/LICENSE +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/README.md +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/setup.cfg +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/epistemic/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/epistemic/belief.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/epistemic/certainty.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/epistemic/failure_graph.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/epistemic/py.typed +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/epistemic/recovery.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/epistemic/session.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/epistemic/stress_tester.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/epistemic/trace.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/__main__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/broker.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/chat_runner.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/cleanup.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/context_seed.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/core.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/_status.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/dag.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/events.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/result.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/endpoints.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/events.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/execution_profiles.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/fallback.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/hf_leaderboard.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/hf_sync.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/indexer.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/llm_client.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/mcp.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/memory.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/model_intelligence.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/model_profiles.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/orchestrator.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/permissions.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/profiles.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/provider_registry.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/repl.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/router.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/rules.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/runner.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/safety.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/spawner.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/suggester.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/teams.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/tools.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/verifier.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/agent/voice.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/architect.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/auth.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/block_export.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/channel.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/ci_manager.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/commands/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/commands/intelligence.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/compliance/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/compliance/_compat.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/compliance/checker.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/compliance/evidence.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/compliance/regulations.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/compliance/reporter.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/compressor.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/config.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/console_utils.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/context_orchestrator.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/context_window.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/credit_analyzer.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/credits.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/datasources/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/datasources/base.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/datasources/citations.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/datasources/fpd.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/datasources/odp.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/datasources/patentsview.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/datasources/pfw.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/datasources/ppubs.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/datasources/ptab.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/differ.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/doctor.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/drive.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/editor.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/belief.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/certainty.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/failure_graph.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/recovery.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/stress_tester.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/esdb/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/esdb/bridge.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/eval/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/eval/builtins.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/eval/runner.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/executor.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/exporter.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/governance_logic.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/governance_store.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/governance_yaml.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/app.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/main_window.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/session_tab.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/theme.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/chat_view.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/input_bar.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/provider_bar.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/token_meter.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/tool_panel.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/update_checker.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/gui/worker.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/history_search.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/importer.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/instinct.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/integrations/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/integrations/agent_skill.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/integrations/aider.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/integrations/base.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/integrations/claude_code.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/integrations/codity.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/integrations/copilot.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/integrations/cursor.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/integrations/gemini.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/integrations/windsurf.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/issue_reporter.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/languages.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/ledger.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/mcp_generator.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/migrations/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m001_governance_yaml.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m002_agents_slim.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m003_compliance_init.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m006_session_governance.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/migrations/runner.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/ollama_cmds.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/patent.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/paths.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/phase.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/plugins.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/profiles.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/rate_limits.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/releaser.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/requirements.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/requirements_parser.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/retrieval.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/safe_write.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/scaffolder.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/serve.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/session.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/session_init.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/session_store.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/cloud.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/corporate.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/cross_platform.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/devops.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/docs.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/embedded.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/governance.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/hardware.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/mobile.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/productivity.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/specsmith_skills.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills/ssh.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/skills_builder.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/sync.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/agents.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/contributing.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/license-MIT.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/security.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/editorconfig.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/gitattributes.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/gitignore.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/go/go.mod.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/go/main.go.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/roles.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/rules.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/verification.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/js/package.json.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/ledger.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/python/cli.py.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/python/init.py.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/readme.md.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/rust/main.rs.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/tool_installer.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/toolrules.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/tools.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/trace.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/updater.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/upgrader.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/validator.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/vcs/__init__.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/vcs/base.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/vcs/bitbucket.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/vcs/github.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/vcs/gitlab.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/vcs_commands.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/wireframes.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith/workspace.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/SOURCES.txt +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/dependency_links.txt +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/entry_points.txt +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/requires.txt +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/top_level.txt +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_CMD_001.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_agent_profiles.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_agent_run_feedback.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_agent_runner_ready.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_ai_client.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_ai_intelligence.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_channel.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_chat_diff_decision.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_chat_runner_openai_compat.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_chat_stdin_protocol.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_cli.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_cli_workflows_history_drive.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_compliance.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_compressor.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_dispatch.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_e2e_nexus.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_endpoints_cli.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_endpoints_store.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_epistemic.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_fallback_chain.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_importer.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_integrations.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_integrations_codity.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_intelligence.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_issue_reporter.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_mcp_client.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_new_modules.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_nexus.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_permissions.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_phase1_4_new.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_phase34_completion.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_rate_limits.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_req_248_262.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_req_358_359.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_scaffolder.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_skill_marketplace.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_skills_mcp.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_smoke.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_suggester.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_tools.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_validator.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_vcs.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_warp_parity.py +0 -0
- {specsmith-0.12.0.dev493 → specsmith-0.12.0.dev496}/tests/test_warp_parity_followup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specsmith
|
|
3
|
-
Version: 0.12.0.
|
|
3
|
+
Version: 0.12.0.dev496
|
|
4
4
|
Summary: Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands.
|
|
5
5
|
Author: BitConcepts
|
|
6
6
|
License-Expression: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specsmith"
|
|
7
|
-
version = "0.12.0.
|
|
7
|
+
version = "0.12.0.dev496"
|
|
8
8
|
description = "Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -441,9 +441,11 @@ def check_ledger_health(root: Path) -> list[AuditResult]:
|
|
|
441
441
|
)
|
|
442
442
|
)
|
|
443
443
|
|
|
444
|
-
# Open TODOs
|
|
445
|
-
|
|
446
|
-
|
|
444
|
+
# Open TODOs — only count lines where the checklist marker is at the
|
|
445
|
+
# start of the trimmed line, not prose references such as:
|
|
446
|
+
# "- TODO closure: all 109 - [ ] items changed to - [x]"
|
|
447
|
+
open_todos = sum(1 for line in lines if line.lstrip().startswith("- [ ]"))
|
|
448
|
+
closed_todos = sum(1 for line in lines if line.lstrip().startswith("- [x]"))
|
|
447
449
|
if open_todos > 20:
|
|
448
450
|
results.append(
|
|
449
451
|
AuditResult(
|
|
@@ -715,8 +717,34 @@ def check_tool_configuration(root: Path) -> list[AuditResult]:
|
|
|
715
717
|
return results
|
|
716
718
|
|
|
717
719
|
|
|
720
|
+
# Types that require explicit configuration and cannot be reliably inferred
|
|
721
|
+
# from file-extension counts alone. When a project declares one of these,
|
|
722
|
+
# auto-detection is skipped so that auxiliary tooling languages (e.g. Python
|
|
723
|
+
# verification scripts in an FPGA project) do not produce a false-positive
|
|
724
|
+
# mismatch. See GitHub issue #194.
|
|
725
|
+
_EXPLICIT_ONLY_TYPES: frozenset[str] = frozenset(
|
|
726
|
+
[
|
|
727
|
+
"fpga-rtl",
|
|
728
|
+
"fpga-rtl-amd",
|
|
729
|
+
"fpga-rtl-intel",
|
|
730
|
+
"fpga-rtl-lattice",
|
|
731
|
+
"mixed-fpga-embedded",
|
|
732
|
+
"mixed-fpga-firmware",
|
|
733
|
+
"embedded-hardware",
|
|
734
|
+
"pcb-hardware",
|
|
735
|
+
"yocto-bsp",
|
|
736
|
+
]
|
|
737
|
+
)
|
|
738
|
+
|
|
739
|
+
|
|
718
740
|
def check_type_mismatch(root: Path) -> list[AuditResult]:
|
|
719
|
-
"""Check if scaffold.yml type matches actual detected project type.
|
|
741
|
+
"""Check if scaffold.yml type matches actual detected project type.
|
|
742
|
+
|
|
743
|
+
For hardware / vendor-specific types that cannot be auto-detected from
|
|
744
|
+
file-extension counts (see ``_EXPLICIT_ONLY_TYPES``), the detection step
|
|
745
|
+
is skipped entirely so that auxiliary languages (e.g. Python scripts in an
|
|
746
|
+
FPGA project) never produce a false-positive mismatch.
|
|
747
|
+
"""
|
|
720
748
|
results: list[AuditResult] = []
|
|
721
749
|
scaffold_path = root / "scaffold.yml"
|
|
722
750
|
if not scaffold_path.exists():
|
|
@@ -731,6 +759,20 @@ def check_type_mismatch(root: Path) -> list[AuditResult]:
|
|
|
731
759
|
with open(scaffold_path) as f:
|
|
732
760
|
raw = yaml.safe_load(f)
|
|
733
761
|
config = ProjectConfig(**raw)
|
|
762
|
+
|
|
763
|
+
# Skip auto-detection for types that must be specified explicitly.
|
|
764
|
+
if config.type in _EXPLICIT_ONLY_TYPES:
|
|
765
|
+
results.append(
|
|
766
|
+
AuditResult(
|
|
767
|
+
name="type-mismatch",
|
|
768
|
+
passed=True,
|
|
769
|
+
message=(
|
|
770
|
+
f"Project type {config.type} is explicitly set; auto-detection skipped"
|
|
771
|
+
),
|
|
772
|
+
)
|
|
773
|
+
)
|
|
774
|
+
return results
|
|
775
|
+
|
|
734
776
|
detected = detect_project(root)
|
|
735
777
|
if detected.inferred_type and detected.inferred_type != config.type:
|
|
736
778
|
results.append(
|
|
@@ -350,6 +350,16 @@ def init(config_path: str | None, output_dir: str, no_git: bool, guided: bool) -
|
|
|
350
350
|
|
|
351
351
|
write_phase(target, "inception")
|
|
352
352
|
|
|
353
|
+
# Auto-register with the MCP governance server (best-effort, never blocks)
|
|
354
|
+
with contextlib.suppress(Exception):
|
|
355
|
+
from specsmith.mcp_server import register_project
|
|
356
|
+
|
|
357
|
+
if register_project(str(target)):
|
|
358
|
+
console.print(
|
|
359
|
+
" [dim]\u2713 Registered with MCP server "
|
|
360
|
+
"([bold]specsmith mcp projects[/bold] to view)[/dim]"
|
|
361
|
+
)
|
|
362
|
+
|
|
353
363
|
|
|
354
364
|
def _load_config_with_inheritance(config_path: str) -> dict[str, object]:
|
|
355
365
|
"""Load scaffold.yml, merging parent config if `extends` is set."""
|
|
@@ -1746,6 +1756,16 @@ def import_project(
|
|
|
1746
1756
|
console.print(f"\n[bold green]Done.[/bold green] {len(created)} governance files generated.")
|
|
1747
1757
|
console.print("Governance files generated. Review project configuration.")
|
|
1748
1758
|
|
|
1759
|
+
# Auto-register with the MCP governance server (best-effort, never blocks)
|
|
1760
|
+
with contextlib.suppress(Exception):
|
|
1761
|
+
from specsmith.mcp_server import register_project
|
|
1762
|
+
|
|
1763
|
+
if register_project(str(root)):
|
|
1764
|
+
console.print(
|
|
1765
|
+
" [dim]\u2713 Registered with MCP server "
|
|
1766
|
+
"([bold]specsmith mcp projects[/bold] to view)[/dim]"
|
|
1767
|
+
)
|
|
1768
|
+
|
|
1749
1769
|
|
|
1750
1770
|
def _run_guided_architecture(cfg: ProjectConfig, target: Path) -> list[Path]:
|
|
1751
1771
|
"""Run interactive architecture definition and generate REQ/TEST stubs."""
|
|
@@ -3207,32 +3227,61 @@ def checkpoint_cmd(project_dir: str, as_json: bool) -> None:
|
|
|
3207
3227
|
|
|
3208
3228
|
# ── Human-readable anchor block ───────────────────────────────────────────
|
|
3209
3229
|
# Designed to be compact and survive context summarization.
|
|
3210
|
-
|
|
3230
|
+
_w = 57 # interior width — must match len(hbar)
|
|
3231
|
+
hbar = "\u2550" * _w # ═══…
|
|
3211
3232
|
vbar = "\u2551" # ║
|
|
3212
3233
|
health_icon = "\u2713" if health_ok else "\u2717"
|
|
3213
3234
|
esdb_icon = "\u2713" if esdb_ok else "\u2717"
|
|
3214
3235
|
wi_str = ", ".join(recent_wis) if recent_wis else "none seen"
|
|
3215
3236
|
|
|
3237
|
+
def _arow(rich: str, plain: str, wide: int = 0) -> str:
|
|
3238
|
+
"""Format one anchor box row: ║ <content padded to _w terminal cols> ║.
|
|
3239
|
+
|
|
3240
|
+
``plain`` is the markup-free string used for width calculation.
|
|
3241
|
+
``wide`` is the count of 2-wide (full-width/emoji) characters in ``plain``
|
|
3242
|
+
that add an extra terminal column beyond their Python ``len()``.
|
|
3243
|
+
"""
|
|
3244
|
+
pad = " " * max(0, _w - len(plain) - wide)
|
|
3245
|
+
return f"[bold cyan]{vbar}[/bold cyan]{rich}{pad}[bold cyan]{vbar}[/bold cyan]"
|
|
3246
|
+
|
|
3216
3247
|
console.print(f"[bold cyan]\u2554{hbar}\u2557[/bold cyan]")
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
console.print(
|
|
3220
|
-
|
|
3248
|
+
|
|
3249
|
+
r1 = f" GOVERNANCE ANCHOR {ts}"
|
|
3250
|
+
console.print(_arow(r1, r1))
|
|
3251
|
+
|
|
3252
|
+
r2_plain = f" Project : {project_name}"
|
|
3253
|
+
console.print(_arow(f" Project : [bold]{project_name}[/bold]", r2_plain))
|
|
3254
|
+
|
|
3255
|
+
r3_plain = f" Phase : {phase_emoji} {phase_label} ({phase_pct}%)"
|
|
3256
|
+
console.print(_arow(r3_plain, r3_plain, wide=1 if phase_emoji else 0))
|
|
3257
|
+
|
|
3258
|
+
r4_plain = (
|
|
3259
|
+
f" Health : {health_icon} clean"
|
|
3260
|
+
if health_ok
|
|
3261
|
+
else f" Health : {health_icon} {audit_failed} issues"
|
|
3221
3262
|
)
|
|
3222
|
-
|
|
3223
|
-
f"[green]{health_icon} clean[/green]"
|
|
3263
|
+
r4_rich = (
|
|
3264
|
+
f" Health : [green]{health_icon} clean[/green]"
|
|
3224
3265
|
if health_ok
|
|
3225
|
-
else f"[red]{health_icon} {audit_failed} issues[/red]"
|
|
3266
|
+
else f" Health : [red]{health_icon} {audit_failed} issues[/red]"
|
|
3226
3267
|
)
|
|
3227
|
-
console.print(
|
|
3228
|
-
|
|
3229
|
-
|
|
3268
|
+
console.print(_arow(r4_rich, r4_plain))
|
|
3269
|
+
|
|
3270
|
+
r5 = (
|
|
3271
|
+
f" REQs : {req_count} TESTs: {test_count}"
|
|
3230
3272
|
f" ESDB: {esdb_records} records ({esdb_icon} chain)"
|
|
3231
3273
|
)
|
|
3232
|
-
console.print(
|
|
3274
|
+
console.print(_arow(r5, r5))
|
|
3275
|
+
|
|
3276
|
+
r6 = f" WIs : {wi_str}"
|
|
3277
|
+
console.print(_arow(r6, r6))
|
|
3278
|
+
|
|
3233
3279
|
if last_preflight:
|
|
3234
|
-
|
|
3235
|
-
|
|
3280
|
+
_pf_max = _w - len(" Preflight: ") # 45 — was incorrectly 55
|
|
3281
|
+
pf_short = last_preflight[:_pf_max]
|
|
3282
|
+
r7 = f" Preflight: {pf_short}"
|
|
3283
|
+
console.print(_arow(r7, r7))
|
|
3284
|
+
|
|
3236
3285
|
console.print(f"[bold cyan]\u255a{hbar}\u255d[/bold cyan]")
|
|
3237
3286
|
console.print(
|
|
3238
3287
|
"[dim]Include this block verbatim in any context summary "
|
|
@@ -9020,42 +9069,63 @@ def mcp_list_cmd(project_dir: str, as_json: bool) -> None:
|
|
|
9020
9069
|
|
|
9021
9070
|
|
|
9022
9071
|
@mcp_group.command(name="serve")
|
|
9023
|
-
@click.option(
|
|
9024
|
-
|
|
9072
|
+
@click.option(
|
|
9073
|
+
"--project-dir",
|
|
9074
|
+
type=click.Path(),
|
|
9075
|
+
default=None,
|
|
9076
|
+
help=(
|
|
9077
|
+
"Set this path as the *primary* project (first slot / default for tool calls "
|
|
9078
|
+
"that omit project_dir). Omit to use the registry automatically."
|
|
9079
|
+
),
|
|
9080
|
+
)
|
|
9081
|
+
@click.option(
|
|
9082
|
+
"--project-dirs",
|
|
9083
|
+
default="",
|
|
9084
|
+
help=(
|
|
9085
|
+
"Extra project directories to add on top of the registry (comma-separated absolute paths)."
|
|
9086
|
+
),
|
|
9087
|
+
)
|
|
9088
|
+
def mcp_serve_cmd(project_dir: str | None, project_dirs: str) -> None:
|
|
9025
9089
|
"""Start the native governance MCP stdio server (REQ-363).
|
|
9026
9090
|
|
|
9027
9091
|
Implements MCP 2024-11-05 over stdin/stdout (JSON-RPC 2.0).
|
|
9028
|
-
Exposes
|
|
9092
|
+
Exposes seven governance tools to any MCP client.
|
|
9029
9093
|
|
|
9030
9094
|
\b
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
governance_phase Current AEE phase + readiness %
|
|
9035
|
-
governance_req_list List all requirements
|
|
9036
|
-
governance_trace_seal Seal a milestone/decision
|
|
9095
|
+
Recommended Warp config (set once, never touch again)::
|
|
9096
|
+
|
|
9097
|
+
{"specsmith-governance": {"command": "specsmith", "args": ["mcp", "serve"]}}
|
|
9037
9098
|
|
|
9038
9099
|
\b
|
|
9039
|
-
|
|
9100
|
+
Then register each project once from inside that project::
|
|
9040
9101
|
|
|
9041
|
-
|
|
9102
|
+
specsmith mcp register
|
|
9103
|
+
|
|
9104
|
+
\b
|
|
9105
|
+
The server reads the registry at startup and serves all registered
|
|
9106
|
+
projects automatically — no config changes needed for new projects.
|
|
9042
9107
|
|
|
9043
9108
|
\b
|
|
9044
|
-
Or pass inline to oz (use ``specsmith mcp install-warp`` for the
|
|
9109
|
+
Or pass inline to oz (use ``specsmith mcp install-warp`` for the snippet)::
|
|
9045
9110
|
|
|
9046
9111
|
oz agent run --mcp "$(specsmith mcp install-warp --json)" --prompt "..."
|
|
9047
9112
|
"""
|
|
9048
9113
|
from specsmith.mcp_server import run_server
|
|
9049
9114
|
|
|
9050
|
-
|
|
9115
|
+
extra = [p.strip() for p in project_dirs.split(",") if p.strip()] if project_dirs else []
|
|
9116
|
+
run_server(project_dir=project_dir, extra_project_dirs=extra)
|
|
9051
9117
|
|
|
9052
9118
|
|
|
9053
9119
|
@mcp_group.command(name="install-warp")
|
|
9054
|
-
@click.option("--project-dir", type=click.Path(), default=".", show_default=True)
|
|
9055
9120
|
@click.option("--json", "as_json", is_flag=True, default=False, help="Emit JSON config only.")
|
|
9056
|
-
def mcp_install_warp_cmd(
|
|
9121
|
+
def mcp_install_warp_cmd(as_json: bool) -> None:
|
|
9057
9122
|
"""Print the Warp MCP config snippet for the governance server (REQ-363).
|
|
9058
9123
|
|
|
9124
|
+
Generates a minimal, registry-aware config — paste it into Warp once
|
|
9125
|
+
and never change it again. Register new projects with::
|
|
9126
|
+
|
|
9127
|
+
specsmith mcp register # in the project directory
|
|
9128
|
+
|
|
9059
9129
|
Copy the output into Warp Settings → Agents → MCP servers, or pass it
|
|
9060
9130
|
to ``oz agent run --mcp '<json>'`` for a one-off cloud agent run.
|
|
9061
9131
|
"""
|
|
@@ -9063,15 +9133,36 @@ def mcp_install_warp_cmd(project_dir: str, as_json: bool) -> None:
|
|
|
9063
9133
|
import shutil
|
|
9064
9134
|
import sys
|
|
9065
9135
|
|
|
9066
|
-
#
|
|
9067
|
-
#
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9136
|
+
# Env vars the server needs regardless of how it is invoked.
|
|
9137
|
+
# SPECSMITH_ALLOW_NON_PIPX=1 — prevents the pipx-enforcement gate from
|
|
9138
|
+
# exiting before the MCP handshake when Warp starts the server directly.
|
|
9139
|
+
# SPECSMITH_NO_AUTO_UPDATE / SPECSMITH_PYPI_CHECKED — suppress network
|
|
9140
|
+
# calls on startup so the server responds immediately.
|
|
9141
|
+
server_env = {
|
|
9142
|
+
"SPECSMITH_ALLOW_NON_PIPX": "1",
|
|
9143
|
+
"SPECSMITH_NO_AUTO_UPDATE": "1",
|
|
9144
|
+
"SPECSMITH_PYPI_CHECKED": "1",
|
|
9145
|
+
}
|
|
9146
|
+
|
|
9147
|
+
# Executable detection strategy (in priority order):
|
|
9148
|
+
# 1. specsmith (or specsmith.exe) on PATH — covers pipx shims and system installs.
|
|
9149
|
+
# 2. python -m specsmith via the current interpreter — reliable fallback
|
|
9150
|
+
# for editable dev installs and venvs where the console script wrapper
|
|
9151
|
+
# is absent or resolves incorrectly (e.g. some Windows pipx setups).
|
|
9152
|
+
specsmith_exe = shutil.which("specsmith") or shutil.which("specsmith.exe")
|
|
9153
|
+
if specsmith_exe:
|
|
9154
|
+
cmd = specsmith_exe
|
|
9155
|
+
args: list[str] = ["mcp", "serve"]
|
|
9156
|
+
else:
|
|
9157
|
+
# Fall back to `python -m specsmith` using the current interpreter.
|
|
9158
|
+
cmd = sys.executable
|
|
9159
|
+
args = ["-m", "specsmith", "mcp", "serve"]
|
|
9160
|
+
|
|
9071
9161
|
config = {
|
|
9072
9162
|
"specsmith-governance": {
|
|
9073
|
-
"command":
|
|
9074
|
-
"args":
|
|
9163
|
+
"command": cmd,
|
|
9164
|
+
"args": args,
|
|
9165
|
+
"env": server_env,
|
|
9075
9166
|
}
|
|
9076
9167
|
}
|
|
9077
9168
|
|
|
@@ -9086,10 +9177,122 @@ def mcp_install_warp_cmd(project_dir: str, as_json: bool) -> None:
|
|
|
9086
9177
|
)
|
|
9087
9178
|
console.print(_json.dumps(config, indent=2))
|
|
9088
9179
|
console.print(
|
|
9089
|
-
"\n[dim]
|
|
9090
|
-
"
|
|
9091
|
-
"
|
|
9092
|
-
"
|
|
9180
|
+
"\n[dim][bold]One-time setup[/bold] — paste this config into Warp once," # noqa: E501
|
|
9181
|
+
" then never touch it again.\n"
|
|
9182
|
+
"\nTo add each project, run this inside the project directory:\n"
|
|
9183
|
+
" [bold]specsmith mcp register[/bold]\n"
|
|
9184
|
+
"\nThe server reads [bold]~/.specsmith/mcp-projects.json[/bold] at startup\n"
|
|
9185
|
+
"and serves all registered projects automatically.\n"
|
|
9186
|
+
"\nView registered projects: [bold]specsmith mcp projects[/bold]\n"
|
|
9187
|
+
"\nVerify server: specsmith mcp serve (then send an initialize message).[/dim]"
|
|
9188
|
+
)
|
|
9189
|
+
|
|
9190
|
+
|
|
9191
|
+
@mcp_group.command(name="register")
|
|
9192
|
+
@click.argument("path", default=".", required=False)
|
|
9193
|
+
def mcp_register_cmd(path: str) -> None:
|
|
9194
|
+
"""Register a project directory with the MCP server registry.
|
|
9195
|
+
|
|
9196
|
+
Run once inside a project directory to add it to
|
|
9197
|
+
``~/.specsmith/mcp-projects.json``. The next ``specsmith mcp serve``
|
|
9198
|
+
invocation will automatically include it — no Warp config changes needed.
|
|
9199
|
+
|
|
9200
|
+
\b
|
|
9201
|
+
Examples::
|
|
9202
|
+
|
|
9203
|
+
specsmith mcp register # register current directory
|
|
9204
|
+
specsmith mcp register /path/to/myproject
|
|
9205
|
+
"""
|
|
9206
|
+
from specsmith.mcp_server import register_project
|
|
9207
|
+
|
|
9208
|
+
root = Path(path).resolve()
|
|
9209
|
+
if not root.exists():
|
|
9210
|
+
console.print(f"[red]\u2717[/red] Path does not exist: {root}")
|
|
9211
|
+
raise SystemExit(1)
|
|
9212
|
+
|
|
9213
|
+
added = register_project(str(root))
|
|
9214
|
+
if added:
|
|
9215
|
+
console.print(f"[green]\u2713[/green] Registered: [bold]{root}[/bold]")
|
|
9216
|
+
if not (root / ".specsmith").exists():
|
|
9217
|
+
console.print(
|
|
9218
|
+
" [yellow]\u26a0[/yellow] No .specsmith/ found. "
|
|
9219
|
+
"Run [bold]specsmith init[/bold] or [bold]specsmith import[/bold] first."
|
|
9220
|
+
)
|
|
9221
|
+
else:
|
|
9222
|
+
console.print(f"[dim]Already registered: {root}[/dim]")
|
|
9223
|
+
console.print(
|
|
9224
|
+
" [dim]specsmith mcp projects ← view all registered[/dim]\n"
|
|
9225
|
+
" [dim]specsmith mcp serve ← start the server[/dim]"
|
|
9226
|
+
)
|
|
9227
|
+
|
|
9228
|
+
|
|
9229
|
+
@mcp_group.command(name="unregister")
|
|
9230
|
+
@click.argument("path", default=".", required=False)
|
|
9231
|
+
def mcp_unregister_cmd(path: str) -> None:
|
|
9232
|
+
"""Remove a project directory from the MCP server registry.
|
|
9233
|
+
|
|
9234
|
+
\b
|
|
9235
|
+
Examples::
|
|
9236
|
+
|
|
9237
|
+
specsmith mcp unregister # unregister current directory
|
|
9238
|
+
specsmith mcp unregister /path/to/myproject
|
|
9239
|
+
"""
|
|
9240
|
+
from specsmith.mcp_server import unregister_project
|
|
9241
|
+
|
|
9242
|
+
root = Path(path).resolve()
|
|
9243
|
+
removed = unregister_project(str(root))
|
|
9244
|
+
if removed:
|
|
9245
|
+
console.print(f"[green]\u2713[/green] Unregistered: [bold]{root}[/bold]")
|
|
9246
|
+
else:
|
|
9247
|
+
console.print(f"[yellow]Not registered: {root}[/yellow]")
|
|
9248
|
+
|
|
9249
|
+
|
|
9250
|
+
@mcp_group.command(name="projects")
|
|
9251
|
+
@click.option("--json", "as_json", is_flag=True, default=False, help="Emit as JSON.")
|
|
9252
|
+
def mcp_projects_cmd(as_json: bool) -> None:
|
|
9253
|
+
"""List all projects registered with the MCP server.
|
|
9254
|
+
|
|
9255
|
+
Shows each registered project path and whether it still exists on disk.
|
|
9256
|
+
The first entry is the default (used when a tool call omits project_dir).
|
|
9257
|
+
"""
|
|
9258
|
+
import json as _json
|
|
9259
|
+
import os
|
|
9260
|
+
|
|
9261
|
+
from specsmith.mcp_server import _registry_file, read_registry
|
|
9262
|
+
|
|
9263
|
+
projects = read_registry()
|
|
9264
|
+
reg_path = _registry_file()
|
|
9265
|
+
|
|
9266
|
+
if as_json:
|
|
9267
|
+
entries = [
|
|
9268
|
+
{"path": p, "exists": Path(p).exists(), "is_default": i == 0}
|
|
9269
|
+
for i, p in enumerate(projects)
|
|
9270
|
+
]
|
|
9271
|
+
click.echo(_json.dumps({"registry": str(reg_path), "projects": entries}, indent=2))
|
|
9272
|
+
return
|
|
9273
|
+
|
|
9274
|
+
if not projects:
|
|
9275
|
+
console.print("[yellow]No projects registered.[/yellow]")
|
|
9276
|
+
console.print(
|
|
9277
|
+
"[dim]Run [bold]specsmith mcp register[/bold] inside a project to add it.[/dim]"
|
|
9278
|
+
)
|
|
9279
|
+
return
|
|
9280
|
+
|
|
9281
|
+
console.print(
|
|
9282
|
+
f"[bold]Registered MCP projects[/bold] ({len(projects)}) [dim]{reg_path}[/dim]\n"
|
|
9283
|
+
)
|
|
9284
|
+
for i, p in enumerate(projects):
|
|
9285
|
+
exists = Path(p).exists()
|
|
9286
|
+
default_tag = " [bold cyan][default][/bold cyan]" if i == 0 else ""
|
|
9287
|
+
health = "[green]\u2713 exists[/green]" if exists else "[red]\u2717 not found[/red]"
|
|
9288
|
+
# Abbreviate long paths using ~ for home
|
|
9289
|
+
display = p.replace(os.path.expanduser("~"), "~")
|
|
9290
|
+
console.print(f" {health} {display}{default_tag}")
|
|
9291
|
+
|
|
9292
|
+
console.print(
|
|
9293
|
+
"\n[dim] specsmith mcp register [path] ← add a project"
|
|
9294
|
+
"\n specsmith mcp unregister [path] ← remove a project"
|
|
9295
|
+
"\n specsmith mcp serve ← start the server[/dim]"
|
|
9093
9296
|
)
|
|
9094
9297
|
|
|
9095
9298
|
|
|
@@ -35,8 +35,8 @@ No external dependencies — stdlib only + existing specsmith modules.
|
|
|
35
35
|
|
|
36
36
|
from __future__ import annotations
|
|
37
37
|
|
|
38
|
-
import contextlib
|
|
39
38
|
import json
|
|
39
|
+
import os
|
|
40
40
|
import sys
|
|
41
41
|
import time
|
|
42
42
|
from pathlib import Path
|
|
@@ -50,7 +50,92 @@ MCP_PROTOCOL_VERSION = "2024-11-05"
|
|
|
50
50
|
SERVER_NAME = "specsmith-governance"
|
|
51
51
|
SERVER_VERSION = "0.12.0"
|
|
52
52
|
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
# Multi-project state — populated by run_server() at startup
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
# Default project dir used when a tool call omits ``project_dir``.
|
|
58
|
+
_DEFAULT_PROJECT_DIR: str = "."
|
|
59
|
+
|
|
60
|
+
# All registered project directories (absolute paths), default first.
|
|
61
|
+
_REGISTERED_PROJECTS: list[str] = []
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
# Persistent project registry (~/.specsmith/mcp-projects.json)
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _registry_file() -> Path:
|
|
70
|
+
"""Return the path to the user-level MCP project registry file.
|
|
71
|
+
|
|
72
|
+
Respects ``SPECSMITH_HOME`` if set; otherwise uses
|
|
73
|
+
``~/.specsmith/mcp-projects.json``.
|
|
74
|
+
"""
|
|
75
|
+
base = os.environ.get("SPECSMITH_HOME", "").strip()
|
|
76
|
+
home = Path(base) if base else Path.home() / ".specsmith"
|
|
77
|
+
return home / "mcp-projects.json"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def read_registry() -> list[str]:
|
|
81
|
+
"""Return all absolute project paths from the registry.
|
|
82
|
+
|
|
83
|
+
Returns an empty list when the registry file is missing or malformed.
|
|
84
|
+
Invalid entries (empty strings, non-strings) are silently skipped.
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
data = json.loads(_registry_file().read_text(encoding="utf-8"))
|
|
88
|
+
projects = data.get("projects", []) if isinstance(data, dict) else []
|
|
89
|
+
return [p for p in projects if isinstance(p, str) and p]
|
|
90
|
+
except Exception: # noqa: BLE001
|
|
91
|
+
return []
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def write_registry(paths: list[str]) -> None:
|
|
95
|
+
"""Persist *paths* to the registry file (creates parent dirs as needed)."""
|
|
96
|
+
reg = _registry_file()
|
|
97
|
+
reg.parent.mkdir(parents=True, exist_ok=True)
|
|
98
|
+
reg.write_text(
|
|
99
|
+
json.dumps({"projects": paths}, indent=2, ensure_ascii=False),
|
|
100
|
+
encoding="utf-8",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def register_project(path: str = ".") -> bool:
|
|
105
|
+
"""Add *path* to the registry. Returns True if it was newly added."""
|
|
106
|
+
abs_path = str(Path(path).resolve())
|
|
107
|
+
projects = read_registry()
|
|
108
|
+
if abs_path in projects:
|
|
109
|
+
return False
|
|
110
|
+
projects.append(abs_path)
|
|
111
|
+
write_registry(projects)
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def unregister_project(path: str = ".") -> bool:
|
|
116
|
+
"""Remove *path* from the registry. Returns True if it was present."""
|
|
117
|
+
abs_path = str(Path(path).resolve())
|
|
118
|
+
projects = read_registry()
|
|
119
|
+
if abs_path not in projects:
|
|
120
|
+
return False
|
|
121
|
+
write_registry([p for p in projects if p != abs_path])
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
|
|
53
125
|
_TOOLS: list[dict[str, Any]] = [
|
|
126
|
+
{
|
|
127
|
+
"name": "governance_project_list",
|
|
128
|
+
"description": (
|
|
129
|
+
"List all project directories registered in this MCP server instance. "
|
|
130
|
+
"Use the returned paths as the `project_dir` argument for other tools "
|
|
131
|
+
"when working with multiple simultaneous projects."
|
|
132
|
+
),
|
|
133
|
+
"inputSchema": {
|
|
134
|
+
"type": "object",
|
|
135
|
+
"properties": {},
|
|
136
|
+
"required": [],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
54
139
|
{
|
|
55
140
|
"name": "governance_audit",
|
|
56
141
|
"description": (
|
|
@@ -195,10 +280,25 @@ _TOOLS: list[dict[str, Any]] = [
|
|
|
195
280
|
|
|
196
281
|
|
|
197
282
|
def _resolve_root(args: dict[str, Any]) -> Path:
|
|
198
|
-
|
|
283
|
+
"""Resolve project_dir from args, falling back to the server default.
|
|
284
|
+
|
|
285
|
+
If ``project_dir`` is not in *args* (or is empty), we use
|
|
286
|
+
``_DEFAULT_PROJECT_DIR`` so callers don't have to repeat the path on
|
|
287
|
+
every tool call when only one project is active.
|
|
288
|
+
"""
|
|
289
|
+
raw = args.get("project_dir") or _DEFAULT_PROJECT_DIR
|
|
199
290
|
return Path(str(raw)).resolve()
|
|
200
291
|
|
|
201
292
|
|
|
293
|
+
def _handle_governance_project_list(args: dict[str, Any]) -> dict[str, Any]: # noqa: ARG001
|
|
294
|
+
"""Return the list of projects registered in this server instance."""
|
|
295
|
+
return {
|
|
296
|
+
"default_project": _DEFAULT_PROJECT_DIR,
|
|
297
|
+
"projects": _REGISTERED_PROJECTS,
|
|
298
|
+
"count": len(_REGISTERED_PROJECTS),
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
|
|
202
302
|
def _handle_governance_audit(args: dict[str, Any]) -> dict[str, Any]:
|
|
203
303
|
"""Run the governance audit and return structured results."""
|
|
204
304
|
root = _resolve_root(args)
|
|
@@ -487,6 +587,7 @@ def _handle_governance_trace_seal(args: dict[str, Any]) -> dict[str, Any]:
|
|
|
487
587
|
# ---------------------------------------------------------------------------
|
|
488
588
|
|
|
489
589
|
_HANDLERS = {
|
|
590
|
+
"governance_project_list": _handle_governance_project_list,
|
|
490
591
|
"governance_audit": _handle_governance_audit,
|
|
491
592
|
"governance_checkpoint": _handle_governance_checkpoint,
|
|
492
593
|
"governance_preflight": _handle_governance_preflight,
|
|
@@ -576,20 +677,57 @@ def _handle_request(msg: dict[str, Any]) -> dict[str, Any] | None:
|
|
|
576
677
|
return None
|
|
577
678
|
|
|
578
679
|
|
|
579
|
-
def run_server(
|
|
680
|
+
def run_server(
|
|
681
|
+
project_dir: str | None = None,
|
|
682
|
+
extra_project_dirs: list[str] | None = None,
|
|
683
|
+
) -> None:
|
|
580
684
|
"""Start the MCP stdio server. Blocks until stdin closes.
|
|
581
685
|
|
|
582
686
|
Reads newline-delimited JSON-RPC 2.0 messages from stdin and
|
|
583
687
|
writes responses to stdout. Stderr is used for diagnostic messages.
|
|
584
|
-
|
|
585
|
-
|
|
688
|
+
|
|
689
|
+
Project discovery order (highest priority first):
|
|
690
|
+
|
|
691
|
+
1. ``project_dir`` — if provided, becomes the *default* project (first
|
|
692
|
+
slot). Any tool call that omits ``project_dir`` will target this.
|
|
693
|
+
2. **Registry** — all paths in ``~/.specsmith/mcp-projects.json`` are
|
|
694
|
+
merged in. Run ``specsmith mcp register`` in a project once to add it.
|
|
695
|
+
3. ``extra_project_dirs`` — additional paths appended after the registry.
|
|
696
|
+
4. **Fallback** — if nothing resolves, the current working directory is
|
|
697
|
+
used (useful for development/testing).
|
|
698
|
+
|
|
699
|
+
All paths are resolved to absolute form; duplicates are silently dropped.
|
|
700
|
+
The server never calls ``os.chdir()``.
|
|
586
701
|
"""
|
|
587
|
-
|
|
702
|
+
global _DEFAULT_PROJECT_DIR, _REGISTERED_PROJECTS
|
|
703
|
+
|
|
704
|
+
all_dirs: list[str] = []
|
|
705
|
+
seen: set[str] = set()
|
|
588
706
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
707
|
+
def _add(p: str) -> None:
|
|
708
|
+
abs_p = str(Path(p).resolve())
|
|
709
|
+
if abs_p not in seen:
|
|
710
|
+
seen.add(abs_p)
|
|
711
|
+
all_dirs.append(abs_p)
|
|
712
|
+
|
|
713
|
+
# 1. Explicit primary project (becomes the default)
|
|
714
|
+
if project_dir is not None:
|
|
715
|
+
_add(project_dir)
|
|
716
|
+
|
|
717
|
+
# 2. Persistent registry
|
|
718
|
+
for p in read_registry():
|
|
719
|
+
_add(p)
|
|
720
|
+
|
|
721
|
+
# 3. Additional explicit dirs
|
|
722
|
+
for p in extra_project_dirs or []:
|
|
723
|
+
_add(p)
|
|
724
|
+
|
|
725
|
+
# 4. CWD fallback when nothing is registered
|
|
726
|
+
if not all_dirs:
|
|
727
|
+
_add(".")
|
|
728
|
+
|
|
729
|
+
_DEFAULT_PROJECT_DIR = all_dirs[0]
|
|
730
|
+
_REGISTERED_PROJECTS = all_dirs
|
|
593
731
|
|
|
594
732
|
for raw_line in sys.stdin:
|
|
595
733
|
raw_line = raw_line.strip()
|
|
@@ -615,4 +753,14 @@ def run_server(project_dir: str = ".") -> None:
|
|
|
615
753
|
_send(response)
|
|
616
754
|
|
|
617
755
|
|
|
618
|
-
__all__ = [
|
|
756
|
+
__all__ = [
|
|
757
|
+
"run_server",
|
|
758
|
+
"read_registry",
|
|
759
|
+
"write_registry",
|
|
760
|
+
"register_project",
|
|
761
|
+
"unregister_project",
|
|
762
|
+
"SERVER_NAME",
|
|
763
|
+
"SERVER_VERSION",
|
|
764
|
+
"_TOOLS",
|
|
765
|
+
"_HANDLERS",
|
|
766
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specsmith
|
|
3
|
-
Version: 0.12.0.
|
|
3
|
+
Version: 0.12.0.dev496
|
|
4
4
|
Summary: Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands.
|
|
5
5
|
Author: BitConcepts
|
|
6
6
|
License-Expression: MIT
|