A shell for the 21st century.

Unix pipes move text, and every pipeline hides a small parser that breaks silently. meersh passes typed records instead, makes language models ordinary pipeline stages, and checks every action against who wrote it, what it touches and whether it can be undone. Written in Rust, for Linux, local models first.

meersh in three minutes

A narrated tour of the real shell in a made-up project. Watch records flow through pipes, a question mark turn plain words into a pipeline, and a model plan a deletion that waits for your approval and can be undone. Then see the permission model decide what you, a model and a coding agent may do, and the kernel hold a program to the folder you allowed.

Objects, not text

PowerShell showed that a shell can pass objects. meersh does it on Linux, with plain data that survives a pipe, a file or a language model.

Typed records

ls returns fs.entry records with a filesize and a date, ps returns proc records. where size > 10mb needs no parsing, and ps | where cpu > 50 | kill needs no xargs.

Objects with methods

$f.delete(), $f.hash(), $p.ports(). Every object carries its identity, so a stale listing can never hit a different file or a reused pid. help fs.entry lists fields and methods.

Legacy tools become records

Adapters turn ip, ss, systemctl, journalctl, git and more into tables when a builtin reads them. Rust tools can send real objects across the process boundary.

meersh
〉ls | where size > 1kb | sort-by size -r
╭───┬───────────────┬──────┬─────────┬─────────────────────╮
│ # │ name          │ type │ size    │ modified            │
├───┼───────────────┼──────┼─────────┼─────────────────────┤
│ 0 │ build-old.bin │ file │ 6.7 MiB │ 2026-10-29 23:00:00 │
│ 1 │ dump.sql      │ file │ 2.0 MiB │ 2026-10-29 23:00:00 │
│ 2 │ README.md     │ file │ 1.2 KiB │ 2026-10-29 23:00:00 │
╰───┴───────────────┴──────┴─────────┴─────────────────────╯

Language models as pipeline stages

A model can write a pipeline, work on data, or act on objects. It never gets more than the role needs, and local models through Ollama are the default.

Transform data

llm map, llm where, llm parse, llm rank, llm group and llm join take a meersh type, turn it into a JSON Schema and return typed values. Rows go in batches, answers are cached, and unsure rows can cascade to a stronger model.

Act on objects

ls | llm "delete all files larger than 5mb" shows the model only the type and its methods, never the files. It writes a pipeline over the input, meersh checks that it can only touch what was piped in, and plans it.

Write pipelines

? the three biggest files here turns a request into a pipeline. The planner sees the request and the types, not the data, so text inside your files cannot steer it. This follows the CaMeL design against prompt injection.

meersh, with qwen3:4b on Ollama
〉ls | llm "delete all files that are larger than 5mb"

  where size > 5mb and type == file | each { $it.delete() }
  # actor wrote this. The instruction asks to delete all files larger than 5mb. The pipeline first filters the table for rows where the size is greater than 5mb and the type is a file. Then, it applies the delete operation to each matching file.

╭───┬───────────┬────────┬───────────────┬────────┬─────────┬────────────┬──────────┬──────────────────╮
│ # │ principal │ action │ target        │ detail │ size    │ reversible │ decision │ because          │
├───┼───────────┼────────┼───────────────┼────────┼─────────┼────────────┼──────────┼──────────────────┤
│ 0 │ actor     │ trash  │ build-old.bin │        │ 6.7 MiB │ true       │ ask      │ not created by … │
╰───┴───────────┴────────┴───────────────┴────────┴─────────┴────────────┴──────────┴──────────────────╯
  1 actions, 6.7 MiB, all reversible. 1 need your approval, 0 refused.
  not applied. Run interactively, or add --approve or --approve-only <rows>.
error: nothing was changed

A real run with meersh -c. The model saw table<fs.entry> and its methods, nothing else. Without a terminal to ask, meersh applies nothing. In the shell the same plan waits for your answer, for all rows or only some.

Every action has an author, a target and a right

rm out/demo.o right after creating it is harmless. rm ~/thesis.tex is a disaster. The danger is in the target and in who decided, so that is what meersh checks.

Remove what you created

A ledger records what every session and agent created. A model may change or delete what it created itself and nothing else. Ownership lapses as soon as someone else edits the file.

Plan first, undo later

Anything that writes, deletes or calls a model is planned before it runs. You approve all of it, some rows, or a narrow rule. Every action is journaled with its inverse, and undo takes it back.

Sandboxes and Landlock

sandbox { ^npm install } runs a program in an overlay and shows what it would change. with {fs.write: ./out, net: none} { ^make } lets the kernel enforce the limits.

meersh
〉policy explain fs.delete dump.sql --principal actor | select principal decision because
<policy.decision>
principal  actor
 decision  ask
  because  not created by this session, can be restored from the trash
〉touch draft.txt; rm draft.txt; undo | select action target result
╭───┬───────────┬─────────╮
│ # │ path      │ action  │
├───┼───────────┼─────────┤
│ 0 │ draft.txt │ created │
╰───┴───────────┴─────────╯
╭───┬───────────┬─────────┬──────╮
│ # │ path      │ action  │ size │
├───┼───────────┼─────────┼──────┤
│ 0 │ draft.txt │ trashed │  0 B │
╰───┴───────────┴─────────┴──────╯
╭───┬────────┬───────────┬─────────────────────────╮
│ # │ action │ target    │ result                  │
├───┼────────┼───────────┼─────────────────────────┤
│ 0 │ trash  │ draft.txt │ restored from the trash │
╰───┴────────┴───────────┴─────────────────────────╯

Five roles, each with its own rights

Unix asks only which user runs a program, and every program of that user may delete every file of that user. meersh also asks who wrote the command. A line you typed, a script, the assistant, a model and a coding agent each run in a role of their own, and each role has its own rights.

user

You

A line you type may do everything Unix allows. Protected paths such as /etc, ~/.ssh and .git ask first. So does a target that a model picked in your pipeline, as in ls | llm where "junk" | rm.

script:<path>

Your scripts

A script file you run has your rights. The journal records each of its actions under the script's name, so you can see what it did and take it back.

plan

The assistant

? request has a model write a pipeline. You see it with an explanation, and nothing runs before you say yes.

actor

A model at work

In ls | llm "delete ..." the model may only filter the objects it was handed and call their methods. You approve the rows, at most 50 per plan.

agent:<name>

Coding agents

Claude Code and other agents work through meersh mcp. Each keeps what it created across sessions and leases the files it edits. Whatever needs you comes back unapplied.

What each role may do

ActionYou and your scriptsAssistant, models and agents
Create a new file in the project allow allow
Change, move or delete what it created itself allow allow
Change or delete any other file in the project allow askThe old content goes to a snapshot or the trash.
Delete someone else's file for good, when neither the trash nor git can bring it back allow deny
Change or delete files outside the project allow deny
Touch a protected path. These are /, your home directory, .git, ~/.ssh and the system directories. ask deny
Stop a process allow allowIf it started the process. Otherwise it asks.
Run an external program allow askThe program runs in an overlay that shows its changes before they are kept.
Send data to a cloud model allow askA local model is allowed.
Change policies, undo actions or switch the model backend allow never

These are the defaults. Rules such as policy allow actor fs.delete owned change them, and only you can write rules. Everything a model does runs as a plan that you approve as a whole, row by row or as a narrow rule. policy explain shows which rule or default decides.

meersh
〉[user plan actor agent:claude] | each { policy explain fs.delete dump.sql -p $it } | select principal decision because
╭───┬──────────────┬──────────┬─────────────────────────────────────────────────────────────╮
│ # │ principal    │ decision │ because                                                     │
├───┼──────────────┼──────────┼─────────────────────────────────────────────────────────────┤
│ 0 │ user         │ allow    │                                                             │
│ 1 │ plan         │ ask      │ not created by this session, can be restored from the trash │
│ 2 │ actor        │ ask      │ not created by this session, can be restored from the trash │
│ 3 │ agent:claude │ ask      │ not created by this session, can be restored from the trash │
╰───┴──────────────┴──────────┴─────────────────────────────────────────────────────────────╯
〉[README.md /etc/hostname] | each { policy explain fs.write $it -p agent:claude } | select target decision because
╭───┬───────────────┬──────────┬────────────────────────────────────────────────────────────────────╮
│ # │ target        │ decision │ because                                                            │
├───┼───────────────┼──────────┼────────────────────────────────────────────────────────────────────┤
│ 0 │ README.md     │ ask      │ not created by this session, a snapshot of the old content is kept │
│ 1 │ /etc/hostname │ deny     │ protected, a system directory (/etc)                               │
╰───┴───────────────┴──────────┴────────────────────────────────────────────────────────────────────╯
claude mcp add --scope user meersh -- ~/.local/bin/meersh mcp --agent claude --unique

Get started

One command installs meersh on any Linux. For the model features, a local Ollama or a logged in Claude Code is enough.

1. Install

One binary for the shell and one multi call binary for the tools, so they serve bash users with text too. Both are linked statically and run on every distribution on x86_64 and ARM64. The script puts them in ~/.local/bin, or in /usr/local/bin as root. Running it again updates meersh.

curl -fsSL https://github.com/ribalba/meersh/releases/latest/download/install.sh | sh
Debian, Ubuntu .deb x86_64 .deb ARM64
Fedora, openSUSE .rpm x86_64 .rpm ARM64
Any Linux .tar.gz x86_64 .tar.gz ARM64

Every version and the checksums are on the releases page. To build from source you need a Rust toolchain. Run make install PREFIX=~/.local in a clone of the repository.

2. Pick your models

Without a config meersh uses Ollama on this machine. Claude Code and any OpenAI compatible server are one section of config.toml each. The installer puts an example in ~/.local/share/doc/meersh, the packages in /usr/share/doc/meersh.

cd ~/.local/share/doc/meersh
cp config.example.toml \
  ~/.config/meersh/config.toml
llm backends

3. Try it

Tab completes commands, flags and the columns of the data flowing in. help is a table like everything else.

meersh
ps | sort-by cpu -r | first 5
help | where effect == destructive

4. Read the design

CONCEPT.md explains the object model and what meersh takes from Nushell, PowerShell and uutils. AUTHORITY.md explains principals, ownership, plans and models that act on objects.