gdmcode 0.1.4__tar.gz → 0.1.5__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.
- {gdmcode-0.1.4 → gdmcode-0.1.5}/PKG-INFO +1 -1
- gdmcode-0.1.5/gdmcode/__init__.py +1 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/tool_orchestrator.py +3 -11
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/cli.py +39 -5
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/integrations/mcp_server.py +1 -1
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/main.py +14 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/repl.py +79 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/__init__.py +15 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/pyproject.toml +1 -1
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_cli_smoke.py +37 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_repl_smoke.py +36 -0
- gdmcode-0.1.4/gdmcode/__init__.py +0 -1
- {gdmcode-0.1.4 → gdmcode-0.1.5}/.gitignore +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/CONTRIBUTING.md +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/README.md +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/config.toml +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/docs/agentic-runtime-audit.md +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/docs/architecture.md +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/docs/cli-reference.md +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/docs/configuration.md +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/docs/deployment.md +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/docs/plugin-guide.md +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/docs/quick-start.md +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/docs/security-hardening.md +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/_internal/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/_internal/constants.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/_internal/domain_skills.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/commit_classifier.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/context_budget.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/daemon.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/dag_validator.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/debug_loop.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/impact_analyzer.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/impact_graph.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/loop.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/orchestrator.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/regression_guard.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/review_gate.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/risk_scorer.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/self_healing.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/smart_test_selector.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/system_prompt.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/task_tracker.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/test_validator.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/transcript.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/verification_loop.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/work_director.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/agent/worktree_manager.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/artifacts/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/artifacts/artifact_store.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/artifacts/verification_graph.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/auth.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/commands.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/config.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/cost_tracker.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/db/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/db/migrations.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/enterprise/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/enterprise/audit_log.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/enterprise/identity.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/enterprise/rbac.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/enterprise/team_config.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/enterprise/usage_analytics.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/exceptions.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/git_workflow.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/integrations/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/integrations/github_actions.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/integrations/sentry_integration.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/integrations/sentry_server.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/integrations/webhook_security.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/memory/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/memory/code_index.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/memory/compressor.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/memory/context_memory.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/memory/continuous_memory.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/memory/conventions.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/memory/db.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/memory/document_index.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/memory/file_cache.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/memory/project_scanner.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/memory/session_store.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/models/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/models/client.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/models/definitions.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/models/router.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/models/schemas.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/permissions.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/remote/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/remote/command_filter.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/remote/models.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/remote/permission_handler.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/remote/phone_ui.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/remote/protocol.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/remote/qr.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/remote/server.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/remote/token_manager.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/remote/tunnel.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/runtime/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/runtime/branch_farm.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/runtime/replay.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/sandbox/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/sandbox/hermetic.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/sandbox/policy.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/sdk/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/sdk/plugin_base.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/sdk/plugin_host.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/sdk/plugin_loader.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/security.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/server/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/server/bridge.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/server/bridge_cli.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/server/bridge_client.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/server/protocol_version.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/session/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/session/event_fanout.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/session/input_broker.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/session/permission_bridge.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/_atomic.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/agent_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/ask_user_tool.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/bash_tool.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/browser_tool.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/browser_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/dep_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/document_reader.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/document_tool.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/document_writer.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/impact_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/playwright_tool.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/quality_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/read_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/result_cache.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/search_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/shell_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/tools/write_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/voice/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/voice/audio_capture.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/voice/audio_playback.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/voice/errors.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/voice/models.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/voice/providers.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/voice/vad.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/gdmcode/voice/voice_loop.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/proxy/Dockerfile +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/proxy/main.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/proxy/requirements.txt +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/remote/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/remote/test_remote_server.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_agent_loop.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_agent_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_api_fallback.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_artifact_store.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_audit_log.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_auth.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_auto_quality.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_autonomy_levels.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_bash_tool.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_batch_api.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_branch_farm.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_bridge.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_bridge_smoke.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_browser_tool_smoke.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_browser_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_btw_queue.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_budget_tracker.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_chrome_extension.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_ci_runner.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_code_index.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_commands.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_compression.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_confidence.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_config.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_continuous_memory.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_convention_drift.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_cost_tracker.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_daemon.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_daemon_stability.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_daemon_watchdog.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_db.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_debate.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_debug_loop.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_debug_loop_smoke.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_dep_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_doctor.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_document_index.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_document_reader.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_document_tool.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_document_writer.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_domain_skills.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_eval_harness.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_event_log.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_failure_taxonomy.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_file_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_git_workflow.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_github_actions.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_health.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_hermetic_sandbox.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_identity_rbac.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_impact_analysis.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_impact_analyzer.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_impact_graph.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_impact_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_injection_gate.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_leaderboard.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_local_models.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_loop.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_loop_p3.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_mcp_server.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_memory.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_migrations.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_mock_provider.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_model_config.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_orchestrator.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_package.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_permissions.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_phase2_modules.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_phone_ui.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_playwright_tool.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_plugin_sdk.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_protocol_version.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_provenance.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_proxy_server.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_quality_integration.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_reasoning_toggle.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_redaction.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_regression_collector.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_regression_guard_integration.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_regression_runner.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_replay.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_resilience.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_result_cache.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_review_gate_expanded.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_risk_scorer.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_rollback.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_router.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_router_compressor_conventions.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_router_escalation.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_sandbox.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_scoring.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_search_tools.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_self_healing.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_semantic_edit.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_sentry_integration.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_session_checkpoint.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_session_controller.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_session_restore.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_signal_handling.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_swebench_adapter.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_swebench_runner.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_system_prompt.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_team_config.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_tool_cache.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_tool_orchestrator.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_tool_timeout.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_tools_registry.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_tunnel_qr.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_usage_analytics.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_verification_graph.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_verification_loop.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_voice_loop.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_voice_providers.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_whole_codebase.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/test_work_director.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/voice/__init__.py +0 -0
- {gdmcode-0.1.4 → gdmcode-0.1.5}/tests/voice/test_audio_foundation.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.5"
|
|
@@ -25,7 +25,7 @@ from typing import Any
|
|
|
25
25
|
from gdmcode._internal.constants import _ALWAYS_DENY_TOOLS, _WRITE_TOOLS
|
|
26
26
|
from gdmcode.exceptions import ToolPermissionError
|
|
27
27
|
from gdmcode.security import AuditLogger
|
|
28
|
-
from gdmcode.tools import REGISTRY, ToolResult
|
|
28
|
+
from gdmcode.tools import REGISTRY, ToolResult, ensure_default_tools_registered
|
|
29
29
|
|
|
30
30
|
__all__ = ["ToolOrchestrator"]
|
|
31
31
|
|
|
@@ -144,15 +144,7 @@ _TOOL_CACHE: _ToolResultCache = _ToolResultCache()
|
|
|
144
144
|
|
|
145
145
|
def _ensure_tools_registered() -> None:
|
|
146
146
|
"""Import all tool modules so they register themselves with REGISTRY."""
|
|
147
|
-
|
|
148
|
-
import gdmcode.tools.read_tools # noqa: F401
|
|
149
|
-
import gdmcode.tools.search_tools # noqa: F401
|
|
150
|
-
import gdmcode.tools.shell_tools # noqa: F401
|
|
151
|
-
import gdmcode.tools.ask_user_tool # noqa: F401
|
|
152
|
-
import gdmcode.tools.write_tools # noqa: F401
|
|
153
|
-
import gdmcode.tools.dep_tools # noqa: F401
|
|
154
|
-
import gdmcode.tools.impact_tools # noqa: F401
|
|
155
|
-
import gdmcode.tools.browser_tool # noqa: F401
|
|
147
|
+
ensure_default_tools_registered()
|
|
156
148
|
|
|
157
149
|
|
|
158
150
|
_ensure_tools_registered()
|
|
@@ -399,4 +391,4 @@ class ToolOrchestrator:
|
|
|
399
391
|
"""Dispatch a registered plugin tool by its namespaced tool_name."""
|
|
400
392
|
if tool_name not in self._plugin_channels:
|
|
401
393
|
raise KeyError(f"Unknown plugin tool: {tool_name}")
|
|
402
|
-
return self._dispatch_plugin(tool_name, args)
|
|
394
|
+
return self._dispatch_plugin(tool_name, args)
|
|
@@ -287,7 +287,8 @@ def cmd_health(
|
|
|
287
287
|
GdmDatabase = _GdmDatabase
|
|
288
288
|
from gdmcode.models.definitions import PROVIDER_BASE_URLS, ModelTier, get_model
|
|
289
289
|
if REGISTRY is None:
|
|
290
|
-
from gdmcode.tools import REGISTRY as _REGISTRY
|
|
290
|
+
from gdmcode.tools import REGISTRY as _REGISTRY, ensure_default_tools_registered
|
|
291
|
+
ensure_default_tools_registered()
|
|
291
292
|
REGISTRY = _REGISTRY
|
|
292
293
|
|
|
293
294
|
@dataclass
|
|
@@ -352,8 +353,28 @@ def cmd_health(
|
|
|
352
353
|
try:
|
|
353
354
|
tools = REGISTRY.all_tools()
|
|
354
355
|
count = len(tools)
|
|
355
|
-
|
|
356
|
-
|
|
356
|
+
if count == 0:
|
|
357
|
+
return CheckResult("tools", "fail", latency_ms=int((time.monotonic() - t0) * 1000), detail="0 tools registered")
|
|
358
|
+
probe_name = "powershell" if sys.platform == "win32" else "bash"
|
|
359
|
+
probe_args = (
|
|
360
|
+
{"command": "$PWD.Path", "timeout": 5}
|
|
361
|
+
if probe_name == "powershell"
|
|
362
|
+
else {"command": "pwd", "timeout": 5, "read_only": True}
|
|
363
|
+
)
|
|
364
|
+
probe = REGISTRY.call(probe_name, probe_args)
|
|
365
|
+
if not probe.ok:
|
|
366
|
+
return CheckResult(
|
|
367
|
+
"tools",
|
|
368
|
+
"fail",
|
|
369
|
+
latency_ms=int((time.monotonic() - t0) * 1000),
|
|
370
|
+
detail=f"{count} registered; {probe_name} probe failed: {probe.error}",
|
|
371
|
+
)
|
|
372
|
+
return CheckResult(
|
|
373
|
+
"tools",
|
|
374
|
+
"ok",
|
|
375
|
+
latency_ms=int((time.monotonic() - t0) * 1000),
|
|
376
|
+
detail=f"{count} registered; {probe_name} probe ok",
|
|
377
|
+
)
|
|
357
378
|
except Exception as exc: # noqa: BLE001
|
|
358
379
|
return CheckResult("tools", "fail", detail=str(exc))
|
|
359
380
|
|
|
@@ -398,7 +419,8 @@ def cmd_health(
|
|
|
398
419
|
]
|
|
399
420
|
|
|
400
421
|
overall_fail = any(
|
|
401
|
-
r.status
|
|
422
|
+
r.status == "fail" and r.priority in ("P0", "P1")
|
|
423
|
+
or r.status == "missing" and r.priority == "P0"
|
|
402
424
|
for r in all_checks
|
|
403
425
|
)
|
|
404
426
|
|
|
@@ -1179,6 +1201,7 @@ def _run_one_shot(cfg: object, prompt: str, *, yes: bool, model_override: str |
|
|
|
1179
1201
|
"""
|
|
1180
1202
|
from rich.status import Status
|
|
1181
1203
|
from gdmcode.agent.loop import EventType
|
|
1204
|
+
from gdmcode.repl import _format_llm_error
|
|
1182
1205
|
|
|
1183
1206
|
if not _has_model_connection(cfg):
|
|
1184
1207
|
console.print("[red]No model connection configured.[/red]")
|
|
@@ -1248,10 +1271,21 @@ def _run_one_shot(cfg: object, prompt: str, *, yes: bool, model_override: str |
|
|
|
1248
1271
|
console.print(event.content)
|
|
1249
1272
|
elif event.type == EventType.ERROR:
|
|
1250
1273
|
status.stop()
|
|
1251
|
-
console.print(
|
|
1274
|
+
console.print(_format_llm_error(event.content))
|
|
1252
1275
|
had_error = True
|
|
1253
1276
|
elif event.type == EventType.TOOL_CALL:
|
|
1277
|
+
status.stop()
|
|
1254
1278
|
console.print(f"[yellow] [tool] {event.tool_name}[/yellow]")
|
|
1279
|
+
elif event.type == EventType.TOOL_RESULT:
|
|
1280
|
+
status.stop()
|
|
1281
|
+
result = event.result
|
|
1282
|
+
if result is not None and getattr(result, "error", None):
|
|
1283
|
+
console.print(f"[red] [tool error] {event.tool_name}:[/red] {getattr(result, 'error')}")
|
|
1284
|
+
had_error = True
|
|
1285
|
+
elif result is not None:
|
|
1286
|
+
output = (getattr(result, "output", "") or "").strip()
|
|
1287
|
+
if output:
|
|
1288
|
+
console.print(f"[dim]{output[:2000]}[/dim]")
|
|
1255
1289
|
elif event.type == EventType.DONE:
|
|
1256
1290
|
status.stop()
|
|
1257
1291
|
except Exception as exc: # noqa: BLE001
|
|
@@ -51,7 +51,7 @@ _INTERNAL_ERROR = -32603
|
|
|
51
51
|
class MCPServer:
|
|
52
52
|
"""Minimal MCP server with stdio transport."""
|
|
53
53
|
|
|
54
|
-
def __init__(self, name: str = "gdmcode", version: str = "0.1.
|
|
54
|
+
def __init__(self, name: str = "gdmcode", version: str = "0.1.5") -> None:
|
|
55
55
|
self._name = name
|
|
56
56
|
self._version = version
|
|
57
57
|
self._tools: dict[str, MCPTool] = {}
|
|
@@ -6,6 +6,7 @@ pyproject.toml's `gdm = "gdmcode.main:main"` script works.
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
from importlib.metadata import version as _pkg_version, PackageNotFoundError
|
|
9
|
+
import sys
|
|
9
10
|
|
|
10
11
|
from gdmcode.cli import app
|
|
11
12
|
|
|
@@ -20,8 +21,21 @@ def _get_version() -> str:
|
|
|
20
21
|
|
|
21
22
|
def main() -> None:
|
|
22
23
|
"""Entry point for the `gdm` CLI command."""
|
|
24
|
+
_configure_stdio()
|
|
23
25
|
app()
|
|
24
26
|
|
|
25
27
|
|
|
28
|
+
def _configure_stdio() -> None:
|
|
29
|
+
"""Avoid Windows cp1252 crashes when Rich output is redirected."""
|
|
30
|
+
for stream_name in ("stdout", "stderr"):
|
|
31
|
+
stream = getattr(sys, stream_name, None)
|
|
32
|
+
if stream is None or not hasattr(stream, "reconfigure"):
|
|
33
|
+
continue
|
|
34
|
+
try:
|
|
35
|
+
stream.reconfigure(encoding="utf-8", errors="replace")
|
|
36
|
+
except (OSError, ValueError):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
26
40
|
if __name__ == "__main__":
|
|
27
41
|
main()
|
|
@@ -162,6 +162,35 @@ def _format_llm_error(error: object) -> str:
|
|
|
162
162
|
return f"[red]{title}{code}.[/red]\n[dim]{escape(raw[:800])}[/dim]\n[yellow]{hint}[/yellow]"
|
|
163
163
|
|
|
164
164
|
|
|
165
|
+
def _is_connection_probe(text: str) -> bool:
|
|
166
|
+
"""Return True for tiny greetings/checks that should not enter tool mode."""
|
|
167
|
+
import re
|
|
168
|
+
|
|
169
|
+
normalized = re.sub(r"[^a-z0-9\s']", " ", text.lower()).strip()
|
|
170
|
+
normalized = re.sub(r"\s+", " ", normalized)
|
|
171
|
+
if not normalized:
|
|
172
|
+
return False
|
|
173
|
+
direct = {
|
|
174
|
+
"hey",
|
|
175
|
+
"hi",
|
|
176
|
+
"hello",
|
|
177
|
+
"yo",
|
|
178
|
+
"ping",
|
|
179
|
+
"test",
|
|
180
|
+
"hello there",
|
|
181
|
+
"hey there",
|
|
182
|
+
"hi there",
|
|
183
|
+
"are you there",
|
|
184
|
+
"you there",
|
|
185
|
+
}
|
|
186
|
+
if normalized in direct:
|
|
187
|
+
return True
|
|
188
|
+
words = normalized.split()
|
|
189
|
+
return len(words) <= 4 and words[0] in {"hey", "hi", "hello"} and all(
|
|
190
|
+
word in {"hey", "hi", "hello", "there", "gdm", "agent", "please"} for word in words
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
165
194
|
def _render_event(event: object, status: Status, console: Console) -> bool:
|
|
166
195
|
"""Render one AgentEvent to the terminal while the spinner is live."""
|
|
167
196
|
from gdmcode.agent.loop import EventType # lazy
|
|
@@ -173,6 +202,7 @@ def _render_event(event: object, status: Status, console: Console) -> bool:
|
|
|
173
202
|
label = f"[dim cyan]Thinking... {snippet}[/dim cyan]"
|
|
174
203
|
status.update(label)
|
|
175
204
|
case EventType.TOOL_CALL:
|
|
205
|
+
status.stop()
|
|
176
206
|
console.print(f"[yellow][tool] {ev.tool_name}({_fmt_args(ev.args)})[/yellow]") # type: ignore[union-attr]
|
|
177
207
|
case EventType.TOOL_RESULT:
|
|
178
208
|
result_str = str(ev.result or "")[:80] # type: ignore[union-attr]
|
|
@@ -384,6 +414,51 @@ def start_repl(cfg: "GdmConfig", db: "GdmDatabase", *, yes: bool = False, model_
|
|
|
384
414
|
console.print("[yellow]Agent loop not available in this installation.[/yellow]")
|
|
385
415
|
return False
|
|
386
416
|
|
|
417
|
+
def _run_connection_probe(text: str) -> None:
|
|
418
|
+
"""Send a tiny no-tools request to confirm the active LLM route."""
|
|
419
|
+
nonlocal cfg
|
|
420
|
+
if not _has_model_route():
|
|
421
|
+
try:
|
|
422
|
+
cfg = _reload_cfg(require_credentials=True) # type: ignore[assignment]
|
|
423
|
+
dispatcher._cfg = cfg # type: ignore[attr-defined]
|
|
424
|
+
dispatcher._provider = getattr(cfg, "provider", dispatcher._provider) # type: ignore[attr-defined]
|
|
425
|
+
except Exception as exc: # noqa: BLE001
|
|
426
|
+
_print_onboarding(console, error=str(exc))
|
|
427
|
+
return
|
|
428
|
+
|
|
429
|
+
status = Status("[cyan]Checking LLM connection...[/cyan]", console=console, spinner="dots")
|
|
430
|
+
status.start()
|
|
431
|
+
try:
|
|
432
|
+
from gdmcode.models.client import GdmClient
|
|
433
|
+
from gdmcode.models.definitions import ModelTier, get_model
|
|
434
|
+
|
|
435
|
+
tier = model_override or ModelTier.CODER
|
|
436
|
+
model_def = get_model(tier, cfg.provider)
|
|
437
|
+
proxy_active = bool(
|
|
438
|
+
getattr(cfg, "proxy_enabled", False)
|
|
439
|
+
and getattr(cfg, "proxy_url", "")
|
|
440
|
+
and getattr(cfg, "proxy_token", "")
|
|
441
|
+
)
|
|
442
|
+
client = (
|
|
443
|
+
GdmClient.for_proxy(getattr(cfg, "proxy_url"), getattr(cfg, "proxy_token"))
|
|
444
|
+
if proxy_active
|
|
445
|
+
else GdmClient(cfg)
|
|
446
|
+
)
|
|
447
|
+
response = client.complete_text(
|
|
448
|
+
text,
|
|
449
|
+
model=model_def.id,
|
|
450
|
+
max_tokens=80,
|
|
451
|
+
system=(
|
|
452
|
+
"You are gdm code's CLI connection check. Reply in one short, friendly "
|
|
453
|
+
"sentence. Confirm the LLM is connected. Do not inspect files or call tools."
|
|
454
|
+
),
|
|
455
|
+
).strip()
|
|
456
|
+
status.stop()
|
|
457
|
+
console.print(response or "[green]LLM connected.[/green]")
|
|
458
|
+
except Exception as exc: # noqa: BLE001
|
|
459
|
+
status.stop()
|
|
460
|
+
console.print(_format_llm_error(exc))
|
|
461
|
+
|
|
387
462
|
if not getattr(cfg, "gdm_quiet", False):
|
|
388
463
|
if _has_model_route():
|
|
389
464
|
console.print("[dim]Agent will initialize on first prompt.[/dim]")
|
|
@@ -587,6 +662,10 @@ def start_repl(cfg: "GdmConfig", db: "GdmDatabase", *, yes: bool = False, model_
|
|
|
587
662
|
console.print("[yellow]Restore not supported by current agent.[/yellow]")
|
|
588
663
|
continue
|
|
589
664
|
|
|
665
|
+
if _is_connection_probe(text):
|
|
666
|
+
_run_connection_probe(text)
|
|
667
|
+
continue
|
|
668
|
+
|
|
590
669
|
pending = _show_pending_btw(db, session_id, console)
|
|
591
670
|
full_message = text
|
|
592
671
|
if pending:
|
|
@@ -20,6 +20,7 @@ __all__ = [
|
|
|
20
20
|
"ToolResult",
|
|
21
21
|
"ToolRegistry",
|
|
22
22
|
"REGISTRY",
|
|
23
|
+
"ensure_default_tools_registered",
|
|
23
24
|
"BrowserTools",
|
|
24
25
|
"BrowserToolResult",
|
|
25
26
|
]
|
|
@@ -155,6 +156,20 @@ class ToolRegistry:
|
|
|
155
156
|
# Module-level singleton — all concrete tool modules import and extend this.
|
|
156
157
|
REGISTRY: ToolRegistry = ToolRegistry()
|
|
157
158
|
|
|
159
|
+
|
|
160
|
+
def ensure_default_tools_registered() -> None:
|
|
161
|
+
"""Import built-in tool modules so they register with REGISTRY."""
|
|
162
|
+
import gdmcode.tools.bash_tool # noqa: F401
|
|
163
|
+
import gdmcode.tools.read_tools # noqa: F401
|
|
164
|
+
import gdmcode.tools.search_tools # noqa: F401
|
|
165
|
+
import gdmcode.tools.shell_tools # noqa: F401
|
|
166
|
+
import gdmcode.tools.ask_user_tool # noqa: F401
|
|
167
|
+
import gdmcode.tools.write_tools # noqa: F401
|
|
168
|
+
import gdmcode.tools.dep_tools # noqa: F401
|
|
169
|
+
import gdmcode.tools.impact_tools # noqa: F401
|
|
170
|
+
import gdmcode.tools.browser_tool # noqa: F401
|
|
171
|
+
|
|
172
|
+
|
|
158
173
|
# Re-export browser tools so callers can do `from gdmcode.tools import BrowserTools`.
|
|
159
174
|
# Import after REGISTRY is defined to avoid circular imports.
|
|
160
175
|
from gdmcode.tools.browser_tools import BrowserToolResult, BrowserTools, BrowserToolsSync # noqa: E402, F401
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Smoke tests for gdm CLI entrypoints."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
from types import SimpleNamespace
|
|
4
5
|
from unittest.mock import MagicMock, patch
|
|
5
6
|
|
|
6
7
|
import pytest
|
|
@@ -57,3 +58,39 @@ class TestCliSmoke:
|
|
|
57
58
|
def test_version_shows_version_string(self) -> None:
|
|
58
59
|
result = runner.invoke(app, ["version"])
|
|
59
60
|
assert "gdm" in result.output.lower() or "0." in result.output
|
|
61
|
+
|
|
62
|
+
def test_health_bootstraps_and_executes_tools(self, tmp_path, monkeypatch) -> None:
|
|
63
|
+
import gdmcode.cli as cli
|
|
64
|
+
|
|
65
|
+
class FakeDb:
|
|
66
|
+
def __init__(self, project_root):
|
|
67
|
+
self.project_root = project_root
|
|
68
|
+
|
|
69
|
+
def execute(self, *_args, **_kwargs):
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
class FakeDaemon:
|
|
73
|
+
is_running = True
|
|
74
|
+
|
|
75
|
+
def __init__(self, db):
|
|
76
|
+
self.db = db
|
|
77
|
+
|
|
78
|
+
def pending_count(self):
|
|
79
|
+
return 0
|
|
80
|
+
|
|
81
|
+
monkeypatch.setattr(cli, "GdmDatabase", FakeDb)
|
|
82
|
+
monkeypatch.setattr(cli, "BackgroundDaemon", FakeDaemon)
|
|
83
|
+
monkeypatch.setattr(cli, "REGISTRY", None)
|
|
84
|
+
monkeypatch.setattr(
|
|
85
|
+
cli,
|
|
86
|
+
"load_config",
|
|
87
|
+
lambda: SimpleNamespace(provider="gemini", api_key="key", project_root=tmp_path),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
response = SimpleNamespace(status_code=200)
|
|
91
|
+
with patch("gdmcode.cli.httpx.get", return_value=response):
|
|
92
|
+
result = runner.invoke(app, ["health"])
|
|
93
|
+
|
|
94
|
+
assert "tools" in result.output
|
|
95
|
+
assert "registered" in result.output
|
|
96
|
+
assert "probe ok" in result.output
|
|
@@ -39,6 +39,13 @@ class TestReplHelpers:
|
|
|
39
39
|
from gdmcode.repl import _fmt_args
|
|
40
40
|
assert _fmt_args({}) == ""
|
|
41
41
|
|
|
42
|
+
def test_connection_probe_detection(self) -> None:
|
|
43
|
+
from gdmcode.repl import _is_connection_probe
|
|
44
|
+
|
|
45
|
+
assert _is_connection_probe("hey")
|
|
46
|
+
assert _is_connection_probe("hello there")
|
|
47
|
+
assert not _is_connection_probe("fix the failing tests")
|
|
48
|
+
|
|
42
49
|
|
|
43
50
|
class TestReplStartRepl:
|
|
44
51
|
"""Test start_repl runs and exits cleanly when given /exit input."""
|
|
@@ -157,6 +164,24 @@ class TestReplStartRepl:
|
|
|
157
164
|
assert "/login grok" in output
|
|
158
165
|
assert "/proxy token" in output
|
|
159
166
|
|
|
167
|
+
def test_plain_hey_with_credentials_uses_no_tool_probe(self, tmp_path, capsys) -> None:
|
|
168
|
+
from gdmcode.repl import start_repl
|
|
169
|
+
|
|
170
|
+
cfg, db = self._make_mocks(tmp_path)
|
|
171
|
+
cfg.api_key = "test-key"
|
|
172
|
+
|
|
173
|
+
with patch("gdmcode.repl._build_input_fn") as mock_build:
|
|
174
|
+
mock_build.return_value = MagicMock(side_effect=["hey", EOFError])
|
|
175
|
+
with patch("gdmcode.models.client.GdmClient.complete_text", return_value="Hey, LLM connected."):
|
|
176
|
+
with patch("gdmcode.repl._run_agent_turn") as run_agent:
|
|
177
|
+
with patch("gdmcode.cost_tracker.CostTracker"):
|
|
178
|
+
with patch("gdmcode.repl._ensure_session", return_value="session-123"):
|
|
179
|
+
start_repl(cfg, db)
|
|
180
|
+
|
|
181
|
+
output = capsys.readouterr().out
|
|
182
|
+
assert "LLM connected" in output
|
|
183
|
+
run_agent.assert_not_called()
|
|
184
|
+
|
|
160
185
|
|
|
161
186
|
class TestReplErrorFormatting:
|
|
162
187
|
def test_format_llm_error_rate_limit(self) -> None:
|
|
@@ -193,6 +218,17 @@ class TestReplErrorFormatting:
|
|
|
193
218
|
output = capsys.readouterr().out
|
|
194
219
|
assert "credit" in output.lower() or "quota" in output.lower()
|
|
195
220
|
|
|
221
|
+
def test_render_tool_call_stops_spinner_before_permission_work(self, capsys) -> None:
|
|
222
|
+
from gdmcode.agent.loop import EventType
|
|
223
|
+
from gdmcode.repl import _render_event
|
|
224
|
+
|
|
225
|
+
status = MagicMock()
|
|
226
|
+
console = MagicMock()
|
|
227
|
+
event = SimpleNamespace(type=EventType.TOOL_CALL, tool_name="bash", args={"command": "ls -F"})
|
|
228
|
+
|
|
229
|
+
assert _render_event(event, status, console) is False
|
|
230
|
+
status.stop.assert_called_once()
|
|
231
|
+
|
|
196
232
|
|
|
197
233
|
class TestCommandDispatcher:
|
|
198
234
|
"""Smoke tests for CommandDispatcher (called by start_repl)."""
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.4"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|