> ## Documentation Index
> Fetch the complete documentation index at: https://mux.coder.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tool Hooks

> Block dangerous commands, lint after edits, and set up your environment

<Note>Tool hooks are experimental. Expect breaking changes while we iterate.</Note>

<Tip>
  **The easiest way to set up tool hooks is to ask Mux.** Just tell Mux what you want (e.g., "lint
  Python files after edits" or "block force pushes") and it will create the hook scripts for you.
</Tip>

Tool hooks let you run your own scripts before and after Mux tool executions.

## What do you want to do?

<CardGroup cols={3}>
  <Card title="Block dangerous commands" icon="shield" href="#block-dangerous-commands">
    Prevent force pushes, rm -rf, etc.
  </Card>

  <Card title="Lint after file edits" icon="check" href="#lint-after-file-edits">
    Run ruff, eslint, tsc after changes
  </Card>

  <Card title="Set up environment" icon="gear" href="#set-up-environment">
    direnv, nvm, virtualenv
  </Card>
</CardGroup>

***

## Block dangerous commands

Create `.mux/tool_pre` to validate commands before they run. Exit non-zero to block:

```bash theme={null}
#!/usr/bin/env bash
# .mux/tool_pre - runs before every tool

if [[ "$MUX_TOOL" == "bash" ]]; then
  script="$MUX_TOOL_INPUT_SCRIPT"

  if echo "$script" | grep -q 'push.*--force'; then
    echo "❌ Force push blocked" >&2
    exit 1
  fi

  if echo "$script" | grep -q 'rm -rf /'; then
    echo "❌ Dangerous rm blocked" >&2
    exit 1
  fi
fi

exit 0  # Allow tool to run
```

```bash theme={null}
chmod +x .mux/tool_pre
```

The agent sees your error message and can adjust its approach.

***

## Lint after file edits

Create `.mux/tool_post` to run validation after tools complete:

```bash theme={null}
#!/usr/bin/env bash
# .mux/tool_post - runs after every tool
set -euo pipefail

[[ "$MUX_TOOL" == file_edit_* ]] || exit 0
file="${MUX_TOOL_INPUT_FILE_PATH:-}"
[[ -n "$file" ]] || exit 0

case "$file" in
  *.py)
    ruff check "$file"
    ;;
  *.ts|*.tsx)
    npx tsc --noEmit "$file"
    ;;
esac
```

```bash theme={null}
chmod +x .mux/tool_post
```

<details>
  <summary>Minimal formatter example (Go)</summary>

  ```bash theme={null}
  #!/usr/bin/env bash
  # .mux/tool_post - runs after every tool
  set -euo pipefail

  [[ "$MUX_TOOL" == file_edit_* ]] || exit 0
  file="${MUX_TOOL_INPUT_FILE_PATH:-}"
  [[ "$file" == *.go ]] || exit 0

  gofmt -w "$file"
  ```
</details>

Lint errors appear in `hook_output` and the agent can fix them.

<Note>
  `hook_output` is only shown in the UI when the hook produces output. For a cleaner experience,
  only print output when the hook has an effect—e.g., skip "Formatted: file" messages if the file
  was already formatted.
</Note>

***

## Set up environment

Create `.mux/tool_env` to configure your shell environment. This file is **sourced** before every `bash` tool call:

```bash theme={null}
# .mux/tool_env - sourced before bash commands

# direnv
eval "$(direnv export bash 2>/dev/null)" || true

# nvm
# export NVM_DIR="$HOME/.nvm"
# [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh"

# Python virtualenv
# source .venv/bin/activate 2>/dev/null || true
```

<Note>
  Unlike hooks, `tool_env` doesn't need to be executable—it's sourced, not run. It only affects
  `bash` tools.
</Note>

***

## Reference

<Accordion title="Environment Variables">
  All hooks receive these environment variables:

  | Variable              | Description                                                      |
  | --------------------- | ---------------------------------------------------------------- |
  | `MUX_TOOL`            | Tool name: `bash`, `file_edit_replace_string`, `file_read`, etc. |
  | `MUX_WORKSPACE_ID`    | Current workspace identifier                                     |
  | `MUX_TOOL_INPUT_PATH` | Path to file containing full tool input (always set)             |

  Mux flattens the tool input into `MUX_TOOL_INPUT_<...>` environment variables (see the appendix below). Fields longer than 8KB are omitted—use `MUX_TOOL_INPUT_PATH` for full access.

  **Post-hook only (`tool_post`):**

  | Variable               | Description                              |
  | ---------------------- | ---------------------------------------- |
  | `MUX_TOOL_RESULT_PATH` | Path to file containing full tool result |

  Flattened result fields are available as `MUX_TOOL_RESULT_<...>`. Fields longer than 8KB are omitted—use `MUX_TOOL_RESULT_PATH` for full access.
</Accordion>

<Accordion title="Exit Codes">
  | Exit Code | `tool_pre` behavior                | `tool_post` behavior                  |
  | --------- | ---------------------------------- | ------------------------------------- |
  | `0`       | Tool executes normally             | Success, output shown to agent        |
  | Non-zero  | Tool blocked, error shown to agent | Failure, error shown in `hook_output` |
</Accordion>

<Accordion title="Hook Priority & Location">
  Mux searches for each hook file in this order:

  1. Project-level: `.mux/<hook>`
  2. User-level: `~/.mux/<hook>`

  This applies to `tool_pre`, `tool_post`, and `tool_env`.

  For SSH workspaces, hooks execute **on the remote machine**.
</Accordion>

<Accordion title="Timeouts">
  Hooks must complete within **10 seconds** or they're terminated. Long-running tools (builds, tests) don't count against this—only hook execution time.

  Keep hooks fast—if you need longer operations, consider running them asynchronously or in the background.
</Accordion>

<Accordion title="Comparison">
  | Feature        | `.mux/tool_pre`          | `.mux/tool_post`      | `.mux/tool_env`         |
  | -------------- | ------------------------ | --------------------- | ----------------------- |
  | **Purpose**    | Block dangerous commands | Lint/validate results | Environment setup       |
  | **Runs**       | Before tool              | After tool            | Sourced in bash shell   |
  | **Applies to** | All tools                | All tools             | `bash` tool only        |
  | **Use case**   | Block force-push         | Run ruff/eslint       | direnv, nvm, virtualenv |
</Accordion>

<Accordion title="Appendix: Tool input env vars (auto-generated)">
  Mux also provides flattened tool input env vars so hook scripts can stay compact.

  * Scalars become `MUX_TOOL_INPUT_<FIELD>`
  * Nested objects become `MUX_TOOL_INPUT_<PARENT>_<CHILD>`
  * Arrays also include `..._COUNT` and per-index variables like `..._<INDEX>`

  If a value is too large for the environment, it may be omitted (not set). Mux also caps the number of flattened env vars and array elements to keep hook execution reliable.

  <details>
    <summary>advisor (1)</summary>

    | Env var                   | JSON path  | Type   | Description |
    | ------------------------- | ---------- | ------ | ----------- |
    | `MUX_TOOL_INPUT_QUESTION` | `question` | string | —           |
  </details>

  <details>
    <summary>agent\_report (5)</summary>

    | Env var                                 | JSON path              | Type    | Description                                                                          |
    | --------------------------------------- | ---------------------- | ------- | ------------------------------------------------------------------------------------ |
    | `MUX_TOOL_INPUT_REPORT_MARKDOWN`        | `reportMarkdown`       | string  | —                                                                                    |
    | `MUX_TOOL_INPUT_REPORT_MARKDOWN_PATH`   | `reportMarkdownPath`   | string  | Path to the markdown report file, usually report.md in the workspace root            |
    | `MUX_TOOL_INPUT_STRUCTURED_OUTPUT`      | `structuredOutput`     | unknown | —                                                                                    |
    | `MUX_TOOL_INPUT_STRUCTURED_OUTPUT_PATH` | `structuredOutputPath` | string  | Path to a JSON file containing the structured output, usually structured-output.json |
    | `MUX_TOOL_INPUT_TITLE`                  | `title`                | string  | —                                                                                    |
  </details>

  <details>
    <summary>agent\_skill\_delete (4)</summary>

    | Env var                    | JSON path  | Type    | Description                                                                                                        |
    | -------------------------- | ---------- | ------- | ------------------------------------------------------------------------------------------------------------------ |
    | `MUX_TOOL_INPUT_CONFIRM`   | `confirm`  | boolean | Must be true to confirm deletion                                                                                   |
    | `MUX_TOOL_INPUT_FILE_PATH` | `filePath` | string  | Relative file path within the skill directory to delete. Required when target is 'file'                            |
    | `MUX_TOOL_INPUT_NAME`      | `name`     | string  | Skill name to delete                                                                                               |
    | `MUX_TOOL_INPUT_TARGET`    | `target`   | enum    | Deletion target: 'file' to delete a specific file, 'skill' to remove the entire skill directory (defaults to file) |
  </details>

  <details>
    <summary>agent\_skill\_list (1)</summary>

    | Env var                               | JSON path             | Type    | Description                                      |
    | ------------------------------------- | --------------------- | ------- | ------------------------------------------------ |
    | `MUX_TOOL_INPUT_INCLUDE_UNADVERTISED` | `includeUnadvertised` | boolean | When true, includes skills with advertise: false |
  </details>

  <details>
    <summary>agent\_skill\_read (1)</summary>

    | Env var               | JSON path | Type   | Description                                       |
    | --------------------- | --------- | ------ | ------------------------------------------------- |
    | `MUX_TOOL_INPUT_NAME` | `name`    | string | Skill name (directory name under the skills root) |
  </details>

  <details>
    <summary>agent\_skill\_read\_file (4)</summary>

    | Env var                    | JSON path  | Type   | Description                                                                    |
    | -------------------------- | ---------- | ------ | ------------------------------------------------------------------------------ |
    | `MUX_TOOL_INPUT_FILE_PATH` | `filePath` | string | Path to the file within the skill directory (relative)                         |
    | `MUX_TOOL_INPUT_LIMIT`     | `limit`    | number | Number of lines to return from offset (optional, returns all if not specified) |
    | `MUX_TOOL_INPUT_NAME`      | `name`     | string | Skill name (directory name under the skills root)                              |
    | `MUX_TOOL_INPUT_OFFSET`    | `offset`   | number | 1-based starting line number (optional, defaults to 1)                         |
  </details>

  <details>
    <summary>agent\_skill\_write (3)</summary>

    | Env var                    | JSON path  | Type   | Description                                                |
    | -------------------------- | ---------- | ------ | ---------------------------------------------------------- |
    | `MUX_TOOL_INPUT_CONTENT`   | `content`  | string | File content to write                                      |
    | `MUX_TOOL_INPUT_FILE_PATH` | `filePath` | string | Relative path within skill directory. Defaults to SKILL.md |
    | `MUX_TOOL_INPUT_NAME`      | `name`     | string | Skill name (directory name under the global skills root)   |
  </details>

  <details>
    <summary>analytics\_query (6)</summary>

    | Env var                         | JSON path         | Type   | Description                                                                          |
    | ------------------------------- | ----------------- | ------ | ------------------------------------------------------------------------------------ |
    | `MUX_TOOL_INPUT_SQL`            | `sql`             | string | DuckDB SQL query to execute                                                          |
    | `MUX_TOOL_INPUT_TITLE`          | `title`           | string | Optional chart title                                                                 |
    | `MUX_TOOL_INPUT_VISUALIZATION`  | `visualization`   | enum   | Optional visualization type for rendering the query result                           |
    | `MUX_TOOL_INPUT_X_AXIS`         | `x_axis`          | string | Optional column name for the visualization X axis                                    |
    | `MUX_TOOL_INPUT_Y_AXIS_<INDEX>` | `y_axis[<INDEX>]` | string | Optional column name(s) for the visualization Y axis                                 |
    | `MUX_TOOL_INPUT_Y_AXIS_COUNT`   | `y_axis.length`   | number | Number of elements in y\_axis (Optional column name(s) for the visualization Y axis) |
  </details>

  <details>
    <summary>ask\_user\_question (8)</summary>

    | Env var                                                        | JSON path                                         | Type    | Description                                        |
    | -------------------------------------------------------------- | ------------------------------------------------- | ------- | -------------------------------------------------- |
    | `MUX_TOOL_INPUT_ANSWERS_<KEY>`                                 | `answers[<KEY>]`                                  | string  | —                                                  |
    | `MUX_TOOL_INPUT_QUESTIONS_<INDEX>_HEADER`                      | `questions[<INDEX>].header`                       | string  | Short label shown in the UI (keep it concise)      |
    | `MUX_TOOL_INPUT_QUESTIONS_<INDEX>_MULTI_SELECT`                | `questions[<INDEX>].multiSelect`                  | boolean | —                                                  |
    | `MUX_TOOL_INPUT_QUESTIONS_<INDEX>_OPTIONS_<INDEX>_DESCRIPTION` | `questions[<INDEX>].options[<INDEX>].description` | string  | —                                                  |
    | `MUX_TOOL_INPUT_QUESTIONS_<INDEX>_OPTIONS_<INDEX>_LABEL`       | `questions[<INDEX>].options[<INDEX>].label`       | string  | —                                                  |
    | `MUX_TOOL_INPUT_QUESTIONS_<INDEX>_OPTIONS_COUNT`               | `questions[<INDEX>].options.length`               | number  | Number of elements in questions\[\<INDEX>].options |
    | `MUX_TOOL_INPUT_QUESTIONS_<INDEX>_QUESTION`                    | `questions[<INDEX>].question`                     | string  | —                                                  |
    | `MUX_TOOL_INPUT_QUESTIONS_COUNT`                               | `questions.length`                                | number  | Number of elements in questions                    |
  </details>

  <details>
    <summary>attach\_file (3)</summary>

    | Env var                     | JSON path   | Type   | Description                                                            |
    | --------------------------- | ----------- | ------ | ---------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_FILENAME`   | `filename`  | string | Optional filename override to present to the model.                    |
    | `MUX_TOOL_INPUT_MEDIA_TYPE` | `mediaType` | string | Optional media type override when the filename/extension is ambiguous. |
    | `MUX_TOOL_INPUT_PATH`       | `path`      | string | The path to the file to attach (absolute or relative)                  |
  </details>

  <details>
    <summary>bash (5)</summary>

    | Env var                            | JSON path           | Type    | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
    | ---------------------------------- | ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_DISPLAY_NAME`      | `display_name`      | string  | Human-readable name for the process (e.g., 'Dev Server', 'TypeCheck Watch'). Required for all bash invocations since any process can be sent to background.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
    | `MUX_TOOL_INPUT_MODEL_INTENT`      | `model_intent`      | string  | Optional. Short user-facing purpose for this command, shown next to the command in collapsed chat. Use a present-participle phrase in plain English, under 100 characters. Do not repeat the command or include duration, because Mux appends those. Examples: 'Running the unit tests', 'Checking repository state', 'Inspecting build output'.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
    | `MUX_TOOL_INPUT_RUN_IN_BACKGROUND` | `run_in_background` | boolean | Run this command in the background without blocking. Use for processes running >5s (dev servers, builds, file watchers). Do NOT use for quick commands (\<5s), interactive processes (no stdin support), or processes requiring real-time output (use foreground with larger timeout instead). Returns immediately with a taskId (bash:\<processId>) and backgroundProcessId. Read output with task\_await (returns only new output since last check). Terminate with task\_terminate using the taskId. List active tasks with task\_list. Process persists until timeout\_secs expires, terminated, or workspace is removed. For long-running tasks like builds or compilations, prefer background mode to continue productive work in parallel. Do not call task\_await in the same parallel tool-call batch; wait for the returned taskId first. When you actually need the output, read it with task\_await; do not poll task\_await just because the process is still running. |
    | `MUX_TOOL_INPUT_SCRIPT`            | `script`            | string  | The bash script/command to execute                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
    | `MUX_TOOL_INPUT_TIMEOUT_SECS`      | `timeout_secs`      | number  | Timeout in seconds. For foreground: max execution time before kill. For background: max lifetime before auto-termination. Start small and increase on retry; avoid large initial values to keep UX responsive                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
  </details>

  <details>
    <summary>bash\_background\_terminate (1)</summary>

    | Env var                     | JSON path    | Type   | Description                        |
    | --------------------------- | ------------ | ------ | ---------------------------------- |
    | `MUX_TOOL_INPUT_PROCESS_ID` | `process_id` | string | Background process ID to terminate |
  </details>

  <details>
    <summary>bash\_output (4)</summary>

    | Env var                         | JSON path        | Type    | Description                                                                                                                                                                                                                                                                                                                                                                                                     |
    | ------------------------------- | ---------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_FILTER`         | `filter`         | string  | Optional regex to filter output lines. By default, only matching lines are returned. When filter\_exclude is true, matching lines are excluded instead. Non-matching lines are permanently discarded and cannot be retrieved later.                                                                                                                                                                             |
    | `MUX_TOOL_INPUT_FILTER_EXCLUDE` | `filter_exclude` | boolean | When true, lines matching 'filter' are excluded instead of kept. Key behavior: excluded lines do NOT cause early return from timeout - waiting continues until non-excluded output arrives or process exits. Use to avoid busy polling on progress spam (e.g., filter='⏳\|waiting\|...' with filter\_exclude=true lets you set a long timeout and only wake on meaningful output). Requires 'filter' to be set. |
    | `MUX_TOOL_INPUT_PROCESS_ID`     | `process_id`     | string  | The ID of the background process to retrieve output from                                                                                                                                                                                                                                                                                                                                                        |
    | `MUX_TOOL_INPUT_TIMEOUT_SECS`   | `timeout_secs`   | number  | Seconds to wait for new output. If no output is immediately available and process is still running, blocks up to this duration. Returns early when output arrives or process exits. Only use long timeouts (>15s) when no other useful work can be done in parallel.                                                                                                                                            |
  </details>

  <details>
    <summary>code\_execution (1)</summary>

    | Env var               | JSON path | Type   | Description                                   |
    | --------------------- | --------- | ------ | --------------------------------------------- |
    | `MUX_TOOL_INPUT_CODE` | `code`    | string | JavaScript code to execute in the PTC sandbox |
  </details>

  <details>
    <summary>complete\_goal (2)</summary>

    | Env var                  | JSON path | Type   | Description                                                                                                                                                                                           |
    | ------------------------ | --------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_GOAL_ID` | `goalId`  | string | Optional optimistic-concurrency token. Pass the `goalId` returned by `get\_goal` to ensure the completion is rejected with a typed conflict error if the user clears or replaces the goal mid-stream. |
    | `MUX_TOOL_INPUT_SUMMARY` | `summary` | string | Required 1-2 sentence justification for completing the current goal.                                                                                                                                  |
  </details>

  <details>
    <summary>desktop\_click (3)</summary>

    | Env var                 | JSON path | Type   | Description                                       |
    | ----------------------- | --------- | ------ | ------------------------------------------------- |
    | `MUX_TOOL_INPUT_BUTTON` | `button`  | enum   | Optional mouse button to click. Defaults to left. |
    | `MUX_TOOL_INPUT_X`      | `x`       | number | Target X coordinate in screen pixels.             |
    | `MUX_TOOL_INPUT_Y`      | `y`       | number | Target Y coordinate in screen pixels.             |
  </details>

  <details>
    <summary>desktop\_double\_click (3)</summary>

    | Env var                 | JSON path | Type   | Description                                              |
    | ----------------------- | --------- | ------ | -------------------------------------------------------- |
    | `MUX_TOOL_INPUT_BUTTON` | `button`  | enum   | Optional mouse button to double-click. Defaults to left. |
    | `MUX_TOOL_INPUT_X`      | `x`       | number | Target X coordinate in screen pixels.                    |
    | `MUX_TOOL_INPUT_Y`      | `y`       | number | Target Y coordinate in screen pixels.                    |
  </details>

  <details>
    <summary>desktop\_drag (4)</summary>

    | Env var                  | JSON path | Type   | Description                             |
    | ------------------------ | --------- | ------ | --------------------------------------- |
    | `MUX_TOOL_INPUT_END_X`   | `endX`    | number | Ending X coordinate in screen pixels.   |
    | `MUX_TOOL_INPUT_END_Y`   | `endY`    | number | Ending Y coordinate in screen pixels.   |
    | `MUX_TOOL_INPUT_START_X` | `startX`  | number | Starting X coordinate in screen pixels. |
    | `MUX_TOOL_INPUT_START_Y` | `startY`  | number | Starting Y coordinate in screen pixels. |
  </details>

  <details>
    <summary>desktop\_key\_press (1)</summary>

    | Env var              | JSON path | Type   | Description                                     |
    | -------------------- | --------- | ------ | ----------------------------------------------- |
    | `MUX_TOOL_INPUT_KEY` | `key`     | string | Key or key combination to press on the desktop. |
  </details>

  <details>
    <summary>desktop\_move\_mouse (2)</summary>

    | Env var            | JSON path | Type   | Description                           |
    | ------------------ | --------- | ------ | ------------------------------------- |
    | `MUX_TOOL_INPUT_X` | `x`       | number | Target X coordinate in screen pixels. |
    | `MUX_TOOL_INPUT_Y` | `y`       | number | Target Y coordinate in screen pixels. |
  </details>

  <details>
    <summary>desktop\_screenshot (2)</summary>

    | Env var                        | JSON path      | Type   | Description                                                     |
    | ------------------------------ | -------------- | ------ | --------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_SCALED_HEIGHT` | `scaledHeight` | number | Optional scaled height hint in pixels for downstream consumers. |
    | `MUX_TOOL_INPUT_SCALED_WIDTH`  | `scaledWidth`  | number | Optional scaled width hint in pixels for downstream consumers.  |
  </details>

  <details>
    <summary>desktop\_scroll (4)</summary>

    | Env var                  | JSON path | Type   | Description                                 |
    | ------------------------ | --------- | ------ | ------------------------------------------- |
    | `MUX_TOOL_INPUT_DELTA_X` | `deltaX`  | number | Optional horizontal scroll delta in pixels. |
    | `MUX_TOOL_INPUT_DELTA_Y` | `deltaY`  | number | Vertical scroll delta in pixels.            |
    | `MUX_TOOL_INPUT_X`       | `x`       | number | Target X coordinate in screen pixels.       |
    | `MUX_TOOL_INPUT_Y`       | `y`       | number | Target Y coordinate in screen pixels.       |
  </details>

  <details>
    <summary>desktop\_type (1)</summary>

    | Env var               | JSON path | Type   | Description                                  |
    | --------------------- | --------- | ------ | -------------------------------------------- |
    | `MUX_TOOL_INPUT_TEXT` | `text`    | string | Text to type into the active desktop target. |
  </details>

  <details>
    <summary>file\_edit\_insert (4)</summary>

    | Env var                        | JSON path       | Type   | Description                                                                             |
    | ------------------------------ | --------------- | ------ | --------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_CONTENT`       | `content`       | string | The content to insert                                                                   |
    | `MUX_TOOL_INPUT_INSERT_AFTER`  | `insert_after`  | string | Anchor text to insert after. Content will be placed immediately after this substring.   |
    | `MUX_TOOL_INPUT_INSERT_BEFORE` | `insert_before` | string | Anchor text to insert before. Content will be placed immediately before this substring. |
    | `MUX_TOOL_INPUT_PATH`          | `path`          | string | Path to the file to edit (absolute or relative to the current workspace)                |
  </details>

  <details>
    <summary>file\_edit\_replace\_lines (7)</summary>

    | Env var                                 | JSON path                 | Type   | Description                                                                                                                                |
    | --------------------------------------- | ------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
    | `MUX_TOOL_INPUT_END_LINE`               | `end_line`                | number | 1-indexed end line (inclusive) to replace                                                                                                  |
    | `MUX_TOOL_INPUT_EXPECTED_LINES_<INDEX>` | `expected_lines[<INDEX>]` | string | Optional safety check. When provided, the current lines in the specified range must match exactly.                                         |
    | `MUX_TOOL_INPUT_EXPECTED_LINES_COUNT`   | `expected_lines.length`   | number | Number of elements in expected\_lines (Optional safety check. When provided, the current lines in the specified range must match exactly.) |
    | `MUX_TOOL_INPUT_NEW_LINES_<INDEX>`      | `new_lines[<INDEX>]`      | string | Replacement lines. Provide an empty array to delete the specified range.                                                                   |
    | `MUX_TOOL_INPUT_NEW_LINES_COUNT`        | `new_lines.length`        | number | Number of elements in new\_lines (Replacement lines. Provide an empty array to delete the specified range.)                                |
    | `MUX_TOOL_INPUT_PATH`                   | `path`                    | string | Path to the file to edit (absolute or relative to the current workspace)                                                                   |
    | `MUX_TOOL_INPUT_START_LINE`             | `start_line`              | number | 1-indexed start line (inclusive) to replace                                                                                                |
  </details>

  <details>
    <summary>file\_edit\_replace\_string (4)</summary>

    | Env var                        | JSON path       | Type   | Description                                                                                                                                           |
    | ------------------------------ | --------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_NEW_STRING`    | `new_string`    | string | The replacement text                                                                                                                                  |
    | `MUX_TOOL_INPUT_OLD_STRING`    | `old_string`    | string | The exact text to replace (must be unique in file if replace\_count is 1). Include enough context (indentation, surrounding lines) to make it unique. |
    | `MUX_TOOL_INPUT_PATH`          | `path`          | string | Path to the file to edit (absolute or relative to the current workspace)                                                                              |
    | `MUX_TOOL_INPUT_REPLACE_COUNT` | `replace_count` | number | Number of occurrences to replace (default: 1). Use -1 to replace all occurrences. If 1, old\_string must be unique in the file.                       |
  </details>

  <details>
    <summary>file\_read (3)</summary>

    | Env var                 | JSON path | Type   | Description                                                                    |
    | ----------------------- | --------- | ------ | ------------------------------------------------------------------------------ |
    | `MUX_TOOL_INPUT_LIMIT`  | `limit`   | number | Number of lines to return from offset (optional, returns all if not specified) |
    | `MUX_TOOL_INPUT_OFFSET` | `offset`  | number | 1-based starting line number (optional, defaults to 1)                         |
    | `MUX_TOOL_INPUT_PATH`   | `path`    | string | The path to the file to read (absolute or relative)                            |
  </details>

  <details>
    <summary>memory (11)</summary>

    | Env var                      | JSON path     | Type   | Description                                                                                     |
    | ---------------------------- | ------------- | ------ | ----------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_COMMAND`     | `command`     | enum   | The memory operation to perform.                                                                |
    | `MUX_TOOL_INPUT_FILE_TEXT`   | `file_text`   | string | create: full contents of the new file.                                                          |
    | `MUX_TOOL_INPUT_INSERT_LINE` | `insert_line` | number | insert: line number to insert after (0 = top of file).                                          |
    | `MUX_TOOL_INPUT_INSERT_TEXT` | `insert_text` | string | insert: text to insert.                                                                         |
    | `MUX_TOOL_INPUT_LIMIT`       | `limit`       | number | view on a file: number of lines to return from offset (optional).                               |
    | `MUX_TOOL_INPUT_NEW_PATH`    | `new_path`    | string | rename: new virtual path (same scope).                                                          |
    | `MUX_TOOL_INPUT_NEW_STR`     | `new_str`     | string | str\_replace: replacement text.                                                                 |
    | `MUX_TOOL_INPUT_OFFSET`      | `offset`      | number | view on a file: 1-based starting line number (optional).                                        |
    | `MUX_TOOL_INPUT_OLD_PATH`    | `old_path`    | string | rename: current virtual path.                                                                   |
    | `MUX_TOOL_INPUT_OLD_STR`     | `old_str`     | string | str\_replace: exact text to replace (must be unique in the file).                               |
    | `MUX_TOOL_INPUT_PATH`        | `path`        | string | Virtual memory path (e.g. /memories/global/notes.md). Required for every command except rename. |
  </details>

  <details>
    <summary>mux\_agents\_write (2)</summary>

    | Env var                      | JSON path    | Type    | Description                                                                            |
    | ---------------------------- | ------------ | ------- | -------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_CONFIRM`     | `confirm`    | boolean | Must be true to apply the write. The agent should ask the user for confirmation first. |
    | `MUX_TOOL_INPUT_NEW_CONTENT` | `newContent` | string  | The full new contents of the AGENTS.md file                                            |
  </details>

  <details>
    <summary>mux\_config\_read (3)</summary>

    | Env var                       | JSON path       | Type   | Description                                                                                                               |
    | ----------------------------- | --------------- | ------ | ------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_FILE`         | `file`          | enum   | Which configuration file to read                                                                                          |
    | `MUX_TOOL_INPUT_PATH_<INDEX>` | `path[<INDEX>]` | string | Optional path segments to read a specific nested value. If omitted, returns the full config.                              |
    | `MUX_TOOL_INPUT_PATH_COUNT`   | `path.length`   | number | Number of elements in path (Optional path segments to read a specific nested value. If omitted, returns the full config.) |
  </details>

  <details>
    <summary>mux\_config\_write (7)</summary>

    | Env var                                          | JSON path                           | Type    | Description                                                                   |
    | ------------------------------------------------ | ----------------------------------- | ------- | ----------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_CONFIRM`                         | `confirm`                           | boolean | Must be true to apply the write. Ask the user for confirmation first.         |
    | `MUX_TOOL_INPUT_FILE`                            | `file`                              | enum    | Which configuration file to write                                             |
    | `MUX_TOOL_INPUT_OPERATIONS_<INDEX>_OP`           | `operations[<INDEX>].op`            | literal | —                                                                             |
    | `MUX_TOOL_INPUT_OPERATIONS_<INDEX>_PATH_<INDEX>` | `operations[<INDEX>].path[<INDEX>]` | string  | —                                                                             |
    | `MUX_TOOL_INPUT_OPERATIONS_<INDEX>_PATH_COUNT`   | `operations[<INDEX>].path.length`   | number  | Number of elements in operations\[\<INDEX>].path                              |
    | `MUX_TOOL_INPUT_OPERATIONS_<INDEX>_VALUE`        | `operations[<INDEX>].value`         | unknown | —                                                                             |
    | `MUX_TOOL_INPUT_OPERATIONS_COUNT`                | `operations.length`                 | number  | Number of elements in operations (Operations to apply to the config document) |
  </details>

  <details>
    <summary>notify (2)</summary>

    | Env var                  | JSON path | Type   | Description                                                                                                 |
    | ------------------------ | --------- | ------ | ----------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_MESSAGE` | `message` | string | Optional notification body with more details (max 200 chars). Keep it brief - users may only see a preview. |
    | `MUX_TOOL_INPUT_TITLE`   | `title`   | string | Short notification title (max 64 chars). Should be concise and actionable.                                  |
  </details>

  <details>
    <summary>review\_pane\_update (4)</summary>

    | Env var                                | JSON path                | Type   | Description                                                                                                                                                                                                                           |
    | -------------------------------------- | ------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_HUNKS_<INDEX>_COMMENT` | `hunks[<INDEX>].comment` | string | Short note (\~1 sentence) telling the user what to look at and why.                                                                                                                                                                   |
    | `MUX_TOOL_INPUT_HUNKS_<INDEX>_PATH`    | `hunks[<INDEX>].path`    | string | Filter in `path[:range]` form, e.g. "src/foo.ts" or "src/foo.ts:42-58". Path is project-relative; use './' or '../' when the path must resolve from the current tool working directory. Range uses new-file line numbers (inclusive). |
    | `MUX_TOOL_INPUT_HUNKS_COUNT`           | `hunks.length`           | number | Number of elements in hunks (List of hunks to flag for review.)                                                                                                                                                                       |
    | `MUX_TOOL_INPUT_OPERATION`             | `operation`              | enum   | 'replace' overwrites the assisted set; 'add' appends to it.                                                                                                                                                                           |
  </details>

  <details>
    <summary>skills\_catalog\_read (3)</summary>

    | Env var                   | JSON path | Type   | Description                                                         |
    | ------------------------- | --------- | ------ | ------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_OWNER`    | `owner`   | string | GitHub owner from the search result (e.g. 'vercel-labs')            |
    | `MUX_TOOL_INPUT_REPO`     | `repo`    | string | GitHub repository name from the search result (e.g. 'agent-skills') |
    | `MUX_TOOL_INPUT_SKILL_ID` | `skillId` | string | Skill ID from the search result                                     |
  </details>

  <details>
    <summary>skills\_catalog\_search (2)</summary>

    | Env var                | JSON path | Type   | Description                                       |
    | ---------------------- | --------- | ------ | ------------------------------------------------- |
    | `MUX_TOOL_INPUT_LIMIT` | `limit`   | number | Maximum number of results to return (default: 10) |
    | `MUX_TOOL_INPUT_QUERY` | `query`   | string | Search query to find skills in the catalog        |
  </details>

  <details>
    <summary>task (11)</summary>

    | Env var                            | JSON path           | Type    | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
    | ---------------------------------- | ------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_AGENT_ID`          | `agentId`           | string  | —                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
    | `MUX_TOOL_INPUT_ISOLATION`         | `isolation`         | enum    | Workspace isolation for the sub-agent. "fork" (the default) runs it in an isolated copy of this workspace created from committed state. "none" runs it directly in this workspace's checkout, sharing the working tree (including uncommitted changes) and skipping the fork + init overhead. Use "none" only for read-only analysis (e.g. the explore agent) or when you instruct the sub-agent to avoid editing shared files, since it can otherwise modify the same files concurrently. Omit to fork. |
    | `MUX_TOOL_INPUT_MODEL`             | `model`             | string  | Optional model override for the sub-agent, parsed with the same alias logic as the UI (an alias or a full 'provider:model' string). Omit this unless the user explicitly instructed a specific model — by default the sub-agent inherits the parent's model. Do not assume any particular model is available.                                                                                                                                                                                            |
    | `MUX_TOOL_INPUT_N`                 | `n`                 | number  | Optional best-of count. Use n when several agents should try the same prompt independently. Mutually exclusive with variants; omit both for a single task. Only use grouped runs for sub-agents without interfering side effects, such as read-only agents like explore.                                                                                                                                                                                                                                 |
    | `MUX_TOOL_INPUT_PROMPT`            | `prompt`            | string  | —                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
    | `MUX_TOOL_INPUT_RUN_IN_BACKGROUND` | `run_in_background` | boolean | —                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
    | `MUX_TOOL_INPUT_SUBAGENT_TYPE`     | `subagent_type`     | string  | —                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
    | `MUX_TOOL_INPUT_THINKING`          | `thinking`          | string  | Optional thinking/reasoning-level override for the sub-agent. Accepts a level name (off, low, medium, high, xhigh, max) or a numeric index (resolved against the chosen model). Omit this unless the user explicitly instructed a specific thinking level — by default the sub-agent inherits the parent's thinking level.                                                                                                                                                                               |
    | `MUX_TOOL_INPUT_TITLE`             | `title`             | string  | —                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
    | `MUX_TOOL_INPUT_VARIANTS_<INDEX>`  | `variants[<INDEX>]` | string  | Optional labels for sibling runs of the same prompt template. Use variants when the task should be repeated across labeled lanes such as issue numbers, commit windows, or frontend/backend/tests/docs review lanes. Mutually exclusive with n. When provided, Mux launches one sibling per label and substitutes \${variant} in the prompt.                                                                                                                                                             |
    | `MUX_TOOL_INPUT_VARIANTS_COUNT`    | `variants.length`   | number  | Number of elements in variants (Optional labels for sibling runs of the same prompt template. Use variants when the task should be repeated across labeled lanes such as issue numbers, commit windows, or frontend/backend/tests/docs review lanes. Mutually exclusive with n. When provided, Mux launches one sibling per label and substitutes \${variant} in the prompt.)                                                                                                                            |
  </details>

  <details>
    <summary>task\_apply\_git\_patch (6)</summary>

    | Env var                            | JSON path           | Type    | Description                                                                                                                    |
    | ---------------------------------- | ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------ |
    | `MUX_TOOL_INPUT_DRY_RUN`           | `dry_run`           | boolean | When true, attempt to apply the patch in a temporary git worktree and then discard it (does not modify the current workspace). |
    | `MUX_TOOL_INPUT_EXPECTED_HEAD_SHA` | `expected_head_sha` | string  | When provided, refuse to apply unless the target repository HEAD matches this SHA.                                             |
    | `MUX_TOOL_INPUT_FORCE`             | `force`             | boolean | When true, allow apply even if the patch was previously applied (and skip clean-tree checks).                                  |
    | `MUX_TOOL_INPUT_PROJECT_PATH`      | `project_path`      | string  | When provided, apply only the patch artifact for this project path.                                                            |
    | `MUX_TOOL_INPUT_TASK_ID`           | `task_id`           | string  | Child task ID whose patch artifact should be applied                                                                           |
    | `MUX_TOOL_INPUT_THREE_WAY`         | `three_way`         | boolean | When true, run git am with --3way                                                                                              |
  </details>

  <details>
    <summary>task\_await (6)</summary>

    | Env var                           | JSON path           | Type    | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
    | --------------------------------- | ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_FILTER`           | `filter`            | string  | Optional regex to filter bash task output lines. By default, only matching lines are returned. When filter\_exclude is true, matching lines are excluded instead. Non-matching lines are discarded and cannot be retrieved later.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
    | `MUX_TOOL_INPUT_FILTER_EXCLUDE`   | `filter_exclude`    | boolean | When true, lines matching 'filter' are excluded instead of kept. Requires 'filter' to be set.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
    | `MUX_TOOL_INPUT_MIN_COMPLETED`    | `min_completed`     | number  | Number of awaited tasks that must complete before this call returns. Defaults to 1, so by default task\_await returns as soon as the FIRST awaited task completes, letting you act on it while the rest keep running. The result still includes every task complete at that moment plus current status (running/queued) for the rest. Tasks that have not yet completed keep running and remain re-awaitable on a later task\_await call. Raise this (e.g. set it to the total number of awaited tasks) when you genuinely need more before proceeding — for example best-of-N synthesis that must compare every candidate. Clamped to the number of awaited tasks; values above that behave like 'wait for all'. |
    | `MUX_TOOL_INPUT_TASK_IDS_<INDEX>` | `task_ids[<INDEX>]` | string  | List of task IDs or workflow run IDs to await — use only real IDs returned by prior task, bash, or workflow\_run results; never fabricate an ID. task\_list can rediscover sub-agent/background bash IDs, but top-level workflow run rediscovery is done by omitting task\_ids. When omitted, waits for active descendant tasks and top-level workflow runs of the current workspace, excluding workflow-owned sub-agents/background bash tasks and nested child workflow runs because those results are consumed through parent workflow runs.                                                                                                                                                                   |
    | `MUX_TOOL_INPUT_TASK_IDS_COUNT`   | `task_ids.length`   | number  | Number of elements in task\_ids (List of task IDs or workflow run IDs to await — use only real IDs returned by prior task, bash, or workflow\_run results; never fabricate an ID. task\_list can rediscover sub-agent/background bash IDs, but top-level workflow run rediscovery is done by omitting task\_ids. When omitted, waits for active descendant tasks and top-level workflow runs of the current workspace, excluding workflow-owned sub-agents/background bash tasks and nested child workflow runs because those results are consumed through parent workflow runs.)                                                                                                                                 |
    | `MUX_TOOL_INPUT_TIMEOUT_SECS`     | `timeout_secs`      | number  | Maximum time to wait in seconds for each task. For bash tasks, this waits for NEW output (or process exit). If exceeded, the result returns status=queued\|starting\|running\|awaiting\_report (task is still active). Defaults to 600 seconds (10 minutes) if not specified. Set to 0 for a non-blocking status check.                                                                                                                                                                                                                                                                                                                                                                                           |
  </details>

  <details>
    <summary>task\_list (2)</summary>

    | Env var                           | JSON path           | Type   | Description                                                                                                                                                                                                                                                    |
    | --------------------------------- | ------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_STATUSES_<INDEX>` | `statuses[<INDEX>]` | enum   | Task statuses to include. Defaults to active tasks: queued, starting, running, awaiting\_report, pending, backgrounded. Pass \['interrupted', 'failed'] to discover workflow runs that may be resumable via workflow\_resume.                                  |
    | `MUX_TOOL_INPUT_STATUSES_COUNT`   | `statuses.length`   | number | Number of elements in statuses (Task statuses to include. Defaults to active tasks: queued, starting, running, awaiting\_report, pending, backgrounded. Pass \['interrupted', 'failed'] to discover workflow runs that may be resumable via workflow\_resume.) |
  </details>

  <details>
    <summary>task\_terminate (2)</summary>

    | Env var                           | JSON path           | Type   | Description                                                                                                                                                                                                                                                                        |
    | --------------------------------- | ------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_TASK_IDS_<INDEX>` | `task_ids[<INDEX>]` | string | List of task IDs to terminate. Sub-agent task IDs and bash task IDs must belong to descendants of the current workspace; workflow run IDs (wfr\_...) must belong to the current workspace and are interrupted (resumable) rather than destroyed.                                   |
    | `MUX_TOOL_INPUT_TASK_IDS_COUNT`   | `task_ids.length`   | number | Number of elements in task\_ids (List of task IDs to terminate. Sub-agent task IDs and bash task IDs must belong to descendants of the current workspace; workflow run IDs (wfr\_...) must belong to the current workspace and are interrupted (resumable) rather than destroyed.) |
  </details>

  <details>
    <summary>todo\_write (3)</summary>

    | Env var                                | JSON path                | Type   | Description                                                                                                                   |
    | -------------------------------------- | ------------------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_TODOS_<INDEX>_CONTENT` | `todos[<INDEX>].content` | string | Task description with tense matching status: past for completed, present progressive for in\_progress, imperative for pending |
    | `MUX_TOOL_INPUT_TODOS_<INDEX>_STATUS`  | `todos[<INDEX>].status`  | enum   | Task status                                                                                                                   |
    | `MUX_TOOL_INPUT_TODOS_COUNT`           | `todos.length`           | number | Number of elements in todos                                                                                                   |
  </details>

  <details>
    <summary>web\_fetch (1)</summary>

    | Env var              | JSON path | Type   | Description                      |
    | -------------------- | --------- | ------ | -------------------------------- |
    | `MUX_TOOL_INPUT_URL` | `url`     | string | The URL to fetch (http or https) |
  </details>

  <details>
    <summary>workflow\_read (1)</summary>

    | Env var               | JSON path | Type   | Description |
    | --------------------- | --------- | ------ | ----------- |
    | `MUX_TOOL_INPUT_NAME` | `name`    | string | —           |
  </details>

  <details>
    <summary>workflow\_resume (3)</summary>

    | Env var                            | JSON path           | Type    | Description                                                                                                                                                                                                                                                       |
    | ---------------------------------- | ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_MODE`              | `mode`              | enum    | Defaults to 'resume', which continues interrupted or crash-orphaned runs from durable state and never re-executes completed steps. Use 'retry\_from\_checkpoint' only for failed runs; it re-executes work after the last checkpoint and is rejected when unsafe. |
    | `MUX_TOOL_INPUT_RUN_ID`            | `run_id`            | string  | Workflow run ID (wfr\_...) to resume. Must belong to the current workspace.                                                                                                                                                                                       |
    | `MUX_TOOL_INPUT_RUN_IN_BACKGROUND` | `run_in_background` | boolean | Defaults to false (foreground): waits until the run reaches a terminal status and returns its result. Set true to resume in the background and continue other work; await the runId with task\_await when you need the result.                                    |
  </details>

  <details>
    <summary>workflow\_run (3)</summary>

    | Env var                            | JSON path           | Type    | Description                                                                                                                                                                                                                                                                                                                                                                |
    | ---------------------------------- | ------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `MUX_TOOL_INPUT_ARGS`              | `args`              | unknown | —                                                                                                                                                                                                                                                                                                                                                                          |
    | `MUX_TOOL_INPUT_NAME`              | `name`              | string  | —                                                                                                                                                                                                                                                                                                                                                                          |
    | `MUX_TOOL_INPUT_RUN_IN_BACKGROUND` | `run_in_background` | boolean | Defaults to false. Prefer foreground mode for a single workflow; when the returned status is completed, the result is available directly. Set true only when you will start another workflow/task or do independent work while it runs. If workflow\_run returns status=running or status=backgrounded, await the returned runId with task\_await before using the result. |
  </details>
</Accordion>

***

## More examples

<Accordion title="Format files after edits">
  ```bash theme={null}
  #!/usr/bin/env bash
  # .mux/tool_post

  if [[ "$MUX_TOOL" == file_edit_* ]]; then
    file="$MUX_TOOL_INPUT_FILE_PATH"
    prettier --write --log-level silent "$file" 2>/dev/null || true
  fi
  ```
</Accordion>

<Accordion title="Log tool execution times">
  ```bash theme={null}
  #!/usr/bin/env bash
  # .mux/tool_post

  # Tool results are available via MUX_TOOL_RESULT_* if needed.
  echo "$(date '+%H:%M:%S') $MUX_TOOL completed" >> /tmp/mux-tools.log
  ```
</Accordion>

<Accordion title="Python hook">
  ```python theme={null}
  #!/usr/bin/env python3
  # .mux/tool_pre

  import os, sys

  tool = os.environ.get('MUX_TOOL', '')
  script = os.environ.get('MUX_TOOL_INPUT_SCRIPT', '')

  if tool == 'bash':
      if 'rm -rf /' in script:
          print("❌ Blocked dangerous command", file=sys.stderr)
          sys.exit(1)

  sys.exit(0)  # Allow
  ```
</Accordion>
