# Execution Tools

Tools that run commands and manage packages.

| Tool | Description |
|---|---|
| `run_command` | Execute a binary with argv (argv-only schema, sandbox-wrapped via `go-pkg/sandbox`). A plain command with no shell metacharacter (`\|`, `&&`, `>`, `*`, `~`) is called directly, never wrapped; pipes, redirects and globbing need an explicit `['sh','-c','...']`, whose script is parsed and validated command-by-command — every binary must be a **bare command name**, and anything on `denied_command` is rejected outright. `['cd','<path>']` is special-cased and mutates `Executor.WorkDir` after verifying the path; `rm` is routed to the trash rather than deleting. Since v1.0.25 the sandbox keeps networking off by default: a command that has to reach a host (`git clone` / `fetch` / `pull` / `push`, `npm` / `pip` / `brew install`, `go mod download`, `curl`) sets `network: true`, and such a call never counts as read-only, so it always goes through the confirm gate. Commands on the read-only allowlist (`configs/jsons/read_only_command.json`, 410 entries, merged with `read_only_command` from `config.json`) skip the confirm gate: since v1.0.25 the binary plus up to two following arguments are matched (`git status`, `docker ps`, `kubectl get`, `ls`, ...), and a match is cancelled when any argument is a sensitive path. The dedicated read-only command tool added in v1.0.23 (`run_command_readonly`, which ran in parallel) was removed in v1.0.25, and the short-lived `run_script` never shipped in a release; use `run_command`, which always runs serially. To write outside `$HOME` the call carries `write_paths`, which are bound in only after a password-backed approval. `sudo` is rejected outright (bare or inside `sh -c`) with guidance to drop it and declare `write_paths`, which raises that sudo confirm instead. Since v1.0.11 a file watcher is rejected before it starts: `--watch` / `--watch=...`, a `chokidar` binary, or an `npm` / `pnpm` / `yarn` / `bun` script that resolves to one — the package script is parsed out of `package.json` and followed up to 5 levels, through `sh -c` too — because `run_command` waits for the process to exit and a watcher never does. The error names the one-shot build to run instead |
| `pkg_manage` | Drive the Linux package manager (apt / dnf / yum / pacman / apk) **outside** the sandbox, so the root operations `bwrap` cannot grant still work. `action` is `install` / `remove` / `update` / `upgrade` / `search` / `info`; `package` is a bare name — no flags, no version pin, no second package — and is required for everything except `update` and `upgrade`. Registered on Linux only. `run_command` cannot substitute: sudo is powerless inside `bwrap`. Language runtimes (node / python) → `run_command` with mise, fnm or uv; language-level packages (pip / npm / cargo) → `run_command` |
