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
5 changes: 4 additions & 1 deletion agents/skills/agent-team/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ Close a worker as soon as its last unit is accepted.

Default team: Opus 5.5 orchestrates and designs, `pi` does the grind (implement, refactor, tests), Gemini 4 Argon reviews and writes, Gemini 3.8 Flash does web research.

`--kind` from the Kind column. This machine pins each kind to its first model above. For Gemini 3.8 Flash, start agy with `-- --model gemini-3.8-flash-medium`. Pass other native args after `--` only when live `herdr agent start` help lists them.
`--kind` from the Kind column. This machine pins each kind to its first model above. For Gemini 3.8 Flash, start agy with `-- --model gemini-3.8-flash-medium`. Start codex with `-- --dangerously-bypass-hook-trust`, or it can stop on a hook review screen.

Workers never need approval: Herdr starts claude and agy with `--dangerously-skip-permissions`, codex config sets `approval_policy = "never"`, and pi has no approvals. A trust or approval dialog on a worker is a config bug: answer it once, then tell the user.

For bulk and web research, agy writes `report.md` and the orchestrator reads only that file.

Expand Down Expand Up @@ -121,6 +123,7 @@ pane="$(herdr pane split --current --direction right --cwd "$PWD" --no-focus | j
# New tab (always pass --workspace, or Herdr uses the focused workspace):
pane="$(herdr tab create --workspace "$HERDR_WORKSPACE_ID" --label "<tab label>" --cwd "$PWD" --no-focus | jq -r .result.root_pane.pane_id)"

trust-workspace "$PWD" # trust this dir in every harness (idempotent)
herdr pane rename "$pane" "Implement (pi)"
herdr agent start "$(echo "$pane" | tr 'A-Z:' 'a-z-')" --kind pi --pane "$pane"
herdr agent prompt "$pane" "Implement <unit>. Write /tmp/agent-team/<team>/impl.md. Stop when done." --wait --until working --until blocked --timeout 10000
Expand Down
1 change: 1 addition & 0 deletions codex/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ approval_policy = "never"
sandbox_mode = "danger-full-access"
notify = ["sh", "-lc", "$HOME/.agents/hooks/jj-snapshot.sh"]
commit_attribution = ""
check_for_update_on_startup = false

[tui]
status_line = ["model-with-reasoning", "context-remaining", "current-dir"]
Expand Down
105 changes: 8 additions & 97 deletions herdr/start-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ end
# --- Agent selection ---

function select_agent
set -l agents claude codex cursor agy pi
set -l agents claude codex agy pi
set -l last_agent (_read_state last-agent)
if test -n "$last_agent"; and contains -- "$last_agent" $agents
set agents $last_agent (string match -v -- "$last_agent" $agents)
Expand All @@ -322,9 +322,6 @@ function select_agent
case claude
set -g agent_kind claude
set -g agent_bin claude
case cursor
set -g agent_kind cursor
set -g agent_bin cursor
case '*'
echo "Unknown agent selection: $agent_choice" >&2
return 1
Expand All @@ -341,98 +338,12 @@ end

# --- Workspace trust ---

function _trust_claude --argument-names cwd
command -q jq; or return 0
set -l claude_file "$HOME/.claude.json"
mkdir -p (dirname "$claude_file")
if test -f "$claude_file"
set -l tmp (mktemp)
if jq --arg cwd "$cwd" \
'.projects = ((.projects // {}) | .[$cwd] = ((.[$cwd] // {}) | .hasTrustDialogAccepted = true))' \
"$claude_file" >"$tmp" 2>/dev/null
mv "$tmp" "$claude_file"
else
rm -f "$tmp"
end
else
jq -n --arg cwd "$cwd" \
'{"projects": {($cwd): {"hasTrustDialogAccepted": true}}}' >"$claude_file" 2>/dev/null
end
end

function _trust_codex --argument-names cwd
set -l codex_file "$HOME/.codex/config.toml"
mkdir -p (dirname "$codex_file")
if not test -f "$codex_file"; or not grep -qF "[projects.\"$cwd\"]" "$codex_file" 2>/dev/null
printf '\n[projects."%s"]\ntrust_level = "trusted"\n' "$cwd" >>"$codex_file"
end
end

function _trust_cursor --argument-names cwd
command -q jq; or return 0
set -l slug (echo "$cwd" | string replace -r '^/' '' | string replace -ra '[^a-zA-Z0-9]+' '-')
set -l cursor_dir "$HOME/.cursor/projects/$slug"
mkdir -p "$cursor_dir"
set -l ts (date -u +'%Y-%m-%dT%H:%M:%S.000Z')
jq -n --arg ts "$ts" --arg cwd "$cwd" \
'{"trustedAt": $ts, "workspacePath": $cwd, "trustMethod": "cli-flag"}' >"$cursor_dir/.workspace-trusted" 2>/dev/null
end

function _trust_agy --argument-names cwd
command -q jq; or return 0
set -l agy_file "$HOME/.gemini/antigravity-cli/settings.json"
mkdir -p (dirname "$agy_file")
if test -f "$agy_file"
set -l tmp (mktemp)
if jq --arg cwd "$cwd" \
'.trustedWorkspaces = (((.trustedWorkspaces // []) + [$cwd]) | unique)' \
"$agy_file" >"$tmp" 2>/dev/null
mv "$tmp" "$agy_file"
else
rm -f "$tmp"
end
else
jq -n --arg cwd "$cwd" \
'{"trustedWorkspaces": [$cwd]}' >"$agy_file" 2>/dev/null
end
end

function _trust_pi --argument-names cwd
command -q jq; or return 0
set -l pi_file "$HOME/.pi/agent/trust.json"
mkdir -p (dirname "$pi_file")
if test -f "$pi_file"
set -l tmp (mktemp)
if jq --arg cwd "$cwd" \
'.[$cwd] = true' \
"$pi_file" >"$tmp" 2>/dev/null
mv "$tmp" "$pi_file"
else
rm -f "$tmp"
end
else
jq -n --arg cwd "$cwd" \
'{($cwd): true}' >"$pi_file" 2>/dev/null
end
end

function _trust_workspace --argument-names agent_kind cwd
# Trust the workspace in every harness, so agent-team siblings of any kind
# start without a trust dialog.
function _trust_workspace --argument-names cwd
test -n "$cwd"; or return 0
test -n "$agent_kind"; or return 0
set cwd (string trim -r -c / -- "$cwd")

switch "$agent_kind"
case claude claude-code
_trust_claude "$cwd"
case codex
_trust_codex "$cwd"
case cursor cursor-agent
_trust_cursor "$cwd"
case agy antigravity antigravity-cli
_trust_agy "$cwd"
case pi
_trust_pi "$cwd"
end
set -l fish (status fish-path)
$fish (path dirname (realpath (status filename)))/trust-workspace "$cwd"
end

# --- Workspace launcher ---
Expand Down Expand Up @@ -488,10 +399,10 @@ function _launch_workspace
| string replace -ar '[^a-z0-9_-]' '-')
set agent_name (string sub -l 32 -- "$agent_name")

_trust_workspace "$agent_kind" "$cwd"
_trust_workspace "$cwd"

set -l agent_extra
test "$agent_kind" = cursor; and set agent_extra -- --trust
test "$agent_kind" = codex; and set agent_extra -- --dangerously-bypass-hook-trust

if command -q setsid
if command -q nohup
Expand Down
40 changes: 15 additions & 25 deletions herdr/tests/start-workspace.fish
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ function write_stubs --argument-names home

printf '%s\n' "#!$real_fish --no-config" 'exit 0' >"$stub_bin/codex"
printf '%s\n' "#!$real_fish --no-config" 'exit 0' >"$stub_bin/claude"
printf '%s\n' "#!$real_fish --no-config" 'exit 0' >"$stub_bin/cursor"
printf '%s\n' "#!$real_fish --no-config" 'exit 0' >"$stub_bin/agy"
printf '%s\n' "#!$real_fish --no-config" 'exit 0' >"$stub_bin/pi"
printf '%s\n' "#!$real_fish --no-config" 'exit 1' >"$stub_bin/timeout"
Expand All @@ -129,7 +128,7 @@ function write_stubs --argument-names home
end
chmod +x "$stub_bin/fzf" "$stub_bin/herdr" "$stub_bin/jj" \
"$stub_bin/ssh" "$stub_bin/codex" "$stub_bin/claude" \
"$stub_bin/cursor" "$stub_bin/agy" "$stub_bin/pi" "$stub_bin/timeout"
"$stub_bin/agy" "$stub_bin/pi" "$stub_bin/timeout"
end

function setup_case --argument-names name
Expand Down Expand Up @@ -237,7 +236,7 @@ or fail "typed bookmark selection created the wrong workspace"

# --- Auto-trust harness tests ---

for agent in claude cursor agy pi
for agent in claude codex agy pi
setup_case "trust_$agent"
set -l t_home "$case_home"
set -l t_repo "$case_repo"
Expand All @@ -247,26 +246,22 @@ for agent in claude cursor agy pi
run_launcher "$t_home" "$t_repo" existing 0 "$t_path"
set -l t_log "$launcher_log"

# Every harness trusts the workspace, whichever agent starts it.
jq -e --arg p "$t_path" '.projects[$p].hasTrustDialogAccepted == true' "$t_home/.claude.json" >/dev/null
or fail "$agent: claude trust entry missing"
grep -qF "[projects.\"$t_path\"]" "$t_home/.codex/config.toml"
or fail "$agent: codex trust entry missing"
jq -e --arg p "$t_path" '(.trustedWorkspaces // []) | index($p) != null' "$t_home/.gemini/antigravity-cli/settings.json" >/dev/null
or fail "$agent: agy trustedWorkspaces missing path"
jq -e --arg p "$t_path" '.[$p] == true' "$t_home/.pi/agent/trust.json" >/dev/null
or fail "$agent: pi trust entry missing"

switch "$agent"
case claude
test -f "$t_home/.claude.json"
or fail "claude trust file not created"
jq -e --arg p "$t_path" '.projects[$p].hasTrustDialogAccepted == true' "$t_home/.claude.json" >/dev/null
or fail "claude trust entry missing"
case cursor
set -l slug (echo "$t_path" | string replace -r '^/' '' | string replace -ra '[^a-zA-Z0-9]+' '-')
test -f "$t_home/.cursor/projects/$slug/.workspace-trusted"
or fail "cursor workspace-trusted file not created"
jq -e --arg p "$t_path" '.workspacePath == $p' "$t_home/.cursor/projects/$slug/.workspace-trusted" >/dev/null
or fail "cursor workspacePath mismatch"
string match -q '*agent start cursor-wcase --kind cursor --pane wCase:p1 -- --trust*' \
case codex
string match -q -- '*agent start codex-wcase --kind codex --pane wCase:p1 -- --dangerously-bypass-hook-trust*' \
(string collect < "$t_log")
or fail "cursor agent not started with --trust flag"
or fail "codex agent not started with --dangerously-bypass-hook-trust"
case agy
test -f "$t_home/.gemini/antigravity-cli/settings.json"
or fail "agy settings file not created"
jq -e --arg p "$t_path" '(.trustedWorkspaces // []) | index($p) != null' "$t_home/.gemini/antigravity-cli/settings.json" >/dev/null
or fail "agy trustedWorkspaces missing path"
string match -q '*agent start agy-wcase --kind agy --pane wCase:p1*' \
(string collect < "$t_log")
or fail "agy agent not started with expected arguments"
Expand All @@ -277,11 +272,6 @@ for agent in claude cursor agy pi
(string collect < "$launcher_setsid_log")
or fail "setsid not called with -f nohup and expected arguments"
end
case pi
test -f "$t_home/.pi/agent/trust.json"
or fail "pi trust.json not created"
jq -e --arg p "$t_path" '.[$p] == true' "$t_home/.pi/agent/trust.json" >/dev/null
or fail "pi trust entry missing"
end
end
set -e FZF_AGENT_CHOICE
Expand Down
38 changes: 38 additions & 0 deletions herdr/trust-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env fish
# Trust a directory in every agent harness, so an agent started there skips
# the folder trust dialog. Usage: trust-workspace [<dir>] (default: $PWD).
# Claude, Codex, and agy match exact paths only; pi also honors parents.

command -q jq; or begin
echo "trust-workspace: jq not found" >&2
exit 1
end

set -g dir $argv[1]
test -n "$dir"; or set dir $PWD
set dir (realpath -s -- "$dir"); or exit 1

# Apply a jq filter to a JSON file in place, seeding it with $seed if missing.
function _jq_update --argument-names file seed filter
mkdir -p (path dirname -- "$file")
test -s "$file"; or printf '%s\n' "$seed" >"$file"
set -l tmp (mktemp)
if jq --arg dir "$dir" "$filter" "$file" >"$tmp"
cat "$tmp" >"$file"
end
rm -f "$tmp"
end

_jq_update "$HOME/.claude.json" '{}' \
'.projects[$dir] = ((.projects[$dir] // {}) + {hasTrustDialogAccepted: true})'

_jq_update "$HOME/.gemini/antigravity-cli/settings.json" '{}' \
'.trustedWorkspaces = (((.trustedWorkspaces // []) + [$dir]) | unique)'

_jq_update "$HOME/.pi/agent/trust.json" '{}' '.[$dir] = true'

set -l codex_file "$HOME/.codex/config.toml"
mkdir -p (path dirname -- "$codex_file")
if not grep -qF "[projects.\"$dir\"]" "$codex_file" 2>/dev/null
printf '\n[projects."%s"]\ntrust_level = "trusted"\n' "$dir" >>"$codex_file"
end
1 change: 1 addition & 0 deletions mise/conf.d/herdr.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"~/.config/herdr/config.toml" = "../../herdr/config.toml"
"~/.local/bin/clone-repo" = "../../herdr/clone-repo"
"~/.local/bin/start-workspace" = "../../herdr/start-workspace"
"~/.local/bin/trust-workspace" = "../../herdr/trust-workspace"
"~/.local/bin/remove-jj-workspace" = "../../herdr/remove-jj-workspace"
"~/.local/bin/recorder-toggle-herdr" = "../../herdr/recorder-toggle"
"~/.local/bin/herdr-url-picker" = "../../herdr/herdr-url-picker"
Expand Down
Loading