specsmith 0.5.0.dev228__tar.gz → 0.6.0.dev230__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 (176) hide show
  1. {specsmith-0.5.0.dev228/src/specsmith.egg-info → specsmith-0.6.0.dev230}/PKG-INFO +1 -1
  2. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/pyproject.toml +1 -1
  3. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/cli.py +56 -8
  4. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230/src/specsmith.egg-info}/PKG-INFO +1 -1
  5. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith.egg-info/SOURCES.txt +1 -0
  6. specsmith-0.6.0.dev230/tests/test_phase34_completion.py +232 -0
  7. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/LICENSE +0 -0
  8. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/README.md +0 -0
  9. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/setup.cfg +0 -0
  10. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/epistemic/__init__.py +0 -0
  11. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/epistemic/belief.py +0 -0
  12. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/epistemic/certainty.py +0 -0
  13. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/epistemic/failure_graph.py +0 -0
  14. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/epistemic/py.typed +0 -0
  15. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/epistemic/recovery.py +0 -0
  16. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/epistemic/session.py +0 -0
  17. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/epistemic/stress_tester.py +0 -0
  18. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/epistemic/trace.py +0 -0
  19. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/__init__.py +0 -0
  20. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/__main__.py +0 -0
  21. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/__init__.py +0 -0
  22. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/broker.py +0 -0
  23. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/chat_runner.py +0 -0
  24. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/cleanup.py +0 -0
  25. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/events.py +0 -0
  26. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/indexer.py +0 -0
  27. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/mcp.py +0 -0
  28. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/memory.py +0 -0
  29. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/orchestrator.py +0 -0
  30. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/repl.py +0 -0
  31. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/router.py +0 -0
  32. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/rules.py +0 -0
  33. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/safety.py +0 -0
  34. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/tools.py +0 -0
  35. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/agent/verifier.py +0 -0
  36. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/architect.py +0 -0
  37. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/auditor.py +0 -0
  38. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/auth.py +0 -0
  39. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/commands/__init__.py +0 -0
  40. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/compressor.py +0 -0
  41. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/config.py +0 -0
  42. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/console_utils.py +0 -0
  43. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/credit_analyzer.py +0 -0
  44. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/credits.py +0 -0
  45. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/differ.py +0 -0
  46. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/doctor.py +0 -0
  47. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/epistemic/__init__.py +0 -0
  48. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/epistemic/belief.py +0 -0
  49. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/epistemic/certainty.py +0 -0
  50. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/epistemic/failure_graph.py +0 -0
  51. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/epistemic/recovery.py +0 -0
  52. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/epistemic/stress_tester.py +0 -0
  53. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/executor.py +0 -0
  54. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/exporter.py +0 -0
  55. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/__init__.py +0 -0
  56. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/app.py +0 -0
  57. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/main_window.py +0 -0
  58. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/session_tab.py +0 -0
  59. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/theme.py +0 -0
  60. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/widgets/__init__.py +0 -0
  61. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/widgets/chat_view.py +0 -0
  62. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/widgets/input_bar.py +0 -0
  63. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/widgets/provider_bar.py +0 -0
  64. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/widgets/token_meter.py +0 -0
  65. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/widgets/tool_panel.py +0 -0
  66. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/widgets/update_checker.py +0 -0
  67. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/gui/worker.py +0 -0
  68. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/importer.py +0 -0
  69. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/integrations/__init__.py +0 -0
  70. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/integrations/agent_skill.py +0 -0
  71. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/integrations/aider.py +0 -0
  72. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/integrations/base.py +0 -0
  73. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/integrations/claude_code.py +0 -0
  74. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/integrations/copilot.py +0 -0
  75. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/integrations/cursor.py +0 -0
  76. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/integrations/gemini.py +0 -0
  77. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/integrations/windsurf.py +0 -0
  78. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/languages.py +0 -0
  79. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/ledger.py +0 -0
  80. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/patent.py +0 -0
  81. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/phase.py +0 -0
  82. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/plugins.py +0 -0
  83. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/profiles.py +0 -0
  84. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/rate_limits.py +0 -0
  85. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/releaser.py +0 -0
  86. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/requirements.py +0 -0
  87. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/requirements_parser.py +0 -0
  88. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/retrieval.py +0 -0
  89. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/scaffolder.py +0 -0
  90. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/serve.py +0 -0
  91. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/session.py +0 -0
  92. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/skills.py +0 -0
  93. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/agents.md.j2 +0 -0
  94. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
  95. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
  96. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/community/contributing.md.j2 +0 -0
  97. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
  98. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
  99. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/community/license-MIT.j2 +0 -0
  100. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
  101. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/community/security.md.j2 +0 -0
  102. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
  103. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
  104. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
  105. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
  106. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
  107. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/editorconfig.j2 +0 -0
  108. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/gitattributes.j2 +0 -0
  109. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/gitignore.j2 +0 -0
  110. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/go/go.mod.j2 +0 -0
  111. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/go/main.go.j2 +0 -0
  112. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
  113. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
  114. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
  115. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
  116. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
  117. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
  118. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/governance/roles.md.j2 +0 -0
  119. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/governance/rules.md.j2 +0 -0
  120. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
  121. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
  122. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/governance/verification.md.j2 +0 -0
  123. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/js/package.json.j2 +0 -0
  124. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/ledger.md.j2 +0 -0
  125. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/python/cli.py.j2 +0 -0
  126. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/python/init.py.j2 +0 -0
  127. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
  128. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/readme.md.j2 +0 -0
  129. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
  130. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/rust/main.rs.j2 +0 -0
  131. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
  132. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
  133. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
  134. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
  135. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
  136. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
  137. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
  138. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/tool_installer.py +0 -0
  139. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/toolrules.py +0 -0
  140. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/tools.py +0 -0
  141. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/trace.py +0 -0
  142. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/updater.py +0 -0
  143. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/upgrader.py +0 -0
  144. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/validator.py +0 -0
  145. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/vcs/__init__.py +0 -0
  146. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/vcs/base.py +0 -0
  147. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/vcs/bitbucket.py +0 -0
  148. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/vcs/github.py +0 -0
  149. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/vcs/gitlab.py +0 -0
  150. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/vcs_commands.py +0 -0
  151. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/wireframes.py +0 -0
  152. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith/workspace.py +0 -0
  153. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith.egg-info/dependency_links.txt +0 -0
  154. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith.egg-info/entry_points.txt +0 -0
  155. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith.egg-info/requires.txt +0 -0
  156. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/src/specsmith.egg-info/top_level.txt +0 -0
  157. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_CMD_001.py +0 -0
  158. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_auditor.py +0 -0
  159. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_chat_diff_decision.py +0 -0
  160. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_chat_stdin_protocol.py +0 -0
  161. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_cli.py +0 -0
  162. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_cli_workflows_history_drive.py +0 -0
  163. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_compressor.py +0 -0
  164. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_e2e_nexus.py +0 -0
  165. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_epistemic.py +0 -0
  166. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_importer.py +0 -0
  167. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_integrations.py +0 -0
  168. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_nexus.py +0 -0
  169. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_phase1_4_new.py +0 -0
  170. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_rate_limits.py +0 -0
  171. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_scaffolder.py +0 -0
  172. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_skill_marketplace.py +0 -0
  173. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_smoke.py +0 -0
  174. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_tools.py +0 -0
  175. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_validator.py +0 -0
  176. {specsmith-0.5.0.dev228 → specsmith-0.6.0.dev230}/tests/test_vcs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specsmith
3
- Version: 0.5.0.dev228
3
+ Version: 0.6.0.dev230
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.5.0.dev228"
7
+ version = "0.6.0.dev230"
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"
@@ -5262,6 +5262,7 @@ def chat_cmd(
5262
5262
  import uuid as _uuid
5263
5263
 
5264
5264
  from specsmith.agent.events import EventEmitter
5265
+ from specsmith.agent.mcp import load_mcp_tools
5265
5266
  from specsmith.agent.memory import append_turn, recent_turns
5266
5267
  from specsmith.agent.router import choose_tier
5267
5268
  from specsmith.agent.rules import load_rules
@@ -5287,6 +5288,14 @@ def chat_cmd(
5287
5288
  if rules_prefix:
5288
5289
  emitter.token(msg_block, "[project rules loaded]\n")
5289
5290
 
5291
+ # Surface configured MCP servers (REQ-121). The actual invocation
5292
+ # path still flows through the safety middleware; here we just announce
5293
+ # availability so consumers can render the list.
5294
+ mcp_tools = load_mcp_tools(root)
5295
+ if mcp_tools:
5296
+ names = ", ".join(t.name for t in mcp_tools)
5297
+ emitter.token(msg_block, f"[mcp servers: {names}]\n")
5298
+
5290
5299
  # Pick a tier (REQ-122) so consumers know which model is in play.
5291
5300
  _utt_lower = utterance.lower()
5292
5301
  if any(k in _utt_lower for k in ("add", "fix", "refactor")):
@@ -5545,13 +5554,27 @@ def notebook_group() -> None:
5545
5554
  default="",
5546
5555
  help="Work item id whose .specsmith/runs/<WI>/ artifacts should be captured.",
5547
5556
  )
5548
- def notebook_record(slug: str, project_dir: str, work_item_id: str) -> None:
5557
+ @click.option(
5558
+ "--session-id",
5559
+ "session_id",
5560
+ default="",
5561
+ help="Session id whose .specsmith/sessions/<id>/turns.jsonl should be captured.",
5562
+ )
5563
+ def notebook_record(slug: str, project_dir: str, work_item_id: str, session_id: str) -> None:
5549
5564
  """Record a notebook for the given SLUG (REQ-123).
5550
5565
 
5551
- Reads `.specsmith/runs/<work_item_id>/` (logs.txt, decision.json, etc.)
5552
- and writes a self-contained Markdown notebook to
5553
- `docs/notebooks/<slug>.md`.
5566
+ Two artifact sources are supported and may be combined:
5567
+
5568
+ * ``--work-item-id`` reads `.specsmith/runs/<WI>/` (preflight/verify
5569
+ logs, decision.json, etc.).
5570
+ * ``--session-id`` reads `.specsmith/sessions/<id>/turns.jsonl` so a
5571
+ conversational chat session can be replayed later.
5572
+
5573
+ Either flag may be omitted; both may be combined to produce a single
5574
+ notebook that captures the full evidence trail.
5554
5575
  """
5576
+ import json as _json
5577
+
5555
5578
  root = Path(project_dir).resolve()
5556
5579
  nb_dir = root / "docs" / "notebooks"
5557
5580
  nb_dir.mkdir(parents=True, exist_ok=True)
@@ -5559,10 +5582,15 @@ def notebook_record(slug: str, project_dir: str, work_item_id: str) -> None:
5559
5582
 
5560
5583
  runs_dir = root / ".specsmith" / "runs"
5561
5584
  artifact_dir = runs_dir / work_item_id if work_item_id else None
5562
- sections: list[str] = [f"# Notebook {slug}\n"]
5585
+ sections: list[str] = [f"# Notebook \u2014 {slug}\n"]
5563
5586
  if work_item_id:
5564
5587
  sections.append(f"- **Work item**: `{work_item_id}`")
5588
+ if session_id:
5589
+ sections.append(f"- **Session**: `{session_id}`")
5590
+
5591
+ captured_any = False
5565
5592
  if artifact_dir and artifact_dir.is_dir():
5593
+ captured_any = True
5566
5594
  sections.append("\n## Captured artifacts\n")
5567
5595
  for path in sorted(artifact_dir.rglob("*")):
5568
5596
  if path.is_file():
@@ -5574,10 +5602,30 @@ def notebook_record(slug: str, project_dir: str, work_item_id: str) -> None:
5574
5602
  continue
5575
5603
  fence = "```"
5576
5604
  sections.append(f"### `{rel}`\n\n{fence}\n{body}\n{fence}\n")
5577
- else:
5605
+
5606
+ if session_id:
5607
+ turns_path = root / ".specsmith" / "sessions" / session_id / "turns.jsonl"
5608
+ if turns_path.is_file():
5609
+ captured_any = True
5610
+ sections.append("\n## Session turns\n")
5611
+ for line in turns_path.read_text(encoding="utf-8").splitlines():
5612
+ line = line.strip()
5613
+ if not line:
5614
+ continue
5615
+ try:
5616
+ turn = _json.loads(line)
5617
+ except ValueError:
5618
+ continue
5619
+ role = str(turn.get("role", "?"))
5620
+ utterance = str(turn.get("utterance") or turn.get("text") or "").strip()
5621
+ ts = str(turn.get("timestamp", "")).strip()
5622
+ header = f"### `{role}`" + (f" \u2014 {ts}" if ts else "")
5623
+ sections.append(f"{header}\n\n{utterance}\n")
5624
+
5625
+ if not captured_any:
5578
5626
  sections.append(
5579
- "\n_No `.specsmith/runs/<WI>/` directory found. "
5580
- "Run `specsmith preflight` and `specsmith verify` first to capture evidence._\n"
5627
+ "\n_No artifacts captured. Pass `--work-item-id <WI>` or "
5628
+ "`--session-id <id>` to populate this notebook._\n"
5581
5629
  )
5582
5630
  target.write_text("\n".join(sections), encoding="utf-8")
5583
5631
  console.print(f"[green]\u2713[/green] Notebook recorded at {target.relative_to(root)}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specsmith
3
- Version: 0.5.0.dev228
3
+ Version: 0.6.0.dev230
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
@@ -164,6 +164,7 @@ tests/test_importer.py
164
164
  tests/test_integrations.py
165
165
  tests/test_nexus.py
166
166
  tests/test_phase1_4_new.py
167
+ tests/test_phase34_completion.py
167
168
  tests/test_rate_limits.py
168
169
  tests/test_scaffolder.py
169
170
  tests/test_skill_marketplace.py
@@ -0,0 +1,232 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 BitConcepts, LLC. All rights reserved.
3
+ """Phase 3-4 completion tests: mcp loader, notebook + cloud commands.
4
+
5
+ Covers REQ-121 (MCP), REQ-123 (notebook), REQ-126 (cloud spawn), REQ-124
6
+ (perf smoke). The CLI commands are exercised through Click's CliRunner so
7
+ the tests stay fully hermetic — no real subprocess, no real PyPI hits.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ from pathlib import Path
14
+
15
+ import pytest
16
+ from click.testing import CliRunner
17
+
18
+ from specsmith.agent.mcp import (
19
+ MCPServerSpec,
20
+ MCPTool,
21
+ load_mcp_tools,
22
+ )
23
+ from specsmith.cli import main
24
+
25
+ # ── MCP loader (REQ-121 / TEST-121) ──────────────────────────────────────────
26
+
27
+
28
+ def test_load_mcp_tools_returns_empty_when_config_missing(tmp_path: Path) -> None:
29
+ """No `.specsmith/mcp.yml` => empty list, not an error."""
30
+ assert load_mcp_tools(tmp_path) == []
31
+
32
+
33
+ def test_load_mcp_tools_parses_one_entry(tmp_path: Path) -> None:
34
+ cfg_dir = tmp_path / ".specsmith"
35
+ cfg_dir.mkdir()
36
+ (cfg_dir / "mcp.yml").write_text(
37
+ "- name: filesystem\n"
38
+ " command: mcp-server-filesystem\n"
39
+ " args: ['--root', '.']\n"
40
+ " env:\n"
41
+ " HOME: /tmp\n",
42
+ encoding="utf-8",
43
+ )
44
+ tools = load_mcp_tools(tmp_path)
45
+ assert len(tools) == 1
46
+ tool = tools[0]
47
+ assert isinstance(tool, MCPTool)
48
+ assert tool.name == "filesystem"
49
+ assert tool.spec.command == "mcp-server-filesystem"
50
+ assert tool.spec.args == ["--root", "."]
51
+ assert tool.spec.env == {"HOME": "/tmp"}
52
+
53
+
54
+ def test_load_mcp_tools_skips_malformed_entries(tmp_path: Path) -> None:
55
+ """Entries missing name or command must be silently dropped."""
56
+ cfg_dir = tmp_path / ".specsmith"
57
+ cfg_dir.mkdir()
58
+ (cfg_dir / "mcp.yml").write_text(
59
+ "- name: ok\n command: mcp-real\n- command: nameless\n- name: commandless\n- not_a_dict\n",
60
+ encoding="utf-8",
61
+ )
62
+ tools = load_mcp_tools(tmp_path)
63
+ assert [t.name for t in tools] == ["ok"]
64
+
65
+
66
+ def test_load_mcp_tools_returns_empty_on_unparseable_yaml(tmp_path: Path) -> None:
67
+ cfg_dir = tmp_path / ".specsmith"
68
+ cfg_dir.mkdir()
69
+ (cfg_dir / "mcp.yml").write_text(": : :\n", encoding="utf-8")
70
+ assert load_mcp_tools(tmp_path) == []
71
+
72
+
73
+ def test_mcp_server_spec_round_trip() -> None:
74
+ spec = MCPServerSpec(name="x", command="y", args=["a"], env={"k": "v"})
75
+ assert spec.name == "x"
76
+ assert spec.args == ["a"]
77
+ assert spec.env == {"k": "v"}
78
+
79
+
80
+ # ── Notebook record / replay (REQ-123 / TEST-123) ────────────────────────────
81
+
82
+
83
+ def test_notebook_record_with_session_id_captures_turns(tmp_path: Path) -> None:
84
+ """`--session-id` materialises turns.jsonl as a `## Session turns` section."""
85
+ sid = "sess_abc"
86
+ sess_dir = tmp_path / ".specsmith" / "sessions" / sid
87
+ sess_dir.mkdir(parents=True)
88
+ turns = [
89
+ {"role": "user", "utterance": "add hello world", "timestamp": "2026-04-29T00:00:00Z"},
90
+ {"role": "agent", "utterance": "Plan: write a test", "timestamp": "2026-04-29T00:00:01Z"},
91
+ ]
92
+ (sess_dir / "turns.jsonl").write_text(
93
+ "\n".join(json.dumps(t) for t in turns) + "\n",
94
+ encoding="utf-8",
95
+ )
96
+
97
+ runner = CliRunner()
98
+ result = runner.invoke(
99
+ main,
100
+ [
101
+ "notebook",
102
+ "record",
103
+ "demo",
104
+ "--project-dir",
105
+ str(tmp_path),
106
+ "--session-id",
107
+ sid,
108
+ ],
109
+ env={"SPECSMITH_NO_AUTO_UPDATE": "1", "SPECSMITH_PYPI_CHECKED": "1"},
110
+ )
111
+ assert result.exit_code == 0, result.output
112
+
113
+ nb = (tmp_path / "docs" / "notebooks" / "demo.md").read_text(encoding="utf-8")
114
+ assert "# Notebook" in nb
115
+ assert sid in nb
116
+ assert "## Session turns" in nb
117
+ assert "add hello world" in nb
118
+ assert "Plan: write a test" in nb
119
+
120
+
121
+ def test_notebook_record_with_no_artifacts_writes_a_helpful_placeholder(
122
+ tmp_path: Path,
123
+ ) -> None:
124
+ runner = CliRunner()
125
+ result = runner.invoke(
126
+ main,
127
+ ["notebook", "record", "empty", "--project-dir", str(tmp_path)],
128
+ env={"SPECSMITH_NO_AUTO_UPDATE": "1", "SPECSMITH_PYPI_CHECKED": "1"},
129
+ )
130
+ assert result.exit_code == 0, result.output
131
+ nb = (tmp_path / "docs" / "notebooks" / "empty.md").read_text(encoding="utf-8")
132
+ assert "No artifacts captured" in nb
133
+
134
+
135
+ def test_notebook_replay_prints_recorded_notebook(tmp_path: Path) -> None:
136
+ nb_dir = tmp_path / "docs" / "notebooks"
137
+ nb_dir.mkdir(parents=True)
138
+ (nb_dir / "demo.md").write_text("# Notebook — demo\nhello\n", encoding="utf-8")
139
+
140
+ runner = CliRunner()
141
+ result = runner.invoke(
142
+ main,
143
+ ["notebook", "replay", "demo", "--project-dir", str(tmp_path)],
144
+ env={"SPECSMITH_NO_AUTO_UPDATE": "1", "SPECSMITH_PYPI_CHECKED": "1"},
145
+ )
146
+ assert result.exit_code == 0
147
+ assert "hello" in result.output
148
+
149
+
150
+ def test_notebook_replay_missing_slug_exits_non_zero(tmp_path: Path) -> None:
151
+ runner = CliRunner()
152
+ result = runner.invoke(
153
+ main,
154
+ ["notebook", "replay", "ghost", "--project-dir", str(tmp_path)],
155
+ env={"SPECSMITH_NO_AUTO_UPDATE": "1", "SPECSMITH_PYPI_CHECKED": "1"},
156
+ )
157
+ assert result.exit_code == 1
158
+ assert "No notebook" in result.output
159
+
160
+
161
+ # ── Cloud spawn (REQ-126 / TEST-126) ─────────────────────────────────────────
162
+
163
+
164
+ def test_cloud_spawn_dry_run_writes_manifest_without_posting(tmp_path: Path) -> None:
165
+ runner = CliRunner()
166
+ result = runner.invoke(
167
+ main,
168
+ [
169
+ "cloud",
170
+ "spawn",
171
+ "add hello world",
172
+ "--project-dir",
173
+ str(tmp_path),
174
+ "--dry-run",
175
+ ],
176
+ env={"SPECSMITH_NO_AUTO_UPDATE": "1", "SPECSMITH_PYPI_CHECKED": "1"},
177
+ )
178
+ assert result.exit_code == 0, result.output
179
+ cloud_root = tmp_path / ".specsmith" / "cloud"
180
+ assert cloud_root.is_dir()
181
+ runs = list(cloud_root.iterdir())
182
+ assert len(runs) == 1, "exactly one run directory should have been created"
183
+ manifest = json.loads((runs[0] / "manifest.json").read_text(encoding="utf-8"))
184
+ assert manifest["utterance"] == "add hello world"
185
+ assert manifest["dry_run"] is True
186
+ assert (runs[0] / "workspace.tar.gz").is_file()
187
+
188
+
189
+ def test_cloud_spawn_help_documents_endpoint(tmp_path: Path) -> None:
190
+ runner = CliRunner()
191
+ result = runner.invoke(main, ["cloud", "spawn", "--help"])
192
+ assert result.exit_code == 0
193
+ assert "--endpoint" in result.output
194
+ assert "--dry-run" in result.output
195
+
196
+
197
+ # ── Perf smoke (REQ-124 / TEST-124) ──────────────────────────────────────────
198
+
199
+
200
+ def test_perf_smoke_script_main_writes_baseline(
201
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
202
+ ) -> None:
203
+ """Run the perf-smoke script's main() with a tiny REQ count and a stubbed
204
+ `_time_preflight` so the test never spawns a real subprocess.
205
+
206
+ Verifies that ``baseline.json`` is written with the expected schema.
207
+ """
208
+ import importlib.util
209
+
210
+ perf_path = Path(__file__).resolve().parent.parent / "scripts" / "perf_smoke.py"
211
+ spec = importlib.util.spec_from_file_location("perf_smoke", perf_path)
212
+ assert spec is not None and spec.loader is not None
213
+ module = importlib.util.module_from_spec(spec)
214
+ spec.loader.exec_module(module)
215
+
216
+ # Replace the slow subprocess call with a deterministic stub.
217
+ monkeypatch.setattr(module, "_time_preflight", lambda *_a, **_k: 0.05)
218
+
219
+ monkeypatch.chdir(tmp_path)
220
+ monkeypatch.setattr(
221
+ "sys.argv", ["perf_smoke.py", "--reqs", "5", "--project-dir", str(tmp_path)]
222
+ )
223
+
224
+ rc = module.main()
225
+ assert rc == 0
226
+
227
+ baseline = json.loads(
228
+ (tmp_path / ".specsmith" / "perf" / "baseline.json").read_text(encoding="utf-8")
229
+ )
230
+ assert baseline["n_reqs"] == 5
231
+ assert len(baseline["timings_seconds"]) == 3
232
+ assert baseline["median_seconds"] == pytest.approx(0.05, abs=1e-3)