Skip to main content

Open Shell

Open Shell

A cross-platform shell whose pipeline carries JSON, not text.

Bash pipes text. PowerShell pipes .NET objects. Open Shell pipes JSON records — so every stage is structured, and the same pipeline works on Linux, macOS, and Windows.

oshell> ls -r . | where .size > 10kb | sort .size --desc | take 5

Status: 0.0.1 (pre-alpha). Python 3.12+, standard library only.

The PyPI name is open-shell-ai (openshell and open-shell are taken). The command you run is openshell.

pip install open-shell-ai
openshell --version

From this repo, without publishing:

python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/openshell -c 'help'
# or:  ./openshell.py -c 'help'

pip ships the core + basic commands. Extra and advanced commands are not on PyPI — they live on the website registry.

Quick start

# Interactive shell
openshell

# One pipeline, then exit
openshell -c 'ls'
openshell -c 'ls -r . | where .size > 10kb | sort .size --desc | take 5'
openshell -c 'help | select .name .summary'

# Force NDJSON (also the default when stdout is not a TTY)
openshell --json -c 'ls | select .name .size'

# Works inside bash / jq
openshell -c 'ls | select .name .size' | jq .name

On a terminal, the last stage renders a table. When piped, it writes one JSON object per line (NDJSON).

How the pipeline works

Each command yields records. Only sinks (to json, to table) print. | passes the record stream downstream. take 5 stops early, so ls -r /usr | take 3 does not walk the whole tree.

ls -r .          →  {name, path, is_dir, size, modified} …
     | where .size > 10kb
     | sort .size --desc
     | take 5
     | select .name .size
     | to table

Predicates use a leading dot for fields. Size units (10kb, 1mb) and =~ (regex) are built in:

where .size > 10mb
where .name =~ "\.py$"
where .is_dir

Errors are records, never raw tracebacks:

{"$t": "error", "code": "cmd.not_found", "message": "…", "hint": "…"}

Basic commands (ship with pip)

Command Usage What it does
pwd pwd Print the working directory
echo echo [TEXT …] Emit arguments as a record
ls ls [PATH …] [-r] [-a] [-l] List files as records
cd cd [PATH] Change the working directory
mkdir mkdir [-p] PATH … Create directories
cp cp [-r] SRC … DEST Copy files or directories
mv mv SRC … DEST Move or rename files
rm rm [-r] [-f] PATH … Remove files or directories
cat cat FILE … Read files as line records
less less [-n N] FILE First N lines (default 20)
head head [-n N] FILE First N lines (default 10)
tail tail [-n N] FILE Last N lines (default 10)
grep grep [-i] PATTERN [FILE …] Search files, or filter pipeline records
find find [PATH] [-name GLOB] [-type f|d] [-size SIZE] [-mtime DAYS] Search for files
ps ps [aux] List running processes
top / htop top [-n N] List processes by CPU usage
kill kill [-SIGNAL] PID … Terminate a process
df df [-h] [PATH …] Disk space usage
du du [-s] [-h] [PATH …] Directory disk usage
where where .FIELD [OP VALUE] Filter (>, <, ==, !=, >=, <=, =~)
select select .FIELD … Keep named fields
sort sort .FIELD [--desc] Sort (buffers the stream)
take take N First N records
substring substring .FIELD START [END] Slice a string field (Python indexes)
to to json|table [--compact] Render the stream
help help [NAME] List loaded commands as records
history history [N] Previously run commands (JSON log at ~/.openshell_history)
env env Dump settings loaded from ~/.openshell
ai ai PROMPT … First command: write and run a pipeline. Later stage: JSON answer
command command Same idea: every command as a record
reload reload Re-read every command *.py from disk
version version Version and runtime
search search [QUERY] List extras on the website registry
install install NAME Download an extra into ~/.config/oshell/command/
remove remove NAME Remove a website extra (not a built-in)

help itself is a source command, so this works:

openshell -c 'help | select .name .usage .origin'

ai reads ~/.openshell:

{
  "ai": "xai",
  "ai_key": "...",
  "ai_mode": "grok-4.6"
}

OpenRouter:

{
  "ai": "openrouter",
  "ai_key": "...",
  "ai_mode": "openrouter/auto"
}
openshell -c 'ai largest 3 files'
openshell -c 'ls | ai largest 3 files'

Website extras (not on PyPI)

Host the registry/ folder so this URL exists:

https://openshell.dev/registry/index.json

openshell -c 'search'
openshell -c 'install count'
openshell -c 'ls | count'
openshell -c 'remove count'

Until the site is live, use the copy in this repo:

export OSHELL_REGISTRY_URL="file://$PWD/registry"
openshell -c 'search | to table'
openshell -c 'install uniq'

Current extras in registry/:

Command Usage What it does
count count Count incoming records
uniq uniq [.FIELD] Drop consecutive duplicates

install writes ~/.config/oshell/command/<file>.py and checks the sha256 in registry/index.json. Built-ins cannot be removed.

Writing a command

One file per command. Drop it in command/ (basic, ships with pip) or registry/commands/ (website extra). The decorator is the name, not the filename: sort.py registers sort.

from openshell import Records, command

@command("greet", "Say hello as a record", "greet [NAME]", source=True)
def greet(_input: Records, args: list[str]) -> Records:
    yield {"hello": args[0] if args else "world"}

@greet.help
def greet_help():
    print("greet [NAME]")
    print("  Say hello as a JSON record. NAME defaults to world.")

