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

# Advanced Integration Recipes for macOS

> Copy-and-paste macOS commands to trigger a Proximate cursor icon from Docker, FFmpeg, pytest, Gradle, n8n, Claude Code, and more.

Once you've created an integration, you get a hook command you can drop into any tool that runs a command when it finishes. This page has a ready-to-use recipe for each tool in the integration library, written for **macOS**.

<Note>
  This page covers **macOS**. Running Proximate on Windows? See [Advanced Integration Recipes for Windows](/advanced-integrations/recipes-windows) — the commands are different.
</Note>

## Before You Start

Create an integration in Proximate first (see the [Setup guide](/advanced-integrations/setup)). With the platform toggle set to **macOS**, copy the **Success** command — it looks like this:

```bash theme={null}
open "proximate://notify?source=YOUR_ID&status=success"
```

Everywhere you see `YOUR_ID` below, use the ID from your own copied command. The status at the end of the URL (`success`, `warning`, `error`, or none) sets the badge color.

## Two Patterns You'll Reuse

Most command-line tools don't have a built-in "when I'm done" hook, so you run the Proximate command right after them. There are two ways to do it.

**Just tell me when it's done** — chain with `&&`:

```bash theme={null}
your-command && open "proximate://notify?source=YOUR_ID&status=success"
```

**Tell me whether it passed or failed** — wrap it in `if` so a failure shows a red icon:

```bash theme={null}
if your-command; then
  open "proximate://notify?source=YOUR_ID&status=success"
else
  open "proximate://notify?source=YOUR_ID&status=error"
fi
```

The recipes below plug each tool into one of these patterns. A few tools have a smarter, native hook — those are called out individually.

## Claude Code CLI

Claude Code has a native **Stop** hook that fires whenever Claude finishes responding. The short version: add this to `~/.claude/settings.json`.

```json ~/.claude/settings.json theme={null}
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "open \"proximate://notify?source=YOUR_ID&status=success\"",
            "async": true
          }
        ]
      }
    ]
  }
}
```

<Card title="Full Claude Code walkthrough" icon="code" href="/advanced-integrations/claude-code-example">
  See the complete step-by-step guide, including how to verify the hook with `/hooks`.
</Card>

## Docker CLI

Docker has no completion hook, so chain the command after a long `build` or `compose up`:

```bash theme={null}
if docker build -t myapp .; then
  open "proximate://notify?source=YOUR_ID&status=success"
else
  open "proximate://notify?source=YOUR_ID&status=error"
fi
```

The same pattern works for `docker compose up --build`, `docker push`, or any other long-running Docker command.

## FFmpeg

Get an icon the moment a long encode finishes:

```bash theme={null}
if ffmpeg -i input.mov -c:v libx264 output.mp4; then
  open "proximate://notify?source=YOUR_ID&status=success"
else
  open "proximate://notify?source=YOUR_ID&status=error"
fi
```

## Gradle

Chain the command after your Gradle task:

```bash theme={null}
if ./gradlew build; then
  open "proximate://notify?source=YOUR_ID&status=success"
else
  open "proximate://notify?source=YOUR_ID&status=error"
fi
```

<Warning>
  Avoid the old `gradle.buildFinished { … }` listener — it's deprecated (since Gradle 7.4) and doesn't work with the configuration cache. Chaining the command in the shell is simpler and future-proof.
</Warning>

## Homebrew

Long `brew upgrade` runs are a perfect fit:

```bash theme={null}
brew upgrade && open "proximate://notify?source=YOUR_ID&status=success"
```

## Jest

The quick way is to chain it after your test command:

```bash theme={null}
jest && open "proximate://notify?source=YOUR_ID&status=success" \
     || open "proximate://notify?source=YOUR_ID&status=error"
```

For a cleaner setup that always reports the right color, add a small **custom reporter**. Save this as `proximate-reporter.js` in your project:

```javascript proximate-reporter.js theme={null}
const { execSync } = require('child_process');

class ProximateReporter {
  onRunComplete(contexts, results) {
    const status = results.success ? 'success' : 'error';
    execSync(`open "proximate://notify?source=YOUR_ID&status=${status}"`);
  }
}

module.exports = ProximateReporter;
```

Then register it in `jest.config.js`:

```javascript jest.config.js theme={null}
module.exports = {
  reporters: ['default', '<rootDir>/proximate-reporter.js'],
};
```

## make

Chain the command after your build:

```bash theme={null}
make && open "proximate://notify?source=YOUR_ID&status=success"
```

Or bake it into your `Makefile` as the last step of a target:

```makefile theme={null}
build:
	# ... your build steps ...
	@open "proximate://notify?source=YOUR_ID&status=success"
```

## n8n (Local)

In a self-hosted n8n, add an **Execute Command** node as the last step of your workflow, so it fires when the workflow finishes.

<Steps>
  <Step title="Add an Execute Command node">
    Add a node and search for **Execute Command**.
  </Step>

  <Step title="Enter the command">
    In the **Command** field, enter:

    ```bash theme={null}
    open "proximate://notify?source=YOUR_ID&status=success"
    ```
  </Step>

  <Step title="Connect it last">
    Wire it in as the final node in the workflow.
  </Step>
</Steps>

<Note>
  The Execute Command node is **disabled by default** in n8n v2.0+ and isn't available on n8n Cloud. If you run n8n in Docker, the command executes inside the container, not on your Mac — run n8n directly on the machine where Proximate is installed.
</Note>

## pytest

Add a `conftest.py` to your project (or extend an existing one) using pytest's native **`pytest_sessionfinish`** hook, which runs after the whole test session and receives the exit status (`0` means everything passed):

```python conftest.py theme={null}
import subprocess

def pytest_sessionfinish(session, exitstatus):
    status = "success" if exitstatus == 0 else "error"
    subprocess.run(["open", f"proximate://notify?source=YOUR_ID&status={status}"])
```

Now every `pytest` run ends with a green icon on pass, red on failure — no shell chaining needed.

## Rust

Chain the command after `cargo build`, `cargo test`, or `cargo run`:

```bash theme={null}
if cargo build --release; then
  open "proximate://notify?source=YOUR_ID&status=success"
else
  open "proximate://notify?source=YOUR_ID&status=error"
fi
```

<Note>
  Cargo's `build.rs` runs *before* compilation, so it's not the place for a finish notification — chaining in the shell is the right approach.
</Note>

## Terraform

Get an icon when a long `apply` completes:

```bash theme={null}
if terraform apply -auto-approve; then
  open "proximate://notify?source=YOUR_ID&status=success"
else
  open "proximate://notify?source=YOUR_ID&status=error"
fi
```

## Xcode CLI

**From the command line** (`xcodebuild`), use the standard pattern:

```bash theme={null}
if xcodebuild -scheme MyApp build; then
  open "proximate://notify?source=YOUR_ID&status=success"
else
  open "proximate://notify?source=YOUR_ID&status=error"
fi
```

**Inside the Xcode app**, you can attach a post-action to a scheme so an icon appears after every build or test:

<Steps>
  <Step title="Open the scheme editor">
    In Xcode, choose **Product → Scheme → Edit Scheme…** (or press **⌘\<**).
  </Step>

  <Step title="Add a post-action">
    Expand **Build** (or **Test**) in the left list, select **Post-actions**, then click **+ → New Run Script Action**.
  </Step>

  <Step title="Enter the command">
    Set **Provide build settings from** to your app target, then enter:

    ```bash theme={null}
    open "proximate://notify?source=YOUR_ID&status=success"
    ```
  </Step>
</Steps>

<Note>
  Scheme post-actions run after the phase completes but don't branch on success or failure, so this is best used as a "build finished" (neutral or success) signal rather than a pass/fail one.
</Note>

## A Couple of Edge Cases

* **`success && … || error` can mislead.** With the one-line `cmd && open success || open error` form, if the `open success` step itself fails, the `error` command runs too. The `if … then … else … fi` form avoids this, which is why the recipes above prefer it whenever pass/fail matters.
* **Keep the quotes.** The `&` in the URL is meaningful to your shell, so the command must stay inside double quotes (`open "…"`). The card copies it that way already.

Don't see your tool? Any tool that can run a command on completion works — use one of the two patterns at the top. If an icon isn't showing up, see [Troubleshooting](/advanced-integrations/troubleshooting).
