Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,20 @@ jobs:
with:
ref: ${{ env.RELEASE_TAG }}

- run: |
rustup toolchain install stable --profile default
rustup default stable

- uses: actions/setup-node@v4
with:
node-version: "24"

- name: Verify release versions
run: node .github/scripts/release/verify-versions.mjs

- name: Verify shell-use crate package
run: cargo publish --locked --package shell-use --dry-run

build-cli:
needs: verify
if: github.event_name == 'push'
Expand Down Expand Up @@ -358,6 +365,37 @@ jobs:
with:
skip-existing: true

publish-crates:
needs:
- verify
- release
if: always() && needs.verify.result == 'success' && (needs.release.result == 'success' || github.event_name == 'workflow_dispatch')
runs-on: ubuntu-latest
environment:
name: crates-io
url: https://crates.io/crates/shell-use
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ env.RELEASE_TAG }}

- run: |
rustup toolchain install stable --profile default
rustup default stable

# Match this workflow and environment in the crate's trusted publisher settings.
- name: Authenticate with crates.io
id: auth
uses: rust-lang/crates-io-auth-action@v1

- name: Publish shell-use to crates.io
run: cargo publish --locked --package shell-use
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}

release:
needs:
- build-cli
Expand Down
47 changes: 42 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ shell-use wait exit

