_ _ _ _
| |_ ___ __ _ ___| |_ ___| (_)
| __/ _ \ / _` / __| __|__ / __| | |
| || (_) | (_| \__ \ ||___| (__| | |
\__\___/ \__,_|___/\__| \___|_|_|
Python-based CLI utility with plugin architecture for AWS, Kubernetes, and Git operations.
- Plugin Architecture: Modular design with dynamic command discovery
- AWS Integration: Identity checking, profile management, region selection, SSM Parameter Store integration
- Kubernetes: Context switching, EKS cluster discovery and integration, context deletion
- Git: Repository management (clone, branch, pull, push, rm, mirror), GitHub and GitLab clone URLs and nested groups
- Workspace: Directory navigation, environment file management (.env.local, .prompt.md)
- Interface: One look across all commands: filtering pickers, colored help, and clean stdout for scripts
- Security: S3 env-store with SSE-KMS for sensitive files (SSM fallback during transition); secret values masked in diffs and SSM previews
- Commands implemented as plugins extending BasePlugin
- Automatic plugin discovery and loading
- Click integration for CLI behavior
- See ARCHITECTURE.md for details
- Python 3.9+
- External tools: git, aws-cli, kubectl (install those required by your commands)
- Python packages: click, rich
pip install toast-cli# From GitHub
pip install git+https://github.com/opspresso/toast-cli.git
# Development mode
git clone https://github.com/opspresso/toast-cli.git
cd toast-cli
pip install -e .toast --help # View available commands
toast am # Show AWS identity
toast cdw # Navigate workspace directories
toast ctx # Manage Kubernetes contexts
toast dot # Manage .env.local files
toast env # Manage AWS profiles
toast git # Manage Git repositories
toast prompt # Manage .prompt.md files
toast region # Manage AWS region
toast clean # Show reclaimable disk data and remove selected items
toast completion # Print the shell completion script
toast shell-init # Print the bash/zsh function that lets cdw change directory
toast ssm # AWS SSM Parameter Store operations
toast version # Display versiontoast clean measures removable data and opens a picker, largest item first. The
footer shows the selected size and the free space after removal.
Reclaimable 84.1 GiB
◉ 6.2 GiB download arduino-packages Arduino board toolchains
❯ ○ 2.6 GiB cache go-cache Go build cache
○ 2.3 GiB cache vscode-cache blocked: VS Code running · VS Code logs
Selected 1 · 6.2 GiB · Free now 166.3 GiB · after 172.5 GiB
↑↓ move · space select · a all except data · n clear · enter confirm · q quit
| Key | Action |
|---|---|
↑ ↓ or j k |
Move. Blocked rows are skipped. |
Space |
Select or clear the row, then move down. |
a |
Select everything except data items. Press again to clear. |
n |
Clear the selection. |
Enter |
Review the selection and confirm it. |
q or Esc |
Quit without removing anything. |
| Command | Action |
|---|---|
toast clean |
Open the picker. Without a terminal, ask for numbers or names. |
toast clean --list |
Show the items and exit. |
toast clean --json |
Print the items as JSON and exit. |
toast clean NAME... |
Remove the named items after a confirmation. |
toast clean NAME... --yes |
Remove the named items without a confirmation. |
| RISK | Meaning |
|---|---|
cache |
Rebuilt on demand. |
download |
Fetched again, so the first use is slow. |
data |
Cannot be restored: iOS simulator data, Android virtual devices, the Trash, project build output, stopped Docker containers and unused volumes. a does not select these items. |
Project items cover ~/workspace and only directories that Git ignores.
project-builds can hold release files, so it is a data item. old-* items keep
the newest version; old-claude also keeps the current one, and old-node keeps the
newest of each major version and the nvm default. simulator-runtimes keeps the most
recently used runtime of each platform. Installed apps and photos are not listed.
If macOS blocks a protected folder, the command says so; grant the terminal Full Disk
Access to include it.
A blocked item stays. A running GitHub Actions job or xcodebuild blocks Docker,
simulator, DerivedData, Gradle, project and runner items. A running Chrome or VS Code
blocks its own items, and a booted simulator blocks simulators. The check runs again
right before removal. On DGX Spark, Docker images and volumes are not listed. If one
item fails, the others continue and the command exits nonzero. Docker runtimes can
return freed space late, so the free-space figure may not rise at once.
# AWS
toast am # Show identity
toast env # Switch profiles
toast region # Switch regions
# Kubernetes
toast ctx # Switch contexts
# Select "+ Add an EKS cluster" to add clusters from the current region
# Select "- Delete a context" to delete one or all contexts, after a confirmation
# Environment Files (.env.local)
toast dot # Compare local and env-store, choose action (default: sync)
toast dot up # Upload .env.local to env-store (S3)
toast dot down # Download .env.local from env-store (alias: dn)
toast dot diff # Show local vs env-store diff without changing files
toast dot ls # List all .env.local files in env-store (S3 + SSM)
# up and sync's upload write S3: they compare local with the S3 copy, skip an
# identical copy, and preview and confirm before replacing an existing S3 copy.
# down and sync's download write the local file from the newest copy (S3 or SSM),
# skip an identical file, and preview and confirm before replacing an existing
# local file, even an empty one.
# The preview shows the destination's current content as - lines and the
# incoming copy as + lines.
# The confirmation names the S3 URI, or the absolute local path and the source,
# and defaults to no. A new destination is written without a confirmation.
# Secret values are masked, including multiline dotenv values (KEY=ab****yz).
# Prompt Files (.prompt.md)
toast prompt # Compare local and env-store, choose action (default: sync)
toast prompt up # Upload .prompt.md to env-store (S3)
toast prompt down # Download .prompt.md from env-store (alias: dn)
toast prompt diff # Show local vs env-store diff without changing files
toast prompt ls # List all .prompt.md files in env-store (S3 + SSM)
# SSM Parameter Store
toast ssm # Interactive mode: browse and select parameters
toast ssm ls # List all parameters
toast ssm ls /toast/ # List parameters under path
toast ssm get /my/param # Get parameter value, masked by default (alias: g)
toast ssm get /my/param --reveal # Get parameter value in plaintext
toast ssm put /my/param 'value' # Store as SecureString; shows masked diff if it exists (alias: p)
toast ssm diff /my/param 'value' # Show masked diff without writing
toast ssm rm /my/param # Delete parameter (alias: d, delete)
# Git Operations
toast git repo-name clone # Clone repository (alias: cl)
toast git repo-name clone -t target-dir # Clone into a custom directory
toast git repo-name branch -b branch-name # Create branch (alias: b)
toast git repo-name pull # Pull changes (alias: p)
toast git repo-name pull -r # Pull with rebase
toast git repo-name push # Push to remote (alias: ps)
toast git repo-name push --mirror # Mirror push for migration, after a confirmation
toast git repo-name push --mirror --yes # Mirror push without a confirmation (alias: -m -y)
toast git repo-name rm # Remove local repositoryClone accepts HTTPS, SSH (including custom ports), and SCP-style Git URLs.
The repository is cloned into the current directory using its name, or -t for a custom name:
cd ~/workspace/gitlab.clush.net/apps/cdp/be
toast git ssh://git@110.45.156.168:30022/apps/cdp/be/ws.git clone
# Alternatively, use HTTPS:
toast git https://gitlab.clush.net/apps/cdp/be/ws.git cloneWith only a repository name, toast git ws clone in this directory uses
git@gitlab.clush.net:apps/cdp/be/ws.git. All groups below the host directory
form the namespace. Use an explicit URL when the SSH host or port differs.
The existing GITHUB_HOST configuration also overrides the host for name-based clones.
Run toast git in a namespace directory, ~/workspace/{git-host}/{namespace}.
Before Git runs, the command shows the host, the namespace, the absolute checkout or
clone path and the Git action on stderr. A clone also shows its source URL with any
HTTP user name, token or password and every query or fragment value replaced by ***.
Git receives the original URL. Git's stdout is the result and
goes to stdout. Git progress and diagnostics and the success line go to stderr. When
Git fails, the command exits with status 1 and shows Git's error.
push --mirror pushes every local branch and tag to
git@{GITHUB_HOST}:{namespace}/{repo}.git and does not change the existing remotes.
It shows that destination and asks for a confirmation that defaults to no, because
it overwrites remote branches and tags and deletes remote refs that do not exist
locally. A plain push asks nothing.
Toast-cli uses a standardized workspace directory structure for organizing projects:
~/workspace/{git-host}/{namespace}/{project}
Examples:
~/workspace/github.com/opspresso/toast-cli~/workspace/github.enterprise.com/myorg/myproject~/workspace/gitlab.clush.net/apps/cdp/be/ws
The host directory is the Git server domain. The namespace can contain nested groups.
First-time Setup:
When you run toast cdw for the first time, it will automatically:
- Create
~/workspacedirectory if it doesn't exist - Create
~/workspace/github.comas the default structure - Display instructions for creating organization and project directories
You can then create your project directories:
mkdir -p ~/workspace/github.com/{org}/{project}toast cdw [QUERY] prints the absolute path of a namespace or repository directory
below ~/workspace. With the shell integration
loaded, toast cdw and the c alias in Aliases change to that path.
c # Pick from every namespace and repository
c toast-cli # The only directory named toast-cli: no picker
c opspresso/toast-cli # Trailing path components
c github.com/acme/api # The exact relative path, when two repositories share a name
c toast cli # Letters to filter by; every term must match- The picker title shows the workspace root. Run inside a repository or namespace,
the picker starts on that directory and marks it
current. QUERYselects candidates in this order: the exact relative path, then directories whose last path components equalQUERY, then the picker's letter filter.- One match prints its path without a picker. Several matches open the picker with only those matches.
- Without a terminal, several matches fail with exit status 1 and list their relative
paths. Run the command again with one of them, for example
toast cdw github.com/acme/api. WithoutQUERY, the command fails and asks for one. - A
QUERYthat matches nothing fails with exit status 1. Use fewer letters, or runtoast cdwto browse. - stderr shows the workspace root, the current directory and the target. stdout receives only the path. A cancelled picker and the first-time setup print nothing to stdout.
- Candidates are directories below
~/workspaceonly. The search skips hidden directories and stops at a repository root, which has a.gitdirectory or, in a worktree or submodule, a.gitfile. Directories inside a repository are not candidates.
Benefits:
- Consistent project organization across all Git hosts
- Automatic detection of the Git host from the workspace path
- Git operations on any host, including nested groups
dot and prompt also work on any host whose directory contains a dot, such as
github.com or gitlab.example.com. They do not map nested groups: the project
root is always the third directory below ~/workspace. In
~/workspace/gitlab.clush.net/apps/cdp/be/ws, the project root is
~/workspace/gitlab.clush.net/apps/cdp, not the repository. Check the Project
line that both commands print before they read or write anything.
Configure custom GitHub hosts for different organizations by creating .toast-config files:
File location: ~/workspace/github.com/{org}/.toast-config
# For organization-specific hosts
echo "GITHUB_HOST=github.enterprise.com" > ~/workspace/github.com/myorg/.toast-config
# For custom SSH hosts (useful for different accounts)
echo "GITHUB_HOST=myorg-github.com" > ~/workspace/github.com/myorg/.toast-configExample SSH config (~/.ssh/config):
Host myorg-github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_myorg
Benefits:
- Different GitHub Enterprise hosts per organization
- Different SSH keys and accounts per organization
- Automatic host detection based on workspace location
- Seamless switching between GitHub accounts
toast dot manages .env.local and toast prompt manages .prompt.md. Both
use the same steps. The examples use toast dot.
-
Configure the env-store. Set the profile, bucket and region with the variables or the config file in Env-store (S3) Storage Paths. The first run in a terminal offers to create the config file. Check that the profile works:
aws sts get-caller-identity --profile {username}-admin. -
Check the target. Change to the project root or one of its subdirectories. Every command prints its target to stderr before it reads the env-store:
Command toast dot diff Project /home/me/workspace/github.com/opspresso/toast-cli Local /home/me/workspace/github.com/opspresso/toast-cli/.env.local Profile me-admin Region ap-northeast-2 S3 s3://env-store-123456789012/local/opspresso/toast-cli/env-local SSM /toast/local/opspresso/toast-cli/env-local (legacy, read-only)Make sure that
Projectis the repository you expect.Regionshowsnot setwhen no region is configured; SSM reads then fail.toast dot lsruns anywhere. It prints the bucket, profile, region and scope it reads, and lists the stored projects on stdout. -
Compare.
toast dot diffshows the differences and changes nothing. It masks.env.localvalues;toast prompt diffshows Markdown as is. When only one copy exists, it names the command that creates the other one. -
Run one direction.
toast dot upreplaces the S3 copy with the local file.toast dot downreplaces the local file with the newest env-store copy. Each asks for confirmation before it overwrites an existing copy.
toast dot without a command (sync) compares and then asks for the direction in
a picker. The picker needs a terminal. Without one, sync fails, changes nothing
and names the up or down command to run. When the copies are identical,
sync succeeds without a terminal.
When a command fails, the lines below the error give the cause and the next step: a directory outside a workspace project, a missing local or env-store copy, a missing bucket or region, or an AWS access error. A failed read changes nothing.
The dot and prompt plugins store files in the S3 env-store bucket. During
the transition from AWS SSM Parameter Store, reads check both backends and use
whichever copy is newest; writes always go to S3 (the bucket is the source of
truth), and SSM copies become stale and are harvested into S3 on the next
upload.
s3://env-store-{account-id}/local/{namespace}/{project}/env-local # .env.local files
s3://env-store-{account-id}/local/{namespace}/{project}/prompt-md # .prompt.md files
/toast/local/{namespace}/{project}/env-local # legacy SSM (read-only fallback)
/toast/local/{namespace}/{project}/prompt-md # legacy SSM (read-only fallback)
Run dot and prompt under ~/workspace/{host}/{org}/{project}. A call
from a subdirectory still uses the project root's file. ls works anywhere.
The namespace is {org} for github.com and {host}/{org} for other hosts:
| Project directory | S3 key |
|---|---|
~/workspace/github.com/opspresso/toast-cli |
local/opspresso/toast-cli/env-local |
~/workspace/gitlab.example.com/group/project |
local/gitlab.example.com/group/project/env-local |
The host must contain a dot. GitLab subgroups are not supported: the directory
after {org} is always the project root.
Both S3 and SSM reads must succeed before comparison or transfer. On an access, network, or region error, fix the reported cause and run the command again. Downloads replace the local file atomically with mode 0600, preserving UTF-8 content and line endings. Empty files count as existing files.
S3 objects are written with SSE-KMS encryption. All env-store access uses a dedicated AWS profile so it is decoupled from your current default profile.
Configuration — precedence: environment variable > config file > default.
Environment variables:
TOAST_ENV_STORE_PROFILE # default: {username}-admin
TOAST_ENV_STORE_BUCKET # default: env-store-{account-id of the profile}
TOAST_ENV_STORE_KMS_KEY # default: bucket/account default KMS key
TOAST_ENV_STORE_REGION # profile region, then AWS_REGION / AWS_DEFAULT_REGION; required for SSMThe profile defaults to your OS username + -admin, and the bucket defaults to
env-store- + the AWS account id of that profile (looked up via
aws sts get-caller-identity). The region resolves to the configured value, then
the profile's region, then AWS_REGION / AWS_DEFAULT_REGION. If none is set,
SSM reads fail. Set TOAST_ENV_STORE_REGION to the region holding your legacy
parameters; toast never assumes a different region to report them as absent.
Config file ~/.config/toast/config (KEY=VALUE format). On first run, if it
is missing, toast prompts for the values and saves them (interactive sessions
only):
ENV_STORE_BUCKET=env-store-{account-id}
ENV_STORE_PROFILE={username}-admin
ENV_STORE_KMS_KEY=
ENV_STORE_REGION=
- Create a file in
toast/plugins/ - Extend
BasePlugin - Implement required methods
- Set name and help variables
from toast import ui
from toast.plugins.base_plugin import BasePlugin
import click
class MyPlugin(BasePlugin):
name = "mycommand"
help = "Command description"
@classmethod
def get_arguments(cls, func):
func = click.option("--option", "-o", help="Option description")(func)
return func
@classmethod
def execute(cls, **kwargs):
option = kwargs.get("option")
ui.result(f"Executing with option: {option}")eval "$(toast shell-init zsh)" # bash: toast shell-init bash
alias t='toast'
alias c='toast cdw' # c [QUERY]: change to the selected directory
alias m='toast am' # AWS identity
alias x='toast ctx' # Kubernetes contexts
alias d='toast dot' # .env.local files
alias p='toast prompt' # .prompt.md files
alias e='toast env' # AWS profiles
alias g='toast git' # Git repositories
alias r='toast region' # AWS region
alias ssm='toast ssm' # SSM Parameter StoreAdd one line to your shell startup file:
eval "$(toast completion zsh)" # ~/.zshrc
eval "$(toast completion bash)" # ~/.bashrc
toast completion fish | source # ~/.config/fish/config.fishA program cannot change the directory of the shell that started it, so toast cdw
alone only prints a path. toast shell-init prints a toast shell function for
bash or zsh. Add one line to your shell startup file, then open a new terminal:
eval "$(toast shell-init zsh)" # ~/.zshrc
eval "$(toast shell-init bash)" # ~/.bashrctoast cdw # Pick a directory; the shell changes to it
toast cdw toast-cli # Change to the only match without a picker
dir="$(toast cdw api)" # Scripts and $(...) still receive the path- The function changes directory only for
toast cdwtyped in an interactive shell with stdout on the terminal. It passes the arguments totoast cdwunchanged, so search and selection work as described in Navigating the workspace. - Help, other commands, pipes, redirections,
$(...), scripts,command toastandpython -m toastrun the executable and print the path as before. - A cancelled picker or the first-time setup keeps the current directory and returns
0. A failed
toast cdw, or a directory that cannot be entered, keeps the current directory and returns nonzero. toast shell-initchanges no file. To remove the integration, delete theevalline from the startup file. To remove it from the current shell, rununset -f toast.- The integration supports bash and zsh. In other shells, change directory with
cd "$(toast cdw)".
A command writes its result to stdout: a path, a value, a table or JSON. It writes
status, warnings, errors, prompts and pickers to stderr. A pipe or $(...) therefore
receives only the result:
cd "$(toast cdw)"
token="$(toast ssm get /app/token --reveal)"
toast env prod | jq -r .Arndot diff, prompt diff and ssm diff write the diff to stdout as their result.
up, down and sync of dot and prompt write the preview of a change to stderr,
so their stdout stays empty.
The shell integration does not change this:
$(toast cdw) still receives the path and leaves the directory unchanged.
In a pipe, tables are tab-separated and never cut at the terminal width, and a diff
is never shortened. Set NO_COLOR to turn off colors.
| Exit status | Meaning |
|---|---|
| 0 | Success. A cancelled picker or a declined confirmation also returns 0 and changes nothing |
| 1 | The operation failed |
| 2 | The command line is invalid |
| 130 | Ctrl-C or end of input at a prompt |
Type to filter a list. Letters can be scattered, and spaces separate terms that must
all match. Use ↑/↓ to move, Enter to select, Ctrl-U to clear the filter and
Esc to quit. clean uses Space to select several rows.
A picker needs a terminal. In a script, name the value: toast ctx prod, toast env prod, toast region ap-northeast-2 or toast cdw github.com/acme/api. toast dot sync has no such form, so use
toast dot up or toast dot down.
ssm put and ssm diff take a value from stdin or a hidden prompt, which keeps it
out of shell history: pbpaste | toast ssm put /app/key. A pipe cannot answer a
confirmation, so add --yes to overwrite an existing parameter.
git REPO rm asks for a confirmation and names uncommitted changes and unpushed
commits. git REPO push --mirror asks for a confirmation and names the remote whose
refs it can replace or delete. Use --yes (-y) in scripts for these two commands
only; any other git command rejects --yes with exit status 2.
env manages static profiles in AWS_SHARED_CREDENTIALS_FILE (default
~/.aws/credentials). It requires both access key fields before replacing the
default credentials, removes a stale session token, and verifies the explicit
default profile. SSO/role configuration in ~/.aws/config is not copied.
ssm put requires permission to read the current parameter. A failed read stops
the write. To keep input out of shell history, use toast ssm and choose the
interactive create/update action; the value prompt hides input. --reveal
prints values literally, including markup-like text.
Git branch/pull/push/rm require an existing repository directory name. Invalid names and symlinks are rejected without rewriting the target. Mirror push uses the full workspace namespace and leaves existing remotes unchanged.
- Development: See CLAUDE.md for guidelines
- License: GNU GPL v3.0
- Contributing: Via GitHub repository
- Documentation: ARCHITECTURE.md and toast.sh