# Gemini CLI push notifications: done, or waiting for approval

> Send a phone push when Gemini CLI finishes a turn or asks to run a tool. Two hooks in settings.json, one script, plus the Pocket Alert MCP server.

*15.10.2026*

<!-- source: https://pocketalert.app/blog/gemini-cli-notifications -->

Gemini CLI can notify you on its own, but only through the terminal: turn on `general.enableNotifications` and it sends a desktop notification or a bell when it finishes or needs you. That helps when you are at the computer and the terminal is in another tab. It does nothing when you are not at the computer.

Hooks close the gap. Since v0.26 they are on by default, and two of the events are exactly what a phone push is for:

- **AfterAgent**: Gemini finished its turn. The payload includes the response.
- **Notification**: Gemini is asking for permission to run a tool.

This guide sends both to your phone through Pocket Alert.

## A note on which Gemini CLI

Since 18 June 2026, Google's documentation says Gemini CLI has been replaced by Antigravity CLI for the free tier and Google One plans. Gemini CLI itself continues with a paid Gemini API key, Vertex AI or enterprise access, and that is what this guide covers. If you use Antigravity CLI, check its documentation for hooks.

## What you need

- Gemini CLI v0.26 or later.
- `jq` and `curl`.
- A Pocket Alert account, an API key from [API Keys](/account/apikeys), and the app on your phone. The Free plan is enough.

Store the key in a file. Gemini CLI can strip environment variables whose names contain `KEY` or `TOKEN` before running hooks, when environment redaction is on or when it runs in GitHub Actions, and a key read from a file is not affected:

```bash
mkdir -p ~/.config/pocketalert
printf '%s' 'YOUR_API_KEY' > ~/.config/pocketalert/key
chmod 600 ~/.config/pocketalert/key
```

## Step 1. The hook script

Gemini CLI sends each hook a JSON object on stdin with `hook_event_name` and `cwd`. `AfterAgent` adds `prompt_response`; `Notification` adds `notification_type` and `message`. Save as `~/.gemini/hooks/pocketalert.sh`:

```bash
#!/usr/bin/env bash
payload=$(cat)
project=$(basename "$(jq -r '.cwd' <<<"$payload")")

case "$(jq -r '.hook_event_name' <<<"$payload")" in
  AfterAgent)
    title="Gemini finished · $project"
    message=$(jq -r '.prompt_response // "Turn complete"' <<<"$payload")
    level=default ;;
  Notification)
    title="Gemini needs approval · $project"
    message=$(jq -r '.message // "Waiting for tool permission"' <<<"$payload")
    level=high ;;
  *) echo '{}'; exit 0 ;;
esac

jq -n --arg t "$title" --arg m "${message:0:300}" --arg l "$level" \
  '{title: $t, message: $m, level: $l}' |
  curl -sS --max-time 5 https://api.pocketalert.app/v1/messages \
    -H "Token: $(cat ~/.config/pocketalert/key)" \
    -H 'Content-Type: application/json' \
    --data-binary @- >/dev/null 2>&1 || true

echo '{}'
```

```bash
chmod +x ~/.gemini/hooks/pocketalert.sh
```

Gemini CLI parses a hook's stdout as JSON, so the script prints `{}` and nothing else; curl's output goes to `/dev/null`. Exit code 2 would block the action, so the script never returns it.

### Or with the Pocket Alert CLI

With the [Pocket Alert CLI](/tools/cli) 2.0 or later installed, the curl block shrinks to one command. The CLI escapes the text itself and takes the key from `pocketalert auth`, which also sidesteps the variable stripping above:

```bash
pocketalert send -t "$title" -m "${message:0:300}" -l "$level" -q >/dev/null 2>&1 || true
```

Keep `-t` and `-m` rather than positional arguments: a reply that starts with a dash, like a Markdown list, would otherwise be read as an option.

## Step 2. Register the hooks

Add a `hooks` block to `~/.gemini/settings.json`, merged with whatever is there:

```json
{
  "hooks": {
    "AfterAgent": [
      {
        "matcher": "*",
        "hooks": [
          {
            "name": "pocketalert-done",
            "type": "command",
            "command": "/Users/you/.gemini/hooks/pocketalert.sh",
            "timeout": 10000
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "*",
        "hooks": [
          {
            "name": "pocketalert-approval",
            "type": "command",
            "command": "/Users/you/.gemini/hooks/pocketalert.sh",
            "timeout": 10000
          }
        ]
      }
    ]
  }
}
```

Replace `/Users/you` with your home directory. `timeout` is in milliseconds here, unlike some other agents. For a single project, put the same block in `.gemini/settings.json` in the repository.

## Step 3. Test it

Run the script with a fake event first:

```bash
echo '{"hook_event_name":"AfterAgent","cwd":"/tmp/demo","prompt_response":"Hook test"}' \
  | ~/.gemini/hooks/pocketalert.sh
```

Then start Gemini CLI and give it a task that ends with a tool call needing approval, such as "create a file called notes.txt". You get **Gemini needs approval** while it waits, and **Gemini finished** after you allow it and the turn ends.

## What lands on your phone

<div class="cmp-table-wrap">
<table class="cmp-table">
<thead><tr><th>Event</th><th>Title</th><th>Body</th><th>Level</th></tr></thead>
<tbody>
<tr><th>Turn finished</th><td>Gemini finished · api</td><td>First 300 characters of the response</td><td>default</td></tr>
<tr><th>Tool permission</th><td>Gemini needs approval · api</td><td>The permission message from Gemini CLI</td><td>high</td></tr>
</tbody>
</table>
</div>

`high` arrives as time-sensitive on iPhone, so approval requests get through Focus. Every push stays in the app and the web dashboard.

## Or let Gemini send it through MCP

With the [Pocket Alert MCP server](/tools/mcp), Gemini can send pushes it writes itself, at the moments you ask for. Add it from the command line:

```bash
gemini mcp add --scope user --transport http \
  --header 'Authorization: Bearer $POCKETALERT_API_KEY' \
  pocketalert https://mcp.pocketalert.app/mcp
```

The single quotes keep `$POCKETALERT_API_KEY` as a variable in the settings file instead of writing the key into it; Gemini CLI expands it when it connects, so export the variable in the shell that starts `gemini`. Without `--scope user` the server is added to the current project only.

The result in `~/.gemini/settings.json`:

```json
{
  "mcpServers": {
    "pocketalert": {
      "url": "https://mcp.pocketalert.app/mcp",
      "type": "http",
      "headers": { "Authorization": "Bearer $POCKETALERT_API_KEY" }
    }
  }
}
```

Run `/mcp` inside Gemini CLI to see the server and its tools. Then a prompt like "run the migration and push me the row count when it's done" ends with a `create_message` call.

## If nothing arrives

- **Run the test command.** No push means the key or network; remove `>/dev/null 2>&1` to see curl's response.
- **Check hooks are enabled.** They are by default, but `hooksConfig.enabled: false` in settings turns them off.
- **Check the path** in `command` is absolute and the script is executable.
- **Check stdout.** Anything other than JSON on stdout makes Gemini CLI ignore the hook's result. Make sure nothing else in the script prints.

## FAQ

### What is the difference from `general.enableNotifications`?

That setting sends terminal notifications to your computer when Gemini needs you or finishes. Hooks run any command, so they can reach your phone, keep a history, and use priority levels.

### Can I approve the tool call from my phone?

Not with this script: it tells you Gemini is waiting, and you approve in the terminal. A `BeforeTool` hook can block a tool call, so an approval gate is possible with an endpoint your phone calls; [Human-in-the-loop notifications](/blog/human-in-the-loop-notifications) shows the pattern.

### Does it work in GitHub Actions?

Yes, and the key file approach matters there: in GitHub Actions, Gemini CLI strips variables with `KEY` or `TOKEN` in the name from the hook's environment. Write the key from a secret to a file in an earlier step.

### Is it free?

Hooks are part of Gemini CLI, and Pocket Alert's Free plan covers 50 messages a day on one device. Gemini itself needs a paid API key or enterprise access for the CLI, as noted above.

[Get an API key](/signup), add the hooks, and let Gemini find you when it needs you.
