Skip to content

Releases: Nickdevcode/Ringly

v0.6.0 — Friendly release notes + /ringly-help

27 May 01:22

Choose a tag to compare

Highlights

  • /ringly-update now shows a friendly summary of what changed before asking for confirmation — read straight from the packaged CHANGELOG, rewritten in plain language for non-developers (≤10 lines, no file paths, no jargon).
  • The slash command now respects your configured language across every line it writes itself. Fixed the long-standing bug where /ringly-update mixed English step headers and prompts even when pluginConfigs.ringly.options.language was pt-BR. The CLI now embeds the resolved language directly in the --check JSON snapshot so the .md has a single source of truth.
  • New /ringly-help slash command runs the translated ringly help and shows the full command list in the chat, with a clear "run these commands in your terminal, not here" warning.
  • New ringly help CLI subcommand replaces yargs' raw English --help output with a translated overview. Works as ringly help, ringly --help, ringly -h, and ringly with no args.

What's changed in detail

Added

  • Friendly release-notes summary in /ringly-update, read from the packaged CHANGELOG.md (no GitHub API call, no rate limits).
  • /ringly-help slash command.
  • ringly help CLI subcommand with full i18n.
  • src/core/changelog.ts — Keep-a-Changelog parser, no new runtime dependency.

Changed

  • ringly update --check JSON snapshot now includes language (resolved server-side, never auto) and notes (structured {version, heading, groups: [{title, items}]}).
  • Interactive ringly update also prints localized notes before the confirmation prompt — parity between terminal and slash command.
  • /ringly-update rewritten with explicit per-step language instructions tied to snapshot.language.

Tests

  • +30 new tests (133 → 163). Full coverage of the parser, the help renderer, and buildNotesFor running against the project's actual CHANGELOG entries.

Compatibility

  • Nothing breaks. The 4 original fields of ringly update --check JSON stay identical (current, latest, hasUpdate, reachable) — the new language and notes fields are additive.
  • Hook bundle is untouched. dist/hook.js / dist/hook.cjs stay at the same size — the changelog parser only loads from the CLI path, never from the hot path that runs on every notification event.
  • All 133 previous tests pass with no behaviour change.

How to update

npm install -g ringly@latest

Or, inside Claude Code, run /ringly-update and try the new flow for yourself — it'll show you a summary of what's in this release before asking to install.

See the full CHANGELOG entry for the complete bilingual (pt-BR + en-US) details.

🤖 Generated with Claude Code

v0.5.2 — leaner hook bundle via read/write split

26 May 06:30

Choose a tag to compare

🇧🇷 Português

Mudado

  • Bundle do hook ficou mais enxuto e separado da escrita de settings (src/core/claudeSettings.ts, src/core/claudeSettingsWrite.ts, src/core/config.ts, src/core/configWrite.ts). Antes, o hook (caminho quente, executado em cada Notification / Stop / SubagentStop) arrastava o módulo inteiro de manipulação de ~/.claude/settings.json — inclusive chmodSync, copyFileSync e readdirSync de node:fs, que são usados só pra escrever / fazer backup / podar backups antigos. Como o hook só lê config, esses imports nunca eram chamados, mas viajavam no bundle e geravam três warnings de tree-shake do Rollup a cada npm run build. Agora claudeSettings.ts ficou estritamente read-only (só existsSync + readFileSync + homedir + join) e claudeSettingsWrite.ts carrega tudo que escreve. O mesmo princípio foi aplicado a config.ts (loadConfig + applyEnvOverrides) vs. configWrite.ts (saveConfig). Resultado: dist/hook.js / dist/hook.cjs não puxam mais chmodSync / copyFileSync / readdirSync, e os warnings somem do build.
  • Comportamento externo: nenhum. Quem usa o CLI (ringly config, ringly init, ringly uninstall) ou o plugin no Claude Code não percebe diferença — toda mudança é interna ao layout dos módulos src/core/. Os 133 testes existentes seguem passando sem alteração de comportamento.

Notas pra quem tá vindo da v0.5.1

  • Nada quebra. A v0.5.1 (fix do spawn EINVAL no Windows) continua entregando exatamente o mesmo comportamento aqui. Esta release é puramente uma refatoração de bundle: hook mais limpo, separação read/write clara, sem custo nenhum pro usuário final.

🇺🇸 English

Changed

  • Hook bundle is leaner and the settings-write surface is decoupled (src/core/claudeSettings.ts, src/core/claudeSettingsWrite.ts, src/core/config.ts, src/core/configWrite.ts). Previously the hook (the hot path, executed on every Notification / Stop / SubagentStop) dragged in the entire ~/.claude/settings.json manipulation module — including chmodSync, copyFileSync, and readdirSync from node:fs, which only the writer needs (write, backup, prune old backups). Since the hook only reads config, those imports were never called but still shipped in the bundle and triggered three Rollup tree-shake warnings on every npm run build. Now claudeSettings.ts is strictly read-only (only existsSync + readFileSync + homedir + join) and claudeSettingsWrite.ts carries everything that writes. The same principle was applied to config.ts (loadConfig + applyEnvOverrides) vs. configWrite.ts (saveConfig). Outcome: dist/hook.js / dist/hook.cjs no longer pull in chmodSync / copyFileSync / readdirSync, and the build warnings are gone.
  • External behavior: none. CLI users (ringly config, ringly init, ringly uninstall) and Claude Code plugin users see no difference — every change is internal to the src/core/ module layout. All 133 existing tests pass with no behavior change.

Notes for v0.5.1 users

  • Nothing breaks. v0.5.1 (the Windows spawn EINVAL fix) keeps delivering the same behavior here. This release is purely a bundle refactor: cleaner hook, clear read/write separation, zero cost to the end user.

📦 Install: npm install -g ringly@0.5.2
📦 Update from Claude Code: /ringly-update
📋 Full CHANGELOG: CHANGELOG.md

v0.5.1 — Spawn npm via shell on Node 20.12+ to unblock ringly update

26 May 05:39

Choose a tag to compare

v0.5.1 — Spawn npm via shell on Node 20.12+ to unblock ringly update on Windows

This patch fixes a regression on Windows where ringly update --yes (and therefore /ringly-update and the SessionStart auto-check) was throwing spawn EINVAL before npm could even start, leaving users with the raw errno and no way to update from inside Claude Code.

Fixed

  • ringly update --yes was failing on Windows with spawn EINVAL before npm even started (src/commands/update.ts). Root cause: since Node 20.12 / 21.7 / 22+ (CVE-2024-27980), Node refuses by design to spawn .bat / .cmd files directly on Windows without shell: true — exactly what we were doing with spawn("npm.cmd", [...], { shell: false }). The error fired before any localized CLI message could appear, so users only saw the raw spawn EINVAL and had no idea what had broken.

    We now pass shell: true on Windows (letting cmd.exe resolve the npm shim); macOS and Linux still use shell: false, behavior unchanged. Since the arguments are hardcoded literals (install -g ringly@latest), there is no command-injection surface. Anyone who was hitting this on Windows via ringly update or /ringly-update can now update normally.

Changed

  • src/commands/update.ts now exports a pure buildNpmInstallSpec(platform) helper that returns { command, args, options }, deciding the shell per platform. This isolates the spawn decision and makes it unit-testable without mocking node:child_process. runNpmInstallLatest stays private and uses the spec.

Tests

  • +4 new tests in test/update.test.ts covering the regression: same command/args across platforms, shell: true on Windows, shell: false on macOS/Linux, and stdio / windowsHide preserved. The EINVAL was reproduced directly on Node v22.14.0 before the fix; the new path runs npm successfully.

Upgrade notes

  • Nothing breaks. The fix is internal to the ringly update subcommand / SessionStart hook / /ringly-update slash command.
  • macOS and Linux users notice no difference.
  • Windows users on modern Node who were hitting spawn EINVAL can now run /ringly-update straight from Claude Code, or ringly update from the terminal, without having to copy-paste npm install -g ringly@latest themselves.

Full changelog: v0.5.0...v0.5.1

v0.5.0 — Drop userConfig, CLI-only configuration

26 May 05:34

Choose a tag to compare

v0.5.0 — Drop userConfig, CLI-only configuration

This is a small but breaking release: it removes Ringly's userConfig block from plugin.json because the official Claude Code plugin-manager schema ships three UX issues we can't fix from the plugin side. Configuration is now exclusively CLI-driven through ringly config, hand-editing ~/.claude/settings.json, or re-running ringly init.

Breaking change

  • Removed userConfig from plugin/.claude-plugin/plugin.json. Up to v0.4.x, Claude Code exposed Ringly under /pluginInstalledRinglyConfigure with a native settings screen. That screen shipped three known issues that came from Claude Code's official userConfig schema, not from our plugin:

    • The language field rendered as a free-text input — the schema has no enum support. A typo on pt-BR / en-US silently fell back to auto.
    • On booleans, Enter only navigated between fields; only Space actually toggled them. Multiple users reported "I unchecked it and it stayed on."
    • No atomic write, no /reload-plugins reminder.

    Until Anthropic adds enum support to the schema and fixes the Enter behavior, we'd rather drop userConfig entirely than pretend the UX was fine. Configuration is now CLI-onlyringly config (the recommended TUI), hand-editing ~/.claude/settings.json, or re-running ringly init. The plugin still shows up under /plugin → Installed (hooks are still registered), but the Configure entry simply does not exist for Ringly anymore. Anyone with values already saved under pluginConfigs.ringly.options loses nothing: the dispatcher and ringly config keep reading and writing the exact same keys. Only the native plugin-manager UI is gone.

Changed

  • displayName and description in plugin.json updated to make it explicit that configuration goes through ringly config, not the plugin manager. Anyone browsing the marketplace sees it right in the description.
  • Final screen of ringly config (TUI ConfigDone) rewritten: the "You can also configure via /plugin → Installed → Ringly → Configure" box was replaced with a "CLI-only configuration — Ringly does not use the plugin-manager screen. To change these settings later, run ringly config again." note. The yellow /reload-plugins reminder is unchanged.
  • ringly doctor hints that pointed at "open /plugin in Claude Code → Installed → Ringly → Configure" now point at ringly config only. The check itself is still called "Ringly settings in ~/.claude/settings.json" and still validates the same pluginConfigs.ringly.options key — only the hint copy changed.
  • pt-BR.json and en-US.json locales gained tui.config.cli_only_title and tui.config.cli_only_body; tui.config.also_available and tui.config.plugin_path were removed (no consumer remained after the ConfigDone rewrite). Doctor hints (cli.doctor.check.plugin.notfound_hint and nooptions_hint) were rewritten to drop any mention of the plugin manager.

Docs

  • README.md gained a "Why we don't use Claude Code's plugin manager" section (pt-BR and en-US) that explains the three problems above, links directly to the official plugin reference, and documents that the removal is intentional, not a bug. The "Three ways to configure" table became "How to configure (the only supported flow)" with three entries (ringly config, hand-edit, ringly init for reinstall). The "How Ringly resolves the config at runtime" section was simplified from four layers to two (settings.json + env-var overrides).
  • plugin/README.md says the same thing, shorter, linking back to the main repo.
  • CONTRIBUTING.md gained an "About plugin.json with no userConfig" section (pt-BR and en-US) so contributors don't try to reintroduce userConfig without context. The note asks them to first confirm Anthropic has added enum support before proposing the change.

Upgrade notes

  • Nothing breaks. ringly config keeps working identically, ringly init keeps working identically, ~/.claude/settings.json keeps being read identically. The only thing that changes is that the /plugin → Installed → Ringly → Configure screen no longer exists.
  • If you configured anything via the plugin manager on v0.4.x, those values live in pluginConfigs.ringly.options in settings.json and are still honored — no migration needed.
  • If you want to reopen the configurator now that the screen is gone, run ringly config in your terminal. You get the full TUI with arrow keys, space to toggle, and a visual language picker.

Full changelog: v0.4.0...v0.5.0

v0.4.0 — Guided self-update with /ringly-update and SessionStart auto-check

26 May 05:38

Choose a tag to compare

v0.4.0 — Guided self-update with /ringly-update and SessionStart auto-check

This release adds a complete self-update story to Ringly: a /ringly-update slash command that runs inside Claude Code, a once-a-day background npm check on session start that fires a native toast when a new version ships, and a new ringly update CLI subcommand that backs both flows.

Added

  • /ringly-update slash command (plugin/commands/ringly-update.md). Runs inside Claude Code. Detects whether the npm CLI is installed, queries npm via ringly update --check, shows the version diff, asks for confirmation through AskUserQuestion, runs ringly update --yes, and reminds you to run /reload-plugins. The allowed-tools field is scoped to ringly:* and npm install -g ringly:* — the command never touches user files directly; everything goes through the CLI.

  • SessionStart hook (plugin/hooks/hooks.json + src/commands/updateCheckHook.ts). Once a day, at session start, the plugin queries npm in the background. When a newer version is available, it dispatches the same native toast the existing notification hooks use — title "Ringly", body localized in pt-BR/en-US, telling you to run /ringly-update. Throttle state is persisted at ${CLAUDE_PLUGIN_DATA}/last-update-check.json. Opt-out via check_updates: false skips all I/O before the throttle file is even read. The hook is fail-silent: any failure logs and exits 0 so it never delays or blocks session start.

  • ringly update CLI subcommand (src/commands/update.ts). Works outside of Claude Code too. Three modes:

    • ringly update — interactive, with a visual box, s/y/N confirmation, and localized strings.
    • ringly update --check — just prints {current, latest, hasUpdate, reachable} JSON for scripts and for /ringly-update consumption.
    • ringly update --yes — skip confirmation and install directly.

    Detects EBUSY / EPERM / access is denied in the npm install stderr and swaps the message for a "close Claude Code and retry" hint on Windows.

  • check_updates: boolean option added to plugin/.claude-plugin/plugin.json#userConfig (default true), with the matching key wired through claudeSettings, RinglyConfig, applyEnvOverrides (CLAUDE_PLUGIN_OPTION_CHECK_UPDATES), and the dispatch.mjs DEFAULT_OPTIONS / eventEnabled switch.

  • src/core/updateCheck.ts — isolated, dependency-free module with:

    • checkForUpdate (Node 20+ fetch with a 3 s AbortController timeout).
    • compareSemver (prerelease-aware).
    • shouldCheckUpdate / recordCheck / readLastCheckRecord (24 h throttle, atomic write of timestamp).
  • src/core/ownVersion.ts — walk-up helper that finds the package.json with name: "ringly" so both update.ts and updateCheckHook.ts resolve the current version correctly when running from source, from the dist/ bundle, or from a globally-installed npm prefix.

Changed

  • plugin/hooks/dispatch.mjs whitelists SessionStart and treats it as a separate path: tries the Node module / CLI binary delegations like the other events, but does not fall back to the embedded toast — the event is a check, not a direct notification, so without the CLI there's nothing useful to fire.
  • src/cli.ts internal hook subcommand now accepts SessionStart as a positional event, used by the dispatcher's CLI-binary fallback path.
  • RinglyConfig gained checkUpdates: boolean, propagated through types.ts, config.ts (DEFAULT_CONFIG + applyEnvOverrides), and claudeSettings.ts (read/write under pluginConfigs.ringly.options.check_updates).

Tests

  • +35 new tests (94 → 129).
  • test/updateCheck.test.ts covers 30 scenarios: semver comparison (equal/greater/lower/prerelease), input validation (bad package names and bad semver), shouldCheckUpdate at every boundary, recordCheck / readLastCheckRecord round-trip and corrupt JSON, checkForUpdate against 200/404/network throw/invalid JSON/AbortController timeout, and a custom registry URL.
  • test/updateCheckHook.test.ts covers 5 hook-level scenarios: opt-out via CLAUDE_PLUGIN_OPTION_CHECK_UPDATES, throttle within 24 h, no-update branch, unreachable-network branch, and tolerance to a malformed last-update-check.json.

Docs

  • README "Updating" section rewritten in pt-BR and en-US covering /ringly-update, the auto-check, the manual update path, and how to disable the check. New CLI commands documented. check_updates added to the options table. "How it works" updated to include SessionStart.
  • plugin/README.md lists all 5 hooks (including SessionStart) and describes the new slash command + check_updates option.
  • CONTRIBUTING.md updated: test count bumped from 94 to 129, the ALLOWED_EVENTS whitelist now lists SessionStart with its special-case behavior, and the project layout includes commands/ringly-update.md, core/ownVersion.ts, core/updateCheck.ts, commands/update.ts, and commands/updateCheckHook.ts.

Upgrade notes

  • After upgrading, run /reload-plugins inside Claude Code so the new SessionStart hook registers.
  • If you don't want the auto-check, set check_updates: false in ~/.claude/settings.json under pluginConfigs.ringly.options (or run ringly config and toggle it).
  • The auto-check is fully fail-silent and bounded by a 24 h throttle, so it will never delay session start or hammer the npm registry.

Full changelog: v0.3.0...v0.4.0

v0.3.0 — Security hardening, legacy removal, atomic writes

26 May 05:38

Choose a tag to compare

v0.3.0 — Security hardening, legacy removal, atomic writes

This is a major release focused on security, robustness, and removing dead weight. It deletes the entire legacy compatibility system, makes the settings.json writes atomic and crash-safe, validates and truncates every untrusted input, and aligns versions across package.json and plugin.json after a previous drift.

Breaking changes

  • Removed the entire legacy system. The src/core/legacy.ts module, the ringly init --migrate-legacy and ringly uninstall --legacy flags, the doctor legacy check, and the migration section in the README were deleted. This system existed to detect and disable pre-Ringly PowerShell hooks at ~/.claude/hooks/notify-toast.ps1 that could fire duplicate notifications. Since Ringly has been stable across several versions, the compatibility bridge became dead weight. Migration: if you still have legacy PowerShell hooks in ~/.claude/hooks/, remove them manually before installing Ringly 0.3.0 — or run ringly uninstall --legacy on 0.2.x before upgrading.

  • Removed the ~/.config/ringly/config.json (env-paths) fallback. Config now lives only in ~/.claude/settings.json under pluginConfigs.ringly.options. Pre-0.2.x installs need to run ringly init once to migrate; 0.2.x+ installs already use settings.json as the primary source and don't need to do anything. Bundle shrunk ~14 KB on cli.js and ~3 KB on hook.js as a result.

  • ringly uninstall now removes the pluginConfigs.ringly key from settings.json (with atomic write + backup) instead of deleting the old config.json. The --keep-config flag still exists to preserve your settings.

