Completion Authoring Guide
CJ's Shell ships with a hybrid completion engine: built-ins are documented in-code, while external commands learn their options and subcommands automatically by reading their manual pages. This guide explains how that pipeline works and how to author or override completion data when you need to fill in gaps or add custom behaviour.
How automatic completions are generated
- On-demand scraping: The first time you request completions for an external command that
resolves in
PATH, cjsh invokesman -P cat <command>(falling back toman <command>) and scrapes the result. The parser looks forOPTIONS,COMMANDS, orSUBCOMMANDSsections, pulls out option switches and subcommand names, preserves option aliases and value metavariables, and condenses their descriptions to a single line. - Caching: Parsed data is written to
~/.cache/cjsh/generated_completions/<command>.txt. The next completion request loads this cache instead of invokingmanagain. Cache entries also store a short summary that feeds inline help and completion source hints. - Nested commands: When a completion entry includes subcommands, cjsh will look for additional
caches named
<command>-<subcommand>.txtand merge their contents. This is howgit-remotecompletions are chained intogit. - Bulk generation: The
generate-completionsbuiltin pre-populates caches for an entirePATHor a list of commands. By default it forces regeneration (--force); pass--no-forceto keep existing manual edits. Use--subcommandsto also pre-generate discovered nested command caches,-j/--jobsto parallelise scraping, and--quietto suppress per-command status output.
Controlling automatic learning
Scraping man pages on the fly is convenient, but it can consume CPU, spawn short-lived man
processes, and keep memory allocations around until the session ends. When you prefer a predictable
footprint, turn learning off and rely exclusively on whatever is already cached (or on
generate-completions).
- Run
cjshopt completion-learning offduring a session to stop future man-page lookups. The toggle persists across restarts if you addcjshopt completion-learning offto~/.cjshrc. - Launch cjsh with
--no-completion-learning(or addcjshopt login-startup-arg --no-completion-learningto~/.cjprofile) to start with learning disabled from the first prompt. - The
generate-completionsbuiltin continues to work either way, so you can keep caches warm with a one-time run and leave learning off during normal interactive use. - When
CJSH_MAN_PATHis set, cjsh always uses thatmanbinary (even outside secure mode). - In secure mode (
--secure), cjsh only usesCJSH_MAN_PATH. If it is unset or invalid, scraping is skipped and cjsh relies on cached data.
If a man page cannot be read (missing man, atypical formatting, or sandbox restrictions), cjsh
creates an empty cache entry. You can delete that file or replace it with a manual definition.
Cache layout and file format
Each cache file is a versioned, tab-separated completion specification:
generated by cjsh from man page for git
format: 2
summary: the stupid content tracker
path: /usr/bin/git
E O --output Write output to a file -o required file FILE --stdout --format 0 0 project-files 0 0 either
E S remote Manage remote repositories none none 0 0 0 0 space
E P remote BRANCH Branch to operate on required branch BRANCH 0 0 git-branches 1 0 space
Key points:
- The header line must stay exactly
generated by cjsh from man page for <command>or cjsh will ignore the file. format: 2selects the rich format. Version 1OandSrecords remain readable for backward compatibility; newly generated files use version 2.summary:andpath:supply display metadata. Either value may be empty.- Rich entries begin with
E. Their tab-separated fields are, in order: record marker, kind (Ooption,Ssubcommand, orPpositional), parent subcommand scope, canonical text, description, aliases, value requirement, value type, value name, enum choices, conflicts, dependencies, repeatable, deprecated, dynamic provider, positional index, variadic, and value separator. - Lists use commas. Literal percent signs, commas, tabs, and newlines inside fields are encoded as
%25,%2C,%09, and%0Arespectively. - A subcommand's nested entries name it in the scope field. Multiple scope components describe a
deeper tree, for example
remote,add. - Value requirements are
none,required, oroptional. Separators arespace,equals, oreither. - Value types are
none,text,file,directory,enum,command,branch,process, orcustom. Enum candidates come from the choices field. Other dynamic values use the named provider hook, or the provider convention matching their value type. - Conflicts hide an entry when any named conflicting entry was already used. Dependencies hide an entry until every named dependency is present. Non-repeatable entries disappear after use.
- Deprecated entries remain available but are labeled as deprecated in the completion menu.
- Values are case-insensitive in matching but should be written the way you want them to appear to users.
- File names are normalised to lower-case with non-alphanumeric characters translated to
_. Letgenerate-completionscreate the skeleton once if you are unsure about the exact spelling.
Dynamic providers are registered in-process with register_dynamic_completion_provider() from
completion_spec.h. The request includes the command path, arguments, cursor argument, current
value, working directory, and declared value metadata. Providers return value/description pairs.
Complete in-memory specifications can similarly be installed with register_command_doc(). These
are the extension points intended for future Bash and Zsh compatibility workers.
Authoring or overriding completions manually
- Create the cache directory (once):
generate-completionsor the first automatic scrape will do this, but you can alsomkdir -p ~/.cache/cjsh/generated_completionsyourself. - Seed a template (optional): Run
generate-completions --no-force <command>to create the cache without overwriting existing edits. Even if scraping fails, the command establishes the correct file name for you to edit. - Edit the cache file: Open
~/.cache/cjsh/generated_completions/<sanitised-name>.txtand adjust the summary or add new lines using the format above. - Add multi-level entries: For subcommand-specific completions (e.g.
kubectl get), use the scope field to keep the nested tree in one rich specification. Separate legacy files such askubectl-get.txtremain supported and are stitched together automatically. - Preserve your changes: Future runs of
generate-completionsdefault to--force. Usegenerate-completions --no-force ...when you want to refresh other commands without clobbering manual content, or keep a copy of the file under version control and reapply as needed.
You can delete a cache file to force cjsh to rescrape the man page on the next completion request. This is useful after upgrading a tool with new options.
Tips and troubleshooting
- Commands without man pages: Some utilities only provide
--help. Create the cache file manually or pointgenerate-completionsat a packaged man page (for example, install the corresponding*-docpackage). - Unusual formatting: If the parser misses options, check the rendered man page. Options that do
not start with
-or subcommands listed outside dedicated sections may need to be added manually. - Custom summaries: Inline help uses the summary line. Tailor it to the way you present the command in your prompts or completion preview.
- Refreshing everything: Remove
~/.cache/cjsh/generated_completionsor rungenerate-completions --forceto rebuild all caches. Beware this overwrites manual edits. - Sandboxed environments: If
manis unavailable, completions fall back to whatever data already exists. Consider bundling cache files with your dotfiles so they can be copied into the cache directory during provisioning. - Too many matches? Reduce menu noise with
cjshopt set-completion-max <number|default|status>(any value >= 1).
With these tools you can match or exceed the curated completion sets provided by fish, bash, or zsh while keeping cjsh's minimal-runtime-dependency footprint.