specsmith 0.12.0.dev494__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.dev494/src/specsmith.egg-info → specsmith-0.12.0.dev496}/PKG-INFO +1 -1
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/pyproject.toml +1 -1
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/auditor.py +46 -4
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/cli.py +186 -56
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/mcp_server.py +113 -15
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496/src/specsmith.egg-info}/PKG-INFO +1 -1
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_auditor.py +89 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_mcp_server.py +99 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/LICENSE +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/README.md +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/setup.cfg +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/belief.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/certainty.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/failure_graph.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/py.typed +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/recovery.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/session.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/stress_tester.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/trace.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/__main__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/broker.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/chat_runner.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/cleanup.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/context_seed.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/core.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/_status.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/dag.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/events.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/result.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/endpoints.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/events.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/execution_profiles.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/fallback.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/hf_leaderboard.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/hf_sync.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/indexer.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/llm_client.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/mcp.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/memory.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/model_intelligence.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/model_profiles.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/orchestrator.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/permissions.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/profiles.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/provider_registry.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/repl.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/router.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/rules.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/runner.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/safety.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/spawner.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/suggester.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/teams.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/tools.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/verifier.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/voice.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/architect.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/auth.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/block_export.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/channel.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/ci_manager.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/commands/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/commands/intelligence.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compliance/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compliance/_compat.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compliance/checker.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compliance/evidence.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compliance/regulations.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compliance/reporter.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compressor.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/config.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/console_utils.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/context_orchestrator.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/context_window.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/credit_analyzer.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/credits.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/base.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/citations.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/fpd.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/odp.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/patentsview.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/pfw.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/ppubs.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/ptab.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/differ.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/doctor.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/drive.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/editor.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/belief.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/certainty.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/failure_graph.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/recovery.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/stress_tester.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/esdb/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/esdb/bridge.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/eval/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/eval/builtins.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/eval/runner.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/executor.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/exporter.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/governance_logic.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/governance_store.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/governance_yaml.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/app.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/main_window.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/session_tab.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/theme.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/chat_view.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/input_bar.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/provider_bar.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/token_meter.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/tool_panel.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/update_checker.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/worker.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/history_search.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/importer.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/instinct.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/agent_skill.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/aider.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/base.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/claude_code.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/codity.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/copilot.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/cursor.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/gemini.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/windsurf.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/issue_reporter.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/languages.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/ledger.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/mcp_generator.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m001_governance_yaml.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m002_agents_slim.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m003_compliance_init.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m006_session_governance.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/runner.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/ollama_cmds.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/patent.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/paths.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/phase.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/plugins.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/profiles.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/rate_limits.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/releaser.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/requirements.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/requirements_parser.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/retrieval.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/safe_write.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/scaffolder.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/serve.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/session.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/session_init.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/session_store.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/cloud.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/corporate.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/cross_platform.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/devops.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/docs.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/embedded.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/governance.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/hardware.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/mobile.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/productivity.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/specsmith_skills.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/ssh.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills_builder.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/sync.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/agents.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/contributing.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/license-MIT.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/security.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/editorconfig.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/gitattributes.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/gitignore.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/go/go.mod.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/go/main.go.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/roles.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/rules.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/verification.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/js/package.json.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/ledger.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/python/cli.py.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/python/init.py.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/readme.md.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/rust/main.rs.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/tool_installer.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/toolrules.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/tools.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/trace.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/updater.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/upgrader.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/validator.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/vcs/__init__.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/vcs/base.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/vcs/bitbucket.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/vcs/github.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/vcs/gitlab.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/vcs_commands.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/wireframes.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/workspace.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/SOURCES.txt +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/dependency_links.txt +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/entry_points.txt +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/requires.txt +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/top_level.txt +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_CMD_001.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_agent_profiles.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_agent_run_feedback.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_agent_runner_ready.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_ai_client.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_ai_intelligence.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_channel.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_chat_diff_decision.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_chat_runner_openai_compat.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_chat_stdin_protocol.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_cli.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_cli_workflows_history_drive.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_compliance.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_compressor.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_dispatch.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_e2e_nexus.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_endpoints_cli.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_endpoints_store.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_epistemic.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_fallback_chain.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_importer.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_integrations.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_integrations_codity.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_intelligence.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_issue_reporter.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_mcp_client.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_new_modules.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_nexus.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_permissions.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_phase1_4_new.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_phase34_completion.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_rate_limits.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_req_248_262.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_req_358_359.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_scaffolder.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_skill_marketplace.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_skills_mcp.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_smoke.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_suggester.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_tools.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_validator.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_vcs.py +0 -0
- {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_warp_parity.py +0 -0
- {specsmith-0.12.0.dev494 → 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."""
|
|
@@ -9049,46 +9069,44 @@ def mcp_list_cmd(project_dir: str, as_json: bool) -> None:
|
|
|
9049
9069
|
|
|
9050
9070
|
|
|
9051
9071
|
@mcp_group.command(name="serve")
|
|
9052
|
-
@click.option(
|
|
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
|
+
)
|
|
9053
9081
|
@click.option(
|
|
9054
9082
|
"--project-dirs",
|
|
9055
9083
|
default="",
|
|
9056
9084
|
help=(
|
|
9057
|
-
"
|
|
9058
|
-
"All registered projects are accessible to any tool call via their "
|
|
9059
|
-
"absolute path as the project_dir argument."
|
|
9085
|
+
"Extra project directories to add on top of the registry (comma-separated absolute paths)."
|
|
9060
9086
|
),
|
|
9061
9087
|
)
|
|
9062
|
-
def mcp_serve_cmd(project_dir: str, project_dirs: str) -> None:
|
|
9088
|
+
def mcp_serve_cmd(project_dir: str | None, project_dirs: str) -> None:
|
|
9063
9089
|
"""Start the native governance MCP stdio server (REQ-363).
|
|
9064
9090
|
|
|
9065
9091
|
Implements MCP 2024-11-05 over stdin/stdout (JSON-RPC 2.0).
|
|
9066
|
-
Exposes seven governance tools to any MCP client
|
|
9092
|
+
Exposes seven governance tools to any MCP client.
|
|
9067
9093
|
|
|
9068
9094
|
\b
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
governance_preflight Preflight a change intent
|
|
9073
|
-
governance_phase Current AEE phase + readiness %
|
|
9074
|
-
governance_req_list List all requirements
|
|
9075
|
-
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"]}}
|
|
9076
9098
|
|
|
9077
9099
|
\b
|
|
9078
|
-
|
|
9100
|
+
Then register each project once from inside that project::
|
|
9079
9101
|
|
|
9080
|
-
|
|
9081
|
-
"args": ["mcp", "serve", "--project-dir", "/path/to/project"]}}
|
|
9102
|
+
specsmith mcp register
|
|
9082
9103
|
|
|
9083
9104
|
\b
|
|
9084
|
-
|
|
9085
|
-
|
|
9086
|
-
{"specsmith-governance": {"command": "specsmith",
|
|
9087
|
-
"args": ["mcp", "serve", "--project-dir", "/path/to/proj1",
|
|
9088
|
-
"--project-dirs", "/path/to/proj2,/path/to/proj3"]}}
|
|
9105
|
+
The server reads the registry at startup and serves all registered
|
|
9106
|
+
projects automatically — no config changes needed for new projects.
|
|
9089
9107
|
|
|
9090
9108
|
\b
|
|
9091
|
-
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)::
|
|
9092
9110
|
|
|
9093
9111
|
oz agent run --mcp "$(specsmith mcp install-warp --json)" --prompt "..."
|
|
9094
9112
|
"""
|
|
@@ -9099,42 +9117,52 @@ def mcp_serve_cmd(project_dir: str, project_dirs: str) -> None:
|
|
|
9099
9117
|
|
|
9100
9118
|
|
|
9101
9119
|
@mcp_group.command(name="install-warp")
|
|
9102
|
-
@click.option("--project-dir", type=click.Path(), default=".", show_default=True)
|
|
9103
|
-
@click.option(
|
|
9104
|
-
"--project-dirs",
|
|
9105
|
-
default="",
|
|
9106
|
-
help="Additional project dirs (comma-separated) to include in the multi-project config.",
|
|
9107
|
-
)
|
|
9108
9120
|
@click.option("--json", "as_json", is_flag=True, default=False, help="Emit JSON config only.")
|
|
9109
|
-
def mcp_install_warp_cmd(
|
|
9121
|
+
def mcp_install_warp_cmd(as_json: bool) -> None:
|
|
9110
9122
|
"""Print the Warp MCP config snippet for the governance server (REQ-363).
|
|
9111
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
|
+
|
|
9112
9129
|
Copy the output into Warp Settings → Agents → MCP servers, or pass it
|
|
9113
9130
|
to ``oz agent run --mcp '<json>'`` for a one-off cloud agent run.
|
|
9114
|
-
|
|
9115
|
-
For multi-project setups pass ``--project-dirs`` with comma-separated
|
|
9116
|
-
absolute paths; one server instance will serve all projects.
|
|
9117
9131
|
"""
|
|
9118
9132
|
import json as _json
|
|
9119
9133
|
import shutil
|
|
9120
9134
|
import sys
|
|
9121
9135
|
|
|
9122
|
-
#
|
|
9123
|
-
#
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
|
|
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
|
+
}
|
|
9129
9146
|
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
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"]
|
|
9133
9160
|
|
|
9134
9161
|
config = {
|
|
9135
9162
|
"specsmith-governance": {
|
|
9136
|
-
"command":
|
|
9163
|
+
"command": cmd,
|
|
9137
9164
|
"args": args,
|
|
9165
|
+
"env": server_env,
|
|
9138
9166
|
}
|
|
9139
9167
|
}
|
|
9140
9168
|
|
|
@@ -9148,22 +9176,124 @@ def mcp_install_warp_cmd(project_dir: str, project_dirs: str, as_json: bool) ->
|
|
|
9148
9176
|
"or pass inline to [bold]oz agent run --mcp '<json>'[/bold]:\n"
|
|
9149
9177
|
)
|
|
9150
9178
|
console.print(_json.dumps(config, indent=2))
|
|
9151
|
-
|
|
9152
|
-
|
|
9153
|
-
|
|
9154
|
-
|
|
9155
|
-
|
|
9156
|
-
|
|
9179
|
+
console.print(
|
|
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
|
+
)
|
|
9157
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]")
|
|
9158
9276
|
console.print(
|
|
9159
|
-
"
|
|
9160
|
-
"governance_audit, governance_preflight,\n"
|
|
9161
|
-
"governance_checkpoint, governance_phase, governance_req_list, and\n"
|
|
9162
|
-
"governance_trace_seal as structured MCP tool calls.\n"
|
|
9163
|
-
"\nFor multiple projects add --project-dirs /path/a,/path/b to serve them "
|
|
9164
|
-
"all from one server instance.\n"
|
|
9165
|
-
"\nVerify with: specsmith mcp serve (then send an initialize message).[/dim]"
|
|
9277
|
+
"[dim]Run [bold]specsmith mcp register[/bold] inside a project to add it.[/dim]"
|
|
9166
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]"
|
|
9296
|
+
)
|
|
9167
9297
|
|
|
9168
9298
|
|
|
9169
9299
|
main.add_command(mcp_group)
|
|
@@ -36,6 +36,7 @@ No external dependencies — stdlib only + existing specsmith modules.
|
|
|
36
36
|
from __future__ import annotations
|
|
37
37
|
|
|
38
38
|
import json
|
|
39
|
+
import os
|
|
39
40
|
import sys
|
|
40
41
|
import time
|
|
41
42
|
from pathlib import Path
|
|
@@ -59,6 +60,68 @@ _DEFAULT_PROJECT_DIR: str = "."
|
|
|
59
60
|
# All registered project directories (absolute paths), default first.
|
|
60
61
|
_REGISTERED_PROJECTS: list[str] = []
|
|
61
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
|
+
|
|
62
125
|
_TOOLS: list[dict[str, Any]] = [
|
|
63
126
|
{
|
|
64
127
|
"name": "governance_project_list",
|
|
@@ -615,7 +678,7 @@ def _handle_request(msg: dict[str, Any]) -> dict[str, Any] | None:
|
|
|
615
678
|
|
|
616
679
|
|
|
617
680
|
def run_server(
|
|
618
|
-
project_dir: str =
|
|
681
|
+
project_dir: str | None = None,
|
|
619
682
|
extra_project_dirs: list[str] | None = None,
|
|
620
683
|
) -> None:
|
|
621
684
|
"""Start the MCP stdio server. Blocks until stdin closes.
|
|
@@ -623,23 +686,48 @@ def run_server(
|
|
|
623
686
|
Reads newline-delimited JSON-RPC 2.0 messages from stdin and
|
|
624
687
|
writes responses to stdout. Stderr is used for diagnostic messages.
|
|
625
688
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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).
|
|
630
698
|
|
|
631
|
-
|
|
632
|
-
|
|
699
|
+
All paths are resolved to absolute form; duplicates are silently dropped.
|
|
700
|
+
The server never calls ``os.chdir()``.
|
|
633
701
|
"""
|
|
634
702
|
global _DEFAULT_PROJECT_DIR, _REGISTERED_PROJECTS
|
|
635
703
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
if
|
|
642
|
-
|
|
704
|
+
all_dirs: list[str] = []
|
|
705
|
+
seen: set[str] = set()
|
|
706
|
+
|
|
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
|
|
643
731
|
|
|
644
732
|
for raw_line in sys.stdin:
|
|
645
733
|
raw_line = raw_line.strip()
|
|
@@ -665,4 +753,14 @@ def run_server(
|
|
|
665
753
|
_send(response)
|
|
666
754
|
|
|
667
755
|
|
|
668
|
-
__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
|
|
@@ -304,3 +304,92 @@ class TestAcceptedWarningsSuppression:
|
|
|
304
304
|
assert report.suppressed_count == 2
|
|
305
305
|
assert report.failed == 0
|
|
306
306
|
assert report.healthy is True
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
# ---------------------------------------------------------------------------
|
|
310
|
+
# Regression: issue #195 — LEDGER open-TODO counter false positive
|
|
311
|
+
# ---------------------------------------------------------------------------
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
class TestLedgerTodoCounterRegression:
|
|
315
|
+
"""Regression tests for #195 — prose '- [ ]' strings must not be counted."""
|
|
316
|
+
|
|
317
|
+
def test_prose_todo_reference_not_counted(self, tmp_path: Path) -> None:
|
|
318
|
+
"""A narrative Checks run: line referencing '- [ ]' must not add open TODOs."""
|
|
319
|
+
from specsmith.auditor import check_ledger_health
|
|
320
|
+
|
|
321
|
+
ledger = (
|
|
322
|
+
"# Ledger\n\n"
|
|
323
|
+
"## Session 2026-05-30\n"
|
|
324
|
+
"### Checks run:\n"
|
|
325
|
+
"- TODO closure: all 109 - [ ] items changed to - [x] (batch, 2026-05-30)\n"
|
|
326
|
+
)
|
|
327
|
+
(tmp_path / "LEDGER.md").write_text(ledger, encoding="utf-8")
|
|
328
|
+
results = check_ledger_health(tmp_path)
|
|
329
|
+
todo_result = next(r for r in results if r.name == "ledger-open-todos")
|
|
330
|
+
assert todo_result.passed
|
|
331
|
+
assert "0 open" in todo_result.message
|
|
332
|
+
|
|
333
|
+
def test_real_open_todo_is_counted(self, tmp_path: Path) -> None:
|
|
334
|
+
"""A genuine checklist item starting with '- [ ]' must still be counted."""
|
|
335
|
+
from specsmith.auditor import check_ledger_health
|
|
336
|
+
|
|
337
|
+
ledger = "# Ledger\n\n" + "\n".join(f"- [ ] item {i}" for i in range(25))
|
|
338
|
+
(tmp_path / "LEDGER.md").write_text(ledger, encoding="utf-8")
|
|
339
|
+
results = check_ledger_health(tmp_path)
|
|
340
|
+
todo_result = next(r for r in results if r.name == "ledger-open-todos")
|
|
341
|
+
assert not todo_result.passed
|
|
342
|
+
assert "25 open" in todo_result.message
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
# ---------------------------------------------------------------------------
|
|
346
|
+
# Regression: issue #194 — check_type_mismatch false positive for FPGA types
|
|
347
|
+
# ---------------------------------------------------------------------------
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class TestTypeMismatchExplicitOnlyTypes:
|
|
351
|
+
"""Regression tests for #194 — hardware/vendor types skip auto-detection."""
|
|
352
|
+
|
|
353
|
+
def test_fpga_rtl_amd_skips_detection(self, tmp_path: Path) -> None:
|
|
354
|
+
"""fpga-rtl-amd type must pass type-mismatch even when Python files dominate."""
|
|
355
|
+
from specsmith.auditor import check_type_mismatch
|
|
356
|
+
|
|
357
|
+
# Lots of .py files to simulate an FPGA project with Python tooling
|
|
358
|
+
src = tmp_path / "python"
|
|
359
|
+
src.mkdir()
|
|
360
|
+
for i in range(20):
|
|
361
|
+
(src / f"tool_{i}.py").write_text("pass\n", encoding="utf-8")
|
|
362
|
+
|
|
363
|
+
(tmp_path / "scaffold.yml").write_text(
|
|
364
|
+
"name: fpga-project\ntype: fpga-rtl-amd\nspec_version: 0.10.1\nvcs_platform: github\n",
|
|
365
|
+
encoding="utf-8",
|
|
366
|
+
)
|
|
367
|
+
results = check_type_mismatch(tmp_path)
|
|
368
|
+
assert len(results) == 1
|
|
369
|
+
assert results[0].passed
|
|
370
|
+
assert "explicitly set" in results[0].message
|
|
371
|
+
|
|
372
|
+
@pytest.mark.parametrize(
|
|
373
|
+
"ptype",
|
|
374
|
+
[
|
|
375
|
+
"fpga-rtl",
|
|
376
|
+
"fpga-rtl-intel",
|
|
377
|
+
"fpga-rtl-lattice",
|
|
378
|
+
"mixed-fpga-embedded",
|
|
379
|
+
"mixed-fpga-firmware",
|
|
380
|
+
"embedded-hardware",
|
|
381
|
+
"pcb-hardware",
|
|
382
|
+
"yocto-bsp",
|
|
383
|
+
],
|
|
384
|
+
)
|
|
385
|
+
def test_all_explicit_only_types_skip_detection(self, tmp_path: Path, ptype: str) -> None:
|
|
386
|
+
"""Every member of _EXPLICIT_ONLY_TYPES must bypass auto-detection."""
|
|
387
|
+
from specsmith.auditor import check_type_mismatch
|
|
388
|
+
|
|
389
|
+
(tmp_path / "scaffold.yml").write_text(
|
|
390
|
+
f"name: proj\ntype: {ptype}\nspec_version: 0.10.1\nvcs_platform: github\n",
|
|
391
|
+
encoding="utf-8",
|
|
392
|
+
)
|
|
393
|
+
results = check_type_mismatch(tmp_path)
|
|
394
|
+
assert len(results) == 1
|
|
395
|
+
assert results[0].passed
|