npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@trygocode/notify

v0.6.4

Published

Free phone + branded desktop notifications for any coding agent (Cursor, Claude Code, OpenCode, Ralph/Homer) via the GoCode app.

Readme

@trygocode/notify

Free phone and branded desktop notifications for any coding agent — Cursor, Claude Code, OpenCode, or any Ralph/Homer loop — delivered to your phone via the GoCode app + FCM, and to your computer as a native "GoCode" banner.

You get an automatic notification when your agent finishes a turn, goes idle waiting for you, errors out, or when an overnight loop completes / halts — on your phone AND on the computer you're working on (on by default) — so you can walk away and let either device tell you when it needs you.

Contents

Install — two equally-supported paths

Both paths converge on the same installer (gocode-notify setup): it pairs this machine, auto-detects your agent runtimes, and merges the hooks + MCP server + anti-double-ping rule into each one's config (never clobbering your existing settings; safe to re-run).

Path 1 — paste a one-liner into your terminal

npx @trygocode/notify@latest setup

This runs the interactive installer: it prompts for the 6-digit pairing code (from the GoCode app → "Connect a coding agent"), then detects and configures Claude Code / Cursor / OpenCode. Re-run any time — it's idempotent; pass --force to re-pair.

Until the package is published to npm, point npx at the local tarball (npm pack in this directory, then npx ./gocode-notify-*.tgz setup) or a path install. The npm publish is a user-owned step — see docs/GOCODE_NOTIFY_MANUAL_STEPS.md.

Path 2 — paste a prompt into your AI agent and let it install

Hand this to Cursor / Claude Code and the agent does the install for you. The --agent-driven flag suppresses interactive prompts and emits one JSON line per step so the agent can verify each one:

Install GoCode phone notifications for this machine. Run:
  npx @trygocode/notify@latest setup --agent-driven --pair-code <CODE>
Then confirm the hooks and MCP server were written, and run
  npx @trygocode/notify@latest test
to send a test push to my phone. Report whether the test push arrived.

Replace <CODE> with the 6-digit code from the GoCode app. --agent-driven is fully idempotent and machine-readable; it never spawns a prompt.

Pairing — step by step

A dev machine running agent hooks has no GoCode login (no JWT), so it can't use GitHub OAuth. Instead you pair it once with a short-lived 6-digit code, which the CLI exchanges for a scoped, push-only API key stored locally. The key can only send pushes to your phone — it can't read chats, settings, or trigger any agent action, and you can revoke it from the app at any time.

  1. In the GoCode app, open Settings → "Connect a coding agent". The app shows a large 6-digit code (valid 10 minutes), a copyable npx @trygocode/notify login --code 123456 line, and a 10:00 countdown. Tap "Generate new code" if it expires.

  2. On the machine, either run the full installer (npx @trygocode/notify@latest setup, which pairs and writes your agent configs) or just pair on its own:

    # Interactive — prompts for the code:
    npx @trygocode/notify@latest login
    
    # Or pass it directly (and optionally label this machine):
    npx @trygocode/notify@latest login --code 123456 --label "MacBook Pro — Cursor"
  3. The CLI calls the server's pair/claim endpoint, receives the API key once, and writes it to ~/.gocode/credentials (chmod 600). The app flips to a "connected ✓" success state showing the machine's label.

  4. Verify the round-trip with a real push to your phone:

    npx @trygocode/notify@latest test

    A "GoCode test" notification should arrive on your paired device. If it doesn't, see Troubleshooting.

Re-pairing. Both login and setup are idempotent — re-running them won't clobber an existing pairing. To deliberately replace the stored key (new machine owner, rotated key), pass --force to setup (or just run login again with a fresh code). Revoke an old machine from the app's "Connected agents" screen.

Server selection. Pairing and every send resolve the server URL in this precedence order: the --server flag → the GOCODE_SERVER env var → the value saved in ~/.gocode/credentials → the built-in default (https://oh.jeltechsolutions.com). You only need --server for a self-hosted or staging GoCode server.

Redistributing under your own server. The built-in default is not baked in as a fixed author box — set GOCODE_DEFAULT_SERVER to point fresh, unpaired installs at your OWN GoCode server without patching the source or having every user pass --server/GOCODE_SERVER. It only changes the fallback default; already-paired machines keep the server in their ~/.gocode/credentials.

The three triggers

| Trigger | Mechanism | Fires when | |---|---|---| | (A) Runtime hook | Cursor stop / Claude Code Stop+Notification / OpenCode session.idle plugin | The MAIN agent finishes a turn, goes idle, or errors — automatic, the killer feature | | (B) MCP tool | gocode_notify tool the agent calls | You explicitly ask "ping me when X is done" mid-task | | (C) Loop shell hook | one line in your loop's completion/halt path | A Ralph/Homer loop reaches completed / halted |

The installed rule/skill tells the agent not to call the MCP tool for done/idle/error pings — those are owned by the deterministic hook (A), so you never get double-pinged.

Cursor stop.status → kind mapping (since 0.3.0; aborted corrected 0.4.0). Cursor's stop hook carries a status field in its stdin JSON. The on-stop dispatcher maps it to the right notification kind automatically:

| Cursor stop.status | Notification kind | Meaning | |---|---|---| | completed | finished | Agent turned cleanly (default — same as before 0.3.0) | | aborted | (suppressed — no notification) | You pressed Stop to interrupt the turn — a manual stop sends nothing | | error | error | Agent hit an error | | absent / unrecognised | finished | Back-compatible fallback |

Before 0.3.0, Cursor only ever sent finished regardless of what the agent did. The status mapping requires no hook re-installation — run gocode-notify setup --force once to apply the updated hook command.

Question detection — "Agent needs you" (since 0.5.0). When the agent pauses to ask you a question (as opposed to finishing its work), the package fires an awaiting_input ping titled "Agent needs you" instead of a misleading finished. The available signal differs per runtime:

| Runtime | Question signal | Status | |---|---|---| | Claude Code | dedicated Notification hook → awaiting_input | ✅ Live — questions show as questions | | OpenCode | permission.asked plugin event → awaiting_input | ✅ Live — OpenCode genuinely exposes a "needs you" signal | | Cursor | postToolUse hook scoped to the AskQuestion tool → awaiting_input | ⏳ Installed but dormant — see note |

Cursor caveat: Cursor currently exposes no "agent needs input" event (its docs list Notification as unsupported), and there is a confirmed-open Cursor bug where the AskQuestion tool fires zero hooks. So a Cursor question is indistinguishable from a completion at the only signal we get (the stop hook), and you'll receive finished until Cursor ships their fix. We install the postToolUse(AskQuestion) hook now so that the moment Cursor fixes the bug, question detection auto-activates with no new install for everyone already running @trygocode/notify@latest. Run gocode-notify setup --force once to wire it in.

OpenCode's hook (A) is a small session.idle plugin written to ~/.config/opencode/plugin/gocode-notify.js; on each session.idle event it fire-and-forgets gocode-notify on-stop --source opencode (the same dispatcher as Cursor/Claude), so OpenCode is a fully-supported runtime with identical behaviour.

Under the hood (since auto-push landed): the runtime hook (A) no longer shells straight to send. It now calls a single end-of-turn dispatcher, gocode-notify on-stop --source <runtime> || true, which decides per repo: if auto-push is enabled for this repo it runs the push flow (and sends ONE notification carrying the commit summary); otherwise it sends the ordinary finished ping exactly as before. One hook entry, one notification per turn. Existing installs upgrade their hook automatically on the next setup/setup --force. send and test remain callable on their own for scripts and power users.

Desktop notifications (branded, on by default)

GoCode Notify also raises a real, branded "GoCode" banner on the computer you're working on — the same instant your phone gets pinged — so you're told whether you're at your desk or away. It's ON by default once the package is installed and is controlled by the same server-synced settings as the phone push, so a toggle in the app or the terminal flips the computer banner everywhere.

Word-for-word identical to the phone. The banner reads exactly the same as the push you'd get on your phone — same status emoji, IDE/source label, project, and per-kind copy (e.g. ✅ Cursor · openhandapp — Agent finished). The wording is a faithful mirror of the server's notification decoration and the Flutter client's, pinned by a cross-check test (notify_copy.test.ts) so the three surfaces can't drift.

Click to open your IDE (macOS). Clicking the banner opens the IDE window for that project — the same editor that raised the turn (Cursor / VS Code), focused on the project you were working in. This is best-effort: when the IDE/project can't be named the banner is simply display-only (never a dead or mis-routed click). It opens the project window, not a specific past chat — IDEs don't expose reliable per-chat deep links.

| OS | How it's shown | Branded as | |---|---|---| | macOS | A one-time osacompiled GoCode.app helper under ~/.gocode/desktop/ raises the banner via the system | "GoCode" + the GoCode icon | | Windows | A WinRT ToastGeneric toast bound to a one-time Start-Menu shortcut carrying the GoCode.Notify AppUserModelID | "GoCode" | | Linux | notify-send -a GoCode -i <icon> | "GoCode" + icon |

Zero new dependencies. It uses only tools that ship with the OS (osacompile/ osascript/sips on macOS, PowerShell + WinRT on Windows, notify-send on Linux) — so the one-line install does not change at all. The first banner builds a tiny branded helper once and caches it; every banner after that just fires it.

Control it

From the GoCode app (Settings → GoCode NotifyDesktop notifications), or from the terminal:

gocode-notify config set desktop.enabled false   # silence the computer banner
gocode-notify config set desktop.sound   false   # keep the banner, drop the sound

The phone push is unaffected — desktop.* only governs the local banner.

Try it right now

# Fire a branded test banner on THIS computer (independent of the phone push):
gocode-notify test --desktop

You should see a "GoCode" notification appear on your screen. On a headless box / CI / SSH session where a banner makes no sense, set GOCODE_NOTIFY_NO_DESKTOP=1 to hard-disable local banners for that machine.

macOS branding note. A bare osascript banner is attributed to "Script Editor". To brand it "GoCode", the package compiles a minimal AppleScript app bundle (~/.gocode/desktop/GoCode.app) once with osacompile (ships with every Mac — no Xcode/Swift) and drops the GoCode icon into it via sips. If that one-time setup ever fails, the banner still fires (unbranded) rather than blocking your turn.

Hand off to your server — launch / autopilot

New in 0.2.0. Notify can now do more than ping your phone — it can hand a big task off to your GoCode server to run as an autonomous Autopilot loop. Close your laptop; the work keeps going server-side in your isolated sandbox, shows up in the GoCode phone app like any other Autopilot run, and your phone buzzes when it's done (or needs you).

This is the opposite direction of the three notify triggers above: instead of your local agent pinging your phone, launch starts a fresh server-side build from an explicit, self-contained task. It reuses the same gck_ pairing — no new login. It reads no conversation content (only the task string you hand it), so it ships in this base package (see the trust note below).

CLI

# Start a loop from a plain task (server synthesizes a PRD, then builds):
gocode-notify launch "Refactor the auth module to use sessions, add tests" \
  --repo owner/repo

# alias:
gocode-notify autopilot "Add a CSV export endpoint with tests" --repo owner/repo

# Or hand it a pre-written checkbox PRD instead of a task string:
gocode-notify launch --prd-file ./my-prd.md --repo owner/repo

| Flag | Meaning | |---|---| | <task> (positional) | Plain-language task the server loop builds. Mutually exclusive with --prd-file. | | --prd-file <path> | A pre-written checkbox PRD (base64-encoded and sent as prd_markdown). | | --repo owner/repo | Optional repo context for synthesis / the loop's target. | | --branch <suffix> | Optional branch suffix the loop pushes to. | | --model "Profile" | Optional LLM-profile name override. | | --runner-kind <kind> | Optional runner override (default openhands-conversation). | | --server <URL> | Same server-resolution precedence as every other command. | | --agent-driven | Emit one machine-readable JSON step line (for installers/agents). |

On success it prints the loop id, a deep link that opens the run in the app, and a "running on your GoCode server — close your laptop; your phone will buzz when it's done" line, then exits 0. Unlike the fire-and-forget send/on-stop hooks, launch is an explicit interactive action: it reports real failures (not paired / unreachable / 4xx) with a clear message and a non-zero exit, and it does not queue to the offline outbox (a stale launch surfacing hours later could duplicate work).

MCP tool

Your desktop agent (Cursor / Claude Code / OpenCode) can fire a remote loop mid-session via the third MCP tool, gocode_launch_autopilot ({ task (required), repo?, branch? }). Its description tells the agent to call it only when you explicitly ask to offload / run in the background / overnight — never for a normal task it can do right there. On success it relays the loop id

  • deep link; on failure it returns an actionable re-pair hint.

Heads-up the rule states: the server loop is a fresh agent with no access to your IDE's open/unsaved files — so the task must be self-contained and point at a repo the server can build from a clean checkout. If the work depends on local uncommitted state, commit/push first, then hand it off.

Trust boundary

launch reads only the explicit task string (and optional repo/branch) you hand it — it does not read or upload your IDE conversation transcript, and it touches only ~/.gocode/credentials (the gck_ key it already uses for notify). It POSTs to your own configurable GoCode server. So the base package's "never reads your conversation content" promise still holds.

Auto-push to git (opt-in)

GoCode Notify can auto-commit and push your work after every agent turn — with an AI-written commit message describing what just changed — so an overnight Cursor/Claude/Ralph session lands as a tidy series of pushed commits and your phone tells you each time, commit summary included.

It is OFF by default and deliberately conservative (see the safety posture below). The full design lives in docs/GOCODE_NOTIFY_AUTO_PUSH_AND_SYNC.md.

Enable it

From the GoCode app (Settings → GoCode NotifyAuto-push after every prompt; a first-enable dialog explains the behaviour), or from the terminal:

# Turn auto-push on for ALL repos (global default):
gocode-notify config set auto_push.enabled true

# Or scope it to just the current repo:
gocode-notify config set auto_push.enabled true --repo

Either editor writes to the server (the single source of truth); the change is picked up by every linked machine on its next turn (within the 60s settings cache, or immediately after gocode-notify config pull).

What happens on each turn (when enabled)

The on-stop dispatcher runs this best-effort push flow (it always exits 0 so a git hiccup can never block or fail your agent's turn):

  1. Resolve the merged Notify settings for this repo (cached ~60s).
  2. If auto_push.enabled is not true for the repo → no-op (plain ping).
  3. Verify it's a git repo; resolve the target branch (per-repo override → global default → current checked-out branch).
  4. Branch guard: never push to main/master unless you have separately set auto_push.allow_protected true. Protected + not allowed → skip, no push.
  5. git add -A; if the tree is clean → exit 0 (no spurious notification).
  6. Compose the commit message (AI, with deterministic fallback — see below).
  7. git commit then git push <remote> <branch>fast-forward only, never --force. A non-FF rejection sends an error notification ("auto-push rejected — pull + retry") and exits 0.
  8. Send ONE finished push whose body carries the commit summary, branch, and short SHA.

The AI commit message (free) + deterministic fallback

The commit subject/body is summarised using credentials you already have on the machine — so it costs GoCode nothing. Resolution chain (first that works wins, hard 8s cap):

  1. commit_message.mode = "deterministic" → skip AI, use the template below.
  2. Otherwise an explicit commit_message.command (e.g. "claude -p", "cursor-agent --print", "ollama run llama3.2") — the staged diff is piped in on stdin with a fixed prompt; output is sanitised (code fences / "Here is…" preambles stripped, subject clamped to ≤72 chars).
  3. Auto-detect from --source: claude on PATH for Claude Code, cursor-agent for Cursor, else ollama if present.
  4. Deterministic fallback (always available — used if the above fail or time out): <type>: <N> file(s) changed on <branch> + a git diff --stat block, where <type> is inferred from the changed paths (docs:/test:/chore:/ feat:). The push never fails because the AI summary was unavailable.

Try it without writing anything

# Print exactly what the end-of-turn dispatcher WOULD do (no git writes, no push):
gocode-notify on-stop --dry-run

Safety posture (conservative defaults)

  • OFF by default, opt-in, with a first-time confirmation in the app.
  • Branch guard ON: main/master are never auto-pushed unless auto_push.allow_protected is explicitly true.
  • No force-push, ever — plain fast-forward git push; non-FF → error notification, no rewrite.
  • Never blocks the agent — every step is best-effort and exits 0, the same contract as send.

Settings sync — the config command

Notify behaviour used to be hard-wired; now it's a small set of per-user settings stored on the server and editable from either the phone app or the terminal. Whatever you change in one place propagates to the other (and to your other linked machines).

gocode-notify config get [--repo]                 # print the merged settings (JSON/pretty)
gocode-notify config set <key> <value> [--repo]   # validate, write to server, refresh cache
gocode-notify config pull                          # force-refresh the local cache from server
  • --repo scopes a read or write to the current repo's per-project override (keyed by a stable repo_key derived from the origin remote) instead of your global defaults. Per-repo overrides deep-merge over the global blob.
  • Writes go to the server and update the local cache so the next stop-hook is immediate. Unknown keys are rejected with the list of valid keys.

Valid keys (mirror of the canonical schema):

| Key | Type | Meaning | |---|---|---| | kinds.finished / kinds.error / kinds.awaiting_input / kinds.loop_completed / kinds.loop_halted / kinds.ralph_waiting | bool | Per-kind notification toggles | | min_duration_seconds | int ≥ 0 | Only notify if the turn ran ≥ N seconds (0 = always) | | quiet_hours.enabled / quiet_hours.start / quiet_hours.end / quiet_hours.tz | bool / HH:MM / HH:MM / IANA tz | Do-not-disturb window | | auto_push.enabled | bool | Master auto-push switch (OFF by default) | | auto_push.default_branch | string | null | Global default push branch (null = current branch) | | auto_push.branch | string | null | --repo only — per-repo push branch override | | auto_push.allow_protected | bool | Permit auto-push to main/master (default false) | | auto_push.remote | string | Git remote to push to (default origin) | | auto_push.skip_git_hooks | bool | Pass --no-verify to the auto-commit (default false) | | commit_message.mode | auto | ai | deterministic | Commit-message strategy | | commit_message.command | string | null | Explicit local summariser command | | commit_message.max_diff_bytes | int > 0 | Diff byte cap fed to the summariser (default 61440) | | desktop.enabled | bool | Branded desktop banner on this computer (ON by default) | | desktop.sound | bool | Play a sound with the desktop banner (default true) |

# Examples
gocode-notify config set quiet_hours "23:00-07:00"     # shorthand: enable + set window
gocode-notify config set min_duration_seconds 30
gocode-notify config set kinds.error false
gocode-notify config set auto_push.branch dev --repo   # push this repo to "dev"

Ralph/Homer opt-in snippet (trigger C)

For power users running a loop they control (this repo's ralph/homer skills, a while :; do … done one-liner, or any custom driver), drop these two lines into the loop's completion/halt path:

# At loop completion:
gocode-notify send --kind loop_completed --source ralph --project "$(basename "$PWD")" || true
# At loop halt (paused_max_failures / awaiting_human):
gocode-notify send --kind loop_halted --source ralph --project "$(basename "$PWD")" \
  --title "Ralph halted — needs you" || true

The ready-to-copy version with comments lives at snippets/ralph-homer.sh.

This is opt-in and never auto-injected — the installer does not edit your loop scripts. Both lines are fire-and-forget (|| true + the CLI's 5s self-timeout), so a failed or slow push can never block or fail your loop.

Notify from any custom script (the minimal one-liner)

Any shell script — a cron job, a Makefile target, a post-build hook, or someone else's automation — can send a push with a single line, no configuration beyond a one-time gocode-notify login pairing:

gocode-notify send --kind finished --source <name> || true

Replace <name> with a short identifier for the script (e.g. ci, build, deploy). The || true guard ensures a failed or slow push never blocks the calling script (the CLI also self-times-out in 5 seconds).

Discovery hint: gocode-notify status always prints this line at the bottom of its report so you can copy-paste it even when you don't have the README handy.

Other useful kinds for custom scripts:

| Kind | When to use | |---|---| | finished | Script completed cleanly | | error | Script hit an error | | awaiting_input | Script paused; a human is needed | | loop_completed | Long-running loop finished all work | | loop_halted | Long-running loop stopped; human needed |

Pass --title "My script" and --body "extra detail" to customise the notification text. Use --project "$(basename "$PWD")" to badge it with the project name on your phone. All flags are optional — only --kind is required.

See snippets/ralph-homer.sh for the full Ralph/Homer lifecycle pattern (completed / halted / stall-edge / resumed).

Troubleshooting

Start here: gocode-notify status prints a one-screen report — whether credentials are present (and the bound user/label), whether the server is reachable, which agent runtimes were detected, and whether each one's config has been written. Most issues below are diagnosable from that output.

| Symptom | Likely cause & fix | |---|---| | test / send prints "not paired" | No ~/.gocode/credentials. Run gocode-notify login and pair from the app (see Pairing). | | Pairing fails ("invalid or expired code") | Codes expire after 10 min and are single-use. Tap "Generate new code" in the app and re-run login with the fresh code. | | status shows Server: not reachable | Network/DNS/firewall, or a wrong server URL. Confirm you can reach https://oh.jeltechsolutions.com; check the --server flag / GOCODE_SERVER env / the server field in ~/.gocode/credentials. | | No push arrives even though test exits 0 | The send is fire-and-forget and exits 0 even on failure — check ~/.gocode/notify.log for the real error. Also confirm push permissions are granted in the GoCode app and the device token is registered (re-open the app once after signing in). | | Double pings (two notifications per event) | The agent is calling the gocode_notify MCP tool and the runtime hook is firing. Re-run setup so the anti-double-ping rule/skill is installed; it tells the agent not to notify for automatic done/idle/error events. | | Hook doesn't fire in Cursor / Claude Code / OpenCode | Re-run setup and check status shows "config written" for that runtime. Restart the agent app so it reloads ~/.cursor/hooks.json / ~/.claude/settings.json / ~/.config/opencode/plugin/gocode-notify.js. The hooks are merged, never clobbered — your existing hooks are preserved. | | Pushes queue up while offline then arrive later | Expected. Sends made while the server is unreachable are enqueued to ~/.gocode/outbox/ (size-capped, drop-oldest) and flushed best-effort on the next send. A missed "done" ping is acceptable; a blocked agent is not. | | npx @trygocode/notify can't find the package | Until it's published to npm, run from a local tarball: npm pack in tools/gocode-notify/, then npx ./gocode-notify-*.tgz <command>. See docs/GOCODE_NOTIFY_MANUAL_STEPS.md. | | Want it gone | gocode-notify uninstall removes exactly the hook/MCP/rule entries this tool added (nothing else). Delete ~/.gocode/ to also drop the stored credentials, and revoke the key from the app's "Connected agents" screen. |

Logs & files. Failures are appended to ~/.gocode/notify.log (size-capped, rotated to notify.log.1). Credentials live in ~/.gocode/credentials (chmod 600); non-secret prefs in ~/.gocode/config.json; the offline queue in ~/.gocode/outbox/.

For the device/secret/publish/deploy steps that are not part of this package (Android google-services.json, iOS APNs, server service-account, WSL rebuild, npm publish, real-device E2E), see docs/GOCODE_NOTIFY_MANUAL_STEPS.md.

Changelog

0.6.1

  • Banner now reliably POPS top-right (not just history). The signed helper posts the notification then exits ~150ms later so macOS does BACKGROUND delivery (the system shows the banner per the user's settings). The previous attempt to force foreground delivery via willPresent suppressed the banner to Notification Center history — verified fixed live on macOS 26.4.
  • Surface the underlying requestAuthorization error in the helper's JSON result (detail/authError) instead of collapsing to a bare "not authorized".
  • Extract the post-add() exit delay to a documented named constant.

0.6.0

  • Real native macOS "Allow Notifications" dialog (signed helper). GoCode now ships a tiny Developer-ID-signed macOS app bundle (GoCodeNotifier.app, Team 88DC7KFY7M) inside the package. On macOS, gocode-notify permissions (and the post-setup nudge) now pop the real system Allow/Don't-Allow permission modal — the thing a plain npx/osascript helper can't do — instead of only opening System Settings and asking you to hunt for the toggle. Once allowed, desktop banners post as a first-class, authorized app (proper attribution + a real click-to-open-your-IDE action).
    • How it ships: the bundle is pre-signed at build time by the maintainer (native/macos/build_and_sign.sh) and committed to assets/, so end-users need no Xcode, no Developer ID, no compile step — the one-line install is unchanged. The signature survives the npm pack/extract round-trip.
    • Two macOS rules it honors (both required for the dialog to appear): the helper is copied to a stable location (~/.gocode/desktop/) — never /tmp — and is always launched via LaunchServices (open), never by exec'ing the inner binary (direct-exec auto-denies and the denial sticks).
    • Hot-path stays fast: fire-and-forget banners read a cached auth status (~/.gocode/desktop/.auth-status), so a notification never does a slow live permission round-trip. The signed banner is used only once you've granted; otherwise it transparently falls back to the legacy applet banner.
    • Own identity: the signed helper uses bundle id com.gocode.notify.helper (distinct from the legacy applet's com.gocode.notify.desktop) so it owns its own row in System Settings → Notifications.
    • Linux/Windows unchanged. This is a macOS-only enhancement; other platforms keep their existing branded-banner paths.

0.5.0

  • Question detection across ALL IDEs — "Agent needs you" vs "finished". When the agent pauses to ask you a question, you now get an awaiting_input ("Agent needs you") notification instead of a misleading finished, on every supported runtime that exposes a question signal:
    • Claude Code — already wired (Notification hook); unchanged.
    • OpenCode — NEW: the plugin now listens to permission.asked (the moment the agent raises a request) and fires awaiting_input. OpenCode genuinely exposes a "the agent needs the user" signal via permission events. (We listen to permission.asked only, not permission.updated/replied, which also fire after the user answers.)
    • Cursor — NEW (dormant): installs a postToolUse hook scoped to the AskQuestion / AskUserQuestion tool. Cursor has a confirmed-open bug where that tool fires zero hooks, and Cursor exposes no Notification event, so the hook is dormant today — but it auto-activates with no new install the moment Cursor ships their fix. Until then, Cursor keeps its finished fallback.
  • Corrected the documented Cursor aborted mapping. The README table previously (incorrectly) showed aborted → awaiting_input; the actual behaviour (since the 0.4.0 fix) is that a manual Stop press (aborted) sends no notification at all. A user force-stop is now clearly distinguished from a genuine IDE question.
  • Run gocode-notify setup --force once to wire the new hooks into Cursor + OpenCode.

0.4.0

  • New: branded desktop notifications (on by default). Every event that pings your phone now also raises a native, GoCode-branded banner on the computer you're working on — macOS (a one-time osacompiled GoCode.app helper + sips icon), Windows (a WinRT ToastGeneric toast bound to a one-time Start-Menu shortcut with the GoCode.Notify AppUserModelID), and Linux (notify-send -a GoCode). Zero new dependencies — OS-native tools only, so the one-line install is unchanged. Controlled by the same server-synced settings as the phone push via the new desktop.enabled / desktop.sound keys (both default ON); toggle from the app or gocode-notify config set desktop.enabled false. Try it with gocode-notify test --desktop. Hard- disable per machine with GOCODE_NOTIFY_NO_DESKTOP=1 (headless / CI / SSH). Best-effort + non-blocking: a failed banner never blocks or fails your turn.
  • Word-for-word match with the phone push. The desktop banner now reads EXACTLY like the phone notification — same status emoji + IDE/source label + project + per-kind title/body (e.g. ✅ Cursor · openhandapp — Agent finished). New src/notify_copy.ts mirrors the server's _decorated_title / _decorated_body + _DEFAULT_TITLES / _DEFAULT_BODIES and the Flutter client's decorateNotificationTitle / sourceLabelFor, pinned by a cross-check test so the CLI/server/app surfaces can't drift.
  • Click to open the IDE window for the project (macOS). Clicking the banner opens the IDE that raised the turn (Cursor / VS Code) focused on the project directory. Implemented zero-dependency: the branded AppleScript applet handles the click re-run (documented macOS behaviour) and open -a <ide> <project>. Best-effort — display-only when the IDE/project can't be named. Opens the project window, not a specific chat (IDEs lack reliable per-chat deep links).

0.3.0

  • New: Cursor stop.status → kind mapping (T-CUR1/T-CUR4). The on-stop dispatcher now reads the Cursor stop hook's stdin JSON and maps status to the right notification kind: completed→finished, aborted→awaiting_input, error→error. Back-compatible: absent/unrecognised status → finished. Run gocode-notify setup --force to pick up the updated hook command.
  • New: ralph_waiting + ralph_resumed kinds (T-C1). NOTIFY_KINDS now includes the Ralph/Homer offline-stall lifecycle kinds. ralph_waiting fires once on the stall edge (server drops repeats until a recovery event re-arms it); ralph_resumed is a silent control event that resets the stall state machine. See snippets/ralph-homer.sh for the edge-trigger pattern.
  • Kind taxonomy (T-S1). The server now classifies every kind into push-worthy vs. silent-info buckets: ralph_question / ralph_advanced are never pushed (Oracle-answerable questions are silent); ralph_halted / ralph_completed / ralph_waiting (edge) are the only loop kinds that reach FCM.
  • Foreground suppression (T-S2/T-S3). The server suppresses a push when the app is open on that exact chat (POST /api/v1/notify/presence heartbeat, 45s TTL). Different-chat or closed app → push goes out as normal.
  • Offline/stall debounce (T-S4). Server-side edge-triggered state machine: a loop retrying every 60s on a quota outage pushes once on stall and stays silent until it recovers and stalls again.

0.2.0

  • New: launch / autopilot command — hand a large, multi-step task off to your GoCode server to run as an autonomous Autopilot loop. Reuses the existing gck_ pairing; prints a loop id + app deep link; explicit failures exit non-zero (no offline queueing). See Hand off to your server.
  • New: gocode_launch_autopilot MCP tool — the third tool on the stdio MCP server, so a desktop agent can fire a remote loop mid-session (explicit offload only). The installed Cursor rule / Claude skill / OpenCode snippet teach the agent when to offload and that the server loop is a fresh, self-contained agent with no access to local files.
  • App monitoring — IDE-launched loops are discovered by the phone app (GET /api/v1/ralph/active), badged "Launched from ", and deep-link from the completion/halt push into the run — full parity with app-launched loops.
  • Fix: --version now reports the real package version. src/version.ts had drifted (published 0.1.6 reported 0.1.3); it is now kept in lockstep with package.json, and the publish workflow refuses to ship a mismatch.

0.1.x

  • Phone notifications for any coding agent via three triggers (runtime hook, MCP tool, loop snippet), opt-in auto-push to git with an AI-written commit message, and server-synced settings.

Develop

cd tools/gocode-notify
npm install
npm run build      # compile TypeScript -> dist/
npm test           # builds, then runs node --test on dist/test/
npm run typecheck  # type-check only, no emit

Zero runtime dependencies beyond the MCP SDK (Node built-in fetch/fs/ readline for everything else). Tests use Node's built-in test runner (node:test).

Layout

| Path | Purpose | |---|---| | src/cli.ts | gocode-notify bin entrypoint + command dispatcher (incl. on-stop, config) | | src/setup.ts | Installer orchestration (pair → detect → write configs) | | src/claude.ts / src/cursor.ts / src/opencode.ts | Per-client config writers (hooks + MCP + rule/skill); hooks call the on-stop dispatcher. OpenCode uses a session.idle plugin instead of a hooks file | | src/send.ts / src/login.ts / src/mcp.ts | Core send, pairing, and MCP server (incl. gocode_launch_autopilot) | | src/desktop_notify.ts | Branded native desktop banners (macOS GoCode.app / Windows WinRT toast / Linux notify-send); zero-dep, best-effort; macOS click-through opens the IDE window | | src/notify_copy.ts | Banner wording mirror — reproduces the server/Flutter title/body decoration so the desktop banner reads word-for-word like the phone | | assets/gocode-icon.{png,ico} | Bundled GoCode icon used to brand desktop banners | | src/launch.ts | Shared launch() core behind the launch/autopilot command + the MCP tool | | src/push.ts | Auto-push flow behind the on-stop dispatcher — git add/commit/push, FF-only | | src/commit_message.ts | AI commit-message resolution chain + deterministic fallback | | src/config.ts | config get\|set\|pull — the dev-machine settings editor (server-synced) | | src/settings.ts | Canonical Notify settings schema + valid-key specs (mirrors the server + app) | | snippets/ralph-homer.sh | Opt-in loop completion/halt snippet (trigger C) | | test/ | node:test smoke + unit tests |