Skip to content

Lead with the unquoted command form, and take -- the way every tool does - #39

Merged
Korijn merged 1 commit into
masterfrom
claude/unquoted-keycmd-support-en0co9
Aug 5, 2026
Merged

Lead with the unquoted command form, and take -- the way every tool does#39
Korijn merged 1 commit into
masterfrom
claude/unquoted-keycmd-support-en0co9

Conversation

@Korijn

@Korijn Korijn commented Aug 5, 2026

Copy link
Copy Markdown
Owner

keycmd already ran a command written out as separate arguments — join_cmd has handled a multi-argument command all along, quoting each one so that the shell hands it on as the word it was. What was missing was the framing: every example on every page put the command in quotes, so quoting read as a requirement rather than as the tool for the one job it is needed for.

Quotes are what you reach for when you want a shell — a pipe, an &&, a credential expanded into the command line — and almost nothing else needs one. keycmd npm install is what the README already meant by "prefix any command with keycmd", and it is the form the docs now lead with.

The bug behind it

Writing a command out unquoted brings -- with it, since every tool that goes on to run another one takes it (uv run --, npx --, env --). keycmd took it as the first word of the command instead:

❯ keycmd --verbose -- echo hi
keycmd: running command: ['/bin/bash', '-c', '-- echo hi']
bash: line 1: --: invalid option

end_of_options now removes the -- that ends keycmd's own options, which argparse leaves sitting in the REMAINDER. Only the leading one — keycmd npm install -- --flag keeps its own. It also makes a command whose first word starts with a dash reachable, which argparse otherwise rejects as an unrecognized option.

Changes

keycmd/cli.pyend_of_options, plus a usage line and an examples block in --help, since that is where someone looks before the docs:

usage: keycmd [options] [--] [command ...]
...
examples:
  keycmd npm install              run a command with the credentials exposed
  keycmd -- ruff --version        -- ends keycmd's own options
  keycmd 'echo $SECRET | wc -c'   quote it to use your shell's syntax
  keycmd --shell                  open a subshell with the credentials exposed

Docs — the unquoted form leads on the landing pages, in the quick start, in both examples and in the guide, which is rewritten around prefix your command--a quoted command line. Quoting is kept where it is genuinely needed and explained as such: printing a secret is the one thing in the quick start that needs it, and it now says so instead of implying that is how keycmd is used. Troubleshooting gains entries for the stale -- failure and for the keycmd --verbose pytest vs keycmd pytest --verbose distinction.

Two caveats documented rather than papered over

  • Under WSL the unquoted form loses its word boundaries: wsl.exe strips the quotes off its own command line before the distribution sees them, which the suite already asserts in tests/test_wsl.py. It is the platform's limit and not fixable from the Windows side, so the WSL guide says so. keycmd npm install is unaffected; only an argument containing spaces is.
  • cmd and Windows PowerShell have argv limits that no amount of quoting lifts (a newline, %VAR%, an embedded ", an empty argument). These were documented in quote's docstring but never surfaced to users.

Testing

13 new cases in tests/test_cli.py: -- across the parse table, -- end to end through main, and a credential reaching a command in the unquoted form across every installed shell.

Full suite (182 passed, 3 skipped), ruff check, ruff format --check, ty check --error-on-warning and mkdocs build --strict all clean. Both forms were also smoke tested end to end against a real keyring backend, including that keycmd python --version reaches python rather than keycmd.

The version is not bumped; that happens in its own commit.


Generated by Claude Code

keycmd already ran a command written out as separate arguments, but the
docs treated it as the exception: every example put the command in quotes,
which reads as a requirement rather than as the tool for the one job it is
needed for. Quotes are what you reach for when you want a shell — a pipe,
an &&, a credential expanded into the command line — and almost nothing
else needs one. `keycmd npm install` is the form the docs now lead with,
on the landing pages, in the quick start, in the examples and in the guide,
which is also what the README meant by "prefix any command with keycmd".

Writing a command out that way brings `--` with it, since every tool that
goes on to run another one takes it. keycmd took it as the first word of
the command instead, so `keycmd -- npm install` reached the shell as
`-- npm install` and failed on it. `end_of_options` removes the one that
ends keycmd's own options, which also makes a command whose first word
starts with a dash reachable; any further `--` is the command's own and is
passed along untouched.

--help gains a usage line and examples covering both forms, since that is
where someone looks before the docs.

The one place the unquoted form cannot keep its word boundaries is WSL,
where wsl.exe strips the quotes off its own command line before the
distribution sees them. That is the platform's limit rather than keycmd's,
and the WSL guide now says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HFqRvGXvoDW1tiX7dJHoYb
@Korijn
Korijn merged commit e93383e into master Aug 5, 2026
11 checks passed
@Korijn
Korijn deleted the claude/unquoted-keycmd-support-en0co9 branch August 5, 2026 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants