feat: reach <target> <command>, with a session per target - #17
Merged
Conversation
bojieli
force-pushed
the
one-shot-target-form
branch
from
August 22, 2026 04:44
f3e4e85 to
4ae3a2d
Compare
Binding a session and using it took two commands, and the first of them defaulted the session name to "default". A second `reach up` with no --name therefore replaced the first session in place, and because the harness launchers resolve their target through that same name, an agent already working through it began running its commands on a different machine. reach could always hold several targets at once; they had to be named by hand, and nothing in the interface said so. Now the target names the session. `reach build-box claude` binds a session to build-box and launches Claude Code against it; `reach build-box exec -- go test ./...` and `reach build-box doctor` work the same way, because dispatch reads a first argument that is not a command as a target and hands the rest to the command. Session flags sit between the two, and flag.Parse's stop-at-first-positional behaviour draws the line: everything from the command onwards belongs to the command. Two targets that derive the same name get numbered variants rather than overwriting each other, and `reach up` now says so on stderr when it repoints a name another session was using. A second command against a target reuses that target's session instead of probing again, since a probe costs a round trip and an authentication. The connection is still re-established while the operator is present, because every connection after that runs in batch mode and cannot prompt for a passphrase or a hardware token — except for commands that never touch the target (log, status, env), where opening one would ask for a token touch to read a local file. Targets may now be spelled without a path: the session works wherever a login on that machine lands, which the probe asks the machine for rather than guessing at a home directory that may not exist. A bare word is read as a host only when the operator's own ssh configuration or hosts file names it, or when it is an address or a dotted name. DNS is deliberately not consulted — resolvers that answer for every name are common enough that a lookup would turn `reach stauts` into a connection attempt on exactly the networks where that is hardest to see. Also fixes --mode accepting a misspelling and behaving as exec, since only "mirror" is ever compared against. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WMXbmmHmEkyWWRc72uvPW
bojieli
force-pushed
the
one-shot-target-form
branch
from
August 22, 2026 04:44
4ae3a2d to
2f9e79f
Compare
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.
What this changes
Binding a session and using it took two commands, and the first defaulted the session name to
default. A secondreach upwith no--nametherefore replaced the first session in place — and because the harness launchers resolve their target through that same name, an agent already working through it began running its commands on a different machine. reach could always hold several targets at once; they had to be named by hand, and nothing in the interface said so. The reported symptom was "I can't open multiple SSH targets".Now the target names the session:
knownCommandsinmain.go), and the rest is dispatched as a normal command line. A drift test parsesdispatchout of the source and fails if the map and the switch disagree — a command missing from the map would be looked up as a hostname, and one listed but unhandled would exit 0 having done nothing.flag.Parsestops at the first non-flag argument, which draws exactly the right line: everything from the command onwards belongs to the command.build-box,build-box-app). A name already held by a different target gets a numbered variant rather than being overwritten, andreach upnow says so on stderr when it repoints a name another session was using.log,status,env), where opening one would ask for a token touch to read a local file.reach build-box claudeworks where a login on that machine lands, whichProbeasks the machine for and records, rather than reach guessing at a home directory that may not exist.Hostentry (including throughInclude), the hosts file, an address, or a dotted name. DNS is deliberately not consulted: resolvers that answer for every name are common enough that a lookup would turnreach stautsinto a connection attempt on exactly the networks where that is hardest to see. A word that is neither a command nor a host is reported as both readings.Also fixes
--modeaccepting a misspelling and silently behaving asexec, since only"mirror"is ever compared against.Checklist
make checkandmake lintpassmake integrationpasses, if this touches transports or file operations — not run; no transport or tier behaviour changedVerified against a
local://target end to end (session bound, command run on the target, session left behind forstatus/down), plus unit tests for host recognition, the flag split, name derivation and collisions, and reuse rules. The ssh paths are covered by unit tests only — no live remote host was used in this branch, so first-connection authentication and multiplexed reuse against a real sshd remain unverified here.🤖 Generated with Claude Code
https://claude.ai/code/session_018WMXbmmHmEkyWWRc72uvPW