specsmith 0.11.5.dev452__tar.gz → 0.11.5.dev453__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 (285) hide show
  1. {specsmith-0.11.5.dev452/src/specsmith.egg-info → specsmith-0.11.5.dev453}/PKG-INFO +1 -1
  2. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/pyproject.toml +1 -1
  3. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/chat_runner.py +54 -9
  4. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/events.py +21 -1
  5. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/runner.py +227 -21
  6. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/cli.py +37 -0
  7. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453/src/specsmith.egg-info}/PKG-INFO +1 -1
  8. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith.egg-info/SOURCES.txt +1 -0
  9. specsmith-0.11.5.dev453/tests/test_agent_run_feedback.py +355 -0
  10. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/LICENSE +0 -0
  11. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/README.md +0 -0
  12. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/setup.cfg +0 -0
  13. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/epistemic/__init__.py +0 -0
  14. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/epistemic/belief.py +0 -0
  15. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/epistemic/certainty.py +0 -0
  16. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/epistemic/failure_graph.py +0 -0
  17. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/epistemic/py.typed +0 -0
  18. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/epistemic/recovery.py +0 -0
  19. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/epistemic/session.py +0 -0
  20. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/epistemic/stress_tester.py +0 -0
  21. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/epistemic/trace.py +0 -0
  22. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/__init__.py +0 -0
  23. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/__main__.py +0 -0
  24. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/__init__.py +0 -0
  25. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/broker.py +0 -0
  26. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/cleanup.py +0 -0
  27. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/context_seed.py +0 -0
  28. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/core.py +0 -0
  29. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/dispatch/__init__.py +0 -0
  30. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/dispatch/_status.py +0 -0
  31. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/dispatch/dag.py +0 -0
  32. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
  33. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/dispatch/events.py +0 -0
  34. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/dispatch/result.py +0 -0
  35. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/endpoints.py +0 -0
  36. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/execution_profiles.py +0 -0
  37. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/fallback.py +0 -0
  38. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/hf_leaderboard.py +0 -0
  39. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/hf_sync.py +0 -0
  40. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/indexer.py +0 -0
  41. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/llm_client.py +0 -0
  42. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/mcp.py +0 -0
  43. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/memory.py +0 -0
  44. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/model_intelligence.py +0 -0
  45. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/model_profiles.py +0 -0
  46. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/orchestrator.py +0 -0
  47. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/permissions.py +0 -0
  48. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/profiles.py +0 -0
  49. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/provider_registry.py +0 -0
  50. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/repl.py +0 -0
  51. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/router.py +0 -0
  52. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/rules.py +0 -0
  53. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/safety.py +0 -0
  54. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/spawner.py +0 -0
  55. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/suggester.py +0 -0
  56. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/teams.py +0 -0
  57. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/tools.py +0 -0
  58. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/verifier.py +0 -0
  59. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/agent/voice.py +0 -0
  60. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/architect.py +0 -0
  61. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/auditor.py +0 -0
  62. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/auth.py +0 -0
  63. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/block_export.py +0 -0
  64. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/channel.py +0 -0
  65. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/ci_manager.py +0 -0
  66. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/commands/__init__.py +0 -0
  67. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/commands/intelligence.py +0 -0
  68. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/compliance/__init__.py +0 -0
  69. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/compliance/_compat.py +0 -0
  70. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/compliance/checker.py +0 -0
  71. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/compliance/evidence.py +0 -0
  72. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/compliance/regulations.py +0 -0
  73. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/compliance/reporter.py +0 -0
  74. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/compressor.py +0 -0
  75. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/config.py +0 -0
  76. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/console_utils.py +0 -0
  77. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/context_orchestrator.py +0 -0
  78. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/context_window.py +0 -0
  79. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/credit_analyzer.py +0 -0
  80. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/credits.py +0 -0
  81. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/datasources/__init__.py +0 -0
  82. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/datasources/base.py +0 -0
  83. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/datasources/citations.py +0 -0
  84. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/datasources/fpd.py +0 -0
  85. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/datasources/odp.py +0 -0
  86. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/datasources/patentsview.py +0 -0
  87. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/datasources/pfw.py +0 -0
  88. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/datasources/ppubs.py +0 -0
  89. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/datasources/ptab.py +0 -0
  90. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/differ.py +0 -0
  91. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/doctor.py +0 -0
  92. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/drive.py +0 -0
  93. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/editor.py +0 -0
  94. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/epistemic/__init__.py +0 -0
  95. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/epistemic/belief.py +0 -0
  96. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/epistemic/certainty.py +0 -0
  97. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/epistemic/failure_graph.py +0 -0
  98. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/epistemic/recovery.py +0 -0
  99. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/epistemic/stress_tester.py +0 -0
  100. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/esdb/__init__.py +0 -0
  101. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/esdb/bridge.py +0 -0
  102. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/eval/__init__.py +0 -0
  103. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/eval/builtins.py +0 -0
  104. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/eval/runner.py +0 -0
  105. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/executor.py +0 -0
  106. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/exporter.py +0 -0
  107. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/governance_logic.py +0 -0
  108. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/governance_store.py +0 -0
  109. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/governance_yaml.py +0 -0
  110. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/__init__.py +0 -0
  111. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/app.py +0 -0
  112. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/main_window.py +0 -0
  113. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/session_tab.py +0 -0
  114. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/theme.py +0 -0
  115. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/widgets/__init__.py +0 -0
  116. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/widgets/chat_view.py +0 -0
  117. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/widgets/input_bar.py +0 -0
  118. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/widgets/provider_bar.py +0 -0
  119. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/widgets/token_meter.py +0 -0
  120. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/widgets/tool_panel.py +0 -0
  121. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/widgets/update_checker.py +0 -0
  122. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/gui/worker.py +0 -0
  123. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/history_search.py +0 -0
  124. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/importer.py +0 -0
  125. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/instinct.py +0 -0
  126. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/integrations/__init__.py +0 -0
  127. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/integrations/agent_skill.py +0 -0
  128. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/integrations/aider.py +0 -0
  129. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/integrations/base.py +0 -0
  130. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/integrations/claude_code.py +0 -0
  131. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/integrations/codity.py +0 -0
  132. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/integrations/copilot.py +0 -0
  133. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/integrations/cursor.py +0 -0
  134. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/integrations/gemini.py +0 -0
  135. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/integrations/windsurf.py +0 -0
  136. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/issue_reporter.py +0 -0
  137. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/languages.py +0 -0
  138. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/ledger.py +0 -0
  139. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/mcp_generator.py +0 -0
  140. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/migrations/__init__.py +0 -0
  141. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/migrations/m001_governance_yaml.py +0 -0
  142. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/migrations/m002_agents_slim.py +0 -0
  143. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/migrations/m003_compliance_init.py +0 -0
  144. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
  145. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
  146. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/migrations/m006_session_governance.py +0 -0
  147. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/migrations/runner.py +0 -0
  148. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/ollama_cmds.py +0 -0
  149. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/patent.py +0 -0
  150. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/paths.py +0 -0
  151. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/phase.py +0 -0
  152. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/plugins.py +0 -0
  153. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/profiles.py +0 -0
  154. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/rate_limits.py +0 -0
  155. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/releaser.py +0 -0
  156. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/requirements.py +0 -0
  157. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/requirements_parser.py +0 -0
  158. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/retrieval.py +0 -0
  159. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/safe_write.py +0 -0
  160. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/scaffolder.py +0 -0
  161. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/serve.py +0 -0
  162. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/session.py +0 -0
  163. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/session_init.py +0 -0
  164. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/session_store.py +0 -0
  165. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills/__init__.py +0 -0
  166. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills/cloud.py +0 -0
  167. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills/corporate.py +0 -0
  168. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills/cross_platform.py +0 -0
  169. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills/devops.py +0 -0
  170. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills/docs.py +0 -0
  171. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills/embedded.py +0 -0
  172. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills/governance.py +0 -0
  173. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills/hardware.py +0 -0
  174. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills/mobile.py +0 -0
  175. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills/productivity.py +0 -0
  176. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills/ssh.py +0 -0
  177. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/skills_builder.py +0 -0
  178. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/sync.py +0 -0
  179. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/agents.md.j2 +0 -0
  180. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
  181. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
  182. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/community/contributing.md.j2 +0 -0
  183. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
  184. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
  185. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/community/license-MIT.j2 +0 -0
  186. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
  187. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/community/security.md.j2 +0 -0
  188. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
  189. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
  190. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
  191. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
  192. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
  193. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/editorconfig.j2 +0 -0
  194. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/gitattributes.j2 +0 -0
  195. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/gitignore.j2 +0 -0
  196. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/go/go.mod.j2 +0 -0
  197. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/go/main.go.j2 +0 -0
  198. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
  199. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
  200. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
  201. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
  202. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
  203. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
  204. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/governance/roles.md.j2 +0 -0
  205. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/governance/rules.md.j2 +0 -0
  206. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
  207. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
  208. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/governance/verification.md.j2 +0 -0
  209. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/js/package.json.j2 +0 -0
  210. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/ledger.md.j2 +0 -0
  211. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/python/cli.py.j2 +0 -0
  212. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/python/init.py.j2 +0 -0
  213. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
  214. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/readme.md.j2 +0 -0
  215. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
  216. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/rust/main.rs.j2 +0 -0
  217. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
  218. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
  219. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
  220. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
  221. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
  222. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
  223. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
  224. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/tool_installer.py +0 -0
  225. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/toolrules.py +0 -0
  226. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/tools.py +0 -0
  227. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/trace.py +0 -0
  228. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/updater.py +0 -0
  229. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/upgrader.py +0 -0
  230. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/validator.py +0 -0
  231. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/vcs/__init__.py +0 -0
  232. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/vcs/base.py +0 -0
  233. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/vcs/bitbucket.py +0 -0
  234. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/vcs/github.py +0 -0
  235. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/vcs/gitlab.py +0 -0
  236. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/vcs_commands.py +0 -0
  237. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/wireframes.py +0 -0
  238. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith/workspace.py +0 -0
  239. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith.egg-info/dependency_links.txt +0 -0
  240. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith.egg-info/entry_points.txt +0 -0
  241. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith.egg-info/requires.txt +0 -0
  242. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/src/specsmith.egg-info/top_level.txt +0 -0
  243. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_CMD_001.py +0 -0
  244. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_agent_profiles.py +0 -0
  245. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_agent_runner_ready.py +0 -0
  246. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_ai_client.py +0 -0
  247. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_ai_intelligence.py +0 -0
  248. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_auditor.py +0 -0
  249. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_channel.py +0 -0
  250. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_chat_diff_decision.py +0 -0
  251. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_chat_runner_openai_compat.py +0 -0
  252. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_chat_stdin_protocol.py +0 -0
  253. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_cli.py +0 -0
  254. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_cli_workflows_history_drive.py +0 -0
  255. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_compliance.py +0 -0
  256. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_compressor.py +0 -0
  257. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_dispatch.py +0 -0
  258. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_e2e_nexus.py +0 -0
  259. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_endpoints_cli.py +0 -0
  260. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_endpoints_store.py +0 -0
  261. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_epistemic.py +0 -0
  262. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_fallback_chain.py +0 -0
  263. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_importer.py +0 -0
  264. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_integrations.py +0 -0
  265. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_integrations_codity.py +0 -0
  266. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_intelligence.py +0 -0
  267. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_issue_reporter.py +0 -0
  268. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_mcp_client.py +0 -0
  269. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_new_modules.py +0 -0
  270. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_nexus.py +0 -0
  271. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_permissions.py +0 -0
  272. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_phase1_4_new.py +0 -0
  273. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_phase34_completion.py +0 -0
  274. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_rate_limits.py +0 -0
  275. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_req_248_262.py +0 -0
  276. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_scaffolder.py +0 -0
  277. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_skill_marketplace.py +0 -0
  278. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_skills_mcp.py +0 -0
  279. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_smoke.py +0 -0
  280. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_suggester.py +0 -0
  281. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_tools.py +0 -0
  282. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_validator.py +0 -0
  283. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_vcs.py +0 -0
  284. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/tests/test_warp_parity.py +0 -0
  285. {specsmith-0.11.5.dev452 → specsmith-0.11.5.dev453}/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.11.5.dev452
