Skip to content

fix(cli): escape quotes in generated completion descriptions - #7173

Open
dv-waynehaffenden wants to merge 1 commit into
Effect-TS:v3from
dv-waynehaffenden:fix/escape-quotes-in-completions
Open

fix(cli): escape quotes in generated completion descriptions#7173
dv-waynehaffenden wants to merge 1 commit into
Effect-TS:v3from
dv-waynehaffenden:fix/escape-quotes-in-completions

Conversation

@dv-waynehaffenden

Copy link
Copy Markdown

What happened

Subcommand descriptions are interpolated into single-quoted strings in the generated zsh and fish scripts, unescaped. A description containing an apostrophe closes the string early, so the rest of the description — and then the generated script itself — comes back as completion candidates.

From a real CLI (packages/cli at 0.77.0), commandDescriptor.ts produced:

_lens_commands() {
    local commands; commands=(
        'status:Show every service discovered in the platform' \
        'down:Stop the platform's services, or just the named ones' \
        ...

The quote closes at platform. In the shell, lens <TAB> then offered individual words from the descriptions (the, named, just, message, onto) alongside fragments of the script's own source (commands commands "$@", _describe -t commands lens). Completions for a prefix that resolved before the first affected element still worked, which is what made it look intermittent.

zsh -n does not catch it: the quotes re-balance across elements, so the file is valid zsh that means something else.

The fix

options.ts already has an escape helper for option specs, but the subcommand path in commandDescriptor.ts has none. Added a minimal one for the two sites that interpolate a description into a single-quoted string:

  • getZshCompletionsInternal — the 'name:description' entries passed to _describe
  • getFishCompletionsInternal — the -d 'description' argument to complete

It escapes quotes only. options.ts's escape also handles [, ] and :, which are delimiters in an option spec but plain text in these two positions — escaping them here would render visible backslashes in the description a user reads.

''\'' closes the string, escapes the quote and reopens; both zsh and fish accept it. Verified in zsh that the escaped form parses as one element with the apostrophe intact:

$ cands=( 'down:Stop the platform'\''s services, or just the named ones' 'up:Boot it' )
count=2
  [down:Stop the platform's services, or just the named ones]
  [up:Boot it]

Tests

Two cases under Completions, covering a subcommand described as Clear the project's cache in each shell: the escaped sequence is present and the raw project's cache is absent.

Disclosure

I could not run the package's test suite locally — this was a sparse checkout without a workspace install, so both changed files were only parse-checked. The assertions were derived by computing the generator's actual output for that input rather than by running vitest, so CI is the first full run.

@changeset-bot

changeset-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 73210ec

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@effect/cli Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@effect-slopcop effect-slopcop Bot added 3.0 Used for issues, pull requests, etc. that are relevant for the `v3` branch targeting Effect v3. bug Something isn't working labels Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3.0 Used for issues, pull requests, etc. that are relevant for the `v3` branch targeting Effect v3. bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant