Thanks Lee.
If I drop down below the DSL level, to just require SSHKit, is that an easy API to work with, and is it documented somewhere? When I search for SSHKit, the examples always use the DSL. (If it's not documented, I can drop into the DSL code to see how it's using SSHKit easily enough.)
I did notice that I didn't have these scoping issues using the DSL from a raw ruby script, so I'll look into a Thor alternative, like you suggested.
As far as choosing Thor, I've written a handful of scripts over the years, and have used ARGV, Rake, OptionParser, and Thor for scripts with arguments. Of those, Thor seems to give the most robust CLI for the least amount of code. The exit code issue would be a nonstarter for many cases, but something I'm not too worried about with this script. I also don't like using a tool via inheritance like Thor requires. However, there are pros and cons to all of the solutions I've tried, so I accept the trade offs.
Rake is a fantastic option, particularly when there are task dependencies. I prefer the Thor CLI interface to the one provided by Rake. My mind doesn't map well to the task argument square bracket syntax of Rake. Also, I prefer flag arguments to positional arguments. I'm not a Rake expert, if it provides flag arguments, I'd gladly stand corrected.