feat(cli): add dub completion <shell> and dub man#107
Merged
Conversation
Adds shell-completion script generation for bash, zsh, and fish, plus a roff man-page generator, both driven by introspecting the live commander.js program tree. Output streams to stdout for users to redirect into the shell completion or MANPATH location of their choice. Completion covers top-level subcommands, per-command flags (read from commander metadata), local-branch completion for co/up/down/delete/ track/untrack via `git for-each-ref`, and file completion where commands take file args. The Homebrew formula now bundles `dub.1` and installs completions for all three shells via `generate_completions_from_executable`, so packaged users get them without extra steps. Docs updated in apps/docs/content/docs/guides/shell-integration.mdx and README.md. 21 new tests cover bash/zsh/fish syntax (validated against the host shell when present) and mandoc/groff rendering of the man page. Completes DUB-67
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
There was a problem hiding this comment.
Pull request overview
Adds generated shell completions and a generated roff man page to the DubStack CLI, driven by introspecting the commander program tree, plus docs/Homebrew integration so packaged installs include these assets.
Changes:
- Introduces
dub completion <shell>(bash/zsh/fish) anddub mancommands that emit generated scripts/pages to stdout. - Adds completion + man generators (
packages/cli/src/lib/completion.ts,packages/cli/src/lib/man.ts) and unit tests validating structure and optional host-tool parsing. - Updates documentation and Homebrew formula to install/generated man page and completions at install time.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Documents new dub completion / dub man usage. |
| packages/cli/src/lib/completion.ts | Implements bash/zsh/fish completion generation from commander metadata. |
| packages/cli/src/lib/man.ts | Implements roff man page generation from commander metadata. |
| packages/cli/src/commands/completion.ts | Adds CLI dispatcher for shell selection + error handling. |
| packages/cli/src/commands/completion.test.ts | Adds tests for completion outputs and optional shell syntax checks. |
| packages/cli/src/commands/man.ts | Adds CLI wrapper for man page generation. |
| packages/cli/src/commands/man.test.ts | Adds tests for man output structure and optional formatter rendering. |
| packages/cli/src/index.ts | Wires completion and man commands into the CLI. |
| apps/docs/content/docs/guides/shell-integration.mdx | Adds docs sections describing completions and man page installation. |
| homebrew/dubstack.rb | Generates and installs man page + completions during brew install, and tests for .TH header. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Adversarial review round 2 found two correctness bugs in the branch- completion router: dub revert (whose target positional is a PR number or SHA) was routed to git-branch completion, and dub trunk returned local branches instead of its subcommand list. Replace the regex with an explicit allow-list (co, up, down, delete, track, untrack) and have zsh prefer subcommand completion whenever a command has any. Add negative tests covering both regressions. Also: escapeFish now doubles backslashes; escapeZsh now also escapes backticks and dollar signs; the zsh __dub_branches helper early-returns on an empty repo so _describe does not show a spurious blank candidate; main installs an EPIPE handler so dub man piped to head exits cleanly; the Homebrew formula passes err: :merge so a broken dub man surfaces in brew test instead of writing a partial man page; docs recommend a user-writable zsh completions directory over the system fpath entry. Completes DUB-67
…cape hardening Addresses Copilot and CodeQL review feedback on PR #107: Bash now walks the argv tokens to find the deepest matching command path, so dub config ai-provider --<Tab> dispatches to ai-provider's flags rather than config's. Zsh emits a second-level state machine for parent commands with subcommands; fish gains a __dub_using_nested predicate. Branch-valued option flags (--parent, --branch, --before, --after) and file-valued flags now drive the right completer in every shell, not just bash. Branch-name completion is now allow-list only: up and down were removed (they take a numeric step count), and the broad regex over the command description is gone so dub revert <target> no longer offers branches for what is a PR number or SHA. escapeZsh now escapes backslashes too (CodeQL js/incomplete-sanitization on alerts 7+8). FILE_VALUE_FLAGS now lists the real --by-file flag instead of a phantom --input-file. The man-page renderer recurses through nested subcommands so dub config ai-provider and similar are documented with their own options instead of just their names; the alias suffix now keeps its leading space outside escapeRoff so labels read "checkout (aliases: co)". Tests: +7 covering nested paths, branch-valued zsh/fish routing, alias spacing, and CodeQL backslash escape coverage. 35 completion + man tests pass, full suite 1480/1480. Completes DUB-67
CI Linux runs the man render check through groff, which emits SGR (ANSI) escape sequences for bold/underline (\e[1m, \e[7m, \e[4m, ...). The reverse- video sequence around "DUB" then "(1)" was breaking the substring assertion that the rendered output contains "DUB(1)". macOS mandoc was untouched because it uses backspace overstrike (N\bN) instead. Strip ANSI CSI sequences alongside the existing backspace stripper. The stripped output now contains "DUB(1)" on both formatters. Completes DUB-67
|
🎉 This PR is included in version 1.10.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
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.
TL;DR
Adds two new CLI commands —
dub completion <shell>anddub man— that emit shell-completion scripts and a roff-formatted man page to stdout, generated by introspecting the commander.js program tree. Homebrew formula now bundles the man page and per-shell completions automatically.Why
DUB-67 is in the Tier 7 polish project: hand the CLI the same affordances every mature tool gives users — Tab-completion and
man dub.Manually-maintained completion tables drift the moment any subcommand or flag changes; generating from commander metadata keeps completions in sync with the binary's own --help.
Before
man dubreturned 'No manual entry'.After
dub completion bash|zsh|fishwrites a working completion script that covers subcommands, per-command flags from commander, branch names for co/up/down/delete/track/untrack, and branch-valued option flags like --parent/--branch.dub manemits roff for the standardman dubexperience; mandoc/groff render the page cleanly.generate_completions_from_executable.File-by-file
packages/cli/src/lib/completion.ts
new +471 / -0
Bash/zsh/fish generators.
describeProgramwalks the commander tree once; each generator produces shell-specific syntax. Branch-arg subcommands and branch-valued flags are routed through__dub_branches(git for-each-ref). Empty branch/file patterns short-circuit so we never emitcase "$x" in ).packages/cli/src/lib/man.ts
new +109 / -0
Roff generator with
escapeRoffto neutralize backslashes, leading dots, and hyphens. Applies escaping to the.THtitle args too so pre-release versions like 1.0.0-beta.1 stay safe.packages/cli/src/commands/completion.ts
new +26 / -0
Thin dispatcher: pick a shell, throw
DubErrorwith actionable recovery hints when the shell is unknown.packages/cli/src/commands/man.ts
new +9 / -0
Thin wrapper that forwards the live
programplus its version to the roff generator.packages/cli/src/index.ts
mod +32 / -0
Wires the two new commands. Both write to stdout via
process.stdout.writeso users can redirect into a file. New imports stay alphabetical in the existing import block.packages/cli/src/commands/completion.test.ts
new +158 / -0
13 tests covering error path, structural assertions per shell (commands, branches, flags, headers), and host-shell syntax validation that gracefully skips when the shell is missing.
packages/cli/src/commands/man.test.ts
new +123 / -0
8 tests: .TH header shape, escape semantics (hyphens, backslashes, pre-release versions), section presence, nested subcommands, and a mandoc/groff render check.
apps/docs/content/docs/guides/shell-integration.mdx
mod +44 / -0
Adds 'Shell completions' and 'Man page' sections to the existing shell-integration guide with per-shell install snippets.
homebrew/dubstack.rb
mod +7 / -0
Formula now generates
dub.1and per-shell completions from the just-installed binary, so packaged users get them withbrew install dubstack. Adds atest docheck thatdub manemits a.THheader.README.md
mod +21 / -0
New
dub completion <shell>/dub mansection in the command reference linking to the docs guide.Where to focus review
packages/cli/src/lib/completion.ts:218-225: Branch names fromgit for-each-refflow through__dub_compreply_lines, which sets a local IFS=newline beforecompgen -Wso branch names with shell-special characters survive word-splitting. Worth confirming the IFS scope is local to the function.packages/cli/src/lib/man.ts:18-99:escapeRoffneutralizes backslashes, hyphens, and leading dots; it is now applied to the.THdate + version args too. Confirm hyphens-as-\-is desirable for arbitrary descriptions (it is — groff treats-as a minus and\-as the ASCII hyphen, which is whatmanexpects in copy).packages/cli/src/lib/completion.ts:83-91: Both generators take the liveprogramand walk its commands/options. Future commands added to commander.js automatically appear in completions and the man page with no extra wiring; this is the explicit non-goal of avoiding hand-maintained lists.homebrew/dubstack.rb:12-25:Utils.safe_popen_readruns the just-installed binary during the formula's install phase. Confirms the binary is on the path and doesn't depend on a non-existent state directory (the man command is read-only and doesn't touch .git/dubstack).Test plan
pnpm buildthendub completion bash | bash -n,dub completion zsh | zsh -n,dub man | mandoc -Tutf8— all OK. fish not installed locally; covered by the in-test parser.pnpm buildsucceeded — dist/index.js 877.95 KB.Quality gates
pnpm checks- passed (biome check . — 334 files checked, 0 errors.)pnpm typecheck- passed (tsc --noEmit across all 3 workspace packages — clean.)pnpm test- passed (vitest — 1466 tests pass across 131 test files (21 new tests for this issue).)Self-QA
See QA fallback evidence.
Deterministic CLI proof: pnpm checks/typecheck/test all green; bash/zsh syntax-checked outputs; mandoc renders the man page; 21 new tests cover the generators directly.
dub completion bash | bash -nreturns 0dub completion zsh | zsh -nreturns 0dub completion fish | fish -nreturns 0 (skipped locally — fish not installed; in-test coverage exercises the generator)dub man | mandoc -Tutf8renders DUB(1) with NAME/SYNOPSIS/DESCRIPTION/COMMANDS sectionsAcceptance criteria
dub completion <shell>works for bash, zsh, fish - Three generators in packages/cli/src/lib/completion.ts; each output validates against its native parser in tests.git for-each-ref refs/heads/; gated on BRANCH_ARG_COMMANDS (co/checkout, up, down, delete, track, untrack) plus BRANCH_VALUE_FLAGS (--parent, --branch, --before, --after).dub manemits valid roff - man.test.ts asserts .TH header, section structure, and renders the output through mandoc/groff when present.dub.1toman1/and runsgenerate_completions_from_executable. Newtest doassertsdub manemits a.THheader.Adversarial review
Iterations: 1
Remaining critical/major: 0/0
Remaining minor/nitpick: 0/0
Dependencies
Rollout
Pure additive: two new commands and a Homebrew formula tweak. Nothing existing changes behavior.
Commit