Skip to content
opspressoPublic

About

Interactive Python CLI for AWS, Kubernetes, Git, and development workspace management.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

toast-cli

 _                  _           _ _
| |_ ___   __ _ ___| |_     ___| (_)
| __/ _ \ / _` / __| __|__ / __| | |
| || (_) | (_| \__ \ ||___| (__| | |
 \__\___/ \__,_|___/\__|   \___|_|_|

build release PyPI website

Python-based CLI utility with plugin architecture for AWS, Kubernetes, and Git operations.

Features

  • 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

Architecture

  • Commands implemented as plugins extending BasePlugin
  • Automatic plugin discovery and loading
  • Click integration for CLI behavior
  • See ARCHITECTURE.md for details

Installation

Requirements

  • Python 3.9+
  • External tools: git, aws-cli, kubectl (install those required by your commands)
  • Python packages: click, rich

From PyPI

pip install toast-cli

Or...

# 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 .

Usage

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 version

Disk cleanup

toast 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.

Examples

# 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 repository

Clone 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 clone

With 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.

Workspace Structure

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:

  1. Create ~/workspace directory if it doesn't exist
  2. Create ~/workspace/github.com as the default structure
  3. Display instructions for creating organization and project directories

You can then create your project directories:

mkdir -p ~/workspace/github.com/{org}/{project}

Navigating the workspace

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.
  • QUERY selects candidates in this order: the exact relative path, then directories whose last path components equal QUERY, 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. Without QUERY, the command fails and asks for one.
  • A QUERY that matches nothing fails with exit status 1. Use fewer letters, or run toast cdw to 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 ~/workspace only. The search skips hidden directories and stops at a repository root, which has a .git directory or, in a worktree or submodule, a .git file. 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.

Configuration

GitHub Host Configuration

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-config

Example 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

Environment and prompt files

toast dot manages .env.local and toast prompt manages .prompt.md. Both use the same steps. The examples use toast dot.

  1. 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.

  2. 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 Project is the repository you expect. Region shows not set when no region is configured; SSM reads then fail. toast dot ls runs anywhere. It prints the bucket, profile, region and scope it reads, and lists the stored projects on stdout.

  3. Compare. toast dot diff shows the differences and changes nothing. It masks .env.local values; toast prompt diff shows Markdown as is. When only one copy exists, it names the command that creates the other one.

  4. Run one direction. toast dot up replaces the S3 copy with the local file. toast dot down replaces 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.

Env-store (S3) Storage Paths

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 SSM

The 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=

Creating Plugins

  1. Create a file in toast/plugins/
  2. Extend BasePlugin
  3. Implement required methods
  4. 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}")

Aliases

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 Store

Shell completion

Add one line to your shell startup file:

eval "$(toast completion zsh)"    # ~/.zshrc
eval "$(toast completion bash)"   # ~/.bashrc
toast completion fish | source    # ~/.config/fish/config.fish

Changing directory with toast cdw

A 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)"   # ~/.bashrc
toast 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 cdw typed in an interactive shell with stdout on the terminal. It passes the arguments to toast cdw unchanged, so search and selection work as described in Navigating the workspace.
  • Help, other commands, pipes, redirections, $(...), scripts, command toast and python -m toast run 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-init changes no file. To remove the integration, delete the eval line from the startup file. To remove it from the current shell, run unset -f toast.
  • The integration supports bash and zsh. In other shells, change directory with cd "$(toast cdw)".

Output and exit status

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 .Arn

dot 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

Pickers

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.

Resources

About

Interactive Python CLI for AWS, Kubernetes, Git, and development workspace management.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages