feat(cli): generate shell completions#94
Merged
Merged
Conversation
Add bash/zsh/fish shell completions for `things` (PR1 of #88). Two halves: - Runtime: wire kongplete.Complete(parser) into main() so the binary answers COMP_LINE queries from the kong command tree — subcommands, flag names, and enum values (e.g. --color). No-op for normal runs. - Static: a `things completions <bash|zsh|fish>` command emits the per-shell stub that delegates back to the binary. It uses the bare command name so the script survives `brew upgrade`, and is what GoReleaser's generate_completions_from_executable will capture (PR2). Tests cover both halves: static stub emission per shell, name substitution, the unsupported-shell guard, and a real COMP_LINE query driven through kongplete in-process. Documented in internal/skill/SKILL.md and README.md. Part of #88
The cask (binary install) has landed; the generate_completions_from_executable block that emits completion files is a separate follow-up. Reword README and SKILL.md so the present state isn't read as 'the whole cask is pending'.
5 tasks
ryanlewis
added a commit
that referenced
this pull request
May 30, 2026
PR 2 of #88 — the release plumbing. Builds on PR 1 (#94), which added the `things completions <shell>` command. ## What Wire `generate_completions_from_executable` into the `homebrew_casks` block so `brew install` runs `things completions bash|zsh|fish` at install time and drops the scripts into the Homebrew prefix. No custom `executable:` — it defaults to the first binary (`things`), which is correct for our root-level archive layout. ```ruby binary "things" generate_completions_from_executable "things", "completions", base_name: "things", shell_parameter_format: :arg, shells: [:bash, :zsh, :fish] ``` ## The non-obvious part: quarantine ordering Completion generation **runs the binary during artifact install**, which is *before* `postflight`. Our binary is unsigned, so it's Gatekeeper-quarantined on download — that's exactly why the cask already removed the quarantine xattr. But that removal lived in **`postflight`**, i.e. *after* the completion run. Left as-is, `generate_completions_from_executable` would invoke a still-quarantined binary, Gatekeeper would block it, and **zero completion files would be written** — silently. Fix: move the `xattr -dr com.apple.quarantine` hook from `postflight` → `preflight` (`hooks.post.install` → `hooks.pre.install`). Homebrew runs `preflight` before artifact install, so the binary is de-quarantined in time. It's strictly safe for runtime too — same removal on the same staged binary, just earlier. ## Docs Flipped the README + `internal/skill/SKILL.md` wording from "doesn't generate completions yet" to "Homebrew generates these on install." (PR 1 had set the honest interim phrasing.) ## Verification - [x] `goreleaser check` passes - [x] `goreleaser release --snapshot --clean --skip=publish` emits `Casks/things.rb` containing the `generate_completions_from_executable` block **and** the `preflight` de-quarantine - [x] `go test ./internal/skill/` passes (SKILL.md is embedded) - [ ] **Post-tag (needs a real release):** `brew install ryanlewis/tap/things` (or `brew reinstall`) writes `bash`/`zsh`/`fish` completion files into the prefix - [ ] **Post-tag:** open a fresh shell, `things <TAB>` shows subcommands The last two require a published tag, so they're for after merge + `/release`. The quarantine-ordering fix is the thing to watch in that test — if completions still don't appear, the next lever is `no_quarantine` or confirming the sandbox can exec the de-quarantined binary.
ryanlewis
added a commit
that referenced
this pull request
May 30, 2026
Release notes header for **v0.4.0**, required at the tagged commit by `.goreleaser.yaml` (`readFile ".github/releases/<tag>.md"`). Merge this, then the `v0.4.0` tag gets cut. - Headlines bash/zsh/fish **shell completions** (#94, #95). - Notes the lipgloss v2 migration (#93) and routine dep updates. - Folds in the skill-doc changes (#89). Also removes the orphaned `v0.3.2.md`: v0.3.2 was never tagged and its only change (#89) is now covered here, so we go straight from v0.3.1 → v0.4.0.
8 tasks
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.
PR 1 of #88 — the CLI half of shell completions. PR 2 (GoReleaser cask wiring) follows.
What
thingsnow ships bash/zsh/fish completions that stay in sync with the CLI surface automatically. Two halves:main()callskongplete.Complete(parser)beforeparser.Parse. When the shell invokesthingswithCOMP_LINEset, it computes candidates from the kong command tree and exits; it's a no-op for normal runs. This gives subcommand, flag-name, and enum-value completion (e.g.--color→auto|always|never) with no custom predictors — i.e. flag-name-only, per the issue's v1 scope.things completions <bash|zsh|fish>command prints the per-shell stub that delegates back to the binary. It emits the bare command name (complete -C things things) rather than kongplete's absolute path, so the script survivesbrew upgrade(which moves the versioned Cellar path). This is exactly what PR 2'sgenerate_completions_from_executable(args: [completions],shell_parameter_format: arg) will capture.Acceptance (PR 1)
things completions bash|zsh|fisheach print a valid script for the named shellCOMP_LINE)internal/skill/SKILL.mdlists the new subcommandmake test(-race) andmake lintpassTests
bashcompinitpreamble + unsupported-shell rejectionrenderCompletionname substitution proven with a non-thingsname (100% covered)COMP_LINEquery driven throughkongpletein-process (WithExitFunc+kong.Writers), asserting subcommands, flag names, and--colorenum values — locks down the load-bearing half against silent regressions from future kong/kongplete bumpsNotes
source <(things completions zsh),things completions fish | source), with thePATHrequirement and zsh-after-compinitcaveat called out.kongplete(willabides/kongplete) is added as a direct dependency; pure-Go, no cgo.🤖 Reviewed by two adversarial multi-agent passes before commit; all confirmed findings folded in.