specsmith 0.15.3.dev572__tar.gz → 0.15.3.dev573__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 (303) hide show
  1. {specsmith-0.15.3.dev572/src/specsmith.egg-info → specsmith-0.15.3.dev573}/PKG-INFO +2 -2
  2. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/pyproject.toml +2 -2
  3. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/cli.py +137 -21
  4. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/sync.py +59 -0
  5. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573/src/specsmith.egg-info}/PKG-INFO +2 -2
  6. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith.egg-info/SOURCES.txt +1 -0
  7. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith.egg-info/requires.txt +1 -1
  8. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_auditor.py +18 -1
  9. specsmith-0.15.3.dev573/tests/test_cli_regressions.py +88 -0
  10. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_esdb_enforcement.py +50 -1
  11. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/LICENSE +0 -0
  12. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/README.md +0 -0
  13. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/setup.cfg +0 -0
  14. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/epistemic/__init__.py +0 -0
  15. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/epistemic/belief.py +0 -0
  16. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/epistemic/certainty.py +0 -0
  17. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/epistemic/failure_graph.py +0 -0
  18. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/epistemic/py.typed +0 -0
  19. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/epistemic/recovery.py +0 -0
  20. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/epistemic/session.py +0 -0
  21. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/epistemic/stress_tester.py +0 -0
  22. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/epistemic/trace.py +0 -0
  23. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/__init__.py +0 -0
  24. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/__main__.py +0 -0
  25. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/__init__.py +0 -0
  26. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/broker.py +0 -0
  27. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/chat_runner.py +0 -0
  28. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/cleanup.py +0 -0
  29. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/context_seed.py +0 -0
  30. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/core.py +0 -0
  31. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/dispatch/__init__.py +0 -0
  32. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/dispatch/_status.py +0 -0
  33. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/dispatch/dag.py +0 -0
  34. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
  35. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/dispatch/events.py +0 -0
  36. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/dispatch/result.py +0 -0
  37. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/endpoints.py +0 -0
  38. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/events.py +0 -0
  39. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/execution_profiles.py +0 -0
  40. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/fallback.py +0 -0
  41. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/hf_leaderboard.py +0 -0
  42. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/hf_sync.py +0 -0
  43. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/indexer.py +0 -0
  44. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/llm_client.py +0 -0
  45. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/mcp.py +0 -0
  46. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/memory.py +0 -0
  47. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/model_intelligence.py +0 -0
  48. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/model_profiles.py +0 -0
  49. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/orchestrator.py +0 -0
  50. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/permissions.py +0 -0
  51. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/profiles.py +0 -0
  52. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/provider_registry.py +0 -0
  53. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/repl.py +0 -0
  54. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/router.py +0 -0
  55. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/rules.py +0 -0
  56. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/runner.py +0 -0
  57. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/safety.py +0 -0
  58. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/spawner.py +0 -0
  59. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/suggester.py +0 -0
  60. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/teams.py +0 -0
  61. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/tools.py +0 -0
  62. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/verifier.py +0 -0
  63. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/agent/voice.py +0 -0
  64. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/architect.py +0 -0
  65. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/auditor.py +0 -0
  66. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/auth.py +0 -0
  67. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/block_export.py +0 -0
  68. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/channel.py +0 -0
  69. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/ci_manager.py +0 -0
  70. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/commands/__init__.py +0 -0
  71. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/commands/intelligence.py +0 -0
  72. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/compliance/__init__.py +0 -0
  73. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/compliance/_compat.py +0 -0
  74. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/compliance/checker.py +0 -0
  75. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/compliance/evidence.py +0 -0
  76. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/compliance/regulations.py +0 -0
  77. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/compliance/reporter.py +0 -0
  78. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/compressor.py +0 -0
  79. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/config.py +0 -0
  80. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/console_utils.py +0 -0
  81. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/context_orchestrator.py +0 -0
  82. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/context_window.py +0 -0
  83. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/credit_analyzer.py +0 -0
  84. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/credits.py +0 -0
  85. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/datasources/__init__.py +0 -0
  86. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/datasources/base.py +0 -0
  87. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/datasources/citations.py +0 -0
  88. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/datasources/fpd.py +0 -0
  89. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/datasources/odp.py +0 -0
  90. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/datasources/patentsview.py +0 -0
  91. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/datasources/pfw.py +0 -0
  92. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/datasources/ppubs.py +0 -0
  93. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/datasources/ptab.py +0 -0
  94. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/differ.py +0 -0
  95. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/doctor.py +0 -0
  96. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/drive.py +0 -0
  97. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/editor.py +0 -0
  98. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/epistemic/__init__.py +0 -0
  99. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/epistemic/belief.py +0 -0
  100. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/epistemic/certainty.py +0 -0
  101. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/epistemic/failure_graph.py +0 -0
  102. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/epistemic/recovery.py +0 -0
  103. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/epistemic/stress_tester.py +0 -0
  104. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/esdb/__init__.py +0 -0
  105. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/esdb/_license.py +0 -0
  106. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/esdb/bridge.py +0 -0
  107. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/esdb/sqlite_store.py +0 -0
  108. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/eval/__init__.py +0 -0
  109. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/eval/builtins.py +0 -0
  110. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/eval/runner.py +0 -0
  111. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/executor.py +0 -0
  112. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/exporter.py +0 -0
  113. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/governance_logic.py +0 -0
  114. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/governance_store.py +0 -0
  115. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/governance_yaml.py +0 -0
  116. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/__init__.py +0 -0
  117. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/app.py +0 -0
  118. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/main_window.py +0 -0
  119. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/session_tab.py +0 -0
  120. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/theme.py +0 -0
  121. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/widgets/__init__.py +0 -0
  122. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/widgets/chat_view.py +0 -0
  123. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/widgets/input_bar.py +0 -0
  124. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/widgets/provider_bar.py +0 -0
  125. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/widgets/token_meter.py +0 -0
  126. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/widgets/tool_panel.py +0 -0
  127. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/widgets/update_checker.py +0 -0
  128. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/gui/worker.py +0 -0
  129. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/history_search.py +0 -0
  130. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/importer.py +0 -0
  131. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/instinct.py +0 -0
  132. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/integrations/__init__.py +0 -0
  133. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/integrations/agent_skill.py +0 -0
  134. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/integrations/aider.py +0 -0
  135. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/integrations/base.py +0 -0
  136. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/integrations/claude_code.py +0 -0
  137. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/integrations/codity.py +0 -0
  138. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/integrations/copilot.py +0 -0
  139. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/integrations/cursor.py +0 -0
  140. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/integrations/gemini.py +0 -0
  141. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/integrations/windsurf.py +0 -0
  142. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/issue_reporter.py +0 -0
  143. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/languages.py +0 -0
  144. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/ledger.py +0 -0
  145. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/mcp_generator.py +0 -0
  146. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/mcp_server.py +0 -0
  147. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/migrations/__init__.py +0 -0
  148. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/migrations/m001_governance_yaml.py +0 -0
  149. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/migrations/m002_agents_slim.py +0 -0
  150. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/migrations/m003_compliance_init.py +0 -0
  151. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
  152. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
  153. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/migrations/m006_session_governance.py +0 -0
  154. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/migrations/runner.py +0 -0
  155. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/ollama_cmds.py +0 -0
  156. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/patent.py +0 -0
  157. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/paths.py +0 -0
  158. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/phase.py +0 -0
  159. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/plugins.py +0 -0
  160. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/profiles.py +0 -0
  161. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/rate_limits.py +0 -0
  162. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/releaser.py +0 -0
  163. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/requirements.py +0 -0
  164. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/requirements_parser.py +0 -0
  165. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/retrieval.py +0 -0
  166. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/safe_write.py +0 -0
  167. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/scaffolder.py +0 -0
  168. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/serve.py +0 -0
  169. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/session.py +0 -0
  170. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/session_init.py +0 -0
  171. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/session_store.py +0 -0
  172. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/__init__.py +0 -0
  173. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/ai_agents.py +0 -0
  174. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/cloud.py +0 -0
  175. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/corporate.py +0 -0
  176. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/cross_platform.py +0 -0
  177. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/data_engineering.py +0 -0
  178. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/devops.py +0 -0
  179. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/docs.py +0 -0
  180. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/embedded.py +0 -0
  181. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/governance.py +0 -0
  182. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/hardware.py +0 -0
  183. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/mobile.py +0 -0
  184. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/platform_engineering.py +0 -0
  185. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/productivity.py +0 -0
  186. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/software_engineering.py +0 -0
  187. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/specsmith_skills.py +0 -0
  188. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/ssh.py +0 -0
  189. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills/web_backend.py +0 -0
  190. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/skills_builder.py +0 -0
  191. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/agents.md.j2 +0 -0
  192. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
  193. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
  194. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/community/contributing.md.j2 +0 -0
  195. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
  196. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
  197. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/community/license-MIT.j2 +0 -0
  198. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
  199. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/community/security.md.j2 +0 -0
  200. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
  201. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
  202. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
  203. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
  204. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
  205. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/editorconfig.j2 +0 -0
  206. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/gitattributes.j2 +0 -0
  207. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/gitignore.j2 +0 -0
  208. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/go/go.mod.j2 +0 -0
  209. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/go/main.go.j2 +0 -0
  210. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
  211. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
  212. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
  213. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
  214. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
  215. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
  216. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/governance/roles.md.j2 +0 -0
  217. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/governance/rules.md.j2 +0 -0
  218. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
  219. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
  220. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/governance/verification.md.j2 +0 -0
  221. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/js/package.json.j2 +0 -0
  222. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/ledger.md.j2 +0 -0
  223. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/python/cli.py.j2 +0 -0
  224. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/python/init.py.j2 +0 -0
  225. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
  226. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/readme.md.j2 +0 -0
  227. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
  228. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/rust/main.rs.j2 +0 -0
  229. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
  230. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
  231. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
  232. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
  233. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
  234. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
  235. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
  236. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/tool_installer.py +0 -0
  237. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/toolrules.py +0 -0
  238. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/tools.py +0 -0
  239. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/trace.py +0 -0
  240. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/updater.py +0 -0
  241. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/upgrader.py +0 -0
  242. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/validator.py +0 -0
  243. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/vcs/__init__.py +0 -0
  244. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/vcs/base.py +0 -0
  245. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/vcs/bitbucket.py +0 -0
  246. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/vcs/github.py +0 -0
  247. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/vcs/gitlab.py +0 -0
  248. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/vcs_commands.py +0 -0
  249. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/wi_store.py +0 -0
  250. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/wireframes.py +0 -0
  251. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith/workspace.py +0 -0
  252. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith.egg-info/dependency_links.txt +0 -0
  253. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith.egg-info/entry_points.txt +0 -0
  254. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/src/specsmith.egg-info/top_level.txt +0 -0
  255. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_CMD_001.py +0 -0
  256. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_agent_profiles.py +0 -0
  257. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_agent_run_feedback.py +0 -0
  258. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_agent_runner_ready.py +0 -0
  259. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_ai_client.py +0 -0
  260. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_ai_intelligence.py +0 -0
  261. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_channel.py +0 -0
  262. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_chat_diff_decision.py +0 -0
  263. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_chat_runner_openai_compat.py +0 -0
  264. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_chat_stdin_protocol.py +0 -0
  265. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_cli.py +0 -0
  266. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_cli_workflows_history_drive.py +0 -0
  267. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_compliance.py +0 -0
  268. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_compliance_governance.py +0 -0
  269. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_compressor.py +0 -0
  270. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_dispatch.py +0 -0
  271. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_e2e_nexus.py +0 -0
  272. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_endpoints_cli.py +0 -0
  273. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_endpoints_store.py +0 -0
  274. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_epistemic.py +0 -0
  275. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_esdb_license.py +0 -0
  276. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_esdb_sqlite.py +0 -0
  277. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_fallback_chain.py +0 -0
  278. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_importer.py +0 -0
  279. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_integrations.py +0 -0
  280. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_integrations_codity.py +0 -0
  281. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_intelligence.py +0 -0
  282. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_issue_reporter.py +0 -0
  283. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_mcp_client.py +0 -0
  284. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_mcp_server.py +0 -0
  285. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_new_modules.py +0 -0
  286. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_nexus.py +0 -0
  287. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_permissions.py +0 -0
  288. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_phase1_4_new.py +0 -0
  289. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_phase34_completion.py +0 -0
  290. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_rate_limits.py +0 -0
  291. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_req_248_262.py +0 -0
  292. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_req_358_359.py +0 -0
  293. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_scaffolder.py +0 -0
  294. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_skill_marketplace.py +0 -0
  295. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_skills_mcp.py +0 -0
  296. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_smoke.py +0 -0
  297. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_suggester.py +0 -0
  298. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_tools.py +0 -0
  299. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_validator.py +0 -0
  300. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_vcs.py +0 -0
  301. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_warp_parity.py +0 -0
  302. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_warp_parity_followup.py +0 -0
  303. {specsmith-0.15.3.dev572 → specsmith-0.15.3.dev573}/tests/test_wi_lifecycle.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specsmith
3
- Version: 0.15.3.dev572
3
+ Version: 0.15.3.dev573
4
4
  Summary: AEE governance toolkit for AI-assisted development — session preflight gates, multi-agent dispatch, requirements↔test traceability, ESDB persistence, MCP server, and skills for Warp, Cursor, Claude Code, Copilot, Windsurf, and Aider.
5
5
  Author: Layer1Labs Silicon, Inc.
6
6
  License-Expression: MIT
@@ -40,7 +40,7 @@ Requires-Dist: pre-commit>=3.0; extra == "dev"
40
40
  Requires-Dist: types-pyyaml>=6.0; extra == "dev"
41
41
  Requires-Dist: cryptography>=42.0; extra == "dev"
42
42
  Provides-Extra: esdb
43
- Requires-Dist: chronomemory>=0.1.7; extra == "esdb"
43
+ Requires-Dist: chronomemory>=0.1.8; extra == "esdb"
44
44
  Requires-Dist: cryptography>=42.0; extra == "esdb"
45
45
  Provides-Extra: docs
46
46
  Requires-Dist: mkdocs>=1.6; extra == "docs"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "specsmith"
7
- version = "0.15.3.dev572"
7
+ version = "0.15.3.dev573"
8
8
  description = "AEE governance toolkit for AI-assisted development — session preflight gates, multi-agent dispatch, requirements↔test traceability, ESDB persistence, MCP server, and skills for Warp, Cursor, Claude Code, Copilot, Windsurf, and Aider."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -60,7 +60,7 @@ dev = [
60
60
  # Install: pip install specsmith[esdb]
61
61
  # Activate: specsmith esdb enable --key-file /path/to/your.esdb.key
62
62
  esdb = [
63
- "chronomemory>=0.1.7", # proprietary commercial — see chronomemory LICENSE
63
+ "chronomemory>=0.1.8", # proprietary commercial — see chronomemory LICENSE
64
64
  "cryptography>=42.0",
65
65
  ]
66
66
  docs = [
@@ -445,8 +445,17 @@ def _interactive_config(no_git: bool) -> ProjectConfig:
445
445
  def audit(fix: bool, project_dir: str) -> None:
446
446
  """Run drift and health checks (Section 23 + 26)."""
447
447
  from specsmith.auditor import run_audit
448
+ from specsmith.sync import auto_migrate_if_needed
448
449
 
449
450
  root = Path(project_dir).resolve()
451
+ auto_counts = auto_migrate_if_needed(root)
452
+ if auto_counts:
453
+ console.print(
454
+ " [cyan]⟳[/cyan] ESDB auto-migrate: "
455
+ f"{auto_counts.get('requirements', 0)} requirements + "
456
+ f"{auto_counts.get('testcases', 0)} testcases "
457
+ f"({auto_counts.get('skipped', 0)} skipped)"
458
+ )
450
459
  console.print(f"[bold]Auditing[/bold] {root}\n")
451
460
  report = run_audit(root)
452
461
 
@@ -1489,14 +1498,20 @@ def diff(project_dir: str, html_output: str | None) -> None:
1489
1498
  default=False,
1490
1499
  help="Run the new-user onboarding checklist (REQ-127).",
1491
1500
  )
1492
- def doctor(project_dir: str, onboarding: bool) -> None:
1501
+ @click.option(
1502
+ "--json",
1503
+ "as_json",
1504
+ is_flag=True,
1505
+ default=False,
1506
+ help="Emit machine-readable health report JSON.",
1507
+ )
1508
+ def doctor(project_dir: str, onboarding: bool, as_json: bool) -> None:
1493
1509
  """Check if verification tools are installed locally.
1494
1510
 
1495
1511
  With --onboarding, walks through a 6-step checklist for new users that
1496
1512
  confirms scaffold.yml, governance files, agent provider setup, optional
1497
1513
  Nexus broker availability, and prints next-step commands (REQ-127).
1498
1514
  """
1499
- from specsmith.doctor import run_doctor
1500
1515
 
1501
1516
  root = Path(project_dir).resolve()
1502
1517
 
@@ -1572,28 +1587,108 @@ def doctor(project_dir: str, onboarding: bool) -> None:
1572
1587
  )
1573
1588
  return
1574
1589
 
1575
- report = run_doctor(root)
1590
+ import importlib.metadata
1591
+ import json as _json
1592
+ import shutil
1593
+ import subprocess
1594
+ import sys
1595
+
1596
+ from specsmith.auditor import run_audit
1597
+ from specsmith.esdb import ESDB_BACKEND, open_default_store
1598
+ from specsmith.phase import read_phase
1576
1599
 
1577
- if not report.checks:
1578
- console.print("[yellow]No scaffold.yml found — cannot determine tools.[/yellow]")
1579
- return
1600
+ def _tool_version(cmd: str) -> tuple[bool, str]:
1601
+ exe = shutil.which(cmd)
1602
+ if not exe:
1603
+ return False, "not found"
1604
+ try:
1605
+ proc = subprocess.run(
1606
+ [exe, "--version"],
1607
+ capture_output=True,
1608
+ text=True,
1609
+ timeout=5,
1610
+ check=False,
1611
+ )
1612
+ out = (proc.stdout or proc.stderr or "").strip().splitlines()
1613
+ if out:
1614
+ return True, out[0]
1615
+ return True, "installed"
1616
+ except (OSError, subprocess.TimeoutExpired):
1617
+ return True, "installed"
1618
+
1619
+ checks: list[dict[str, str]] = []
1620
+
1621
+ def _add(name: str, passed: bool, detail: str, warn: bool = False) -> None:
1622
+ status = "warn" if warn else ("pass" if passed else "fail")
1623
+ checks.append({"name": name, "status": status, "detail": detail})
1624
+
1625
+ py_ok = sys.version_info >= (3, 10)
1626
+ _add(
1627
+ "python",
1628
+ py_ok,
1629
+ f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
1630
+ )
1580
1631
 
1581
- console.print(f"[bold]Doctor[/bold] checking {len(report.checks)} tools\n")
1582
- for check in report.checks:
1583
- if check.installed:
1584
- ver = f" ({check.version})" if check.version else ""
1585
- console.print(f" [green]✓[/green] {check.category}: {check.name}{ver}")
1586
- else:
1587
- console.print(f" [red]✗[/red] {check.category}: {check.name} — not found")
1632
+ git_ok, git_detail = _tool_version("git")
1633
+ _add("git", git_ok, git_detail)
1588
1634
 
1589
- console.print()
1590
- if report.missing_count == 0:
1591
- console.print(f"[bold green]All {report.installed_count} tools available.[/bold green]")
1635
+ _add("specsmith", True, __version__)
1636
+
1637
+ try:
1638
+ chrono_ver = importlib.metadata.version("chronomemory")
1639
+ _add("chronomemory", True, chrono_ver)
1640
+ except importlib.metadata.PackageNotFoundError:
1641
+ _add("chronomemory", False, "not installed")
1642
+
1643
+ esdb_open = False
1644
+ chain_ok = False
1645
+ record_count = 0
1646
+ try:
1647
+ with open_default_store(root, warn=False) as store: # type: ignore[attr-defined]
1648
+ esdb_open = True
1649
+ record_count = int(store.record_count())
1650
+ chain_ok = store.chain_valid() is not False
1651
+ except Exception as exc: # noqa: BLE001
1652
+ _add("esdb backend", False, f"open failed: {exc}")
1653
+ if esdb_open:
1654
+ _add("esdb backend", True, f"{ESDB_BACKEND} open")
1655
+ _add(
1656
+ "esdb chain",
1657
+ chain_ok,
1658
+ f"{'valid' if chain_ok else 'invalid'} ({record_count} records)",
1659
+ )
1660
+ _add("esdb record count", True, str(record_count))
1661
+
1662
+ audit_report = run_audit(root)
1663
+ _add(
1664
+ "audit",
1665
+ audit_report.healthy,
1666
+ "clean" if audit_report.healthy else f"{audit_report.failed} issue(s)",
1667
+ )
1668
+
1669
+ phase_key = read_phase(root)
1670
+ _add("phase", True, phase_key)
1671
+
1672
+ for tool in ("ruff", "pytest", "pipx"):
1673
+ ok, detail = _tool_version(tool)
1674
+ _add(tool, ok, detail)
1675
+
1676
+ overall = "pass" if all(c["status"] != "fail" for c in checks) else "fail"
1677
+
1678
+ if as_json:
1679
+ click.echo(_json.dumps({"checks": checks, "overall": overall}, indent=2))
1680
+ return
1681
+
1682
+ icon = {"pass": "✅", "fail": "❌", "warn": "⚠️"}
1683
+ console.print("specsmith doctor")
1684
+ console.print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
1685
+ for c in checks:
1686
+ console.print(f" {c['name']:<14} {icon[c['status']]} {c['detail']}")
1687
+ console.print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
1688
+ if overall == "pass":
1689
+ console.print("All checks passed.")
1592
1690
  else:
