specsmith 0.10.1.dev250__tar.gz → 0.10.1.dev262__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.10.1.dev250/src/specsmith.egg-info → specsmith-0.10.1.dev262}/PKG-INFO +7 -1
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/pyproject.toml +12 -1
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/chat_runner.py +208 -50
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/repl.py +11 -33
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/runner.py +8 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/cli.py +243 -0
- specsmith-0.10.1.dev262/src/specsmith/commands/__init__.py +7 -0
- specsmith-0.10.1.dev262/src/specsmith/commands/ask.py +21 -0
- specsmith-0.10.1.dev262/src/specsmith/commands/commit.py +31 -0
- specsmith-0.10.1.dev262/src/specsmith/commands/context.py +17 -0
- specsmith-0.10.1.dev262/src/specsmith/commands/fix.py +19 -0
- specsmith-0.10.1.dev262/src/specsmith/commands/plan.py +19 -0
- specsmith-0.10.1.dev262/src/specsmith/commands/registry.py +94 -0
- specsmith-0.10.1.dev262/src/specsmith/commands/review.py +18 -0
- specsmith-0.10.1.dev262/src/specsmith/commands/test.py +17 -0
- specsmith-0.10.1.dev262/src/specsmith/eval/__init__.py +17 -0
- specsmith-0.10.1.dev262/src/specsmith/eval/graders.py +137 -0
- specsmith-0.10.1.dev262/src/specsmith/eval/harness.py +78 -0
- specsmith-0.10.1.dev262/src/specsmith/eval/types.py +94 -0
- specsmith-0.10.1.dev262/src/specsmith/feature_flags.py +90 -0
- specsmith-0.10.1.dev262/src/specsmith/instinct.py +172 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/__init__.py +24 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/app.py +133 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/bridge.py +227 -0
- {specsmith-0.10.1.dev250/src/specsmith/commands → specsmith-0.10.1.dev262/src/specsmith/tui/screens}/__init__.py +1 -1
- specsmith-0.10.1.dev262/src/specsmith/tui/screens/project.py +93 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/screens/session.py +170 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/screens/settings.py +100 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/themes/__init__.py +3 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/widgets/__init__.py +3 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/widgets/chat.py +132 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/widgets/command_block.py +74 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/widgets/governance_bar.py +66 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/widgets/input_bar.py +117 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/widgets/provider_bar.py +123 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/widgets/token_meter.py +37 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/widgets/tool_block.py +99 -0
- specsmith-0.10.1.dev262/src/specsmith/tui/widgets/vcs_bar.py +60 -0
- specsmith-0.10.1.dev262/src/specsmith/webui/__init__.py +29 -0
- specsmith-0.10.1.dev262/src/specsmith/webui/app.py +636 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262/src/specsmith.egg-info}/PKG-INFO +7 -1
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith.egg-info/SOURCES.txt +38 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith.egg-info/requires.txt +8 -0
- specsmith-0.10.1.dev262/tests/test_commands.py +139 -0
- specsmith-0.10.1.dev262/tests/test_eval.py +156 -0
- specsmith-0.10.1.dev262/tests/test_feature_flags.py +76 -0
- specsmith-0.10.1.dev262/tests/test_instinct.py +82 -0
- specsmith-0.10.1.dev262/tests/test_tui.py +342 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/LICENSE +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/README.md +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/setup.cfg +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/epistemic/__init__.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/epistemic/belief.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/epistemic/certainty.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/epistemic/failure_graph.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/epistemic/py.typed +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/epistemic/recovery.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/epistemic/session.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/epistemic/stress_tester.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/epistemic/trace.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/__init__.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/__main__.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/__init__.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/broker.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/cleanup.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/core.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/endpoints.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/events.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/fallback.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/indexer.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/mcp.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/memory.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/orchestrator.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/profiles.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/router.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/rules.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/safety.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/suggester.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/tools.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/verifier.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/agent/voice.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/architect.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/auditor.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/auth.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/block_export.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/compressor.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/config.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/console_utils.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/credit_analyzer.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/credits.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/differ.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/doctor.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/drive.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/epistemic/__init__.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/epistemic/belief.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/epistemic/certainty.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/epistemic/failure_graph.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/epistemic/recovery.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/epistemic/stress_tester.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/executor.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/exporter.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/__init__.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/app.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/main_window.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/session_tab.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/theme.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/widgets/__init__.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/widgets/chat_view.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/widgets/input_bar.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/widgets/provider_bar.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/widgets/token_meter.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/widgets/tool_panel.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/widgets/update_checker.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/gui/worker.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/history_search.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/importer.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/integrations/__init__.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/integrations/agent_skill.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/integrations/aider.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/integrations/base.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/integrations/claude_code.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/integrations/copilot.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/integrations/cursor.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/integrations/gemini.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/integrations/windsurf.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/languages.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/ledger.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/patent.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/phase.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/plugins.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/profiles.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/rate_limits.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/releaser.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/requirements.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/requirements_parser.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/retrieval.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/scaffolder.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/serve.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/session.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/skills.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/agents.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/community/contributing.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/community/license-MIT.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/community/security.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/editorconfig.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/gitattributes.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/gitignore.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/go/go.mod.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/go/main.go.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/governance/belief-registry.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/governance/epistemic-axioms.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/governance/failure-modes.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/governance/lifecycle.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/governance/roles.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/governance/rules.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/governance/session-protocol.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/governance/uncertainty-map.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/governance/verification.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/js/package.json.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/ledger.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/python/cli.py.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/python/init.py.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/readme.md.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/rust/main.rs.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/tool_installer.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/toolrules.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/tools.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/trace.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/updater.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/upgrader.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/validator.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/vcs/__init__.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/vcs/base.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/vcs/bitbucket.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/vcs/github.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/vcs/gitlab.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/vcs_commands.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/wireframes.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith/workspace.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith.egg-info/dependency_links.txt +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith.egg-info/entry_points.txt +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/src/specsmith.egg-info/top_level.txt +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_CMD_001.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_agent_profiles.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_agent_runner_ready.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_auditor.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_chat_diff_decision.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_chat_runner_openai_compat.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_chat_stdin_protocol.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_cli.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_cli_workflows_history_drive.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_compressor.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_e2e_nexus.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_endpoints_cli.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_endpoints_store.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_epistemic.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_fallback_chain.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_importer.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_integrations.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_mcp_client.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_nexus.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_phase1_4_new.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_phase34_completion.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_rate_limits.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_scaffolder.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_skill_marketplace.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_smoke.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_suggester.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_tools.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_validator.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_vcs.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_warp_parity.py +0 -0
- {specsmith-0.10.1.dev250 → specsmith-0.10.1.dev262}/tests/test_warp_parity_followup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specsmith
|
|
3
|
-
Version: 0.10.1.
|
|
3
|
+
Version: 0.10.1.dev262
|
|
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
|
|
@@ -38,6 +38,8 @@ Requires-Dist: ruff>=0.4; extra == "dev"
|
|
|
38
38
|
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
39
39
|
Requires-Dist: pre-commit>=3.0; extra == "dev"
|
|
40
40
|
Requires-Dist: types-pyyaml>=6.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest-textual-snapshot>=1.0; extra == "dev"
|
|
41
43
|
Provides-Extra: docs
|
|
42
44
|
Requires-Dist: mkdocs>=1.6; extra == "docs"
|
|
43
45
|
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
|
|
@@ -51,6 +53,10 @@ Provides-Extra: mistral
|
|
|
51
53
|
Requires-Dist: openai>=1.0; extra == "mistral"
|
|
52
54
|
Provides-Extra: gui
|
|
53
55
|
Requires-Dist: PySide6>=6.6; extra == "gui"
|
|
56
|
+
Provides-Extra: tui
|
|
57
|
+
Requires-Dist: textual>=3.0; extra == "tui"
|
|
58
|
+
Provides-Extra: webui
|
|
59
|
+
Requires-Dist: nicegui>=3.0; extra == "webui"
|
|
54
60
|
Provides-Extra: ag2
|
|
55
61
|
Requires-Dist: ag2[ollama]; extra == "ag2"
|
|
56
62
|
Provides-Extra: history-semantic
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specsmith"
|
|
7
|
-
version = "0.10.1.
|
|
7
|
+
version = "0.10.1.dev262"
|
|
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"
|
|
@@ -51,6 +51,8 @@ dev = [
|
|
|
51
51
|
"mypy>=1.10",
|
|
52
52
|
"pre-commit>=3.0",
|
|
53
53
|
"types-pyyaml>=6.0",
|
|
54
|
+
"pytest-asyncio>=0.23",
|
|
55
|
+
"pytest-textual-snapshot>=1.0",
|
|
54
56
|
]
|
|
55
57
|
docs = [
|
|
56
58
|
"mkdocs>=1.6",
|
|
@@ -65,6 +67,8 @@ openai = ["openai>=1.0"]
|
|
|
65
67
|
gemini = ["google-genai>=1.0"]
|
|
66
68
|
mistral = ["openai>=1.0"] # Mistral uses the openai SDK pointed at api.mistral.ai
|
|
67
69
|
gui = ["PySide6>=6.6"]
|
|
70
|
+
tui = ["textual>=3.0"]
|
|
71
|
+
webui = ["nicegui>=3.0"]
|
|
68
72
|
# AG2 agent shell (Planner/Builder/Verifier over Ollama)
|
|
69
73
|
ag2 = ["ag2[ollama]"]
|
|
70
74
|
# Optional semantic backend for `specsmith history search --semantic` (REQ-135).
|
|
@@ -182,8 +186,15 @@ module = [
|
|
|
182
186
|
"specsmith.serve",
|
|
183
187
|
"specsmith.toolrules",
|
|
184
188
|
"specsmith.tool_installer",
|
|
189
|
+
"specsmith.commands.*",
|
|
190
|
+
"specsmith.eval.*",
|
|
191
|
+
"specsmith.feature_flags",
|
|
192
|
+
"specsmith.instinct",
|
|
193
|
+
"specsmith.tui.*",
|
|
194
|
+
"specsmith.webui.*",
|
|
185
195
|
]
|
|
186
196
|
ignore_errors = true
|
|
187
197
|
|
|
188
198
|
[tool.pytest.ini_options]
|
|
189
199
|
testpaths = ["tests"]
|
|
200
|
+
asyncio_mode = "auto"
|
|
@@ -17,6 +17,7 @@ green on machines that have no LLM configured at all.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
+
import inspect as _inspect
|
|
20
21
|
import json
|
|
21
22
|
import os
|
|
22
23
|
from dataclasses import dataclass, field
|
|
@@ -324,7 +325,7 @@ def _run_openai(
|
|
|
324
325
|
# ``stream_options.include_usage`` makes the final SSE chunk carry a
|
|
325
326
|
# populated ``usage`` block (otherwise streaming responses emit it as
|
|
326
327
|
# ``None``). Older SDK versions silently ignore unknown kwargs.
|
|
327
|
-
stream = client.chat.completions.create(
|
|
328
|
+
stream = client.chat.completions.create( # type: ignore[call-overload,unused-ignore]
|
|
328
329
|
model=os.environ.get("OPENAI_MODEL", "gpt-4o-mini"),
|
|
329
330
|
messages=messages,
|
|
330
331
|
stream=True,
|
|
@@ -344,6 +345,63 @@ def _run_openai(
|
|
|
344
345
|
return ("".join(pieces) if pieces else None), usage
|
|
345
346
|
|
|
346
347
|
|
|
348
|
+
# ---------------------------------------------------------------------------
|
|
349
|
+
# OpenAI function-calling tool definitions (generated from AVAILABLE_TOOLS)
|
|
350
|
+
# ---------------------------------------------------------------------------
|
|
351
|
+
|
|
352
|
+
_TOOL_DEFS: list[dict[str, Any]] | None = None
|
|
353
|
+
_TOOL_MAP: dict[str, Any] | None = None
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _openai_tool_defs() -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
357
|
+
"""Build OpenAI function-calling tool definitions from AVAILABLE_TOOLS.
|
|
358
|
+
|
|
359
|
+
Returns ``(definitions, name_to_func_map)``.
|
|
360
|
+
"""
|
|
361
|
+
global _TOOL_DEFS, _TOOL_MAP # noqa: PLW0603
|
|
362
|
+
if _TOOL_DEFS is not None and _TOOL_MAP is not None:
|
|
363
|
+
return _TOOL_DEFS, _TOOL_MAP
|
|
364
|
+
|
|
365
|
+
from specsmith.agent.tools import AVAILABLE_TOOLS
|
|
366
|
+
|
|
367
|
+
TYPE_MAP = {str: "string", int: "integer", float: "number", bool: "boolean"}
|
|
368
|
+
defs: list[dict[str, Any]] = []
|
|
369
|
+
fmap: dict[str, Any] = {}
|
|
370
|
+
for fn in AVAILABLE_TOOLS:
|
|
371
|
+
sig = _inspect.signature(fn)
|
|
372
|
+
props: dict[str, Any] = {}
|
|
373
|
+
required: list[str] = []
|
|
374
|
+
for pname, param in sig.parameters.items():
|
|
375
|
+
ann = param.annotation
|
|
376
|
+
# Unwrap Optional (str | None)
|
|
377
|
+
origin = getattr(ann, "__origin__", None)
|
|
378
|
+
if origin is type(str | None): # types.UnionType
|
|
379
|
+
args = [a for a in ann.__args__ if a is not type(None)]
|
|
380
|
+
ann = args[0] if args else str
|
|
381
|
+
ptype = TYPE_MAP.get(ann, "string")
|
|
382
|
+
props[pname] = {"type": ptype}
|
|
383
|
+
if param.default is _inspect.Parameter.empty:
|
|
384
|
+
required.append(pname)
|
|
385
|
+
defs.append(
|
|
386
|
+
{
|
|
387
|
+
"type": "function",
|
|
388
|
+
"function": {
|
|
389
|
+
"name": fn.__name__,
|
|
390
|
+
"description": (fn.__doc__ or "").strip().split("\n")[0],
|
|
391
|
+
"parameters": {
|
|
392
|
+
"type": "object",
|
|
393
|
+
"properties": props,
|
|
394
|
+
"required": required,
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
}
|
|
398
|
+
)
|
|
399
|
+
fmap[fn.__name__] = fn
|
|
400
|
+
_TOOL_DEFS = defs
|
|
401
|
+
_TOOL_MAP = fmap
|
|
402
|
+
return defs, fmap
|
|
403
|
+
|
|
404
|
+
|
|
347
405
|
def _run_openai_compat(
|
|
348
406
|
messages: list[dict[str, str]],
|
|
349
407
|
emitter: EventEmitter,
|
|
@@ -353,18 +411,16 @@ def _run_openai_compat(
|
|
|
353
411
|
) -> tuple[str | None, _UsageDelta]:
|
|
354
412
|
"""Stream from a user-registered OpenAI-v1-compatible endpoint (REQ-142).
|
|
355
413
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
414
|
+
Supports tool calling: sends tool definitions in the request, detects
|
|
415
|
+
tool_calls in the streaming response, executes the tools locally, and
|
|
416
|
+
loops back with results until the model produces a final text answer.
|
|
417
|
+
Capped at 20 iterations to prevent infinite loops.
|
|
360
418
|
"""
|
|
361
419
|
usage = _UsageDelta()
|
|
362
420
|
base_url = endpoint.base_url.rstrip("/")
|
|
363
421
|
url = f"{base_url}/chat/completions"
|
|
364
422
|
model = endpoint.default_model or os.environ.get("SPECSMITH_OPENAI_COMPAT_MODEL", "")
|
|
365
423
|
if not model:
|
|
366
|
-
# The endpoint did not pin a default model and the env override is
|
|
367
|
-
# absent. We cannot fabricate one; fall back to the auto-detect chain.
|
|
368
424
|
return None, usage
|
|
369
425
|
|
|
370
426
|
headers: dict[str, str] = {
|
|
@@ -373,23 +429,11 @@ def _run_openai_compat(
|
|
|
373
429
|
}
|
|
374
430
|
try:
|
|
375
431
|
token = endpoint.resolve_token()
|
|
376
|
-
except Exception: # noqa: BLE001
|
|
432
|
+
except Exception: # noqa: BLE001
|
|
377
433
|
return None, usage
|
|
378
434
|
if token:
|
|
379
435
|
headers["Authorization"] = f"Bearer {token}"
|
|
380
436
|
|
|
381
|
-
body = json.dumps(
|
|
382
|
-
{
|
|
383
|
-
"model": model,
|
|
384
|
-
"messages": messages,
|
|
385
|
-
"stream": True,
|
|
386
|
-
# Many vLLM/llama.cpp builds honour OpenAI's stream_options;
|
|
387
|
-
# the request is harmless if they don't.
|
|
388
|
-
"stream_options": {"include_usage": True},
|
|
389
|
-
}
|
|
390
|
-
).encode("utf-8")
|
|
391
|
-
req = Request(url, data=body, headers=headers, method="POST") # noqa: S310 - user-supplied
|
|
392
|
-
|
|
393
437
|
ctx = None
|
|
394
438
|
if not endpoint.verify_tls and url.startswith("https://"):
|
|
395
439
|
import ssl
|
|
@@ -398,37 +442,151 @@ def _run_openai_compat(
|
|
|
398
442
|
ctx.check_hostname = False
|
|
399
443
|
ctx.verify_mode = ssl.CERT_NONE
|
|
400
444
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
445
|
+
tool_defs, tool_map = _openai_tool_defs()
|
|
446
|
+
# Use a mutable copy of messages for the tool-call loop
|
|
447
|
+
loop_messages: list[dict[str, Any]] = [dict(m) for m in messages]
|
|
448
|
+
all_text: list[str] = []
|
|
449
|
+
MAX_TOOL_ITERS = 20
|
|
450
|
+
|
|
451
|
+
for _loop in range(MAX_TOOL_ITERS):
|
|
452
|
+
body = json.dumps(
|
|
453
|
+
{
|
|
454
|
+
"model": model,
|
|
455
|
+
"messages": loop_messages,
|
|
456
|
+
"stream": True,
|
|
457
|
+
"stream_options": {"include_usage": True},
|
|
458
|
+
"tools": tool_defs,
|
|
459
|
+
}
|
|
460
|
+
).encode("utf-8")
|
|
461
|
+
req = Request(url, data=body, headers=headers, method="POST") # noqa: S310
|
|
462
|
+
|
|
463
|
+
pieces: list[str] = []
|
|
464
|
+
# Accumulate tool_calls fragments from the stream
|
|
465
|
+
tc_accum: dict[int, dict[str, str]] = {} # index → {id, name, arguments}
|
|
466
|
+
finish_reason = ""
|
|
467
|
+
|
|
468
|
+
try:
|
|
469
|
+
with urlopen(req, timeout=120, context=ctx) as resp: # noqa: S310
|
|
470
|
+
for raw_line in resp:
|
|
471
|
+
line = raw_line.decode("utf-8", errors="replace").rstrip("\n\r")
|
|
472
|
+
if not line.startswith("data:"):
|
|
473
|
+
continue
|
|
474
|
+
payload = line[len("data:") :].strip()
|
|
475
|
+
if not payload or payload == "[DONE]":
|
|
476
|
+
if payload == "[DONE]":
|
|
477
|
+
break
|
|
478
|
+
continue
|
|
479
|
+
try:
|
|
480
|
+
obj = json.loads(payload)
|
|
481
|
+
except ValueError:
|
|
482
|
+
continue
|
|
483
|
+
choices = obj.get("choices") or []
|
|
484
|
+
usage_obj = obj.get("usage")
|
|
485
|
+
if usage_obj:
|
|
486
|
+
usage.tokens_in += int(usage_obj.get("prompt_tokens") or 0)
|
|
487
|
+
usage.tokens_out += int(usage_obj.get("completion_tokens") or 0)
|
|
488
|
+
if not choices:
|
|
489
|
+
continue
|
|
490
|
+
choice = choices[0] or {}
|
|
491
|
+
delta = choice.get("delta") or {}
|
|
492
|
+
fr = choice.get("finish_reason") or ""
|
|
493
|
+
if fr:
|
|
494
|
+
finish_reason = fr
|
|
495
|
+
# Text content
|
|
496
|
+
chunk = str(delta.get("content") or "")
|
|
497
|
+
if chunk:
|
|
498
|
+
emitter.emit({"type": "llm_chunk", "text": chunk})
|
|
499
|
+
pieces.append(chunk)
|
|
500
|
+
# Tool call fragments
|
|
501
|
+
for tc_delta in delta.get("tool_calls") or []:
|
|
502
|
+
idx = tc_delta.get("index", 0)
|
|
503
|
+
if idx not in tc_accum:
|
|
504
|
+
tc_accum[idx] = {
|
|
505
|
+
"id": tc_delta.get("id", f"call_{idx}"),
|
|
506
|
+
"name": "",
|
|
507
|
+
"arguments": "",
|
|
508
|
+
}
|
|
509
|
+
fn = tc_delta.get("function") or {}
|
|
510
|
+
if fn.get("name"):
|
|
511
|
+
tc_accum[idx]["name"] = fn["name"]
|
|
512
|
+
if fn.get("arguments"):
|
|
513
|
+
tc_accum[idx]["arguments"] += fn["arguments"]
|
|
514
|
+
except (URLError, TimeoutError, OSError):
|
|
515
|
+
return None, usage
|
|
516
|
+
|
|
517
|
+
text_this_turn = "".join(pieces)
|
|
518
|
+
if text_this_turn:
|
|
519
|
+
all_text.append(text_this_turn)
|
|
520
|
+
|
|
521
|
+
# If the model didn't call any tools, we're done
|
|
522
|
+
if finish_reason != "tool_calls" and not tc_accum:
|
|
523
|
+
break
|
|
524
|
+
|
|
525
|
+
# Execute tool calls
|
|
526
|
+
assistant_msg: dict[str, Any] = {
|
|
527
|
+
"role": "assistant",
|
|
528
|
+
"content": text_this_turn or None,
|
|
529
|
+
"tool_calls": [],
|
|
530
|
+
}
|
|
531
|
+
tool_result_msgs: list[dict[str, Any]] = []
|
|
532
|
+
|
|
533
|
+
for idx in sorted(tc_accum):
|
|
534
|
+
tc = tc_accum[idx]
|
|
535
|
+
name = tc["name"]
|
|
536
|
+
call_id = tc["id"]
|
|
537
|
+
try:
|
|
538
|
+
args = json.loads(tc["arguments"]) if tc["arguments"] else {}
|
|
539
|
+
except ValueError:
|
|
540
|
+
args = {}
|
|
541
|
+
|
|
542
|
+
assistant_msg["tool_calls"].append(
|
|
543
|
+
{
|
|
544
|
+
"id": call_id,
|
|
545
|
+
"type": "function",
|
|
546
|
+
"function": {"name": name, "arguments": tc["arguments"]},
|
|
547
|
+
}
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
# Emit tool_started
|
|
551
|
+
emitter.emit({"type": "tool_started", "name": name, "args": args})
|
|
552
|
+
|
|
553
|
+
# Execute
|
|
554
|
+
fn = tool_map.get(name)
|
|
555
|
+
is_error = False
|
|
556
|
+
if fn:
|
|
413
557
|
try:
|
|
414
|
-
|
|
415
|
-
except
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
558
|
+
result_text = fn(**args)
|
|
559
|
+
except Exception as exc: # noqa: BLE001
|
|
560
|
+
result_text = f"[ERROR] {exc}"
|
|
561
|
+
is_error = True
|
|
562
|
+
else:
|
|
563
|
+
result_text = f"[Unknown tool: {name}]"
|
|
564
|
+
is_error = True
|
|
565
|
+
|
|
566
|
+
# Emit tool_finished
|
|
567
|
+
emitter.emit(
|
|
568
|
+
{
|
|
569
|
+
"type": "tool_finished",
|
|
570
|
+
"name": name,
|
|
571
|
+
"result": str(result_text)[:4000],
|
|
572
|
+
"is_error": is_error,
|
|
573
|
+
"args": args,
|
|
574
|
+
}
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
tool_result_msgs.append(
|
|
578
|
+
{
|
|
579
|
+
"role": "tool",
|
|
580
|
+
"tool_call_id": call_id,
|
|
581
|
+
"content": str(result_text)[:8000],
|
|
582
|
+
}
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
# Append assistant + tool results and loop for next turn
|
|
586
|
+
loop_messages.append(assistant_msg)
|
|
587
|
+
loop_messages.extend(tool_result_msgs)
|
|
588
|
+
|
|
589
|
+
return ("".join(all_text) if all_text else None), usage
|
|
432
590
|
|
|
433
591
|
|
|
434
592
|
def _run_gemini(
|
|
@@ -10,9 +10,13 @@ from specsmith.agent.broker import (
|
|
|
10
10
|
run_preflight,
|
|
11
11
|
)
|
|
12
12
|
from specsmith.agent.orchestrator import Orchestrator
|
|
13
|
+
from specsmith.commands.registry import available_commands
|
|
14
|
+
from specsmith.commands.registry import dispatch as dispatch_command
|
|
13
15
|
|
|
14
16
|
NEXUS_BANNER = "Nexus — Local-first Agentic Development Environment (Specsmith-governed)"
|
|
15
17
|
|
|
18
|
+
_REGISTERED_COMMANDS = set(available_commands())
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
def main():
|
|
18
22
|
print(NEXUS_BANNER)
|
|
@@ -27,7 +31,8 @@ def main():
|
|
|
27
31
|
|
|
28
32
|
print(
|
|
29
33
|
"Agents ready. Type plain English to use the natural-language broker, "
|
|
30
|
-
"or use slash commands (/plan, /ask, /fix, /
|
|
34
|
+
"or use slash commands (/plan, /ask, /fix, /test, /review, /context, "
|
|
35
|
+
"/commit, /pr, /undo, /why, /exit). "
|
|
31
36
|
"Toggle governance details with /why."
|
|
32
37
|
)
|
|
33
38
|
|
|
@@ -59,38 +64,11 @@ def main():
|
|
|
59
64
|
print(f"Governance details: {state}")
|
|
60
65
|
continue
|
|
61
66
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
elif command == "/ask":
|
|
69
|
-
if not args:
|
|
70
|
-
print("Usage: /ask <question>")
|
|
71
|
-
continue
|
|
72
|
-
orchestrator.run_task(f"[ASK] Clarify intent and answer the following question: {args}")
|
|
73
|
-
|
|
74
|
-
elif command == "/fix":
|
|
75
|
-
if not args:
|
|
76
|
-
print("Usage: /fix <issue description or file>")
|
|
77
|
-
continue
|
|
78
|
-
orchestrator.run_task(f"[FIX] Modify code to fix the following issue: {args}")
|
|
79
|
-
|
|
80
|
-
elif command == "/test":
|
|
81
|
-
orchestrator.run_task(f"[TEST] Run tests for the project. {args}")
|
|
82
|
-
|
|
83
|
-
elif command == "/commit":
|
|
84
|
-
orchestrator.run_task(f"[COMMIT] Create a git commit for the current changes. {args}")
|
|
85
|
-
|
|
86
|
-
elif command == "/pr":
|
|
87
|
-
orchestrator.run_task(f"[PR] Prepare a pull request for the current branch. {args}")
|
|
88
|
-
|
|
89
|
-
elif command == "/undo":
|
|
90
|
-
orchestrator.run_task(f"[UNDO] Revert the last action or commit. {args}")
|
|
91
|
-
|
|
92
|
-
elif command == "/context":
|
|
93
|
-
orchestrator.run_task(f"[CONTEXT] Show repo knowledge and search context for: {args}")
|
|
67
|
+
# Dispatch registered slash commands through the registry.
|
|
68
|
+
if command in _REGISTERED_COMMANDS:
|
|
69
|
+
cmd_result = dispatch_command(command, args, project_dir, orchestrator)
|
|
70
|
+
print(cmd_result.output)
|
|
71
|
+
continue
|
|
94
72
|
|
|
95
73
|
else:
|
|
96
74
|
# Default mode (REQ-084 + REQ-086): plain English flows through the
|
|
@@ -188,6 +188,13 @@ class AgentRunner:
|
|
|
188
188
|
"""
|
|
189
189
|
version = self._package_version()
|
|
190
190
|
if self.json_events:
|
|
191
|
+
# Report tool count so the VS Code extension can show "(N tools)"
|
|
192
|
+
try:
|
|
193
|
+
from specsmith.agent.tools import AVAILABLE_TOOLS as _AT
|
|
194
|
+
|
|
195
|
+
_tool_count = len(_AT)
|
|
196
|
+
except Exception: # noqa: BLE001
|
|
197
|
+
_tool_count = 0
|
|
191
198
|
self._emitter.ready(
|
|
192
199
|
agent="nexus",
|
|
193
200
|
version=version,
|
|
@@ -197,6 +204,7 @@ class AgentRunner:
|
|
|
197
204
|
profile_id=self.profile_id or "",
|
|
198
205
|
capabilities=_capabilities(),
|
|
199
206
|
endpoint_id=self.endpoint_id or "",
|
|
207
|
+
tools=_tool_count,
|
|
200
208
|
)
|
|
201
209
|
else:
|
|
202
210
|
print(
|