Skip to content
Open
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
64 changes: 30 additions & 34 deletions setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,17 @@ else
fixable "claude"
fi

# 4. Agent teams enabled
# 4. Agent teams enabled — ZO-scoped file preferred, global settings accepted
echo -e "${DIM}Checking agent teams...${RESET}"
SETTINGS_FILE="$HOME/.claude/settings.json"
if [[ -f "$SETTINGS_FILE" ]]; then
if grep -q "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS" "$SETTINGS_FILE" 2>/dev/null; then
pass "Agent teams enabled in global settings"
else
fail "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS not set in $SETTINGS_FILE"
fixable "agent-teams-env"
fi
ZO_SETTINGS_FILE="${ZO_CLAUDE_SETTINGS:-$HOME/.zo/settings.json}"
if grep -q "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS" "$ZO_SETTINGS_FILE" 2>/dev/null; then
pass "Agent teams enabled in ZO settings ($ZO_SETTINGS_FILE)"
elif grep -q "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS" "$SETTINGS_FILE" 2>/dev/null; then
pass "Agent teams enabled in global settings"
else
fail "Global settings not found at $SETTINGS_FILE"
fixable "global-settings"
fail "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS not set in $ZO_SETTINGS_FILE or $SETTINGS_FILE"
fixable "agent-teams-env"
fi

# 5. Project settings.json
Expand Down Expand Up @@ -208,17 +206,34 @@ fix_item() {
echo -e " ${RED}✗${RESET} Claude CLI install failed — try manually: curl -fsSL https://claude.ai/install.sh | bash"
return 1
;;
global-settings)
echo -e " ${AMBER}→${RESET} Creating global settings at $SETTINGS_FILE..."
mkdir -p "$HOME/.claude"
cat > "$SETTINGS_FILE" << 'SETTINGS_EOF'
agent-teams-env)
echo -e " ${AMBER}→${RESET} Enabling agent teams in $ZO_SETTINGS_FILE..."
mkdir -p "$(dirname "$ZO_SETTINGS_FILE")"
if [[ -f "$ZO_SETTINGS_FILE" ]]; then
# Merge into existing ZO settings without clobbering other keys
if python3 -c "
import json
with open('$ZO_SETTINGS_FILE') as f:
data = json.load(f)
data.setdefault('env', {})['CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS'] = '1'
with open('$ZO_SETTINGS_FILE', 'w') as f:
json.dump(data, f, indent=2)
f.write('\n')
" 2>/dev/null; then
echo -e " ${GREEN}✓${RESET} Agent teams env merged into ZO settings"
return 0
fi
echo -e " ${RED}✗${RESET} Failed to update $ZO_SETTINGS_FILE — add manually"
return 1
fi
cat > "$ZO_SETTINGS_FILE" << 'SETTINGS_EOF'
{
"env": {
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1"
}
}
SETTINGS_EOF
echo -e " ${GREEN}✓${RESET} Global settings created with agent teams enabled"
echo -e " ${GREEN}✓${RESET} ZO settings created with agent teams enabled"
return 0
;;
zo-cli)
Expand Down Expand Up @@ -247,25 +262,6 @@ SETTINGS_EOF
echo -e " ${RED}✗${RESET} zo symlink created but not working — check PATH includes ~/.local/bin"
return 1
;;
agent-teams-env)
echo -e " ${AMBER}→${RESET} Adding agent teams env to $SETTINGS_FILE..."
# Use python to merge the env key into existing settings
if python3 -c "
import json, sys
with open('$SETTINGS_FILE') as f:
data = json.load(f)
data.setdefault('env', {})['CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS'] = '1'
with open('$SETTINGS_FILE', 'w') as f:
json.dump(data, f, indent=2)
f.write('\n')
" 2>/dev/null; then
echo -e " ${GREEN}✓${RESET} Agent teams env added to global settings"
return 0
else
echo -e " ${RED}✗${RESET} Failed to update settings — add manually"
return 1
fi
;;
esac
}

Expand Down
22 changes: 22 additions & 0 deletions src/zo/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@
re.compile(r"too many requests", re.IGNORECASE),
]

_ZO_USER_SETTINGS = Path.home() / ".zo" / "settings.json"


def zo_user_settings_path() -> Path | None:
"""Return the ZO-scoped Claude settings file, if present.

``~/.zo/settings.json`` (override via ``ZO_CLAUDE_SETTINGS``) is passed
to every spawned Claude session via ``--settings``, so ZO-specific
config such as ``CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`` never has to
live in the user's global ``~/.claude/settings.json``.
"""
path = Path(os.environ.get("ZO_CLAUDE_SETTINGS") or _ZO_USER_SETTINGS)
return path if path.is_file() else None


class LifecycleWrapper:
"""Manages the lifecycle of a Claude Code lead orchestrator session.
Expand Down Expand Up @@ -229,11 +243,16 @@ def _launch_tmux(
env_prefix = ""
for k, v in (extra_env or {}).items():
env_prefix += f'{k}={shlex.quote(v)} '
settings_flag = ""
zo_settings = zo_user_settings_path()
if zo_settings:
settings_flag = f' --settings {shlex.quote(str(zo_settings))}'
interactive_cmd = (
f'{env_prefix}'
f'{shlex.quote(claude_abs)}'
f' --model {shlex.quote(model)}'
f' --max-turns {max_turns}'
f'{settings_flag}'
f'{add_dir_flags}'
)
subprocess.run(
Expand Down Expand Up @@ -439,6 +458,9 @@ def _launch_headless(
"--max-turns", str(max_turns),
"--add-dir", cwd,
]
zo_settings = zo_user_settings_path()
if zo_settings:
cmd.extend(["--settings", str(zo_settings)])
if bypass_permissions:
from zo.permissions_overlay import ensure_bypass_disclaimer_accepted
# --dangerously-skip-permissions / bypass mode refuses to start
Expand Down