3
+ Version: 0.11.5.dev453
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.11.5.dev452"
7
+ version = "0.11.5.dev453"
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"
@@ -34,6 +34,24 @@ from specsmith.agent.verifier import (
34
34
 
35
35
  DEFAULT_OLLAMA_HOST = "http://127.0.0.1:11434"
36
36
  DEFAULT_OLLAMA_MODEL = os.environ.get("SPECSMITH_OLLAMA_MODEL", "qwen2.5:7b")
37
+
38
+ # Ordered preference list used by _pick_ollama_model() when the default
39
+ # model is not installed. Lighter / faster models first so the REPL
40
+ # stays responsive on developer hardware.
41
+ _OLLAMA_MODEL_PREFERENCE = [
42
+ "qwen2.5:7b",
43
+ "qwen2.5-coder:7b-instruct",
44
+ "qwen3:8b",
45
+ "mistral:7b-instruct-q4_0",
46
+ "llama3:8b-instruct-q4_K_M",
47
+ "qwen2.5:14b",
48
+ "qwen2.5-coder:14b",
49
+ "qwen3:14b",
50
+ "mistral-nemo:12b",
51
+ "phi4:14b-q4_K_M",
52
+ "deepseek-r1:14b",
53
+ "qwen3:30b-a3b",
54
+ ]
37
55
  SYSTEM_PROMPT = (
38
56
  "You are Nexus, the local-first agentic developer assistant inside "
39
57
  "Specsmith. Always end your response with the canonical contract:\n"
@@ -239,6 +257,40 @@ def run_single_prompt(prompt: str, *, max_tokens: int = 500) -> str | None: # n
239
257
  # ---------------------------------------------------------------------------
240
258
 
241
259
 
260
+ def _ollama_alive(host: str) -> bool:
261
+ try:
262
+ with urlopen(f"{host}/api/tags", timeout=2): # noqa: S310
263
+ return True
264
+ except (URLError, TimeoutError, OSError):
265
+ return False
266
+
267
+
268
+ def _pick_ollama_model(host: str) -> str:
269
+ """Return the best available Ollama model for this machine.
270
+
271
+ Checks the installed model list from ``/api/tags`` and walks
272
+ ``_OLLAMA_MODEL_PREFERENCE`` to find the first match. Falls back
273
+ to ``DEFAULT_OLLAMA_MODEL`` when the API is unreachable or the list
274
+ is empty. An explicit ``SPECSMITH_OLLAMA_MODEL`` env var always wins.
275
+ """
276
+ env_override = os.environ.get("SPECSMITH_OLLAMA_MODEL", "").strip()
277
+ if env_override:
278
+ return env_override
279
+ try:
280
+ with urlopen(f"{host}/api/tags", timeout=2) as resp: # noqa: S310
281
+ data = json.loads(resp.read())
282
+ installed = {m["name"] for m in data.get("models", []) if m.get("name")}
283
+ for candidate in _OLLAMA_MODEL_PREFERENCE:
284
+ if candidate in installed:
285
+ return candidate
286
+ # None of the preferred models found — use whatever is first
287
+ if installed:
288
+ return sorted(installed)[0]
289
+ except Exception: # noqa: BLE001 — best-effort; fall through to default
290
+ pass
291
+ return DEFAULT_OLLAMA_MODEL
292
+
293
+
242
294
  def _run_ollama(
243
295
  messages: list[dict[str, str]],
244
296
  emitter: EventEmitter,
@@ -246,12 +298,13 @@ def _run_ollama(
246
298
  ) -> tuple[str | None, _UsageDelta]:
247
299
  """Stream from a local Ollama daemon using only stdlib."""
248
300
  host = os.environ.get("OLLAMA_HOST", DEFAULT_OLLAMA_HOST).rstrip("/")
249
- model = os.environ.get("SPECSMITH_OLLAMA_MODEL", DEFAULT_OLLAMA_MODEL)
250
301
  usage = _UsageDelta()
251
302
 
252
303
  if not _ollama_alive(host):
253
304
  return None, usage
254
305
 
306
+ model = _pick_ollama_model(host)
307
+
255
308
  payload = json.dumps({"model": model, "messages": messages, "stream": True}).encode("utf-8")
256
309
  req = Request( # noqa: S310 - URL is a hardcoded localhost default
257
310
  f"{host}/api/chat",
@@ -284,14 +337,6 @@ def _run_ollama(
284
337
  return ("".join(pieces) if pieces else None), usage
285
338
 
286
339
 
287
- def _ollama_alive(host: str) -> bool:
288
- try:
289
- with urlopen(f"{host}/api/tags", timeout=2): # noqa: S310
290
- return True
291
- except (URLError, TimeoutError, OSError):
292
- return False
293
-
294
-
295
340
  def _run_anthropic(
296
341
  messages: list[dict[str, str]],
297
342
  emitter: EventEmitter,
@@ -231,4 +231,24 @@ class EventEmitter:
231
231
  )
232
232
 
233
233
 
234
- __all__ = ["EventEmitter"]
234
+ class PlainTextEmitter(EventEmitter):
235
+ """Human-readable variant of EventEmitter for interactive terminal sessions.
236
+
237
+ Writes LLM token chunks directly to the stream (no JSON wrapping) so
238
+ ``specsmith run`` without ``--json-events`` produces readable output.
239
+ All non-token protocol events are silently dropped — system messages
240
+ are handled separately by ``AgentRunner._default_emit_event``.
241
+ """
242
+
243
+ def token(self, block_id: str, text: str) -> None: # noqa: ARG002
244
+ self.stream.write(text)
245
+ with contextlib.suppress(Exception):
246
+ self.stream.flush()
247
+
248
+ def emit(self, event: dict[str, Any]) -> None:
249
+ # Drop JSONL protocol frames in plain-text mode; token() handles
250
+ # the only output that matters for interactive sessions.
251
+ pass
252
+
253
+
254
+ __all__ = ["EventEmitter", "PlainTextEmitter"]
@@ -27,13 +27,160 @@ from pathlib import Path
27
27
  from typing import Any
28
28
 
29
29
  from specsmith.agent.core import AgentState, ModelTier
30
- from specsmith.agent.events import EventEmitter
30
+ from specsmith.agent.events import EventEmitter, PlainTextEmitter
31
31
 
32
32
  # These imports are kept lazy in the public API so that a busted optional
33
33
  # dependency (e.g. ``ag2``) doesn't keep the bridge from emitting ``ready``.
34
34
  # The import itself happens on the first call that actually needs the
35
35
  # orchestrator group chat.
36
- __all__ = ["AgentRunner", "_capabilities"]
36
+ __all__ = ["AgentRunner", "ProviderStatus", "check_providers", "_capabilities"]
37
+
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # Provider health check
41
+ # ---------------------------------------------------------------------------
42
+
43
+
44
+ from dataclasses import dataclass # noqa: E402 — after __all__
45
+
46
+
47
+ @dataclass
48
+ class ProviderStatus:
49
+ """Health snapshot for a single LLM provider."""
50
+
51
+ name: str
52
+ available: bool
53
+ model: str = "" # resolved model name (empty when unavailable)
54
+ note: str = "" # human-readable reason (error or extra context)
55
+ model_count: int = 0 # number of installed models (Ollama only)
56
+
57
+ @property
58
+ def icon(self) -> str:
59
+ return "\u2713" if self.available else "\u2717"
60
+
61
+
62
+ def check_providers() -> list[ProviderStatus]:
63
+ """Probe every supported LLM provider and return a status list.
64
+
65
+ Safe to call at any time — never raises, never blocks for more than
66
+ a couple of seconds. Used by ``_print_banner`` and ``specsmith run
67
+ --check``.
68
+ """
69
+ import importlib
70
+ import os
71
+
72
+ from specsmith.agent.chat_runner import (
73
+ _OLLAMA_MODEL_PREFERENCE,
74
+ DEFAULT_OLLAMA_HOST,
75
+ DEFAULT_OLLAMA_MODEL,
76
+ _ollama_alive,
77
+ )
78
+
79
+ results: list[ProviderStatus] = []
80
+
81
+ # ── Ollama ───────────────────────────────────────────────────────────
82
+ host = os.environ.get("OLLAMA_HOST", DEFAULT_OLLAMA_HOST).rstrip("/")
83
+ if _ollama_alive(host):
84
+ try:
85
+ import json
86
+ from urllib.request import urlopen
87
+
88
+ with urlopen(f"{host}/api/tags", timeout=2) as resp: # noqa: S310
89
+ data = json.loads(resp.read())
90
+ installed = [m["name"] for m in data.get("models", []) if m.get("name")]
91
+ installed_set = set(installed)
92
+ model = os.environ.get("SPECSMITH_OLLAMA_MODEL", "").strip()
93
+ source = "env"
94
+ if not model:
95
+ source = "auto"
96
+ for candidate in _OLLAMA_MODEL_PREFERENCE:
97
+ if candidate in installed_set:
98
+ model = candidate
99
+ break
100
+ if not model and installed:
101
+ model = sorted(installed_set)[0]
102
+ if not model:
103
+ model = DEFAULT_OLLAMA_MODEL
104
+ note = f"{source}, {len(installed)} installed"
105
+ results.append(
106
+ ProviderStatus(
107
+ name="ollama",
108
+ available=True,
109
+ model=model,
110
+ note=note,
111
+ model_count=len(installed),
112
+ )
113
+ )
114
+ except Exception as exc: # noqa: BLE001
115
+ results.append(
116
+ ProviderStatus(name="ollama", available=False, note=f"error reading tags: {exc}")
117
+ )
118
+ else:
119
+ results.append(
120
+ ProviderStatus(
121
+ name="ollama",
122
+ available=False,
123
+ note=f"not running at {host} — start with: ollama serve",
124
+ )
125
+ )
126
+
127
+ # ── Anthropic ────────────────────────────────────────────────────────
128
+ key = os.environ.get("ANTHROPIC_API_KEY", "")
129
+ if not key:
130
+ results.append(
131
+ ProviderStatus(name="anthropic", available=False, note="no ANTHROPIC_API_KEY")
132
+ )
133
+ elif importlib.util.find_spec("anthropic") is None:
134
+ results.append(
135
+ ProviderStatus(
136
+ name="anthropic",
137
+ available=False,
138
+ note="key set but SDK missing — run: pipx inject specsmith anthropic",
139
+ )
140
+ )
141
+ else:
142
+ model = os.environ.get("ANTHROPIC_MODEL", "claude-haiku-4-5")
143
+ results.append(
144
+ ProviderStatus(name="anthropic", available=True, model=model, note="key configured")
145
+ )
146
+
147
+ # ── OpenAI ───────────────────────────────────────────────────────────
148
+ key = os.environ.get("OPENAI_API_KEY", "")
149
+ if not key:
150
+ results.append(ProviderStatus(name="openai", available=False, note="no OPENAI_API_KEY"))
151
+ elif importlib.util.find_spec("openai") is None:
152
+ results.append(
153
+ ProviderStatus(
154
+ name="openai",
155
+ available=False,
156
+ note="key set but SDK missing — run: pipx inject specsmith openai",
157
+ )
158
+ )
159
+ else:
160
+ model = os.environ.get("OPENAI_MODEL", "gpt-4o-mini")
161
+ results.append(
162
+ ProviderStatus(name="openai", available=True, model=model, note="key configured")
163
+ )
164
+
165
+ # ── Gemini ───────────────────────────────────────────────────────────
166
+ key = os.environ.get("GOOGLE_API_KEY", "")
167
+ if not key:
168
+ results.append(ProviderStatus(name="gemini", available=False, note="no GOOGLE_API_KEY"))
169
+ elif importlib.util.find_spec("google.genai") is None:
170
+ results.append(
171
+ ProviderStatus(
172
+ name="gemini",
173
+ available=False,
174
+ note="key set but SDK missing — run: pipx inject specsmith google-genai",
175
+ )
176
+ )
177
+ else:
178
+ model = os.environ.get("GEMINI_MODEL", "gemini-2.5-flash")
179
+ results.append(
180
+ ProviderStatus(name="gemini", available=True, model=model, note="key configured")
181
+ )
182
+
183
+ return results
37
184
 
38
185
 
39
186
  # ---------------------------------------------------------------------------
@@ -156,7 +303,14 @@ class AgentRunner:
156
303
  self.endpoint_id = (endpoint_id or "").strip() or None
157
304
  self.profile_id = (profile_id or "").strip() or None
158
305
 
159
- self._emitter = emitter or EventEmitter(stream=sys.stdout)
306
+ # Use a plain-text emitter in interactive mode so LLM tokens render
307
+ # as readable prose instead of raw JSONL frames on the terminal.
308
+ if emitter is not None:
309
+ self._emitter = emitter
310
+ elif json_events:
311
+ self._emitter = EventEmitter(stream=sys.stdout)
312
+ else:
313
+ self._emitter = PlainTextEmitter(stream=sys.stdout)
160
314
  self._state = AgentState(
161
315
  provider_name=self.provider_name,
162
316
  model_name=self.model,
@@ -187,37 +341,58 @@ class AgentRunner:
187
341
  # ── Public lifecycle ───────────────────────────────────────────────
188
342
 
189
343
  def _print_banner(self) -> None:
190
- """Emit the ``ready`` handshake (or print a plain banner).
344
+ """Emit the ``ready`` handshake (or print a plain banner.
191
345
 
192
346
  Called exactly once at process start. The bridge waits up to 20 s
193
- for this frame; when ``json_events`` is False we still emit a
194
- terminal-friendly banner so interactive ``specsmith run`` users
195
- see the same boot text they used to.
347
+ for this frame; when ``json_events`` is False we print a human-
348
+ readable provider status table so the user knows exactly what will
349
+ respond to their commands before they type anything.
196
350
  """
197
351
  version = self._package_version()
198
352
  if self.json_events:
353
+ # When a model/provider was explicitly set (e.g. via CLI flag or test),
354
+ # use those. Only probe check_providers() when no model was pinned.
355
+ if self.model:
356
+ provider_for_ready = self.provider_name
357
+ model_for_ready = self.model
358
+ else:
359
+ statuses = check_providers()
360
+ active = next((s for s in statuses if s.available), None)
361
+ provider_for_ready = active.name if active else self.provider_name
362
+ model_for_ready = active.model if active else ""
199
363
  self._emitter.ready(
200
364
  agent="nexus",
201
365
  version=version,
202
366
  project_dir=self.project_dir,
203
- provider=self.provider_name,
204
- model=self.model,
367
+ provider=provider_for_ready,
368
+ model=model_for_ready,
205
369
  profile_id=self.profile_id or "",
206
370
  capabilities=_capabilities(),
207
371
  endpoint_id=self.endpoint_id or "",
208
372
  )
209
373
  else:
210
- print(
211
- f"Nexus {version} Local-first Agentic Development Environment "
212
- f"(Specsmith-governed)\n"
213
- f" project: {self.project_dir}\n"
214
- f" provider: {self.provider_name}\n"
215
- f" model: {self.model or '(default)'}\n"
216
- f" profile: {self.profile_id or '(default)'}\n"
217
- "Type plain English, or use slash commands "
218
- "(/plan, /ask, /fix, /test, /commit, /pr, /why, /exit).",
219
- flush=True,
220
- )
374
+ statuses = check_providers()
375
+ active_count = sum(1 for s in statuses if s.available)
376
+ lines = [
377
+ f"Nexus {version} — specsmith run",
378
+ f" project : {self.project_dir}",
379
+ f" profile : {self.profile_id or '(default)'}",
380
+ "",
381
+ " LLM providers:",
382
+ ]
383
+ for s in statuses:
384
+ if s.available:
385
+ lines.append(
386
+ f" {s.icon} {s.name:<10} ✓ ready model: {s.model} ({s.note})"
387
+ )
388
+ else:
389
+ lines.append(f" {s.icon} {s.name:<10} ✗ {s.note}")
390
+ lines.append("")
391
+ if active_count == 0:
392
+ lines.append(" ⚠ No provider available — commands will return no response.")
393
+ else:
394
+ lines.append(" Type plain English, or use /plan /ask /fix /test /commit /pr /exit")
395
+ print("\n".join(lines), flush=True)
221
396
 
222
397
  def run_interactive(self) -> None:
223
398
  """Read stdin lines and dispatch each to :meth:`_handle_command`."""
@@ -230,6 +405,10 @@ class AgentRunner:
230
405
  if line.strip().lower() in {"/exit", "/quit"}:
231
406
  break
232
407
  self._handle_command(line)
408
+ # Ensure streamed tokens are followed by a newline so the
409
+ # next input prompt doesn't bleed onto the response line.
410
+ if not self.json_events:
411
+ print(flush=True)
233
412
  if self.json_events:
234
413
  self._emit_event(type="turn_done")
235
414
  if self._hard_stop:
@@ -316,7 +495,12 @@ class AgentRunner:
316
495
 
317
496
  block_id = self._next_block_id()
318
497
  try:
319
- from specsmith.agent.chat_runner import run_chat
498
+ from specsmith.agent.chat_runner import (
499
+ DEFAULT_OLLAMA_HOST,
500
+ _ollama_alive,
501
+ _pick_ollama_model,
502
+ run_chat,
503
+ )
320
504
 
321
505
  result = run_chat(
322
506
  full_prompt,
@@ -336,6 +520,28 @@ class AgentRunner:
336
520
  )
337
521
  return None
338
522
 
523
+ if result is None:
524
+ # All providers failed — give the user an actionable explanation
525
+ # rather than silent emptiness.
526
+ import os as _os
527
+
528
+ host = _os.environ.get("OLLAMA_HOST", DEFAULT_OLLAMA_HOST).rstrip("/")
529
+ if _ollama_alive(host):
530
+ model = _pick_ollama_model(host)
531
+ hint = (
532
+ f"Ollama is running but returned no response "
533
+ f"(model: {model}). "
534
+ "Try: ollama run " + model
535
+ )
536
+ else:
537
+ hint = (
538
+ "No LLM provider available. Options:\n"
539
+ " • Start Ollama: ollama serve\n"
540
+ " • Set ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_API_KEY"
541
+ )
542
+ self._emit_event(type="system", message=hint)
543
+ return None
544
+
339
545
  # Aggregate metrics into the session state (C1).
340
546
  # ``run_chat`` now reports tokens_in / tokens_out / cost_usd off the
341
547
  # provider response (Ollama prompt_eval_count + eval_count, OpenAI
@@ -3661,6 +3661,13 @@ def abort_cmd(pid: int | None, abort_all_flag: bool, project_dir: str) -> None:
3661
3661
 
3662
3662
  @main.command(name="run")
3663
3663
  @click.option("--project-dir", type=click.Path(exists=True), default=".")
3664
+ @click.option(
3665
+ "--check",
3666
+ "check_only",
3667
+ is_flag=True,
3668
+ default=False,
3669
+ help="Check provider availability and exit (no REPL started).",
3670
+ )
3664
3671
  @click.option(
3665
3672
  "--task",
3666
3673
  "task",
@@ -3718,6 +3725,7 @@ def abort_cmd(pid: int | None, abort_all_flag: bool, project_dir: str) -> None:
3718
3725
  )
3719
3726
  def run_cmd(
3720
3727
  project_dir: str,
3728
+ check_only: bool,
3721
3729
  task: str,
3722
3730
  provider_name: str | None,
3723
3731
  model: str | None,
@@ -3737,10 +3745,39 @@ def run_cmd(
3737
3745
  Ollama running → local LLMs (zero config)
3738
3746
  SPECSMITH_PROVIDER=<name> → explicit override
3739
3747
 
3748
+ Use --check to validate provider configuration without starting a session.
3749
+
3740
3750
  Install a provider:
3741
3751
  pipx inject specsmith anthropic # Claude
3742
3752
  pipx inject specsmith openai # GPT/O-series
3743
3753
  """
3754
+ from specsmith.agent.runner import check_providers
3755
+
3756
+ if check_only:
3757
+ statuses = check_providers()
3758
+ any_ok = any(s.available for s in statuses)
3759
+ console.print("[bold]specsmith run --check[/bold]\n")
3760
+ for s in statuses:
3761
+ if s.available:
3762
+ console.print(
3763
+ f" [green]\u2713[/green] {s.name:<10} "
3764
+ f"model: [bold]{s.model}[/bold] ({s.note})"
3765
+ )
3766
+ else:
3767
+ console.print(f" [red]\u2717[/red] {s.name:<10} {s.note}")
3768
+ console.print()
3769
+ if any_ok:
3770
+ active = next(s for s in statuses if s.available)
3771
+ console.print(
3772
+ f"[bold green]Ready.[/bold green] Primary provider: {active.name} / {active.model}"
3773
+ )
3774
+ else:
3775
+ console.print(
3776
+ "[bold red]No provider available.[/bold red] Start Ollama or set an API key."
3777
+ )
3778
+ raise SystemExit(1)
3779
+ return
3780
+
3744
3781
  from specsmith.agent.core import ModelTier
3745
3782
  from specsmith.agent.runner import AgentRunner
3746
3783
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specsmith
3
- Version: 0.11.5.dev452
3
+ Version: 0.11.5.dev453
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
@@ -239,6 +239,7 @@ src/specsmith/vcs/github.py
239
239
  src/specsmith/vcs/gitlab.py
240
240
  tests/test_CMD_001.py
241
241
  tests/test_agent_profiles.py
242
+ tests/test_agent_run_feedback.py
242
243
  tests/test_agent_runner_ready.py
243
244
  tests/test_ai_client.py
244
245
  tests/test_ai_intelligence.py