Skip to content

Fix Fish completions to avoid filesystem fallback#1199

Open
leo-ar wants to merge 1 commit into
Fission-AI:mainfrom
leo-ar:fix/fish-completion-no-file-fallback
Open

Fix Fish completions to avoid filesystem fallback#1199
leo-ar wants to merge 1 commit into
Fission-AI:mainfrom
leo-ar:fix/fish-completion-no-file-fallback

Conversation

@leo-ar

@leo-ar leo-ar commented Jun 11, 2026

Copy link
Copy Markdown

Fix Fish completions to avoid filesystem fallback

Summary

Fish shell completions for OpenSpec would fall back to suggesting filenames and directories unless
a completion rule explicitly suppressed it with -f. This PR fixes the generator so that command,
subcommand, flag, and positional completions never suggest filesystem entries unless the argument
is actually a path.

Problem

Fish has a default fallback behavior: if a completion rule does not explicitly opt out with -f,
Fish will suggest filenames after command-specific results (or instead of them). The OpenSpec Fish
generator was not emitting -f anywhere, which caused:

  • openspec <TAB> to show filesystem entries alongside command names
  • openspec workspace <TAB> to mix subcommand names with filesystem entries
  • non-path flag values (e.g. --initiative, --title) to offer file suggestions
  • free-form positional arguments to trigger file completion

What changed

src/core/completions/types.ts

Added completionType?: PositionalType to FlagDefinition. This field is optional and reuses the
existing PositionalType union. In practice only 'path' is used: it signals that a flag's value
is a filesystem path and file completion should be preserved. All other takesValue flags default
to suppressing file completion.

src/core/completions/command-registry.ts

Annotated 8 flags with completionType: 'path':

  • new change --store-path
  • set change --store-path
  • workspace open --store-path
  • context-store setup --path
  • initiative create --store-path
  • initiative show --store-path
  • initiative list --store-path
  • initiative ls --store-path

All other takesValue flags accept free-form strings or IDs and receive no annotation.

src/core/completions/generators/fish-generator.ts

Updated generator rules:

  • top-level command and subcommand listing entries: add -f
  • boolean flags: add -f
  • enum flag values: add -f
  • free-form takesValue flags (no completionType): emit -r -f
  • path takesValue flags (completionType: 'path'): emit -r only, preserving file completion

Added generateIndexedPositionalCompletions to walk positionals[] when a command defines
multiple positionals, generating one condition per index. The generator prefers positionals[]
over the legacy acceptsPositional + positionalType path; both remain supported.

Added collectValueFlags to gather all value-consuming flag names for a command so the positional
index helper can skip their consumed tokens when counting.

src/core/completions/templates/fish-templates.ts

Added __fish_openspec_positional_index to FISH_STATIC_HELPERS.

The function takes three kinds of arguments:

  • $argv[1] (target): the zero-based positional index to match
  • $argv[2] (depth): how many command/subcommand name tokens precede the first positional
    (1 for top-level commands, 2 for subcommands)
  • $argv[3..] (value_flags): long and short names of flags that consume the next token

It counts non-flag, non-consumed tokens in commandline -opc and returns true when
count == target + depth. The depth offset lets the same function handle both
openspec init <TAB> (depth=1) and openspec schema fork <TAB> (depth=2) without
needing separate helpers.

test/core/completions/generators/fish-generator.test.ts

  • Added completionLine() helper for targeted line-level assertions
  • Updated existing tests to assert -f is present on command, subcommand, flag, and positional
    completion lines
  • Added new tests:
    • completionType: 'path' flag emits -r without -f
    • boolean flags emit -f
    • enum flag values emit -f
    • dynamic helper positionals (change-id, spec-id, etc.) emit -f
    • schema fork: position 0 suggests schema names with -f; position 1 emits -f with no source
    • workspace relink: position 0 emits -f; position 1 allows file completion (no -f)
    • __fish_openspec_positional_index helper is present in generated output

Behavior changes

Scenario Before After
openspec <TAB> commands + filesystem entries commands only
openspec workspace <TAB> subcommands + filesystem entries subcommands only
openspec show --type <TAB> change spec + filesystem entries change spec only
openspec init --store-path <TAB> filesystem entries filesystem entries (preserved)
openspec schema fork <TAB> schema names + filesystem entries schema names only
openspec schema fork core <TAB> schema names + filesystem entries no completions (free-form)
openspec workspace relink name <TAB> filesystem entries filesystem entries (preserved — path positional)

Verification

pnpm exec vitest run test/core/completions
pnpm run build
pnpm exec tsc -p tsconfig.json --noEmit

The completion test suite covers all four shell generators. Bash, Zsh, and PowerShell generator
outputs are unchanged; their existing tests pass without modification.

Summary by CodeRabbit

  • New Features

    • Added support for indexed positional argument completions in shell scripts.
  • Bug Fixes

    • Improved Fish shell completion logic to prevent filesystem-based suggestions for commands, flags, and positional arguments unless explicitly configured for path completion.

