From d4c382dc6d7284c388d8b8893cd844c6a03c7e7c Mon Sep 17 00:00:00 2001 From: Bryan Johnson Date: Wed, 27 May 2026 20:11:19 -0700 Subject: [PATCH] v0.8.3: tab-completion trailing-space no longer breaks command dispatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The slash-command completer (__larry_complete_slash) intentionally appends a trailing space after a unique match for arg-command ergonomics, but the main_loop dispatcher matched exact `case` globs — so a completed `/quit ` missed the `/quit)` arm and fell through to "unknown command". Latent since v0.6.6 (tab completion). Fixed by rtrimming the dispatch key once at the `case "$input"` boundary, which also transitively protects the sub-command dispatchers (/origin, /phi-auto, /phi-sidecar, /mouse) that consume the same $input via _slash_args. Interior `/load FILE` spacing is preserved. Added a shared rtrim() helper to lib/cygwin-safe.sh next to strip_cr. Co-Authored-By: Clover (Claude Opus 4.7) --- CHANGELOG.md | 13 +++++++++++++ VERSION | 2 +- larry.sh | 14 +++++++++++++- lib/cygwin-safe.sh | 23 +++++++++++++++++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6615284..c9863b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ All notable changes to `cloverleaf-larry` / `larry-anywhere` are recorded here. Versioning is loose-semver; bumps trigger the in-process self-update on every running client via `LARRY_BASE_URL` + `MANIFEST`. +## v0.8.3 — 2026-05-27 + +- **Tab-completion trailing space no longer breaks command dispatch.** The + slash-command completer intentionally appends a trailing space after a + unique match (so arg-taking commands feel snappy), but the main_loop + dispatcher matched exact `case` globs, so `/quit ` (completed) missed the + `/quit)` arm and fell through to "unknown command". Latent since v0.6.6 + when tab completion shipped. Fixed by rtrimming the dispatch key once at + the `case "$input"` boundary (`larry.sh`), which tolerates the completer's + space, a user-typed trailing space, and any CR remnant while preserving + interior `/load FILE` argument spacing. Added a shared `rtrim()` helper to + `lib/cygwin-safe.sh` (and the inline fallback) next to `strip_cr`. + ## v0.8.2 — 2026-05-27 Microsoft Presidio sidecar for free-text NER. Closes V1 from Vera's audit — diff --git a/VERSION b/VERSION index 100435b..ee94dd8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.2 +0.8.3 diff --git a/larry.sh b/larry.sh index 000a00f..3b03c16 100755 --- a/larry.sh +++ b/larry.sh @@ -57,7 +57,7 @@ set -o pipefail # ───────────────────────────────────────────────────────────────────────────── # Config # ───────────────────────────────────────────────────────────────────────────── -LARRY_VERSION="0.8.2" +LARRY_VERSION="0.8.3" LARRY_HOME="${LARRY_HOME:-$HOME/.larry}" # ───────────────────────────────────────────────────────────────────────────── @@ -850,6 +850,7 @@ if [ -n "$LARRY_LIB_DIR" ] && [ -r "$LARRY_LIB_DIR/cygwin-safe.sh" ]; then else coerce_int() { local r="${1:-}" d="${2:-0}" c; c=$(printf '%s' "$r" | tr -cd '0-9'); printf '%s' "${c:-$d}"; } strip_cr() { local v="${1:-}"; printf '%s' "${v//$'\r'/}"; } + rtrim() { local v="${1:-}"; printf '%s' "${v%"${v##*[![:space:]]}"}"; } fi # v0.7.0: HL7 v2.x schema for inline tab completion + /hl7 / /hl7-fields slash @@ -4742,6 +4743,17 @@ main_loop() { local input="$LARRY_INPUT" [ -z "$input" ] && continue + # v0.8.3 — rtrim the dispatch key before the exact-match `case` below. + # Tab completion (__larry_complete_slash) intentionally appends a trailing + # space after a unique match, but bash `case` globs are literal: "/quit " + # never matches the "/quit)" arm and falls through to "unknown command". + # Stripping trailing whitespace here tolerates the completer's space, a + # user-typed trailing space, and any CR remnant in one defensive line. + # Trailing-only: interior "/load FILE" spacing is preserved, so argument + # parsing (${input#/load } etc.) is unaffected. Pure parameter expansion, + # no subshell. See lib/cygwin-safe.sh rtrim() for the shared helper. + input="${input%"${input##*[![:space:]]}"}" + case "$input" in /quit|/exit|/q) larry_say "bye."; break ;; /help) print_help; continue ;; diff --git a/lib/cygwin-safe.sh b/lib/cygwin-safe.sh index 75ca7b9..71ddd03 100755 --- a/lib/cygwin-safe.sh +++ b/lib/cygwin-safe.sh @@ -66,6 +66,29 @@ strip_cr() { printf '%s' "${v//$'\r'/}" } +# rtrim VAL — return VAL with all TRAILING whitespace removed (spaces, tabs, +# and CR — anything in [:space:]). Leading and interior whitespace untouched. +# +# Use immediately before a `case "$X" in ...) esac` pattern dispatcher whose +# arms are exact-string globs (e.g. /quit) /help)). Bash case patterns are +# literal globs, so a trailing space makes "/quit " miss "/quit)" and fall +# through to the catch-all. This bites tab completion: __larry_complete_slash +# intentionally appends a friendly trailing space after a unique match (so +# arg-taking commands feel snappy), which the exact-match dispatcher then +# rejects for no-arg commands. rtrim at the dispatch boundary tolerates the +# completer's space, a user-typed trailing space, and any CR remnant in one +# defensive line — without removing the completer's UX nicety. +# +# Trailing-only by design: interior spaces separate a command from its +# argument (/load FILE), so we must never collapse those. The expansion +# below strips the run of trailing whitespace chars only. +# +# Pure bash parameter expansion — no subshell, no external tools. +rtrim() { + local v="${1:-}" + printf '%s' "${v%"${v##*[![:space:]]}"}" +} + # read_clean VAR [PROMPT] — like `read -r VAR`, but every captured byte that # is \r gets stripped before the assignment. #