specsmith 0.6.0.dev232__tar.gz → 0.6.0.dev234__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.6.0.dev232/src/specsmith.egg-info → specsmith-0.6.0.dev234}/PKG-INFO +6 -1
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/pyproject.toml +12 -1
- specsmith-0.6.0.dev234/src/specsmith/agent/voice.py +135 -0
- specsmith-0.6.0.dev234/src/specsmith/block_export.py +106 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/cli.py +295 -87
- specsmith-0.6.0.dev234/src/specsmith/cloud_serve.py +150 -0
- specsmith-0.6.0.dev234/src/specsmith/drive.py +126 -0
- specsmith-0.6.0.dev234/src/specsmith/history_search.py +159 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/serve.py +58 -4
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234/src/specsmith.egg-info}/PKG-INFO +6 -1
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith.egg-info/SOURCES.txt +8 -1
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith.egg-info/requires.txt +7 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_phase34_completion.py +14 -18
- specsmith-0.6.0.dev234/tests/test_warp_parity.py +421 -0
- specsmith-0.6.0.dev234/tests/test_warp_parity_followup.py +368 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/LICENSE +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/README.md +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/setup.cfg +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/epistemic/__init__.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/epistemic/belief.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/epistemic/certainty.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/epistemic/failure_graph.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/epistemic/py.typed +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/epistemic/recovery.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/epistemic/session.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/epistemic/stress_tester.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/epistemic/trace.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/__init__.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/__main__.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/__init__.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/broker.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/chat_runner.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/cleanup.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/events.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/indexer.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/mcp.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/memory.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/orchestrator.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/repl.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/router.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/rules.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/safety.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/suggester.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/tools.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/agent/verifier.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/architect.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/auditor.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/auth.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/commands/__init__.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/compressor.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/config.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/console_utils.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/credit_analyzer.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/credits.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/differ.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/doctor.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/epistemic/__init__.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/epistemic/belief.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/epistemic/certainty.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/epistemic/failure_graph.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/epistemic/recovery.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/epistemic/stress_tester.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/executor.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/exporter.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/__init__.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/app.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/main_window.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/session_tab.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/theme.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/widgets/__init__.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/widgets/chat_view.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/widgets/input_bar.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/widgets/provider_bar.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/widgets/token_meter.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/widgets/tool_panel.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/widgets/update_checker.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/gui/worker.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/importer.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/integrations/__init__.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/integrations/agent_skill.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/integrations/aider.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/integrations/base.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/integrations/claude_code.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/integrations/copilot.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/integrations/cursor.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/integrations/gemini.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/integrations/windsurf.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/languages.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/ledger.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/patent.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/phase.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/plugins.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/profiles.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/rate_limits.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/releaser.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/requirements.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/requirements_parser.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/retrieval.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/scaffolder.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/session.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/skills.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/agents.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/community/contributing.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/community/license-MIT.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/community/security.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/editorconfig.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/gitattributes.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/gitignore.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/go/go.mod.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/go/main.go.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/governance/roles.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/governance/rules.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/governance/verification.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/js/package.json.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/ledger.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/python/cli.py.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/python/init.py.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/readme.md.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/rust/main.rs.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/tool_installer.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/toolrules.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/tools.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/trace.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/updater.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/upgrader.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/validator.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/vcs/__init__.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/vcs/base.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/vcs/bitbucket.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/vcs/github.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/vcs/gitlab.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/vcs_commands.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/wireframes.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith/workspace.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith.egg-info/dependency_links.txt +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith.egg-info/entry_points.txt +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/src/specsmith.egg-info/top_level.txt +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_CMD_001.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_auditor.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_chat_diff_decision.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_chat_stdin_protocol.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_cli.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_cli_workflows_history_drive.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_compressor.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_e2e_nexus.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_epistemic.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_importer.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_integrations.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_mcp_client.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_nexus.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_phase1_4_new.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_rate_limits.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_scaffolder.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_skill_marketplace.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_smoke.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_suggester.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_tools.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_validator.py +0 -0
- {specsmith-0.6.0.dev232 → specsmith-0.6.0.dev234}/tests/test_vcs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specsmith
|
|
3
|
-
Version: 0.6.0.
|
|
3
|
+
Version: 0.6.0.dev234
|
|
4
4
|
Summary: Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands.
|
|
5
5
|
Author: BitConcepts
|
|
6
6
|
License-Expression: MIT
|
|
@@ -53,6 +53,11 @@ Provides-Extra: gui
|
|
|
53
53
|
Requires-Dist: PySide6>=6.6; extra == "gui"
|
|
54
54
|
Provides-Extra: ag2
|
|
55
55
|
Requires-Dist: ag2[ollama]; extra == "ag2"
|
|
56
|
+
Provides-Extra: history-semantic
|
|
57
|
+
Requires-Dist: sentence-transformers>=2.2; extra == "history-semantic"
|
|
58
|
+
Requires-Dist: numpy>=1.24; extra == "history-semantic"
|
|
59
|
+
Provides-Extra: voice
|
|
60
|
+
Requires-Dist: whisper-cpp-python>=0.2; extra == "voice"
|
|
56
61
|
Provides-Extra: agent
|
|
57
62
|
Requires-Dist: anthropic>=0.56; extra == "agent"
|
|
58
63
|
Requires-Dist: openai>=1.0; extra == "agent"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specsmith"
|
|
7
|
-
version = "0.6.0.
|
|
7
|
+
version = "0.6.0.dev234"
|
|
8
8
|
description = "Applied Epistemic Engineering toolkit — AEE agent sessions, execution profiles, FPGA/HDL governance, tool installer, 50+ CLI commands."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -67,6 +67,11 @@ mistral = ["openai>=1.0"] # Mistral uses the openai SDK pointed at api.mistral.
|
|
|
67
67
|
gui = ["PySide6>=6.6"]
|
|
68
68
|
# AG2 agent shell (Planner/Builder/Verifier over Ollama)
|
|
69
69
|
ag2 = ["ag2[ollama]"]
|
|
70
|
+
# Optional semantic backend for `specsmith history search --semantic` (REQ-135).
|
|
71
|
+
# Falls back gracefully to keyword matching if these are not installed.
|
|
72
|
+
history-semantic = ["sentence-transformers>=2.2", "numpy>=1.24"]
|
|
73
|
+
# Optional whisper-cpp wrapper for the voice agent input (REQ-141).
|
|
74
|
+
voice = ["whisper-cpp-python>=0.2"]
|
|
70
75
|
# Install all optional LLM providers
|
|
71
76
|
agent = ["anthropic>=0.56", "openai>=1.0"]
|
|
72
77
|
# Convenience bundle: everything
|
|
@@ -138,6 +143,12 @@ module = [
|
|
|
138
143
|
"yaml.*",
|
|
139
144
|
"keyring", # optional OS credential store; stubs not published
|
|
140
145
|
"keyring.*",
|
|
146
|
+
"numpy", # optional [history-semantic] extra (REQ-135)
|
|
147
|
+
"numpy.*",
|
|
148
|
+
"sentence_transformers", # optional [history-semantic] extra (REQ-135)
|
|
149
|
+
"sentence_transformers.*",
|
|
150
|
+
"whisper_cpp_python", # optional [voice] extra (REQ-141)
|
|
151
|
+
"whisper_cpp_python.*",
|
|
141
152
|
]
|
|
142
153
|
ignore_missing_imports = true
|
|
143
154
|
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 BitConcepts, LLC. All rights reserved.
|
|
3
|
+
"""Voice transcription wrapper (REQ-141).
|
|
4
|
+
|
|
5
|
+
Wraps the optional ``whisper_cpp_python`` library so the rest of specsmith
|
|
6
|
+
can call ``transcribe(path)`` without caring whether the extra is
|
|
7
|
+
installed. When the library is missing, ``transcribe`` raises
|
|
8
|
+
``VoiceUnavailableError`` with a friendly install hint so the caller can
|
|
9
|
+
surface it to the user.
|
|
10
|
+
|
|
11
|
+
The wrapper supports three modes:
|
|
12
|
+
|
|
13
|
+
* **real** -- ``whisper_cpp_python`` is installed and a model file is
|
|
14
|
+
available (auto-located under ``~/.specsmith/voice/`` or pointed to via
|
|
15
|
+
``SPECSMITH_VOICE_MODEL``). Real audio decoding.
|
|
16
|
+
* **stub** -- ``SPECSMITH_VOICE_STUB=<text>`` is set. Returns the literal
|
|
17
|
+
text without touching the audio file. Used by tests and CI so we don't
|
|
18
|
+
need to ship a 500MB model file.
|
|
19
|
+
* **unavailable** -- neither of the above. ``transcribe`` raises.
|
|
20
|
+
|
|
21
|
+
The CLI exposes this as ``specsmith voice transcribe <wav>``.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import os
|
|
27
|
+
from dataclasses import dataclass
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class VoiceUnavailableError(RuntimeError):
|
|
32
|
+
"""Raised when whisper-cpp is not installed and no stub is set."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class TranscribeResult:
|
|
37
|
+
text: str
|
|
38
|
+
backend: str # 'whisper-cpp', 'stub', 'unavailable'
|
|
39
|
+
model: str = ""
|
|
40
|
+
duration_s: float = 0.0
|
|
41
|
+
|
|
42
|
+
def to_dict(self) -> dict[str, object]:
|
|
43
|
+
return {
|
|
44
|
+
"text": self.text,
|
|
45
|
+
"backend": self.backend,
|
|
46
|
+
"model": self.model,
|
|
47
|
+
"duration_s": round(self.duration_s, 3),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def default_model_dir() -> Path:
|
|
52
|
+
return Path.home() / ".specsmith" / "voice"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _resolve_model_path() -> Path | None:
|
|
56
|
+
"""Return the on-disk model path, or None if no model is configured."""
|
|
57
|
+
env = os.environ.get("SPECSMITH_VOICE_MODEL", "").strip()
|
|
58
|
+
if env:
|
|
59
|
+
p = Path(env)
|
|
60
|
+
if p.is_file():
|
|
61
|
+
return p
|
|
62
|
+
# Auto-locate the smallest .bin under ~/.specsmith/voice/
|
|
63
|
+
voice_dir = default_model_dir()
|
|
64
|
+
if voice_dir.is_dir():
|
|
65
|
+
candidates = sorted(voice_dir.glob("*.bin"), key=lambda x: x.stat().st_size)
|
|
66
|
+
if candidates:
|
|
67
|
+
return candidates[0]
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def is_available() -> bool:
|
|
72
|
+
"""Cheap probe: True iff transcription would succeed without raising."""
|
|
73
|
+
if os.environ.get("SPECSMITH_VOICE_STUB", "").strip():
|
|
74
|
+
return True
|
|
75
|
+
try:
|
|
76
|
+
import whisper_cpp_python # noqa: F401 (presence-only check)
|
|
77
|
+
except Exception: # noqa: BLE001
|
|
78
|
+
return False
|
|
79
|
+
return _resolve_model_path() is not None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def transcribe(path: Path) -> TranscribeResult:
|
|
83
|
+
"""Transcribe a wav/flac/mp3 file to text.
|
|
84
|
+
|
|
85
|
+
Order of resolution:
|
|
86
|
+
1. If ``SPECSMITH_VOICE_STUB`` is set, return its value verbatim. This
|
|
87
|
+
lets tests run without a model file.
|
|
88
|
+
2. Otherwise import ``whisper_cpp_python`` and run a real transcription.
|
|
89
|
+
3. If neither is available, raise :class:`VoiceUnavailableError` with
|
|
90
|
+
an actionable message.
|
|
91
|
+
"""
|
|
92
|
+
import time as _time
|
|
93
|
+
|
|
94
|
+
if not path.exists():
|
|
95
|
+
raise FileNotFoundError(f"audio file not found: {path}")
|
|
96
|
+
|
|
97
|
+
stub = os.environ.get("SPECSMITH_VOICE_STUB", "").strip()
|
|
98
|
+
if stub:
|
|
99
|
+
return TranscribeResult(text=stub, backend="stub")
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
import whisper_cpp_python
|
|
103
|
+
except Exception as exc: # noqa: BLE001
|
|
104
|
+
raise VoiceUnavailableError(
|
|
105
|
+
"whisper-cpp-python is not installed. Run "
|
|
106
|
+
"`pipx inject specsmith whisper-cpp-python` "
|
|
107
|
+
"(or `pip install specsmith[voice]`)."
|
|
108
|
+
) from exc
|
|
109
|
+
|
|
110
|
+
model_path = _resolve_model_path()
|
|
111
|
+
if model_path is None:
|
|
112
|
+
raise VoiceUnavailableError(
|
|
113
|
+
"No whisper model found. Set SPECSMITH_VOICE_MODEL or place a "
|
|
114
|
+
f".bin model under {default_model_dir()}."
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
start = _time.perf_counter()
|
|
118
|
+
whisper = whisper_cpp_python.Whisper(model_path=str(model_path))
|
|
119
|
+
out = whisper.transcribe(str(path))
|
|
120
|
+
text = out.get("text") if isinstance(out, dict) else str(out)
|
|
121
|
+
return TranscribeResult(
|
|
122
|
+
text=str(text or "").strip(),
|
|
123
|
+
backend="whisper-cpp",
|
|
124
|
+
model=model_path.name,
|
|
125
|
+
duration_s=_time.perf_counter() - start,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
__all__ = [
|
|
130
|
+
"TranscribeResult",
|
|
131
|
+
"VoiceUnavailableError",
|
|
132
|
+
"default_model_dir",
|
|
133
|
+
"is_available",
|
|
134
|
+
"transcribe",
|
|
135
|
+
]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 BitConcepts, LLC. All rights reserved.
|
|
3
|
+
"""Per-block share/export for `specsmith chat` (REQ-134).
|
|
4
|
+
|
|
5
|
+
Reads ``.specsmith/sessions/<session_id>/events.jsonl`` (the chat replay log
|
|
6
|
+
or, fallback, ``turns.jsonl``) and slices a single block out as a
|
|
7
|
+
self-contained Markdown / JSON / HTML snippet.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import html
|
|
13
|
+
import json
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _events_path(project_dir: Path, session_id: str) -> Path | None:
|
|
19
|
+
base = project_dir / ".specsmith" / "sessions" / session_id
|
|
20
|
+
candidates = [
|
|
21
|
+
base / "events.jsonl",
|
|
22
|
+
base / "turns.jsonl",
|
|
23
|
+
]
|
|
24
|
+
for c in candidates:
|
|
25
|
+
if c.is_file():
|
|
26
|
+
return c
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _read_events(events_path: Path) -> list[dict[str, Any]]:
|
|
31
|
+
out: list[dict[str, Any]] = []
|
|
32
|
+
for line in events_path.read_text(encoding="utf-8").splitlines():
|
|
33
|
+
line = line.strip()
|
|
34
|
+
if not line:
|
|
35
|
+
continue
|
|
36
|
+
try:
|
|
37
|
+
obj = json.loads(line)
|
|
38
|
+
except ValueError:
|
|
39
|
+
continue
|
|
40
|
+
if isinstance(obj, dict):
|
|
41
|
+
out.append(obj)
|
|
42
|
+
return out
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def slice_block(events: list[dict[str, Any]], block_id: str) -> list[dict[str, Any]]:
|
|
46
|
+
"""Return all events tagged with ``block_id``, plus the bracketing
|
|
47
|
+
block_start/block_complete events that defined it.
|
|
48
|
+
"""
|
|
49
|
+
out: list[dict[str, Any]] = []
|
|
50
|
+
for evt in events:
|
|
51
|
+
if evt.get("block_id") == block_id or evt.get("id") == block_id:
|
|
52
|
+
out.append(evt)
|
|
53
|
+
return out
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def export_block(
|
|
57
|
+
project_dir: Path,
|
|
58
|
+
session_id: str,
|
|
59
|
+
block_id: str,
|
|
60
|
+
*,
|
|
61
|
+
fmt: str = "md",
|
|
62
|
+
) -> str:
|
|
63
|
+
"""Export the events for ``block_id`` as a string in ``fmt``.
|
|
64
|
+
|
|
65
|
+
Raises FileNotFoundError if no session log exists.
|
|
66
|
+
Raises KeyError if the block is not found.
|
|
67
|
+
"""
|
|
68
|
+
events_path = _events_path(project_dir, session_id)
|
|
69
|
+
if events_path is None:
|
|
70
|
+
raise FileNotFoundError(f"No session log for {session_id} in {project_dir}")
|
|
71
|
+
events = _read_events(events_path)
|
|
72
|
+
matching = slice_block(events, block_id)
|
|
73
|
+
if not matching:
|
|
74
|
+
raise KeyError(f"block_id {block_id} not found in session {session_id}")
|
|
75
|
+
if fmt == "json":
|
|
76
|
+
return json.dumps(matching, indent=2)
|
|
77
|
+
if fmt == "html":
|
|
78
|
+
rows = "".join(
|
|
79
|
+
f"<li><pre>{html.escape(json.dumps(evt, indent=2))}</pre></li>" for evt in matching
|
|
80
|
+
)
|
|
81
|
+
return (
|
|
82
|
+
f"<!DOCTYPE html><html><head><meta charset='utf-8'>"
|
|
83
|
+
f"<title>specsmith block {html.escape(block_id)}</title></head>"
|
|
84
|
+
f"<body><h1>Block {html.escape(block_id)}</h1>"
|
|
85
|
+
f"<p>session {html.escape(session_id)}</p><ol>{rows}</ol></body></html>"
|
|
86
|
+
)
|
|
87
|
+
# default: markdown
|
|
88
|
+
lines: list[str] = [
|
|
89
|
+
f"# Block `{block_id}`",
|
|
90
|
+
f"_session_: `{session_id}`",
|
|
91
|
+
"",
|
|
92
|
+
]
|
|
93
|
+
for evt in matching:
|
|
94
|
+
kind = str(evt.get("type", "event"))
|
|
95
|
+
lines.append(f"## {kind}")
|
|
96
|
+
if "text" in evt:
|
|
97
|
+
lines.append(str(evt["text"]))
|
|
98
|
+
else:
|
|
99
|
+
lines.append("```json")
|
|
100
|
+
lines.append(json.dumps(evt, indent=2))
|
|
101
|
+
lines.append("```")
|
|
102
|
+
lines.append("")
|
|
103
|
+
return "\n".join(lines)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
__all__ = ["export_block", "slice_block"]
|