diff --git a/lib/hook-utils.sh b/lib/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/lib/hook-utils.sh +++ b/lib/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/lib/hook-utils.test.sh b/lib/hook-utils.test.sh index 3b2414fa3..b7a43a660 100755 --- a/lib/hook-utils.test.sh +++ b/lib/hook-utils.test.sh @@ -1612,6 +1612,38 @@ for bs_bad in "abc" "0" "-1" "1e3" ""; do rm -f "$bs_rc_file" "$bs_out_file" "$bs_err_file" done +# Sub-minimum positive values are a silent disable — same class as exact zero (#1883). +for bs_submin in "0.0000001" "0.000001"; do + bs_rc_file="$(mktemp)" + bs_out_file="$(mktemp)" + bs_err_file="$(mktemp)" + printf '{"ok":true}' | { + CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT="$bs_submin" hook::buffer_stdin \ + >"$bs_out_file" 2>"$bs_err_file" + echo "$?" >"$bs_rc_file" + } + bs_rc=$(cat "$bs_rc_file") + if [[ "$bs_rc" == "0" ]] && [[ "$(cat "$bs_out_file")" == '{"ok":true}' ]] && + [[ ! -s "$bs_err_file" ]]; then + ok "buffer_stdin: sub-minimum stdin_read_timeout '$bs_submin' → default" + else + fail "buffer_stdin sub-minimum timeout '$bs_submin': rc=$bs_rc" + fi + rm -f "$bs_rc_file" "$bs_out_file" "$bs_err_file" +done +resolved=$(CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT=0.00001 hook::resolve_read_timeout) +if [[ "$resolved" == "0.00001" ]]; then + ok "resolve_read_timeout: floor value 0.00001 is honored" +else + fail "resolve_read_timeout floor: got '$resolved' (expected 0.00001)" +fi +resolved=$(CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT=0.0000001 hook::resolve_read_timeout) +if [[ "$resolved" == "2" ]]; then + ok "resolve_read_timeout: below-floor value degrades to default" +else + fail "resolve_read_timeout below floor: got '$resolved' (expected 2)" +fi + # A VALID non-default value must still be honored — the guard must not collapse # every setting to the default. 0.5 s against a producer that holds the pipe until # the verdict is in, so the stall cannot lose a race with EOF. diff --git a/plugins/actionlint/.claude-plugin/plugin.json b/plugins/actionlint/.claude-plugin/plugin.json index 4ea6d2f35..a86e1d474 100644 --- a/plugins/actionlint/.claude-plugin/plugin.json +++ b/plugins/actionlint/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "actionlint", - "version": "0.8.6", + "version": "0.8.7", "description": "Lint GitHub Actions workflow files on edit via actionlint, surfacing findings as advisory context.", "author": { "name": "Melodic Software", diff --git a/plugins/actionlint/CHANGELOG.md b/plugins/actionlint/CHANGELOG.md index 0951053f7..d1cb9e9b4 100644 --- a/plugins/actionlint/CHANGELOG.md +++ b/plugins/actionlint/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `actionlint` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.8.7] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.8.6] ### Changed diff --git a/plugins/actionlint/hooks/hook-utils.sh b/plugins/actionlint/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/actionlint/hooks/hook-utils.sh +++ b/plugins/actionlint/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/autonomy/.claude-plugin/plugin.json b/plugins/autonomy/.claude-plugin/plugin.json index 40f6cf6e3..9797bf9c7 100644 --- a/plugins/autonomy/.claude-plugin/plugin.json +++ b/plugins/autonomy/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "autonomy", - "version": "0.16.4", + "version": "0.16.5", "description": "Governed autonomous agent operation: role-topology, binding-seam, wiring-vs-advisor, telemetry, return-accounting, trigger-dispatch, per-work-class guardrail-matrix, standing-routine-catalog, and design-only runner-charter contracts for climbing the AI-adoption ladder, plus a guided-setup skill that discovers an adopting org's state, writes its schema-versioned binding, wires standards-pinned OTLP emission with a zero-cost file-artifact default, wires human-attested return capture at the task boundary, wires signal adapters with one governed dispatch entrypoint, binds the five-class guardrail matrix to an org's isolation substrates with an in-boundary live-validation probe before recording each fail-closed binding, and stands up standing-routine-catalog classes as scheduled temporal signal adapters behind the one governed queue with free scheduling defaults wired as reviewable changes and each routine's work-class mapping homed on the security surface.", "author": { "name": "Melodic Software", diff --git a/plugins/autonomy/CHANGELOG.md b/plugins/autonomy/CHANGELOG.md index f133485e6..bdb06ba8d 100644 --- a/plugins/autonomy/CHANGELOG.md +++ b/plugins/autonomy/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `autonomy` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.16.5] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + Versions 0.1.0–0.7.0 predate this file (introduced with 0.7.1); their history lives in the merged work-package PRs (#333, #343, #356, #372, #377, #600, #676). diff --git a/plugins/autonomy/hooks/hook-utils.sh b/plugins/autonomy/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/autonomy/hooks/hook-utils.sh +++ b/plugins/autonomy/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/bash-format/.claude-plugin/plugin.json b/plugins/bash-format/.claude-plugin/plugin.json index e2fe2edd0..bfbdd0cd0 100644 --- a/plugins/bash-format/.claude-plugin/plugin.json +++ b/plugins/bash-format/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "bash-format", - "version": "0.7.6", + "version": "0.7.7", "description": "Auto-format and lint shell scripts on edit via shfmt + ShellCheck, using the consuming repo's own .editorconfig and .shellcheckrc.", "author": { "name": "Melodic Software", diff --git a/plugins/bash-format/CHANGELOG.md b/plugins/bash-format/CHANGELOG.md index 2876de20c..1a9848280 100644 --- a/plugins/bash-format/CHANGELOG.md +++ b/plugins/bash-format/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `bash-format` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.7.7] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.7.6] ### Changed diff --git a/plugins/bash-format/hooks/hook-utils.sh b/plugins/bash-format/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/bash-format/hooks/hook-utils.sh +++ b/plugins/bash-format/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/biome-format/.claude-plugin/plugin.json b/plugins/biome-format/.claude-plugin/plugin.json index 8d0f3b8ce..6a34024ab 100644 --- a/plugins/biome-format/.claude-plugin/plugin.json +++ b/plugins/biome-format/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "biome-format", - "version": "0.6.6", + "version": "0.6.7", "description": "Auto-format and lint JS/TS/JSX/JSON on edit via Biome, only when a biome.json governs the repo \u2014 using the consuming repo's own Biome config.", "author": { "name": "Melodic Software", diff --git a/plugins/biome-format/CHANGELOG.md b/plugins/biome-format/CHANGELOG.md index 2622cfdb3..ca404adfd 100644 --- a/plugins/biome-format/CHANGELOG.md +++ b/plugins/biome-format/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `biome-format` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.6.7] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.6.6] ### Changed diff --git a/plugins/biome-format/hooks/hook-utils.sh b/plugins/biome-format/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/biome-format/hooks/hook-utils.sh +++ b/plugins/biome-format/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/claude-ops/.claude-plugin/plugin.json b/plugins/claude-ops/.claude-plugin/plugin.json index aaeb2ad65..c54f220cf 100644 --- a/plugins/claude-ops/.claude-plugin/plugin.json +++ b/plugins/claude-ops/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "claude-ops", - "version": "0.31.2", + "version": "0.31.3", "description": "Claude Code operations toolkit. Ten skills: inventory (read-only enumeration of the complete invocable surface \u2014 every built-in CLI command with aliases and hidden/gated status, every bundled skill, and every component of every installed plugin across all marketplaces; reads the shipped binary because upstream publishes no built-in command list, and carries an integrity verdict so a drifted build reports counts as floors rather than silently short totals), audit-install-state (read-only audit of the machine-scope ~/.claude installation directory and ~/.claude.json \u2014 full inventory split into an authored surface and rolled-up bulk trees, product-managed retention vs genuinely unmanaged state, filename-scheme resolution before any process-liveness check, and deliberate/mid-experiment detection; reports, never deletes), audit-performance (read-only slowness-diagnostic capture run at the moment the machine or a session feels slow \u2014 CLI version, retention-sweep health including the silent unparsable-settings pause, a timed census walk of the install tree as a sweep-cost proxy, active-session and plugin-fleet counts, a process census, and a bundled known-performance-issues reference; separates the three documented suspects \u2014 accumulated state, version regression, component bloat \u2014 and routes remediation out; reports, never mutates), observability (read locally captured telemetry \u2014 OTEL store, collector, hook-event JSONL, ccusage \u2014 with trend reports and store pruning), known-issues (search known Claude product GitHub bugs, check service health, maintain a persistent tracked-issue registry), changelog (ingest Claude Code changelog entries and integrate them into the current repo), plugins (bring a machine's plugin fleet current on demand \u2014 marketplace refresh, effective-scope updates including in-repo project/local installs, new-plugin install per policy, scope-divergence detection and explicit convergence), morning-brief (read-only gh-based operator morning view \u2014 queue-label counts, merge-ready PRs, parked decisions with their RECOMMENDED lines, and loop-lane telemetry freshness), lanes (start/restart/stop/status loop lanes as named background Claude Code sessions seeded from canonical prompt files, with per-lane model/effort, a repo-pull + marketplace-refresh launch step, and a consume-restarts action \u2014 an OS-schedulable reader that relaunches stopped lanes whose telemetry carries a restart_request), and a re-runnable setup action that settles where the known-issues registry lives. Plus a family of seven advisory *-audit telemetry-emitter hooks (API errors, config changes, instruction loads, permission denials, pre-compaction, skill usage, tool failures) that emit the shared hook-telemetry envelope, and a reference sink that maps envelopes into the hook-events.jsonl the observability skill reads.", "author": { "name": "Melodic Software", diff --git a/plugins/claude-ops/CHANGELOG.md b/plugins/claude-ops/CHANGELOG.md index 5de2bbc85..7ad9cc0bc 100644 --- a/plugins/claude-ops/CHANGELOG.md +++ b/plugins/claude-ops/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `claude-ops` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.31.3] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.31.2] ### Changed diff --git a/plugins/claude-ops/hooks/hook-utils.sh b/plugins/claude-ops/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/claude-ops/hooks/hook-utils.sh +++ b/plugins/claude-ops/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/context-guard/.claude-plugin/plugin.json b/plugins/context-guard/.claude-plugin/plugin.json index 95ca16d9a..8cd30a856 100644 --- a/plugins/context-guard/.claude-plugin/plugin.json +++ b/plugins/context-guard/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "context-guard", - "version": "0.7.4", + "version": "0.7.5", "description": "Per-session context-window observability plus the first shipped consumer: a statusline wrapper tees each session's context_window fields to a per-session snapshot file, a zone resolver classifies usage into smart/acceptable/dumb bands (percentage bands plus window-class token bands, conservative-min combination, zones.json SSOT with shipped defaults), a reader contract fixes how consuming sessions interpret the snapshots, and zone-crossing hooks report once per transition into a worse zone across two channels \u2014 the continuation menu to the operator, who owns that choice, and to the model only the zone determination plus the counter-steer that a zone word is not a decay signal (advisory by default; an optional blocking mode gates new mutating work on a fresh dumb-zone snapshot with handoff-writing exempt), with a PostCompact hook persisting an evidence-degraded marker.", "author": { "name": "Melodic Software", diff --git a/plugins/context-guard/CHANGELOG.md b/plugins/context-guard/CHANGELOG.md index 057702470..00c9d19a1 100644 --- a/plugins/context-guard/CHANGELOG.md +++ b/plugins/context-guard/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to the `context-guard` plugin. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.7.5] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.7.4] ### Changed diff --git a/plugins/context-guard/hooks/hook-utils.sh b/plugins/context-guard/hooks/hook-utils.sh index 8576bd232..0776e2f76 100755 --- a/plugins/context-guard/hooks/hook-utils.sh +++ b/plugins/context-guard/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/desktop-notification/.claude-plugin/plugin.json b/plugins/desktop-notification/.claude-plugin/plugin.json index 8b3239ba7..df4c54c75 100644 --- a/plugins/desktop-notification/.claude-plugin/plugin.json +++ b/plugins/desktop-notification/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "desktop-notification", - "version": "0.6.6", + "version": "0.6.7", "description": "Alert you when Claude Code needs input \u2014 an audible terminal bell, an OSC 9 terminal notification, and an OS-native toast (macOS/Linux) on permission and idle prompts.", "author": { "name": "Melodic Software", diff --git a/plugins/desktop-notification/CHANGELOG.md b/plugins/desktop-notification/CHANGELOG.md index bbc22fbb5..d017e23fe 100644 --- a/plugins/desktop-notification/CHANGELOG.md +++ b/plugins/desktop-notification/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `desktop-notification` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.6.7] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.6.6] ### Changed diff --git a/plugins/desktop-notification/hooks/hook-utils.sh b/plugins/desktop-notification/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/desktop-notification/hooks/hook-utils.sh +++ b/plugins/desktop-notification/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/eol-normalizer/.claude-plugin/plugin.json b/plugins/eol-normalizer/.claude-plugin/plugin.json index 9103727ed..c6ac3d191 100644 --- a/plugins/eol-normalizer/.claude-plugin/plugin.json +++ b/plugins/eol-normalizer/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "eol-normalizer", - "version": "0.6.6", + "version": "0.6.7", "description": "Normalize a written file's working-tree line endings to its .gitattributes eol value on edit \u2014 symmetric CRLF/LF driven by git check-attr, advisory and never blocking.", "author": { "name": "Melodic Software", diff --git a/plugins/eol-normalizer/CHANGELOG.md b/plugins/eol-normalizer/CHANGELOG.md index d588397ad..6a22fa29f 100644 --- a/plugins/eol-normalizer/CHANGELOG.md +++ b/plugins/eol-normalizer/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `eol-normalizer` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.6.7] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.6.6] ### Changed diff --git a/plugins/eol-normalizer/hooks/hook-utils.sh b/plugins/eol-normalizer/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/eol-normalizer/hooks/hook-utils.sh +++ b/plugins/eol-normalizer/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/go-format/.claude-plugin/plugin.json b/plugins/go-format/.claude-plugin/plugin.json index 6d4d14205..2f210b4aa 100644 --- a/plugins/go-format/.claude-plugin/plugin.json +++ b/plugins/go-format/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "go-format", - "version": "0.3.6", + "version": "0.3.7", "description": "Auto-fix Go formatting and import management on edit via goimports \u2014 runs unconditionally (no consumer-config gate), skipping generated files.", "author": { "name": "Melodic Software", diff --git a/plugins/go-format/CHANGELOG.md b/plugins/go-format/CHANGELOG.md index b46cbe6e8..a5eff7f2a 100644 --- a/plugins/go-format/CHANGELOG.md +++ b/plugins/go-format/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `go-format` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.3.7] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.3.6] ### Changed diff --git a/plugins/go-format/hooks/hook-utils.sh b/plugins/go-format/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/go-format/hooks/hook-utils.sh +++ b/plugins/go-format/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/guardrails/.claude-plugin/plugin.json b/plugins/guardrails/.claude-plugin/plugin.json index a66c7e17e..d014b2c34 100644 --- a/plugins/guardrails/.claude-plugin/plugin.json +++ b/plugins/guardrails/.claude-plugin/plugin.json @@ -135,5 +135,5 @@ "min": 1 } }, - "version": "0.28.7" + "version": "0.28.8" } diff --git a/plugins/guardrails/CHANGELOG.md b/plugins/guardrails/CHANGELOG.md index 4041bd23c..7996b246b 100644 --- a/plugins/guardrails/CHANGELOG.md +++ b/plugins/guardrails/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `guardrails` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.28.8] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.28.7] ### Fixed diff --git a/plugins/guardrails/hooks/hook-utils.sh b/plugins/guardrails/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/guardrails/hooks/hook-utils.sh +++ b/plugins/guardrails/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/markdown-format/.claude-plugin/plugin.json b/plugins/markdown-format/.claude-plugin/plugin.json index aec1e5eb1..7f49af78a 100644 --- a/plugins/markdown-format/.claude-plugin/plugin.json +++ b/plugins/markdown-format/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "markdown-format", - "version": "0.11.9", + "version": "0.11.10", "description": "Auto-format and lint Markdown on edit via markdownlint-cli2 \u2014 only in repos that carry their own markdownlint config.", "author": { "name": "Melodic Software", diff --git a/plugins/markdown-format/CHANGELOG.md b/plugins/markdown-format/CHANGELOG.md index b3a794dba..3051b816c 100644 --- a/plugins/markdown-format/CHANGELOG.md +++ b/plugins/markdown-format/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `markdown-format` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.11.10] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.11.9] ### Changed diff --git a/plugins/markdown-format/hooks/hook-utils.sh b/plugins/markdown-format/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/markdown-format/hooks/hook-utils.sh +++ b/plugins/markdown-format/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/powershell-format/.claude-plugin/plugin.json b/plugins/powershell-format/.claude-plugin/plugin.json index 500f17bc6..c0198f081 100644 --- a/plugins/powershell-format/.claude-plugin/plugin.json +++ b/plugins/powershell-format/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "powershell-format", - "version": "0.7.6", + "version": "0.7.7", "description": "Auto-format and lint PowerShell on edit via PSScriptAnalyzer, only when a PSScriptAnalyzerSettings.psd1 governs the repo \u2014 using the consuming repo's own analyzer settings.", "author": { "name": "Melodic Software", diff --git a/plugins/powershell-format/CHANGELOG.md b/plugins/powershell-format/CHANGELOG.md index 5860be5ae..ffecf7cc2 100644 --- a/plugins/powershell-format/CHANGELOG.md +++ b/plugins/powershell-format/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `powershell-format` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.7.7] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.7.6] ### Changed diff --git a/plugins/powershell-format/hooks/hook-utils.sh b/plugins/powershell-format/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/powershell-format/hooks/hook-utils.sh +++ b/plugins/powershell-format/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/rate-limit-guard/.claude-plugin/plugin.json b/plugins/rate-limit-guard/.claude-plugin/plugin.json index b59829d09..11f766907 100644 --- a/plugins/rate-limit-guard/.claude-plugin/plugin.json +++ b/plugins/rate-limit-guard/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "rate-limit-guard", - "version": "0.5.9", + "version": "0.5.10", "description": "Shared rate-limit guard for loop lanes: a statusline wrapper tees the subscription rate-limit windows to a fixed machine-scope file, a StopFailure hook records rate-limit stops reactively, and a reader contract fixes how consuming sessions pause and resume.", "author": { "name": "Melodic Software", diff --git a/plugins/rate-limit-guard/CHANGELOG.md b/plugins/rate-limit-guard/CHANGELOG.md index 55aa13997..6a57b5dd3 100644 --- a/plugins/rate-limit-guard/CHANGELOG.md +++ b/plugins/rate-limit-guard/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `rate-limit-guard` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.5.10] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.5.9] ### Changed diff --git a/plugins/rate-limit-guard/hooks/hook-utils.sh b/plugins/rate-limit-guard/hooks/hook-utils.sh index 8576bd232..0776e2f76 100755 --- a/plugins/rate-limit-guard/hooks/hook-utils.sh +++ b/plugins/rate-limit-guard/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/ruff-format/.claude-plugin/plugin.json b/plugins/ruff-format/.claude-plugin/plugin.json index e2a412f5b..ccad36721 100644 --- a/plugins/ruff-format/.claude-plugin/plugin.json +++ b/plugins/ruff-format/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "ruff-format", - "version": "0.6.6", + "version": "0.6.7", "description": "Auto-format and lint Python on edit via Ruff, only when a Ruff config governs the repo \u2014 using the consuming repo's own Ruff config.", "author": { "name": "Melodic Software", diff --git a/plugins/ruff-format/CHANGELOG.md b/plugins/ruff-format/CHANGELOG.md index fc5e2a674..5d49e9b65 100644 --- a/plugins/ruff-format/CHANGELOG.md +++ b/plugins/ruff-format/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `ruff-format` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.6.7] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.6.6] ### Changed diff --git a/plugins/ruff-format/hooks/hook-utils.sh b/plugins/ruff-format/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/ruff-format/hooks/hook-utils.sh +++ b/plugins/ruff-format/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/source-control/.claude-plugin/plugin.json b/plugins/source-control/.claude-plugin/plugin.json index 53d6bda27..71afbf791 100644 --- a/plugins/source-control/.claude-plugin/plugin.json +++ b/plugins/source-control/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "source-control", - "version": "0.53.3", + "version": "0.53.4", "description": "Git and GitHub delivery workflow: /commit (Conventional Commits + Co-Authored-By trailer via safe heredoc mechanics), /pull-request (prep, create, CI monitoring, review-comment triage, merge, CI-log fetch), /babysit-prs (self-pacing fleet loop \u2014 safe by default; opt-in worker/autopilot tiers add gate-checked merge and thread resolution behind a deterministic Python engine), /babysit-loop (the loop-lane merge lane: a standing or drain loop that invokes babysit-prs per cycle, configured through repo-scoped babysit_loop_* keys on the layered source-control.md seam, with merge authority human-only until the target repo's tracked config adopts the lane, a gate-proven C2-mechanical baseline once adopted, and standing merge-rung raises binding from the team-tracked layer only \u2014 with one named exception, where an invocation line explicitly typing both the autopilot tier keyword and the dedicated raise argument --merge c3-this-run widens that single invocation's merge authority up to C3 behind a fresh independent frontier-tier resolver, while C4-structural and C5-untrusted-provenance stay unconditionally human-merge), /worktree (create, status, cleanup, audit for parallel-session isolation), /setup (check the effective commit-subject / PR-title convention merged across its config layers and the babysit-prs config, or apply \u2014 interview the repo and write the convention config to a chosen layer), and /resolve-conflicts (intent-first merge/rebase conflict resolution with a semantic-conflict sweep \u2014 never --abort). The commit-subject / PR-title convention is configurable via a source-control.md config written by a re-runnable setup skill, layered across a ~/.claude user-global file, the tracked team file, and a gitignored .claude/source-control.local.md personal overlay merged per key; Conventional Commits is the default when no convention is declared.", "author": { "name": "Melodic Software", diff --git a/plugins/source-control/CHANGELOG.md b/plugins/source-control/CHANGELOG.md index 748eece5a..56014b885 100644 --- a/plugins/source-control/CHANGELOG.md +++ b/plugins/source-control/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `source-control` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.53.4] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.53.3] ### Changed diff --git a/plugins/source-control/hooks/hook-utils.sh b/plugins/source-control/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/source-control/hooks/hook-utils.sh +++ b/plugins/source-control/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t" diff --git a/plugins/typos-format/.claude-plugin/plugin.json b/plugins/typos-format/.claude-plugin/plugin.json index 073329712..78197f2cb 100644 --- a/plugins/typos-format/.claude-plugin/plugin.json +++ b/plugins/typos-format/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "typos-format", - "version": "0.6.7", + "version": "0.6.8", "description": "Spell-check on edit via typos-cli, unconditionally \u2014 report-only by default, honoring the consuming repo's own typos configuration when one is present.", "author": { "name": "Melodic Software", diff --git a/plugins/typos-format/CHANGELOG.md b/plugins/typos-format/CHANGELOG.md index a950e961e..072a91694 100644 --- a/plugins/typos-format/CHANGELOG.md +++ b/plugins/typos-format/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to the `typos-format` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.6.8] + +### Changed + +- **Synced `hook-utils.sh`:** refuse sub-minimum `stdin_read_timeout` values (#1883). + ## [0.6.7] ### Changed diff --git a/plugins/typos-format/hooks/hook-utils.sh b/plugins/typos-format/hooks/hook-utils.sh index 8576bd232..0776e2f76 100644 --- a/plugins/typos-format/hooks/hook-utils.sh +++ b/plugins/typos-format/hooks/hook-utils.sh @@ -561,6 +561,10 @@ hook::json_complete() { # shell is exact where a version check would be a guess. Reading /dev/null hits # EOF immediately, so a valid timeout produces no stderr at all. The probe is # skipped for the default, which is known-good everywhere. +# Minimum 0.00001 s (10 µs): smaller positive values are a silent disable — the +# read returns before payload bytes arrive, same class as exact zero (#1883). +readonly HOOK_STDIN_READ_TIMEOUT_MIN_MICROS=10 + hook::resolve_read_timeout() { local t="${CLAUDE_PLUGIN_OPTION_STDIN_READ_TIMEOUT:-2}" if [[ "$t" != "2" ]]; then @@ -569,6 +573,14 @@ hook::resolve_read_timeout() { probe=$(read -r -t "$t" discard &1) if ! [[ "$t" =~ ^[0-9]+(\.[0-9]+)?$ ]] || [[ "$t" =~ ^0+(\.0+)?$ ]] || [[ -n "$probe" ]]; then t=2 + elif [[ "$t" =~ ^([0-9]+)(\.([0-9]+))?$ ]]; then + local whole="${BASH_REMATCH[1]}" frac="${BASH_REMATCH[3]:-}" + frac="${frac}000000" + frac="${frac:0:6}" + local micros=$((10#$whole * 1000000 + 10#$frac)) + if ((micros < HOOK_STDIN_READ_TIMEOUT_MIN_MICROS)); then + t=2 + fi fi fi printf '%s' "$t"