red-alert-statusline

June 22, 2026 ยท View on GitHub

A Claude Code statusline that shows real-time Pikud HaOref (Israel Home Front Command) rocket and missile alerts directly in your Claude session's status bar.

๐Ÿš€ MISSILES ยท 5 cities ยท ืชืœ ืื‘ื™ื‘ - ืžืจื›ื– ื”ืขื™ืจ
โš ๏ธ PRE-ALERT ยท ื ืชื ื™ื” ยท ื—ื“ืจื”
โœ… ALL CLEAR ยท ืจืžืช ื’ืŸ

Statusline in action:

image

What it shows

๐Ÿš€ MISSILES ยท ืชืœ ืื‘ื™ื‘ - ืžืจื›ื– ื”ืขื™ืจ ยท ืจืžืช ื’ืŸ - ืžืขืจื‘
^  ^           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|  |           Cities affected (โ‰ค3 inline; >3 cycles one at a time)
|  Alert category label
Alert icon
IconCategoryDisplay duration
โš ๏ธ PRE-ALERTCategory 14 โ€” imminent threat warningUp to 20 minutes
๐Ÿš€ MISSILESCategory 160s after last siren
โœˆ๏ธ HOSTILE AIRCRAFTCategory 260s after last detection
๐ŸŒ EARTHQUAKECategory 360s
๐ŸŒŠ TSUNAMICategory 460s
โ˜ข๏ธ RADIOLOGICALCategory 560s
โ˜ฃ๏ธ HAZMATCategory 660s
๐Ÿ”ซ INFILTRATIONCategory 760s
โœ… ALL CLEARCategory 13 โ€” event concluded15s
๐Ÿ”” DRILLCategories 101โ€“10760s
๐Ÿ‡ฎ๐Ÿ‡ฑ Pikud HaOref: all clearFade-out10s, then silent

City display โ€” no filter set:

When RED_ALERT_CITIES is not set, all cities from the alert are shown using this strategy:

  • 3 or fewer cities โ€” displayed inline, separated by ยท:
    ๐Ÿš€ MISSILES ยท ืชืœ ืื‘ื™ื‘ - ืžืจื›ื– ื”ืขื™ืจ ยท ืจืžืช ื’ืŸ - ืžืขืจื‘
    
  • More than 3 cities โ€” shows the total count and cycles through cities one at a time:
    ๐Ÿš€ MISSILES ยท 5 cities ยท ืชืœ ืื‘ื™ื‘          โ† one Claude response
    ๐Ÿš€ MISSILES ยท 5 cities ยท ืจืžืช ื’ืŸ           โ† next response, 2s later
    ๐Ÿš€ MISSILES ยท 5 cities ยท ื—ื•ืœื•ืŸ            โ† next response after that
    

The cycling is time-based (no background timer needed): the script divides the current unix timestamp by 2 and takes % city_count to pick which city to display. Each time Claude responds and 2+ seconds have passed, the next city in rotation appears.

City display โ€” filter set, no match: the statusline shows nothing, as if there is no alert.

Pre-alert priority: Category 14 pre-alerts (early warning before sirens) are shown first, above any other alert type. If no real alert (cat 1โ€“7) follows within 20 minutes, the pre-alert expires automatically.


Why it's useful

The official Home Front Command API is a live snapshot โ€” alerts appear on the endpoint only for the seconds that sirens are sounding, then disappear. This statusline maintains a 60-second persistence window so you see the alert even after the raw JSON has gone empty, giving you the full shelter window to act.


Prerequisites

  • bash 3.2+ (the macOS system bash is fine)
  • curl (pre-installed on macOS)
  • jq โ€” install with brew install jq

Geo-blocking note: The official oref.org.il API may block non-Israeli IP addresses (Akamai CDN). If you see no alerts during active events, try running the daemon on a server with an Israeli IP (e.g., GCP me-west1). The statusline script will simply stay silent if the API is unreachable.


Quick start

# 1. Install jq if you don't have it
brew install jq

# 2. Clone this repo (or it's already your working directory)
git clone https://github.com/lirantal/red-alert-statusline
cd red-alert-statusline

# 3. Install the statusline
jq --arg p "$(pwd)/red-alert.sh" \
   '.statusLine = {"type": "command", "command": $p}' \
   ~/.claude/settings.json > ~/.claude/settings.json.tmp \
   && mv ~/.claude/settings.json.tmp ~/.claude/settings.json

# 4. Restart Claude Code โ€” the daemon starts automatically on first response

Manual installation

Add this to ~/.claude/settings.json:

{
  "statusLine": {
    "type": "command",
    "command": "/absolute/path/to/red-alert-statusline/red-alert.sh"
  }
}

Configuration

All settings are environment variables. Set them in your shell profile or in the "env" block of ~/.claude/settings.json.

VariableDefaultDescription
RED_ALERT_CITIES(unset)Comma-separated city filter (English or Hebrew). If unset, all cities are shown.
RED_ALERT_POLL_INTERVAL2Daemon poll frequency in seconds.

City filter examples

When RED_ALERT_CITIES is set, the statusline is silent for alerts that don't affect your cities, and shows only the matching cities from an alert that does.

# Single city โ€” English name
export RED_ALERT_CITIES="Tel Aviv"

# Multiple cities โ€” comma-separated, no spaces around commas required
export RED_ALERT_CITIES="Tel Aviv,Ramat Gan,Netanya"
export RED_ALERT_CITIES="Tel Aviv, Ramat Gan, Netanya"   # spaces OK, they're trimmed

# Hebrew names โ€” direct substring match against the API's data[] array
export RED_ALERT_CITIES="ืชืœ ืื‘ื™ื‘,ืจืžืช ื’ืŸ"

# Mixed English and Hebrew
export RED_ALERT_CITIES="Tel Aviv,ื—ื™ืคื”,Beer Sheva"

# Zone/area name โ€” matches all cities in that zone if they appear in the alert
export RED_ALERT_CITIES="Gush Dan"   # won't work; use individual city names

# Spelling variants โ€” all of these resolve to the same Hebrew city (ื‘ืืจ ืฉื‘ืข)
export RED_ALERT_CITIES="Beer Sheva"
export RED_ALERT_CITIES="Beersheba"
export RED_ALERT_CITIES="Beersheva"

# Via ~/.claude/settings.json env block (persists across sessions):
{
  "env": {
    "RED_ALERT_CITIES": "Tel Aviv,Ramat Gan,Netanya"
  },
  "statusLine": {
    "type": "command",
    "command": "/path/to/red-alert.sh"
  }
}

Important: Alert city names from the API include district suffixes in Hebrew, e.g.:

  • "ืชืœ ืื‘ื™ื‘ - ืžืจื›ื– ื”ืขื™ืจ" (Tel Aviv - City Center)
  • "ืชืœ ืื‘ื™ื‘ - ื“ืจื•ื ื”ืขื™ืจ" (Tel Aviv - South)
  • "ืืฉืงืœื•ืŸ - ืฆืคื•ืŸ" (Ashkelon - North)

Filtering by "Tel Aviv" or "ืชืœ ืื‘ื™ื‘" matches all Tel Aviv districts because the match is substring-based.

Matching logic (applied in order, first match wins):

  1. Direct substring โ€” the filter term appears anywhere in the Hebrew city name
  2. English โ†’ Hebrew lookup โ€” ~70 major cities with common spelling variants are pre-mapped; the Hebrew value is then substring-matched
  3. Word-level fuzzy โ€” each word in your filter (โ‰ฅ3 chars) is individually checked against the city name, enabling partial matches

If your city is not in the lookup table, use the Hebrew name directly (copy the value field from cities.json in eladnava/pikud-haoref-api).


How it works

There are two scripts with distinct jobs:

red-alert-daemon.sh โ€” the poller

Runs permanently in the background. You never interact with it directly โ€” red-alert.sh starts it automatically the first time Claude Code calls the statusline.

What it does every 2 seconds:

  1. curl the official Pikud HaOref endpoint (alerts.json)
  2. Strip the UTF-8 BOM and NUL bytes that the API sometimes injects
  3. If the response contains a valid JSON alert, parse cat (category), id, title, and data (the cities array)
  4. Update /tmp/red_alert_state.json atomically (write to a temp file, then mv) so red-alert.sh never reads a half-written file

Key behaviour: when the API returns empty (no active siren), the daemon does nothing โ€” it leaves the last state untouched. The display script handles expiry. This is intentional: the live endpoint is a narrow snapshot that goes empty within seconds of a siren ending, but the threat window is 60+ seconds.

State file written to /tmp/red_alert_state.json:

{
  "alert_id":         "134168709720000000",
  "cat":              "1",
  "title":            "ื™ืจื™ ืจืงื˜ื•ืช ื•ื˜ื™ืœื™ื",
  "cities":           ["ืชืœ ืื‘ื™ื‘ - ืžืจื›ื– ื”ืขื™ืจ", "ืจืžืช ื’ืŸ - ืžืขืจื‘"],
  "last_seen_unix":   1711234567,
  "pre_alert_active": false,
  "pre_alert_time":   0,
  "cleared_unix":     0
}

Daemon log: /tmp/red_alert_daemon.log

red-alert.sh โ€” the statusline renderer

Called by Claude Code after every response. Must return instantly (no network I/O).

What it does:

  1. Checks if the daemon is running (by PID file); starts it in the background if not
  2. Reads /tmp/red_alert_state.json with a single jq call
  3. Computes how many seconds have passed since each event (last_seen_unix, pre_alert_time, cleared_unix)
  4. Applies city filtering if RED_ALERT_CITIES is set
  5. Runs the state machine (pre-alert โ†’ all-clear โ†’ active โ†’ fade-out โ†’ silent)
  6. Prints the formatted string to stdout; Claude Code displays it in the status bar
red-alert.sh (called each response)      red-alert-daemon.sh (always running)
        โ”‚                                          โ”‚
        โ”‚โ”€โ”€ is daemon running? โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚
        โ”‚   no โ†’ start it in background           โ”‚  every 2s:
        โ”‚                                         โ”‚  curl alerts.json
        โ”‚                                         โ”‚  parse response
        โ”‚<โ”€โ”€ read /tmp/red_alert_state.json โ”€โ”€โ”€โ”€โ”€โ”€โ”‚  write state (atomic mv)
        โ”‚
        โ”‚   compute event ages
        โ”‚   filter cities by RED_ALERT_CITIES
        โ”‚   pick state: pre-alert / alert / clear / fade-out / silent
        โ”‚   format city list (inline โ‰ค3, cycling >3)
        โ–ผ
  stdout โ†’ Claude Code status bar

Alert timing state machine

Event detected by daemon
        โ”‚
        โ–ผ
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚ cat=14 (pre-alert)  โ†’ show โš ๏ธ  PRE-ALERT for 20 min     โ”‚  highest priority
  โ”‚                        expires if no cat 1-7 follows    โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ”‚ superseded by real alert or 20 min elapsed
        โ–ผ
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚ cat=1-7 (active)    โ†’ show alert for 60s                โ”‚
  โ”‚                        even after API goes empty        โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ”‚ cat=13 received
        โ–ผ
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚ cat=13 (all clear)  โ†’ show โœ… ALL CLEAR for 15s         โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ”‚ 15s elapsed (or 60s with no cat=13)
        โ–ผ
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚ fade-out window     โ†’ show ๐Ÿ‡ฎ๐Ÿ‡ฑ Pikud HaOref: all clear   โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ”‚ 10s elapsed
        โ–ผ
     silent (statusline shows nothing)

Uninstall

# Remove from settings.json
jq 'del(.statusLine)' ~/.claude/settings.json > ~/.claude/settings.json.tmp \
  && mv ~/.claude/settings.json.tmp ~/.claude/settings.json

# Stop the daemon
kill $(cat /tmp/red_alert_daemon.pid 2>/dev/null) 2>/dev/null || true

# Clean up state files
rm -f /tmp/red_alert_state.json /tmp/red_alert_daemon.pid /tmp/red_alert_daemon.log

Troubleshooting

Statusline shows nothing during an active alert

  • Check if the daemon is running: cat /tmp/red_alert_daemon.pid && kill -0 $(cat /tmp/red_alert_daemon.pid)
  • Check daemon logs: tail -20 /tmp/red_alert_daemon.log
  • Test the API directly: curl -s "https://www.oref.org.il/warningMessages/alert/alerts.json" -H "Referer: https://www.oref.org.il/"
  • If the API returns a 403 or empty response from your IP, you're geo-blocked โ€” the API primarily serves Israeli IPs.

Statusline shows โš ๏ธ red-alert: jq not found

Install jq: brew install jq

Daemon keeps restarting

Check /tmp/red_alert_daemon.log for errors. Common cause: curl not in PATH when launched from a non-interactive shell. Verify: which curl.

City filter not matching

  • Use the Hebrew city name directly for reliable matching.
  • Check cities.json in eladnava/pikud-haoref-api to find the exact value string used in alerts for your city.
  • Alert city names include district suffixes (e.g., "ืชืœ ืื‘ื™ื‘ - ืžืจื›ื– ื”ืขื™ืจ"). Filtering by "ืชืœ ืื‘ื™ื‘" will match all Tel Aviv districts.

Force a state reset

rm -f /tmp/red_alert_state.json /tmp/red_alert_daemon.pid
kill $(cat /tmp/red_alert_daemon.pid 2>/dev/null) 2>/dev/null || true

The daemon will restart and re-initialize on the next Claude response.

Documentation

Contributing

Please consult CONTRIBUTING for guidelines on contributing to this project.