diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0df645b..aadd4cf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,6 +29,10 @@ 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" @@ -36,6 +40,9 @@ jobs: - 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' @@ -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 diff --git a/README.md b/README.md index 0995ae5..f907de4 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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> { + 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 @@ -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+ @@ -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 diff --git a/SKILL.md b/SKILL.md index 49028fe..4c8fd0b 100644 --- a/SKILL.md +++ b/SKILL.md @@ -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 @@ -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> { + 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 @@ -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`, @@ -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