Security & robustness

  • Atomic write of ~/.claude/settings.json (src/core/atomicWrite.ts). Previously, writeFileSync wrote directly to the final file, and a race between ringly config (TUI) and a hook fired by Claude Code could corrupt the file or lose changes. Writes now go to a temp file (settings.json.tmp.<pid>.<rand>) and only get renamed to the final file via an atomic rename (atomic on NTFS since Windows Vista; guaranteed by POSIX). On failure the temp file is removed — no partial files.
  • Lightweight payload validation (src/core/payloadGuards.ts). JSON from Claude Code's stdin now passes through coerceClaudeHookPayload, which accepts only whitelisted hook_event_name values, truncates message / agent_type / error_type / error at 500 chars, truncates cwd / transcript_path at 1024 chars, and drops unknown fields. No new dependency; addresses DoS-via-large-payload risk and adds defense in depth.
  • Stdin limit reduced from 10 MB to 256 KB in both the CLI (src/core/stdin.ts) and the plugin dispatcher (plugin/hooks/dispatch.mjs). Real Claude Code payloads are typically <2 KB; 256 KB is already a generous defense.
  • Validation of appId loaded from config (src/core/config.ts). Only [A-Za-z0-9._-]{1,128} is accepted; invalid values fall back to Claude.Code.CLI with a warning. Defense in depth — PowerShell escaping was already safe, but explicit validation prevents surprises if a third-party plugin writes garbage to the field.
  • settings.json set to mode 0600 on Linux/macOS after each write. Since the file may contain tokens from other plugins, restricting reads to the owner is the right posture. Windows still inherits ~/.claude ACLs.
  • Automatic GC of old backups. Previously, each ringly config left a permanent settings.json.ringly-bak.<timestamp>. Backups older than 7 days are now removed automatically before creating a new one.
  • Log rotation at 5 MB (src/core/logger.ts). The ringly.log was growing unbounded in debug mode. Files over 5 MB are now rotated to ringly.log.1 (overwriting the previous rotation). The size check is throttled to once per minute to keep the hot path cheap.

Behavior

  • Locale detection reordered: precedence is now CLAUDE_PLUGIN_OPTION_LANGUAGEIntl.DateTimeFormatLANG / LC_* → fallback en-US. Previously LANG came before Intl, which gave wrong results for BR users running Claude Code from WSL/Git Bash with LANG=C.UTF-8.
  • macOS/Linux toast now warns explicitly that it is not implemented instead of failing silently. The toast channel's isAvailable() returns true for windows/macos/linux, and the macOS/Linux stubs print a clear stderr message (once per process) pointing to the GitHub tracker. Previously, ringly test on macOS was a silent no-op.

Build & packaging

  • Smaller npm tarball. package.json#files now only includes bin/, dist/, plugin/, scripts/, README.md, LICENSE, CHANGELOG.md. The src/, tsup.config.ts, and tsconfig.json entries were removed — end users don't need source nor build config (that path is only used via npm install -g <github-shorthand>, already covered by scripts/prepare.js). Final tarball: ~242 KB / 22 files.
  • sideEffects: false enabled in package.json so consumers importing ringly/hook get real tree-shaking.
  • Versions synced: package.json and plugin/.claude-plugin/plugin.json are both at 0.3.0 now. Previously there was a mismatch (package.json@0.2.4 vs plugin.json@0.2.1, last published tag v0.2.3).
  • npm run lint/lint:fix/format scripts now also cover plugin/hooks/ (matching biome.json includes).

CI / Release

  • ci.yml now runs npm run lint (covers plugin/hooks/) plus smoke execution of dist/cli.js --version, --help, and a sample hook on ubuntu-latest. Catches top-level runtime / circular import errors before publish.
  • release.yml verifies that the git tag matches both package.json#version and plugin.json#version before publishing to npm. Blocks silent inconsistencies.
  • Dispatcher timeout standardized to 12 s (was 10 s), giving margin over the 8 s PowerShell timeout to avoid killing the child Node before the toast finishes.

Tests

  • +12 new tests across new files: payloadGuards, stdin, notifier, channels, runHook. Critical-path coverage is now >80% for src/core/ and src/channels/.
  • Additional claudeSettings tests verify the atomic write leaves no orphan .tmp.* files and that backup GC respects the 7-day window.
  • detectSystemLanguage tests validate the new Intl-first fallback order with mocked Intl.DateTimeFormat.

Coverage thresholds were adjusted in vitest.config.ts to 65% on branches (others at 70%), with src/platform/** and integration-heavy commands (init/config/doctor/test/uninstall) excluded from measurement since they exercise PowerShell/AUMID/child_process flows.

Also includes the v0.2.4 Windows toast fix

If you skipped v0.2.4 (the retroactively-tagged Windows fix), the same change is included here: the PowerShell WinRT COM adapter bug (PowerShell#9816) that silently dropped toasts on Windows 11 / PowerShell 5.1 is fixed by reading $notifier.Setting.value__ directly.

Upgrade notes

  • After upgrading, run /reload-plugins inside Claude Code so the hooks pick up the new dispatcher.
  • If you had legacy PowerShell hooks at ~/.claude/hooks/notify-toast.ps1, remove them by hand or downgrade to 0.2.x, run ringly uninstall --legacy, then upgrade to 0.3.0.

Full changelog: v0.2.3...v0.3.0

v0.2.4 — Bypass WinRT COM adapter bug breaking toast notifications

26 May 05:39

Choose a tag to compare

v0.2.4 — Bypass WinRT COM adapter bug breaking toast notifications

🏷️ Note: this tag was created retroactively from commit 9f2da8e to align the GitHub release history with the [0.2.4] entry that already existed in CHANGELOG.md. The fix below is also included in v0.3.0 — if you're on v0.2.x and just want this specific fix without the v0.3.0 breaking changes (legacy system removal), you can pin to ringly@0.2.4.

This release fixes a critical Windows 11 / PowerShell 5.1 bug that caused toast notifications to be silently dropped, even when everything else looked correctly configured.

Fixed

  • Silently blocked notifications on Windows 11 / PowerShell 5.1. On several setups — especially Claude Code installed via global npm with the AUMID registered through a shortcut — the toast never appeared visually; only a short beep played. Root cause: a known PowerShell bug (PowerShell#9816) where WinRT objects implement IInspectable but not IDispatch. As a result, $notifier.Setting returned a mis-typed enum when compared with [NotificationSetting]::Enabled or concatenated into a string. The comparison silently produced a false-positive BLOCKED: classification with an empty reason (visible in logs as BLOCKED: with nothing after the colon), aborting Show() before the toast could fire.

    The check now reads $notifier.Setting.value__ — the intrinsic backing field of any .NET enum, accessed directly without going through the broken COM adapter — and compares against integer 0 (Enabled). If the read fails for any reason, the code falls through to Show() rather than block; any real error surfaces via the catch with a meaningful message instead of a phantom block. Non-zero values are mapped to readable reasons (DisabledForApplication, DisabledForUser, DisabledByGroupPolicy, DisabledByManifest).

  • Removed the [Console]::Beep(800, 200) fallback on the blocked path. That short beep was being emitted whenever the check false-positived and misled users into thinking the notification had actually arrived — when in fact it was just the beep. When the toast is genuinely blocked by system configuration, the CLI now returns BLOCKED: with a readable reason and emits no sound.

Tests

  • +14 new tests in test/psTemplates.test.ts covering the new robust check, the reason mapping, the absence of the misleading beep, and single-quote escaping in AUMID, XML, and shortcut path.

Upgrade notes

  • If you've been seeing the "short beep but no toast" symptom on Windows 11, upgrade to v0.2.4 (or v0.3.0+). The fix is purely platform-level — no config or migration needed.
  • If you want the modernized v0.3.0 (security hardening, atomic writes, legacy removal) and not just this fix, jump straight to v0.3.0.

Full changelog: v0.2.3...v0.2.4

v0.2.3 — Version bump

26 May 05:38

Choose a tag to compare

v0.2.3 — Version bump

Maintenance release. Only package.json and package-lock.json were touched to bump the version to 0.2.3 so the next published artifact lines up cleanly with the npm tag.

Changed

  • Version bump to 0.2.3 (db32ed4) — package.json and package-lock.json updated. No code or behavior changes since v0.2.2.

Upgrade notes

  • This is a version-alignment release. If you're already on v0.2.2 there's no functional reason to upgrade unless you want your installed CLI's --version output to read 0.2.3.

Full changelog: v0.2.2...v0.2.3

v0.2.2 — Install flow documentation fix

26 May 05:38

Choose a tag to compare

v0.2.2 — Install flow documentation fix

This is a docs-only patch that rewrites the install instructions in both README.md and plugin/README.md to match the actual install flow more accurately. No code changes.

Fixed

  • fix: Fixed install flow (cb66df6) — README and plugin/README.md had outdated steps that didn't match the real ringly init + /plugin marketplace add flow. Rewrote the sections so that someone discovering Ringly for the first time can follow the steps end-to-end without hitting dead ends. 151 lines changed in README.md and 14 in plugin/README.md.

Upgrade notes

  • No runtime changes; safe to skip if you're already on v0.2.1 and don't need the updated docs.

Full changelog: v0.2.1...v0.2.2

v0.2.1 — Hooks read settings.json directly + full TUI/CLI i18n

26 May 05:38

Choose a tag to compare

v0.2.1 — Hooks read settings.json directly + full TUI/CLI i18n

This release fixes a silent-failure bug that affected every previous version: the hook dispatcher was relying on CLAUDE_PLUGIN_OPTION_* environment variables that Claude Code does not export to hooks, so every user's config — language, event toggles, sound — was being silently ignored at runtime. It also lands full internationalization across the TUI and CLI.

Breaking changes

  • Hooks now read ~/.claude/settings.json directly. In every previous release, dispatch.mjs depended on CLAUDE_PLUGIN_OPTION_* environment variables that Claude Code does not actually export to hooks. The user's choices configured through the plugin manager were silently ignored, and the fallback always derived the language from the OS LANG. This is fixed: the dispatcher reads settings.json on every hook invocation and applies the language, event, and sound filters before any dispatch.

Fixed

  • Full TUI and CLI internationalization. Strings across the TUI (Welcome, LanguagePicker, HookPicker, SoundDebugPicker, AumidRegister, Done, ConfigDone) and the non-interactive commands (doctor, uninstall, test, init --non-interactive) were hardcoded in English. Now the entire interface respects the chosen language (pt-BR or en-US). During ringly init the TUI always starts in English — a deliberate design choice so every user gets a predictable starting point — and switches in real time the moment the user picks a language. Non-interactive commands honor the saved language.
  • Event filter now respected on every path. Previously, disabling events_stop from the TUI/plugin manager didn't stop dispatch.mjs from firing the embedded fallback — only the "rich" Node CLI path filtered. The filter now runs before any dispatch, on every path.
  • sound: false now silences the embedded fallback. The toast XML now uses <audio silent="true"/> when the user disables sound via settings.json. Before, sound played anyway.
  • Windows EINVAL spawn fix in tryCliBinary: .cmd/.bat files now run with shell: true, letting the rich CLI path actually work on Windows. Before, it always fell back to embedded.
  • tryNodeModule now also tries npm root -g as a second resolution strategy, so ringly/hook is found even when the plugin lives in ~/.claude/plugins/cache/ (which has no node_modules).
  • CLI loadConfig() now prefers ~/.claude/settings.json over the legacy env-paths config.json. The local config.json is still read as a fallback for pre-0.2.x installs and is still written (alongside settings.json) for compatibility. It will be removed in a future release.
  • ringly test without --lang now uses the user's actual config (from settings.json), instead of always falling back to internal defaults.

Docs

  • README and CHANGELOG corrected the runtime-resolution docs (env vars were never the real source) and added the recommendation to use ringly config for the best UX, since the plugin manager schema does not support enum and free-text input made typos silently fall back to auto.

Upgrade notes

  • After upgrading, run /reload-plugins inside Claude Code so the hooks pick up the corrected dispatcher.
  • If you had language or event toggles configured through the plugin manager on v0.2.0 and they "didn't seem to work" — they really weren't working. They start working on v0.2.1 without any migration needed; the values were saved in settings.json correctly all along, just not being read at runtime.

Full changelog: v0.2.0...v0.2.1