runner: launch bare, and run shell functions as targets - #19
Open
cameronsjo wants to merge 2 commits into
Open
Conversation
cli-capture required a target on the command line and resolved it through exec.LookPath, so two things were impossible: starting the tool and deciding what to run afterwards, and targeting anything that is not a PATH binary. A zsh function like `claude-as` failed with a bare "executable file not found". Two ways in now: - No command at all launches an interactive $SHELL as the target. ProxyEnv already injects into whatever is launched, so every command run inside that shell is captured. - -shell wraps a named target in `$SHELL -ic`, which loads the rc file first, so aliases, functions, and rc-only PATH entries resolve. Arguments are single-quoted so they stay literal, but argv[0] is left bare when it matches a narrow metacharacter-free charset: in zsh, quoting any part of a word suppresses alias expansion, so a quoted name would still find a function while silently missing a user's alias. Start now pre-resolves argv[0] and returns ErrTargetNotFound, letting the command layer name the remedy — -shell for a target the user named, a broken $SHELL for a derived one. Also folds the duplicate POSIX quoting helper in internal/export into a shared internal/shellquote so the escaping rule has one home. Session-Name: bare-launch-shell-target Session-Id: 011b334d-55e0-41b7-8439-e0ffee421aff Model: claude-opus-5 Harness: claude-code 2.1.220 Machine: cf6e768835c7 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The README documented `-leader <key>` with a `ctrl+space` example, but main.go never defined the flag — passing it exited 2 with "flag provided but not defined", so the only way to move the leader was a config file. Two halves of the same gap: - Define -leader and let it win over the config file, mirroring how -theme already overrides cfg.Theme.Base. - Accept "ctrl+space" as a leader name. Terminals send NUL for it, the same byte as Ctrl+@, so bubbletea reports it as "ctrl+@" and the ctrl+a..ctrl+z scan never produced it. It is a common leader precisely because no target app claims it, and the README already promised it worked. Session-Name: bare-launch-shell-target Session-Id: 011b334d-55e0-41b7-8439-e0ffee421aff Model: claude-opus-5 Harness: claude-code 2.1.220 Machine: cf6e768835c7 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Collaborator
Author
|
Follow-up commit
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
cli-capturerequired a target on the command line and resolved it throughexec.LookPath, so two things were impossible: starting the tool and decidingwhat to run afterwards, and targeting anything that is not a
PATHbinary. Azsh function like
claude-asfailed with a bare "executable file not found".Two ways in
$SHELLas the target.ProxyEnvalready injects into whatever is launched, so every command runinside that shell is captured.
-shellwraps a named target in$SHELL -ic, which loads the rc filefirst, so aliases, functions, and rc-only
PATHentries resolve.Quoting
Arguments are single-quoted so they stay literal.
argv[0]is left bare whenit matches a narrow metacharacter-free charset — in zsh, quoting any part of a
word suppresses alias expansion, so a quoted name would still find a function
while silently missing a user's alias. A security pass confirmed no input after
--escapes the quoting; the boundary cases are pinned by tests.-shellis a deliberate waiver of the project's "pass args separately, neverthrough a shell" rule, with the reason recorded at the call site: resolving a
name only the user's shell knows requires the user's shell, the string is built
from the user's own argv, and the default path stays exec-direct.
Errors
Startnow pre-resolvesargv[0]and returnsErrTargetNotFound, letting thecommand layer name the remedy —
-shellfor a target the user named, a broken$SHELLfor a derived one.Before:
After:
Also
Folds the duplicate POSIX quoting helper in
internal/exportinto a sharedinternal/shellquote, so the escaping rule has one home.Verification
go build ./... && go vet ./... && go test ./...— 16 packages ok, gofmt clean.Error paths checked against the built binary. The interactive paths (bare launch
lands a shell; traffic from inside it is captured;
-shellruns the function)need a real TTY and were verified only as far as target launch.
Session-Name: bare-launch-shell-target
Session-Id: 011b334d-55e0-41b7-8439-e0ffee421aff
Model: claude-opus-5
Harness: claude-code 2.1.220
Machine: cf6e768835c7