Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions profiles/agents/packages/hive_cli/src/hive_cli/commands/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
from cyclopts import App, Parameter

from ..agents import detect_agent
from ..config import KNOWN_AGENTS, get_agent_config, get_runtime_settings, get_settings
from ..config import (
KNOWN_AGENTS,
get_agent_config,
get_extra_dirs_args,
get_runtime_settings,
get_settings,
)
from ..utils import error, format_yellow
from .exec_runner import run_in_worktree

Expand Down Expand Up @@ -235,6 +241,8 @@ def run_with_dynamic_agent(command: list[str]) -> int:
if current_agent_config:
skip_perm_args = current_agent_config.skip_permissions_args

extra_dir_args = get_extra_dirs_args(current_agent_name)

# Handle resume logic if enabled and agent has resume args
if resume:
current_agent_config = get_agent_config(current_agent_name)
Expand All @@ -244,6 +252,7 @@ def run_with_dynamic_agent(command: list[str]) -> int:
current_cmd[0],
*current_agent_config.resume_args,
*skip_perm_args,
*extra_dir_args,
*args,
]
child_env = get_runtime_settings().build_child_env()
Expand All @@ -256,9 +265,14 @@ def run_with_dynamic_agent(command: list[str]) -> int:
return 0
# Resume failed, fall back to base command

# Build final command with skip-permissions args
if skip_perm_args:
final_cmd = [current_cmd[0], *skip_perm_args, *current_cmd[1:]]
# Build final command with skip-permissions and extra-dirs args
if skip_perm_args or extra_dir_args:
final_cmd = [
current_cmd[0],
*skip_perm_args,
*extra_dir_args,
*current_cmd[1:],
]
else:
final_cmd = current_cmd

Expand All @@ -267,12 +281,21 @@ def run_with_dynamic_agent(command: list[str]) -> int:
result = subprocess.run(final_cmd, env=child_env)
return result.returncode

# Compute extra-dirs args for initial agent
initial_extra_dirs = get_extra_dirs_args(detected.name)
has_extra_dirs = bool(initial_extra_dirs)

# Use dynamic runner when:
# - restart/restart_confirmation mode (needs restart loop)
# - resume is enabled AND agent has resume_args (needs retry logic)
# - skip-permissions is enabled (needs arg injection)
# - extra_dirs configured (needs arg injection per agent)
use_dynamic_runner = (
restart or restart_confirmation or has_resume_args or has_skip_permissions
restart
or restart_confirmation
or has_resume_args
or has_skip_permissions
or has_extra_dirs
)

# Determine auto_select settings: CLI overrides config
Expand All @@ -281,14 +304,15 @@ def run_with_dynamic_agent(command: list[str]) -> int:
if auto_select_branch is None and config.worktrees.auto_select.enabled:
auto_select_branch = config.worktrees.auto_select.branch

# Build initial command with skip-permissions args if applicable
# Build initial command with skip-permissions and extra-dirs args if applicable
initial_skip_args: list[str] = []
if rt.skip_permissions:
init_agent_config = get_agent_config(detected.name)
if init_agent_config:
initial_skip_args = init_agent_config.skip_permissions_args
if initial_skip_args:
initial_cmd = [detected.command, *initial_skip_args, *args]
injected_args = [*initial_skip_args, *initial_extra_dirs]
if injected_args:
initial_cmd = [detected.command, *injected_args, *args]
else:
initial_cmd = [detected.command, *args]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,40 @@ def get_agent_order() -> list[str]:
return settings.agents.order


def get_extra_dirs_args(agent_name: str) -> list[str]:
"""Build CLI arguments for extra directories.

Reads extra_dirs from settings, resolves each path relative to the main
repo root (so relative paths work identically from worktrees), then
pairs each resolved path with the agent's extra_dirs_flag.

Args:
agent_name: Name of the agent (to look up extra_dirs_flag).

Returns:
List like [flag, path1, flag, path2, ...], or [] if no dirs or
the agent has no extra_dirs_flag configured.
"""
from ..git import expand_path, get_main_repo

settings = get_settings()
dirs = settings.extra_dirs
if not dirs:
return []

agent_cfg = settings.agents.configs.get(agent_name, AgentConfig())
flag = agent_cfg.extra_dirs_flag
if not flag:
return []

main_repo = get_main_repo()
result: list[str] = []
for d in dirs:
resolved = expand_path(d, main_repo)
result.extend([flag, str(resolved)])
return result


__all__ = [
# Base
"HiveBaseSettings",
Expand Down Expand Up @@ -165,4 +199,5 @@ def get_agent_order() -> list[str]:
# Helpers
"get_agent_config",
"get_agent_order",
"get_extra_dirs_args",
]
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,32 @@ agents:
- copilot

configs:
# Claude uses --continue flag
claude:
resume_args: ["--continue"]
skip_permissions_args: ["--dangerously-skip-permissions"]
extra_dirs_flag: "--add-dir"

# Copilot uses --continue flag
copilot:
resume_args: ["--continue"]
skip_permissions_args: ["--allow-all"]
extra_dirs_flag: "--add-dir"

# Codex uses resume subcommand with --last
codex:
resume_args: ["resume", "--last"]
skip_permissions_args: ["--full-auto"]
extra_dirs_flag: "--add-dir"

# Gemini uses --resume with latest argument
gemini:
resume_args: ["--resume", "latest"]
skip_permissions_args: ["-y"]
extra_dirs_flag: "--include-directories"

# Cursor agent CLI uses resume subcommand
# Cursor agent CLI - no extra dirs support
agent:
resume_args: ["resume"]
skip_permissions_args: ["-f"]

# Cursor agent alternative name
# Cursor agent alternative name - no extra dirs support
cursor-agent:
resume_args: ["resume"]
skip_permissions_args: ["-f"]
Expand Down Expand Up @@ -84,3 +84,7 @@ zellij:
github:
fetch_issues: true
issue_limit: 20

# Additional directories to pass to the agent via its extra_dirs_flag.
# Relative paths resolve against the main repo root.
extra_dirs: []
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ class AgentConfig(BaseModel):
Attributes:
resume_args: Arguments to add for resume functionality.
skip_permissions_args: Arguments to add for skip-permissions mode.
extra_dirs_flag: CLI flag the agent uses for additional directories
(e.g., "--add-dir" for Claude, "--directory" for Cursor).
"""

resume_args: Annotated[list[str], Field(default_factory=list)]
skip_permissions_args: Annotated[list[str], Field(default_factory=list)]
extra_dirs_flag: str | None = None


class AgentsConfig(HiveBaseSettings):
Expand Down Expand Up @@ -166,10 +169,13 @@ class HiveConfig(BaseModel):
worktrees: Git worktree configuration.
zellij: Zellij configuration.
github: GitHub integration configuration.
extra_dirs: Additional directories to pass to the agent.
Relative paths are resolved against the main repo root.
"""

agents: Annotated[AgentsConfig, Field(default_factory=AgentsConfig)]
resume: Annotated[ResumeConfig, Field(default_factory=ResumeConfig)]
worktrees: Annotated[WorktreesConfig, Field(default_factory=WorktreesConfig)]
zellij: Annotated[ZellijConfig, Field(default_factory=ZellijConfig)]
github: Annotated[GitHubConfig, Field(default_factory=GitHubConfig)]
extra_dirs: Annotated[list[str], Field(default_factory=list)]
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class HiveSettings(HiveBaseSettings):
worktrees: Annotated[WorktreesConfig, Field(default_factory=WorktreesConfig)]
zellij: Annotated[ZellijConfig, Field(default_factory=ZellijConfig)]
github: Annotated[GitHubConfig, Field(default_factory=GitHubConfig)]
extra_dirs: Annotated[list[str], Field(default_factory=list)]

@classmethod
def settings_customise_sources(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
WorktreeInfo,
create_worktree,
delete_worktree,
expand_path,
fetch_origin,
get_all_branches,
get_current_branch,
Expand All @@ -21,6 +22,7 @@
"WorktreeInfo",
"create_worktree",
"delete_worktree",
"expand_path",
"fetch_origin",
"get_all_branches",
"get_current_branch",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def sanitize_branch_name(branch: str) -> str:
return sanitized.strip("-")


def _expand_path(path_str: str, main_repo: Path) -> Path:
def expand_path(path_str: str, main_repo: Path) -> Path:
"""Expand a path string, handling ~, env vars, and relative paths.

Args:
Expand All @@ -65,7 +65,7 @@ def _expand_path(path_str: str, main_repo: Path) -> Path:

# If relative, resolve against main_repo
if not path.is_absolute():
path = main_repo / path
path = (main_repo / path).resolve()

return path

Expand Down Expand Up @@ -118,7 +118,7 @@ def get_worktrees_base(main_repo: Path | None = None) -> Path:
# collide, so the base already groups by repo name implicitly
pass

return _expand_path(template, main_repo)
return expand_path(template, main_repo)


def _find_existing_worktree(branch: str, main_repo: Path) -> Path | None:
Expand Down Expand Up @@ -160,7 +160,7 @@ def _compute_worktree_path(branch: str, main_repo: Path) -> Path:
expanded = template.replace("{repo}", repo_name).replace(
"{branch}", safe_branch
)
return _expand_path(expanded, main_repo)
return expand_path(expanded, main_repo)

base = get_worktrees_base(main_repo)

Expand Down
Loading