steerdev 1.1.61__tar.gz → 1.1.62__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.
- steerdev-1.1.62/AGENTS.md +122 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/PKG-INFO +27 -4
- {steerdev-1.1.61 → steerdev-1.1.62}/README.md +25 -3
- {steerdev-1.1.61 → steerdev-1.1.62}/pyproject.toml +2 -1
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/agent_loop.py +5 -4
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/tasks.py +2 -1
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/cli.py +233 -15
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/config/models.py +98 -2
- steerdev-1.1.62/src/steerdev_agent/executor/__init__.py +139 -0
- steerdev-1.1.62/src/steerdev_agent/executor/acp.py +400 -0
- steerdev-1.1.62/src/steerdev_agent/executor/acp_client.py +442 -0
- steerdev-1.1.62/src/steerdev_agent/executor/acp_registry.py +154 -0
- steerdev-1.1.62/src/steerdev_agent/executor/acp_spec.py +128 -0
- steerdev-1.1.62/src/steerdev_agent/executor/acp_stream.py +228 -0
- steerdev-1.1.62/src/steerdev_agent/executor/codex.py +137 -0
- steerdev-1.1.62/src/steerdev_agent/executor/pi.py +356 -0
- steerdev-1.1.62/src/steerdev_agent/executor/pi_stream.py +280 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/runner.py +31 -6
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/steerdev.yaml +1 -1
- steerdev-1.1.62/src/steerdev_agent/workspace/tool_detection.py +90 -0
- steerdev-1.1.62/tests/fixtures/fake_acp_echo.py +123 -0
- steerdev-1.1.62/tests/test_acp_executor.py +293 -0
- steerdev-1.1.62/tests/test_acp_registry.py +104 -0
- steerdev-1.1.62/tests/test_acp_stream.py +259 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_agent_loop_extended.py +4 -4
- steerdev-1.1.62/tests/test_cli_provider.py +209 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_config.py +13 -2
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_config_extended.py +2 -2
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_conflict_mitigation.py +14 -3
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_executor.py +35 -0
- steerdev-1.1.62/tests/test_pi_executor.py +332 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_workspace.py +31 -1
- steerdev-1.1.61/AGENTS.md +0 -57
- steerdev-1.1.61/src/steerdev_agent/executor/__init__.py +0 -73
- steerdev-1.1.61/src/steerdev_agent/workspace/tool_detection.py +0 -20
- {steerdev-1.1.61 → steerdev-1.1.62}/.github/workflows/pre-commit.yml +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/.github/workflows/publish.yml +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/.gitignore +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/.pre-commit-config.yaml +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/CLAUDE.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/docs/superpowers/plans/2026-06-05-wave-id-implies-unlimited-max-tasks.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/docs/superpowers/plans/2026-07-15-unified-merge-flow-agent.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/docs/superpowers/specs/2026-06-05-wave-id-implies-unlimited-max-tasks-design.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/scripts/pre-commit-version-bump.sh +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/snapshots/steerdev-agent-v1/Dockerfile +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/snapshots/steerdev-agent-v1/start-agent.sh +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/__init__.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/__init__.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/activity.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/agents.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/canals.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/client.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/commands.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/configs.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/context.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/events.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/hooks.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/implementation_plan.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/merger.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/messages.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/prd.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/projects.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/reports.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/runs.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/sessions.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/specs.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/workflow_runs.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/api/workflows.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/config/__init__.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/config/platform.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/config/settings.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/evidence.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/evidence_assets.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/executor/base.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/executor/claude.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/executor/stream.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/git/__init__.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/git/commit.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/git/pr.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/git_ops.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/handlers/__init__.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/handlers/prd.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/integration.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/merge_settings.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/prompt/__init__.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/prompt/builder.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/prompt/templates.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/prompt/workflow_template.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/py.typed +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/retry.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/__init__.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/claude_setup.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/repo_setup.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/ci/canal-integration.yml +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/claude_md_section.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/settings.json +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/skills/steerdev-activity-skill/SKILL.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/skills/steerdev-canal-workflow-skill/SKILL.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/skills/steerdev-context-skill/SKILL.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/skills/steerdev-git-workflow-skill/SKILL.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/skills/steerdev-merge-into-canal-skill/SKILL.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/skills/steerdev-progress-logging-skill/SKILL.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/skills/steerdev-single-task-merge-skill/SKILL.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/skills/steerdev-specs-management-skill/SKILL.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/skills/steerdev-task-management-skill/SKILL.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/skills/steerdev-wave-tasks-merge-skill/SKILL.md +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/setup/templates/worktrunk.config.toml +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/update_check.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/version.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/workflow/__init__.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/workflow/context.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/workflow/executor.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/workflow/memory.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/workspace/__init__.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/workspace/preparation.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/workspace/project_manager.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/src/steerdev_agent/worktree.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/__init__.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_agent_loop.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_agent_loop_wave_pr.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_agents_api.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_api_client.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_canal_enroll.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_claude_executor.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_claude_setup.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_client_methods.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_commands_api.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_context_search.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_git_commit.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_git_ops.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_git_pr.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_hooks_post_tool_use.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_merge_settings.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_platform_config.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_preparation.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_preparation_provenance.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_projects_api.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_prompt.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_reports_client.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_resolve_executor_config.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_resolve_max_tasks.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_retry.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_runner_boundary_commit.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_runner_merge_modes.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_runner_wave_pr.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_runner_worktrees.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_stream_parser.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_tasks.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_tasks_update_branch.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_version.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_wave_scoped_fetch.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_workflow_context.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_workflow_memory.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_workflow_prompt_template.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_workflow_runs_api.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_workspace_extended.py +0 -0
- {steerdev-1.1.61 → steerdev-1.1.62}/tests/test_worktree.py +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Guidance for AI coding agents working in this repository.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
**steerdev-agent** is a Python CLI that orchestrates AI coding agents via subprocess execution, streaming events to the steerdev.com API. It fetches tasks, builds prompts, launches agents (Claude Code and pi are wired; `openai-codex` is a stub), and reports activity.
|
|
8
|
+
|
|
9
|
+
## Project Structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
src/steerdev_agent/
|
|
13
|
+
├── cli.py # Typer CLI commands
|
|
14
|
+
├── runner.py # Main orchestrator
|
|
15
|
+
├── agent_loop.py # Persistent agent mode
|
|
16
|
+
├── version.py # Version management
|
|
17
|
+
├── integration.py # Integration utilities
|
|
18
|
+
├── api/ # API clients (tasks, sessions, events, workflows, etc.)
|
|
19
|
+
├── config/ # Pydantic config models and settings
|
|
20
|
+
├── executor/ # Agent subprocess execution (base, claude, stream)
|
|
21
|
+
├── workflow/ # Multi-phase workflow execution and memory
|
|
22
|
+
├── prompt/ # Prompt building and templates
|
|
23
|
+
├── setup/ # Project setup and templates
|
|
24
|
+
├── handlers/ # Task/PRD handlers
|
|
25
|
+
└── git/ # Git utilities
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Development
|
|
29
|
+
|
|
30
|
+
- **Python 3.12+** with **uv** package manager
|
|
31
|
+
- `uv sync` — install deps
|
|
32
|
+
- `uv run pytest` — run tests
|
|
33
|
+
- `uv run ruff check . && uv run ruff format .` — lint and format
|
|
34
|
+
- `uv run basedpyright` — type check
|
|
35
|
+
|
|
36
|
+
## Conventions
|
|
37
|
+
|
|
38
|
+
- Type hints on all function signatures
|
|
39
|
+
- Pydantic v2 for data models and config
|
|
40
|
+
- Async/await for I/O (HTTP, subprocess)
|
|
41
|
+
- `loguru` for logging
|
|
42
|
+
- Specific exception classes (no bare `except`)
|
|
43
|
+
- Absolute imports (`from steerdev_agent.config.models import ...`)
|
|
44
|
+
- Import order: stdlib, third-party, local
|
|
45
|
+
|
|
46
|
+
## Key Integration Points
|
|
47
|
+
|
|
48
|
+
- **steerdev.com API**: Bearer auth via `STEERDEV_API_KEY`. Events POSTed to `/api/v1/sessions/{id}/events`.
|
|
49
|
+
- **Executor**: Launches agents as subprocesses with `--output-format stream-json`. Parses newline-delimited JSON events.
|
|
50
|
+
- **Config**: YAML config file + environment variable overrides.
|
|
51
|
+
|
|
52
|
+
## Environment Variables
|
|
53
|
+
|
|
54
|
+
| Variable | Purpose |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `STEERDEV_API_KEY` | API authentication |
|
|
57
|
+
| `STEERDEV_PROJECT_ID` | Default project |
|
|
58
|
+
| `STEERDEV_PROVIDER` | Harness provider: `claude` (default), `pi`, `openai-codex`, `acp:<agent>` |
|
|
59
|
+
| `STEERDEV_ACP_HARNESS` | ACP agent name when the provider is bare `acp` (equivalent to `--acp-agent`) |
|
|
60
|
+
| `CLAUDE_CODE_OAUTH_TOKEN` / `ANTHROPIC_API_KEY` | Claude credentials (sandbox-injected; locally use `claude login` instead) |
|
|
61
|
+
| `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `OPENROUTER_API_KEY` / `GEMINI_API_KEY` | pi credentials — whichever matches the selected model's provider (sandbox-injected; locally use `pi` + `/login`) |
|
|
62
|
+
| `OPENAI_API_KEY` | Codex credentials for `openai-codex` (sandbox-injected) |
|
|
63
|
+
| Per-harness (e.g. `GEMINI_API_KEY` for `acp:gemini`) | ACP credentials — declared per agent in the registry (`credential_env`) |
|
|
64
|
+
|
|
65
|
+
## Providers
|
|
66
|
+
|
|
67
|
+
The agent supports multiple harness providers via `--provider` (or `STEERDEV_PROVIDER`):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
steerdev run --provider claude --project-id abc123 # default (Claude Code)
|
|
71
|
+
steerdev run --provider pi --project-id abc123 # pi.dev coding agent
|
|
72
|
+
steerdev run --provider acp:gemini --project-id abc123 # any ACP harness (Gemini CLI here)
|
|
73
|
+
steerdev run --provider openai-codex --project-id abc123 # stub (not yet wired)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`claude` (Claude Code) and `pi` (the [pi.dev](https://pi.dev) coding agent) are
|
|
77
|
+
wired end-to-end. `pi` is launched headless as `pi --mode json "<prompt>"`, with
|
|
78
|
+
resume via `--session`, model via `--model`, and tool allow/deny lists via
|
|
79
|
+
`--tools`/`--exclude-tools`; see `executor/pi.py` and `executor/pi_stream.py`.
|
|
80
|
+
`openai-codex` is a clearly-stubbed seam (`executor/codex.py`) that fails fast
|
|
81
|
+
with a "not yet supported" message.
|
|
82
|
+
|
|
83
|
+
### ACP harnesses (`acp:<agent>`)
|
|
84
|
+
|
|
85
|
+
`acp:<agent>` drives **any** harness speaking the
|
|
86
|
+
[Agent Client Protocol](https://agentclientprotocol.com) (JSON-RPC 2.0 over
|
|
87
|
+
stdio) through a single executor — steerdev acts as the ACP *Client*
|
|
88
|
+
(filesystem, terminals, permissions) and the harness is the ACP *Agent*.
|
|
89
|
+
Built-in registry entries (`executor/acp_registry.py`): `acp:gemini`
|
|
90
|
+
(Gemini CLI via `gemini --experimental-acp`), `acp:claude-code` (via
|
|
91
|
+
`npx @zed-industries/claude-code-acp`), `acp:goose` (`goose acp`).
|
|
92
|
+
|
|
93
|
+
Adding a new harness requires **no code** — declare it under `acp.agents` in
|
|
94
|
+
`steerdev.yaml`:
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
acp:
|
|
98
|
+
# default agent for `--provider acp`
|
|
99
|
+
agent: gemini
|
|
100
|
+
# auto_approve_all (default) | auto_approve_workdir | read_only | escalate
|
|
101
|
+
permission_policy: auto_approve_all
|
|
102
|
+
agents:
|
|
103
|
+
myagent:
|
|
104
|
+
name: myagent
|
|
105
|
+
command: my-acp-agent
|
|
106
|
+
args: ["--serve"]
|
|
107
|
+
credential_env: ["MY_AGENT_API_KEY"]
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Key modules: `executor/acp.py` (`ACPExecutor` — spawn, initialize,
|
|
111
|
+
session/new|load, prompt), `executor/acp_client.py` (`SteerdevAcpClient` +
|
|
112
|
+
`AcpPermissionPolicy` — fs/terminal/permission callbacks), `executor/acp_stream.py`
|
|
113
|
+
(ACP `session/update` → canonical `EventType` translation), `executor/acp_spec.py`
|
|
114
|
+
+ `executor/acp_registry.py` (declarative launch specs). ACP turns end with a
|
|
115
|
+
`stopReason`, not an exit code — `wait()` synthesizes one (nonzero only on
|
|
116
|
+
refusal/error).
|
|
117
|
+
|
|
118
|
+
Credentials arrive via ENV (the sandbox bootstrap injects them); the agent never
|
|
119
|
+
fetches credentials itself. For providers with a persisted local login (`claude`,
|
|
120
|
+
`pi`, and ACP agents with `requires_local_login`) a missing credential env var
|
|
121
|
+
only warns; for the rest the matching env var must be present or the command
|
|
122
|
+
hard-fails.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: steerdev
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.62
|
|
4
4
|
Summary: Backend task runner for steerdev.com - orchestrates CLI coding agents with activity reporting
|
|
5
5
|
Project-URL: Homepage, https://github.com/pentoai/steerdev-agent
|
|
6
6
|
Project-URL: Repository, https://github.com/pentoai/steerdev-agent
|
|
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
16
16
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
17
|
Classifier: Topic :: Terminals
|
|
18
18
|
Requires-Python: >=3.12
|
|
19
|
+
Requires-Dist: agent-client-protocol<0.11,>=0.10
|
|
19
20
|
Requires-Dist: httpx>=0.27.0
|
|
20
21
|
Requires-Dist: loguru>=0.7.3
|
|
21
22
|
Requires-Dist: pydantic-settings>=2.0.0
|
|
@@ -43,7 +44,7 @@ Task runner for [steerdev.com](https://steerdev.com) — orchestrates AI coding
|
|
|
43
44
|
|
|
44
45
|
## What It Does
|
|
45
46
|
|
|
46
|
-
SteerDev Agent connects your AI coding agent (Claude Code
|
|
47
|
+
SteerDev Agent connects your AI coding agent (Claude Code or pi; `openai-codex` is a stub) to the steerdev.com platform:
|
|
47
48
|
|
|
48
49
|
1. **Fetches tasks** from your SteerDev project
|
|
49
50
|
2. **Builds prompts** with project context, task details, and workflow instructions
|
|
@@ -299,7 +300,7 @@ api:
|
|
|
299
300
|
project_id_env: STEERDEV_PROJECT_ID
|
|
300
301
|
|
|
301
302
|
executor:
|
|
302
|
-
type: claude # claude,
|
|
303
|
+
type: claude # claude (wired), pi (wired), openai-codex (stub)
|
|
303
304
|
permission_mode: default
|
|
304
305
|
allowed_tools: []
|
|
305
306
|
disallowed_tools: []
|
|
@@ -316,8 +317,30 @@ worktrees:
|
|
|
316
317
|
| `STEERDEV_API_KEY` | API key for steerdev.com |
|
|
317
318
|
| `STEERDEV_PROJECT_ID` | Default project ID |
|
|
318
319
|
| `STEERDEV_AGENT_NAME` | Agent name for session tracking |
|
|
320
|
+
| `STEERDEV_PROVIDER` | Harness provider: `claude` (default), `pi`, `openai-codex` |
|
|
321
|
+
| `CLAUDE_CODE_OAUTH_TOKEN` / `ANTHROPIC_API_KEY` | Claude credentials (sandbox-injected; locally use `claude login`) |
|
|
322
|
+
| `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `OPENROUTER_API_KEY` / `GEMINI_API_KEY` | pi credentials — whichever matches the selected model's provider (sandbox-injected; locally use `pi` + `/login`) |
|
|
323
|
+
| `OPENAI_API_KEY` | Codex credentials for `openai-codex` (sandbox-injected) |
|
|
319
324
|
|
|
320
|
-
> **Note:** CLI agents
|
|
325
|
+
> **Note:** Locally, CLI agents like Claude Code and pi can be logged in
|
|
326
|
+
> independently (`claude login`, or `pi` + `/login`) before running steerdev — no
|
|
327
|
+
> LLM API keys are strictly needed. In a sandbox, the bootstrap injects the
|
|
328
|
+
> per-provider credentials above; steerdev never fetches credentials itself.
|
|
329
|
+
|
|
330
|
+
### Providers
|
|
331
|
+
|
|
332
|
+
Select the harness with `--provider` (or `STEERDEV_PROVIDER`). Defaults to `claude`:
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
steerdev run --provider claude --project-id YOUR_PROJECT_ID # Claude Code
|
|
336
|
+
steerdev run --provider pi --project-id YOUR_PROJECT_ID # pi.dev coding agent
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
`claude` (Claude Code) and `pi` (the [pi.dev](https://pi.dev) coding agent) are
|
|
340
|
+
wired end-to-end. `pi` runs headless as `pi --mode json "<prompt>"` and streams
|
|
341
|
+
its JSON events back to steerdev. `openai-codex` is a clearly-stubbed seam that
|
|
342
|
+
fails fast with a "not yet supported" message, reserved for a future harness
|
|
343
|
+
protocol.
|
|
321
344
|
|
|
322
345
|
## Architecture
|
|
323
346
|
|
|
@@ -6,7 +6,7 @@ Task runner for [steerdev.com](https://steerdev.com) — orchestrates AI coding
|
|
|
6
6
|
|
|
7
7
|
## What It Does
|
|
8
8
|
|
|
9
|
-
SteerDev Agent connects your AI coding agent (Claude Code
|
|
9
|
+
SteerDev Agent connects your AI coding agent (Claude Code or pi; `openai-codex` is a stub) to the steerdev.com platform:
|
|
10
10
|
|
|
11
11
|
1. **Fetches tasks** from your SteerDev project
|
|
12
12
|
2. **Builds prompts** with project context, task details, and workflow instructions
|
|
@@ -262,7 +262,7 @@ api:
|
|
|
262
262
|
project_id_env: STEERDEV_PROJECT_ID
|
|
263
263
|
|
|
264
264
|
executor:
|
|
265
|
-
type: claude # claude,
|
|
265
|
+
type: claude # claude (wired), pi (wired), openai-codex (stub)
|
|
266
266
|
permission_mode: default
|
|
267
267
|
allowed_tools: []
|
|
268
268
|
disallowed_tools: []
|
|
@@ -279,8 +279,30 @@ worktrees:
|
|
|
279
279
|
| `STEERDEV_API_KEY` | API key for steerdev.com |
|
|
280
280
|
| `STEERDEV_PROJECT_ID` | Default project ID |
|
|
281
281
|
| `STEERDEV_AGENT_NAME` | Agent name for session tracking |
|
|
282
|
+
| `STEERDEV_PROVIDER` | Harness provider: `claude` (default), `pi`, `openai-codex` |
|
|
283
|
+
| `CLAUDE_CODE_OAUTH_TOKEN` / `ANTHROPIC_API_KEY` | Claude credentials (sandbox-injected; locally use `claude login`) |
|
|
284
|
+
| `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `OPENROUTER_API_KEY` / `GEMINI_API_KEY` | pi credentials — whichever matches the selected model's provider (sandbox-injected; locally use `pi` + `/login`) |
|
|
285
|
+
| `OPENAI_API_KEY` | Codex credentials for `openai-codex` (sandbox-injected) |
|
|
282
286
|
|
|
283
|
-
> **Note:** CLI agents
|
|
287
|
+
> **Note:** Locally, CLI agents like Claude Code and pi can be logged in
|
|
288
|
+
> independently (`claude login`, or `pi` + `/login`) before running steerdev — no
|
|
289
|
+
> LLM API keys are strictly needed. In a sandbox, the bootstrap injects the
|
|
290
|
+
> per-provider credentials above; steerdev never fetches credentials itself.
|
|
291
|
+
|
|
292
|
+
### Providers
|
|
293
|
+
|
|
294
|
+
Select the harness with `--provider` (or `STEERDEV_PROVIDER`). Defaults to `claude`:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
steerdev run --provider claude --project-id YOUR_PROJECT_ID # Claude Code
|
|
298
|
+
steerdev run --provider pi --project-id YOUR_PROJECT_ID # pi.dev coding agent
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
`claude` (Claude Code) and `pi` (the [pi.dev](https://pi.dev) coding agent) are
|
|
302
|
+
wired end-to-end. `pi` runs headless as `pi --mode json "<prompt>"` and streams
|
|
303
|
+
its JSON events back to steerdev. `openai-codex` is a clearly-stubbed seam that
|
|
304
|
+
fails fast with a "not yet supported" message, reserved for a future harness
|
|
305
|
+
protocol.
|
|
284
306
|
|
|
285
307
|
## Architecture
|
|
286
308
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "steerdev"
|
|
3
|
-
version = "1.1.
|
|
3
|
+
version = "1.1.62"
|
|
4
4
|
description = "Backend task runner for steerdev.com - orchestrates CLI coding agents with activity reporting"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -28,6 +28,7 @@ dependencies = [
|
|
|
28
28
|
"pyyaml>=6.0.0",
|
|
29
29
|
"python-dotenv>=1.2.1",
|
|
30
30
|
"tenacity>=9.0.0",
|
|
31
|
+
"agent-client-protocol>=0.10,<0.11",
|
|
31
32
|
]
|
|
32
33
|
|
|
33
34
|
[project.optional-dependencies]
|
|
@@ -46,6 +46,7 @@ from steerdev_agent.config.models import (
|
|
|
46
46
|
WorktreeConfig,
|
|
47
47
|
)
|
|
48
48
|
from steerdev_agent.executor import ExecutorFactory
|
|
49
|
+
from steerdev_agent.executor.acp_registry import application_for_agent_type
|
|
49
50
|
from steerdev_agent.executor.base import EventType
|
|
50
51
|
from steerdev_agent.executor.claude import ClaudeExecutorError
|
|
51
52
|
|
|
@@ -747,7 +748,7 @@ class AgentLoop(CommandExecutor):
|
|
|
747
748
|
json={
|
|
748
749
|
"name": self.agent_name,
|
|
749
750
|
"project_id": self.project_id,
|
|
750
|
-
"application": self.agent_type,
|
|
751
|
+
"application": application_for_agent_type(self.agent_type),
|
|
751
752
|
"hostname": socket.gethostname(),
|
|
752
753
|
},
|
|
753
754
|
)
|
|
@@ -917,7 +918,7 @@ class AgentLoop(CommandExecutor):
|
|
|
917
918
|
json={
|
|
918
919
|
"name": self.agent_name,
|
|
919
920
|
"project_id": self.project_id,
|
|
920
|
-
"application": self.agent_type,
|
|
921
|
+
"application": application_for_agent_type(self.agent_type),
|
|
921
922
|
"hostname": socket.gethostname(),
|
|
922
923
|
},
|
|
923
924
|
)
|
|
@@ -1152,7 +1153,7 @@ class WorkspaceAgentLoop(CommandExecutor):
|
|
|
1152
1153
|
try:
|
|
1153
1154
|
result = await agents_client.register_workspace(
|
|
1154
1155
|
name=self.agent_name,
|
|
1155
|
-
application=self.agent_type,
|
|
1156
|
+
application=application_for_agent_type(self.agent_type),
|
|
1156
1157
|
hostname=socket.gethostname(),
|
|
1157
1158
|
workspace_path=str(self.workspace_path),
|
|
1158
1159
|
capabilities=detect_installed_tools(),
|
|
@@ -1355,7 +1356,7 @@ class WorkspaceAgentLoop(CommandExecutor):
|
|
|
1355
1356
|
try:
|
|
1356
1357
|
await agents_client.register_workspace(
|
|
1357
1358
|
name=self.agent_name,
|
|
1358
|
-
application=self.agent_type,
|
|
1359
|
+
application=application_for_agent_type(self.agent_type),
|
|
1359
1360
|
hostname=socket.gethostname(),
|
|
1360
1361
|
workspace_path=str(self.workspace_path),
|
|
1361
1362
|
capabilities=detect_installed_tools(),
|
|
@@ -468,11 +468,12 @@ class TasksClient(SteerDevClient):
|
|
|
468
468
|
payload["actual_files"] = actual_files
|
|
469
469
|
if predicted_files is not None:
|
|
470
470
|
payload["predicted_files"] = predicted_files
|
|
471
|
-
payload["source"] = "agent_reported"
|
|
472
471
|
|
|
473
472
|
if not payload:
|
|
474
473
|
return False
|
|
475
474
|
|
|
475
|
+
payload["source"] = "agent_reported"
|
|
476
|
+
|
|
476
477
|
response = self.patch(f"/tasks/{task_id}/files", json=payload)
|
|
477
478
|
if response.status_code not in (200, 204):
|
|
478
479
|
logger.warning(f"Failed to report files for task {task_id}: {response.status_code}")
|
|
@@ -13,7 +13,11 @@ from loguru import logger
|
|
|
13
13
|
from rich.console import Console
|
|
14
14
|
from rich.panel import Panel
|
|
15
15
|
|
|
16
|
-
from steerdev_agent.config.models import
|
|
16
|
+
from steerdev_agent.config.models import (
|
|
17
|
+
SUPPORTED_PROVIDERS,
|
|
18
|
+
SteerDevConfig,
|
|
19
|
+
is_supported_provider,
|
|
20
|
+
)
|
|
17
21
|
from steerdev_agent.update_check import check_for_update
|
|
18
22
|
from steerdev_agent.version import get_version
|
|
19
23
|
|
|
@@ -22,6 +26,133 @@ console = Console()
|
|
|
22
26
|
# Load environment variables from .env file in current working directory
|
|
23
27
|
load_dotenv(Path.cwd() / ".env", override=True)
|
|
24
28
|
|
|
29
|
+
# ============================================================================
|
|
30
|
+
# Provider (harness) resolution helpers
|
|
31
|
+
# ============================================================================
|
|
32
|
+
# Candidate credential env vars per provider, in priority order. The sandbox
|
|
33
|
+
# bootstrap injects these; the agent never fetches credentials itself. For
|
|
34
|
+
# providers that support a persisted local login (claude, pi), a missing env
|
|
35
|
+
# var only warns; for the rest it hard-fails.
|
|
36
|
+
PROVIDER_CREDENTIAL_ENV: dict[str, tuple[str, ...]] = {
|
|
37
|
+
"claude": ("CLAUDE_CODE_OAUTH_TOKEN", "ANTHROPIC_API_KEY"),
|
|
38
|
+
"pi": ("ANTHROPIC_API_KEY", "OPENAI_API_KEY", "OPENROUTER_API_KEY", "GEMINI_API_KEY"),
|
|
39
|
+
"openai-codex": ("OPENAI_API_KEY",),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Providers whose CLI can fall back to a persisted local login session, so a
|
|
43
|
+
# missing credential env var is a warning rather than a hard failure.
|
|
44
|
+
LOCAL_LOGIN_PROVIDERS: dict[str, str] = {
|
|
45
|
+
"claude": "claude login",
|
|
46
|
+
"pi": "pi (then /login)",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _resolve_provider(cli_value: str | None, config: SteerDevConfig) -> str:
|
|
51
|
+
"""Resolve the harness provider with precedence flag > env > config > default.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
cli_value: Value of the ``--provider`` option (None if not passed).
|
|
55
|
+
config: Loaded SteerDev configuration.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
The resolved provider identifier (defaults to "claude").
|
|
59
|
+
"""
|
|
60
|
+
if cli_value:
|
|
61
|
+
return cli_value
|
|
62
|
+
env_value = os.environ.get("STEERDEV_PROVIDER")
|
|
63
|
+
if env_value:
|
|
64
|
+
return env_value
|
|
65
|
+
if config.executor.type:
|
|
66
|
+
return config.executor.type
|
|
67
|
+
return "claude"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _resolve_acp_provider(provider: str, acp_agent: str | None, config: SteerDevConfig) -> str:
|
|
71
|
+
"""Normalize a bare ``acp`` provider to the full ``acp:<agent>`` form.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
provider: The resolved provider identifier.
|
|
75
|
+
acp_agent: Value of the ``--acp-agent`` option (None if not passed).
|
|
76
|
+
config: Loaded SteerDev configuration.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
The provider with the ACP agent name attached when applicable.
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
typer.Exit: If the ACP provider has no agent name to resolve.
|
|
83
|
+
"""
|
|
84
|
+
if provider != "acp":
|
|
85
|
+
return provider
|
|
86
|
+
agent_name = acp_agent or config.acp.agent
|
|
87
|
+
if not agent_name:
|
|
88
|
+
console.print(
|
|
89
|
+
"[red]Error: provider 'acp' requires an agent name. Use "
|
|
90
|
+
"--provider acp:<agent> (e.g. acp:gemini), pass --acp-agent, or "
|
|
91
|
+
"set acp.agent in the config.[/red]"
|
|
92
|
+
)
|
|
93
|
+
raise typer.Exit(1)
|
|
94
|
+
return f"acp:{agent_name}"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _check_provider_credentials(provider: str, config: SteerDevConfig | None = None) -> None:
|
|
98
|
+
"""Validate that credentials for a provider are present in the environment.
|
|
99
|
+
|
|
100
|
+
Providers that support a persisted local login (``claude``, ``pi`` — see
|
|
101
|
+
``LOCAL_LOGIN_PROVIDERS``; ACP harnesses via ``requires_local_login``) only
|
|
102
|
+
warn when no candidate env var is set, so a local login session keeps
|
|
103
|
+
working. Other providers (e.g. ``openai-codex``) hard-fail.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
provider: The resolved provider identifier.
|
|
107
|
+
config: Loaded configuration (for ACP registry overrides).
|
|
108
|
+
|
|
109
|
+
Raises:
|
|
110
|
+
typer.Exit: If a provider without a local-login fallback has no
|
|
111
|
+
credentials in the environment.
|
|
112
|
+
"""
|
|
113
|
+
candidates = PROVIDER_CREDENTIAL_ENV.get(provider, ())
|
|
114
|
+
local_login_hint = LOCAL_LOGIN_PROVIDERS.get(provider)
|
|
115
|
+
|
|
116
|
+
# ACP providers carry their credential contract in the harness spec.
|
|
117
|
+
if provider.startswith("acp:"):
|
|
118
|
+
from steerdev_agent.executor.acp_registry import (
|
|
119
|
+
acp_harness_from_provider,
|
|
120
|
+
resolve_acp_spec,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
harness = acp_harness_from_provider(provider)
|
|
124
|
+
overrides = config.acp.agents if config else None
|
|
125
|
+
try:
|
|
126
|
+
spec = resolve_acp_spec(harness or "", overrides)
|
|
127
|
+
except ValueError as e:
|
|
128
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
129
|
+
raise typer.Exit(1) from e
|
|
130
|
+
candidates = tuple(spec.credential_env)
|
|
131
|
+
if spec.requires_local_login:
|
|
132
|
+
local_login_hint = spec.local_login_hint or spec.command
|
|
133
|
+
|
|
134
|
+
has_credential = any(os.environ.get(var) for var in candidates)
|
|
135
|
+
if has_credential:
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
if local_login_hint is not None:
|
|
139
|
+
vars_list = " or ".join(candidates) if candidates else "(none configured)"
|
|
140
|
+
console.print(
|
|
141
|
+
f"[yellow]Warning: no credentials for '{provider}' in the environment "
|
|
142
|
+
f"({vars_list}). Relying on a local `{local_login_hint}` "
|
|
143
|
+
f"session.[/yellow]"
|
|
144
|
+
)
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
vars_list = " or ".join(candidates) if candidates else "(none configured)"
|
|
148
|
+
console.print(
|
|
149
|
+
f"[red]Error: provider '{provider}' requires credentials in the "
|
|
150
|
+
f"environment. Set {vars_list} (the sandbox bootstrap normally injects "
|
|
151
|
+
f"these).[/red]"
|
|
152
|
+
)
|
|
153
|
+
raise typer.Exit(1)
|
|
154
|
+
|
|
155
|
+
|
|
25
156
|
app = typer.Typer(
|
|
26
157
|
name="steerdev",
|
|
27
158
|
help="Backend task runner for steerdev.com - orchestrates CLI coding agents",
|
|
@@ -1614,18 +1745,6 @@ def run(
|
|
|
1614
1745
|
envvar="STEERDEV_AGENT_ID",
|
|
1615
1746
|
),
|
|
1616
1747
|
] = None,
|
|
1617
|
-
provider: Annotated[
|
|
1618
|
-
str | None,
|
|
1619
|
-
typer.Option(
|
|
1620
|
-
"--provider",
|
|
1621
|
-
help=(
|
|
1622
|
-
"AI provider / executor type to run tasks with (e.g. claude)."
|
|
1623
|
-
" Overrides steerdev.yaml executor.type."
|
|
1624
|
-
" Reads from STEERDEV_PROVIDER env var."
|
|
1625
|
-
),
|
|
1626
|
-
envvar="STEERDEV_PROVIDER",
|
|
1627
|
-
),
|
|
1628
|
-
] = None,
|
|
1629
1748
|
model: Annotated[
|
|
1630
1749
|
str | None,
|
|
1631
1750
|
typer.Option(
|
|
@@ -1769,6 +1888,22 @@ def run(
|
|
|
1769
1888
|
),
|
|
1770
1889
|
),
|
|
1771
1890
|
] = None,
|
|
1891
|
+
provider: Annotated[
|
|
1892
|
+
str | None,
|
|
1893
|
+
typer.Option(
|
|
1894
|
+
"--provider",
|
|
1895
|
+
help="Harness provider: claude | pi | openai-codex | acp:<agent> (default: claude)",
|
|
1896
|
+
envvar="STEERDEV_PROVIDER",
|
|
1897
|
+
),
|
|
1898
|
+
] = None,
|
|
1899
|
+
acp_agent: Annotated[
|
|
1900
|
+
str | None,
|
|
1901
|
+
typer.Option(
|
|
1902
|
+
"--acp-agent",
|
|
1903
|
+
help="ACP agent name when --provider is 'acp' (e.g. gemini, claude-code, goose)",
|
|
1904
|
+
envvar="STEERDEV_ACP_HARNESS",
|
|
1905
|
+
),
|
|
1906
|
+
] = None,
|
|
1772
1907
|
) -> None:
|
|
1773
1908
|
"""Run the agent for a project!
|
|
1774
1909
|
|
|
@@ -1855,6 +1990,23 @@ def run(
|
|
|
1855
1990
|
f"canals={merge_flags.enable_canals} ({merge_flags.canals_source})"
|
|
1856
1991
|
)
|
|
1857
1992
|
|
|
1993
|
+
# Resolve provider: CLI flag > STEERDEV_PROVIDER env > config > default.
|
|
1994
|
+
resolved_provider = _resolve_provider(provider, config)
|
|
1995
|
+
resolved_provider = _resolve_acp_provider(resolved_provider, acp_agent, config)
|
|
1996
|
+
if not is_supported_provider(resolved_provider):
|
|
1997
|
+
supported = ", ".join((*SUPPORTED_PROVIDERS, "acp:<agent>"))
|
|
1998
|
+
console.print(
|
|
1999
|
+
f"[red]Error: unsupported provider '{resolved_provider}'. Supported: {supported}.[/red]"
|
|
2000
|
+
)
|
|
2001
|
+
raise typer.Exit(1)
|
|
2002
|
+
resolved_executor_config = config.executor.model_copy(
|
|
2003
|
+
update={
|
|
2004
|
+
"type": resolved_provider,
|
|
2005
|
+
"acp_agents": config.acp.agents,
|
|
2006
|
+
"acp_permission_policy": config.acp.permission_policy,
|
|
2007
|
+
}
|
|
2008
|
+
)
|
|
2009
|
+
|
|
1858
2010
|
worktree_status = (
|
|
1859
2011
|
f"enabled ({resolved_worktree_config.provider})"
|
|
1860
2012
|
if resolved_worktree_config.enabled
|
|
@@ -1879,6 +2031,7 @@ def run(
|
|
|
1879
2031
|
f"[bold blue]SteerDev Agent[/bold blue]\n"
|
|
1880
2032
|
f"Project ID: {resolved_project_id}\n"
|
|
1881
2033
|
f"Agent Name: {agent_name_display}\n"
|
|
2034
|
+
f"Provider: {resolved_provider}\n"
|
|
1882
2035
|
f"Working Directory: {working_dir or 'current'}\n"
|
|
1883
2036
|
f"Provider: {resolved_executor_config.type}\n"
|
|
1884
2037
|
f"Model: {resolved_model or 'default'}\n"
|
|
@@ -1912,12 +2065,28 @@ def run(
|
|
|
1912
2065
|
if default_branch is not None:
|
|
1913
2066
|
resolved_branch_config.default_branch = default_branch
|
|
1914
2067
|
|
|
2068
|
+
# Preflight provider checks (skip in dry-run so command preview works offline).
|
|
2069
|
+
if not dry_run:
|
|
2070
|
+
from steerdev_agent.workspace.tool_detection import (
|
|
2071
|
+
cli_for_provider,
|
|
2072
|
+
is_provider_cli_available,
|
|
2073
|
+
)
|
|
2074
|
+
|
|
2075
|
+
_check_provider_credentials(resolved_provider, config)
|
|
2076
|
+
if not is_provider_cli_available(resolved_provider, config.acp.agents):
|
|
2077
|
+
console.print(
|
|
2078
|
+
f"[red]Error: CLI '{cli_for_provider(resolved_provider, config.acp.agents)}' "
|
|
2079
|
+
f"for provider '{resolved_provider}' not found in PATH.[/red]"
|
|
2080
|
+
)
|
|
2081
|
+
raise typer.Exit(1)
|
|
2082
|
+
|
|
1915
2083
|
try:
|
|
1916
2084
|
result = asyncio.run(
|
|
1917
2085
|
run_agent(
|
|
1918
2086
|
project_id=resolved_project_id,
|
|
1919
2087
|
working_directory=str(working_dir) if working_dir else None,
|
|
1920
2088
|
api_key=resolved_api_key,
|
|
2089
|
+
agent_type=resolved_provider,
|
|
1921
2090
|
agent_name=agent_name,
|
|
1922
2091
|
model=resolved_model,
|
|
1923
2092
|
max_turns=resolved_max_turns,
|
|
@@ -2106,6 +2275,22 @@ def agent(
|
|
|
2106
2275
|
help="Default branch for sync/rebase/PR targets (default: from config or 'main')",
|
|
2107
2276
|
),
|
|
2108
2277
|
] = None,
|
|
2278
|
+
provider: Annotated[
|
|
2279
|
+
str | None,
|
|
2280
|
+
typer.Option(
|
|
2281
|
+
"--provider",
|
|
2282
|
+
help="Harness provider: claude | pi | openai-codex | acp:<agent> (default: claude)",
|
|
2283
|
+
envvar="STEERDEV_PROVIDER",
|
|
2284
|
+
),
|
|
2285
|
+
] = None,
|
|
2286
|
+
acp_agent: Annotated[
|
|
2287
|
+
str | None,
|
|
2288
|
+
typer.Option(
|
|
2289
|
+
"--acp-agent",
|
|
2290
|
+
help="ACP agent name when --provider is 'acp' (e.g. gemini, claude-code, goose)",
|
|
2291
|
+
envvar="STEERDEV_ACP_HARNESS",
|
|
2292
|
+
),
|
|
2293
|
+
] = None,
|
|
2109
2294
|
) -> None:
|
|
2110
2295
|
"""Run the agent in persistent mode.
|
|
2111
2296
|
|
|
@@ -2188,6 +2373,37 @@ def agent(
|
|
|
2188
2373
|
if default_branch is not None:
|
|
2189
2374
|
resolved_branch_config.default_branch = default_branch
|
|
2190
2375
|
|
|
2376
|
+
# Resolve provider: CLI flag > STEERDEV_PROVIDER env > config > default.
|
|
2377
|
+
resolved_provider = _resolve_provider(provider, config)
|
|
2378
|
+
resolved_provider = _resolve_acp_provider(resolved_provider, acp_agent, config)
|
|
2379
|
+
if not is_supported_provider(resolved_provider):
|
|
2380
|
+
supported = ", ".join((*SUPPORTED_PROVIDERS, "acp:<agent>"))
|
|
2381
|
+
console.print(
|
|
2382
|
+
f"[red]Error: unsupported provider '{resolved_provider}'. Supported: {supported}.[/red]"
|
|
2383
|
+
)
|
|
2384
|
+
raise typer.Exit(1)
|
|
2385
|
+
resolved_executor_config = config.executor.model_copy(
|
|
2386
|
+
update={
|
|
2387
|
+
"type": resolved_provider,
|
|
2388
|
+
"acp_agents": config.acp.agents,
|
|
2389
|
+
"acp_permission_policy": config.acp.permission_policy,
|
|
2390
|
+
}
|
|
2391
|
+
)
|
|
2392
|
+
|
|
2393
|
+
# Preflight provider checks (agent mode always launches real subprocesses).
|
|
2394
|
+
from steerdev_agent.workspace.tool_detection import (
|
|
2395
|
+
cli_for_provider,
|
|
2396
|
+
is_provider_cli_available,
|
|
2397
|
+
)
|
|
2398
|
+
|
|
2399
|
+
_check_provider_credentials(resolved_provider, config)
|
|
2400
|
+
if not is_provider_cli_available(resolved_provider, config.acp.agents):
|
|
2401
|
+
console.print(
|
|
2402
|
+
f"[red]Error: CLI '{cli_for_provider(resolved_provider, config.acp.agents)}' "
|
|
2403
|
+
f"for provider '{resolved_provider}' not found in PATH.[/red]"
|
|
2404
|
+
)
|
|
2405
|
+
raise typer.Exit(1)
|
|
2406
|
+
|
|
2191
2407
|
# Resolve workspace path: CLI > config
|
|
2192
2408
|
resolved_workspace = workspace or config.workspace.workspace_dir
|
|
2193
2409
|
|
|
@@ -2217,12 +2433,13 @@ def agent(
|
|
|
2217
2433
|
asyncio.run(
|
|
2218
2434
|
run_workspace_agent_loop(
|
|
2219
2435
|
workspace_path=str(resolved_workspace),
|
|
2436
|
+
agent_type=resolved_provider,
|
|
2220
2437
|
agent_name=agent_name,
|
|
2221
2438
|
api_key=resolved_api_key,
|
|
2222
2439
|
model=resolved_model,
|
|
2223
2440
|
max_turns=resolved_max_turns,
|
|
2224
2441
|
agent_loop_config=agent_loop_config,
|
|
2225
|
-
executor_config=
|
|
2442
|
+
executor_config=resolved_executor_config,
|
|
2226
2443
|
workspace_config=config.workspace,
|
|
2227
2444
|
force_workflow_id=workflow_id,
|
|
2228
2445
|
enable_waves=merge_flags.enable_waves,
|
|
@@ -2273,13 +2490,14 @@ def agent(
|
|
|
2273
2490
|
asyncio.run(
|
|
2274
2491
|
run_agent_loop(
|
|
2275
2492
|
project_id=resolved_project_id,
|
|
2493
|
+
agent_type=resolved_provider,
|
|
2276
2494
|
agent_name=agent_name,
|
|
2277
2495
|
working_directory=str(working_dir) if working_dir else None,
|
|
2278
2496
|
api_key=resolved_api_key,
|
|
2279
2497
|
model=resolved_model,
|
|
2280
2498
|
max_turns=resolved_max_turns,
|
|
2281
2499
|
agent_loop_config=agent_loop_config,
|
|
2282
|
-
executor_config=
|
|
2500
|
+
executor_config=resolved_executor_config,
|
|
2283
2501
|
force_workflow_id=resolved_workflow_id,
|
|
2284
2502
|
enable_waves=merge_flags.enable_waves,
|
|
2285
2503
|
enable_canals=merge_flags.enable_canals,
|