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.
Files changed (289) hide show
  1. {specsmith-0.12.0.dev494/src/specsmith.egg-info → specsmith-0.12.0.dev496}/PKG-INFO +1 -1
  2. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/pyproject.toml +1 -1
  3. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/auditor.py +46 -4
  4. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/cli.py +186 -56
  5. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/mcp_server.py +113 -15
  6. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496/src/specsmith.egg-info}/PKG-INFO +1 -1
  7. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_auditor.py +89 -0
  8. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_mcp_server.py +99 -0
  9. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/LICENSE +0 -0
  10. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/README.md +0 -0
  11. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/setup.cfg +0 -0
  12. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/__init__.py +0 -0
  13. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/belief.py +0 -0
  14. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/certainty.py +0 -0
  15. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/failure_graph.py +0 -0
  16. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/py.typed +0 -0
  17. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/recovery.py +0 -0
  18. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/session.py +0 -0
  19. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/stress_tester.py +0 -0
  20. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/epistemic/trace.py +0 -0
  21. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/__init__.py +0 -0
  22. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/__main__.py +0 -0
  23. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/__init__.py +0 -0
  24. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/broker.py +0 -0
  25. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/chat_runner.py +0 -0
  26. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/cleanup.py +0 -0
  27. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/context_seed.py +0 -0
  28. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/core.py +0 -0
  29. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/__init__.py +0 -0
  30. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/_status.py +0 -0
  31. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/dag.py +0 -0
  32. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
  33. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/events.py +0 -0
  34. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/dispatch/result.py +0 -0
  35. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/endpoints.py +0 -0
  36. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/events.py +0 -0
  37. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/execution_profiles.py +0 -0
  38. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/fallback.py +0 -0
  39. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/hf_leaderboard.py +0 -0
  40. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/hf_sync.py +0 -0
  41. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/indexer.py +0 -0
  42. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/llm_client.py +0 -0
  43. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/mcp.py +0 -0
  44. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/memory.py +0 -0
  45. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/model_intelligence.py +0 -0
  46. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/model_profiles.py +0 -0
  47. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/orchestrator.py +0 -0
  48. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/permissions.py +0 -0
  49. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/profiles.py +0 -0
  50. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/provider_registry.py +0 -0
  51. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/repl.py +0 -0
  52. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/router.py +0 -0
  53. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/rules.py +0 -0
  54. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/runner.py +0 -0
  55. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/safety.py +0 -0
  56. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/spawner.py +0 -0
  57. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/suggester.py +0 -0
  58. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/teams.py +0 -0
  59. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/tools.py +0 -0
  60. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/verifier.py +0 -0
  61. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/agent/voice.py +0 -0
  62. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/architect.py +0 -0
  63. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/auth.py +0 -0
  64. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/block_export.py +0 -0
  65. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/channel.py +0 -0
  66. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/ci_manager.py +0 -0
  67. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/commands/__init__.py +0 -0
  68. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/commands/intelligence.py +0 -0
  69. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compliance/__init__.py +0 -0
  70. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compliance/_compat.py +0 -0
  71. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compliance/checker.py +0 -0
  72. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compliance/evidence.py +0 -0
  73. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compliance/regulations.py +0 -0
  74. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compliance/reporter.py +0 -0
  75. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/compressor.py +0 -0
  76. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/config.py +0 -0
  77. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/console_utils.py +0 -0
  78. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/context_orchestrator.py +0 -0
  79. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/context_window.py +0 -0
  80. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/credit_analyzer.py +0 -0
  81. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/credits.py +0 -0
  82. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/__init__.py +0 -0
  83. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/base.py +0 -0
  84. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/citations.py +0 -0
  85. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/fpd.py +0 -0
  86. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/odp.py +0 -0
  87. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/patentsview.py +0 -0
  88. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/pfw.py +0 -0
  89. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/ppubs.py +0 -0
  90. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/datasources/ptab.py +0 -0
  91. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/differ.py +0 -0
  92. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/doctor.py +0 -0
  93. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/drive.py +0 -0
  94. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/editor.py +0 -0
  95. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/__init__.py +0 -0
  96. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/belief.py +0 -0
  97. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/certainty.py +0 -0
  98. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/failure_graph.py +0 -0
  99. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/recovery.py +0 -0
  100. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/epistemic/stress_tester.py +0 -0
  101. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/esdb/__init__.py +0 -0
  102. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/esdb/bridge.py +0 -0
  103. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/eval/__init__.py +0 -0
  104. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/eval/builtins.py +0 -0
  105. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/eval/runner.py +0 -0
  106. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/executor.py +0 -0
  107. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/exporter.py +0 -0
  108. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/governance_logic.py +0 -0
  109. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/governance_store.py +0 -0
  110. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/governance_yaml.py +0 -0
  111. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/__init__.py +0 -0
  112. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/app.py +0 -0
  113. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/main_window.py +0 -0
  114. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/session_tab.py +0 -0
  115. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/theme.py +0 -0
  116. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/__init__.py +0 -0
  117. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/chat_view.py +0 -0
  118. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/input_bar.py +0 -0
  119. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/provider_bar.py +0 -0
  120. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/token_meter.py +0 -0
  121. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/tool_panel.py +0 -0
  122. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/widgets/update_checker.py +0 -0
  123. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/gui/worker.py +0 -0
  124. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/history_search.py +0 -0
  125. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/importer.py +0 -0
  126. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/instinct.py +0 -0
  127. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/__init__.py +0 -0
  128. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/agent_skill.py +0 -0
  129. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/aider.py +0 -0
  130. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/base.py +0 -0
  131. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/claude_code.py +0 -0
  132. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/codity.py +0 -0
  133. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/copilot.py +0 -0
  134. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/cursor.py +0 -0
  135. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/gemini.py +0 -0
  136. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/integrations/windsurf.py +0 -0
  137. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/issue_reporter.py +0 -0
  138. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/languages.py +0 -0
  139. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/ledger.py +0 -0
  140. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/mcp_generator.py +0 -0
  141. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/__init__.py +0 -0
  142. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m001_governance_yaml.py +0 -0
  143. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m002_agents_slim.py +0 -0
  144. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m003_compliance_init.py +0 -0
  145. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
  146. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
  147. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/m006_session_governance.py +0 -0
  148. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/migrations/runner.py +0 -0
  149. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/ollama_cmds.py +0 -0
  150. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/patent.py +0 -0
  151. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/paths.py +0 -0
  152. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/phase.py +0 -0
  153. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/plugins.py +0 -0
  154. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/profiles.py +0 -0
  155. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/rate_limits.py +0 -0
  156. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/releaser.py +0 -0
  157. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/requirements.py +0 -0
  158. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/requirements_parser.py +0 -0
  159. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/retrieval.py +0 -0
  160. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/safe_write.py +0 -0
  161. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/scaffolder.py +0 -0
  162. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/serve.py +0 -0
  163. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/session.py +0 -0
  164. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/session_init.py +0 -0
  165. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/session_store.py +0 -0
  166. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/__init__.py +0 -0
  167. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/cloud.py +0 -0
  168. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/corporate.py +0 -0
  169. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/cross_platform.py +0 -0
  170. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/devops.py +0 -0
  171. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/docs.py +0 -0
  172. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/embedded.py +0 -0
  173. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/governance.py +0 -0
  174. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/hardware.py +0 -0
  175. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/mobile.py +0 -0
  176. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/productivity.py +0 -0
  177. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/specsmith_skills.py +0 -0
  178. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills/ssh.py +0 -0
  179. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/skills_builder.py +0 -0
  180. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/sync.py +0 -0
  181. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/agents.md.j2 +0 -0
  182. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
  183. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
  184. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/contributing.md.j2 +0 -0
  185. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
  186. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
  187. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/license-MIT.j2 +0 -0
  188. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
  189. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/community/security.md.j2 +0 -0
  190. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
  191. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
  192. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
  193. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
  194. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
  195. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/editorconfig.j2 +0 -0
  196. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/gitattributes.j2 +0 -0
  197. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/gitignore.j2 +0 -0
  198. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/go/go.mod.j2 +0 -0
  199. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/go/main.go.j2 +0 -0
  200. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
  201. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
  202. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
  203. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
  204. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
  205. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
  206. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/roles.md.j2 +0 -0
  207. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/rules.md.j2 +0 -0
  208. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
  209. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
  210. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/governance/verification.md.j2 +0 -0
  211. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/js/package.json.j2 +0 -0
  212. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/ledger.md.j2 +0 -0
  213. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/python/cli.py.j2 +0 -0
  214. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/python/init.py.j2 +0 -0
  215. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
  216. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/readme.md.j2 +0 -0
  217. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
  218. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/rust/main.rs.j2 +0 -0
  219. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
  220. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
  221. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
  222. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
  223. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
  224. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
  225. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
  226. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/tool_installer.py +0 -0
  227. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/toolrules.py +0 -0
  228. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/tools.py +0 -0
  229. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/trace.py +0 -0
  230. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/updater.py +0 -0
  231. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/upgrader.py +0 -0
  232. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/validator.py +0 -0
  233. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/vcs/__init__.py +0 -0
  234. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/vcs/base.py +0 -0
  235. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/vcs/bitbucket.py +0 -0
  236. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/vcs/github.py +0 -0
  237. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/vcs/gitlab.py +0 -0
  238. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/vcs_commands.py +0 -0
  239. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/wireframes.py +0 -0
  240. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith/workspace.py +0 -0
  241. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/SOURCES.txt +0 -0
  242. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/dependency_links.txt +0 -0
  243. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/entry_points.txt +0 -0
  244. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/requires.txt +0 -0
  245. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/src/specsmith.egg-info/top_level.txt +0 -0
  246. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_CMD_001.py +0 -0
  247. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_agent_profiles.py +0 -0
  248. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_agent_run_feedback.py +0 -0
  249. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_agent_runner_ready.py +0 -0
  250. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_ai_client.py +0 -0
  251. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_ai_intelligence.py +0 -0
  252. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_channel.py +0 -0
  253. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_chat_diff_decision.py +0 -0
  254. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_chat_runner_openai_compat.py +0 -0
  255. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_chat_stdin_protocol.py +0 -0
  256. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_cli.py +0 -0
  257. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_cli_workflows_history_drive.py +0 -0
  258. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_compliance.py +0 -0
  259. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_compressor.py +0 -0
  260. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_dispatch.py +0 -0
  261. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_e2e_nexus.py +0 -0
  262. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_endpoints_cli.py +0 -0
  263. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_endpoints_store.py +0 -0
  264. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_epistemic.py +0 -0
  265. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_fallback_chain.py +0 -0
  266. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_importer.py +0 -0
  267. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_integrations.py +0 -0
  268. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_integrations_codity.py +0 -0
  269. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_intelligence.py +0 -0
  270. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_issue_reporter.py +0 -0
  271. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_mcp_client.py +0 -0
  272. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_new_modules.py +0 -0
  273. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_nexus.py +0 -0
  274. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_permissions.py +0 -0
  275. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_phase1_4_new.py +0 -0
  276. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_phase34_completion.py +0 -0
  277. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_rate_limits.py +0 -0
  278. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_req_248_262.py +0 -0
  279. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_req_358_359.py +0 -0
  280. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_scaffolder.py +0 -0
  281. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_skill_marketplace.py +0 -0
  282. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_skills_mcp.py +0 -0
  283. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_smoke.py +0 -0
  284. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_suggester.py +0 -0
  285. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_tools.py +0 -0
  286. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_validator.py +0 -0
  287. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_vcs.py +0 -0
  288. {specsmith-0.12.0.dev494 → specsmith-0.12.0.dev496}/tests/test_warp_parity.py +0 -0
  289. {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.dev494
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.dev494"
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
- open_todos = sum(1 for line in lines if "- [ ]" in line)
446
- closed_todos = sum(1 for line in lines if "- [x]" in line)
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("--project-dir", type=click.Path(), default=".", show_default=True)
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
- "Additional project directories to register (comma-separated absolute paths). "
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
- governance_project_list List all registered projects
9070
- governance_audit Run governance audit
9071
- governance_checkpoint Emit GOVERNANCE ANCHOR JSON
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
- Single-project Warp config (Settings Agents MCP servers)::
9100
+ Then register each project once from inside that project::
9079
9101
 
9080
- {"specsmith-governance": {"command": "specsmith",
9081
- "args": ["mcp", "serve", "--project-dir", "/path/to/project"]}}
9102
+ specsmith mcp register
9082
9103
 
9083
9104
  \b
9084
- Multi-project Warp config::
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 full snippet)::
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(project_dir: str, project_dirs: str, as_json: bool) -> None:
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
- # Resolve the specsmith CLI executable: prefer the one on PATH, then
9123
- # fall back to deriving it from the current interpreter (dev installs).
9124
- specsmith_exe = shutil.which("specsmith") or str(
9125
- Path(sys.executable).parent / ("specsmith.exe" if sys.platform == "win32" else "specsmith")
9126
- )
9127
- default_dir = str(Path(project_dir).resolve())
9128
- args: list[str] = ["mcp", "serve", "--project-dir", default_dir]
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
- extra_dirs = [p.strip() for p in project_dirs.split(",") if p.strip()] if project_dirs else []
9131
- if extra_dirs:
9132
- args += ["--project-dirs", ",".join(extra_dirs)]
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": specsmith_exe,
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
- if extra_dirs:
9152
- console.print(
9153
- f"\n[dim]Serving {1 + len(extra_dirs)} projects. "
9154
- "Call [bold]governance_project_list[/bold] to see all registered paths,\n"
9155
- "then pass any path as [bold]project_dir[/bold] to target a specific project.[/dim]"
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
- "\n[dim]After adding, Warp/Oz can call governance_project_list, "
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
- ``project_dir`` becomes the default for tool calls that omit
627
- ``project_dir``. ``extra_project_dirs`` registers additional projects
628
- accessible via ``governance_project_list`` and addressable by any tool
629
- using their absolute path as ``project_dir``.
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
- The server no longer calls ``os.chdir()``; all paths are resolved
632
- absolutely, so the server can serve multiple projects from one process.
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
- # Register projects — always use absolute paths
637
- _DEFAULT_PROJECT_DIR = str(Path(project_dir).resolve())
638
- _REGISTERED_PROJECTS = [_DEFAULT_PROJECT_DIR]
639
- for extra in extra_project_dirs or []:
640
- abs_extra = str(Path(extra).resolve())
641
- if abs_extra not in _REGISTERED_PROJECTS:
642
- _REGISTERED_PROJECTS.append(abs_extra)
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__ = ["run_server", "SERVER_NAME", "SERVER_VERSION", "_TOOLS", "_HANDLERS"]
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.dev494
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