specsmith 0.6.0.dev231__tar.gz → 0.6.0.dev232__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 (179) hide show
  1. {specsmith-0.6.0.dev231/src/specsmith.egg-info → specsmith-0.6.0.dev232}/PKG-INFO +1 -1
  2. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/pyproject.toml +1 -1
  3. specsmith-0.6.0.dev232/src/specsmith/agent/suggester.py +264 -0
  4. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/cli.py +30 -0
  5. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232/src/specsmith.egg-info}/PKG-INFO +1 -1
  6. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith.egg-info/SOURCES.txt +2 -0
  7. specsmith-0.6.0.dev232/tests/test_suggester.py +88 -0
  8. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/LICENSE +0 -0
  9. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/README.md +0 -0
  10. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/setup.cfg +0 -0
  11. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/epistemic/__init__.py +0 -0
  12. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/epistemic/belief.py +0 -0
  13. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/epistemic/certainty.py +0 -0
  14. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/epistemic/failure_graph.py +0 -0
  15. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/epistemic/py.typed +0 -0
  16. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/epistemic/recovery.py +0 -0
  17. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/epistemic/session.py +0 -0
  18. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/epistemic/stress_tester.py +0 -0
  19. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/epistemic/trace.py +0 -0
  20. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/__init__.py +0 -0
  21. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/__main__.py +0 -0
  22. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/__init__.py +0 -0
  23. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/broker.py +0 -0
  24. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/chat_runner.py +0 -0
  25. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/cleanup.py +0 -0
  26. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/events.py +0 -0
  27. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/indexer.py +0 -0
  28. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/mcp.py +0 -0
  29. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/memory.py +0 -0
  30. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/orchestrator.py +0 -0
  31. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/repl.py +0 -0
  32. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/router.py +0 -0
  33. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/rules.py +0 -0
  34. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/safety.py +0 -0
  35. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/tools.py +0 -0
  36. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/agent/verifier.py +0 -0
  37. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/architect.py +0 -0
  38. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/auditor.py +0 -0
  39. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/auth.py +0 -0
  40. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/commands/__init__.py +0 -0
  41. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/compressor.py +0 -0
  42. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/config.py +0 -0
  43. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/console_utils.py +0 -0
  44. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/credit_analyzer.py +0 -0
  45. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/credits.py +0 -0
  46. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/differ.py +0 -0
  47. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/doctor.py +0 -0
  48. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/epistemic/__init__.py +0 -0
  49. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/epistemic/belief.py +0 -0
  50. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/epistemic/certainty.py +0 -0
  51. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/epistemic/failure_graph.py +0 -0
  52. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/epistemic/recovery.py +0 -0
  53. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/epistemic/stress_tester.py +0 -0
  54. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/executor.py +0 -0
  55. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/exporter.py +0 -0
  56. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/__init__.py +0 -0
  57. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/app.py +0 -0
  58. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/main_window.py +0 -0
  59. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/session_tab.py +0 -0
  60. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/theme.py +0 -0
  61. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/widgets/__init__.py +0 -0
  62. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/widgets/chat_view.py +0 -0
  63. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/widgets/input_bar.py +0 -0
  64. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/widgets/provider_bar.py +0 -0
  65. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/widgets/token_meter.py +0 -0
  66. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/widgets/tool_panel.py +0 -0
  67. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/widgets/update_checker.py +0 -0
  68. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/gui/worker.py +0 -0
  69. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/importer.py +0 -0
  70. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/integrations/__init__.py +0 -0
  71. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/integrations/agent_skill.py +0 -0
  72. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/integrations/aider.py +0 -0
  73. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/integrations/base.py +0 -0
  74. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/integrations/claude_code.py +0 -0
  75. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/integrations/copilot.py +0 -0
  76. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/integrations/cursor.py +0 -0
  77. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/integrations/gemini.py +0 -0
  78. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/integrations/windsurf.py +0 -0
  79. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/languages.py +0 -0
  80. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/ledger.py +0 -0
  81. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/patent.py +0 -0
  82. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/phase.py +0 -0
  83. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/plugins.py +0 -0
  84. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/profiles.py +0 -0
  85. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/rate_limits.py +0 -0
  86. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/releaser.py +0 -0
  87. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/requirements.py +0 -0
  88. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/requirements_parser.py +0 -0
  89. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/retrieval.py +0 -0
  90. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/scaffolder.py +0 -0
  91. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/serve.py +0 -0
  92. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/session.py +0 -0
  93. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/skills.py +0 -0
  94. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/agents.md.j2 +0 -0
  95. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
  96. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
  97. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/community/contributing.md.j2 +0 -0
  98. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
  99. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
  100. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/community/license-MIT.j2 +0 -0
  101. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
  102. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/community/security.md.j2 +0 -0
  103. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
  104. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
  105. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
  106. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
  107. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
  108. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/editorconfig.j2 +0 -0
  109. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/gitattributes.j2 +0 -0
  110. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/gitignore.j2 +0 -0
  111. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/go/go.mod.j2 +0 -0
  112. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/go/main.go.j2 +0 -0
  113. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
  114. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
  115. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
  116. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
  117. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
  118. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
  119. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/governance/roles.md.j2 +0 -0
  120. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/governance/rules.md.j2 +0 -0
  121. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
  122. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
  123. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/governance/verification.md.j2 +0 -0
  124. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/js/package.json.j2 +0 -0
  125. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/ledger.md.j2 +0 -0
  126. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/python/cli.py.j2 +0 -0
  127. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/python/init.py.j2 +0 -0
  128. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
  129. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/readme.md.j2 +0 -0
  130. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
  131. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/rust/main.rs.j2 +0 -0
  132. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
  133. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
  134. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
  135. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
  136. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
  137. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
  138. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
  139. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/tool_installer.py +0 -0
  140. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/toolrules.py +0 -0
  141. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/tools.py +0 -0
  142. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/trace.py +0 -0
  143. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/updater.py +0 -0
  144. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/upgrader.py +0 -0
  145. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/validator.py +0 -0
  146. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/vcs/__init__.py +0 -0
  147. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/vcs/base.py +0 -0
  148. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/vcs/bitbucket.py +0 -0
  149. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/vcs/github.py +0 -0
  150. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/vcs/gitlab.py +0 -0
  151. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/vcs_commands.py +0 -0
  152. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/wireframes.py +0 -0
  153. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith/workspace.py +0 -0
  154. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith.egg-info/dependency_links.txt +0 -0
  155. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith.egg-info/entry_points.txt +0 -0
  156. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith.egg-info/requires.txt +0 -0
  157. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/src/specsmith.egg-info/top_level.txt +0 -0
  158. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_CMD_001.py +0 -0
  159. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_auditor.py +0 -0
  160. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_chat_diff_decision.py +0 -0
  161. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_chat_stdin_protocol.py +0 -0
  162. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_cli.py +0 -0
  163. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_cli_workflows_history_drive.py +0 -0
  164. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_compressor.py +0 -0
  165. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_e2e_nexus.py +0 -0
  166. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_epistemic.py +0 -0
  167. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_importer.py +0 -0
  168. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_integrations.py +0 -0
  169. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_mcp_client.py +0 -0
  170. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_nexus.py +0 -0
  171. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_phase1_4_new.py +0 -0
  172. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_phase34_completion.py +0 -0
  173. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_rate_limits.py +0 -0
  174. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_scaffolder.py +0 -0
  175. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_skill_marketplace.py +0 -0
  176. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_smoke.py +0 -0
  177. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_tools.py +0 -0
  178. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_validator.py +0 -0
  179. {specsmith-0.6.0.dev231 → specsmith-0.6.0.dev232}/tests/test_vcs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specsmith
3
- Version: 0.6.0.dev231
3
+ Version: 0.6.0.dev232
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.6.0.dev231"
7
+ version = "0.6.0.dev232"
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"
@@ -0,0 +1,264 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 BitConcepts, LLC. All rights reserved.
3
+ """Lightweight NL-to-command suggester for `specsmith suggest-command` (REQ-131).
4
+
5
+ Given a partial natural-language fragment, return a structured suggestion
6
+ that the VS Code extension renders as inline ghost-text in the chat input.
7
+ Three classification buckets:
8
+
9
+ * ``command`` -- the input is shell-y (starts with an imperative verb that
10
+ maps to a known CLI). Suggest a concrete shell command.
11
+ * ``utterance`` -- the input is plain English meant for the agent. Suggest
12
+ a refined utterance that names a likely component (best-effort).
13
+ * ``passthrough`` -- input is too short or ambiguous; echo it back so the
14
+ ghost-text matches what the user typed (no-op suggestion).
15
+
16
+ The suggester is **deterministic and LLM-free**. The IDE may layer an LLM
17
+ predictor on top, but the CLI baseline must always succeed quickly. If the
18
+ extension wants a richer suggestion, it can call `specsmith preflight
19
+ --predict-only` separately for utterances.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import re
25
+ from dataclasses import dataclass, field
26
+ from pathlib import Path
27
+ from typing import Any
28
+
29
+ # Common imperative verbs that map to shell-y intents.
30
+ _SHELL_VERBS = {
31
+ "run",
32
+ "exec",
33
+ "execute",
34
+ "kill",
35
+ "stop",
36
+ "start",
37
+ "restart",
38
+ "build",
39
+ "test",
40
+ "lint",
41
+ "format",
42
+ "git",
43
+ "cd",
44
+ "ls",
45
+ "cat",
46
+ "rm",
47
+ "mv",
48
+ "cp",
49
+ "find",
50
+ "grep",
51
+ "ps",
52
+ "top",
53
+ "open",
54
+ "edit",
55
+ "tail",
56
+ "head",
57
+ "make",
58
+ "npm",
59
+ "pnpm",
60
+ "yarn",
61
+ "pip",
62
+ "pipx",
63
+ "uv",
64
+ "pytest",
65
+ "ruff",
66
+ "mypy",
67
+ "cargo",
68
+ "go",
69
+ "docker",
70
+ "kubectl",
71
+ "terraform",
72
+ }
73
+
74
+ # Map verb -> default refined command.
75
+ _VERB_TEMPLATES: dict[str, str] = {
76
+ "run tests": "pytest -q",
77
+ "run lint": "ruff check .",
78
+ "run mypy": "mypy src/",
79
+ "format": "ruff format .",
80
+ "lint": "ruff check .",
81
+ "test": "pytest -q",
82
+ "build": "python -m build",
83
+ "git status": "git --no-pager status",
84
+ "git log": "git --no-pager log --oneline -20",
85
+ "git diff": "git --no-pager diff",
86
+ }
87
+
88
+
89
+ @dataclass
90
+ class CommandSuggestion:
91
+ """Output payload of :func:`suggest_command`."""
92
+
93
+ kind: str # "command" | "utterance" | "passthrough"
94
+ suggestion: str
95
+ confidence: float = 0.5
96
+ reasoning: str = ""
97
+ candidates: list[str] = field(default_factory=list)
98
+
99
+ def to_dict(self) -> dict[str, Any]:
100
+ return {
101
+ "kind": self.kind,
102
+ "suggestion": self.suggestion,
103
+ "confidence": round(self.confidence, 3),
104
+ "reasoning": self.reasoning,
105
+ "candidates": list(self.candidates),
106
+ }
107
+
108
+
109
+ def classify(text: str) -> str:
110
+ """Return ``command``, ``utterance``, or ``passthrough``."""
111
+ stripped = text.strip()
112
+ if len(stripped) < 2:
113
+ return "passthrough"
114
+ first = stripped.split()[0].lower()
115
+ if first in _SHELL_VERBS:
116
+ return "command"
117
+ return "utterance"
118
+
119
+
120
+ def suggest_command(text: str, *, project_dir: Path | None = None) -> CommandSuggestion:
121
+ """Return a structured suggestion for ``text``.
122
+
123
+ The suggester is deterministic. It looks for verb prefixes and a short
124
+ catalogue of common templates; if nothing matches, it returns the input
125
+ unchanged with kind=``passthrough``.
126
+ """
127
+ stripped = text.strip()
128
+ kind = classify(stripped)
129
+ if kind == "passthrough":
130
+ return CommandSuggestion(
131
+ kind="passthrough",
132
+ suggestion=text,
133
+ confidence=0.0,
134
+ reasoning="input too short to suggest",
135
+ )
136
+ if kind == "utterance":
137
+ return _suggest_utterance(stripped, project_dir=project_dir)
138
+ return _suggest_shell(stripped)
139
+
140
+
141
+ def _suggest_shell(text: str) -> CommandSuggestion:
142
+ lower = text.lower()
143
+ # Direct multi-word template match (e.g. "run tests").
144
+ for phrase, command in _VERB_TEMPLATES.items():
145
+ if lower.startswith(phrase):
146
+ return CommandSuggestion(
147
+ kind="command",
148
+ suggestion=command,
149
+ confidence=0.85,
150
+ reasoning=f"matched template '{phrase}'",
151
+ )
152
+ # Single-verb fallback: if the user typed "git" alone, propose
153
+ # `git status`. If "test", propose pytest -q.
154
+ first = lower.split()[0]
155
+ fallback = {
156
+ "git": "git --no-pager status",
157
+ "ls": "ls -la",
158
+ "test": "pytest -q",
159
+ "lint": "ruff check .",
160
+ "format": "ruff format .",
161
+ "build": "python -m build",
162
+ "find": "find . -name '*.py'",
163
+ }.get(first)
164
+ if fallback and lower.strip() == first:
165
+ return CommandSuggestion(
166
+ kind="command",
167
+ suggestion=fallback,
168
+ confidence=0.7,
169
+ reasoning=f"single verb '{first}' resolved to default command",
170
+ )
171
+ # Pass through what the user typed; mark as command anyway so the IDE
172
+ # knows it's shell-y rather than NL.
173
+ return CommandSuggestion(
174
+ kind="command",
175
+ suggestion=text,
176
+ confidence=0.3,
177
+ reasoning="recognised as shell command but no template applied",
178
+ )
179
+
180
+
181
+ _REQ_REGEX = re.compile(r"REQ-[A-Z0-9-]+", re.IGNORECASE)
182
+ _KNOWN_VERBS = ("add", "fix", "refactor", "remove", "rename", "document", "test")
183
+
184
+
185
+ def _suggest_utterance(text: str, *, project_dir: Path | None) -> CommandSuggestion:
186
+ lower = text.lower()
187
+ candidates: list[str] = []
188
+
189
+ # If the text already names a REQ, surface it verbatim with a higher
190
+ # confidence — the user is already specific.
191
+ matched = _REQ_REGEX.findall(text)
192
+ if matched:
193
+ return CommandSuggestion(
194
+ kind="utterance",
195
+ suggestion=text,
196
+ confidence=0.9,
197
+ reasoning=f"references {matched[0]} explicitly",
198
+ candidates=matched,
199
+ )
200
+
201
+ # If the text starts with a change verb but doesn't name a component,
202
+ # suggest a refined version that asks the user to add a target.
203
+ first = lower.split()[0] if lower.split() else ""
204
+ if first in _KNOWN_VERBS and len(lower.split()) <= 3:
205
+ return CommandSuggestion(
206
+ kind="utterance",
207
+ suggestion=f"{text.rstrip()} (please name the component or file)",
208
+ confidence=0.6,
209
+ reasoning=f"verb '{first}' lacks an explicit target",
210
+ )
211
+
212
+ # Project-aware refinement: scan REQUIREMENTS.md for keywords that match
213
+ # the input and propose the first hit. Best-effort; never blocks.
214
+ if project_dir is not None:
215
+ candidates = _scan_requirements(text, project_dir)
216
+ if candidates:
217
+ return CommandSuggestion(
218
+ kind="utterance",
219
+ suggestion=f"{text.rstrip()} ({candidates[0]})",
220
+ confidence=0.65,
221
+ reasoning=f"matched {candidates[0]} from REQUIREMENTS.md",
222
+ candidates=candidates,
223
+ )
224
+
225
+ # Default: echo back unchanged.
226
+ return CommandSuggestion(
227
+ kind="utterance",
228
+ suggestion=text,
229
+ confidence=0.4,
230
+ reasoning="no project-specific refinement available",
231
+ )
232
+
233
+
234
+ def _scan_requirements(text: str, project_dir: Path) -> list[str]:
235
+ """Return up to 5 REQ ids whose description shares words with ``text``."""
236
+ candidates: list[tuple[int, str]] = []
237
+ for path in (
238
+ project_dir / "REQUIREMENTS.md",
239
+ project_dir / "docs" / "REQUIREMENTS.md",
240
+ ):
241
+ if not path.is_file():
242
+ continue
243
+ try:
244
+ content = path.read_text(encoding="utf-8")
245
+ except OSError:
246
+ continue
247
+ words = {w.lower() for w in re.findall(r"[A-Za-z]{4,}", text)}
248
+ if not words:
249
+ return []
250
+ for match in re.finditer(
251
+ r"^###?\s+(REQ-[A-Z0-9-]+)\s*(.*?)(?=^###?\s+REQ|^##\s|\Z)",
252
+ content,
253
+ re.MULTILINE | re.DOTALL,
254
+ ):
255
+ req_id, body = match.group(1), match.group(2)
256
+ body_words = {w.lower() for w in re.findall(r"[A-Za-z]{4,}", body)}
257
+ score = len(words & body_words)
258
+ if score > 0:
259
+ candidates.append((score, req_id))
260
+ candidates.sort(key=lambda x: (-x[0], x[1]))
261
+ return [req for _, req in candidates[:5]]
262
+
263
+
264
+ __all__ = ["CommandSuggestion", "classify", "suggest_command"]
@@ -4440,6 +4440,36 @@ def info_cmd(as_json: bool, section: str) -> None:
4440
4440
  # ---------------------------------------------------------------------------
4441
4441
 
4442
4442
 
4443
+ # ---------------------------------------------------------------------------
4444
+ # specsmith suggest-command — NL-to-command suggester (REQ-131)
4445
+ # ---------------------------------------------------------------------------
4446
+
4447
+
4448
+ @main.command(name="suggest-command")
4449
+ @click.argument("text")
4450
+ @click.option("--project-dir", type=click.Path(exists=True), default=".")
4451
+ @click.option(
4452
+ "--json",
4453
+ "as_json",
4454
+ is_flag=True,
4455
+ default=True,
4456
+ help="Emit suggestion as JSON (default; only mode for now).",
4457
+ )
4458
+ def suggest_command_cmd(text: str, project_dir: str, as_json: bool) -> None:
4459
+ """Suggest a refined command or utterance for a partial input (REQ-131).
4460
+
4461
+ Returns a JSON object: ``{kind, suggestion, confidence, reasoning, candidates}``.
4462
+ ``kind`` is one of ``command``, ``utterance``, ``passthrough``. The
4463
+ extension renders the suggestion as inline ghost-text.
4464
+ """
4465
+ import json as _json
4466
+
4467
+ from specsmith.agent.suggester import suggest_command
4468
+
4469
+ result = suggest_command(text, project_dir=Path(project_dir).resolve())
4470
+ click.echo(_json.dumps(result.to_dict(), indent=2))
4471
+
4472
+
4443
4473
  @main.command(name="scan")
4444
4474
  @click.option("--project-dir", type=click.Path(exists=True), default=".")
4445
4475
  @click.option("--json", "as_json", is_flag=True, default=False, help="Output as JSON.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specsmith
3
- Version: 0.6.0.dev231
3
+ Version: 0.6.0.dev232
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
@@ -70,6 +70,7 @@ src/specsmith/agent/repl.py
70
70
  src/specsmith/agent/router.py
71
71
  src/specsmith/agent/rules.py
72
72
  src/specsmith/agent/safety.py
73
+ src/specsmith/agent/suggester.py
73
74
  src/specsmith/agent/tools.py
74
75
  src/specsmith/agent/verifier.py
75
76
  src/specsmith/commands/__init__.py
@@ -170,6 +171,7 @@ tests/test_rate_limits.py
170
171
  tests/test_scaffolder.py
171
172
  tests/test_skill_marketplace.py
172
173
  tests/test_smoke.py
174
+ tests/test_suggester.py
173
175
  tests/test_tools.py
174
176
  tests/test_validator.py
175
177
  tests/test_vcs.py
@@ -0,0 +1,88 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 BitConcepts, LLC. All rights reserved.
3
+ """Tests for the NL-to-command suggester (REQ-131 / TEST-131)."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ from pathlib import Path
9
+
10
+ from click.testing import CliRunner
11
+
12
+ from specsmith.agent.suggester import classify, suggest_command
13
+ from specsmith.cli import main
14
+
15
+
16
+ def test_classify_passthrough_for_short_input() -> None:
17
+ assert classify("") == "passthrough"
18
+ assert classify("a") == "passthrough"
19
+
20
+
21
+ def test_classify_command_for_shell_verb() -> None:
22
+ assert classify("git status") == "command"
23
+ assert classify("run tests") == "command"
24
+ assert classify("pytest -q") == "command"
25
+
26
+
27
+ def test_classify_utterance_for_natural_language() -> None:
28
+ assert classify("add a logging module") == "utterance"
29
+ assert classify("explain the broker") == "utterance"
30
+
31
+
32
+ def test_suggest_command_template_match() -> None:
33
+ out = suggest_command("run tests")
34
+ assert out.kind == "command"
35
+ assert out.suggestion == "pytest -q"
36
+ assert out.confidence >= 0.8
37
+
38
+
39
+ def test_suggest_command_single_verb_fallback() -> None:
40
+ out = suggest_command("git")
41
+ assert out.kind == "command"
42
+ assert "git" in out.suggestion
43
+ assert "status" in out.suggestion
44
+
45
+
46
+ def test_suggest_command_explicit_req_increases_confidence() -> None:
47
+ out = suggest_command("fix REQ-130 broken")
48
+ assert out.kind == "utterance"
49
+ assert "REQ-130" in out.candidates
50
+ assert out.confidence >= 0.85
51
+
52
+
53
+ def test_suggest_command_change_verb_without_target() -> None:
54
+ out = suggest_command("fix it")
55
+ assert out.kind == "utterance"
56
+ assert "name the component" in out.suggestion
57
+
58
+
59
+ def test_suggest_command_project_aware_match(tmp_path: Path) -> None:
60
+ (tmp_path / "REQUIREMENTS.md").write_text(
61
+ "## REQ-LOG-001\n- Description: add structured logging to the broker module.\n",
62
+ encoding="utf-8",
63
+ )
64
+ out = suggest_command(
65
+ "document the structured logging behaviour for broker module",
66
+ project_dir=tmp_path,
67
+ )
68
+ assert out.kind == "utterance"
69
+ assert "REQ-LOG-001" in out.candidates
70
+
71
+
72
+ def test_suggest_command_passthrough_for_short_text() -> None:
73
+ out = suggest_command("a")
74
+ assert out.kind == "passthrough"
75
+ assert out.suggestion == "a"
76
+
77
+
78
+ def test_cli_suggest_command_emits_json(tmp_path: Path) -> None:
79
+ runner = CliRunner()
80
+ result = runner.invoke(
81
+ main,
82
+ ["suggest-command", "git status", "--project-dir", str(tmp_path)],
83
+ env={"SPECSMITH_NO_AUTO_UPDATE": "1", "SPECSMITH_PYPI_CHECKED": "1"},
84
+ )
85
+ assert result.exit_code == 0, result.output
86
+ payload = json.loads(result.output)
87
+ assert payload["kind"] == "command"
88
+ assert payload["suggestion"] == "git --no-pager status"