1593
- console.print(
1594
- f"[bold red]{report.missing_count} tool(s) missing.[/bold red] "
1595
- f"{report.installed_count} installed."
1596
- )
1691
+ console.print("One or more checks failed.")
1597
1692
 
1598
1693
 
1599
1694
  @main.command()
@@ -6019,10 +6114,11 @@ def sync_cmd(project_dir: str, check_only: bool, as_json: bool) -> None:
6019
6114
  """
6020
6115
  import json as _json
6021
6116
 
6022
- from specsmith.sync import run_sync
6117
+ from specsmith.sync import auto_migrate_if_needed, run_sync
6023
6118
 
6024
6119
  root = Path(project_dir).resolve()
6025
6120
  result = run_sync(root, dry_run=check_only)
6121
+ auto_counts = {} if check_only else auto_migrate_if_needed(root)
6026
6122
 
6027
6123
  if as_json:
6028
6124
  click.echo(
@@ -6036,6 +6132,8 @@ def sync_cmd(project_dir: str, check_only: bool, as_json: bool) -> None:
6036
6132
  "tests_changed": result.tests_changed,
6037
6133
  "in_sync": not result.changed,
6038
6134
  "dry_run": result.dry_run,
6135
+ "auto_migrated": bool(auto_counts),
6136
+ "auto_migrate_counts": auto_counts,
6039
6137
  },
6040
6138
  indent=2,
6041
6139
  )
@@ -6049,6 +6147,13 @@ def sync_cmd(project_dir: str, check_only: bool, as_json: bool) -> None:
6049
6147
  console.print(f"[green]\u2713[/green] {result.message}")
6050
6148
  else:
6051
6149
  console.print("[green]\u2713[/green] Machine state already in sync.")
6150
+ if auto_counts:
6151
+ console.print(
6152
+ " [cyan]⟳[/cyan] ESDB auto-migrate: "
6153
+ f"{auto_counts.get('requirements', 0)} requirements + "
6154
+ f"{auto_counts.get('testcases', 0)} testcases "
6155
+ f"({auto_counts.get('skipped', 0)} skipped)"
6156
+ )
6052
6157
 
6053
6158
  if check_only and result.changed:
6054
6159
  raise SystemExit(1)
@@ -10475,8 +10580,10 @@ def esdb_status_cmd(project_dir: str, as_json: bool) -> None:
10475
10580
 
10476
10581
  from specsmith.esdb import CHRONO_AVAILABLE, open_default_store
10477
10582
  from specsmith.esdb._license import check_license, resolve_license_path
10583
+ from specsmith.sync import auto_migrate_if_needed
10478
10584
 
10479
10585
  root = Path(project_dir).resolve()
10586
+ auto_counts = auto_migrate_if_needed(root)
10480
10587
 
10481
10588
  # Determine license status without side-effect warnings
10482
10589
  lic_path = resolve_license_path()
@@ -10542,6 +10649,8 @@ def esdb_status_cmd(project_dir: str, as_json: bool) -> None:
10542
10649
  "chain_valid": chain_ok,
10543
10650
  "counts": counts,
10544
10651
  "license": license_info,
10652
+ "auto_migrated": bool(auto_counts),
10653
+ "auto_migrate_counts": auto_counts,
10545
10654
  },
10546
10655
  indent=2,
10547
10656
  )
@@ -10572,6 +10681,13 @@ def esdb_status_cmd(project_dir: str, as_json: bool) -> None:
10572
10681
  console.print(
10573
10682
  " [dim]Use 'specsmith esdb enable --key-file <path>' to activate ChronoStore.[/dim]"
10574
10683
  )
10684
+ if auto_counts:
10685
+ console.print(
10686
+ " [cyan]⟳[/cyan] Auto-migrated from legacy JSON: "
10687
+ f"{auto_counts.get('requirements', 0)} requirements + "
10688
+ f"{auto_counts.get('testcases', 0)} testcases "
10689
+ f"({auto_counts.get('skipped', 0)} skipped)"
10690
+ )
10575
10691
 
10576
10692
 
10577
10693
  @esdb_group.command(name="enable")
@@ -553,6 +553,65 @@ def _sync_esdb(root: Path, state_dir: Path) -> None:
553
553
  pass
554
554
 
555
555
 
556
+ def _json_file_has_content(path: Path) -> bool:
557
+ """Return True when *path* exists and contains a non-empty JSON list.
558
+
559
+ Falls back to a small size heuristic for malformed JSON so we still treat
560
+ legacy state files with obvious content as migratable.
561
+ """
562
+ if not path.is_file():
563
+ return False
564
+ try:
565
+ payload = json.loads(path.read_text(encoding="utf-8"))
566
+ return isinstance(payload, list) and len(payload) > 0
567
+ except (OSError, ValueError):
568
+ with contextlib.suppress(OSError):
569
+ return path.stat().st_size > 2
570
+ return False
571
+
572
+
573
+ def _should_auto_migrate(store: Any, specsmith_dir: Path) -> bool:
574
+ """Return True when ESDB is empty and legacy JSON sources have content.
575
+
576
+ This is used by CLI surfaces (`sync`, `audit`, `esdb status`) to
577
+ opportunistically bootstrap ESDB from `.specsmith/*.json` exactly once.
578
+ """
579
+ if store.record_count() > 0:
580
+ return False
581
+ candidates = (
582
+ specsmith_dir / "requirements.json",
583
+ specsmith_dir / "testcases.json",
584
+ )
585
+ return any(_json_file_has_content(path) for path in candidates)
586
+
587
+
588
+ def auto_migrate_if_needed(root: Path) -> dict[str, int]:
589
+ """Best-effort ESDB bootstrap from legacy JSON when the store is empty.
590
+
591
+ Returns migration counts on success, or an empty dict when no migration was
592
+ needed or if any backend error occurred.
593
+ """
594
+ from specsmith.esdb import open_default_store
595
+
596
+ specsmith_dir = root / ".specsmith"
597
+ if not specsmith_dir.exists():
598
+ return {}
599
+ try:
600
+ with open_default_store(root, warn=False) as store: # type: ignore[attr-defined]
601
+ if not _should_auto_migrate(store, specsmith_dir):
602
+ return {}
603
+ counts = store.migrate_from_json(specsmith_dir)
604
+ if isinstance(counts, dict):
605
+ return {
606
+ "requirements": int(counts.get("requirements", 0)),
607
+ "testcases": int(counts.get("testcases", 0)),
608
+ "skipped": int(counts.get("skipped", 0)),
609
+ }
610
+ return {}
611
+ except Exception: # noqa: BLE001 - never block caller commands
612
+ return {}
613
+
614
+
556
615
  def check_sync(root: Path) -> SyncResult:
557
616
  """Check whether machine state is in sync without writing anything.
