specsmith 0.16.5.dev630__tar.gz → 0.16.5.dev640__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 (338) hide show
  1. {specsmith-0.16.5.dev630/src/specsmith.egg-info → specsmith-0.16.5.dev640}/PKG-INFO +8 -3
  2. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/README.md +7 -2
  3. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/pyproject.toml +3 -1
  4. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/broker.py +18 -3
  5. specsmith-0.16.5.dev640/src/specsmith/agent/model_router.py +242 -0
  6. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/runner.py +66 -6
  7. specsmith-0.16.5.dev640/src/specsmith/architect.py +902 -0
  8. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/auditor.py +84 -2
  9. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/cli.py +1285 -93
  10. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/esdb/__init__.py +68 -1
  11. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/governance_logic.py +25 -4
  12. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/governance_yaml.py +34 -2
  13. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/instinct.py +44 -6
  14. specsmith-0.16.5.dev640/src/specsmith/local_model.py +435 -0
  15. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/__init__.py +2 -0
  16. specsmith-0.16.5.dev640/src/specsmith/migrations/m007_yaml_first.py +148 -0
  17. specsmith-0.16.5.dev640/src/specsmith/project_metrics.py +353 -0
  18. specsmith-0.16.5.dev640/src/specsmith/quality_report.py +568 -0
  19. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/scaffolder.py +43 -0
  20. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/session_init.py +55 -6
  21. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/governance.py +90 -0
  22. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/software_engineering.py +174 -0
  23. specsmith-0.16.5.dev640/src/specsmith/skills/specsmith_skills.py +29 -0
  24. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/sync.py +30 -13
  25. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/agents.md.j2 +11 -7
  26. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/security.md.j2 +1 -1
  27. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/updater.py +16 -0
  28. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/upgrader.py +24 -0
  29. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640/src/specsmith.egg-info}/PKG-INFO +8 -3
  30. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith.egg-info/SOURCES.txt +16 -0
  31. specsmith-0.16.5.dev640/tests/test_architect_interview.py +388 -0
  32. specsmith-0.16.5.dev640/tests/test_architect_v2.py +374 -0
  33. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_architecture.py +1 -0
  34. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_auditor.py +17 -0
  35. specsmith-0.16.5.dev640/tests/test_cleanup_cmd.py +234 -0
  36. specsmith-0.16.5.dev640/tests/test_esdb_backend_switch.py +180 -0
  37. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_esdb_license.py +95 -1
  38. specsmith-0.16.5.dev640/tests/test_issues_263_264.py +240 -0
  39. specsmith-0.16.5.dev640/tests/test_local_model.py +471 -0
  40. specsmith-0.16.5.dev640/tests/test_markdown_deprecation.py +248 -0
  41. specsmith-0.16.5.dev640/tests/test_migration_direction.py +210 -0
  42. specsmith-0.16.5.dev640/tests/test_model_router.py +258 -0
  43. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_nexus.py +3 -1
  44. specsmith-0.16.5.dev640/tests/test_project_metrics.py +281 -0
  45. specsmith-0.16.5.dev640/tests/test_quality_report.py +245 -0
  46. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_typing_guardrails.py +1 -1
  47. specsmith-0.16.5.dev630/src/specsmith/architect.py +0 -154
  48. specsmith-0.16.5.dev630/src/specsmith/skills/specsmith_skills.py +0 -267
  49. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/LICENSE +0 -0
  50. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/setup.cfg +0 -0
  51. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/__init__.py +0 -0
  52. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/belief.py +0 -0
  53. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/certainty.py +0 -0
  54. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/failure_graph.py +0 -0
  55. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/py.typed +0 -0
  56. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/recovery.py +0 -0
  57. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/session.py +0 -0
  58. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/stress_tester.py +0 -0
  59. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/trace.py +0 -0
  60. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/__init__.py +0 -0
  61. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/__main__.py +0 -0
  62. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/__init__.py +0 -0
  63. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/chat_runner.py +0 -0
  64. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/cleanup.py +0 -0
  65. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/context_seed.py +0 -0
  66. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/core.py +0 -0
  67. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/dispatch/__init__.py +0 -0
  68. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/dispatch/_status.py +0 -0
  69. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/dispatch/dag.py +0 -0
  70. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
  71. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/dispatch/events.py +0 -0
  72. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/dispatch/result.py +0 -0
  73. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/endpoints.py +0 -0
  74. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/events.py +0 -0
  75. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/execution_profiles.py +0 -0
  76. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/fallback.py +0 -0
  77. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/hf_leaderboard.py +0 -0
  78. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/hf_sync.py +0 -0
  79. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/indexer.py +0 -0
  80. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/llm_client.py +0 -0
  81. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/mcp.py +0 -0
  82. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/memory.py +0 -0
  83. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/model_intelligence.py +0 -0
  84. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/model_profiles.py +0 -0
  85. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/orchestrator.py +0 -0
  86. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/permissions.py +0 -0
  87. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/profiles.py +0 -0
  88. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/provider_registry.py +0 -0
  89. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/repl.py +0 -0
  90. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/router.py +0 -0
  91. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/rules.py +0 -0
  92. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/safety.py +0 -0
  93. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/spawner.py +0 -0
  94. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/suggester.py +0 -0
  95. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/teams.py +0 -0
  96. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/tools.py +0 -0
  97. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/verifier.py +0 -0
  98. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/voice.py +0 -0
  99. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/approvals.py +0 -0
  100. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/auth.py +0 -0
  101. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/block_export.py +0 -0
  102. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/channel.py +0 -0
  103. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/ci_manager.py +0 -0
  104. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/commands/__init__.py +0 -0
  105. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/commands/intelligence.py +0 -0
  106. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/commands/issues_policy.py +0 -0
  107. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/commands/reporting.py +0 -0
  108. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compliance/__init__.py +0 -0
  109. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compliance/_compat.py +0 -0
  110. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compliance/checker.py +0 -0
  111. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compliance/evidence.py +0 -0
  112. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compliance/regulations.py +0 -0
  113. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compliance/reporter.py +0 -0
  114. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compressor.py +0 -0
  115. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/config.py +0 -0
  116. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/console_utils.py +0 -0
  117. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/context_orchestrator.py +0 -0
  118. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/context_window.py +0 -0
  119. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/credit_analyzer.py +0 -0
  120. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/credits.py +0 -0
  121. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/dashboard.py +0 -0
  122. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/__init__.py +0 -0
  123. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/base.py +0 -0
  124. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/citations.py +0 -0
  125. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/fpd.py +0 -0
  126. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/odp.py +0 -0
  127. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/patentsview.py +0 -0
  128. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/pfw.py +0 -0
  129. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/ppubs.py +0 -0
  130. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/ptab.py +0 -0
  131. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/differ.py +0 -0
  132. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/doctor.py +0 -0
  133. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/drive.py +0 -0
  134. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/editor.py +0 -0
  135. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/epistemic/__init__.py +0 -0
  136. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/epistemic/belief.py +0 -0
  137. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/epistemic/certainty.py +0 -0
  138. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/epistemic/failure_graph.py +0 -0
  139. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/epistemic/recovery.py +0 -0
  140. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/epistemic/stress_tester.py +0 -0
  141. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/esdb/_license.py +0 -0
  142. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/esdb/bridge.py +0 -0
  143. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/esdb/sqlite_store.py +0 -0
  144. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/eval/__init__.py +0 -0
  145. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/eval/builtins.py +0 -0
  146. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/eval/runner.py +0 -0
  147. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/executor.py +0 -0
  148. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/exporter.py +0 -0
  149. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/governance_store.py +0 -0
  150. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/governed_pr.py +0 -0
  151. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/__init__.py +0 -0
  152. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/app.py +0 -0
  153. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/main_window.py +0 -0
  154. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/session_tab.py +0 -0
  155. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/theme.py +0 -0
  156. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/__init__.py +0 -0
  157. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/chat_view.py +0 -0
  158. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/input_bar.py +0 -0
  159. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/provider_bar.py +0 -0
  160. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/token_meter.py +0 -0
  161. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/tool_panel.py +0 -0
  162. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/update_checker.py +0 -0
  163. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/worker.py +0 -0
  164. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/history_search.py +0 -0
  165. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/importer.py +0 -0
  166. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/__init__.py +0 -0
  167. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/agent_skill.py +0 -0
  168. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/aider.py +0 -0
  169. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/base.py +0 -0
  170. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/claude_code.py +0 -0
  171. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/codity.py +0 -0
  172. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/copilot.py +0 -0
  173. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/cursor.py +0 -0
  174. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/gemini.py +0 -0
  175. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/windsurf.py +0 -0
  176. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/issue_reporter.py +0 -0
  177. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/languages.py +0 -0
  178. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/ledger.py +0 -0
  179. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/mcp_generator.py +0 -0
  180. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/mcp_server.py +0 -0
  181. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/m001_governance_yaml.py +0 -0
  182. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/m002_agents_slim.py +0 -0
  183. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/m003_compliance_init.py +0 -0
  184. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
  185. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
  186. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/m006_session_governance.py +0 -0
  187. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/runner.py +0 -0
  188. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/ollama_cmds.py +0 -0
  189. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/patent.py +0 -0
  190. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/paths.py +0 -0
  191. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/phase.py +0 -0
  192. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/plugins.py +0 -0
  193. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/policy.py +0 -0
  194. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/profiles.py +0 -0
  195. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/rate_limits.py +0 -0
  196. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/recover.py +0 -0
  197. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/releaser.py +0 -0
  198. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/requirements.py +0 -0
  199. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/requirements_parser.py +0 -0
  200. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/retrieval.py +0 -0
  201. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/risk.py +0 -0
  202. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/safe_write.py +0 -0
  203. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/serve.py +0 -0
  204. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/session.py +0 -0
  205. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/session_store.py +0 -0
  206. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/__init__.py +0 -0
  207. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/ai_agents.py +0 -0
  208. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/cloud.py +0 -0
  209. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/corporate.py +0 -0
  210. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/cross_platform.py +0 -0
  211. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/data_engineering.py +0 -0
  212. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/devops.py +0 -0
  213. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/docs.py +0 -0
  214. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/embedded.py +0 -0
  215. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/hardware.py +0 -0
  216. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/mobile.py +0 -0
  217. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/platform_engineering.py +0 -0
  218. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/productivity.py +0 -0
  219. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/ssh.py +0 -0
  220. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/web_backend.py +0 -0
  221. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills_builder.py +0 -0
  222. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
  223. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
  224. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/contributing.md.j2 +0 -0
  225. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
  226. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
  227. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/license-MIT.j2 +0 -0
  228. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
  229. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
  230. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
  231. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
  232. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
  233. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
  234. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/editorconfig.j2 +0 -0
  235. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/gitattributes.j2 +0 -0
  236. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/gitignore.j2 +0 -0
  237. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/go/go.mod.j2 +0 -0
  238. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/go/main.go.j2 +0 -0
  239. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
  240. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
  241. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
  242. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
  243. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
  244. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
  245. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/roles.md.j2 +0 -0
  246. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/rules.md.j2 +0 -0
  247. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
  248. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
  249. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/verification.md.j2 +0 -0
  250. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/js/package.json.j2 +0 -0
  251. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/ledger.md.j2 +0 -0
  252. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/python/cli.py.j2 +0 -0
  253. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/python/init.py.j2 +0 -0
  254. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
  255. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/readme.md.j2 +0 -0
  256. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
  257. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/rust/main.rs.j2 +0 -0
  258. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
  259. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
  260. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
  261. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
  262. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
  263. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
  264. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
  265. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/tool_installer.py +0 -0
  266. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/toolrules.py +0 -0
  267. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/tools.py +0 -0
  268. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/trace.py +0 -0
  269. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/transcripts.py +0 -0
  270. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/validator.py +0 -0
  271. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/vcs/__init__.py +0 -0
  272. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/vcs/base.py +0 -0
  273. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/vcs/bitbucket.py +0 -0
  274. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/vcs/github.py +0 -0
  275. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/vcs/gitlab.py +0 -0
  276. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/vcs_commands.py +0 -0
  277. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/wi_store.py +0 -0
  278. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/wireframes.py +0 -0
  279. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/workspace.py +0 -0
  280. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith.egg-info/dependency_links.txt +0 -0
  281. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith.egg-info/entry_points.txt +0 -0
  282. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith.egg-info/requires.txt +0 -0
  283. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith.egg-info/top_level.txt +0 -0
  284. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_CMD_001.py +0 -0
  285. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_agent_profiles.py +0 -0
  286. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_agent_run_feedback.py +0 -0
  287. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_agent_runner_ready.py +0 -0
  288. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_ai_client.py +0 -0
  289. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_ai_intelligence.py +0 -0
  290. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_channel.py +0 -0
  291. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_chat_diff_decision.py +0 -0
  292. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_chat_runner_openai_compat.py +0 -0
  293. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_chat_stdin_protocol.py +0 -0
  294. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_cli.py +0 -0
  295. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_cli_regressions.py +0 -0
  296. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_cli_workflows_history_drive.py +0 -0
  297. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_compliance.py +0 -0
  298. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_compliance_governance.py +0 -0
  299. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_compressor.py +0 -0
  300. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_dispatch.py +0 -0
  301. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_e2e_nexus.py +0 -0
  302. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_endpoints_cli.py +0 -0
  303. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_endpoints_store.py +0 -0
  304. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_epistemic.py +0 -0
  305. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_esdb_enforcement.py +0 -0
  306. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_esdb_sqlite.py +0 -0
  307. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_esdb_verify_chain_cli.py +0 -0
  308. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_fallback_chain.py +0 -0
  309. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_golden_path.py +0 -0
  310. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_importer.py +0 -0
  311. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_init_modes.py +0 -0
  312. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_integrations.py +0 -0
  313. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_integrations_codity.py +0 -0
  314. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_intelligence.py +0 -0
  315. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_issue_policy_features.py +0 -0
  316. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_issue_reporter.py +0 -0
  317. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_mcp_client.py +0 -0
  318. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_mcp_server.py +0 -0
  319. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_new_modules.py +0 -0
  320. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_permissions.py +0 -0
  321. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_phase1_4_new.py +0 -0
  322. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_phase34_completion.py +0 -0
  323. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_rate_limits.py +0 -0
  324. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_reporting_commands.py +0 -0
  325. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_req_248_262.py +0 -0
  326. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_req_358_359.py +0 -0
  327. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_scaffolder.py +0 -0
  328. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_schema_migrations.py +0 -0
  329. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_skill_marketplace.py +0 -0
  330. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_skills_mcp.py +0 -0
  331. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_smoke.py +0 -0
  332. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_suggester.py +0 -0
  333. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_tools.py +0 -0
  334. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_validator.py +0 -0
  335. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_vcs.py +0 -0
  336. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_warp_parity.py +0 -0
  337. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_warp_parity_followup.py +0 -0
  338. {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_wi_lifecycle.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specsmith
3
- Version: 0.16.5.dev630
3
+ Version: 0.16.5.dev640
4
4
  Summary: AEE governance toolkit for AI-assisted development — session preflight gates, multi-agent dispatch, requirements↔test traceability, ESDB persistence, MCP server, and skills for Warp, Cursor, Claude Code, Copilot, Windsurf, and Aider.
5
5
  Author: Layer1Labs Silicon, Inc.
6
6
  License-Expression: MIT
@@ -261,11 +261,16 @@ command — not to library imports. Use this when you want the AEE belief-state
261
261
  in your own application without managing a pipx environment.
262
262
 
263
263
  **ESDB — Epistemic State Database**
264
+ Terminology used in this repo is strict:
265
+ - **ESDB** = the specification/data model category.
266
+ - **SQLite backend** = the free/default ESDB implementation bundled in `specsmith`.
267
+ - **ChronoMemory** = the commercial package.
268
+ - **ChronoStore** = the backend engine/class provided by the ChronoMemory package.
264
269
 
265
270
  | Tier | Package | License | What you get |
266
271
  |------|---------|---------|-------------|
267
272
  | **Default** | `specsmith` (built-in) | MIT, free | SQLite backend — requirements, test cases, confidence filtering |
268
- | **Commercial** | `chronomemory` via `specsmith[esdb]` | Proprietary — license required (see [COMMERCIAL-LICENSE.md](https://github.com/layer1labs/specsmith/blob/develop/COMMERCIAL-LICENSE.md)) | ChronoStore: tamper-evident SHA-256 WAL, OEA anti-hallucination fields, Rust acceleration, epistemic rollback |
273
+ | **Commercial** | `chronomemory` via `specsmith[esdb]` | Proprietary — license required (see [COMMERCIAL-LICENSE.md](https://github.com/layer1labs/specsmith/blob/develop/COMMERCIAL-LICENSE.md)) | ChronoMemory package (ChronoStore backend): tamper-evident SHA-256 WAL, OEA anti-hallucination fields, Rust acceleration, epistemic rollback |
269
274
 
270
275
  See `docs/editions.md` for the full OSS vs commercial feature matrix.
271
276
 
@@ -281,7 +286,7 @@ specsmith esdb status # shows: SQLite (free, MIT) — active by default
281
286
  - Commit canonical ChronoMemory state files: `.chronomemory/events.wal` and `.chronomemory/snapshot.json`.
282
287
  - Do not commit ChronoMemory timestamped backup copies under `.chronomemory/backup/` (regenerated by `specsmith save` / `specsmith esdb backup`).
283
288
 
284
- **Upgrading to chronomemory ChronoStore (commercial):**
289
+ **Upgrading to ChronoMemory (ChronoStore backend, commercial):**
285
290
 
286
291
  If you hold a chronomemory ESDB license, activate the commercial backend:
287
292
 
@@ -183,11 +183,16 @@ command — not to library imports. Use this when you want the AEE belief-state
183
183
  in your own application without managing a pipx environment.
184
184
 
185
185
  **ESDB — Epistemic State Database**
186
+ Terminology used in this repo is strict:
187
+ - **ESDB** = the specification/data model category.
188
+ - **SQLite backend** = the free/default ESDB implementation bundled in `specsmith`.
189
+ - **ChronoMemory** = the commercial package.
190
+ - **ChronoStore** = the backend engine/class provided by the ChronoMemory package.
186
191
 
187
192
  | Tier | Package | License | What you get |
188
193
  |------|---------|---------|-------------|
189
194
  | **Default** | `specsmith` (built-in) | MIT, free | SQLite backend — requirements, test cases, confidence filtering |
190
- | **Commercial** | `chronomemory` via `specsmith[esdb]` | Proprietary — license required (see [COMMERCIAL-LICENSE.md](https://github.com/layer1labs/specsmith/blob/develop/COMMERCIAL-LICENSE.md)) | ChronoStore: tamper-evident SHA-256 WAL, OEA anti-hallucination fields, Rust acceleration, epistemic rollback |
195
+ | **Commercial** | `chronomemory` via `specsmith[esdb]` | Proprietary — license required (see [COMMERCIAL-LICENSE.md](https://github.com/layer1labs/specsmith/blob/develop/COMMERCIAL-LICENSE.md)) | ChronoMemory package (ChronoStore backend): tamper-evident SHA-256 WAL, OEA anti-hallucination fields, Rust acceleration, epistemic rollback |
191
196
 
192
197
  See `docs/editions.md` for the full OSS vs commercial feature matrix.
193
198
 
@@ -203,7 +208,7 @@ specsmith esdb status # shows: SQLite (free, MIT) — active by default
203
208
  - Commit canonical ChronoMemory state files: `.chronomemory/events.wal` and `.chronomemory/snapshot.json`.
204
209
  - Do not commit ChronoMemory timestamped backup copies under `.chronomemory/backup/` (regenerated by `specsmith save` / `specsmith esdb backup`).
205
210
 
206
- **Upgrading to chronomemory ChronoStore (commercial):**
211
+ **Upgrading to ChronoMemory (ChronoStore backend, commercial):**
207
212
 
208
213
  If you hold a chronomemory ESDB license, activate the commercial backend:
209
214
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "specsmith"
7
- version = "0.16.5.dev630"
7
+ version = "0.16.5.dev640"
8
8
  description = "AEE governance toolkit for AI-assisted development — session preflight gates, multi-agent dispatch, requirements↔test traceability, ESDB persistence, MCP server, and skills for Warp, Cursor, Claude Code, Copilot, Windsurf, and Aider."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -182,6 +182,8 @@ module = [
182
182
  "whisper_cpp_python.*",
183
183
  "chronomemory", # git dep — no published stubs yet
184
184
  "chronomemory.*",
185
+ "tomllib", # stdlib in 3.11+; not available when checking against python_version=3.10
186
+ "tomli", # optional 3.10 backport; not installed in the dev environment
185
187
  ]
186
188
  ignore_missing_imports = true
187
189
 
@@ -71,6 +71,7 @@ class Intent(str, Enum):
71
71
 
72
72
  READ_ONLY_ASK = "read_only_ask"
73
73
  CHANGE = "change"
74
+ REFACTOR = "refactor" # structural change with no behaviour change — highest scope-creep risk
74
75
  RELEASE = "release"
75
76
  DESTRUCTIVE = "destructive"
76
77
 
@@ -95,10 +96,19 @@ _RELEASE_PATTERNS = (
95
96
  re.compile(r"\bbump\s+(the\s+)?version\b", re.IGNORECASE),
96
97
  )
97
98
 
99
+ _REFACTOR_PATTERNS = (
100
+ re.compile(r"\b(refactor|rewrite|extract|reorganise|reorganize|restructure)\b", re.IGNORECASE),
101
+ re.compile(r"\brename\s+\w", re.IGNORECASE), # rename <identifier>
102
+ re.compile(r"\bmove\s+.+?\bto\b", re.IGNORECASE), # move X to Y
103
+ re.compile(r"\bsplit\s+.+?\binto\b", re.IGNORECASE), # split X into Y
104
+ re.compile(r"\bconsolidate\b", re.IGNORECASE),
105
+ re.compile(r"\binline\s+the\b", re.IGNORECASE),
106
+ )
107
+
98
108
  _CHANGE_PATTERNS = (
99
109
  re.compile(r"\b(fix|repair|patch)\b", re.IGNORECASE),
100
110
  re.compile(r"\b(add|implement|create|introduce|build)\b", re.IGNORECASE),
101
- re.compile(r"\b(refactor|rewrite|rename|extract)\b", re.IGNORECASE),
111
+ re.compile(r"\b(rename)\b", re.IGNORECASE),
102
112
  re.compile(r"\b(update|migrate|upgrade)\b", re.IGNORECASE),
103
113
  re.compile(r"\b(remove|delete)\s+(the\s+)?(unused|stale|legacy)\b", re.IGNORECASE),
104
114
  )
@@ -113,8 +123,10 @@ _READ_ONLY_PATTERNS = (
113
123
  def classify_intent(utterance: str) -> Intent:
114
124
  """Classify the user's natural-language utterance.
115
125
 
116
- Order: DESTRUCTIVE > RELEASE > CHANGE > READ_ONLY_ASK > READ_ONLY_ASK
117
- (default).
126
+ Order: DESTRUCTIVE > RELEASE > REFACTOR > CHANGE > READ_ONLY_ASK.
127
+ REFACTOR is checked before CHANGE so that structural-change verbs
128
+ (refactor, extract, reorganise ...) get the tighter scope-discipline
129
+ governance rather than the generic change governance.
118
130
  """
119
131
  if not utterance or not utterance.strip():
120
132
  return Intent.READ_ONLY_ASK
@@ -125,6 +137,9 @@ def classify_intent(utterance: str) -> Intent:
125
137
  for p in _RELEASE_PATTERNS:
126
138
  if p.search(text):
127
139
  return Intent.RELEASE
140
+ for p in _REFACTOR_PATTERNS:
141
+ if p.search(text):
142
+ return Intent.REFACTOR
128
143
  for p in _CHANGE_PATTERNS:
129
144
  if p.search(text):
130
145
  return Intent.CHANGE
@@ -0,0 +1,242 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 Layer1Labs Silicon, Inc. All rights reserved.
3
+ """Per-turn intent classifier and Ollama model router (REQ-388, REQ-389).
4
+
5
+ ``ModelRouter`` classifies each user utterance into one of three roles
6
+ (``general``, ``coding``, ``reasoning``) using a fast keyword + slash-command
7
+ heuristic and returns the best Ollama model tag for that role. The router
8
+ only changes the active model when the role classification changes, so a
9
+ long coding session stays on the coder model without emitting a switch event
10
+ on every turn.
11
+
12
+ Usage
13
+ -----
14
+ ::
15
+
16
+ router = ModelRouter({"general": "qwen2.5:14b",
17
+ "coding": "qwen2.5-coder:14b",
18
+ "reasoning": "deepseek-r1:14b"})
19
+ model_tag, switched = router.route("write a function that ...")
20
+ if switched:
21
+ print(f"→ switched to {model_tag}")
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ __all__ = ["ModelRouter", "classify_intent"]
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # Intent classification
30
+ # ---------------------------------------------------------------------------
31
+
32
+ # Slash commands that strongly signal coding work.
33
+ _CODING_SLASH: frozenset[str] = frozenset({"/code", "/fix", "/refactor", "/test", "/commit", "/pr"})
34
+
35
+ # Slash commands that signal analytical / planning work.
36
+ _REASONING_SLASH: frozenset[str] = frozenset({"/architect", "/plan", "/audit", "/review", "/why"})
37
+
38
+ # Keywords that indicate the user wants code written or bugs fixed.
39
+ _CODING_KEYWORDS: frozenset[str] = frozenset(
40
+ {
41
+ "write",
42
+ "implement",
43
+ "function",
44
+ "method",
45
+ "class",
46
+ "bug",
47
+ "error",
48
+ "fix",
49
+ "patch",
50
+ "test",
51
+ "debug",
52
+ "refactor",
53
+ "import",
54
+ "syntax",
55
+ "compile",
56
+ "code",
57
+ "script",
58
+ "module",
59
+ "api",
60
+ "endpoint",
61
+ "snippet",
62
+ "variable",
63
+ "loop",
64
+ "array",
65
+ "dict",
66
+ "parse",
67
+ "format",
68
+ "regex",
69
+ "unittest",
70
+ "pytest",
71
+ "mock",
72
+ "stub",
73
+ "boilerplate",
74
+ "scaffold",
75
+ "decorator",
76
+ "lambda",
77
+ "iterator",
78
+ "generator",
79
+ "async",
80
+ "await",
81
+ "coroutine",
82
+ "type",
83
+ "hint",
84
+ "annotation",
85
+ "dataclass",
86
+ }
87
+ )
88
+
89
+ # Keywords that indicate the user wants analysis, architecture, or deep reasoning.
90
+ _REASONING_KEYWORDS: frozenset[str] = frozenset(
91
+ {
92
+ "analyze",
93
+ "analyse",
94
+ "explain",
95
+ "reason",
96
+ "strategy",
97
+ "architecture",
98
+ "design",
99
+ "evaluate",
100
+ "compare",
101
+ "tradeoff",
102
+ "trade-off",
103
+ "review",
104
+ "audit",
105
+ "assess",
106
+ "consider",
107
+ "approach",
108
+ "decision",
109
+ "structure",
110
+ "plan",
111
+ "requirements",
112
+ "spec",
113
+ "diagram",
114
+ "model",
115
+ "rationale",
116
+ "justify",
117
+ "pros",
118
+ "cons",
119
+ "recommend",
120
+ "alternative",
121
+ "consequence",
122
+ "impact",
123
+ "risk",
124
+ "bottleneck",
125
+ "complexity",
126
+ "scalability",
127
+ }
128
+ )
129
+
130
+
131
+ def classify_intent(text: str) -> str:
132
+ """Classify *text* as ``"coding"``, ``"reasoning"``, or ``"general"``.
133
+
134
+ The classification is intentionally fast (O(n) word scan, no ML) so it
135
+ adds negligible latency to each turn. Rules in priority order:
136
+
137
+ 1. Slash-command prefix → maps directly to a role.
138
+ 2. Keyword match → first-match wins; coding keywords checked before
139
+ reasoning keywords so ``/fix a bug in the design docs`` routes to
140
+ coding.
141
+ 3. Default → ``"general"``.
142
+ """
143
+ text_stripped = text.strip()
144
+
145
+ # --- Rule 1: explicit slash-command ---
146
+ head = text_stripped.split()[0].lower() if text_stripped else ""
147
+ if head in _CODING_SLASH:
148
+ return "coding"
149
+ if head in _REASONING_SLASH:
150
+ return "reasoning"
151
+
152
+ # --- Rule 2: keyword scan ---
153
+ words = set(text_stripped.lower().split())
154
+ if words & _CODING_KEYWORDS:
155
+ return "coding"
156
+ if words & _REASONING_KEYWORDS:
157
+ return "reasoning"
158
+
159
+ return "general"
160
+
161
+
162
+ # ---------------------------------------------------------------------------
163
+ # ModelRouter
164
+ # ---------------------------------------------------------------------------
165
+
166
+
167
+ class ModelRouter:
168
+ """Routes each user turn to the appropriate Ollama model (REQ-388).
169
+
170
+ Parameters
171
+ ----------
172
+ roles:
173
+ Mapping of role name (``"general"``, ``"coding"``, ``"reasoning"``)
174
+ to Ollama model tag. Missing roles fall back to ``"general"``; if
175
+ ``"general"`` is also missing the router returns ``(None, False)``
176
+ and the caller uses whatever model was already set.
177
+ """
178
+
179
+ def __init__(self, roles: dict[str, str | None]) -> None:
180
+ # Strip out None values so lookups never return None.
181
+ self._roles: dict[str, str] = {k: v for k, v in roles.items() if v}
182
+ self._current_role: str | None = None
183
+ self._current_model: str | None = None
184
+
185
+ # ── Public interface ────────────────────────────────────────────────────
186
+
187
+ def route(self, text: str) -> tuple[str | None, bool]:
188
+ """Classify *text* and return the model tag for the detected role.
189
+
190
+ Returns
191
+ -------
192
+ (model_tag, switched)
193
+ *model_tag* is the Ollama tag to use for this turn, or ``None``
194
+ when no role→model mapping is configured.
195
+ *switched* is ``True`` when the model changed from the previous
196
+ turn (useful for emitting a switch notification).
197
+ """
198
+ if not self._roles:
199
+ return None, False
200
+
201
+ role = classify_intent(text)
202
+
203
+ # Resolve: prefer exact role match, fallback to general, then any.
204
+ model = (
205
+ self._roles.get(role) or self._roles.get("general") or next(iter(self._roles.values()))
206
+ )
207
+
208
+ if model == self._current_model:
209
+ return model, False
210
+
211
+ prev_role = self._current_role
212
+ self._current_role = role
213
+ self._current_model = model
214
+ switched = prev_role is not None # first call is not a "switch"
215
+ return model, switched
216
+
217
+ @property
218
+ def active_model(self) -> str | None:
219
+ """Model tag currently in use, or ``None`` before the first turn."""
220
+ return self._current_model
221
+
222
+ @property
223
+ def active_role(self) -> str | None:
224
+ """Role name currently in use, or ``None`` before the first turn."""
225
+ return self._current_role
226
+
227
+ def table(self) -> str:
228
+ """Human-readable routing table for the ``/models`` slash command."""
229
+ if not self._roles:
230
+ return " (no multi-model routing configured)"
231
+ role_order = ["general", "coding", "reasoning"]
232
+ lines: list[str] = [" Multi-model routing:"]
233
+ for role in role_order:
234
+ if role in self._roles:
235
+ active_marker = " ◀ active" if role == self._current_role else ""
236
+ lines.append(f" {role:<10} → {self._roles[role]}{active_marker}")
237
+ # Any roles not in the standard order
238
+ for role, model in self._roles.items():
239
+ if role not in role_order:
240
+ active_marker = " ◀ active" if role == self._current_role else ""
241
+ lines.append(f" {role:<10} → {model}{active_marker}")
242
+ return "\n".join(lines)
@@ -237,6 +237,7 @@ SLASH_COMMANDS: dict[str, str] = {
237
237
  "/undo": "[UNDO] Revert the last action: ",
238
238
  "/context": "[CONTEXT] Surface repo context relevant to: ",
239
239
  "/search": "[SEARCH] Search the repo and external docs for: ",
240
+ "/models": "", # handled inline — shows the multi-model routing table
240
241
  }
241
242
 
242
243
 
@@ -334,6 +335,10 @@ class AgentRunner:
334
335
  # back to single-profile behaviour so existing setups keep working.
335
336
  self._routing = self._load_routing()
336
337
 
338
+ # Multi-model router (REQ-389). Loaded from .specsmith/local-models.yml
339
+ # if present; falls back to None (single-model mode) on any error.
340
+ self._model_router = self._load_model_router()
341
+
337
342
  # Consumers may swap this with a closure that routes through their
338
343
  # own bus (see ``serve._AgentThread``). The default writes JSONL.
339
344
  self._emit_event: Callable[..., None] = self._default_emit_event
@@ -383,15 +388,21 @@ class AgentRunner:
383
388
  for s in statuses:
384
389
  if s.available:
385
390
  lines.append(
386
- f" {s.icon} {s.name:<10} ready model: {s.model} ({s.note})"
391
+ f" {s.icon} {s.name:<10} \u2713 ready model: {s.model} ({s.note})"
387
392
  )
388
393
  else:
389
- lines.append(f" {s.icon} {s.name:<10} {s.note}")
394
+ lines.append(f" {s.icon} {s.name:<10} \u2717 {s.note}")
390
395
  lines.append("")
391
396
  if active_count == 0:
392
- lines.append(" No provider available — commands will return no response.")
397
+ lines.append(" \u26a0 No provider available — commands will return no response.")
393
398
  else:
394
- lines.append(" Type plain English, or use /plan /ask /fix /test /commit /pr /exit")
399
+ # Show multi-model routing if the router is configured (REQ-389).
400
+ if self._model_router is not None:
401
+ lines.append(self._model_router.table())
402
+ lines.append("")
403
+ lines.append(
404
+ " Type plain English, or use /plan /ask /fix /test /commit /pr /models /exit"
405
+ )
395
406
  print("\n".join(lines), flush=True)
396
407
 
397
408
  def run_interactive(self) -> None:
@@ -447,6 +458,14 @@ class AgentRunner:
447
458
  self._history = []
448
459
  self._emit_event(type="system", message="History cleared.")
449
460
  return None
461
+ if text.strip() == "/models":
462
+ msg = (
463
+ self._model_router.table()
464
+ if self._model_router is not None
465
+ else " (multi-model routing not configured — run: specsmith local-model setup)"
466
+ )
467
+ self._emit_event(type="system", message=msg)
468
+ return None
450
469
  if text.startswith("/model "):
451
470
  new_model = text.split(maxsplit=1)[1].strip()
452
471
  self.model = new_model
@@ -494,6 +513,26 @@ class AgentRunner:
494
513
  )
495
514
 
496
515
  block_id = self._next_block_id()
516
+
517
+ # ── Multi-model routing (REQ-389) ──────────────────────────────────
518
+ # Ask the router which Ollama model best fits this utterance, then
519
+ # temporarily set SPECSMITH_OLLAMA_MODEL so _run_ollama picks it up.
520
+ # The env var is restored (or removed) after the turn completes.
521
+ import os as _os
522
+
523
+ _prev_ollama_model = _os.environ.get("SPECSMITH_OLLAMA_MODEL")
524
+ if self._model_router is not None:
525
+ routed_model, switched = self._model_router.route(text)
526
+ if routed_model:
527
+ _os.environ["SPECSMITH_OLLAMA_MODEL"] = routed_model
528
+ if switched:
529
+ role = self._model_router.active_role or "?"
530
+ self._emit_event(
531
+ type="system",
532
+ message=f"\u21bb model \u2192 {routed_model} (role: {role})",
533
+ )
534
+ # ──────────────────────────────────────────────────────────────────
535
+
497
536
  try:
498
537
  from specsmith.agent.chat_runner import (
499
538
  DEFAULT_OLLAMA_HOST,
@@ -519,12 +558,16 @@ class AgentRunner:
519
558
  recoverable=True,
520
559
  )
521
560
  return None
561
+ finally:
562
+ # Restore SPECSMITH_OLLAMA_MODEL to its pre-turn state (REQ-389).
563
+ if _prev_ollama_model is None:
564
+ _os.environ.pop("SPECSMITH_OLLAMA_MODEL", None)
565
+ else:
566
+ _os.environ["SPECSMITH_OLLAMA_MODEL"] = _prev_ollama_model
522
567
 
523
568
  if result is None:
524
569
  # All providers failed — give the user an actionable explanation
525
570
  # rather than silent emptiness.
526
- import os as _os
527
-
528
571
  host = _os.environ.get("OLLAMA_HOST", DEFAULT_OLLAMA_HOST).rstrip("/")
529
572
  if _ollama_alive(host):
530
573
  model = _pick_ollama_model(host)
@@ -655,6 +698,23 @@ class AgentRunner:
655
698
  except Exception: # noqa: BLE001
656
699
  return None
657
700
 
701
+ def _load_model_router(self) -> Any | None:
702
+ """Load ModelRouter from .specsmith/local-models.yml (REQ-389).
703
+
704
+ Returns ``None`` when no multi-role config is found, preserving
705
+ single-model behaviour for existing setups.
706
+ """
707
+ try:
708
+ from specsmith.agent.model_router import ModelRouter
709
+ from specsmith.local_model import load_local_models_config
710
+
711
+ roles = load_local_models_config(self.project_dir)
712
+ if not roles:
713
+ return None
714
+ return ModelRouter(roles)
715
+ except Exception: # noqa: BLE001
716
+ return None
717
+
658
718
  # ── Event plumbing ────────────────────────────────────────────────
659
719
 
660
720
  def _default_emit_event(self, **kwargs: Any) -> None: