Skip to main content
shtick's start screen: the pixel $HTICK wordmark, version, shell and directory, the input bar and the key bar

✓ shtick

A playground for writing and testing shell scripts. Run shell code cell by cell in a persistent session, step through scripts, trace them, lint them, compare shells, sandbox them and assert on the results.

PyPI tests python license

See it in action

shtick running in a terminal: a loop with stdout and a red stderr line, %trace listing the commands a function ran, and a shellcheck warning in the key bar while typing

The name: sh + tick (✓, a passing check) — and a shtick is a routine you rehearse until it works.

Why shtick

  • Readable runs. Every cell is a block: your code, its output on a rail (stderr in red), and a footer with the exit status, time and directory. Variables, functions, cd and set options carry over between cells.
  • Know what a script does. Open a script and step through it command by command (%open, %next, %step), or %trace it to see every command that ran — with variables expanded — apart from the output.
  • Catch mistakes while typing. shellcheck lints the input as you type and puts the most important finding in the key bar. Typing tar -x shows what each flag means.
  • Try things without fear. %sandbox on runs cells in a throwaway directory and lists the files each cell created, changed or deleted.
  • Turn experiments into tests. %expect exit 0, %expect stdout contains "done", %test to re-run everything in a fresh session, %save-test to get a file that shtick test checks in CI.
  • Tight loops. %watch build.sh re-runs a script on every save; %break 42 stops %run at a line so you can look around.
  • Portable scripts. %compare bash dash zsh runs the same code in each shell side by side; on macOS, %compare /bin/bash bash shows what bash 3.2 does differently.

Install

curl -fsSL https://raw.githubusercontent.com/arthurdaquinosilva/shtick/main/install.sh | sh

The script puts shtick in its own virtual environment (~/.local/share/shtick/venv) and links the shtick command into ~/.local/bin, telling you if that isn't on your PATH. Run it again to upgrade; add -s -- 0.1.1 after sh for a specific version, or -s -- --uninstall to remove it (your config and history stay). Read it first if you like — it's short.

Or with a Python package manager:

pipx install shtick     # the `shtick` command everywhere, isolated from your projects
pip install shtick      # or into the current environment

To try the latest unreleased code: pipx install git+https://github.com/arthurdaquinosilva/shtick.git.

Then run shtick. Requires Python 3.10+ on macOS or Linux, and bash. shellcheck is optional but recommended (brew install shellcheck, apt install shellcheck); dash and zsh are used when installed.

Quick tour

> for f in *.log; do grep -c ERROR "$f"; done    # a cell: output, exit status, time, directory
> %open deploy.sh staging    # load a script ($1=staging) and list its commands
> %next                      # run the next one · %step: edit it first · %run: the rest
> %trace --next              # run the next command with every executed step listed
> %sandbox on --copy         # work in a temporary copy of this directory
> ./build.sh
> %expect exit 0             # check the last cell and record the check
> %expect file exists dist/app.tar.gz
> %test                      # re-run all cells in a fresh session, check expectations
> %save-test build.shtick    # later, in CI: shtick test build.shtick
> %watch build.sh            # run it again on every save · q stops
> %compare bash dash         # the previous cell in both shells, side by side
> %save build-steps.sh       # the cells that worked, as a script
> %help                      # every key and command
Enter run the cell (adds a newline while the code is unfinished — decided by the shell's own parser)
Shift+Enter · Alt+Enter · Ctrl+J insert a newline
Tab complete commands, files, $variables and %commands
→ · ↑ ↓ · Ctrl+R accept the grey suggestion · history · search history
Ctrl+O edit the cell in $EDITOR
Ctrl+C clear the input · interrupt a running cell (again: kill the session)
Ctrl+D exit · end a running cell's stdin

vi mode: shtick --vi, or %vi --save to keep it. Esc for normal mode, Enter runs from normal mode, j/k history, / search, v opens $EDITOR.

Shift+Enter needs a terminal that reports modified keys (iTerm2, WezTerm, Ghostty, kitty, xterm; inside tmux set extended-keys on). Alt+Enter and Ctrl+J work everywhere.

Documentation

Guide What's inside
Features Cells and the session, typing, scripts, tracing, linting, the sandbox, expectations and tests, portability
Command reference Every %command with its options (generated from the code)
Configuration config.toml, themes, profiles, command-line options, test files
How it works The session engine: how cells run, finish, read input and get interrupted
Releasing How versions are published to PyPI

Command line

shtick                          # interactive, in bash
shtick --shell dash             # or sh, zsh, a path like /bin/bash
shtick deploy.sh staging        # open a script to step through
shtick -c 'echo hi; false'      # run code as a cell, print the block, exit with its status
shtick test *.shtick            # run saved tests; exit status 1 if any check fails (--lint: shellcheck too)
shtick --vi --theme phosphor --profile work

Development

git clone https://github.com/arthurdaquinosilva/shtick.git && cd shtick
python -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/python -m pytest                     # engine, commands, and the UI in a pseudo-terminal
.venv/bin/python scripts/gen_command_docs.py   # regenerate docs/commands.md
.venv/bin/python scripts/screenshot.py         # re-record docs/assets/*.svg from a real session

shtick is built on prompt_toolkit (input and layout), Rich (output) and Pygments (highlighting). It's the sibling of ember, an interactive Python shell.

Limits

  • Not a login shell. shtick is for writing and testing scripts, not for replacing zsh or bash day to day. There's no job control (fg, bg, Ctrl+Z).
  • No full-screen programs in cells. vim, htop, less or an ssh session need a real terminal; run them outside shtick. %tty on helps programs that only check whether they're on a terminal (colors, [ -t 1 ]), at the cost of merging stderr into stdout.
  • Cells run non-interactively, like scripts: read -p prompts aren't printed unless %tty on, no .bashrc or .zshrc is read — set aliases = "auto" in the config to bring your aliases in.
  • exit, exec and a failing command under set -e end the session. shtick says so and starts a new one in the same directory, but variables and functions are gone.
  • The sandbox is a working directory, not a security boundary: absolute paths, cd .. and $HOME reach the real filesystem (the footer warns when a cell leaves it).
  • Linting needs shellcheck, which doesn't support zsh.

License

MIT © Arthur D'Aquino

Metadata

Release files for shtick 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for shtick 0.1.3
File Size Uploaded
shtick-0.1.3.tar.gz 91.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for shtick 0.1.3
File Interpreter ABI Platform
shtick-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 177.7 kB

Release files / shtick-0.1.3.tar.gz

Download URL shtick-0.1.3.tar.gz
Size 91.4 kB
Tags Source
SHA-256 checksum
How to use checksums
01d5ed6d9884253b93967e371c2650bc291d686073e0b3bebf79e2e34fca9dc4
BLAKE2b-256 checksum
How to use checksums
1c55d15a7a6c48637016b9898a5bb7aedb0e20e8a4308f045b38d1ab46336689
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / shtick-0.1.3-py3-none-any.whl

Download URL shtick-0.1.3-py3-none-any.whl
Size 86.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3577dc07b405bb760c5cac1cab820beacfdc9accfd24a9e133a5f9f1073ad5fa
BLAKE2b-256 checksum
How to use checksums
0243d1969ea9225c4c980d6e91ef82a484ec5076e5d05dcbec02e719b9239876
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.5

2 release files

0.1.4

2 release files

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page