558
617
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specsmith
3
- Version: 0.15.3.dev572
3
+ Version: 0.15.3.dev573
4
4
  Summary: AEE governance toolkit for AI-assisted development — session preflight gates, multi-agent dispatch, requirements↔test traceability, ESDB persistence, MCP server, and skills for Warp, Cursor, Claude Code, Copilot, Windsurf, and Aider.
5
5
  Author: Layer1Labs Silicon, Inc.
6
6
  License-Expression: MIT
@@ -40,7 +40,7 @@ Requires-Dist: pre-commit>=3.0; extra == "dev"
40
40
  Requires-Dist: types-pyyaml>=6.0; extra == "dev"
41
41
  Requires-Dist: cryptography>=42.0; extra == "dev"
42
42
  Provides-Extra: esdb
43
- Requires-Dist: chronomemory>=0.1.7; extra == "esdb"
43
+ Requires-Dist: chronomemory>=0.1.8; extra == "esdb"
44
44
  Requires-Dist: cryptography>=42.0; extra == "esdb"
45
45
  Provides-Extra: docs
46
46
  Requires-Dist: mkdocs>=1.6; extra == "docs"
@@ -259,6 +259,7 @@ tests/test_chat_diff_decision.py
259
259
  tests/test_chat_runner_openai_compat.py
260
260
  tests/test_chat_stdin_protocol.py
261
261
  tests/test_cli.py
262
+ tests/test_cli_regressions.py
262
263
  tests/test_cli_workflows_history_drive.py
263
264
  tests/test_compliance.py
264
265
  tests/test_compliance_governance.py
@@ -36,7 +36,7 @@ mkdocs>=1.6
36
36
  mkdocs-material>=9.5
37
37
 
38
38
  [esdb]
39
- chronomemory>=0.1.7
39
+ chronomemory>=0.1.8
40
40
  cryptography>=42.0
41
41
 
42
42
  [gemini]
@@ -8,7 +8,7 @@ from pathlib import Path
8
8
 
9
9
  import pytest
10
10
 
11
- from specsmith.auditor import run_audit
11
+ from specsmith.auditor import check_industrial_artifacts, run_audit
12
12
 
13
13
 
14
14
  @pytest.fixture
@@ -89,6 +89,23 @@ class TestAuditDetectsIssues:
89
89
  assert "REQ-CLI-002" in coverage[0].message
90
90
 
91
91
 
92
+ def test_industrial_artifacts_normalizes_windows_declared_paths(tmp_path: Path) -> None:
93
+ (tmp_path / "scaffold.yml").write_text(
94
+ "name: test\n"
95
+ "type: cli-python\n"
96
+ "industrial_artifacts:\n"
97
+ " canopen_eds:\n"
98
+ " - path: hardware\\device.eds\n",
99
+ encoding="utf-8",
100
+ )
101
+ (tmp_path / "hardware").mkdir()
102
+ (tmp_path / "hardware" / "device.eds").write_text("EDS CONTENT", encoding="utf-8")
103
+
104
+ results = check_industrial_artifacts(tmp_path)
105
+ assert results
106
+ assert any(r.name == "industrial-artifacts" and r.passed for r in results)
107
+
108
+
92
109
  # ---------------------------------------------------------------------------
93
110
  # Regression: issue #183 — letter-suffix TEST IDs in req trace
94
111
  # ---------------------------------------------------------------------------
@@ -0,0 +1,88 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 Layer1Labs Silicon, Inc. All rights reserved.
3
+ """Targeted CLI regressions for #249, #254, and #212."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ from pathlib import Path
9
+
10
+ import yaml
11
+ from click.testing import CliRunner
12
+
13
+ from specsmith.cli import main
14
+
15
+
16
+ def _invoke(args: list[str]):
17
+ runner = CliRunner()
18
+ return runner.invoke(
19
+ main,
20
+ args,
21
+ env={
22
+ "SPECSMITH_NO_AUTO_UPDATE": "1",
23
+ "SPECSMITH_PYPI_CHECKED": "1",
24
+ "SPECSMITH_ALLOW_NON_PIPX": "1",
25
+ },
26
+ )
27
+
28
+
29
+ def test_esdb_enable_same_path_skips_copy2(monkeypatch, tmp_path: Path) -> None:
30
+ """#249: esdb enable must not call copy2 when src and dst resolve equal."""
31
+
32
+ class _Status:
33
+ valid = True
34
+ customer = "Test Customer"
35
+ expires_at = "2099-12-31T00:00:00Z"
36
+ reason = ""
37
+
38
+ home = tmp_path / "home"
39
+ key_dest = home / ".specsmith" / "esdb.key"
40
+ key_dest.parent.mkdir(parents=True)
41
+ key_dest.write_text("{}", encoding="utf-8")
42
+
43
+ monkeypatch.setattr("specsmith.esdb._license.verify_license_file", lambda _: _Status())
44
+ monkeypatch.setattr(Path, "home", classmethod(lambda cls: home))
45
+ monkeypatch.setattr(
46
+ "shutil.copy2", lambda *_a, **_k: (_ for _ in ()).throw(AssertionError("copy2"))
47
+ )
48
+
49
+ result = _invoke(["esdb", "enable", "--key-file", str(key_dest), "--json"])
50
+ assert result.exit_code == 0, result.output
51
+ payload = json.loads(result.output)
52
+ assert payload["ok"] is True
53
+
54
+
55
+ def test_phase_next_skips_suppressed_phases(tmp_path: Path) -> None:
56
+ """#254: phase next should skip phases listed in suppressed_phases."""
57
+ scaffold = {
58
+ "name": "phase-regression",
59
+ "type": "cli-python",
60
+ "language": "python",
61
+ "aee_phase": "requirements",
62
+ "suppressed_phases": ["test_spec"],
63
+ }
64
+ (tmp_path / "scaffold.yml").write_text(yaml.safe_dump(scaffold), encoding="utf-8")
65
+
66
+ result = _invoke(["phase", "next", "--force", "--project-dir", str(tmp_path)])
67
+ assert result.exit_code == 0, result.output
68
+
69
+ updated = yaml.safe_load((tmp_path / "scaffold.yml").read_text(encoding="utf-8"))
70
+ assert updated["aee_phase"] == "implementation"
71
+ assert "Skipped suppressed phase" in result.output
72
+
73
+
74
+ def test_doctor_json_outputs_overall_key(tmp_path: Path) -> None:
75
+ """#212: doctor --json should return valid JSON with an overall status key."""
76
+ result = _invoke(["doctor", "--project-dir", str(tmp_path), "--json"])
77
+ assert result.exit_code == 0, result.output
78
+ payload = json.loads(result.output)
79
+ assert "checks" in payload
80
+ assert "overall" in payload
81
+ assert payload["overall"] in ("pass", "fail")
82
+
83
+
84
+ def test_doctor_human_exits_zero(tmp_path: Path) -> None:
85
+ """#212: doctor human output should exit 0 in normal environments."""
86
+ result = _invoke(["doctor", "--project-dir", str(tmp_path)])
87
+ assert result.exit_code == 0, result.output
88
+ assert "specsmith doctor" in result.output
@@ -12,7 +12,7 @@ from click.testing import CliRunner
12
12
 
13
13
  from specsmith.cli import main
14
14
  from specsmith.esdb.sqlite_store import SqliteRecord, SqliteStore
15
- from specsmith.sync import run_sync
15
+ from specsmith.sync import _should_auto_migrate, auto_migrate_if_needed, run_sync
16
16
 
17
17
 
18
18
  def _run(args: list[str]): # type: ignore[return] # noqa: ANN201
@@ -79,3 +79,52 @@ def test_save_uses_sqlite_store_backup(tmp_path: Path, monkeypatch: pytest.Monke
79
79
  backup_path = Path(backup_step["path"])
80
80
  assert backup_path.is_file()
81
81
  assert backup_path.suffix == ".sqlite3"
82
+
83
+
84
+ def test_should_auto_migrate_true_when_store_empty(tmp_path: Path) -> None:
85
+ state = tmp_path / ".specsmith"
86
+ state.mkdir()
87
+ (state / "requirements.json").write_text(
88
+ json.dumps([{"id": "REQ-001", "title": "R"}]), encoding="utf-8"
89
+ )
90
+ (state / "testcases.json").write_text("[]", encoding="utf-8")
91
+
92
+ with SqliteStore(tmp_path) as store:
93
+ assert _should_auto_migrate(store, state) is True
94
+
95
+
96
+ def test_should_auto_migrate_false_when_store_non_empty(tmp_path: Path) -> None:
97
+ state = tmp_path / ".specsmith"
98
+ state.mkdir()
99
+ (state / "requirements.json").write_text(
100
+ json.dumps([{"id": "REQ-001", "title": "R"}]), encoding="utf-8"
101
+ )
102
+ (state / "testcases.json").write_text("[]", encoding="utf-8")
103
+
104
+ with SqliteStore(tmp_path) as store:
105
+ store.upsert(SqliteRecord(id="REQ-000", kind="requirement", label="already-there"))
106
+ assert _should_auto_migrate(store, state) is False
107
+
108
+
109
+ def test_auto_migrate_if_needed_populates_sqlite_from_legacy_json(
110
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
111
+ ) -> None:
112
+ monkeypatch.setenv("SPECSMITH_ESDB_BACKEND", "sqlite")
113
+ state = tmp_path / ".specsmith"
114
+ state.mkdir()
115
+ (state / "requirements.json").write_text(
116
+ json.dumps([{"id": "REQ-101", "title": "Auto migrated req"}]), encoding="utf-8"
117
+ )
118
+ (state / "testcases.json").write_text(
119
+ json.dumps(
120
+ [{"id": "TEST-101", "title": "Auto migrated test", "requirement_id": "REQ-101"}]
121
+ ),
122
+ encoding="utf-8",
123
+ )
124
+
125
+ counts = auto_migrate_if_needed(tmp_path)
126
+ assert counts.get("requirements") == 1
127
+ assert counts.get("testcases") == 1
128
+
129
+ with SqliteStore(tmp_path) as store:
130
+ assert store.record_count() >= 2