Integrations

syncfu works with anything that can run a shell command or make an HTTP request. Here are step-by-step guides for the most common tools.

Claude Code

Claude Code supports lifecycle hooks that fire shell commands at key points. Add hooks to ~/.claude/settings.json:

Notify on every tool call

// ~/.claude/settings.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{
          "type": "command",
          "command": "syncfu send -t 'Claude: Bash complete' -p normal 'Tool call finished'"
        }]
      }
    ],
    "Stop": [
      {
        "hooks": [{
          "type": "command",
          "command": "syncfu send -t 'Claude Code done' -p high --action 'open:Open project:primary' 'Agent session complete'"
        }]
      }
    ]
  }
}

HITL gate before destructive commands

// PreToolUse hook — blocks until you approve
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{
          "type": "command",
          "command": "syncfu send -t 'Claude wants to run a command' -p critical --wait -a 'allow:Allow' -a 'deny:Deny:danger' \"$TOOL_INPUT\""
        }]
      }
    ]
  }
}

Slash command & skill file

syncfu ships with ready-to-use Claude Code files:

# Install the /syncfu slash command and skill
cp claude/command.md ~/.claude/commands/syncfu.md
cp claude/skill.md ~/.claude/skills/syncfu.md

# Then in Claude Code:
/syncfu build passed, all 142 tests green
/syncfu critical: database connection pool exhausted
/syncfu ask me whether to deploy

Cursor

Cursor does not expose native lifecycle hooks. Wrap agent invocations in a shell script:

#!/usr/bin/env bash
# .cursor/run-agent.sh — wrap Cursor agent calls

syncfu send -t "Cursor agent started" -p low "Running: $*"

"$@"
EXIT=$?

if [ $EXIT -eq 0 ]; then
  syncfu send -t "Cursor agent done" -p normal \
    --action "view:View changes:primary" "Finished successfully"
else
  syncfu send -t "Cursor agent failed" -p high "Exit code $EXIT"
fi

exit $EXIT

For persistent notifications across all sessions, add to your shell precmd hook (zsh) or PROMPT_COMMAND (bash).

GitHub Actions

Add a notification step to any workflow. Requires network access from GitHub to your desktop (via ngrok or Cloudflare Tunnel):

# .github/workflows/ci.yml
- name: Notify syncfu
  if: always()
  run: |
    syncfu send -t "${{ github.workflow }} — ${{ job.status }}" \
      -s github-actions \
      -p ${{ job.status == 'success' && 'normal' || 'critical' }} \
      -i ${{ job.status == 'success' && 'circle-check' || 'circle-x' }} \
      "${{ github.repository }}@${{ github.ref_name }}"
  env:
    SYNCFU_SERVER: http://your-machine:9868

Shell aliases

Add to ~/.zshrc or ~/.bashrc:

# Notification aliases
alias notify='syncfu send -t'
alias notify-done='syncfu send -t "Done"'
alias notify-fail='syncfu send -t "Failed" -p critical -i circle-x'

# Usage
long-running-command; notify-done "Finished!"
cargo build && notify-done "Build complete" || notify-fail "Build failed"

# Pipe error monitoring
tail -f app.log | grep --line-buffered ERROR | while read line; do
  syncfu send -t "Error" -p high "$line"
done

Remote machines

Point the CLI at a remote syncfu instance using the SYNCFU_SERVER environment variable:

# In .zshrc / .bashrc
export SYNCFU_SERVER=http://your-desktop:9868

# Now syncfu send from any SSH session routes to your desktop
ssh prod-server
syncfu send -t "Deploy complete" "v2.1 is live on production"

Node.js

// Send a notification from Node.js
await fetch("http://localhost:9868/notify", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    sender: "my-app",
    title: "Task Complete",
    body: "All 47 tests passing",
    icon: "circle-check",
    priority: "normal",
    actions: [
      { id: "open", label: "Open PR", style: "primary" },
      { id: "dismiss", label: "Dismiss", style: "secondary" },
    ],
  }),
});

Python

import requests

# Simple notification
requests.post("http://localhost:9868/notify", json={
    "sender": "my-script",
    "title": "Pipeline Complete",
    "body": "Processed 2.4M rows in 12 minutes",
    "icon": "check",
    "priority": "normal",
})

# Decision gate with --wait via SSE
resp = requests.post("http://localhost:9868/notify", json={
    "title": "Deploy to production?",
    "body": f"Build {build_id} passed all tests.",
    "priority": "critical",
    "actions": [
        {"id": "deploy", "label": "Deploy", "style": "primary"},
        {"id": "cancel", "label": "Cancel", "style": "danger"},
    ],
})
notif_id = resp.json()["id"]

# Block until user responds
result = requests.get(f"http://localhost:9868/notify/{notif_id}/wait")
action = result.json()
if action["action_id"] == "deploy":
    trigger_deploy(build_id)

curl (any language)

# Fire-and-forget notification
curl -X POST localhost:9868/notify \
  -H "Content-Type: application/json" \
  -d '{"sender":"ci","title":"Build","body":"Done","priority":"high","icon":"rocket"}'

# Get notification ID for updates
ID=$(curl -s -X POST localhost:9868/notify \
  -H "Content-Type: application/json" \
  -d '{"sender":"ci","title":"Migrating","body":"Starting...","progress":{"value":0}}' \
  | jq -r .id)

# Update progress
curl -X POST "localhost:9868/notify/$ID/update" \
  -H "Content-Type: application/json" \
  -d '{"progress":{"value":0.5,"label":"50%"},"body":"Halfway done..."}'

Docker containers

# From a Docker container, use the host network gateway
# macOS / Windows:
curl -X POST http://host.docker.internal:9868/notify \
  -H "Content-Type: application/json" \
  -d '{"sender":"container","title":"Job Done","body":"Container task complete"}'

# Linux (default bridge):
curl -X POST http://172.17.0.1:9868/notify \
  -H "Content-Type: application/json" \
  -d '{"sender":"container","title":"Job Done","body":"Container task complete"}'

Claude Code

PostToolUse and Stop hooks, PreToolUse HITL gates, /syncfu slash command, skill file for full API access.

CI/CD

GitHub Actions, Jenkins, GitLab CI, CircleCI — any pipeline that can run a curl command. Expose via tunnel for remote runners.

Any language

HTTP POST to localhost:9868. Works from Python, Node.js, Go, Ruby, Rust, Java, or any language with an HTTP client.

Shell & cron

Shell aliases, cron jobs, bash scripts. The CLI syncfu send works from any terminal or automated task.

Connect syncfu to your stack

Open source, runs locally, zero config. If it can make an HTTP request, it can send you a notification.

Get started →