- `shell-use agent-context` prints versioned JSON for every command, flag, enum, default, and exit code. It is generated from the cli, so it cannot drift from the real surface.
- `shell-use usage` prints a one-screen cheatsheet.
- `shell-use skill` prints the full workflow guide ([SKILL.md](SKILL.md)).
- `shell-use skill` prints the full workflow guide ([SKILL.md](https://github.com/microsoft/shell-use/blob/main/SKILL.md)).

### Skill quick start

Expand All @@ -81,9 +81,46 @@ Each command returns a stable exit code (see [Exit codes](#exit-codes)), so an a

## Programmatic usage

`shell-use` python & node client libraries that drive shell-use with the same commands as the cli. The clients manage the sessions for you without a daemon.
`shell-use` provides a Rust library plus Python and Node client libraries. These libraries manage in-process sessions without the cli daemon.

### Python ([`shell-use`](bindings/python/README.md))
### Rust ([`shell-use`](https://crates.io/crates/shell-use))

```sh
cargo add shell-use
```

```rust
use shell_use::{OpenOptions, Operation, Session};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let session = Session::new(format!("rust-example-{}", std::process::id()));
session.open(OpenOptions::default())?;
session.execute(Operation::Submit {
data: Some("echo hello".into()),
})?;
session.execute(Operation::WaitCommand {
timeout_ms: Some(30_000),
})?;
session.execute(Operation::ExpectText {
text: "hello".into(),
regex: false,
full: false,
strict: false,
not: false,
fg: None,
bg: None,
timeout_ms: Some(5_000),
})?;
session.execute(Operation::ExpectExitCode {
code: 0,
timeout_ms: Some(5_000),
})?;
session.close()?;
Ok(())
}
```

### Python ([`shell-use`](https://github.com/microsoft/shell-use/blob/main/bindings/python/README.md))

```sh
pip install shell-use
Expand All @@ -104,7 +141,7 @@ async def main():
asyncio.run(main())
```

### Node ([`@microsoft/shell-use`](bindings/js/README.md))
### Node ([`@microsoft/shell-use`](https://github.com/microsoft/shell-use/blob/main/bindings/js/README.md))

```sh
npm install @microsoft/shell-use # Node 20+
Expand Down Expand Up @@ -282,7 +319,7 @@ re-fits the frame.
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `usage` | Compact command cheatsheet. |
| `agent-context` | Versioned JSON describing every command, flag, enum, default, and the exit-code taxonomy (generated from the cli, so it can't drift). |
| `skill` | Long-form workflow guide ([SKILL.md](SKILL.md)). |
| `skill` | Long-form workflow guide ([SKILL.md](https://github.com/microsoft/shell-use/blob/main/SKILL.md)). |

### Exit codes

Expand Down
72 changes: 55 additions & 17 deletions SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: shell-use
description: "Drive, inspect, assert on, record, and watch a real terminal from the command line with the shell-use cli. Use when running shells (bash, zsh, fish, PowerShell, pwsh, cmd, xonsh, elvish, nushell) or TUI programs (vim, less, top, etc.) in a headless PTY; sending keystrokes, key combos, or mouse input; resizing, writing raw bytes, or signaling the child; waiting for a command to finish or the screen to settle; asserting on terminal text, colors, exit codes, output, or snapshots; capturing text or full-color SVG screenshots; recording and replaying asciinema sessions; watching a live cli session while an agent drives it; or driving process-local sessions from Python or Node with the shell-use bindings."
description: "Drive, inspect, assert on, record, and watch a real terminal from the command line with the shell-use cli. Use when running shells (bash, zsh, fish, PowerShell, pwsh, cmd, xonsh, elvish, nushell) or TUI programs (vim, less, top, etc.) in a headless PTY; sending keystrokes, key combos, or mouse input; resizing, writing raw bytes, or signaling the child; waiting for a command to finish or the screen to settle; asserting on terminal text, colors, exit codes, output, or snapshots; capturing text or full-color SVG screenshots; recording and replaying asciinema sessions; watching a live cli session while an agent drives it; or driving process-local sessions from Rust, Python, or Node."
---

# shell-use
Expand Down Expand Up @@ -230,28 +230,62 @@ the commands the agent runs; resizing the window re-fits the frame.

This works only with standalone CLI sessions.

## Programmatic use (Python and JavaScript)
## Programmatic use (Rust, Python, and JavaScript)

The Python and JavaScript packages bind the Rust terminal engine directly and
run sessions in-process. Session names, registries, and recordings are
process-local. A native session cannot be listed, attached to, controlled, or
monitored from another process, including by the standalone CLI.
The Rust crate and the Python and JavaScript packages run the terminal engine
in-process. Session names, registries, and recordings are process-local. A
native session cannot be listed, attached to, controlled, or monitored from
another process, including by the standalone CLI.

Language packages do not install or require the `shell-use` CLI. Only the
standalone CLI uses the daemon and JSON-over-local-socket protocol described
elsewhere in this guide.
These programmatic APIs do not install or require the `shell-use` CLI. Only
the standalone CLI uses the daemon and JSON-over-local-socket protocol
described elsewhere in this guide.

Node is the supported JavaScript runtime. Bun and Deno compatibility is best
effort and does not gate releases. Deno requires a local `node_modules`
directory and `--allow-ffi` in addition to read/write permissions.

```sh
cargo add shell-use # Rust 1.88+
pip install shell-use # Python 3.8+, imported as `shell_use`
npm install @microsoft/shell-use # Node 20+ (ESM only)
bun add @microsoft/shell-use # Bun (best effort)
deno add npm:@microsoft/shell-use # Deno 2 (best effort)
```

Rust:

```rust
use shell_use::{OpenOptions, Operation, Session};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let session = Session::new(format!("rust-example-{}", std::process::id()));
session.open(OpenOptions::default())?;
session.execute(Operation::Submit {
data: Some("echo hello".into()),
})?;
session.execute(Operation::WaitCommand {
timeout_ms: Some(30_000),
})?;
session.execute(Operation::ExpectText {
text: "hello".into(),
regex: false,
full: false,
strict: false,
not: false,
fg: None,
bg: None,
timeout_ms: Some(5_000),
})?;
session.execute(Operation::ExpectExitCode {
code: 0,
timeout_ms: Some(5_000),
})?;
session.close()?;
Ok(())
}
```

Python:

```python
Expand Down Expand Up @@ -283,10 +317,14 @@ await su.expectExitCode(0);
await su.close();
```

Methods mirror the cli commands: `open` / `run`, `submit` / `type` / `write`,
`press` / `keys`, `mouse.click|move|down|up|drag|scroll`, `resize`, `signal` /
`kill`, `state`, `text`, `cells`, the dedicated `get_command` / `get_output` /
`get_exit_code` / `get_cwd` / `get_cursor` / `get_size` / `get_title` methods,
The Rust crate exposes `Session` and `SessionRegistry` for terminal ownership,
plus the `Operation` and `OperationResult` enums for the command surface.

Python and JavaScript methods mirror the cli commands: `open` / `run`, `submit`
/ `type` / `write`, `press` / `keys`, `mouse.click|move|down|up|drag|scroll`,
`resize`, `signal` / `kill`, `state`, `text`, `cells`, the dedicated
`get_command` / `get_output` / `get_exit_code` / `get_cwd` / `get_cursor` /
`get_size` / `get_title` methods,
`screenshot`, `wait_text` / `wait_idle` / `wait_command` / `wait_exit` /
`wait_ready`, `expect_text` / `expect_exit_code` / `expect_output` /
`expect_snapshot`, and `close`. Python module-level helpers are `sessions`,
Expand All @@ -300,10 +338,10 @@ The constructors accept a session name plus timeout and artifact options:
the program then its arguments (`await su.run("vim", "file.txt")` in Python,
`await su.run("vim", ["file.txt"])` in JavaScript).

Failures raise typed errors instead of returning exit codes, one class per row of
the applicable [exit-code table](#exit-codes): `ExpectationError` (1),
`UsageError` (2), `NoSessionError` (3), and `InternalError` (5), all subclasses
of `ShellUseError`.
Python and JavaScript failures raise typed errors instead of returning exit
codes, one class per row of the applicable [exit-code table](#exit-codes):
`ExpectationError` (1), `UsageError` (2), `NoSessionError` (3), and
`InternalError` (5), all subclasses of `ShellUseError`.

## Supported shells & integration

Expand Down
Loading