diff --git a/agents/skills/agent-team/SKILL.md b/agents/skills/agent-team/SKILL.md index cb089916..1bdc7c10 100644 --- a/agents/skills/agent-team/SKILL.md +++ b/agents/skills/agent-team/SKILL.md @@ -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. @@ -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 "" --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 . Write /tmp/agent-team//impl.md. Stop when done." --wait --until working --until blocked --timeout 10000 diff --git a/codex/config.toml b/codex/config.toml index db1f9de7..ca9eefa1 100644 --- a/codex/config.toml +++ b/codex/config.toml @@ -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"] diff --git a/herdr/start-workspace b/herdr/start-workspace index 5614c3de..26d5ffeb 100755 --- a/herdr/start-workspace +++ b/herdr/start-workspace @@ -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) @@ -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 @@ -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 --- @@ -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 diff --git a/herdr/tests/start-workspace.fish b/herdr/tests/start-workspace.fish index ed5c9268..2274a2a0 100755 --- a/herdr/tests/start-workspace.fish +++ b/herdr/tests/start-workspace.fish @@ -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" @@ -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 @@ -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" @@ -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" @@ -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 diff --git a/herdr/trust-workspace b/herdr/trust-workspace new file mode 100755 index 00000000..590375e7 --- /dev/null +++ b/herdr/trust-workspace @@ -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 [] (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 diff --git a/mise/conf.d/herdr.toml b/mise/conf.d/herdr.toml index fd9f6091..e3c66464 100644 --- a/mise/conf.d/herdr.toml +++ b/mise/conf.d/herdr.toml @@ -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"