Zush is a CLI framework for building nested CLI applications.
Project description
zush
Zack's useful shell — a Click-based CLI that discovers and runs plugin commands from configured environments. One entry point (zush) loads plugins and exposes their commands as subcommands, with hooks and a shared context.
Requirements
- Python 3.12+
- Click ≥ 8.0
Installation
# From the repo (e.g. with uv)
uv sync
uv run zush --help
Or install as a package and use the zush console script.
Quick start
# List commands (built-in tree view)
zush self map
# Run a plugin command (once you have envs configured)
zush <group> <command> ...
Try it without config — use the repo playground and skip cache:
uv run zush --mock-path ./playground self map
uv run zush --mock-path ./playground demo greet
--mock-path / -m uses only that directory as the plugin env and disables cache/sentry.
Config
Location: ~/.zush/config.toml
| Key | Description |
|---|---|
envs |
List of paths to scan for plugins (folders or site-packages). |
env_prefix |
Package name prefix(es), default ["zush_"]. Only packages whose name starts with one of these are loaded. |
playground |
Optional path scanned first (overloaded index); first-wins merge. Good for local dev. |
include_current_env |
Optional boolean; when true, also scan the current interpreter's site-packages (e.g. the uv env running zush). |
Example:
envs = ["/path/to/my/envs", "/another/path"]
env_prefix = ["zush_", "my_"]
playground = "/path/to/zush/playground" # optional
include_current_env = true # also scan the env running `zush`
Config, cache, and sentry live under ~/.zush/ by default. When embedding zush, you can pass a custom storage so config/cache use a different directory; include_current_env controls whether the current interpreter's site-packages are also scanned.
Plugins
- Discovery: From each env path, zush looks for directories whose name starts with one of the
env_prefixvalues and that contain__zush__.pyat the root. - Contract: In
__zush__.py, export a plugin instance (e.g. an object with a.commandsdict).commandsisdict[str, click.Command | click.Group]; keys are dotted paths (e.g.demo.greet,tools.convert). - Hooks (optional): On the same instance you can define
before_cmd,after_cmd,on_error,on_ctx_match(lists of patterns/callbacks). These are registered with the core and run around command execution or when the shared context is updated; they are not exposed as CLI commands.
Minimal plugin:
# my_env/zush_hello/__zush__.py
import click
class ZushPlugin:
def __init__(self):
self.commands = {
"hello": click.Command("hello", callback=lambda: click.echo("Hello"))
}
ZushPlugin = ZushPlugin() # export instance
Helper (optional): Use zush.plugin for a chainable builder so you don’t manage dotted keys by hand:
# my_env/zush_hello/__zush__.py
import click
from zush.plugin import Plugin
p = Plugin()
p.group("hello", help="Greetings").command("say", callback=lambda: click.echo("Hi"), help="Say hi")
ZushPlugin = p
See playground/zush_demo and playground/zush_hooks_demo for examples.
Reserved group: self
selfis reserved; plugins cannot register commands under it.- Built-in command:
zush self map— prints the command tree (liketree).
Embedding
Zush can be used as a subcommand group of another Click app, with its own config and storage:
import click
from zush import create_zush_group
from zush.config import Config
from zush.paths import DirectoryStorage
from pathlib import Path
app = click.Group("myapp")
# Default: use ~/.zush for config/cache
app.add_command(create_zush_group(), "zush")
# Custom envs and storage directory
storage = DirectoryStorage(Path("/myapp/data/zush"))
config = Config(envs=[Path("/my/envs")], env_prefix=["zush_"])
app.add_command(create_zush_group(config=config, storage=storage), "zush")
Then: myapp zush self map, myapp zush <plugin commands>, etc.
Factory signature: create_zush_group(name="zush", config=None, storage=None, mock_path=None). Omitted config/storage use default (load from ~/.zush). mock_path overrides envs and disables cache for that run.
Playground
The playground/ directory contains sample plugins (zush_demo, zush_hooks_demo). Use --mock-path ./playground to run against them without editing config. See playground/README.md for details.
Development
uv sync --extra dev
uv run pytest
Tests live in tests/; pythonpath is set to src.
Memory bank
Project context and design live in memory-bank/. Cline (and similar tooling) reads these files at task start as the source of truth for scope, architecture, and current focus.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zush-0.1.1.tar.gz.
File metadata
- Download URL: zush-0.1.1.tar.gz
- Upload date:
- Size: 10.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a35cc82fa6f162689ceb12e339711a4eb9841bdaf09d35724f185fe55e267ed5
|
|
| MD5 |
310831e8b00afe8a9009b2b182a4e0a3
|
|
| BLAKE2b-256 |
0a133c166b3841fec9b958bc61ffff6a756a7313f3eb5040f7d6f529ff112e6a
|
Provenance
The following attestation bundles were made for zush-0.1.1.tar.gz:
Publisher:
publish.yml on ZackaryW/zush
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zush-0.1.1.tar.gz -
Subject digest:
a35cc82fa6f162689ceb12e339711a4eb9841bdaf09d35724f185fe55e267ed5 - Sigstore transparency entry: 1097419713
- Sigstore integration time:
-
Permalink:
ZackaryW/zush@b6a3818de11e997430e4d0aed6734f76aa5b33ea -
Branch / Tag:
refs/heads/main - Owner: https://github.com/ZackaryW
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b6a3818de11e997430e4d0aed6734f76aa5b33ea -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file zush-0.1.1-py3-none-any.whl.
File metadata
- Download URL: zush-0.1.1-py3-none-any.whl
- Upload date:
- Size: 14.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fba78a40059a0a4699ffe1ad86514ce168ec0afc48fa6f793af8b1b9695b344b
|
|
| MD5 |
05f80b199595dcf3811347029cd2ebcf
|
|
| BLAKE2b-256 |
53fae9ad62f6a0b900a0997ce8983d43ee9d91bcea0faf7858934fcb5abfa1a3
|
Provenance
The following attestation bundles were made for zush-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on ZackaryW/zush
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zush-0.1.1-py3-none-any.whl -
Subject digest:
fba78a40059a0a4699ffe1ad86514ce168ec0afc48fa6f793af8b1b9695b344b - Sigstore transparency entry: 1097419714
- Sigstore integration time:
-
Permalink:
ZackaryW/zush@b6a3818de11e997430e4d0aed6734f76aa5b33ea -
Branch / Tag:
refs/heads/main - Owner: https://github.com/ZackaryW
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b6a3818de11e997430e4d0aed6734f76aa5b33ea -
Trigger Event:
workflow_dispatch
-
Statement type: