From 33c9281489d37c579f13d15f60b402d629528299 Mon Sep 17 00:00:00 2001 From: Juan Barbat Date: Fri, 21 Aug 2026 17:25:55 -0300 Subject: [PATCH 1/4] feat(fish): port safe agent helpers (cherry picked from commit fee5b9f2d51e2236e6eab24de5ff73bc7acf6623) --- .github/workflows/ci.yml | 1 + README.md | 15 +++ fish/functions/_cortex_resolve_target.fish | 11 ++ fish/functions/_cortex_run_agent.fish | 11 ++ fish/functions/cc.fish | 5 + fish/functions/ccclip.fish | 43 +++++++ fish/functions/ccd.fish | 16 +++ fish/functions/ccx.fish | 14 +++ fish/functions/oc.fish | 9 ++ fish/functions/ocb.fish | 4 + fish/tests/w6-agent-helpers.fish | 139 +++++++++++++++++++++ nix/home.nix | 8 ++ 12 files changed, 276 insertions(+) create mode 100644 fish/functions/_cortex_resolve_target.fish create mode 100644 fish/functions/_cortex_run_agent.fish create mode 100644 fish/functions/cc.fish create mode 100644 fish/functions/ccclip.fish create mode 100644 fish/functions/ccd.fish create mode 100644 fish/functions/ccx.fish create mode 100644 fish/functions/oc.fish create mode 100644 fish/functions/ocb.fish create mode 100644 fish/tests/w6-agent-helpers.fish diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4a404c..c33b251 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,6 +62,7 @@ jobs: fish fish/tests/w4b-screenshots.fish fish fish/tests/w5a-hremote.fish fish fish/tests/w5b-herdr-ssh.fish + fish fish/tests/w6-agent-helpers.fish - name: Validate JSON configs shell: bash diff --git a/README.md b/README.md index 649867b..1428da1 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,21 @@ Para validar con `HOME`, `PATH` y comandos macOS falsos aislados: /opt/homebrew/bin/fish fish/tests/w4b-screenshots.fish ``` +## Helpers Fish de agentes seguros (W6) + +W6 agrega `cc`, `oc`, `ocb`, `ccx`, `ccd` y `ccclip` como helpers Fish opt-in. Ejecutan Claude Code u OpenCode únicamente en el directorio validado; `ccx` entrega el contexto por stdin y `ccclip` escribe al clipboard solo al invocarse. No incluye `ccb`, bypasses de permisos ni auto-aprobación. + +| Área | Owner en W6 | +| --- | --- | +| Helpers y soporte privado | `fish/functions/{_cortex_resolve_target,_cortex_run_agent,cc,oc,ocb,ccx,ccd,ccclip}.fish` | +| Home Manager | Mapea cada una de esas funciones de forma explícita; no administra binarios de agentes, clipboard, worktrees, estado, historial ni configuración de proveedores | + +Para validar con agentes y clipboard falsos aislados: + +```bash +/opt/homebrew/bin/fish fish/tests/w6-agent-helpers.fish +``` + ## Estructura ``` diff --git a/fish/functions/_cortex_resolve_target.fish b/fish/functions/_cortex_resolve_target.fish new file mode 100644 index 0000000..72eeb40 --- /dev/null +++ b/fish/functions/_cortex_resolve_target.fish @@ -0,0 +1,11 @@ +function _cortex_resolve_target + set -l target "$argv[1]" + test -n "$target"; or set target . + + if not test -d "$target" + printf 'Directorio no encontrado: %s\n' "$target" >&2 + return 1 + end + + cd "$target"; and pwd +end diff --git a/fish/functions/_cortex_run_agent.fish b/fish/functions/_cortex_run_agent.fish new file mode 100644 index 0000000..ff5cefd --- /dev/null +++ b/fish/functions/_cortex_run_agent.fish @@ -0,0 +1,11 @@ +function _cortex_run_agent + set -l target "$argv[1]" + set -e argv[1] + set -l original_dir "$PWD" + + cd "$target"; or return 1 + command $argv + set -l agent_status $status + cd "$original_dir"; or return 1 + return "$agent_status" +end diff --git a/fish/functions/cc.fish b/fish/functions/cc.fish new file mode 100644 index 0000000..0d6a641 --- /dev/null +++ b/fish/functions/cc.fish @@ -0,0 +1,5 @@ +# Usage: cc [path] +function cc + set -l resolved (_cortex_resolve_target "$argv[1]"); or return 1 + _cortex_run_agent "$resolved" claude +end diff --git a/fish/functions/ccclip.fish b/fish/functions/ccclip.fish new file mode 100644 index 0000000..db8f35b --- /dev/null +++ b/fish/functions/ccclip.fish @@ -0,0 +1,43 @@ +# Usage: ccclip [-n|--line-numbers] +function ccclip + if test (count $argv) -eq 0 + printf 'Uso: ccclip [archivo2 ...] [-n|--line-numbers]\n' >&2 + return 1 + end + + set -l with_numbers false + set -l files + for arg in $argv + switch "$arg" + case -n --line-numbers + set with_numbers true + case '*' + set -a files "$arg" + end + end + + begin + for file in $files + if not test -f "$file" + printf 'Archivo no encontrado: %s\n' "$file" >&2 + continue + end + + set -l extension (path extension "$file" | string trim -c .) + printf '```%s\n// File: %s\n' "$extension" "$file" + set -l content + if test "$with_numbers" = true + set content (nl -ba "$file" | string collect) + else + set content (command cat "$file" | string collect) + end + printf '%s\n```\n\n' "$content" + end + end | pbcopy + set -l clipboard_status $pipestatus[-1] + if test "$clipboard_status" -ne 0 + return "$clipboard_status" + end + + printf 'Contexto copiado al clipboard (%d archivos)\n' (count $files) +end diff --git a/fish/functions/ccd.fish b/fish/functions/ccd.fish new file mode 100644 index 0000000..9f3fd98 --- /dev/null +++ b/fish/functions/ccd.fish @@ -0,0 +1,16 @@ +# Usage: ccd [subpath] +function ccd + set -l subpath "$argv[1]" + set -l workspace "$WORKSPACE_DIR" + test -n "$workspace"; or set workspace "$HOME/dev" + set -l target "$workspace" + test -n "$subpath"; and set target "$workspace/$subpath" + + if test -d "$target" + cd "$target" + printf 'Navegando a: %s\n' "$target" + else + printf 'Directorio no encontrado: %s\n' "$target" >&2 + return 1 + end +end diff --git a/fish/functions/ccx.fish b/fish/functions/ccx.fish new file mode 100644 index 0000000..7292837 --- /dev/null +++ b/fish/functions/ccx.fish @@ -0,0 +1,14 @@ +# Usage: ccx [path] +function ccx + set -l context "$argv[1]" + if test -z "$context" + printf 'Uso: ccx [directorio]\n' >&2 + return 1 + end + + set -l resolved (_cortex_resolve_target "$argv[2]"); or return 1 + begin + printf '%s\n' "$context" | _cortex_run_agent "$resolved" claude + return $pipestatus[-1] + end +end diff --git a/fish/functions/oc.fish b/fish/functions/oc.fish new file mode 100644 index 0000000..34f37a6 --- /dev/null +++ b/fish/functions/oc.fish @@ -0,0 +1,9 @@ +# Usage: oc [path] +function oc + set -l resolved (_cortex_resolve_target "$argv[1]"); or return 1 + set -l flags + if set -q OPENCODE_DEFAULT_FLAGS; and test -n "$OPENCODE_DEFAULT_FLAGS" + set flags (string replace -ra '[[:space:]]+' \n -- "$OPENCODE_DEFAULT_FLAGS") + end + _cortex_run_agent "$resolved" opencode $flags +end diff --git a/fish/functions/ocb.fish b/fish/functions/ocb.fish new file mode 100644 index 0000000..694b2c3 --- /dev/null +++ b/fish/functions/ocb.fish @@ -0,0 +1,4 @@ +# Usage: ocb [path] +function ocb + oc $argv +end diff --git a/fish/tests/w6-agent-helpers.fish b/fish/tests/w6-agent-helpers.fish new file mode 100644 index 0000000..a2295cc --- /dev/null +++ b/fish/tests/w6-agent-helpers.fish @@ -0,0 +1,139 @@ +#!/usr/bin/env fish + +set -l repo_root (cd (dirname (dirname (status filename))); and pwd) +set -g functions_dir "$repo_root/functions" +set -g workspace (mktemp -d) +test -n "$workspace"; or exit 1 +function _cleanup_w6 --on-event fish_exit + test -d "$workspace"; and rm -rf -- "$workspace" +end +function fail + printf 'FAIL: %s\n' "$argv" >&2 + exit 1 +end +function contains + string match -q "*$argv[2]*" -- "$argv[1]"; or fail "$argv[3]" +end + +set -g home "$workspace/home" +set -g bin "$workspace/bin" +set -g repo "$workspace/repo" +set -g target "$workspace/target dir" +set -g workspace_dir "$workspace/workspace dir" +mkdir -p "$home/dev" "$bin" "$repo" "$target" "$workspace_dir/sub dir" +printf '#!/bin/sh\nprintf "claude|%%s|cwd=%%s|stdin=" "$*" "$PWD" >> "$W6_LOG"\nwhile IFS= read -r line; do printf "%%s\\n" "$line" >> "$W6_LOG"; done\nprintf "\\n" >> "$W6_LOG"\nexit "${W6_CLAUDE_STATUS:-0}"\n' > "$bin/claude" +printf '#!/bin/sh\nprintf "opencode|%%s|cwd=%%s\\n" "$*" "$PWD" >> "$W6_LOG"\nexit "${W6_OPENCODE_STATUS:-0}"\n' > "$bin/opencode" +printf '#!/bin/sh\nprintf "pbcopy|" >> "$W6_LOG"\nwhile IFS= read -r line; do printf "%%s\\n" "$line" >> "$W6_LOG"; done\nprintf "\\n" >> "$W6_LOG"\nexit "${W6_PBCOPY_STATUS:-0}"\n' > "$bin/pbcopy" +printf '#!/bin/sh\nfor file; do while IFS= read -r line || [ -n "$line" ]; do printf "%%s\\n" "$line"; done < "$file"; done\n' > "$bin/cat" +printf '%s\n' '#!/bin/sh' 'for file; do' ' case "$file" in -*) continue ;; esac' ' i=0' ' while IFS= read -r line || [ -n "$line" ]; do' ' i=$((i + 1))' ' printf "%6s\\t%s\\n" "$i" "$line"' ' done < "$file"' 'done' > "$bin/nl" +chmod +x "$bin"/* +printf 'alpha\n' > "$target/code file.fish" +printf 'beta\n' > "$target/other.txt" +printf '' > "$target/empty.txt" + +function run_w6 + env -i HOME="$home" PATH="$bin:/usr/bin:/bin" W6_LOG="$workspace/log" \ + W6_CLAUDE_STATUS="$W6_CLAUDE_STATUS" W6_OPENCODE_STATUS="$W6_OPENCODE_STATUS" W6_PBCOPY_STATUS="$W6_PBCOPY_STATUS" OPENCODE_DEFAULT_FLAGS="$OPENCODE_DEFAULT_FLAGS" WORKSPACE_DIR="$WORKSPACE_DIR" \ + /opt/homebrew/bin/fish --no-config -c 'set -gx fish_function_path $argv[1] $fish_function_path; cd "$argv[2]"; and $argv[3] $argv[4..-1]' \ + "$functions_dir" "$repo" $argv +end +function run_w6_unavailable + env -i HOME="$home" PATH="/usr/bin:/bin" /opt/homebrew/bin/fish --no-config \ + -c 'set -gx fish_function_path $argv[1] $fish_function_path; cd "$argv[2]"; and $argv[3] $argv[4..-1]' \ + "$functions_dir" "$repo" $argv +end + +printf '' > "$workspace/log" +run_w6 cc; or fail 'cc must use the current directory without arguments' +contains (string collect <"$workspace/log") "claude||cwd=$repo" 'cc default directory' +printf '' > "$workspace/log" +run_w6 cc "$target"; or fail 'cc must succeed' +set -l log (string collect <"$workspace/log") +contains "$log" "claude||cwd=$target" 'cc target and arguments' +string match -q '*enable-auto-mode*' -- "$log"; and fail 'cc must not enable auto approval' + +printf '' > "$workspace/log" +run_w6 oc "$target"; or fail 'oc must succeed without defaults' +contains (string collect <"$workspace/log") "opencode||cwd=$target" 'oc target' +printf '' > "$workspace/log" +set -gx OPENCODE_DEFAULT_FLAGS (printf '%s\t%s\n%s' --model test --plain | string collect --no-trim-newlines) +run_w6 oc "$target"; or fail 'oc must tokenize shell whitespace in defaults' +contains (string collect <"$workspace/log") "opencode|--model test --plain|cwd=$target" 'oc tokenizes shell whitespace without eval' +set -gx OPENCODE_DEFAULT_FLAGS '--model test --plain' +run_w6 ocb "$target"; or fail 'ocb must succeed' +contains (string collect <"$workspace/log") "opencode|--model test --plain|cwd=$target" 'ocb forwards defaults' +set -e OPENCODE_DEFAULT_FLAGS +set -gx W6_OPENCODE_STATUS 24 +run_w6 oc "$target" >/dev/null 2>&1; and fail 'opencode status must propagate' +set -e W6_OPENCODE_STATUS + +printf '' > "$workspace/log" +run_w6 ccx 'initial context with spaces' "$target"; or fail 'ccx must succeed' +contains (string collect <"$workspace/log") "claude||cwd=$target|stdin=initial context with spaces" 'ccx forwards context through stdin' +if run_w6 ccx >"$workspace/ccx.out" 2>&1 + fail 'ccx without context must fail' +end +contains (string collect <"$workspace/ccx.out") 'Uso: ccx' 'ccx usage' + +if run_w6 cc "$workspace/missing" >"$workspace/missing.out" 2>&1 + fail 'missing agent target must fail' +end +contains (string collect <"$workspace/missing.out") 'Directorio no encontrado' 'missing target error' +set -gx W6_CLAUDE_STATUS 23 +run_w6 cc "$target" >/dev/null 2>&1; and fail 'claude status must propagate' +set -e W6_CLAUDE_STATUS +set -gx W6_CLAUDE_STATUS 25 +run_w6 ccx context "$target" >/dev/null 2>&1; and fail 'ccx status must propagate' +set -e W6_CLAUDE_STATUS +if run_w6_unavailable cc "$target" >"$workspace/unavailable.out" 2>&1 + fail 'unavailable claude must fail' +end +contains (string collect <"$workspace/unavailable.out") 'Unknown command' 'unavailable claude error' + +set -gx WORKSPACE_DIR '' +set -l ccd_default (run_w6 ccd) +contains "$ccd_default" "Navegando a: $home/dev" 'ccd default directory' +set -gx WORKSPACE_DIR "$workspace_dir" +set -l ccd_output (run_w6 ccd 'sub dir') +contains "$ccd_output" "Navegando a: $workspace_dir/sub dir" 'ccd uses workspace override' +source "$functions_dir/ccd.fish" +cd "$repo" +ccd 'sub dir' >/dev/null; or fail 'ccd must change the invoking Fish process directory' +test "$PWD" = "$workspace_dir/sub dir"; or fail 'ccd persists PWD in the invoking Fish process' +if run_w6 ccd missing >"$workspace/ccd.out" 2>&1 + fail 'ccd missing directory must fail' +end +contains (string collect <"$workspace/ccd.out") 'Directorio no encontrado' 'ccd validates target' +set -e WORKSPACE_DIR + +printf '' > "$workspace/log" +run_w6 ccclip "$target/code file.fish" -n; or fail 'ccclip must copy content' +set log (string collect <"$workspace/log") +contains "$log" "pbcopy|```fish" 'ccclip fences extension' +contains "$log" "// File: $target/code file.fish" 'ccclip preserves spaced path' +contains "$log" '1 alpha' 'ccclip includes line numbers' +printf '' > "$workspace/log" +run_w6 ccclip "$target/code file.fish"; or fail 'ccclip must copy newline-terminated content' +set log (string collect <"$workspace/log") +set -l expected_fence (string join \n alpha '```' | string collect) +contains "$log" "$expected_fence" 'ccclip trims trailing linefeeds before the closing fence' +printf '' > "$workspace/log" +run_w6 ccclip "$target/empty.txt"; or fail 'ccclip must copy empty files' +printf '' > "$workspace/log" +run_w6 ccclip "$workspace/missing"; or fail 'ccclip empty context preserves successful clipboard status' +contains (string collect <"$workspace/log") 'pbcopy|' 'ccclip hands empty context to clipboard' +set -gx W6_PBCOPY_STATUS 31 +run_w6 ccclip "$target/other.txt" >/dev/null 2>&1; and fail 'clipboard failure must propagate' +set -e W6_PBCOPY_STATUS +if run_w6 ccclip >"$workspace/clip.out" 2>&1 + fail 'ccclip without files must fail' +end +contains (string collect <"$workspace/clip.out") 'Uso: ccclip' 'ccclip usage' + +for file in "$functions_dir/_cortex_resolve_target.fish" "$functions_dir/_cortex_run_agent.fish" "$functions_dir/cc.fish" "$functions_dir/oc.fish" "$functions_dir/ocb.fish" "$functions_dir/ccx.fish" "$functions_dir/ccd.fish" "$functions_dir/ccclip.fish" + set -l source (string collect < "$file") + string match -rqi -- '(dangerously-skip-permissions|bypass-permissions|skip-permissions|auto-approv|--yes)' "$source"; and fail "unsafe W6 source: $file" +end +string match -rqi -- '(dangerously-skip-permissions|bypass-permissions|skip-permissions|auto-approv)' "$log"; and fail 'unsafe generated command' + +printf 'PASS: Fish W6 agent helpers\n' diff --git a/nix/home.nix b/nix/home.nix index 1f056ce..c4b0cb9 100644 --- a/nix/home.nix +++ b/nix/home.nix @@ -74,5 +74,13 @@ "fish/functions/last.fish".source = ../fish/functions/last.fish; "fish/functions/ssd.fish".source = ../fish/functions/ssd.fish; "fish/functions/imgclip.fish".source = ../fish/functions/imgclip.fish; + "fish/functions/_cortex_resolve_target.fish".source = ../fish/functions/_cortex_resolve_target.fish; + "fish/functions/_cortex_run_agent.fish".source = ../fish/functions/_cortex_run_agent.fish; + "fish/functions/cc.fish".source = ../fish/functions/cc.fish; + "fish/functions/oc.fish".source = ../fish/functions/oc.fish; + "fish/functions/ocb.fish".source = ../fish/functions/ocb.fish; + "fish/functions/ccx.fish".source = ../fish/functions/ccx.fish; + "fish/functions/ccd.fish".source = ../fish/functions/ccd.fish; + "fish/functions/ccclip.fish".source = ../fish/functions/ccclip.fish; }; } From dd1b21a23139bfaccc4ff3bd56721ce5308f2b9f Mon Sep 17 00:00:00 2001 From: Juan Barbat Date: Fri, 21 Aug 2026 17:57:17 -0300 Subject: [PATCH 2/4] feat(fish): enable autonomous agent shortcuts (cherry picked from commit a7868c41e271af8372403eae80f4ea0b835ce412) --- README.md | 4 ++-- fish/functions/cc.fish | 2 +- fish/functions/oc.fish | 2 +- fish/tests/w6-agent-helpers.fish | 17 ++++++++--------- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 1428da1..3353f52 100644 --- a/README.md +++ b/README.md @@ -151,9 +151,9 @@ Para validar con `HOME`, `PATH` y comandos macOS falsos aislados: /opt/homebrew/bin/fish fish/tests/w4b-screenshots.fish ``` -## Helpers Fish de agentes seguros (W6) +## Helpers Fish de agentes (W6) -W6 agrega `cc`, `oc`, `ocb`, `ccx`, `ccd` y `ccclip` como helpers Fish opt-in. Ejecutan Claude Code u OpenCode únicamente en el directorio validado; `ccx` entrega el contexto por stdin y `ccclip` escribe al clipboard solo al invocarse. No incluye `ccb`, bypasses de permisos ni auto-aprobación. +**ADVERTENCIA:** por elección explícita del owner, `cc` ejecuta Claude Code con `--dangerously-skip-permissions` y `oc`/`ocb` ejecutan OpenCode con `--auto`; estos shortcuts intencionalmente omiten o autoaprueban permisos. W6 agrega `cc`, `oc`, `ocb`, `ccx`, `ccd` y `ccclip` como helpers Fish opt-in en el directorio validado; `ccx` entrega el contexto por stdin y `ccclip` escribe al clipboard solo al invocarse. No incluye `ccb`. | Área | Owner en W6 | | --- | --- | diff --git a/fish/functions/cc.fish b/fish/functions/cc.fish index 0d6a641..99531aa 100644 --- a/fish/functions/cc.fish +++ b/fish/functions/cc.fish @@ -1,5 +1,5 @@ # Usage: cc [path] function cc set -l resolved (_cortex_resolve_target "$argv[1]"); or return 1 - _cortex_run_agent "$resolved" claude + _cortex_run_agent "$resolved" claude --dangerously-skip-permissions end diff --git a/fish/functions/oc.fish b/fish/functions/oc.fish index 34f37a6..c0aaaa0 100644 --- a/fish/functions/oc.fish +++ b/fish/functions/oc.fish @@ -5,5 +5,5 @@ function oc if set -q OPENCODE_DEFAULT_FLAGS; and test -n "$OPENCODE_DEFAULT_FLAGS" set flags (string replace -ra '[[:space:]]+' \n -- "$OPENCODE_DEFAULT_FLAGS") end - _cortex_run_agent "$resolved" opencode $flags + _cortex_run_agent "$resolved" opencode --auto $flags end diff --git a/fish/tests/w6-agent-helpers.fish b/fish/tests/w6-agent-helpers.fish index a2295cc..64f9dd3 100644 --- a/fish/tests/w6-agent-helpers.fish +++ b/fish/tests/w6-agent-helpers.fish @@ -45,23 +45,23 @@ end printf '' > "$workspace/log" run_w6 cc; or fail 'cc must use the current directory without arguments' -contains (string collect <"$workspace/log") "claude||cwd=$repo" 'cc default directory' +string match -q -- "claude|--dangerously-skip-permissions|cwd=$repo|stdin=" (string collect <"$workspace/log"); or fail 'cc default command order' printf '' > "$workspace/log" run_w6 cc "$target"; or fail 'cc must succeed' set -l log (string collect <"$workspace/log") -contains "$log" "claude||cwd=$target" 'cc target and arguments' -string match -q '*enable-auto-mode*' -- "$log"; and fail 'cc must not enable auto approval' +string match -q -- "claude|--dangerously-skip-permissions|cwd=$target|stdin=" "$log"; or fail 'cc target command order' printf '' > "$workspace/log" run_w6 oc "$target"; or fail 'oc must succeed without defaults' -contains (string collect <"$workspace/log") "opencode||cwd=$target" 'oc target' +string match -q -- "opencode|--auto|cwd=$target" (string collect <"$workspace/log"); or fail 'oc default command order' printf '' > "$workspace/log" set -gx OPENCODE_DEFAULT_FLAGS (printf '%s\t%s\n%s' --model test --plain | string collect --no-trim-newlines) run_w6 oc "$target"; or fail 'oc must tokenize shell whitespace in defaults' -contains (string collect <"$workspace/log") "opencode|--model test --plain|cwd=$target" 'oc tokenizes shell whitespace without eval' +string match -q -- "opencode|--auto --model test --plain|cwd=$target" (string collect <"$workspace/log"); or fail 'oc tokenizes defaults after one auto flag' set -gx OPENCODE_DEFAULT_FLAGS '--model test --plain' +printf '' > "$workspace/log" run_w6 ocb "$target"; or fail 'ocb must succeed' -contains (string collect <"$workspace/log") "opencode|--model test --plain|cwd=$target" 'ocb forwards defaults' +string match -q -- "opencode|--auto --model test --plain|cwd=$target" (string collect <"$workspace/log"); or fail 'ocb forwards defaults after one auto flag' set -e OPENCODE_DEFAULT_FLAGS set -gx W6_OPENCODE_STATUS 24 run_w6 oc "$target" >/dev/null 2>&1; and fail 'opencode status must propagate' @@ -130,10 +130,9 @@ if run_w6 ccclip >"$workspace/clip.out" 2>&1 end contains (string collect <"$workspace/clip.out") 'Uso: ccclip' 'ccclip usage' -for file in "$functions_dir/_cortex_resolve_target.fish" "$functions_dir/_cortex_run_agent.fish" "$functions_dir/cc.fish" "$functions_dir/oc.fish" "$functions_dir/ocb.fish" "$functions_dir/ccx.fish" "$functions_dir/ccd.fish" "$functions_dir/ccclip.fish" +for file in "$functions_dir/_cortex_resolve_target.fish" "$functions_dir/_cortex_run_agent.fish" "$functions_dir/ocb.fish" "$functions_dir/ccx.fish" "$functions_dir/ccd.fish" "$functions_dir/ccclip.fish" set -l source (string collect < "$file") - string match -rqi -- '(dangerously-skip-permissions|bypass-permissions|skip-permissions|auto-approv|--yes)' "$source"; and fail "unsafe W6 source: $file" + string match -rqi -- '(dangerously-skip-permissions|bypass-permissions|skip-permissions|auto-approv|--yes)' "$source"; and fail "unexpected unsafe W6 source: $file" end -string match -rqi -- '(dangerously-skip-permissions|bypass-permissions|skip-permissions|auto-approv)' "$log"; and fail 'unsafe generated command' printf 'PASS: Fish W6 agent helpers\n' From a7aa57872212a3d05108ebb91b3a0ccfb392fcbc Mon Sep 17 00:00:00 2001 From: Juan Barbat Date: Sat, 22 Aug 2026 02:51:01 -0300 Subject: [PATCH 3/4] test(fish): resolve agent interpreter portably --- fish/tests/w6-agent-helpers.fish | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fish/tests/w6-agent-helpers.fish b/fish/tests/w6-agent-helpers.fish index 64f9dd3..1ee090d 100644 --- a/fish/tests/w6-agent-helpers.fish +++ b/fish/tests/w6-agent-helpers.fish @@ -11,6 +11,7 @@ function fail printf 'FAIL: %s\n' "$argv" >&2 exit 1 end +set -g fish_bin (status fish-path); and test -n "$fish_bin"; or fail 'could not resolve Fish interpreter' function contains string match -q "*$argv[2]*" -- "$argv[1]"; or fail "$argv[3]" end @@ -34,11 +35,11 @@ printf '' > "$target/empty.txt" function run_w6 env -i HOME="$home" PATH="$bin:/usr/bin:/bin" W6_LOG="$workspace/log" \ W6_CLAUDE_STATUS="$W6_CLAUDE_STATUS" W6_OPENCODE_STATUS="$W6_OPENCODE_STATUS" W6_PBCOPY_STATUS="$W6_PBCOPY_STATUS" OPENCODE_DEFAULT_FLAGS="$OPENCODE_DEFAULT_FLAGS" WORKSPACE_DIR="$WORKSPACE_DIR" \ - /opt/homebrew/bin/fish --no-config -c 'set -gx fish_function_path $argv[1] $fish_function_path; cd "$argv[2]"; and $argv[3] $argv[4..-1]' \ + "$fish_bin" --no-config -c 'set -gx fish_function_path $argv[1] $fish_function_path; cd "$argv[2]"; and $argv[3] $argv[4..-1]' \ "$functions_dir" "$repo" $argv end function run_w6_unavailable - env -i HOME="$home" PATH="/usr/bin:/bin" /opt/homebrew/bin/fish --no-config \ + env -i HOME="$home" PATH="/usr/bin:/bin" "$fish_bin" --no-config \ -c 'set -gx fish_function_path $argv[1] $fish_function_path; cd "$argv[2]"; and $argv[3] $argv[4..-1]' \ "$functions_dir" "$repo" $argv end From 7392cb9884d629214831067a118d867d56beb15b Mon Sep 17 00:00:00 2001 From: Juan Barbat Date: Sat, 22 Aug 2026 12:08:16 -0300 Subject: [PATCH 4/4] test(fish): make missing-agent assertion portable --- fish/tests/w6-agent-helpers.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fish/tests/w6-agent-helpers.fish b/fish/tests/w6-agent-helpers.fish index 1ee090d..9874454 100644 --- a/fish/tests/w6-agent-helpers.fish +++ b/fish/tests/w6-agent-helpers.fish @@ -89,7 +89,7 @@ set -e W6_CLAUDE_STATUS if run_w6_unavailable cc "$target" >"$workspace/unavailable.out" 2>&1 fail 'unavailable claude must fail' end -contains (string collect <"$workspace/unavailable.out") 'Unknown command' 'unavailable claude error' +contains (string collect <"$workspace/unavailable.out") 'claude' 'unavailable claude identifies command' set -gx WORKSPACE_DIR '' set -l ccd_default (run_w6 ccd)