Every command accepts --help (-h is left for real flags such as df -h). If you do not define a help function, Open Shell prints the summary, usage, and every option mentioned in the usage string. Define one with @fn.help to print your own text instead.

No registration step. Files starting with _ are skipped. A broken file is reported and skipped; it does not kill the shell.

User-local commands (no install):

~/.config/oshell/command/my_cmd.py

Or any directory on $OSHELL_COMMAND_PATH (separated by os.pathsep).

Load order, later wins on a name clash:

  1. <install>/oshell_command/*.py — basic set from pip
  2. ~/.config/oshell/command/*.py — website extras and your files
  3. $OSHELL_COMMAND_PATH

How commands load, and how @command / @fn.help hook into the registry:

flowchart TD
    Start(["openshell starts"]) --> Load[load_commands]
    Load --> Alias["Alias this file as the openshell module"]
    Alias --> Dirs["Walk command dirs: later wins"]

    subgraph search [Search path]
        Builtin["1. command/ or oshell_command/"]
        UserDir["2. ~/.config/oshell/command/"]
        Extra["3. OSHELL_COMMAND_PATH"]
        Builtin --> UserDir --> Extra
    end

    Dirs --> Builtin
    Extra --> Exists{"Directory exists?"}
    Exists -->|no| NextDir[Next directory]
    Exists -->|yes| Files["Each *.py, skip _*"]
    Files --> Exec[exec_module]

    subgraph hooks [Annotations hook on import]
        Decorate["@command name, summary, usage, source"]
        Decorate --> Register["COMMANDS name = Command"]
        Register --> Attach["Attach fn.help hook"]
        Attach --> HasHelp{"Custom help defined?"}
        HasHelp -->|"@fn.help"| Custom[Set help_fn]
        HasHelp -->|no| Empty[help_fn stays empty]
    end

    Exec --> Decorate
    Custom --> Origin["Set origin to filename"]
    Empty --> Origin
    Origin --> MoreFiles{"More files?"}
    MoreFiles -->|yes| Files
    MoreFiles -->|no| NextDir
    NextDir --> MoreDirs{"More directories?"}
    MoreDirs -->|yes| Exists
    MoreDirs -->|no| Ready(["COMMANDS ready"])

    Ready --> Pipe[run_pipeline]
    Pipe --> Found{"Command found?"}
    Found -->|no| Missing[cmd.not_found]
    Found -->|yes| HelpFlag{"args include --help?"}
    HelpFlag -->|yes| UseHelp{"help_fn set?"}
    UseHelp -->|yes| PrintCustom[Call custom help]
    UseHelp -->|no| PrintDefault[Print usage and options]
    HelpFlag -->|no| RunFn[Call cmd.fn]

CLI

openshell                 start the interactive shell
openshell -c PIPELINE     run one pipeline and exit
openshell --json -c ...   force NDJSON output
openshell --version
openshell --help
Variable Meaning
OSHELL_REGISTRY_URL Catalog root (default https://openshell.dev/registry)
OSHELL_COMMAND_PATH Extra command directories
NO_COLOR Disable ANSI color

Repository

openshell.py              core: registry, loader, pipeline, REPL
command/                  basic commands (shipped on PyPI)
registry/                 website extras (not in the wheel)
  index.json              catalog + sha256
  commands/*.py           one extra command per file
skills/                   design plan and build steps
image/                    banner

The repo folder is command/. It installs as oshell_command/ so it does not squat the name command in site-packages.

Publish

pip install build twine
rm -rf dist
python -m build
twine upload dist/*

Confirm extras stayed off the wheel:

unzip -l dist/open_shell_ai-*.whl | grep -E 'count|uniq|registry' || echo ok

Then upload registry/ to https://openshell.dev/registry.

License

Apache-2.0

Metadata

Release files for open-shell-ai 0.0.2

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

Source distribution (sdist)

Source distribution for open-shell-ai 0.0.2
File Size Uploaded Provenance Actions
open_shell_ai-0.0.2.tar.gz 39.7 kB Without provenance Details for open_shell_ai-0.0.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for open-shell-ai 0.0.2
File Interpreter ABI Platform Actions
open_shell_ai-0.0.2-py3-none-any.whl Python 3 none any Details for open_shell_ai-0.0.2-py3-none-any.whl

Total release size: 87.2 kB

Release files / open_shell_ai-0.0.2.tar.gz

Download URL open_shell_ai-0.0.2.tar.gz
Size 39.7 kB
Tags Source
SHA-256 checksum
How to use checksums
9017bcffc37028aeb400e4b2c4f3ed646e8a1e6f57fb06bf4b301153a60ddf19
BLAKE2b-256 checksum
How to use checksums
1ba3e407ed33c3e8116f8f2c88c4db3c70f8c713ccb71137ab016d7de3789ad4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.1

Release files / open_shell_ai-0.0.2-py3-none-any.whl

Download URL open_shell_ai-0.0.2-py3-none-any.whl
Size 47.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
71112d629a5dcb42473490aecbf0ef1af44002eeeae8b0aa2d29a2f652b118d2
BLAKE2b-256 checksum
How to use checksums
a1c81a8aabb102023df5507f3d02566a33aa80617d17eb0e1c812bc35cdb1e58
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.1

Security for open-shell-ai 0.0.2

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.0.2 This release

2 release files No provenance

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