A polished, self-contained Zsh setup with no frameworks, prompt themes, or third-party plugins.
| What you want to do | Entry point | What happens next |
|---|---|---|
| Browse a known folder | Type ~/, ./, /, or another directory path, then press Tab |
Right enters a child; Left goes back; Enter inserts an editable path |
| Recall a visited folder | Option-Tab at the prompt | Filter this shell's native directory stack; Enter inserts its path, Ctrl-O browses it |
| Find a file below a folder | Path + Tab → Ctrl-F | Review the displayed source and scope, enter a discovery query, then press Enter |
| Act on a found file | Select it and press Enter | Choose Open with default app, Reveal in Finder, Copy path, or Insert path |
| Switch a local Git branch | g |
Filter recent local branches; Enter switches, Ctrl-Y copies the name |
| Review Git changes | g → Ctrl-X |
Read working changes or selected-branch commits, drill into files and colored diffs |
| Recall a command | Ctrl-R | Match remembered fragments in any order; selection returns an editable command |
| Discover your custom tools | compozsh |
Explore loaded public functions and their safe help |
These tools share a responsive full-screen layout, visible action hints and a Ctrl-K keyboard guide. Escape returns or cancels. Inserting a directory does not change it immediately: review the visible path, then press Enter at the normal prompt. File actions are explicit and use the exact selected item.
Ctrl-F defaults to scoped Git discovery within repositories, Spotlight at home or root on macOS, and bounded filesystem search elsewhere. It shows the source, scope and Searching… before capture; failed sources are reported separately from empty results and never silently replaced with a home-directory crawl. See search coverage and limits.
Option-Tab needs Option as Meta in the active
Terminal profile. Updating from 1.x? Read the migration guide:
the filesystem workspace replaces the old d and f commands.
Using an AI chat alongside your shell? See the native tab workflow for full-sized sessions in one Terminal window.
- A compact two-line prompt that adds a project-runtime line only when relevant
- A compact Git summary with exact staged, modified, untracked, conflicted, stashed, ahead, and behind counts
- Clear Git operation warnings for merges, rebases, cherry-picks, and bisects
- Read-only Git review with staged/unstaged changes, local commit history, a file navigator and independently scrollable, numbered diff reader
- Command duration for commands that take at least two seconds
- Active Python virtual environment and background-job indicators
- Automatic runtime or toolchain detection for more than 35 project types, including native, web, JVM, .NET, functional, scientific, game, scripting, and infrastructure languages
- Package-manager, build-tool, framework, workspace, and container context
- Runtime mismatch warnings from project version files
- Shared, deduplicated shell history
- Protection against accidentally overwriting non-empty files with
> - Case-insensitive native Zsh completion plus a contextual fuzzy directory
picker on
Tab - Prefix-based history search with Up/Down or
Ctrl-P/Ctrl-N - A native fuzzy
Ctrl-Rhistory picker with ranked, order-independent fragments and deduplicated results - One path + Tab filesystem workspace with Browse, explicit scoped Search, separate Recents, and safe file actions
- A live
compozshtool explorer that discovers public add-on functions, searches them fuzzily, and opens their safe self-documentation - Live native history autosuggestions with character, word, and full acceptance
Ctrl-X Ctrl-Eto edit the current command in$EDITOR- Live native syntax highlighting for commands, arguments, operators, strings, variables, comments, assignments, and redirections
- Searchable, arrow-driven recent-directory and Git-branch selectors with numbered direct shortcuts
- Terminal-only native colors for file listings, completion, and matches
- A consistent semantic Git palette for status, diffs, branches, and remotes
- Colored manual pages with highlighted headings, options, and references
- A useful terminal tab title and a few small navigation aliases
cpdirto copy the current working directory without selecting terminal text- An optional first-loaded
~/.zsh.addons/local/init.zshfor machine setup - Automatic loading of focused, order-independent
.zsh.addons/**/.zsh.<name>files - A native Xcode workspace for choosing schemes and destinations, then building, testing, analyzing, cleaning, or launching an app in Simulator
- An Xcode add-on that exports Apple-authored skills for common coding agents
The .zshrc file is only a tiny bootstrap. Every shared feature lives
as a native, readable peer in .zsh.addons, with no framework or
plug-in manager.
The configuration has one explicit initialization boundary followed by peers:
“Configuration base” means ${ZDOTDIR:-$HOME} throughout this README. Diagrams
and examples use ~ for the normal layout when ZDOTDIR is unset; if it is
set, place the active .zshrc, initializer, and private peers beneath
$ZDOTDIR instead.
flowchart TB
subgraph repository["Repository-managed source · do not personalize here"]
bootstrap["<repository>/.zshrc<br/>minimal bootstrap"]
shared["Shared .zsh.NAME peers<br/>repository directory or installed copies"]
starter["<repository>/templates/init.zsh<br/>inert commented starter"]
end
subgraph home["User home directory"]
active["~/.zshrc<br/>symlink or copied bootstrap<br/>do not edit by hand"]
initializer["~/.zsh.addons/local/init.zsh<br/>user-editable · optional"]
private["~/.zsh.addons/**/.zsh.NAME<br/>user-editable private peers"]
disabled["renamed or removed file<br/>not loaded"]
end
discovery["Peer discovery<br/>order-independent"]
ready["Ready interactive Zsh"]
bootstrap -->|"install by symlink or copy"| active
starter -->|"copy once when private file is absent"| initializer
active -->|"1 · source first when present"| initializer
initializer -->|"2 · continue"| discovery
discovery -->|"load matching shared peers"| shared
discovery -->|"load matching private peers"| private
shared --> ready
private --> ready
shared -.-> disabled
private -.-> disabled
classDef managed fill:#dbeafe,stroke:#2563eb,color:#111827
classDef installed fill:#fef3c7,stroke:#d97706,color:#111827
classDef editable fill:#dcfce7,stroke:#16a34a,color:#111827
classDef muted fill:#f3f4f6,stroke:#6b7280,color:#374151
classDef runtime fill:#ede9fe,stroke:#7c3aed,color:#111827
class bootstrap,shared,starter managed
class active,discovery installed
class initializer,private editable
class disabled muted
class ready runtime
NAME represents any non-empty add-on name. For example,
.zsh.company-tools and work/.zsh.ruby both match the real
.zsh.<name> convention and load automatically.
Use the diagram as a placement rule:
- Treat the repository
.zshrc, the contents of its shared.zsh.<name>peers, and an installed~/.zshrcas managed code. Do not place personal settings in them. Edit shared contents only when deliberately contributing to the project through Git. - Put early machine setup—
PATH, Homebrew, runtime selection, trusted vendor hooks, and environment inputs required by peers—in~/.zsh.addons/local/init.zsh. - Put a cohesive personal feature in any file below
~/.zsh.addonswhose basename starts with.zsh., such as~/.zsh.addons/work/.zsh.company. No manifest or registration step is required; start a new shell withexec zshand it appears automatically. - Disable an add-on by renaming it so the basename no longer starts with
.zsh., for example.zsh.companyto.disabled.zsh.company, or remove it. Renaming a repository peer is the supported toggle, but it intentionally appears as a local change ingit status; its contents remain untouched.
There is deliberately no “core feature” tier. Shell behavior, line editing,
prompt rendering, tools, and optional integrations are all add-ons with the
same status and naming convention. The optional local initializer is not a core
tier: it is the single ordered boundary for prerequisites that must exist before
peers initialize, such as Homebrew, PATH, a selected Ruby, trusted early
agent hooks, environment variables, and documented public defaults.
Its init.zsh basename intentionally does not match .zsh.<name>, so peer
discovery cannot load it twice.
Add-ons need no manifest or registration list: a correctly named file beside the resolved bootstrap or in the user's add-on directory is enough, and renaming it disables it. The loader uses lexical traversal only for reproducible diagnostics. After the initializer, peer units cannot depend on traversal order: renaming or loading the same enabled files in another order must produce the same final shell. This prevents numeric prefixes, dependency metadata, extra initialization phases, and other plug-in-manager ceremony from creeping in.
Repository layout:
compozsh/
├── .agents/skills/ repository-scoped agent workflows
│ └── compozsh-platform-review/
│ ├── SKILL.md first-party macOS modernization audit
│ └── scripts/ privacy-safe native platform inventory
├── .zshrc minimal initializer and peer-discovery bootstrap
├── .zsh.addons/ all shared peer features
│ ├── .zsh.shell shell options, history, and native tool colors
│ ├── .zsh.editor completion, temporary-screen pickers, and editing
│ ├── .zsh.find bounded search, path details, and explicit file actions
│ ├── .zsh.git-review read-only working changes, commits, files and diffs
│ ├── .zsh.git-syntax optional bounded system-Vim token snapshots
│ ├── .zsh.help live tool discovery and help snapshots
│ ├── .zsh.highlighting command-line syntax and semantic UI palette
│ ├── .zsh.navigation directory/branch workspaces, details and copying
│ ├── .zsh.output semantic palette, help styling, native output wrappers
│ ├── .zsh.prompt prompt, Git state, and project/toolchain context
│ ├── .zsh.tools small commands and safe Git cleanup
│ ├── .zsh.xcode native Xcode workspace and agent-skill integration
│ └── support/
│ └── git-syntax.vim trusted adapter; not an autoloaded shell add-on
├── templates/
│ └── init.zsh inert starter copied once for private initialization
├── install.zsh safe symlink/copy installer with preview and rollback
├── docs/ static showcase, independent of the shell
│ ├── index.html accessible page and installation guide
│ ├── styles.css responsive visual design
│ ├── app.mjs safe browser-only demo interactions
│ ├── demo-data.mjs synthetic task examples and preview outcomes
│ ├── search.mjs small pure demo-search algorithm
│ └── README.md local review and GitHub Pages configuration
├── tests/ isolated shell and optional website checks
│ ├── run.zsh dependency-free test runner
│ ├── support.zsh assertions and disposable-shell helpers
│ ├── *_test.zsh focused behavioral specifications
│ ├── site.test.mjs optional Node tests for browser search
│ ├── site-scenes.test.mjs optional Node checks for demo fixtures
│ └── site.browser.mjs optional local browser interaction checks
├── LICENSE GNU GPL version 3 or any later version
├── README.md user-facing behavior and installation
└── AGENTS.md contributor and coding-agent contract
The autoloaded convention is .zsh.<name>, not .zshrc.<name>. Each shipped
peer owns one focused concern and can still be sourced independently:
| File | Responsibility | Main user-facing behavior |
|---|---|---|
.zsh.shell |
Base interactive-shell policy | Safe redirection, shared history, directory-stack behavior, and terminal-aware native colors |
.zsh.editor |
Completion and ZLE editing | Native completion; the continuous-screen Browse/Search/Recents workspace and prompt Recents shortcut; location trail, captured file summaries, shallow previews, actions and Back bookmarks; shared screen lifecycle, layout, Control shortcuts, responsive Escape and keyboard guide; fuzzy Ctrl-R and history autosuggestions |
.zsh.find |
Workspace search and path actions | Scoped Git, home/root Spotlight and bounded filesystem defaults; explicit source choices and failure reporting; filename-first results and type-aware actions on exact files, folders and links |
.zsh.git-review |
Read-only Git review | g → Ctrl-X opens working changes or selected-branch commits; arrow-driven file → focused diff → full-context reading, Ctrl-R file-list/diff refresh, individual untracked files inside new directories, and bounded on-selection previews |
.zsh.git-syntax |
Optional captured-code syntax | Apple's system Vim supplies lexical tokens for supported Git review files; bounded capture, plain fallback and no new shortcut or configuration requirement |
.zsh.help |
Live tool discovery | compozsh fuzzily explores loaded public add-on functions with a responsive help inspector and canonical documentation |
.zsh.highlighting |
Live command-line semantics | Distinct styles for commands, aliases, functions, arguments, operators, paths, strings, variables, and comments; shared semantic UI and picker styles |
.zsh.navigation |
Native Recents and Git movement | Private native-stack provider and Recents view with editable path insertion for the filesystem workspace; g branch picker with commit/upstream details, copying and small navigation aliases |
.zsh.output |
Semantic command-output colors | Terminal-aware colors for Git, grep, man, and optional help styling, driven by the customizable ZSH_OUTPUT_COLORS palette |
.zsh.prompt |
Prompt facts, layout, and rendering | Responsive prompt, Git state, command duration, jobs, virtual environments, and project/toolchain context |
.zsh.tools |
Focused utility commands | mkcd, cpdir, guarded git-discard-all, and prompt-refresh |
.zsh.xcode |
Native Xcode integration | xcode opens a scheme/destination/action workspace over Apple’s CLI tools; update_xcode_skills exports Apple-authored skills to detected coding agents |
~/.zsh.addons/local/init.zsh is different from those peers. It is a private,
user-editable initializer and the only file with a guaranteed position: it
loads first. The repository ships a valid, fully commented
templates/init.zsh starter. Copy it once into the home
directory, then use the private copy only for inputs or prerequisites that must
exist before peers load: command paths, Homebrew or language-manager setup,
runtime selection, trusted early vendor hooks, and documented public defaults.
The tracked starter remains inert and immutable.
To add a new personal unit, create any regular file beneath
~/.zsh.addons whose basename follows .zsh.<name>:
~/.zsh.addons/
├── local/
│ └── init.zsh loaded first; private machine setup
├── .zsh.my-shortcuts discovered automatically as a peer
└── work/
└── .zsh.company-tools nested peers work too
No list needs updating and no command installs the peer. Run exec zsh after
creating, renaming, or removing one.
- Zsh 5.9 or newer (current macOS releases ship with Zsh 5.9); older releases are deliberately unsupported
- Git, for cloning and the repository-aware prompt, navigation, and tools
- A terminal with Unicode and color support; no patched font is required
- Full Xcode, only for the optional native Xcode workspace; Xcode 27 or newer is required for the separate Apple skill exporter
Check the installed versions with:
zsh --version
git --versionCompozsh targets a deliberately modern Zsh baseline. The minimum supported version advances when newer language or shell features materially improve correctness, security, performance, or maintainability. Superseded compatibility paths are removed instead of preserved indefinitely.
Apple's system shell defines the compatibility ceiling. Each major macOS release triggers a review of its bundled Zsh, but Compozsh's minimum never exceeds the version shipped by the latest generally available macOS. Running Compozsh must not require a Homebrew, MacPorts, or user-compiled replacement shell.
This is modern-first, not novelty-first. A newer mechanism must provide a concrete benefit and pass the same correctness, safety, and performance checks as any other change. Minimum-version increases are documented as breaking changes so users can make an informed upgrade.
The repository ships a compozsh-platform-review agent skill for deliberate
reviews after macOS major, minor, or patch updates. Codex discovers it from the
repository's .agents/skills directory when a task is opened anywhere inside
the clone. Invoke it manually after updating:
$compozsh-platform-review Audit this Mac after the macOS update and recommend evidence-backed Compozsh improvements.
The audit compares Apple's bundled Zsh, Terminal.app, and default command-line tools with the current product. It separates locally observed capabilities, official release documentation, and inferred opportunities; then evaluates correctness, security, performance, UI ergonomics, code deletion and architectural simplification. The default result is a read-only recommendation report. It does not update software, change preferences, edit Compozsh, or recommend third-party dependencies.
An exact before/after inventory is optional. Before upgrading, save one outside the repository:
/bin/zsh .agents/skills/compozsh-platform-review/scripts/snapshot-platform.zsh \
--output "$HOME/Documents/compozsh-platform-before.tsv"After upgrading, include that file in the skill request. Without a baseline the skill still uses the current system, primary release sources, local man pages, isolated probes and measured prototypes, while disclosing the missing exact comparison. Snapshots omit personal paths, names, host data, preferences and hardware identifiers. The structure follows the current OpenAI skill guidance.
The repository includes a native Zsh installer. It validates the tracked files, shows its complete plan, preserves existing state, and rolls back an incomplete activation. It does not download a framework or run code from another project.
Save the location in repo_dir; every later command then works from any current
directory:
repo_dir="$HOME/Projects/compozsh"
git clone https://github.com/bitbemol/compozsh.git "$repo_dir"If the repository is already cloned, skip git clone and set repo_dir to its
existing location. The clone already contains a complete .zshrc; do not
create, empty, or replace that repository file.
Run a dry run first:
zsh "$repo_dir/install.zsh" --symlink --dry-runThe plan names the active configuration base and every action. Zsh uses
${ZDOTDIR:-$HOME} as that base, so the familiar ~/.zshrc paths below become
$ZDOTDIR/.zshrc when ZDOTDIR is set.
An existing active .zshrc may contain important setup. It does not need to be
empty, and you should not delete or truncate it. The installer archives it under
.zsh-backups/compozsh-<timestamp>/ before replacement. It never
prints the old file's potentially private contents.
Run the same command without --dry-run:
zsh "$repo_dir/install.zsh" --symlinkReview the displayed plan and answer y to continue. The installer then:
- links the active
.zshrcto the repository bootstrap; - keeps an existing private
.zsh.addonstree intact; - copies
templates/init.zshonly whenlocal/init.zshis absent; - archives any active
.zshrcit replaces; and - restores the prior state if activation fails.
Start a new shell after a successful installation:
exec zshFor direct Option-Tab → Recents and the other Meta-based shortcuts, enable Terminal → Settings → Profiles → your active profile → Keyboard → Use Option as Meta key. Where separate left/right choices are available, enabling Left Option is sufficient. Then hold that Option key and press Tab at the prompt. This is a manual, per-profile preference; the installer leaves it unchanged. See Option as Meta: one-time setup for the meaning, text-entry tradeoffs, and troubleshooting.
| Option | Behavior |
|---|---|
--symlink |
Recommended. Link the bootstrap to the repository; shared peers stay beside it and update with Git. |
--copy |
Copy the bootstrap and shared peers into a marked ~/.zsh.addons/compozsh namespace. Private peers remain separate. |
--dry-run |
Validate and print the exact plan without changing the filesystem. |
--clean |
Archive the active .zshrc and complete .zsh.addons tree, then install a fresh configuration. Nothing is deleted. |
--yes |
Accept a previously reviewed plan without an interactive prompt; useful for a non-interactive terminal. |
--help |
Show the installation, update, and recovery guide. |
Choose exactly one of --symlink or --copy. When an interactive terminal is
available, omitting both opens a small mode chooser. --copy marks the namespace
it owns and refuses to replace a same-named unmarked directory. Use --clean
only when you intentionally want to archive all private add-ons and begin with
the inert initializer again.
Clean mode affects only the active .zshrc and .zsh.addons. It does not touch
.zshenv, .zprofile, .zlogin, .zlogout, the repository, or exported agent
skills. The completion message prints the exact recovery-backup path whenever a
backup was created.
Open a new terminal, or reload the current shell:
exec zshUsing exec zsh is preferable to repeatedly sourcing .zshrc, because it
starts a clean shell instead of stacking hooks and other state.
With the recommended symlink installation, pull the repository and start a clean shell. The linked bootstrap and adjacent add-ons update together:
git -C "$repo_dir" pull
exec zshFor a copied installation, pull and rerun the installer. It stages the complete new shared namespace before replacing the marked old one:
git -C "$repo_dir" pull
zsh "$repo_dir/install.zsh" --copy
exec zshThe symlink installer is idempotent, so rerunning it is also safe. Keep
repository peers unmodified; put machine setup in local/init.zsh and personal
features in private .zsh.<name> peers instead.
The optional showcase lives in docs/: plain HTML, CSS, and browser JavaScript,
with no build step, framework, remote fonts, analytics, or shell dependency.
It demonstrates Compozsh with synthetic data; it never executes shell commands.
The README remains the authoritative source for complete product instructions.
The terminal groups examples into Context, History, Files, Git, and Tools. Files includes Browse, Recents, scoped Git search and home-index examples. Choose an example, refine its sample results, and select a file to preview its action menu. All outcomes are simulations; sample actions never open applications or access your clipboard. The feature guide below each task links to its complete documentation.
To review it locally from the repository root, use any static server. If Python 3 is available as a development tool:
python3 -m http.server 4173 --bind 127.0.0.1 --directory docsOpen http://127.0.0.1:4173/. No server is installed or started by Compozsh.
See website maintenance and GitHub Pages setup for browser
checks, deployment settings, and the local-review-before-publishing rule.
Yes. Zsh does not include a complete unit-test framework, so this repository
uses a deliberately small harness built from ordinary Zsh functions, arrays,
subshells, and exit statuses. There is no testing library, plug-in, package
manager, or downloaded test runner. A few tests invoke normal system tools such
as mktemp, env, and Git when those tools are part of the behavior being
tested.
Run the complete suite from the repository root:
zsh tests/run.zshThe runner discovers every regular tests/*_test.zsh file and sources it. Each
file defines test functions and registers their human-readable descriptions
with test_case. Every registered test executes in its own subshell, so its
variables, options, directory changes, functions, aliases, and traps cannot
leak into the next test.
Tests that need a shell under test launch another Zsh through env -i with:
- a newly created temporary
HOMEandZDOTDIR; - a minimal fixed environment and locale;
-fso normal startup files are skipped;-dso global startup files are skipped; and-ionly when the behavior requires an interactive shell.
Consequently, the suite does not read or modify the active ~/.zshrc, private
add-ons, or history. Destructive helper coverage creates its own disposable Git
repository. A validated cleanup trap removes only the temporary directory made
for that test.
Interactive regressions use Zsh's bundled zsh/zpty module to send keys and
real resize signals to an isolated terminal. Resize checks cover the painted
display, search and selection state, existing highlights, and cleanup; a
layout calculation alone does not establish that the redraw worked.
The repaint regression also observes Zsh's automatic refresh before the resize
handler, using a separate event pipe so test markers do not disturb the screen.
Screen-lifecycle checks verify paired alternate-screen entry/exit, cleanup on
selection, cancellation, Ctrl-C and failed input, and inline fallbacks. Full
screen clears must occur only inside the owned alternate screen; no test allows
scrollback erasure. Workspace checks cover anchored search/footer rows, visible
match spans, and exact restoration of a multiline draft and shell prompt after
a real resize. They also exercise redraws while the input reader has an empty
field separator, so rendering cannot depend on the reader's temporary state.
Terminal.app fullscreen/windowed transitions still need a manual visual check;
a PTY does not reproduce the application's scrollback reflow.
Unix commands—including Zsh functions—finish with a numeric exit status. Zero means success; any nonzero value means failure. The harness uses that same native contract:
- an assertion returns
0when its comparison is true; - a failed assertion prints its expected and actual values to stderr and
returns
1; - the surrounding test function propagates that failure;
- the runner prints
PASSorFAIL, counts the results, and itself exits nonzero if any test failed.
For example:
PASS mkcd creates and enters exactly one requested directory
FAIL fuzzy history finds unordered fragments and abbreviated commands
18 passed, 1 failed · 2200.0 ms
That final process status makes the same command suitable for local development and future continuous integration. Inspect it manually when useful:
zsh tests/run.zsh
print -r -- "suite status: $?" # 0 passed; nonzero failedTo run only tests whose descriptions contain a case-insensitive fragment:
zsh tests/run.zsh fuzzy
zsh tests/run.zsh git-discard-allA filtered run shortens the inner TDD loop. Always run the complete unfiltered suite before considering the change finished.
Place the test beside the closest concern in an existing *_test.zsh file, or
create a focused new file with that suffix. Use an underscore-prefixed function
for the implementation and register it with a behavior-oriented description:
_test_mkcd_rejects_a_missing_operand() {
test_make_temp_dir || return
local output='' exit_status=0
output=$(test_run_noninteractive "$TEST_TMP_DIR/home" '
source "$1/.zsh.addons/.zsh.tools"
mkcd
' "$TEST_REPO_ROOT" 2>&1) || exit_status=$?
test_assert_equal 2 "$exit_status" \
'mkcd accepted a missing directory' || return
test_assert_contains "$output" 'usage: mkcd <directory>' \
'mkcd omitted its usage diagnostic'
}
test_case 'mkcd rejects a missing directory operand' \
_test_mkcd_rejects_a_missing_operandThe structure is arrange, act, assert: create isolated state, exercise the
observable behavior, then compare the result. Guard setup and intermediate
assertions with || return so a failure cannot be hidden by a later successful
command. Prefer a public command or durable state over private implementation
details; directly test an underscore-prefixed helper only when its algorithm is
the precise contract being protected.
The suite currently protects bootstrap and discovery behavior, initializer precedence, order-independent and standalone add-ons, fuzzy history and directory matching, contextual completion hierarchy and fallbacks, syntax classification, palette extension, public tools, destructive-operation safety, and synchronization between the shipped add-ons and this README.
For every observable feature or bug fix:
- Describe the intended behavior and edge cases.
- Add the smallest test that expresses that behavior without implementing it.
- Run its description filter and see
FAIL. Confirm it fails because the behavior is missing or broken—not because the test has a syntax or setup mistake. This is the red phase. - Implement the smallest correct change and rerun the focused test until it
says
PASS. This is the green phase. - Improve names, boundaries, duplication, or performance without changing the behavior. Keep rerunning the test. This is the refactor phase.
- Run
zsh tests/run.zsh, syntax checks, and the relevant real-terminal checks before finishing.
Do not weaken an assertion merely to make it green. A regression test for a bug must demonstrably fail against the buggy implementation and remain afterward to prevent the bug from returning.
Interactive terminal behavior still needs a real PTY check. Automated child shells can protect the underlying collector, layout calculation, state, and fallback contracts, but they cannot fully prove colors, cursor placement, resize behavior, or physical key ergonomics.
The configuration enables Zsh 5.9's CLOBBER_EMPTY together with
NO_CLOBBER. A normal > redirection may create a file or reuse an existing
empty file, but it refuses to erase a non-empty file accidentally:
print 'replacement' > important.txtWhen replacing the file is intentional, use Zsh's explicit force-redirection operator:
print 'replacement' >| important.txtThe editable command line is highlighted live using Zsh's native lexer and line
editor; no plugin or background process is involved. Resolution follows Zsh's
own rules, so aliases, functions, builtins, external commands, suffix aliases,
global aliases, and AUTO_CD directories remain visually distinct.
Each region carries a Zsh 5.9 memo tag. Redrawing the command line therefore replaces only this configuration's colors and preserves highlights owned by other native ZLE widgets.
| Style | Meaning |
|---|---|
| Bold purple | Regular alias |
| Bold peach | Suffix alias |
| Bold underlined lavender | Global alias |
| Bold sky blue | Shell function |
| Bold turquoise | Zsh builtin such as cd or print |
| Bold green | External command such as git or mkdir |
| Bold underlined azure | Existing directory |
| Bold underlined lime | Executable file used as an argument |
| Underlined pink | Symbolic link |
| Bold underlined violet | Symbolic link to a directory |
| Underlined orange | Regular file with multiple hard links |
| Underlined light gray | Regular file |
| Bold underlined coral | Socket, FIFO, device, or other special file |
| Light gray | Ordinary argument |
| Cyan | Option or flag |
| Bold gold | Reserved word |
| Underlined pale yellow | Structural delimiter |
| Yellow | Conditional operator |
| Bold orange | Arithmetic expression |
| Bold pink | Pipeline operator |
| Bold coral | Boolean operator |
| Bold gray | List or case separator |
| Bold blue | Redirection operator |
| Underlined sand | Redirection target |
| Blue | Assignment |
| Khaki | Quoted string |
| Light blue | Variable expansion |
| Purple | Command or process substitution |
| Salmon | Glob or brace expansion |
| Bold pink-red | History expansion |
| Bright cyan | Escape sequence |
| Peach | Number |
| Gray | Comment |
| Dim gray | Unaccepted history autosuggestion |
The highlighter understands command positions, precommand modifiers, leading
assignments, declaration assignments, pipelines, boolean lists, redirections,
grouping, conditionals, arithmetic commands, loops, case blocks, function
declarations, and Zsh's contextual always keyword. Literal path arguments are
checked without evaluating substitutions or globs. Directory-stack paths such
as ~1 are resolved using Zsh's current stack.
Every shell category has a unique color-and-attribute signature. Related concepts stay within recognizable color families, while prompt identity, location, Git state, and project metadata use a separate public palette. An unresolved command deliberately keeps the terminal's normal text color; it does not turn red merely because the word is still being typed. Git subcommands and other program-specific arguments remain arguments because only the invoked program can interpret their meaning.
A hard link has no unique shell syntax: every linked name is an ordinary file. The orange hard-link style therefore means the filesystem reports a regular file with a link count greater than one. Symlinks are separate filesystem objects and can be identified directly.
Command-line syntax and prompt UI use separate public maps, so changing an
identity or path color cannot silently change a syntax category. Set only the
desired role in ~/.zsh.addons/local/init.zsh. For example:
typeset -gA ZSH_HIGHLIGHT_STYLES ZSH_PROMPT_COLORS
ZSH_HIGHLIGHT_STYLES[command]='fg=118,bold'
ZSH_HIGHLIGHT_STYLES[comment]='fg=243'
ZSH_PROMPT_COLORS[identity]=110
ZSH_PROMPT_COLORS[path]=117The editor uses macOS-friendly Emacs navigation, so it does not depend on the Home, End, or forward-Delete keys missing from compact Apple keyboards:
| Key | Action |
|---|---|
Ctrl-A |
Move to the beginning of the line |
Ctrl-E |
Move to the end of the line |
Ctrl-B / Ctrl-F |
Move backward / forward one character |
Option-B / Option-F |
Move backward / forward one word |
Option-Left / Option-Right |
Move backward / forward one word |
Ctrl-D |
Delete under the cursor; on an empty command line, send EOF (normally exits the shell) |
Option-Backspace / Ctrl-W |
Delete the previous word |
Option-D |
Delete the next word |
Ctrl-U |
Delete the editable line |
Ctrl-K |
Delete from the cursor to the end |
Ctrl-Y |
Restore the most recently deleted text |
Ctrl-_ |
Undo the last edit |
Up / Down or Ctrl-P / Ctrl-N |
Search history using the typed prefix |
Tab |
Open the directory picker for a lone AUTO_CD path; otherwise complete natively |
Option-Tab |
Open Recents directly; selection inserts an editable path, cancellation preserves the draft/cursor (requires Option as Meta) |
Shift-Tab |
Complete backward natively; switch panes in pickers, or go Back in the folder browser |
Ctrl-R |
Open fuzzy history search |
Ctrl-L |
Redraw a clean terminal screen |
Ctrl-X Ctrl-E |
Edit the command in $EDITOR |
Home, End, and Fn/Globe-based key sequences remain supported as optional aliases
when the terminal sends them.
Cmd-C, Cmd-V, Cmd-K, and other Command-key shortcuts remain owned by the
terminal application and macOS. They are intentionally not shadowed by Zsh;
the shell generally never receives those key combinations.
Ctrl-Tab / Ctrl-Shift-Tab also stay with Terminal's next/previous tab actions,
as listed in Apple's Terminal shortcuts.
Meta is a modifier role, like Control or Shift, used by terminal programs
to distinguish extra shortcuts. Your keyboard does not need a key labeled
Meta: Terminal can assign that role to Option (⌥). Notation such as M-b
means “hold Meta and press b.” Terminal-style Meta shortcuts commonly use an
Escape-prefixed character sequence; see the GNU keyboard notation guide.
Enable the setting for each Terminal profile you use:
- Open Terminal → Settings → Profiles and select the profile used by your current window or tab.
- Open Keyboard and enable Use Option as Meta key.
- If your Terminal version offers separate left/right choices, Left Option alone is enough. Right Option can retain normal character entry. If the setting is a single checkbox, it may apply to both keys; follow that UI.
- At the ordinary prompt, hold the enabled Option key and press Tab. Recent directories should open. Press Escape to return to your draft.
This is a per-profile preference, not a system-wide keyboard remapping. Compozsh never changes it automatically. Apple's Keyboard settings guide documents the preference and its profile scope.
With Meta enabled, Option-Tab sends the ESC TAB sequence that Compozsh binds
to Recents. You press Option-Tab together; the bytes are transport details.
Inside a Compozsh full-screen tool, Option-Up/Down similarly pages the
focused pane. It complements the always-supported Fn-Up/Down page gesture.
Terminal.app versions and profiles may encode that gesture as either an xterm
modified-arrow sequence or a Meta prefix followed by an ordinary arrow;
Compozsh accepts both forms without exposing their bytes as filter text.
Standalone Escape still cancels a picker. Enabling Meta does not assign an
action to every Option combination; the receiving shell or application decides
which combinations it supports.
Text-entry tradeoff: a Meta-enabled Option key can stop producing the alternate symbols or accents your keyboard layout normally assigns to it. Keeping the other Option key in its normal mode, where supported, preserves that access. This also affects other terminal programs using the same profile; ordinary Command shortcuts remain with macOS and Terminal.
No shell reload is needed for the Terminal preference itself. Use exec zsh
after installing or updating Compozsh's code so its bindings are loaded. That
starts a fresh shell and resets its in-memory directory stack; Recents initially
contains the current folder and grows as you navigate.
If Option-Tab still completes like plain Tab, check the active profile, the Option key you enabled, and custom keyboard mappings. Zsh cannot distinguish gestures delivered as identical bytes. You can still reach Recents through path + Tab → Ctrl-X → Go to · Recent directories without Meta enabled. The rest of Compozsh, including path + Tab and Control-based picker commands, works without this preference.
Keep an AI CLI chat running in one tab and use another for navigation, Git review, tests, and commands. Each tab fills the same Terminal window; switching tabs leaves the other session running. This workflow uses Terminal.app's built-in controls and needs no additional installation, Compozsh configuration, or automation permission.
- Start your usual AI CLI in the first tab.
- Press Cmd-T to open a second tab for your working shell.
- Press Ctrl-Tab to switch between them. With two tabs, it toggles directly between chat and shell, including while the AI CLI or a Compozsh view is open.
- Optionally press Ctrl-Cmd-F to enter or leave full screen.
| Key | Action and owner |
|---|---|
Cmd-T |
Terminal: open a new tab |
Ctrl-Tab / Ctrl-Shift-Tab |
Terminal: select the next / previous tab |
Ctrl-Cmd-F |
Terminal: enter / leave full screen |
Option-Tab at the prompt |
Compozsh: open recent directories; requires Option as Meta |
Ctrl-R at the prompt |
Compozsh: search command history |
Terminal owns tab switching, so these controls work while another program owns the shell's input. See Apple's Terminal shortcuts.
For optional refinements, open Terminal → Settings → General:
- Choose New tabs open with → Same Working Directory to start a new tab in the active tab's folder. Later directory changes remain independent.
- Enable Use ⌘-1 through ⌘-9 to switch tabs for direct access to numbered tabs, for example chat, shell, and tests. These are Terminal preferences; Compozsh does not change them. See Apple's General settings guide.
Tabs have separate shell sessions, directory stacks, and scrollback. Compozsh still shares command history, and tabs working in the same repository share its files, index, and checked-out branch: coordinate edits and branch switches with any running AI or build. Switching tabs preserves running programs; closing a tab or quitting Terminal can terminate them. Tabs provide no detached-session or restart-persistence guarantee.
The filesystem workspace has three named views: Browse for a folder's
children, Search for explicit discovery below that folder, and Recents
for this shell's visited locations. Use Ctrl-X for its actions/view menu.
A direct Option-Tab shortcut opens Recents from the ordinary prompt (with
Option as Meta enabled).
g stays separate because its scope and actions are Git-specific.
The two entry points serve the same navigation task with different starting knowledge:
| Starting point | Gesture | Source and primary result |
|---|---|---|
| A known folder (path-led) | ~/, ./, or another directory path + Tab |
Browse that folder; Enter inserts an editable path |
| A remembered location (recall-led) | Option-Tab | Search this shell's directory stack; Enter inserts an editable path |
Both resolve an exact location and can continue in the same browser. From Recents, Ctrl-O browses the selected folder; that folder can then scope a Search. Recents never broadens into a whole-disk search. The visible Enter label always names the next action, and Escape returns without applying it.
Both entry points finish at the ordinary prompt with the selected, safely quoted
path visible and the cursor at its end. Selection replaces the existing command
draft; it does not change directory yet. Review or edit the path, then press
Enter at the prompt to change directory through Zsh's AUTO_CD. Cancelling
or copying instead keeps your original command, cursor and directory unchanged.
When the editable line contains only an AUTO_CD directory path, Tab opens
the same searchable picker used elsewhere in Compozsh:
❯ ~/Projects/
Compozsh / Directory browser Enter: insert · Results
Hidden: off · child directories · 3 files · 2 shown
~ › Projects
Filter folders ‹›
[1] ● example-app/
[2] experiments/
⏎ insert · Esc cancel · ^K keys · ^Y copy · ↑↓ move
This condensed example shows the navigator's title, breadcrumb and search. On a wide terminal, a quiet Location trail sits to the left of the main folder list, with a secondary Preview pane on the right. The trail appears from 120 columns; below that, the list and preview take priority. Below 100 columns the list uses the full width and previews become a focused view. Shortcuts stay near the bottom; very short windows omit the breadcrumb.
A folder with no child directories can still contain files. The browser shows its captured file count in the header. With no matching directories, the main body distinguishes no child directories, no visible entries, and no directories match this filter. It shows counts and up to eight non-directory names from the same level capture, with a remaining-count notice. These are informational: they have no selection numbers and cannot be inserted or executed by Enter. Ctrl-F opens scoped Search and its file actions. Hidden names follow the visibility setting; an empty visible scope does not mean the folder has no hidden entries. Counts and samples refresh on entering/re-entering a level or changing visibility; typing and resizing use the captured facts.
| Browser key | What it does |
|---|---|
| Right / Tab | Enter the selected folder |
| Left / Shift-Tab | Return to the preceding level, restoring selection and filter |
| Ctrl-O | Preview the selected folder; with no selection, preview the current folder |
| Ctrl-F | Search descendants of the displayed folder; Return submits the query |
| Ctrl-E / Ctrl-B | Focus the preview / return to the folder list |
| Ctrl-X | Open folder actions, including use current folder |
| Ctrl-T | Toggle hidden folders |
| Ctrl-Y | Copy the absolute path and close, if pbcopy is available |
| Ctrl-K | Open the shared keyboard guide |
Hold Control while pressing the letter; no sequential Escape chords or Meta setting are required. Escape cancels with a short terminal-sequence decoding allowance; Ctrl-G cancels immediately. Ordinary letters and punctuation remain search input. These shortcuts apply only while the picker is open.
Preview reads only names and entry types from one explicitly requested
folder. ▸ marks a directory, · another entry, and ↗ a symbolic link.
It never reads file contents or traverses child symbolic links. Explicitly
previewing a selected link to a folder follows that requested target.
The sample stops at 40 entries in filesystem order, with a visible limit notice
when more exist. Hidden names follow the browser toggle. Moving selection,
filtering or resizing never loads a preview; press Ctrl-O again to refresh.
Only the most recently requested preview is retained, until you change levels
or close. A requested preview uses the available reading height; Ctrl-E and
page keys let you read longer snapshots. Narrow windows focus it immediately.
With no directory selected, the current-folder preview opens in the full main
body at any width. Ctrl-B returns to the informational summary; Ctrl-E returns
to that captured preview without scanning again.
Ctrl-X options offers insertion, explicit directory changes, copying, opening with the default app, Finder reveal, scoped Search, and separate Recents in one searchable menu:
| Group | Meaning |
|---|---|
| Selected folder | Actions on the highlighted folder, including insertion, directory change, copying, opening, and Finder reveal when available |
| Selected file / Selected link | Search-result actions on the exact highlighted item: insertion, copying, opening, and Finder reveal; directory links offer an explicit Enter linked directory action |
| Current folder | Actions on the opened folder, including insertion, directory change, Browse, and scoped Search; copying/opening/reveal target it when no item is selected |
| Go to | Independent navigation to Recent directories, sourced from this shell's native stack |
Rows stay together by group, and every row retains its group label when filtered or paged. Details show the exact path an action uses. With an empty filter, digits apply visible action rows directly; no extra group-selection step is needed. Unavailable capabilities are omitted. Search always uses the current folder. Escape closes the menu back to the same filter and selection. Apps launch only after you select an action and the picker screen is restored. Open with default app opens a folder in its registered app (normally Finder), or a file in its associated app. Reveal in Finder opens the containing folder and selects the exact item; it does not open that item.
The fragment already typed stays as a required fuzzy match, while the picker
query starts empty. A visible digit therefore inserts its row immediately; you
can instead type another fuzzy filter or move with Up/Down and
Ctrl-P/Ctrl-N. Press Tab or Right Arrow to open the selected directory
inside the same picker. Shift-Tab or Left Arrow returns to the previous
level reached during that picker session. Back restores that level's filter,
selected directory, and scroll position. The directory is identified by its
path, so a newly added sibling cannot silently change the selection. If the
selected path has disappeared, selection starts at the first remaining match.
Back at the starting level keeps the current view and explains the boundary.
A visible number or Enter inserts the selected full path and appends /; it
does not change directory or execute anything. Press Enter once the picker
closes to let Zsh's native AUTO_CD enter that path. Empty folders are usable
levels: Ctrl-X → current folder selects them, or Left goes Back.
Unreadable or removed children preserve the last usable level with a notice.
Press Ctrl-T to toggle hidden directories.
A plain . still filters normally. Hidden directories start off unless the
typed name begins with ., and the chosen visibility follows you through this
navigator session. Toggling preserves the filter and selected path where it
remains visible. An empty result view can be recovered by toggling again or
clearing the filter; the original typed path fragment remains a required match.
Ctrl-Y or Option-W (with Meta enabled) copies the selected absolute
path, without shell escaping or a trailing newline, and closes the navigator.
Your original editable command is preserved. Copy appears when pbcopy is
available; SSH sessions use the clipboard of the machine running Zsh. A copy
failure is reported without replacing the command line.
Only immediate child directories are collected. Symbolic links to directories
are included, and spaces or shell-special characters are escaped before
insertion or drill-down. Drill-down, Back, and hidden-folder toggles capture only
the requested level; a failed transition may recapture the previous level to
recover. Filtering, selection movement, and resizing use the captured children.
Browsing launches no subprocess and performs no
recursive scan or network access. Folder enumeration can still wait on a slow
or unavailable mounted filesystem. Explicit copying invokes pbcopy, and
explicit Finder reveal invokes open -R, after the picker screen is restored.
Captured children and navigation bookmarks are
released on exit, with no persistent index or saved navigation history.
The contextual behavior is deliberately narrow. Commands, options, multiple
words such as git switch, file arguments, a cursor in the middle of the line,
missing or unreadable parents, and an exact command that shares a directory
name all delegate to Zsh's original expand-or-complete widget. Outside the
picker, Shift-Tab continues to use native reverse completion.
At most ten rows are shown so every visible index remains a single direct key. Arrows scroll through all matching children at the current level, and page keys jump through the list. Right/Tab still opens a child; Left/Shift-Tab returns to the parent. Scrolling does not traverse other directory levels. Change the smaller bound from the local initializer when desired:
ZSH_DIRECTORY_PICKER_MAX_RESULTS=8Open a folder with path + Tab, then press Ctrl-F. In Search descendants, enter the fragments you remember and press Return to capture results using the source shown above the query. Escape returns to the same browsing filter, selection, viewport and preview focus. The scope is the displayed current folder, never the highlighted child:
| Starting path | Scope |
|---|---|
./ + Tab |
Current directory and descendants |
~/ + Tab |
Home and descendants |
/ + Tab |
Filesystem root; defaults to Spotlight for indexed Mac-wide discovery on macOS |
~/Projects/ + Tab |
That folder and descendants |
Opening any path only lists its immediate children. Filter folders narrows that captured level. Search descendants is a separate, explicit discovery step: typing, paging, moving selection, and resizing never start a deeper scan. Inside a Git repository, a subfolder remains the scope; it is not silently expanded to the repository root. Ctrl-F selects a default once on entry:
| Displayed folder | Default source |
|---|---|
| Inside a Git worktree | Git, scoped to that folder (takes precedence) |
Exactly home (~/) or root (/) on macOS |
Spotlight index |
| Other folders, or home/root on other systems | Bounded filesystem walk |
Ctrl-X offers explicit source choices. The resolved source stays fixed when editing or retrying the query. A missing or failed Spotlight command never triggers a fallback filesystem walk. To search a home subfolder through Spotlight, choose Search Spotlight index in Ctrl-X.
| Search source in Ctrl-X | Coverage |
|---|---|
| Search filesystem | Bounded native breadth-first walk, including hidden entries; skips .git, unreadable folders, and traversal through directory symlinks |
| Search Git files | Tracked and non-ignored untracked files below the current folder, plus matching containing directories; empty folders may be absent |
| Search Spotlight index | macOS indexed paths below the current folder; seeds filenames with the longest query fragment |
Git and Spotlight entries appear when their commands are available. Git must also recognize the chosen folder as part of a working tree. An unavailable source or successful search with no matches stays inside the workspace with a distinct explanation. If a source fails after returning some matches, those remain usable with a failure and partial warning. Spotlight can miss excluded, unavailable, or unindexed paths, and abbreviated or directory-only seeds may miss even indexed files. Root scope is not an exhaustive search-until-found guarantee. Mounted filesystems can still block.
Searching… is painted with the scope and query before capture starts. Capture is synchronous: blocked source reads can delay keyboard input and resize handling until they return. A first search can take longer while the OS loads index data or checks access; Compozsh cannot bypass macOS privacy controls. The waiting screen is a status, not a progress estimate. Results then use the current window size; filtering, paging, and repainting do not recapture.
Matching is case-insensitive and operates on paths, not file contents.
Space-separated fragments may appear in any order; each fragment may also
abbreviate characters in order. For example, net cli can match
Sources/Network/Client.swift. Query text is literal; shell quoting is not
needed inside the workspace. Empty/whitespace-only search submissions do nothing.
After capture, Filter results refines only those captured paths. Ctrl-F reopens the submitted query: edit it and press Return to capture again using the same folder and source, or Escape to keep the existing results and position. Use Ctrl-X to choose another source. The header preserves the source, scope, original query, candidate count, and any partial warning.
Results show filenames first and quiet parent paths. ● marks selection, ·
marks a file, ▸ a directory, and ↗ a symbolic link. Full paths remain intact
for actions even when labels are abbreviated.
- Enter or a visible digit inserts a directory path into the editable line.
- For files and links, Enter → choose an action is the primary workflow: open the shared File actions menu, then choose Open, Reveal, Copy or Insert. No additional shortcut is needed for these actions.
- Ctrl-X options is optional: it also exposes source choices and folder operations. It opens grouped actions for the exact selected file, folder, or link; folder results also offer Open with default app and Reveal in Finder here.
- Ctrl-Y copies the literal absolute path and closes when
pbcopyis available. - Escape from file actions restores the same query, selection, and scroll position without recapturing. Ctrl-C aborts the entire workspace.
| File action | Effect |
|---|---|
| Open with default app | Explicitly use macOS associations; this can launch an application, so open only trusted files |
| Reveal in Finder | Open the containing folder and select the exact item using open -R |
| Copy path | Copy its absolute path without a trailing newline |
| Insert path into command line | Return a shell-quoted path to the editable prompt; review before running it |
| Enter linked directory | Explicitly follow a directory symlink and change directory |
Opening/revealing requires macOS open; copying requires pbcopy. Missing
actions are omitted, and broken links have no Open action. Actions operate on
the host running Zsh, after screen cleanup, and recheck mutable filesystem facts.
Selection never executes a file. Content previews are not provided.
The shared Path inspector displays captured type and full path; Ctrl-E/B or Tab switches focus. It is secondary and width-aware, with a 48-column cap. Resize and detail scrolling do not rediscover paths.
Direct walks inspect at most 20,000 entries; every provider retains at most 2,000 matches. Partial results are labeled. Up to ten rows are visible at once; arrows and the shared page keys reach later captured matches. Visible number slots are local to the viewport; numbers become query text after typing.
ZSH_FILE_SEARCH_MAX_RESULTS=10
ZSH_FILE_SEARCH_MAX_VISITED=20000
ZSH_FILE_SEARCH_MAX_CANDIDATES=2000This is a breaking interface change in 2.0.0. The shared configuration and private add-on layout stay the same; the filesystem entry points change:
| Previous interface | Current workflow |
|---|---|
d |
Option-Tab at the ordinary prompt |
d --list |
Native dirs -v |
f query |
./ + Tab → Ctrl-F → enter the query and submit |
f --home query |
~/ + Tab → Ctrl-F → enter the query and submit |
f --root path query |
Open that path with Tab → Ctrl-F → enter the query and submit |
Explicit f search-source flags |
Ctrl-X options in the workspace; choose the desired source |
f --list / f --print0 |
Native find, git ls-files, or mdfind for scripts |
Selecting a directory now inserts its quoted path for review; submit it at the normal prompt to change directory. Escape is cancellation; use Control keys for in-view actions and Ctrl-K to check the current map. Ctrl-F starts/edits scoped search; Ctrl-E focuses details. Update private aliases or scripts that called the retired commands; the installer does not rewrite personal files.
d and f have been removed. Recents is available through Option-Tab
(Option as Meta) at the prompt and Ctrl-X → Go to · Recent directories inside the path
workspace. Search scopes come from the path you open. g remains the separate
Git workspace.
The former f --list / f --print0 scripting API is also removed. Use native
find, git ls-files, or mdfind with their documented quoting and NUL-output
options for scripting. dirs -v prints native stack indexes. Run exec zsh
after updating to clear functions and bindings held by the old shell.
compozsh --help includes the workspace reference; Ctrl-K shows the
contextual keyboard guide inside every view.
As text is entered at the end of the command line, the unused suffix of the newest matching history entry appears in dim gray:
❯ git sw│itch feature/native-ghost
└─ unaccepted suggestion
Suggestions use a case-sensitive literal prefix, which makes them stable and
predictable while typing. They are display-only: pressing Enter executes
exactly the editable text before the ghost suffix. Accept one explicitly with:
| Key | Action |
|---|---|
Right Arrow or Ctrl-F |
Accept one character |
Option-F or Option-Right |
Accept through the next word |
Ctrl-E |
Accept the complete suggestion when already at line end |
The wrapped keys retain their normal behavior when a suggestion is unavailable
or the cursor is not at the end of the line. Thus, the first Ctrl-E moves a
mid-line cursor to the end and a second accepts the visible suffix. End or
Fn/Globe-Right also accepts the complete suggestion when its escape sequence
is available. Accepted text participates in Zsh's normal undo history, so
Ctrl-_ can undo an acceptance.
The redraw hook searches Zsh's live in-memory history and caches the current
match while its prefix grows. It launches no subprocess and writes no index.
It deliberately stays hidden for an empty or private leading-space command,
an active selection or completion suffix, a moved cursor, recursive editing,
multiline input, and any history entry containing terminal control bytes. The
Ctrl-R picker temporarily owns the display, then restores suggestions when it
closes.
Both behavior and appearance can be changed from the local initializer:
typeset -gA ZSH_HIGHLIGHT_STYLES
ZSH_AUTOSUGGEST_ENABLED=0
ZSH_HIGHLIGHT_STYLES[autosuggestion]='fg=241'Press Ctrl-R to open an in-memory history picker. Start typing any characters
you remember. A single fragment may be abbreviated in character order, so
gtsw can find git switch feature/example. Separate remembered fragments
with spaces and they may appear anywhere in the command: -c swift finds
swift build -c release even though the command stores them in the opposite
order. Search is case-insensitive and ranks results in predictable tiers:
- Commands beginning with the literal query
- Commands containing the literal query
- Commands containing every literal fragment, in any order
- If no literal-fragment result exists, character-ordered fuzzy matches for every fragment; each fragment stays within one command word to avoid noisy cross-word matches
Each tier is newest-first, and duplicate command lines are shown only once.
If the editable command line already contains text, that text becomes the
initial query. Press Ctrl-U inside the picker to clear it and browse recent
commands instead.
History, file, directory, and branch selectors share the same safe renderer. A stable
dedicated Search ‹query› row keeps user input separate from header metadata
and prevents the layout from shifting after the first character. Long queries
use the available row width and abbreviate visually while preserving the full
value for matching. Query fields, headers, indexes, selected rows, empty states,
and help rows use separate semantic colors that can be customized from
~/.zsh.addons/local/init.zsh without changing the picker logic:
typeset -gA ZSH_HIGHLIGHT_STYLES
ZSH_HIGHLIGHT_STYLES[picker-header]='fg=75,bold'
ZSH_HIGHLIGHT_STYLES[picker-location]='fg=39'
ZSH_HIGHLIGHT_STYLES[picker-query]='fg=16,bg=44,bold'
ZSH_HIGHLIGHT_STYLES[picker-index]='fg=44,bold'
ZSH_HIGHLIGHT_STYLES[picker-selected]='fg=16,bg=75,bold'
ZSH_HIGHLIGHT_STYLES[picker-selected-inactive]='fg=252,bg=238'
ZSH_HIGHLIGHT_STYLES[picker-focus]='fg=75,bold'
ZSH_HIGHLIGHT_STYLES[picker-match]='fg=81,bold,underline'
ZSH_HIGHLIGHT_STYLES[picker-text]='fg=252'
ZSH_HIGHLIGHT_STYLES[picker-muted]='fg=242'
ZSH_HIGHLIGHT_STYLES[picker-empty]='fg=203,bold'Existing history-search-* overrides remain accepted and are migrated to the
corresponding shared picker role when the shared palette initializes.
| Key | Action |
|---|---|
| Type | Refine the fuzzy query |
Backspace |
Remove the last query character |
Option-Backspace or Ctrl-W |
Remove the last query word |
Ctrl-U |
Clear the query |
Ctrl-L |
Redraw a clean search screen |
Down, Ctrl-N, Tab, or Ctrl-R |
Select the next result |
Up, Ctrl-P, or Shift-Tab |
Select the previous result |
Fn-Down / Fn-Up |
Page down / up on Apple keyboards |
Option-Down / Option-Up |
Page down / up with Option-as-Meta enabled |
Ctrl-V / Ctrl-D |
Page down / up without requiring Option-as-Meta |
Enter |
Put the selected command on the editable line |
Esc or Ctrl-G |
Cancel and restore the original line |
Ctrl-C |
Hard-abort the search and current editable line |
Selection never executes a command. It returns the full original history entry,
including multiline commands, to the normal editor so it can be reviewed or
changed before pressing Enter again. Display rows make control characters
visible, truncate to the current terminal width, and reduce automatically in a
short terminal window.
All pickers use the same scrolling viewport: Ctrl-R history, Recents,
g branches, Files search, the compozsh tool explorer, and contextual directory
completion with Tab. Keep moving beyond the last visible row to reveal later
matches; move up to return. The header shows the visible range
and more ↓ while additional matches may remain, then the exact total once
matching is exhausted. Movement stops at the beginning and end. Changing the
query resets to its first match. Page keys control whichever pane has focus,
including the Help, Branch, and Path detail panels, in wide and narrow layouts.
Focused readers derive their page distance from the currently visible pane
height and retain one overlapping line as a reading landmark. Resizing Terminal
or changing its font therefore changes the next page distance automatically.
Number shortcuts refer to the rows currently displayed and update as you
scroll; they never select hidden results. Ctrl-R keeps digits as search text.
Matching grows a buffered prefix only when needed. Ordinary navigation and resize redraw the visible rows; closing the picker releases its result buffer.
In Terminal.app, each picker opens on the terminal's native alternate screen,
the same separate display buffer used by tools such as less. Your previous
terminal output is temporarily hidden and restored when the picker closes.
This applies to history, directories, branches, files and their action menus,
the tool explorer, and contextual directory completion.
The directory browser keeps that screen open throughout navigation. Entering folders, going back, toggling hidden items, requesting a preview and returning from folder actions update the same workspace. Your shell reappears when the browsing session ends; copying, changing directory and opening Finder happen after its screen has been restored.
A dedicated title bar identifies the tool: Compozsh / Directory browser,
Compozsh / Branches, Compozsh / History, and so on. When space allows,
the right side shows the selected item's Enter action and the focused view,
such as Enter: switch · Results or Enter: insert · Preview. It shows
Keyboard guide while the guide is open, and avoids advertising a selection
action when nothing is selected.
A quieter status row shows the captured source and result count, followed by the separate location/source row, dedicated search and a quiet divider. The title uses the existing first screen row, preserving result capacity. Narrow windows hide optional title metadata, then the Compozsh prefix, before abbreviating the tool name. The context row is omitted in very short windows. Results and details occupy the body, with shortcuts anchored near the bottom. Filtering down to one result keeps these landmarks in place. Your unfinished command and prompt are hidden while browsing, then restored when you leave; each tool keeps its existing accept and cancel behavior.
Every tool uses the same shortcut bar. Enter, Escape, and Ctrl-K for
keys have priority; other complete hints appear as space allows. The bar
names the actual action (cd, switch, insert, or file actions), and only
advertises copying or details when supported. It never cuts a shortcut in half.
Press Ctrl-K to open the keyboard guide. Arrow keys scroll line by line;
Fn/Option-Up/Down or Ctrl-V/Ctrl-D scroll by a page. Ctrl-K, Escape, Ctrl-G or Enter closes the guide and restores
the exact search, selection, scroll position and pane focus. Ctrl-C aborts the
whole picker. Typing or pressing a number in the guide cannot apply a result.
A plain ? remains searchable. Primary shortcuts use Control and require no
Terminal profile changes. Escape has a 20 ms allowance to distinguish a lone
key from terminal sequences such as arrows and bracketed paste; it no longer
waits half a second for a following letter. Ctrl-G has no decoding delay.
Option-Up/Down, Option-V, Option-W and Option-Backspace remain optional Meta
alternatives for paging, page-up, copy and word deletion. Fn-Up/Down also page
when sent by the terminal.
There is no need to type Escape followed by a letter for any picker action.
These are modal picker controls: for example, Ctrl-K shows keys and Ctrl-D pages up here. After closing, normal shell editing is unchanged: Ctrl-K deletes to the end of the line and Ctrl-D retains Zsh's delete/EOF behavior. Command-key shortcuts continue to belong to Terminal.app.
| Shared key | Behavior |
|---|---|
| Up/Down or Ctrl-P/N | Move results; scroll when details have focus |
| Enter | Apply the action named in the footer |
| Escape / Ctrl-G | Cancel, or go back from a secondary view |
| Ctrl-C | Abort |
| Ctrl-U / Ctrl-W | Clear the filter / delete its last word |
| Fn-Up/Down or Option-Up/Down | Page up/down in the focused view; Option requires Meta |
| Ctrl-V / Ctrl-D | Page down / up without requiring Option-as-Meta |
| Tab / Shift-Tab | Switch list/details focus when a panel exists |
| Ctrl-Y / Option-W | Copy the selected value and close, when available |
| Ctrl-K | Open or close the keyboard guide |
| Ctrl-L | Redraw |
| Ctrl-F | Search descendants in Browse; edit discovery query in Search results |
| Ctrl-E / Ctrl-B | Focus details / list, when available |
| Ctrl-O | Browse a selected recent location; inside the browser, preview a folder |
| Ctrl-X | Open review on Branches; options for filesystem actions/sources/views |
| Right / Left in Git review | Progress files → focused diff → full-file context / reverse those steps |
| Ctrl-R in Git review | Refresh the selected snapshot, preserving focus and source area |
| Ctrl-T | Toggle hidden folders in the browser |
This is the shared Compozsh picker key map. The footer and Ctrl-K guide show available actions. The documented Git-reader controls apply only within review: Ctrl-R still opens history at the prompt and advances results in other pickers. Ctrl-X is inactive in the reader; the arrow flow handles context disclosure.
The directory browser has one explicit hierarchy convention: Right/Tab enters a folder and Left/Shift-Tab goes Back; Ctrl-E/B focuses preview/list. Ctrl-F is available only in Browse and Search results; other pickers leave it inert. These modal controls do not change ordinary prompt editing or suggestions. Its guide shows previews, folder actions and hidden-folder controls. History selection always inserts an editable command; it never runs it. Other tools retain their documented primary actions.
This design applies recognition cues, consistency and progressive disclosure. These are usability principles informing the layout, not proof that a particular terminal interface is neurologically optimal. Real keyboard tests and user feedback remain the acceptance criteria.
Typed filter fragments receive visible emphasis in result rows, including ordered-character abbreviations. The selected row keeps its selection color and underlines those fragments. This is display-only: matching, ranking, full paths, and action values are unchanged. Only complete fragments visible in the rendered label can be emphasized; queries over 256 characters or 16 fragments still search normally but skip this extra decoration.
The result list retains its existing row limits and number shortcuts: eight rows for history and ten for navigation by default, reduced in short windows. Information panels stay secondary and compact. Focusing a panel gives its captured text the available body height, making long help easier to read; returning to the list restores the compact preview. Capture limits are unchanged.
Window and font-size changes repaint the temporary screen from the captured results,
preserving the query and selection. Selection, cancellation, and Ctrl-C release
that screen before any subsequent navigation, clipboard, file, or help action.
The normal screen and scrollback are never cleared to remove picker frames.
Terminals without paired alternate-screen capabilities retain the inline picker.
compozsh --list retains its plain-output mode. See the filesystem migration
section for the removal of d and f.
The picker captures Zsh's loaded history when opened and uses its native
pattern engine, with no subprocess, extra history file, or disk index. It
can search up to 50,000 entries retained across shell restarts and shows at most
eight rows at a time by default. Matching considers the full loaded history,
and scrolling can reach later matches; the result limit only bounds the picker
display. Override that bounded value from
the local initializer if desired:
ZSH_HISTORY_SEARCH_MAX_RESULTS=12Running man uses the native formatting already embedded in each manual page
and renders it through less with the prompt palette:
man git-switch
man zshoptionsHeadings and bold terms are cyan, underlined references are yellow, and
standout or search matches use a cyan background. The colors are scoped to the
man function, so opening an ordinary file with less is unaffected.
The function respects an existing MANPAGER, MANCOLOR, or
LESS_TERMCAP_* value. Set one in the local initializer to replace an
individual default without editing the shared configuration.
Interactive output uses color only where the producing tool exposes reliable semantics. Compozsh supplies one semantic palette to those native engines; it never parses and repaints arbitrary command text. Pipes, substitutions, and redirected data therefore remain byte-compatible.
| Output family | Behavior |
|---|---|
ls, ll, and la |
Native file-type colors for directories, links, executables, sockets, pipes, devices, privileged files, writable directories, and dataless files |
| File completion | The same file-type families rendered with the 256-color syntax palette |
grep |
Matching text uses the shared match color when stdout is a terminal |
| Git porcelain | Native semantic colors for status, diffs, logs, branches, interactive prompts, advice, and remote success/warning/error messages used by commands such as clone, fetch, pull, and push |
curl |
curl's own styled HTTP headers are preserved on terminals; response bodies are deliberately untouched |
| Compilers, runtimes, and TUIs | Their own native color and terminal behavior is preserved |
| Man pages | Scoped heading, reference, selection, and search-match colors through less |
| Compozsh help | Shared heading, option, example, and safety emphasis for known help text on 256-color terminals; exact plain output elsewhere |
| JSON, CSV, logs, arbitrary tables, and binary data | Left unchanged because their meaning cannot be inferred safely from plain bytes |
Long ls metadata such as permissions, owners, sizes, and timestamps remains
neutral; filenames carry the file-type color. Parsing the human-readable column
layout would break on spaces, locales, ACL markers, and unusual filenames.
Likewise, a curl body may be JSON, HTML, source text, an image, or compressed
data. Only curl can safely decide how to render its protocol metadata.
The Git wrapper adds temporary -c color.* values only for known human-facing
subcommands when stdout or stderr is a terminal. Git still performs the
terminal detection, explicit options come last, and configuration inspection
or plumbing commands receive the original argv. This explicitly disables color
for one invocation:
git -c color.ui=never logThe automatic behavior is terminal-aware. These stay plain and safe:
ls -la > files.txt
git diff > change.patch
matches=$(grep TODO README.md)
grep TODO README.md | wc -l
curl https://example.invalid/data > response.binTo intentionally preserve grep colors through a pager, request it explicitly:
grep --color=always TODO README.md | less -RThe public ZSH_OUTPUT_COLORS map keeps Git, grep, manual-page, and help emphasis
consistent. Override only desired semantic roles in the local initializer
before peers load:
typeset -gA ZSH_OUTPUT_COLORS
ZSH_OUTPUT_COLORS[success]=118
ZSH_OUTPUT_COLORS[warning]=221
ZSH_OUTPUT_COLORS[error]=203
ZSH_OUTPUT_COLORS[match]=199Available roles are heading, accent, success, warning, error,
info, muted, match, and text; values are terminal color indexes
from 0 through 255. Invalid local values fall back safely. Existing
GREP_COLOR, GREP_COLORS, and LESS_TERMCAP_* values retain precedence.
Set LSCOLORS separately for BSD ls, or unset CLICOLOR in the local
initializer to disable automatic file-listing colors on a particular machine.
The bootstrap optionally loads ~/.zsh.addons/local/init.zsh before every peer
add-on. Its one responsibility is establishing pre-peer inputs and
prerequisites: Homebrew or language-manager setup, command and completion paths,
selected toolchain versions, trusted hooks that require early loading,
environment variables, and documented public defaults.
The installer creates the private file from the inert tracked starter only when it does not already exist. To recreate it manually without running a complete installation, use the active Zsh configuration base:
config_base=${ZDOTDIR:-$HOME}
if [[ ! -s "$repo_dir/templates/init.zsh" ]]; then
print -u2 -r -- 'Initializer setup stopped: set repo_dir correctly first.'
else
mkdir -p "$config_base/.zsh.addons/local"
if [[ ! -e "$config_base/.zsh.addons/local/init.zsh" &&
! -L "$config_base/.zsh.addons/local/init.zsh" ]]; then
cp "$repo_dir/templates/init.zsh" \
"$config_base/.zsh.addons/local/init.zsh"
else
print -r -- 'Keeping the existing private initializer.'
fi
fiThe starter is copied, never symlinked: the repository version stays immutable, and later updates cannot overwrite private machine data. Its examples are all commented, so the untouched file is valid Zsh and has no effect.
Aliases, ordinary functions, and app-specific behavior usually do not need the
first-load exception. Give them focused private peers instead—for example,
~/.zsh.addons/work/.zsh.work-shortcuts:
alias cdmywork='cd "$HOME/Developer/Work"'
startwork() {
cd -- "$HOME/Developer/Work" || return
git status --short --branch
}This is the only source-order exception. It establishes the environment peers need, but it must not call peer functions during source because they are not loaded yet. Shared scalar defaults, palettes, extension arrays, and convenience aliases preserve values established here. To replace a shared command or full prompt, disable its peer instead of defining a colliding private peer.
To load the initializer from a different location, export ZSH_LOCAL_INIT
before Zsh starts, usually from .zshenv or the parent process:
export ZSH_LOCAL_INIT="$HOME/.config/zsh/machine-init.zsh"Keep passwords and API tokens out of the repository even when adding examples.
Prefer the system keychain or a dedicated secret store. The committed
initializer starter contains no active configuration;
each user's real init.zsh remains outside the repository in their home
directory.
Migrating an older installation is a one-time move:
config_base=${ZDOTDIR:-$HOME}
legacy_init="$config_base/.zshrc.local"
private_init="$config_base/.zsh.addons/local/init.zsh"
if [[ -f $legacy_init && ! -e $private_init && ! -L $private_init ]]; then
mkdir -p "$config_base/.zsh.addons/local"
mv "$legacy_init" "$private_init"
elif [[ -f $legacy_init ]]; then
print -r -- 'Keeping both files; review and merge the legacy initializer manually.'
fi
exec zshReview the moved file instead of preserving it as a catch-all. Keep only genuine
pre-peer prerequisites in init.zsh; move aliases, ordinary functions, and
app-specific behavior into focused private .zsh.<name> peers. Any action that
needs a shared peer must run later from an interactive command, hook, or widget.
After the local initializer, the bootstrap recursively loads every regular file
matching .zsh.addons/**/.zsh.<name>. A symlink installation loads shared peers
beside the resolved repository .zshrc and private peers from the configuration
base's .zsh.addons. A copied installation places a marked, installer-owned
shared namespace inside that user add-on tree and scans the complete tree once.
Do not personalize the managed namespace: change shared code in the repository
and rerun install.zsh --copy; keep private peers beside it.
.zsh.addons/
├── .zsh.xcode
└── work/
└── .zsh.company-tools
There is no registration list and no core-module list. Create a matching file
and the next shell loads it automatically. Disable one without deleting it by
changing its prefix, for example from .zsh.xcode to
.disabled.zsh.xcode, or remove the file.
Add-ons are executable shell configuration, not data. Keep this directory under
the same ownership and code-review boundary as .zshrc. The loader never
discovers code from the current project and does not follow nested symlink
directories.
Lexical loading makes errors repeatable, but it is not a dependency mechanism. Each file owns its defaults, helpers, hooks, cleanup, and fallback behavior. It must not call a peer while being sourced or rely on its filename sorting before another. Optional collaboration is checked only when the feature runs, so a missing unit degrades cleanly and every permutation of the same enabled files converges on the same shell state.
Put a shared integration under the repository's .zsh.addons directory. Put a
private peer under the configuration base's .zsh.addons. In copy mode, the
installer-owned shared namespace is nested beside those private peers; it is
not a place for personal edits. Nested concern directories are allowed, but the
file itself must begin with .zsh.:
.zsh.addons/
└── database/
└── .zsh.postgres
Keep startup work minimal: define functions and settings while the file loads, then perform tool discovery, filesystem writes, or expensive work only when the user calls the public command. Do not add dependencies or an ordering prefix; make the unit stand on its own. A small add-on follows this shape:
_postgres_tools_available() {
(( $+commands[psql] ))
}
_compozsh_help_postgres-status() {
local -a printer=(print -rl --)
(( ${+functions[_output_print_help]} )) && printer=(_output_print_help)
"${printer[@]}" 'usage: postgres-status' \
'List the PostgreSQL databases available to psql.' \
'' \
' Requires psql on PATH and a reachable configured PostgreSQL server.' \
' Uses normal psql connection settings and may prompt to authenticate.' \
' Takes no arguments; --help shows this guide without connecting.' \
'' \
'Examples:' \
' postgres-status Run psql --list with your current settings.'
}
postgres-status() {
emulate -L zsh
if (( $# == 1 )) && [[ $1 == --help ]]; then
_compozsh_help_postgres-status
return 0
fi
(( $# == 0 )) || {
print -u2 -r -- 'usage: postgres-status'
return 2
}
_postgres_tools_available || {
print -u2 -r -- 'postgres-status: psql is not installed'
return 1
}
command psql --list
}Start a clean shell with exec zsh after adding or renaming an add-on. Keep only
genuine pre-peer prerequisites in local/init.zsh; use a focused private peer
for aliases, functions, apps, defaults, hooks, optional requirements, or other
behavior that does not require that ordered boundary.
Function names beginning with _ are internal implementation details. Zsh does
not enforce private access, but the leading underscore is this repository's
explicit “do not call or override” convention. Internal helpers may change,
move, or disappear during a refactor without preserving compatibility.
Documented names without a leading underscore form the user-facing interface. For example, call:
update_xcode_skillsDo not call its helpers directly:
# Internal implementation details—not user commands:
_detect_xcode_skill_vendor
_install_xcode_skills_for_agentNew add-ons should follow the same rule: choose a clear unprefixed name only
for a command users are meant to run, document it here, and prefix supporting
functions and state with _.
Every public add-on function intended to be run directly supports --help, so
its current usage is discoverable without opening this README. The complete
shipped add-on command surface is:
mkcd --help
cpdir --help
git-discard-all --help
prompt-refresh --help
g --help
xcode --help
update_xcode_skills --help
compozsh --helpAll Compozsh-owned help follows one stable contract:
- The first line, ignoring color codes, begins with lowercase
usage:and shows the command's exact invocation syntax. - The second line explains what the command does in one concise sentence.
- Further lines explain defaults, data sources, search scope and limits, options, keys, fallbacks, and practical examples where applicable. Commands that change state describe their targets, confirmation, and recovery limits.
- Help returns status
0on standard output and writes no error diagnostics. On supported 256-color terminals, the optional output peer adds semantic emphasis. Pipes, redirects, command substitutions, unsupported terminals, and a nonemptyNO_COLORretain identical plain text. - Asking for help never navigates, copies, scans, detects tools, changes files, prompts for input, uses the network, or requires optional dependencies.
This makes --help safe to inspect anywhere, including a machine that does not
have the command's optional tools installed.
Usage and section headings use ZSH_OUTPUT_COLORS[heading], option labels use
accent, descriptions and example commands use info, and safety/limit or
recovery headings use warning. Body text stays neutral. The same renderer
styles direct help and compozsh help <command>; disabling .zsh.output leaves
all guides usable as plain text. Help styling only checks terminal capabilities
and runs no external commands. For example:
g --help # Colored in a supported terminal.
NO_COLOR=1 g --help # Plain for this invocation.
g --help > branch-help.txt # Plain, copyable documentation.The standalone zsh install.zsh --help stays plain so displaying installation
help never loads shell add-ons or private configuration.
For your own tools, the optional printer pattern in the add-on example above
uses the same colors. Keep headings unindented, body text indented by two
spaces, and option/example labels separated from explanations by at least two
spaces. Name the examples section Examples:. Existing plain-print providers
continue working unchanged.
Use the guides to answer questions such as “does this search the whole disk?”,
“why is this branch missing?”, “does selection run the tool or just show help?”,
and “what happens to my existing files?”. Simple utilities keep their guides
short; search, navigation, installation, and export tools document their wider
behavior. zsh install.zsh --help covers installation separately, including
symlink/copy updates, private files, and recovery backups.
Run the explorer with no arguments for the fuzzy picker:
compozshThe explorer shows the selected tool's help beside its result list at widths of 100 columns or more. Narrower windows use a switchable full-width detail view. This interface uses native Zsh 5.9 and macOS Terminal.app capabilities; no additional application or UI package is required.
All information panels—Help, Location, Branch, Path, and file-action descriptions—share one list-first layout. The list receives about two thirds of the usable width; details use the remaining third, capped at 48 columns. Extra width goes to the list. Below 100 columns, the list stays full-width until you focus details.
Beside short lists, passive details use up to six rows with quieter headings.
Focusing the panel uses the available body height on the owned full screen;
the inline fallback allows up to twelve reading rows. Longer result lists retain
their visible rows; passive details use that same
height. Warnings keep their emphasis, and longer content remains scrollable.
Moving focus keeps pane widths stable. Every split panel uses the same focus
language: list focus keeps the selected row bright blue; detail focus subdues
that row and marks the active panel with a cyan ┃ rail plus its ▸ heading.
The selected item therefore remains visible while the emphasis identifies
which pane receives movement and page keys. Single-pane narrow layouts use the
focused heading without drawing a divider. Resizing preserves selection and
uses the captured data without rerunning providers.
| Key | Action |
|---|---|
| Up/Down or Ctrl-P/N | Select a tool, or scroll when the help pane has focus |
| Right or Ctrl-E | Focus the help pane |
| Left or Ctrl-B | Return to the tool list |
| Tab or Shift-Tab | Switch panes |
| Fn-Up/Down | Page up/down in the focused pane on Apple keyboards |
| Option-Up/Down | Page up/down with Option-as-Meta enabled |
| Ctrl-V / Ctrl-D | Page down/up without requiring Option-as-Meta |
| Type or paste | Refine the filter and return to the list |
| Enter | Close the browser and print the selected tool's complete help |
| Visible digit, with an empty filter and list focus | Show that tool's full help |
| Escape or Ctrl-G | Cancel; Ctrl-C aborts |
Search and selection survive switching panes and resizing. The focused help
pane has the shared focus rail and ▸ marker, a scroll position, and contextual keyboard hints. Help
uses the shared semantic output palette when available; missing peers retain
the ordinary picker and plain documentation paths.
Help companions are captured once when opening the browser: at most 64
providers, 32,768 characters per provider, and 256 wrapped preview lines.
Truncated previews are labeled, and Enter still shows the complete guide.
Scrolling, filtering, and resizing consume the in-memory snapshot without
executing tools or rereading files. Closing the browser releases the snapshot.
These bounds limit preview data; they do not sandbox a private help provider.
As with --help, user-defined companions must return promptly and be static
and side-effect-free. Functions lacking a same-file companion are never run.
Use its noninteractive forms when you want copyable output or already know the command name:
compozsh --list
compozsh help gSet the maximum visible picker rows before peers load, normally in the private initializer:
ZSH_TOOL_PICKER_MAX_RESULTS=12The catalog is regenerated from Zsh's live in-memory function-source metadata
every time it is requested. It does not scan files, maintain a registry, write a
cache, or depend on add-on loading order. Public functions defined directly in
~/.zsh.addons/local/init.zsh or any private .zsh.<name> peer appear
automatically; removing or redefining one updates the next view immediately.
Functions from external hooks outside an add-on tree and names beginning with
_ remain hidden.
The explorer never invokes a public function to test whether it understands
--help. Instead, a documented command owns a private companion named
_compozsh_help_<command> in the same file. The public command calls that
provider for --help, and the explorer calls the same provider directly. This
keeps documentation DRY and makes exploration safe. A user function without a
matching provider still appears as no help; selecting it reports its source
without executing it.
Add-on authors must give every new public command the same contract. Define its
companion provider beside it and handle --help before detection or operational
logic; the .zsh.postgres example above is the canonical starting shape.
g --help documents Compozsh's branch-picker mode; use git --help for Git's
own documentation.
Programmatic extension functions such as prompt_add_project_segment are APIs,
not terminal commands, and document their call signature where they are used.
Transparent wrappers such as grep and man preserve the help behavior of the
underlying system command.
Run this anywhere inside an Xcode project:
xcodeCompozsh walks upward to the nearest directory containing a literal
.xcworkspace or .xcodeproj; it does not recursively search the repository.
When both exist at that scope, the workspace is offered first. The full-screen
dashboard then uses Xcode to discover shared schemes and concrete destinations.
Choose either setting directly, then apply Build, Test, Analyze, or
Clean. Selecting a Simulator also exposes Build & Run, which builds the
scheme, boots that exact simulator, installs the app, and launches its bundle.
Physical-device launching remains in Xcode.
The dashboard coordinates the first-party tools already installed with Xcode:
xcodebuild, xcrun, simctl, open, and plutil. Its filtering, focus,
resize behavior, guide, and temporary-screen cleanup are the same shared
Compozsh interaction system used by the other full-screen tools. No additional
package, daemon, project file, or registration step is introduced.
Apple documents the underlying surface in the
Xcode command-line tool reference
and its guidance for
running and interpreting tests.
Opening the workspace performs read-only Xcode discovery. Captured scheme and
destination output is size-bounded. Discovery and actions disable automatic
package resolution and package updates and require versions from
Package.resolved; Compozsh does not enable provisioning updates or bypass
package-plugin or macro validation. Xcode can still inspect project and package
metadata during discovery.
Build-related choices are explicit execution boundaries. A project can contain build scripts, package plugins, macros, tests, and application code, so review an unfamiliar repository before applying an action. The full-screen dashboard closes before the selected action starts, leaving Xcode’s native output in the normal terminal.
Arguments keep direct access to the underlying CLI and preserve its status:
xcode -version
xcode -showBuildSettings -scheme AppIn a noninteractive or dumb terminal, bare xcode runs a safe xcodebuild -list
for the nearest container. Full Xcode must be selected through
xcode-select; the standalone Command Line Tools package does not provide this
workspace’s complete build and Simulator surface.
Xcode 27 includes Apple-authored agent skills, but an external Codex
installation may not discover them automatically. Apple documents this in the
Xcode 27 release notes.
The .zsh.xcode add-on provides:
update_xcode_skillsThe function first detects compatible coding agents installed locally, whether
through their CLI or standard macOS application. It then checks the Xcode
selected by xcode-select, exports its portable skills once, and installs real
copies only for the detected agents:
| Agent | Detected installation | Destination |
|---|---|---|
| Codex | codex, ChatGPT.app, or Codex.app |
~/.agents/skills/ and Xcode's Codex directory |
| Claude Code | claude or Claude.app |
~/.claude/skills/ |
| Gemini CLI | gemini |
~/.agents/skills/ |
| Google Antigravity | agy, antigravity, or Antigravity.app |
~/.gemini/config/skills/ |
| Kiro | kiro-cli, kiro, or Kiro.app |
~/.kiro/skills/ |
CLI names must resolve through PATH. Applications are checked in the system
and per-user Applications directories. The updater does not create configuration
directories for absent vendors, and it stops before exporting anything when no
supported local coding agent is found.
The installer marks only the skill directories it creates. A refresh builds a complete sibling copy and swaps it into place only after the copy succeeds, so stale files disappear and a failed refresh leaves the previous skill intact. A same-named personal skill without the marker is preserved and reported instead of overwritten. Detected agents do not need to be open, running, authenticated, or configured when the function runs.
The shared ~/.agents/skills directory follows the current
Codex skill convention and is
also discovered by
Gemini CLI.
The remaining locations follow the native conventions documented by
Claude Code and
Kiro. Xcode's Codex-specific destination
comes from Apple's release-note workaround. Using the shared directory avoids
installing a duplicate Gemini copy that could appear twice in its selector.
Antigravity
uses its documented global skill directory across its IDE and CLI.
Start a new agent session after a successful export. Gemini CLI can discover
the changes immediately with /skills reload; Claude Code also notices changes
live when its top-level skills directory already existed. Kiro can confirm the
result with /context show.
Exporting happens only when update_xcode_skills is called. Opening a shell
never launches Xcode or writes skill files.
Xcode also exposes live project operations to external agents through
xcrun mcpbridge. That complements static skills: exported skills provide
Apple's guidance, while the bridge provides live Xcode tools for the project
currently open in Xcode. After allowing external agents in Xcode's Intelligence
settings, configure Codex once with:
codex mcp add xcode -- xcrun mcpbridgeSee Apple's external-agent setup for the required Xcode setting and workflow.
The navigation peer ships five small aliases and preserves an initializer value when the same alias already exists:
| Alias | Expands to |
|---|---|
.. |
cd .. |
... |
cd ../.. |
.... |
cd ../../.. |
ll |
ls -lah |
la |
ls -A |
Zsh already remembers recently visited directories because this configuration enables its native directory stack. At the prompt, hold Option and press Tab to open Recents directly, even with an unfinished command or the cursor in its middle. Enable Option as Meta in the active Terminal profile as described in macOS keyboard shortcuts. Inside path + Tab, use Ctrl-X → Go to · Recent directories. Recents is a separate view of visited locations; it is never mixed into a folder's children:
Recent directories · this shell · 3 shown
Current: ~/Projects/example-app
Search ‹›
[ 0] ● example-app/ current · ~/Projects
[ 1] api/ previous · ~/Work
[ 2] api/ visited · ~/Personal
⏎ insert · Esc cancel · ^K keys · ^Y copy · ↑↓ move · Tab details
Folder names are primary, with quiet parent paths so identically named folders
stay distinguishable. Filtering still searches the complete captured path,
including parents. Current and previous-location cues help you regain context.
The Location pane shows the full absolute path, native ~N stack index, whether
the entry is a directory or link, and its availability when the picker opened.
Unavailable entries remain visible. Selection inserts their path; Zsh checks
the destination when you submit it at the prompt.
Tab or Right focuses details; Shift-Tab or Left returns to the list. Ctrl-Y
(or Option-W) copies the literal absolute path without a trailing newline
and closes the picker, without changing directory. It requires pbcopy; failures
are reported. Ctrl-K opens the shared keyboard guide.
The workspace checks type and availability for the first 200 visited entries
when opened. Later entries remain searchable and show Not checked in details.
Opening Recents never lists their children, reads project files, runs Git, or builds
a second directory history. Ctrl-O explicitly opens the selected
location in the same folder browser.
Browse children with Right/Tab, preview with Ctrl-O, and use Enter/digits to
insert a path. Ctrl-X offers explicit directory changes; current folder lets you use
an empty location. In that browser, Escape returns to your unchanged recent-list
filter and selection; browsing alone never changes the shell's directory.
Copying, revealing or inserting a path closes without changing directory.
Filtering, paging and resize use that snapshot; a new invocation refreshes it.
Directory changes are revalidated by cd. With no previous locations, your
current folder is still useful for details or copying. Private directories and
bookmarks are not persisted by this UI.
With an empty filter, press any visible digit from 0 through 9 to insert its
quoted path directly. You can also use the arrows or Ctrl-P/Ctrl-N, type to
filter, and press Enter. Both return to the normal prompt with that path
visible, replacing any unfinished command. Press Enter at the prompt to change
directory, just as with path + Tab. Escape or copying preserves your draft and
cursor instead. Once filtering begins,
digits become normal search text so names containing numbers remain searchable.
On the initial unfiltered screen, slot 0 is the current directory. Slot
numbers update while scrolling; they are not native directory-stack indexes.
Run dirs -v for a native list with actual stack indexes.
Zsh's native ~1, ~2, and other directory-stack expansions still work. They
are shell syntax rather than a feature owned by this configuration; the visual
selector is now the primary direct interface.
Inside a Git working tree, run g without arguments for the branch selector.
Its compact list view looks like this:
Branches · recent checkouts · 3 shown
example-app
Search ‹›
[ 0] ● feature/prompt-navigation
[ 1] main
[ 2] feature/runtime-line
⏎ switch · Esc cancel · ^K keys · ^X review · ^Y copy · ↑↓ move
The familiar Git shorthand remains intact: g status, g switch, and every
other argument-bearing form delegate directly to git. With an empty filter,
press a visible digit to switch immediately; after typing a filter, digits are
search text.
The Branch panel shows the full name, current-branch indicator, latest
commit ID and subject, and configured upstream. At 100 columns or more it sits
beside a wider branch list, capped at 48 columns and compact until focused.
Smaller windows use a switchable full-width detail view. Right/Ctrl-E
focuses details, Left/Ctrl-B returns to the list, and Tab switches panes.
Up/Down scrolls focused details; typing returns to filtering. Enter still
switches branches, and copying works from either pane. Immediate digit
selection requires an empty query and focus on the visible list.
An additional read-only Git batch captures these details before opening the
picker. It reads at most 262,144 characters across up to 401 recent refs and
shows at most 512 characters per commit subject. Unavailable or capped records
show a notice while their branches remain selectable. No fetch is performed;
upstream names describe local configuration, not server freshness. Rerun g
to refresh the snapshot.
Select a row and press Option-W or Ctrl-Y to copy its branch name without
switching. Modified keys intentionally leave every printable character,
including c, available for fuzzy filtering. Clipboard support uses macOS's
built-in pbcopy; the hint and action disappear on hosts where it is missing.
During SSH, this copies to the clipboard of the machine running Zsh, not
automatically to the client Mac.
The branch list starts with the current local branch, then uses up to 200
matching checkout entries in Git's HEAD reflog. It includes switches recorded
there through git switch, an IDE, or another terminal without maintaining a
second history file. Deleted branches, detached commit IDs, remote-only refs,
and local branches absent from that recent history are omitted. Use g branch
to list every local branch, or g branch -a to include remote refs. Selection
uses git switch --no-guess, so Git will still refuse unsafe switches when
local changes conflict or a branch is already checked out in another worktree.
Both navigation selectors use captured in-memory labels while typing and resizing. Directory collection launches no process; branch discovery runs Git during the initial capture, with no further Git calls while filtering. The number of visible rows is bounded by the terminal height and defaults to ten. Arrows and page keys can reach every match in the captured list, with no extra Git commands or directory scans. Override the viewport size locally if desired:
ZSH_NAVIGATION_PICKER_MAX_RESULTS=12Run g → Ctrl-X review to choose a review context:
| View | Scope | Enter does |
|---|---|---|
| Working changes | The current checkout, independent of the highlighted branch | Focus the selected staged or unstaged diff |
| Branch commits | Captured local history of the selected branch | List that commit's changed files |
| Commit files | Selected commit versus its first parent, or the empty tree for a root commit | Focus the selected file's diff |
Working changes and Commit files use a two-pane review workspace. A narrow
file navigator sits on the left; the selected file's continuous diff occupies
most of the width on the right. Selecting another file updates the reader
directly. Focus is visible independently from selection: the active file list
uses its bright blue selected row; while reading, that row becomes subdued and
a cyan ┃ rail plus ▸ heading marks the active document. This preserves the
selected-file landmark without implying that arrow keys still move the list.
Below 90 columns the focused document owns the full width, so its heading is
the focus cue and no pane divider is drawn. Focused diff is the default:
changes with three unchanged lines
before and after each hunk, with old/new line numbers. Nearby hunks can merge.
Green row backgrounds and + mark additions; red backgrounds and -
mark removals. Syntax colors remain on the code itself. Unchanged rows keep
the terminal background. Addition/removal counts describe the retained diff.
The optional .zsh.git-syntax peer adds prototype code highlighting using
Apple's /usr/bin/vim. Initial coverage is Swift, Zsh (.zshrc, .zsh.*,
*.zsh), shell (*.sh), JSON and Python. Nothing is installed, and your Vim
configuration is not needed. The selected file's status reports syntax coverage
or a plain fallback. Navigation and reading work the same in either case.
This is lexical highlighting of the captured source fragments, not IDE semantic analysis. Old and new sides are analyzed separately, with fresh lexical context at gaps between hunks. A focused hunk starting inside an omitted multiline string/comment can be colored imperfectly; full-file context provides the captured preceding source when available. Apple controls which Vim version and syntax definitions ship with each macOS release. OS updates may improve coverage; they do not automatically enable additional languages here.
Token capture happens once per retained file/context snapshot, outside input and rendering. Scrolling, resizing and revisiting that snapshot launch no Vim process. Syntax has its own 64 KiB input, 3,000 document-row, 2,048-character source-line, 4,096-token and 300 ms subprocess limits. Exceeding a limit, an unsupported file type or unavailable Vim leaves the full retained diff readable with its row backgrounds; code is never truncated just to color it. Rendering/metadata work and Git capture have separate costs. Unicode syntax requires a UTF-8 shell locale; other locales keep a plain fallback for non-ASCII source so character offsets remain safe.
Vim receives captured text through a pipe, never a repository file. The adapter
disables user configuration, modelines, plugins, swap and Vim history; it loads
an allowlist of system syntax definitions. No source snapshots are written to
temporary files. Only validated numeric token spans return to the shared ZLE
renderer. This is configuration hardening, not an OS sandbox. Both installer
modes include the adapter automatically; disabling .zsh.git-syntax keeps
ordinary diff review available.
Review colors live in ZSH_HIGHLIGHT_STYLES: review-added, review-removed,
review-keyword, review-string, review-number, review-comment, review-type,
review-function and review-variable. Set overrides in your private initializer.
Keep foreground roles legible against both diff backgrounds; the blue file-list
selection remains independent. Token foregrounds inherit their diff-row style.
Tab / Shift-Tab switches panes; Enter / Ctrl-E focuses the diff and Ctrl-B returns to files. Up/Down scrolls line by line; Fn-Up/Down or Option-Up/Down pages the focused pane, with Ctrl-V / Ctrl-D as Control alternatives. The page stride follows the current reader height and retains one overlapping source line, so resizing or changing font size updates it automatically. Reading stops at the end of that file; returning to a file restores its reading position. Typing filters captured file paths and change status. Empty-filter digits select visible slots and focus reading. Below 90 columns, the focused pane occupies the full width.
Right progressively discloses files → focused diff → full-file context. Left reverses those steps. At either end, another arrow leaves the view unchanged. Tab and Ctrl-E/B are direct pane-focus shortcuts and preserve the current context mode; Right from the file navigator always enters focused diff. The context mode stays until changed through disclosure; new workspaces start focused. This also works in the single-pane layout on narrower terminals.
Ctrl-R refreshes the file list and selected diff, preserving your filter, exact file/change kind, pane focus and source area when still available. It also works with an empty list or a filter that currently has no matches. Ctrl-X remains review on the main Branches screen and is inactive inside the reader. Ctrl-K shows the complete keyboard guide.
Expanding keeps the current source area visible, using the first visible code line as its anchor (including wrapped continuations). Collapsing retains that line when it is still present. If you scrolled into unchanged code omitted by focused mode, it selects the nearest retained context line, preferring earlier context on an exact tie; the status reports that adjustment. Removed lines use old-file coordinates; additions and unchanged lines use new-file coordinates. Each visited file retains its own anchor across mode changes. Selecting the already-active mode leaves the reading position alone.
Untracked files are files Git is not tracking yet. Selecting an individual
regular text file previews its contents as numbered green additions—every line
is new. Files inside new directories appear individually too. Their full
relative path is the primary navigator label and a separate compact New
column states what happened: parent/folder/file.ext New. The renderer may
middle-abbreviate the path when space is tight, while the reader header retains
it exactly. The review subtitle and detail context keep Git's precise
Untracked term, and filtering matches the full relative path. This prevents
the state and folder from collapsing into one ambiguous truncated token.
Ignored files remain excluded. Git owns this enumeration at entry/refresh;
movement and redraw never scan directories. New-file previews and metadata
notices have a single reading level:
Right enters the reader, Left returns to files. No extra expansion is offered.
This does not stage or execute the file, even if it has
executable permissions. Large previews retain the limits below.
Refresh also seeks the current source line. Positions refer to line numbers, not content tracking across concurrent edits. Truncated captures seek the nearest available line with a limit notice; metadata-only or failed reads start at their notice. The viewport may clamp near the end of a document.
Escape goes Back through files → commits → branches, retaining each list's filter, selection and viewport. The entire journey owns one alternate-screen session, including resize and loading states. Branch selection still switches and Ctrl-Y still copies on the main branch screen; review views have no staging, discard, commit, checkout or clipboard actions.
Entering review captures the file list. Selecting a file requests a bounded diff outside the renderer; the four most recently read file/context snapshots are reused until refresh or workspace exit. Selecting a file through filtering may load its diff too. Scrolling, focus, guide and resize reuse captured facts. To follow edits from an AI or another editor, keep Working changes open and press Ctrl-R whenever you want the latest observation. New and removed changes update the navigator; only the selected diff is loaded immediately. Other files are captured when selected. Refresh clears all cached diff/syntax views and other files' reading bookmarks, so changing context mode cannot bring back an older snapshot. Updates are manual; there is no background watcher or automatic movement while you read.
If the selected path/change kind is no longer among the filtered results, the status explains the change and focus returns to the file list at its first match (or an empty state). Your filter is retained; Ctrl-U clears it. A failed file-list refresh retains the previous snapshot with a visible retry message. Ctrl-R retries; Ctrl-L only redraws existing data. If Git's safety configuration cannot be refreshed, cached documents remain readable and new captures wait for a successful retry. Commit-file refresh keeps the captured commit ID; reopen branch history to see new commits. A working diff is a new observation: it can reflect edits made after its file list was captured. Commit browsing resolves the branch tip once and uses full object IDs thereafter, even if that branch moves. Nothing is fetched. An unborn repository can still review working changes through Ctrl-X. Replacement refs are ignored so review reads the literal captured objects.
Coverage is deliberately bounded and labeled:
- Each capture retains at most 256 KiB, with up to 1,000 change rows
or 200 commits. A file with both staged and unstaged edits has two rows.
Status labels use
Mmodified,Aadded,Ddeleted, andTtype change. - Each document retains 10,000 logical lines, with up to 20,000 wrapped display lines. All captured hunks share one scrollable reader. Limits are labeled; choose focused diff or use ordinary Git for larger files.
- At most four raw diff snapshots (up to 1 MiB combined) are cached for the current workspace, plus bounded syntax metadata, the selected document and small reading bookmarks. Refresh replaces both diff and token snapshots.
- Binary diffs and conflicts show notices. For untracked previews, a NUL byte in the captured prefix produces a binary notice; this is a bounded content check, not a universal file-format detector. Empty files have their own notice. Symlinks are not followed, including parent-directory symlinks; missing, special or unreadable files report unavailable. A nested Git repository may still appear as a folder notice; review it from its own repository instead. Submodules are excluded; renames appear as separate deletion/addition entries.
- Git's bounds above limit output/retention, not elapsed time. Large worktrees or slow storage can delay a synchronous read. Listing individual files in large untracked trees does more work than grouped-folder status. Busy and failure states are explicit. Syntax uses a short-lived, deadline-bound child; there is no resident worker or persistent review cache.
Review disables external diff and text-conversion commands, configured clean/process filters, filesystem-monitor hooks, optional index writes and Git network transport for its own calls. Repository configuration is untouched. Files that normally use filters can consequently look different from your configured command-line diff; review shows Git's unfiltered comparison. This is a defensive read-only workflow, not a sandbox for Git or the filesystem.
The optional .zsh.git-review peer supplies these views. Disabling it leaves
g switching and copying unchanged. g --help includes this workflow and
its limits; g <arguments> continues to delegate to ordinary Git.
Inside a Git repository, the prompt can show:
| Mark | Meaning |
|---|---|
✓ |
clean working tree |
+2 |
two staged files |
!3 |
three modified files |
?1 |
one untracked file |
x1 |
one conflicted file |
≡2 |
two stashes |
⇡3 / ⇣1 |
commits ahead of / behind the upstream branch |
⚡rebase |
an in-progress Git operation |
Git colors are green for clean, yellow for local changes, cyan when behind, magenta when detached, and red for conflicts or an in-progress operation.
When the current directory is inside a recognized project, the prompt inserts a second line and moves the input arrow to a third line:
╭─ user@host ~/Projects/web-app git:main ✓
├─ web-app node 24.5.0 · pnpm · next · workspace · docker
╰─ ❯
Detection walks upward from the current directory, so the line remains visible
inside project subdirectories. It recognizes standard project files such as
Package.swift, package.json, pyproject.toml, Cargo.toml, go.mod,
CMakeLists.txt, composer.json, build.zig, project.godot, gleam.toml,
dune-project, fpm.toml, Terraform files, Scheme/Lisp source files, and their
equivalents for the other supported runtimes.
The top row also responds to the current terminal width. Git stays inline when the location and complete summary fit:
╭─ user@host ~/Projects/app git:main ✓
├─ app swift 6.3 · xcode
╰─ ❯
On a narrower window or a long branch, Git moves to its own tree-aligned row:
╭─ user@host ~/Projects/example-app
├─ git:codex/topic-noncopyable-ownership no-upstream
├─ example-app swift 6.3 · xcode
╰─ ❯
Zsh updates $COLUMNS when the window or font size changes. The active command
line is redrawn immediately, without waiting for Enter and without rerunning Git
or toolchain detection for every resize event. If the Git summary itself cannot
fit on a very narrow row, its middle is temporarily abbreviated with …;
widening the window restores the complete text.
At extremely narrow widths, the first-row location follows the same rule after
Git has moved down. It keeps the beginning and current-directory end visible,
for example ~/Pro…mple-app, rather than allowing the row to wrap. The full
location returns as soon as it fits again.
The project row is responsive too. It always keeps the project name, then shows complete runtime, tool, framework, and environment items from left to right for as long as they fit. Narrowing the terminal hides trailing items; widening it restores them from the in-memory result without rerunning project detection.
The right-side clock remains on completed prompts, providing a useful historical timestamp. Completed prompts are ordinary terminal scrollback rather than live Zsh UI, so neither their layout nor their clock can be repositioned after a resize; the terminal may reflow them at very narrow widths. The active prompt continues to adapt immediately.
Built-in language and toolchain coverage includes:
- Native and systems: C/C++, Objective-C, Assembly, Rust, Go, Zig, and Fortran
- Apple, web, and scripting: Swift, Node.js, Deno, TypeScript, PHP, Ruby, Perl, Lua, Bash, PowerShell, and Python
- JVM and .NET: Java, Kotlin, Scala, Groovy, C#, F#, and Visual Basic
- Functional and BEAM: Haskell, OCaml, Scheme, Racket, Common Lisp, Elixir, Erlang, and Gleam
- Scientific, game, and infrastructure: R, Julia, Dart, Godot/GDScript, and Terraform/OpenTofu
The detected project directory appears first, followed by every applicable
runtime and its installed version. A missing tool is shown as not-installed.
Runtime versions are cached per project and executable so prompt rendering stays
fast and version-manager shims can differ between repositories. Outside a
recognized project, the extra line disappears and the prompt stays two lines.
Runtime commands are resolved from the user's PATH; the prompt never executes
programs from a repository's node_modules, build directory, or wrapper scripts
merely because the user entered that directory.
The contextual labels cover the major supporting ecosystems without parsing dependency files or executing repository code:
| Area | Recognized context |
|---|---|
| Native builds | CMake, Meson, Autotools, Bazel, Conan, vcpkg, xmake, Make, Ninja, Just, Task |
| Apple | Xcode workspaces, SwiftPM, CocoaPods, Carthage, Tuist, XcodeGen, SwiftLint, SwiftFormat, Fastlane, Mint |
| JavaScript and web | npm, pnpm, Yarn, Bun, Turbo, Nx, Lerna, Rush, Next.js, Nuxt, Vite, Astro, Remix, Gatsby, Angular, SvelteKit, Expo, React Native, Electron, Storybook, Prisma |
| Python and data | uv, Poetry, PDM, Pixi, Hatch, Pipenv, Conda, pip, Jupyter, Django, MkDocs |
| JVM and .NET | Maven, Gradle, sbt, Mill, solutions, NuGet, Android |
| Other language ecosystems | Cargo, Go modules, buf, Stack, Cabal, Hpack, Bundler, Composer, pub, Melos, Mix, renv, pak, LuaRocks, CPAN, Carton, Dist::Zilla, fpm, dune, opam, rebar3 |
| Infrastructure | Terraform/OpenTofu, Terragrunt, TFLint, Pulumi, Helm, Kustomize, Serverless, Supabase |
| Development environments | Workspaces, Docker, Dev Containers, Nix, devenv, mise, direnv, Vagrant |
These labels describe files present in the project. They do not claim that the corresponding package-manager or build command is installed.
Installed runtime versions are cached for prompt speed. After installing or upgrading a tool without opening a new shell, refresh them with:
prompt-refreshFor example, the adaptive line can look like:
├─ storefront node 24.5.0 · typescript 6.0.1 · pnpm · next · docker
├─ edge-api deno 2.5.0 · task
├─ ios-client objective-c clang 21.0.0 · xcode · xcworkspace · cocoapods · swiftlint
├─ mobile kotlin gradle-managed · gradle · android
├─ docs python 3.14.0 · pdm · jupyter · mkdocs
├─ services dotnet 10.0.100 · solution · nuget
├─ game godot 4.5.1
├─ infrastructure terraform 1.14.0-tofu · terragrunt · tflint · helm
├─ numerical-model fortran gfortran 15.2.0 · fpm
Version files such as .nvmrc, .node-version, .deno-version, .python-version,
.swift-version, .ruby-version, .java-version, .php-version,
.kotlin-version, .lua-version, .terraform-version, go.mod, and
.tool-versions are compared with the active runtime. The other built-ins also
accept a matching .<language>-version file where that convention is useful.
Mismatches are placed at the end of the line in red:
├─ web-app node 24.5.0 · pnpm · next · ⚠ node wants 22
Compiled and Lisp-family languages show the implementation that actually runs the code rather than inventing a language version:
├─ native-app c/c++ clang 21.0.0 · asm nasm 2.16.03 · cmake
├─ sicp scheme mit-scheme 12.1
├─ lisp-notes lisp sbcl 2.5.7 · asdf
C/C++, Objective-C, Assembly, and Fortran detection looks for common source
extensions in the project root and conventional source directories. Scheme
recognizes .scm and .ss, Racket recognizes .rkt, and Common Lisp
recognizes .lisp, .cl, and .asd. Because shell scripts often appear as
helpers in otherwise unrelated repositories, Bash is shown only when a
directory looks like a shell-first project.
There are too many useful languages and toolchains to hard-code all of them.
The prompt therefore exposes two extension points that can live in a focused
private peer such as ~/.zsh.addons/.zsh.project-v:
typeset -ga PROMPT_PROJECT_MARKERS PROMPT_PROJECT_CONTEXT_FUNCTIONS
PROMPT_PROJECT_MARKERS+=(v.mod)
v_prompt_context() {
[[ -f "$1/v.mod" ]] || return
local version
version=$(v version 2>/dev/null) || version='not-installed'
prompt_add_project_segment "v ${version}" cyan
}
PROMPT_PROJECT_CONTEXT_FUNCTIONS+=(v_prompt_context)PROMPT_PROJECT_MARKERS teaches the upward project-root search about a new
manifest. Each function in PROMPT_PROJECT_CONTEXT_FUNCTIONS receives that
root path. prompt_add_project_segment safely escapes and colors the text before
placing it on the project line. Because the arrays preserve earlier values and
the function runs only during prompt collection, this remains independent of
peer traversal order.
Run cpdir to place the exact current working-directory path on the macOS
clipboard without selecting terminal text or copying the output of pwd:
cpdirThe copied value has no trailing newline, so it can be pasted directly into a command, editor, or file dialog. The terminal prints a short confirmation after the copy succeeds. Directory names containing spaces are preserved exactly.
Only the displayed path is colored, using ZSH_PROMPT_COLORS[path]; the
surrounding sentence stays neutral. NO_COLOR, redirects, unsupported terminals,
or an unavailable/invalid path palette keep the confirmation plain. Clipboard
contents always remain the exact path, with no styling bytes.
cpdir uses macOS's built-in pbcopy and fails with a clear diagnostic when
that command is unavailable. In an SSH session it copies to the clipboard of
the remote machine running Zsh, not automatically to the client Mac.
The prompt implementation, including PROMPT, RPROMPT, project detection,
and _prompt_update, lives in .zsh.addons/.zsh.prompt. Prefer documented
palette and extension-point defaults in ~/.zsh.addons/local/init.zsh; edit
that focused unit only when changing shared prompt behavior such as the duration
threshold.
git-discard-all provides a guarded equivalent of an IDE's “Discard All
Changes” action. It works from any subdirectory, previews every affected path,
and requires a y confirmation before changing anything:
git-discard-allIt restores both staged and unstaged tracked files to HEAD, then deletes
untracked files and directories throughout the repository. It deliberately
keeps ignored files, stashes, commits, submodule contents, and nested Git
repositories. It also refuses to run without an existing commit or while a
merge, rebase, cherry-pick, revert, or bisect operation is active.
This operation is irreversible for changes that have not been committed or
stashed. Use git stash --include-untracked instead when the work may be needed
later.
Use the same configuration base as installation, then inspect the active bootstrap without printing its contents:
config_base=${ZDOTDIR:-$HOME}
ls -ld "$config_base/.zshrc"Archive the active object rather than deleting it. Moving a symlink does not move or alter the repository target:
stamp=$(date +%Y%m%d-%H%M%S)
mv "$config_base/.zshrc" "$config_base/.zshrc.uninstalled.$stamp"Inspect the add-on tree next:
ls -la "$config_base/.zsh.addons"For a copy installation that should retain private configuration, archive only the installer-owned shared namespace outside the recursively discovered add-on tree:
uninstall_backup="$config_base/.zsh-backups/uninstall-$stamp"
mkdir -p "$uninstall_backup"
mv "$config_base/.zsh.addons/compozsh" \
"$uninstall_backup/copied-addons"For a complete removal, archive the whole add-on tree instead:
mv "$config_base/.zsh.addons" \
"$config_base/.zsh.addons.uninstalled.$stamp"Do not archive the complete tree when keeping local/init.zsh or private peers.
With a symlink installation, the tree contains only those private files, so it
can remain untouched. Agent skills exported by update_xcode_skills are
independent managed copies and remain installed.
Installer-created recovery snapshots remain under .zsh-backups. List them and
restore deliberately after first archiving any new active objects:
ls -lt "$config_base/.zsh-backups"
backup="$config_base/.zsh-backups/compozsh-YYYYMMDD-HHMMSS"
ls -la "$backup"This project is free software licensed under the
GNU General Public License v3.0 or later, identified by SPDX as
GPL-3.0-or-later.
You may use, study, modify, share, and sell the software under those terms.
When you convey covered versions, the GPL requires the corresponding source and
the same freedoms to remain available to recipients; it does not allow covered
work to be turned into proprietary software. See LICENSE for the
complete terms.