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.
- {specsmith-0.16.5.dev630/src/specsmith.egg-info → specsmith-0.16.5.dev640}/PKG-INFO +8 -3
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/README.md +7 -2
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/pyproject.toml +3 -1
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/broker.py +18 -3
- specsmith-0.16.5.dev640/src/specsmith/agent/model_router.py +242 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/runner.py +66 -6
- specsmith-0.16.5.dev640/src/specsmith/architect.py +902 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/auditor.py +84 -2
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/cli.py +1285 -93
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/esdb/__init__.py +68 -1
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/governance_logic.py +25 -4
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/governance_yaml.py +34 -2
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/instinct.py +44 -6
- specsmith-0.16.5.dev640/src/specsmith/local_model.py +435 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/__init__.py +2 -0
- specsmith-0.16.5.dev640/src/specsmith/migrations/m007_yaml_first.py +148 -0
- specsmith-0.16.5.dev640/src/specsmith/project_metrics.py +353 -0
- specsmith-0.16.5.dev640/src/specsmith/quality_report.py +568 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/scaffolder.py +43 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/session_init.py +55 -6
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/governance.py +90 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/software_engineering.py +174 -0
- specsmith-0.16.5.dev640/src/specsmith/skills/specsmith_skills.py +29 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/sync.py +30 -13
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/agents.md.j2 +11 -7
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/security.md.j2 +1 -1
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/updater.py +16 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/upgrader.py +24 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640/src/specsmith.egg-info}/PKG-INFO +8 -3
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith.egg-info/SOURCES.txt +16 -0
- specsmith-0.16.5.dev640/tests/test_architect_interview.py +388 -0
- specsmith-0.16.5.dev640/tests/test_architect_v2.py +374 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_architecture.py +1 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_auditor.py +17 -0
- specsmith-0.16.5.dev640/tests/test_cleanup_cmd.py +234 -0
- specsmith-0.16.5.dev640/tests/test_esdb_backend_switch.py +180 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_esdb_license.py +95 -1
- specsmith-0.16.5.dev640/tests/test_issues_263_264.py +240 -0
- specsmith-0.16.5.dev640/tests/test_local_model.py +471 -0
- specsmith-0.16.5.dev640/tests/test_markdown_deprecation.py +248 -0
- specsmith-0.16.5.dev640/tests/test_migration_direction.py +210 -0
- specsmith-0.16.5.dev640/tests/test_model_router.py +258 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_nexus.py +3 -1
- specsmith-0.16.5.dev640/tests/test_project_metrics.py +281 -0
- specsmith-0.16.5.dev640/tests/test_quality_report.py +245 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_typing_guardrails.py +1 -1
- specsmith-0.16.5.dev630/src/specsmith/architect.py +0 -154
- specsmith-0.16.5.dev630/src/specsmith/skills/specsmith_skills.py +0 -267
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/LICENSE +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/setup.cfg +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/belief.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/certainty.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/failure_graph.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/py.typed +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/recovery.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/session.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/stress_tester.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/epistemic/trace.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/__main__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/chat_runner.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/cleanup.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/context_seed.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/core.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/dispatch/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/dispatch/_status.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/dispatch/dag.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/dispatch/dispatcher.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/dispatch/events.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/dispatch/result.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/endpoints.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/events.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/execution_profiles.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/fallback.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/hf_leaderboard.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/hf_sync.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/indexer.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/llm_client.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/mcp.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/memory.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/model_intelligence.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/model_profiles.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/orchestrator.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/permissions.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/profiles.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/provider_registry.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/repl.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/router.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/rules.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/safety.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/spawner.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/suggester.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/teams.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/tools.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/verifier.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/agent/voice.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/approvals.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/auth.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/block_export.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/channel.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/ci_manager.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/commands/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/commands/intelligence.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/commands/issues_policy.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/commands/reporting.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compliance/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compliance/_compat.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compliance/checker.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compliance/evidence.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compliance/regulations.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compliance/reporter.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/compressor.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/config.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/console_utils.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/context_orchestrator.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/context_window.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/credit_analyzer.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/credits.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/dashboard.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/base.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/citations.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/fpd.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/odp.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/patentsview.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/pfw.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/ppubs.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/datasources/ptab.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/differ.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/doctor.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/drive.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/editor.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/epistemic/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/epistemic/belief.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/epistemic/certainty.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/epistemic/failure_graph.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/epistemic/recovery.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/epistemic/stress_tester.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/esdb/_license.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/esdb/bridge.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/esdb/sqlite_store.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/eval/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/eval/builtins.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/eval/runner.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/executor.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/exporter.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/governance_store.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/governed_pr.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/app.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/main_window.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/session_tab.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/theme.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/chat_view.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/input_bar.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/provider_bar.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/token_meter.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/tool_panel.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/widgets/update_checker.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/gui/worker.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/history_search.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/importer.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/agent_skill.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/aider.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/base.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/claude_code.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/codity.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/copilot.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/cursor.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/gemini.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/integrations/windsurf.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/issue_reporter.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/languages.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/ledger.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/mcp_generator.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/mcp_server.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/m001_governance_yaml.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/m002_agents_slim.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/m003_compliance_init.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/m004_ledger_esdb.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/m005_agent_run_tool.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/m006_session_governance.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/migrations/runner.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/ollama_cmds.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/patent.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/paths.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/phase.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/plugins.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/policy.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/profiles.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/rate_limits.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/recover.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/releaser.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/requirements.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/requirements_parser.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/retrieval.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/risk.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/safe_write.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/serve.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/session.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/session_store.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/ai_agents.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/cloud.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/corporate.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/cross_platform.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/data_engineering.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/devops.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/docs.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/embedded.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/hardware.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/mobile.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/platform_engineering.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/productivity.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/ssh.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills/web_backend.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/skills_builder.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/contributing.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/license-MIT.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/editorconfig.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/gitattributes.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/gitignore.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/go/go.mod.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/go/main.go.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/roles.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/rules.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/governance/verification.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/js/package.json.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/ledger.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/python/cli.py.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/python/init.py.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/readme.md.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/rust/main.rs.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/tool_installer.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/toolrules.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/tools.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/trace.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/transcripts.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/validator.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/vcs/__init__.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/vcs/base.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/vcs/bitbucket.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/vcs/github.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/vcs/gitlab.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/vcs_commands.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/wi_store.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/wireframes.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith/workspace.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith.egg-info/dependency_links.txt +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith.egg-info/entry_points.txt +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith.egg-info/requires.txt +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/src/specsmith.egg-info/top_level.txt +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_CMD_001.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_agent_profiles.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_agent_run_feedback.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_agent_runner_ready.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_ai_client.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_ai_intelligence.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_channel.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_chat_diff_decision.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_chat_runner_openai_compat.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_chat_stdin_protocol.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_cli.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_cli_regressions.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_cli_workflows_history_drive.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_compliance.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_compliance_governance.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_compressor.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_dispatch.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_e2e_nexus.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_endpoints_cli.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_endpoints_store.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_epistemic.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_esdb_enforcement.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_esdb_sqlite.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_esdb_verify_chain_cli.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_fallback_chain.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_golden_path.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_importer.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_init_modes.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_integrations.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_integrations_codity.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_intelligence.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_issue_policy_features.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_issue_reporter.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_mcp_client.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_mcp_server.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_new_modules.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_permissions.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_phase1_4_new.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_phase34_completion.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_rate_limits.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_reporting_commands.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_req_248_262.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_req_358_359.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_scaffolder.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_schema_migrations.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_skill_marketplace.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_skills_mcp.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_smoke.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_suggester.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_tools.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_validator.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_vcs.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_warp_parity.py +0 -0
- {specsmith-0.16.5.dev630 → specsmith-0.16.5.dev640}/tests/test_warp_parity_followup.py +0 -0
- {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.
|
|
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
|
|
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
|
|
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.
|
|
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(
|
|
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 >
|
|
117
|
-
|
|
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}
|
|
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}
|
|
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("
|
|
397
|
+
lines.append(" \u26a0 No provider available — commands will return no response.")
|
|
393
398
|
else:
|
|
394
|
-
|
|
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:
|