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 $EXITFor 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:9868Shell 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 →