@leo-ar leo-ar requested a review from TabishB as a code owner June 11, 2026 02:38
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Fish shell completion generation is refactored to prevent filesystem fallback for command, flag, and positional argument completions unless explicitly marked as path types. A new positional index helper function computes argument positions, the type contract adds completionType to flag definitions, command metadata is updated to mark path-related flags, and the generator emits appropriate Fish completion flags to control fallback behavior.

Changes

Fish Completion Fallback Prevention

Layer / File(s) Summary
Completion type contract
src/core/completions/types.ts
FlagDefinition adds an optional completionType?: PositionalType field to specify the completion category for flag values.
Registry command metadata updates
src/core/completions/command-registry.ts
Path-related flags in new change, set change, workspace open, context-store setup, and multiple initiative subcommands are annotated with completionType: 'path'.
Fish template positional index helper
src/core/completions/templates/fish-templates.ts
FISH_STATIC_HELPERS template adds __fish_openspec_positional_index function to compute positional argument indices by counting eligible tokens while skipping flags and value-consuming flag parameters.
Fish generator core logic refactoring
src/core/completions/generators/fish-generator.ts
FishGenerator is refactored to emit -f flags in top-level command and flag completions to prevent file fallback, support indexed positional completions with depth and value-consuming flags, and use completionType to selectively enable file fallback for path-typed flags.
Test coverage for new behavior
test/core/completions/generators/fish-generator.test.ts
Test suite updated with a completionLine helper to verify exact Fish complete invocations, validate -f flags in command and positional completions, assert -r -f for value-taking flags, and confirm selective file completion for path-typed indexed positionals.
Changeset metadata
.changeset/fish-completion-no-file-fallback.md
Changeset entry documents the patch for @fission-ai/openspec describing the Fish completion filesystem fallback prevention.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • TabishB

Poem

🐰 A fluent fish swims through completion's tide,
No more false files shall confuse the guide.
With -f flags and completionType care,
Path suggestions bloom where they belong, fair!
Indexed positionals leap with grace,
Bash completion finds its proper place. 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly summarizes the main change: preventing Fish completions from falling back to filesystem suggestions. It is specific, concise, and reflects the primary improvement described throughout the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/core/completions/generators/fish-generator.test.ts (1)

428-429: 💤 Low value

Consider breaking up the long assertion for maintainability.

The assertion spans the entire Fish completion condition string. While this ensures exact format, it's brittle—generator changes to whitespace, condition order, or quoting would break the test even if the semantic behavior is correct.

♻️ More maintainable alternative

Check the key components separately rather than the full string:

-      expect(secondLine).toContain("complete -c openspec -n '__fish_openspec_using_subcommand workspace; and __fish_openspec_using_subcommand relink; and __fish_openspec_positional_index 1 2 --workspace'");
-      expect(secondLine).not.toContain('-f');
+      expect(secondLine).toContain('__fish_openspec_using_subcommand workspace');
+      expect(secondLine).toContain('__fish_openspec_using_subcommand relink');
+      expect(secondLine).toContain('__fish_openspec_positional_index 1 2 --workspace');
+      expect(secondLine).not.toContain('-f');

This preserves semantic correctness checks while being more resilient to formatting changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/core/completions/generators/fish-generator.test.ts` around lines 428 -
429, The long brittle assertion on secondLine should be split into multiple
targeted checks: replace the single expect(secondLine).toContain(...) with
separate expectations that secondLine contains the completion command prefix
("complete -c openspec -n"), contains "__fish_openspec_using_subcommand
workspace", contains "__fish_openspec_using_subcommand relink", and contains
"__fish_openspec_positional_index 1 2 --workspace", while keeping the existing
expect(secondLine).not.toContain('-f'); this uses the secondLine variable in
test/core/completions/generators/fish-generator.test.ts and makes the test
resilient to formatting or ordering changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/core/completions/generators/fish-generator.test.ts`:
- Around line 428-429: The long brittle assertion on secondLine should be split
into multiple targeted checks: replace the single
expect(secondLine).toContain(...) with separate expectations that secondLine
contains the completion command prefix ("complete -c openspec -n"), contains
"__fish_openspec_using_subcommand workspace", contains
"__fish_openspec_using_subcommand relink", and contains
"__fish_openspec_positional_index 1 2 --workspace", while keeping the existing
expect(secondLine).not.toContain('-f'); this uses the secondLine variable in
test/core/completions/generators/fish-generator.test.ts and makes the test
resilient to formatting or ordering changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0d28a349-8fdf-42aa-b558-1cdba32f0860

📥 Commits

Reviewing files that changed from the base of the PR and between 1b06fdd and d033217.

📒 Files selected for processing (6)
  • .changeset/fish-completion-no-file-fallback.md
  • src/core/completions/command-registry.ts
  • src/core/completions/generators/fish-generator.ts
  • src/core/completions/templates/fish-templates.ts
  • src/core/completions/types.ts
  • test/core/completions/generators/fish-generator.test.ts

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.

1 participant