steerdev 1.0.59__tar.gz → 1.0.60__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.
Files changed (116) hide show
  1. {steerdev-1.0.59 → steerdev-1.0.60}/PKG-INFO +1 -1
  2. {steerdev-1.0.59 → steerdev-1.0.60}/pyproject.toml +1 -1
  3. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/agent_loop.py +11 -0
  4. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/cli.py +27 -0
  5. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/config/models.py +20 -0
  6. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/prompt/builder.py +15 -3
  7. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/prompt/templates.py +10 -3
  8. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/runner.py +66 -24
  9. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/skills/steerdev-merge-into-canal-skill/SKILL.md +6 -5
  10. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/skills/steerdev-single-task-merge-skill/SKILL.md +10 -40
  11. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/skills/steerdev-wave-tasks-merge-skill/SKILL.md +10 -22
  12. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/steerdev.yaml +5 -0
  13. steerdev-1.0.60/src/steerdev_agent/workspace/preparation.py +243 -0
  14. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_config.py +29 -0
  15. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_conflict_mitigation.py +2 -0
  16. steerdev-1.0.60/tests/test_preparation.py +358 -0
  17. {steerdev-1.0.59 → steerdev-1.0.60}/.github/workflows/pre-commit.yml +0 -0
  18. {steerdev-1.0.59 → steerdev-1.0.60}/.github/workflows/publish.yml +0 -0
  19. {steerdev-1.0.59 → steerdev-1.0.60}/.gitignore +0 -0
  20. {steerdev-1.0.59 → steerdev-1.0.60}/.pre-commit-config.yaml +0 -0
  21. {steerdev-1.0.59 → steerdev-1.0.60}/AGENTS.md +0 -0
  22. {steerdev-1.0.59 → steerdev-1.0.60}/CLAUDE.md +0 -0
  23. {steerdev-1.0.59 → steerdev-1.0.60}/README.md +0 -0
  24. {steerdev-1.0.59 → steerdev-1.0.60}/scripts/pre-commit-version-bump.sh +0 -0
  25. {steerdev-1.0.59 → steerdev-1.0.60}/snapshots/steerdev-agent-v1/Dockerfile +0 -0
  26. {steerdev-1.0.59 → steerdev-1.0.60}/snapshots/steerdev-agent-v1/start-agent.sh +0 -0
  27. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/__init__.py +0 -0
  28. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/__init__.py +0 -0
  29. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/activity.py +0 -0
  30. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/agents.py +0 -0
  31. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/canals.py +0 -0
  32. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/client.py +0 -0
  33. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/commands.py +0 -0
  34. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/configs.py +0 -0
  35. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/context.py +0 -0
  36. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/events.py +0 -0
  37. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/hooks.py +0 -0
  38. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/implementation_plan.py +0 -0
  39. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/merger.py +0 -0
  40. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/messages.py +0 -0
  41. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/prd.py +0 -0
  42. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/reports.py +0 -0
  43. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/runs.py +0 -0
  44. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/sessions.py +0 -0
  45. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/specs.py +0 -0
  46. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/tasks.py +0 -0
  47. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/workflow_runs.py +0 -0
  48. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/api/workflows.py +0 -0
  49. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/config/__init__.py +0 -0
  50. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/config/platform.py +0 -0
  51. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/config/settings.py +0 -0
  52. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/evidence.py +0 -0
  53. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/evidence_assets.py +0 -0
  54. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/executor/__init__.py +0 -0
  55. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/executor/base.py +0 -0
  56. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/executor/claude.py +0 -0
  57. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/executor/stream.py +0 -0
  58. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/handlers/__init__.py +0 -0
  59. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/handlers/prd.py +0 -0
  60. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/integration.py +0 -0
  61. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/prompt/__init__.py +0 -0
  62. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/prompt/workflow_template.py +0 -0
  63. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/py.typed +0 -0
  64. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/retry.py +0 -0
  65. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/__init__.py +0 -0
  66. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/claude_setup.py +0 -0
  67. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/repo_setup.py +0 -0
  68. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/ci/canal-integration.yml +0 -0
  69. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/claude_md_section.md +0 -0
  70. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/settings.json +0 -0
  71. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/skills/steerdev-activity-skill/SKILL.md +0 -0
  72. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/skills/steerdev-canal-workflow-skill/SKILL.md +0 -0
  73. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/skills/steerdev-context-skill/SKILL.md +0 -0
  74. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/skills/steerdev-git-workflow-skill/SKILL.md +0 -0
  75. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/skills/steerdev-progress-logging-skill/SKILL.md +0 -0
  76. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/skills/steerdev-specs-management-skill/SKILL.md +0 -0
  77. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/skills/steerdev-task-management-skill/SKILL.md +0 -0
  78. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/setup/templates/worktrunk.config.toml +0 -0
  79. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/update_check.py +0 -0
  80. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/version.py +0 -0
  81. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/workflow/__init__.py +0 -0
  82. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/workflow/context.py +0 -0
  83. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/workflow/executor.py +0 -0
  84. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/workflow/memory.py +0 -0
  85. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/workspace/__init__.py +0 -0
  86. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/workspace/project_manager.py +0 -0
  87. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/workspace/tool_detection.py +0 -0
  88. {steerdev-1.0.59 → steerdev-1.0.60}/src/steerdev_agent/worktree.py +0 -0
  89. {steerdev-1.0.59 → steerdev-1.0.60}/tests/__init__.py +0 -0
  90. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_agent_loop.py +0 -0
  91. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_agent_loop_extended.py +0 -0
  92. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_agents_api.py +0 -0
  93. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_api_client.py +0 -0
  94. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_claude_executor.py +0 -0
  95. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_claude_setup.py +0 -0
  96. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_client_methods.py +0 -0
  97. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_commands_api.py +0 -0
  98. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_config_extended.py +0 -0
  99. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_context_search.py +0 -0
  100. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_executor.py +0 -0
  101. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_platform_config.py +0 -0
  102. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_prompt.py +0 -0
  103. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_reports_client.py +0 -0
  104. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_retry.py +0 -0
  105. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_runner_merge_modes.py +0 -0
  106. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_runner_worktrees.py +0 -0
  107. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_stream_parser.py +0 -0
  108. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_tasks.py +0 -0
  109. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_version.py +0 -0
  110. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_workflow_context.py +0 -0
  111. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_workflow_memory.py +0 -0
  112. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_workflow_prompt_template.py +0 -0
  113. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_workflow_runs_api.py +0 -0
  114. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_workspace.py +0 -0
  115. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_workspace_extended.py +0 -0
  116. {steerdev-1.0.59 → steerdev-1.0.60}/tests/test_worktree.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: steerdev
3
- Version: 1.0.59
3
+ Version: 1.0.60
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "steerdev"
3
- version = "1.0.59"
3
+ version = "1.0.60"
4
4
  description = "Backend task runner for steerdev.com - orchestrates CLI coding agents with activity reporting"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -36,6 +36,7 @@ from steerdev_agent.api.sessions import SessionCreateRequest, SessionsClient
36
36
  from steerdev_agent.api.tasks import TasksClient
37
37
  from steerdev_agent.config.models import (
38
38
  AgentLoopConfig,
39
+ BranchConfig,
39
40
  EvidenceConfig,
40
41
  ExecutorConfig,
41
42
  RetryConfig,
@@ -77,6 +78,7 @@ class CommandExecutor:
77
78
  _workflow_id: str | None
78
79
  _enable_waves: bool
79
80
  _enable_canals: bool
81
+ _branch_config: BranchConfig
80
82
  _worktree_config: WorktreeConfig
81
83
  _evidence_config: EvidenceConfig
82
84
  _agent_loop_config: AgentLoopConfig
@@ -181,6 +183,7 @@ class CommandExecutor:
181
183
  evidence_config=self._evidence_config,
182
184
  executor_config=self._executor_config,
183
185
  force_workflow_id=None,
186
+ branch_config=self._branch_config,
184
187
  shutdown_event=self._shutdown_event,
185
188
  agent_id=self._agent_id,
186
189
  )
@@ -498,6 +501,7 @@ class AgentLoop(CommandExecutor):
498
501
  worktree_config: WorktreeConfig | None = None,
499
502
  evidence_config: EvidenceConfig | None = None,
500
503
  retry_config: RetryConfig | None = None,
504
+ branch_config: BranchConfig | None = None,
501
505
  ) -> None:
502
506
  self.project_id = project_id
503
507
  self.agent_name = agent_name
@@ -509,6 +513,7 @@ class AgentLoop(CommandExecutor):
509
513
  self._agent_loop_config = agent_loop_config or AgentLoopConfig()
510
514
  self._executor_config = executor_config or ExecutorConfig()
511
515
  self._retry_config = retry_config or RetryConfig()
516
+ self._branch_config = branch_config or BranchConfig()
512
517
  self._workflow_id = force_workflow_id
513
518
  self._enable_waves = enable_waves
514
519
  self._enable_canals = enable_canals
@@ -865,6 +870,7 @@ class WorkspaceAgentLoop(CommandExecutor):
865
870
  worktree_config: WorktreeConfig | None = None,
866
871
  evidence_config: EvidenceConfig | None = None,
867
872
  retry_config: RetryConfig | None = None,
873
+ branch_config: BranchConfig | None = None,
868
874
  ) -> None:
869
875
  self.workspace_path = Path(workspace_path)
870
876
  self.agent_name = agent_name
@@ -875,6 +881,7 @@ class WorkspaceAgentLoop(CommandExecutor):
875
881
  self._agent_loop_config = agent_loop_config or AgentLoopConfig()
876
882
  self._executor_config = executor_config or ExecutorConfig()
877
883
  self._retry_config = retry_config or RetryConfig()
884
+ self._branch_config = branch_config or BranchConfig()
878
885
  self._workspace_config = workspace_config or WorkspaceConfig()
879
886
  self._workflow_id = force_workflow_id
880
887
  self._enable_waves = enable_waves
@@ -1287,6 +1294,7 @@ async def run_agent_loop(
1287
1294
  worktree_config: WorktreeConfig | None = None,
1288
1295
  evidence_config: EvidenceConfig | None = None,
1289
1296
  retry_config: RetryConfig | None = None,
1297
+ branch_config: BranchConfig | None = None,
1290
1298
  ) -> None:
1291
1299
  """Run the project-scoped agent loop.
1292
1300
 
@@ -1308,6 +1316,7 @@ async def run_agent_loop(
1308
1316
  worktree_config=worktree_config,
1309
1317
  evidence_config=evidence_config,
1310
1318
  retry_config=retry_config,
1319
+ branch_config=branch_config,
1311
1320
  )
1312
1321
  await agent.start()
1313
1322
 
@@ -1328,6 +1337,7 @@ async def run_workspace_agent_loop(
1328
1337
  worktree_config: WorktreeConfig | None = None,
1329
1338
  evidence_config: EvidenceConfig | None = None,
1330
1339
  retry_config: RetryConfig | None = None,
1340
+ branch_config: BranchConfig | None = None,
1331
1341
  ) -> None:
1332
1342
  """Run the workspace (multi-project) agent loop.
1333
1343
 
@@ -1349,5 +1359,6 @@ async def run_workspace_agent_loop(
1349
1359
  worktree_config=worktree_config,
1350
1360
  evidence_config=evidence_config,
1351
1361
  retry_config=retry_config,
1362
+ branch_config=branch_config,
1352
1363
  )
1353
1364
  await agent.start()
@@ -1719,6 +1719,13 @@ def run(
1719
1719
  help="Delay between retries in seconds (default: from config or 3600)",
1720
1720
  ),
1721
1721
  ] = None,
1722
+ default_branch: Annotated[
1723
+ str | None,
1724
+ typer.Option(
1725
+ "--default-branch",
1726
+ help="Default branch for sync/rebase/PR targets (default: from config or 'main')",
1727
+ ),
1728
+ ] = None,
1722
1729
  ) -> None:
1723
1730
  """Run the agent for a project!
1724
1731
 
@@ -1820,6 +1827,11 @@ def run(
1820
1827
  if retry_delay is not None:
1821
1828
  retry_config.delay_seconds = retry_delay
1822
1829
 
1830
+ # Resolve branch config: CLI > config > default
1831
+ resolved_branch_config = config.branches.model_copy()
1832
+ if default_branch is not None:
1833
+ resolved_branch_config.default_branch = default_branch
1834
+
1823
1835
  try:
1824
1836
  result = asyncio.run(
1825
1837
  run_agent(
@@ -1838,6 +1850,7 @@ def run(
1838
1850
  force_workflow_id=resolved_workflow_id,
1839
1851
  dry_run=dry_run,
1840
1852
  retry_config=retry_config,
1853
+ branch_config=resolved_branch_config,
1841
1854
  )
1842
1855
  )
1843
1856
 
@@ -1998,6 +2011,13 @@ def agent(
1998
2011
  help="Submit evidence reports after task completion (default: from config or enabled)",
1999
2012
  ),
2000
2013
  ] = None,
2014
+ default_branch: Annotated[
2015
+ str | None,
2016
+ typer.Option(
2017
+ "--default-branch",
2018
+ help="Default branch for sync/rebase/PR targets (default: from config or 'main')",
2019
+ ),
2020
+ ] = None,
2001
2021
  ) -> None:
2002
2022
  """Run the agent in persistent mode.
2003
2023
 
@@ -2075,6 +2095,11 @@ def agent(
2075
2095
  if retry_delay is not None:
2076
2096
  retry_config.delay_seconds = retry_delay
2077
2097
 
2098
+ # Resolve branch config: CLI > config > default
2099
+ resolved_branch_config = config.branches.model_copy()
2100
+ if default_branch is not None:
2101
+ resolved_branch_config.default_branch = default_branch
2102
+
2078
2103
  # Resolve workspace path: CLI > config
2079
2104
  resolved_workspace = workspace or config.workspace.workspace_dir
2080
2105
 
@@ -2099,6 +2124,7 @@ def agent(
2099
2124
  worktree_config=resolved_worktree_config,
2100
2125
  evidence_config=resolved_evidence_config,
2101
2126
  retry_config=retry_config,
2127
+ branch_config=resolved_branch_config,
2102
2128
  )
2103
2129
  )
2104
2130
  except KeyboardInterrupt:
@@ -2140,6 +2166,7 @@ def agent(
2140
2166
  worktree_config=resolved_worktree_config,
2141
2167
  evidence_config=resolved_evidence_config,
2142
2168
  retry_config=retry_config,
2169
+ branch_config=resolved_branch_config,
2143
2170
  )
2144
2171
  )
2145
2172
  except KeyboardInterrupt:
@@ -246,6 +246,22 @@ class CanalConfig(BaseModel):
246
246
  ]
247
247
 
248
248
 
249
+ class BranchConfig(BaseModel):
250
+ """Branch configuration for git operations.
251
+
252
+ Controls which branch is used for pre-flight sync, rebase targets, and PR base.
253
+ Users set default_branch to whatever their project uses — main, dev, develop, etc.
254
+ """
255
+
256
+ default_branch: Annotated[
257
+ str,
258
+ Field(
259
+ default="main",
260
+ description="Default branch for sync, rebase, and PR targets (e.g. main, dev, develop)",
261
+ ),
262
+ ]
263
+
264
+
249
265
  class EvidenceConfig(BaseModel):
250
266
  """Evidence report configuration.
251
267
 
@@ -341,6 +357,10 @@ class SteerDevConfig(BaseModel):
341
357
  CanalConfig,
342
358
  Field(default_factory=CanalConfig, description="Canal workflow configuration"),
343
359
  ]
360
+ branches: Annotated[
361
+ BranchConfig,
362
+ Field(default_factory=BranchConfig, description="Branch configuration for git operations"),
363
+ ]
344
364
  workspace: Annotated[
345
365
  WorkspaceConfig,
346
366
  Field(default_factory=WorkspaceConfig, description="Workspace agent configuration"),
@@ -68,6 +68,10 @@ class PromptContext(BaseModel):
68
68
  resume_message: str | None = Field(
69
69
  default=None, description="Message for resume (if resuming session)"
70
70
  )
71
+ default_branch: str | None = Field(
72
+ default=None,
73
+ description="Default branch for git operations (e.g. main, dev). Falls back to 'main'.",
74
+ )
71
75
 
72
76
 
73
77
  class PromptBuilder:
@@ -105,7 +109,7 @@ class PromptBuilder:
105
109
 
106
110
  # Build components
107
111
  project_info = self._build_project_info(context.project)
108
- task_info = self._build_task_info(context.task, context.project)
112
+ task_info = self._build_task_info(context.task, context.project, context.default_branch)
109
113
  instructions = self._build_instructions(context)
110
114
 
111
115
  # Add workflow phase if present
@@ -157,12 +161,14 @@ class PromptBuilder:
157
161
  self,
158
162
  task: TaskContext | None,
159
163
  project: ProjectContext | None,
164
+ default_branch: str | None = None,
160
165
  ) -> str:
161
166
  """Build task information section.
162
167
 
163
168
  Args:
164
169
  task: Task context or None.
165
170
  project: Project context for working directory fallback.
171
+ default_branch: Default branch for git operations (falls back to "main").
166
172
 
167
173
  Returns:
168
174
  Formatted task information.
@@ -204,8 +210,14 @@ class PromptBuilder:
204
210
  wave_section += f"\n\n**Tasks in This Wave:**\n{task.wave.wave_tasks_summary}"
205
211
  task_info = f"{task_info}{wave_section}"
206
212
 
207
- # Add git pre-flight sync instructions for new tasks (not resumed sessions)
208
- task_info = f"{task_info}\n\n{self.templates.GIT_PREFLIGHT_SYNC}"
213
+ # Add git branch configuration and pre-flight sync instructions
214
+ branch = default_branch or "main"
215
+ task_info = (
216
+ f"{task_info}\n\n{self.templates.GIT_BRANCH_CONFIG.format(default_branch=branch)}"
217
+ )
218
+ task_info = (
219
+ f"{task_info}\n{self.templates.GIT_PREFLIGHT_SYNC.format(default_branch=branch)}"
220
+ )
209
221
 
210
222
  return task_info
211
223
 
@@ -246,13 +246,20 @@ Create tasks that can be completed by a single developer in a reasonable time (1
246
246
  Larger work should be broken into multiple tasks.
247
247
  """
248
248
 
249
- # Git pre-flight sync instructions injected into task prompts
249
+ # Git pre-flight sync instructions injected into task prompts.
250
+ # Parameterized with {default_branch} — call .format(default_branch=...) before use.
250
251
  GIT_PREFLIGHT_SYNC = """## Git Pre-flight Sync
251
252
  Before starting this task, ensure you are on an up-to-date default branch:
252
253
  1. `git stash push -m "steerdev-preflight" 2>/dev/null || true`
253
- 2. `git checkout main && git pull origin main --ff-only`
254
- 3. If ff-only fails: `git fetch origin && git reset --hard origin/main`
254
+ 2. `git checkout {default_branch} && git pull origin {default_branch} --ff-only`
255
+ 3. If ff-only fails: `git fetch origin && git reset --hard origin/{default_branch}`
255
256
  Only skip this if you are continuing mid-wave on an existing wave branch.
257
+ """
258
+
259
+ # Git branch configuration note injected into task prompts
260
+ GIT_BRANCH_CONFIG = """## Git Branch Configuration
261
+ - Default branch: `{default_branch}`
262
+ Use this branch for all fetch, rebase, and merge operations.
256
263
  """
257
264
 
258
265
  # Phase context instructions appended to phase prompts
@@ -19,7 +19,13 @@ from steerdev_agent.api.events import EventsClient
19
19
  from steerdev_agent.api.runs import RunCreateRequest, RunsClient
20
20
  from steerdev_agent.api.sessions import SessionCreateRequest, SessionsClient
21
21
  from steerdev_agent.api.tasks import TasksClient
22
- from steerdev_agent.config.models import EvidenceConfig, ExecutorConfig, RetryConfig, WorktreeConfig
22
+ from steerdev_agent.config.models import (
23
+ BranchConfig,
24
+ EvidenceConfig,
25
+ ExecutorConfig,
26
+ RetryConfig,
27
+ WorktreeConfig,
28
+ )
23
29
  from steerdev_agent.executor import ExecutorFactory
24
30
  from steerdev_agent.executor.base import AgentExecutor, EventType, StreamEvent
25
31
  from steerdev_agent.executor.claude import ClaudeExecutorError
@@ -277,6 +283,7 @@ class Runner:
277
283
  force_workflow_id: str | None = None,
278
284
  dry_run: bool = False,
279
285
  retry_config: RetryConfig | None = None,
286
+ branch_config: BranchConfig | None = None,
280
287
  shutdown_event: asyncio.Event | None = None,
281
288
  agent_id: str | None = None,
282
289
  ) -> None:
@@ -314,6 +321,7 @@ class Runner:
314
321
  self.workflow_id = force_workflow_id
315
322
  self.dry_run = dry_run
316
323
  self._retry_config = retry_config or RetryConfig()
324
+ self._branch_config = branch_config or BranchConfig()
317
325
  self._shutdown_event = shutdown_event
318
326
 
319
327
  # Executor configuration
@@ -723,6 +731,7 @@ class Runner:
723
731
  linear_identifier=task.get("linear_identifier"),
724
732
  wave=wave_context,
725
733
  ),
734
+ default_branch=self._branch_config.default_branch,
726
735
  )
727
736
  prompt = self._prompt_builder.build(context)
728
737
 
@@ -753,25 +762,45 @@ class Runner:
753
762
  if self._sessions_client and not self.dry_run:
754
763
  await self._sessions_client.mark_running(session_id)
755
764
 
756
- # Set up worktree isolation if enabled
765
+ # Prepare workspace (git fetch, checkout, branch creation)
757
766
  effective_working_dir = self.working_directory
758
767
  worktree_branch: str | None = None
759
768
  legacy_worktree_name: str | None = None
760
769
 
761
- if self._worktree_config.enabled:
762
- if self._worktree_config.provider == "worktrunk":
763
- from steerdev_agent.worktree import WorktrunkClient, compute_branch_name
770
+ if self._worktree_config.enabled and self._worktree_config.provider == "claude":
771
+ # Legacy: pass --worktree to Claude CLI (bypasses preparation)
772
+ legacy_worktree_name = compute_worktree_name(task_id, wave_context)
773
+ console.print(f"[dim]Using legacy worktree: {legacy_worktree_name}[/dim]")
774
+ else:
775
+ from steerdev_agent.workspace.preparation import WorkspacePreparation
764
776
 
765
- wt_client = WorktrunkClient(self.working_directory)
766
- worktree_branch = compute_branch_name(task, wave_context)
767
- effective_working_dir = await wt_client.switch(worktree_branch, create=True)
768
- console.print(
769
- f"[dim]Worktree: {effective_working_dir} (branch: {worktree_branch})[/dim]"
770
- )
771
- else:
772
- # Legacy: pass --worktree to Claude CLI
773
- legacy_worktree_name = compute_worktree_name(task_id, wave_context)
774
- console.print(f"[dim]Using legacy worktree: {legacy_worktree_name}[/dim]")
777
+ preparation = WorkspacePreparation(
778
+ working_directory=self.working_directory,
779
+ worktree_config=self._worktree_config,
780
+ branch_config=self._branch_config,
781
+ )
782
+ prep_result = await preparation.prepare(task, wave_context)
783
+ if not prep_result.success:
784
+ error_msg = f"Workspace preparation failed: {prep_result.error}"
785
+ logger.error(error_msg)
786
+ if self._sessions_client and not self.dry_run:
787
+ await self._sessions_client.mark_failed(
788
+ session_id, metadata={"error": error_msg}
789
+ )
790
+ return {
791
+ "success": False,
792
+ "error": error_msg,
793
+ "events_sent": self._events_sent,
794
+ }
795
+ effective_working_dir = prep_result.working_directory
796
+ if prep_result.is_worktree:
797
+ worktree_branch = prep_result.branch_name
798
+ console.print(
799
+ f"[dim]Workspace ready: {effective_working_dir} "
800
+ f"(branch: {prep_result.branch_name})[/dim]"
801
+ )
802
+ for warning in prep_result.warnings:
803
+ console.print(f"[yellow]Warning: {warning}[/yellow]")
775
804
 
776
805
  # Create executor using factory
777
806
  self._executor = ExecutorFactory.create(
@@ -948,19 +977,29 @@ class Runner:
948
977
  resolved_wf_id = workflow_id or self.workflow_id
949
978
  console.print(f"[dim]Using workflow: {resolved_wf_id}[/dim]")
950
979
 
951
- # Set up worktree isolation if enabled
980
+ # Prepare workspace (git fetch, checkout, branch creation)
952
981
  effective_working_dir = self.working_directory
953
982
  worktree_branch: str | None = None
954
983
 
955
- if self._worktree_config.enabled and self._worktree_config.provider == "worktrunk":
956
- from steerdev_agent.worktree import WorktrunkClient, compute_branch_name
984
+ from steerdev_agent.workspace.preparation import WorkspacePreparation
957
985
 
958
- wt_client = WorktrunkClient(self.working_directory)
959
- worktree_branch = compute_branch_name(task, wave_context)
960
- effective_working_dir = await wt_client.switch(worktree_branch, create=True)
961
- console.print(
962
- f"[dim]Worktree: {effective_working_dir} (branch: {worktree_branch})[/dim]"
963
- )
986
+ preparation = WorkspacePreparation(
987
+ working_directory=self.working_directory,
988
+ worktree_config=self._worktree_config,
989
+ branch_config=self._branch_config,
990
+ )
991
+ prep_result = await preparation.prepare(task, wave_context)
992
+ if not prep_result.success:
993
+ error_msg = f"Workspace preparation failed: {prep_result.error}"
994
+ logger.error(error_msg)
995
+ return {"success": False, "error": error_msg}
996
+ effective_working_dir = prep_result.working_directory
997
+ if prep_result.is_worktree:
998
+ worktree_branch = prep_result.branch_name
999
+ console.print(
1000
+ f"[dim]Workspace ready: {effective_working_dir} "
1001
+ f"(branch: {prep_result.branch_name})[/dim]"
1002
+ )
964
1003
 
965
1004
  # Build task context for workflow
966
1005
  task_context = build_workflow_task_context(
@@ -1354,6 +1393,7 @@ async def run_agent(
1354
1393
  force_workflow_id: str | None = None,
1355
1394
  dry_run: bool = False,
1356
1395
  retry_config: RetryConfig | None = None,
1396
+ branch_config: BranchConfig | None = None,
1357
1397
  ) -> dict[str, Any]:
1358
1398
  """Run the steerdev agent.
1359
1399
 
@@ -1377,6 +1417,7 @@ async def run_agent(
1377
1417
  force_workflow_id: Workflow ID override for multi-phase execution.
1378
1418
  dry_run: If True, print the command without executing it.
1379
1419
  retry_config: Retry configuration for failed tasks.
1420
+ branch_config: Branch configuration for git operations.
1380
1421
 
1381
1422
  Returns:
1382
1423
  Run result metadata.
@@ -1399,6 +1440,7 @@ async def run_agent(
1399
1440
  force_workflow_id=force_workflow_id,
1400
1441
  dry_run=dry_run,
1401
1442
  retry_config=retry_config,
1443
+ branch_config=branch_config,
1402
1444
  )
1403
1445
 
1404
1446
  # Install signal handler that kills the subprocess immediately on Ctrl+C
@@ -18,20 +18,20 @@ Use this skill when a workflow phase tells you to merge the **current task branc
18
18
 
19
19
  ## Pre-check
20
20
 
21
- Before merging into a canal, verify your current branch has been pushed and has a PR. Do NOT switch branches or sync to main — this skill operates on the current task/wave branch.
21
+ Before merging into a canal, verify your current branch has been pushed and has a PR. Do NOT switch branches or sync to the default branch — this skill operates on the current task/wave branch.
22
22
 
23
23
  ## Rebase Before Canal Merge
24
24
 
25
- Before merging into the canal, ensure your branch is up to date:
25
+ Before merging into the canal, ensure your branch is up to date. Refer to the **Git Branch Configuration** section in your prompt for the project's default branch name.
26
26
 
27
27
  ```bash
28
- git fetch origin main
29
- git merge-tree --write-tree origin/main HEAD
28
+ git fetch origin <default-branch>
29
+ git merge-tree --write-tree origin/<default-branch> HEAD
30
30
  ```
31
31
 
32
32
  - **If clean** (exit 0): Rebase and force-push with lease:
33
33
  ```bash
34
- git rebase origin/main
34
+ git rebase origin/<default-branch>
35
35
  git push --force-with-lease origin HEAD
36
36
  ```
37
37
  - **If conflicts**: Attempt rebase. If it fails on files you did not modify, abort (`git rebase --abort`) and re-queue the task:
@@ -75,6 +75,7 @@ steerdev canal pr CANAL_ID
75
75
  - Do not force-push to resolve canal conflicts
76
76
  - Do not manually resolve canal merge conflicts outside the supported tooling
77
77
  - If canal merge fails, report the blocker with task comments or status updates
78
+ - Replace `<default-branch>` in commands above with the actual branch name from the Git Branch Configuration section
78
79
 
79
80
  ## Example
80
81
 
@@ -14,36 +14,16 @@ Use this skill when a workflow phase tells you to complete **one task on one bra
14
14
  - One pull request
15
15
  - One task result update that includes the PR URL
16
16
 
17
- ## Worktree Mode
17
+ ## Workspace Ready
18
18
 
19
- If you are running in a **worktrunk worktree**, you are already on the correct branch in an isolated directory. **Skip branch creation and pre-flight sync** — go directly to implementation.
19
+ The CLI has already prepared your workspace: fetched the latest changes, synced to the default branch, and created or checked out your task branch. **Do not run pre-flight sync steps** — proceed directly to implementation.
20
20
 
21
- ## Pre-flight Sync (non-worktree mode only)
22
-
23
- If NOT in a worktrunk worktree, sync before creating your task branch:
24
-
25
- 1. Stash any uncommitted changes (if any):
26
- ```bash
27
- git stash push -m "steerdev-preflight" 2>/dev/null || true
28
- ```
29
-
30
- 2. Switch to the default branch and pull latest:
31
- ```bash
32
- git checkout main && git pull origin main --ff-only
33
- ```
34
- If `main` doesn't exist, try `master`. If `--ff-only` fails (local main diverged), force-reset:
35
- ```bash
36
- git fetch origin && git reset --hard origin/main
37
- ```
38
-
39
- 3. Now proceed to create your task branch from the up-to-date default branch.
40
-
41
- **Why:** The agent may still be on a previous task's branch. Without this step, new branches are created from stale code, causing desync with merged PRs.
21
+ Refer to the **Git Branch Configuration** section in your prompt for the project's default branch name.
42
22
 
43
23
  ## Workflow
44
24
 
45
25
  1. Ensure the task is already marked `started`
46
- 2. Create or switch to the task branch (skip if in a worktrunk worktree):
26
+ 2. Create or switch to the task branch (if not already on it):
47
27
 
48
28
  ```bash
49
29
  steerdev git branch TASK_ID [--name NAME]
@@ -56,26 +36,26 @@ This fetches the task from the API and builds the branch name automatically:
56
36
 
57
37
  3. Implement the requested changes
58
38
  4. Commit with a conventional commit message that explains why the change exists
59
- 5. Rebase onto the latest target branch before pushing:
39
+ 5. Rebase onto the latest default branch before pushing:
60
40
 
61
41
  ```bash
62
- git fetch origin main
42
+ git fetch origin <default-branch>
63
43
  ```
64
44
 
65
45
  Dry-run merge check (requires git 2.38+):
66
46
  ```bash
67
- git merge-tree --write-tree origin/main HEAD
47
+ git merge-tree --write-tree origin/<default-branch> HEAD
68
48
  ```
69
49
 
70
50
  - **If exit code 0** (clean merge): Rebase and push:
71
51
  ```bash
72
- git rebase origin/main
52
+ git rebase origin/<default-branch>
73
53
  git push -u origin HEAD
74
54
  ```
75
55
 
76
56
  - **If exit code non-zero** (conflicts detected): Attempt rebase:
77
57
  ```bash
78
- git rebase origin/main
58
+ git rebase origin/<default-branch>
79
59
  ```
80
60
  - If rebase succeeds after resolving conflicts in files you modified: `git push --force-with-lease origin HEAD`
81
61
  - If conflicts are in files you did NOT modify: abort the rebase (`git rebase --abort`) and re-queue the task for a fresh attempt:
@@ -95,22 +75,12 @@ steerdev git pr --title "TITLE" --task-id TASK_ID [--body "BODY"]
95
75
  7. Update the task result with the PR URL
96
76
  8. Log completion in the root `docs/` progress files
97
77
 
98
- ## Branch Naming Convention
99
-
100
- Branch names should link to the external ticket tracker (e.g. Linear) for traceability on GitHub:
101
-
102
- | Has External ID? | Format | Example |
103
- |---|---|---|
104
- | Yes | `task/<TICKET-ID>-<slug>` | `task/PROJ-123-add-user-auth` |
105
- | No | `task/<short-uuid>-<slug>` | `task/abc12345-add-user-auth` |
106
-
107
- **Why this matters:** GitHub integrations (Linear, Jira, etc.) match ticket IDs in branch names to link PRs/branches back to issues. Using internal UUIDs breaks this linkage.
108
-
109
78
  ## Conventions
110
79
 
111
80
  - Always use `steerdev git branch` to create branches (it resolves the external ticket ID automatically)
112
81
  - Create exactly one PR for the task
113
82
  - Include the PR URL in the task result summary
83
+ - Replace `<default-branch>` in commands above with the actual branch name from the Git Branch Configuration section
114
84
 
115
85
  ## Example
116
86
 
@@ -14,28 +14,15 @@ Use this skill when a workflow phase tells you to complete **multiple related ta
14
14
  - One shared pull request for the wave
15
15
  - All related tasks updated with the same PR URL
16
16
 
17
- ## Worktree Mode
17
+ ## Workspace Ready
18
18
 
19
- If you are running in a **worktrunk worktree**, you are already on the correct `wave/<N>` branch in an isolated directory. **Skip branch creation and pre-flight sync** — go directly to implementation.
19
+ The CLI has already prepared your workspace: fetched the latest changes, synced to the default branch (for new waves), or stayed on the current wave branch (for continuing waves). **Do not run pre-flight sync steps** — proceed directly to implementation.
20
20
 
21
- ## Pre-flight Sync (non-worktree mode only)
22
-
23
- If NOT in a worktrunk worktree, check if you need to sync before creating the wave branch:
24
-
25
- - **Starting a new wave** (not on `wave/<N>` branch yet): Sync to default branch first:
26
- ```bash
27
- git stash push -m "steerdev-preflight" 2>/dev/null || true
28
- git checkout main && git pull origin main --ff-only
29
- ```
30
- If `--ff-only` fails: `git fetch origin && git reset --hard origin/main`
31
-
32
- - **Continuing a wave** (already on `wave/<N>` branch): Do NOT switch to main. Stay on the wave branch and continue with the next task.
33
-
34
- **Why:** The agent may still be on a previous task/wave branch. New waves must branch from updated main. Mid-wave tasks must stay on the wave branch.
21
+ Refer to the **Git Branch Configuration** section in your prompt for the project's default branch name.
35
22
 
36
23
  ## Workflow
37
24
 
38
- 1. Create or switch to the shared wave branch (skip if in a worktrunk worktree):
25
+ 1. Create or switch to the shared wave branch (if not already on it):
39
26
 
40
27
  ```bash
41
28
  git checkout -b wave/<wave-number>
@@ -54,26 +41,26 @@ git commit -m "[PROJ-123] feat: add user endpoint"
54
41
  git commit -m "[task:abc12345] feat: add user endpoint"
55
42
  ```
56
43
 
57
- 3. After the wave work is ready for review, rebase onto the latest target branch:
44
+ 3. After the wave work is ready for review, rebase onto the latest default branch:
58
45
 
59
46
  ```bash
60
- git fetch origin main
47
+ git fetch origin <default-branch>
61
48
  ```
62
49
 
63
50
  Dry-run merge check (requires git 2.38+):
64
51
  ```bash
65
- git merge-tree --write-tree origin/main HEAD
52
+ git merge-tree --write-tree origin/<default-branch> HEAD
66
53
  ```
67
54
 
68
55
  - **If exit code 0** (clean merge): Rebase and push:
69
56
  ```bash
70
- git rebase origin/main
57
+ git rebase origin/<default-branch>
71
58
  git push -u origin HEAD
72
59
  ```
73
60
 
74
61
  - **If exit code non-zero** (conflicts detected): Attempt rebase:
75
62
  ```bash
76
- git rebase origin/main
63
+ git rebase origin/<default-branch>
77
64
  ```
78
65
  - If rebase succeeds after resolving conflicts in files you modified: `git push --force-with-lease origin HEAD`
79
66
  - If conflicts are in files you did NOT modify: abort the rebase (`git rebase --abort`) and re-queue the current task:
@@ -110,6 +97,7 @@ Run `steerdev tasks get TASK_ID` to find the task's `linear_identifier` before c
110
97
  - Do not create per-task PRs inside the wave
111
98
  - Keep commits traceable by including the external ticket ID (or internal ID fallback) in each commit
112
99
  - Include the shared PR URL in every related task result
100
+ - Replace `<default-branch>` in commands above with the actual branch name from the Git Branch Configuration section
113
101
 
114
102
  ## Example
115
103
 
@@ -72,6 +72,11 @@ worktrees:
72
72
  # Commands to run in pre-merge hook (empty = use worktrunk.config.toml defaults)
73
73
  pre_merge_checks: []
74
74
 
75
+ # Branch configuration for git operations
76
+ branches:
77
+ # Default branch for sync, rebase, and PR targets (e.g. main, dev, develop)
78
+ default_branch: main
79
+
75
80
  # Evidence reports: submit a review-ready report after each task/workflow
76
81
  evidence:
77
82
  enabled: true