F-1 (HIGH — blocks regression): hl7-diff --format count always returned 0
because the early-exit in END fired before the diff loop ran. Fix: remove
the early exit; suppress per-diff printf in emit() for count mode; emit
DIFF_COUNT after the loop. count/text/tsv all agree (13 diffs on fixture,
0 on identical pair, exit codes correct). Ref: lib/hl7-diff.sh.
F-5 (MEDIUM — PHI leak): hl7-sanitize silently passed LF-delimited HL7
through as cleartext (awk RS="\r" never split on LF). Fix: detect CR
absence via python3 binary read; normalise LF/CRLF→CR via `tr` before
the awk pass. Both file and stdin paths handled. CR path is a zero-overhead
passthrough. Before: 0 tokens, cleartext PHI. After: 6 tokens, all PID
fields replaced with [[MRN_0001]] etc. Ref: lib/hl7-sanitize.sh.
F-2 (MEDIUM): nc-make-jump emitted { PORT {} } for file/ICL inbounds
because the guard only tested for empty ORIG_PORT; protocol-nested returns
the literal "{}" for empty blocks. Fix: case guard rejects empty, "{}", and
any non-numeric value with a clear "is it a TCP listener?" error (exit 1).
TCP inbounds (numeric PORT) still generate correctly. Ref: lib/nc-make-jump.sh.
F-3 (MEDIUM — manual marquee example): nc-msgs mrn=<bare> returned 0 on
real Epic MRNs stored as "5720501458^^^MRN". Fix: in field_matches "="
operator, when expected has no ^ and the stored repetition does, compare
component-1 (text before first ^). Full-componented and mrn.1= paths
unchanged. Fixture: bare mrn=5720501458 now matches 2/3 messages correctly.
Ref: lib/nc-msgs.sh.
All four files pass bash -n. MANIFEST regenerated (54 entries, --check=0).
Tested against synthetic fixtures on .135 (no live engine required for these
logic bugs). Work-box re-verify commands in audit §4-B.
Co-Authored-By: Clover (claude-sonnet-4-6) <noreply@anthropic.com>
An install switching TO broker-mode (the v0.9.0 default) carried long-lived
Anthropic/OAuth credentials from the pre-broker era. Broker-mode authenticates
via short-lived broker tokens and never uses them — they are a pure security
liability on the box, acutely so on a PHI box. On the next self-update the agent
now cleans them up automatically:
- Secure-deletes $LARRY_HOME/.api-key and .oauth.json (reuses the
uninstall-larry.sh shred -u -z -n3 -> overwrite -> rm logic).
- Strips the ANTHROPIC_API_KEY / CLAUDE_CODE_OAUTH_TOKEN LINES from
$LARRY_HOME/.env and from ~/.bashrc, ~/.bash_profile, ~/.profile (backup
first); every other line is kept.
- Idempotent (.broker-cred-wiped marker, written only after a run that removed
something); silent no-op when clean.
- Hard-guarded on LARRY_AUTH_MODE=broker: does NOT fire under the apikey escape
hatch (which legitimately still needs the key). Only the two Anthropic/OAuth
vars are touched (LARRY_* / GITEA_TOKEN are still needed in broker mode).
- Prints a reminder to ALSO revoke at the source (local deletion != server
revocation), per the decommission / kill-switch docs.
Fires at the broker-resolution block (after self_update synced a fresh
lib/broker.sh, before the fail-closed preflight). New functions in
lib/broker.sh: _broker_wipe_obsolete_credentials,
_broker_strip_cred_lines_from_env, _broker_strip_cred_lines_from_rc.
VERSION + MANIFEST regenerated. Tested: 31/31 assertions pass across the
upgrade-wipe, apikey-non-wipe, clean-no-op, idempotency, dangerous-path-guard,
and selective-line-strip paths.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 3 of the Larry remote kill-switch (Pax design; Mack's broker on .135 LAN
8181 / Tailscale 100.86.16.114:8181). Deployed Larry no longer holds a long-lived
sk-ant-… key: it holds a per-deployment enrollment secret, mints a short-lived
token from the broker, and routes every LLM call THROUGH the broker /v1/messages
(real key injected server-side). set-authorized <id> false => the deployment 401s
and dies, no box access required.
- LARRY_AUTH_MODE=broker is the DEFAULT (was apikey). Self-update flips existing
installs to broker-mode too, so upgrading Gundersen delivers the kill-switch.
Escape hatch (documented, not default): LARRY_AUTH_MODE=apikey (no kill-switch,
never for PHI boxes).
- New lib/broker.sh: enroll+mint, fail-closed heartbeat, best-effort PHI wipe
(reuses uninstall-larry.sh's shred/overwrite secure-delete + LARRY_HOME guard).
- Fail-closed preflight at launch + in-REPL heartbeat (default 60s, 3-miss budget):
disabled => refuse to run (+ PHI wipe for profile:phi); unreachable past budget
=> refuse to run (NO wipe on a network blip — only an explicit disable wipes).
- call_api / call_api_stream broker branch: Bearer short-lived token, no x-api-key,
token never on disk.
- install-larry.sh enrollment provisioning: LARRY_DEPLOYMENT_ID + LARRY_ENROLL_SECRET
(+ LARRY_PROFILE/LARRY_BROKER_URL) baked 0600 + into the shim; box shows up in the
dashboard ready to toggle.
- /auth reports broker state.
Reachability (flagged for Bryan): the broker is LAN + Tailscale only (no public
route). Egress-restricted boxes reach it over Tailscale (default URL = tailnet).
A box that can reach neither fail-closes = won't run (correct kill, useless work
state) — such a box MUST run Tailscale, or Bryan must stand up a hardened public
broker ingress.
Bug fixed in test: _broker_json_field jq `// empty` rendered literal false as
empty, mis-classifying a DISABLED deployment as an unreachable MISS (delaying
fail-close + skipping the PHI wipe). Fixed to `if has($k) then .[$k] else "" end`.
Verified end-to-end against the live broker: enroll -> mint -> proxied call ->
disable -> instant 401 + heartbeat fail-close + 5 PHI files shredded.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One-run `larry uninstall` / uninstall-larry.sh that:
- stops detached larry.sh REPL + phi-presidio-sidecar + larry-tunnel
(pgrep+kill by pattern, never kills itself/parent/uninstall-larry)
- SECURELY deletes cleartext PHI (auto-phi.log, lookup.tsv, sessions/*.log.md)
via shred -u -z -n 3, with overwrite-then-rm fallback on Windows/MobaXterm
where shred is absent, honest per-platform "secure achieved?" reporting,
and a find-less bash-glob fallback for session files
- strips ANTHROPIC_API_KEY|CLAUDE_CODE_OAUTH_TOKEN|LARRY_*|GITEA_TOKEN from
shell rc with a timestamped backup (default), or prints them under --keep-rc
- removes ~/larry, ~/.local/bin/larry, ~/bin/larry, ~/larry-anywhere (our shims
only; foreign `larry` preserved), then self-removes a standalone checkout
- prints a FINISH-AT-THE-SOURCE reminder: revoke API key + OAuth grant + PAT,
plus a BAA/PHI-disclosure note
- hard rm-rf-/ guards (empty/unset/root/$HOME/non-larry LARRY_HOME refused),
scoped strictly to the built target list; DRY-RUN default; new --keep-rc and
--no-shred flags
Tested: full real run, dry-run scope, all rm-rf guards, --keep-data,
no-shred(Windows) fallback, idempotency, standalone-checkout self-uninstall.
MANIFEST regenerated so the self-update ships it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two operator-requested features:
1. `larry uninstall` / uninstall-larry.sh — there was no uninstaller before.
Reverses install-larry.sh exactly: removes $LARRY_HOME (bundle + bin/jq +
optional phi-venv + all runtime artifacts incl. log/headers.log, sessions,
journal, lessons, creds) and the `larry` PATH shim. DRY-RUN by default;
--yes to delete, --keep-data to preserve user data. Removes ONLY what the
installer created (shim removed only if it carries our auto-gen header;
shell rc / Cloverleaf sites / $HCIROOT never touched). Stops running PHI
sidecar / tunnel via their own pidfiles. Shipped by the installer +
manifest-synced; dispatched early like `larry tools` so it works offline.
2. --no-api (env LARRY_NO_API=1) — deterministic-only mode making ZERO LLM API
calls (zero cost). REPL + all local/deterministic commands still work; a
free-text prompt is routed to the matching `larry tools <name>` instead of
the model. No API key required (first-run auth prompt skipped). call_api /
call_api_stream hard-refuse as defense in depth.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Point at a site and provision server_jump thread sets for ALL inbound root
threads (route the existing-env inbound feed to a new env). Pure composition
of validated tools (nc_find_inbound, nc-parse, nc_make_jump, nc_insert_protocol,
nc_add_route) under ONE journal session — whole batch rolls back in one command.
ALL-OR-NOTHING: steps gated on prior success, first failure auto-rolls-back the
session (exit 6); pre-flight collision check aborts (exit 5) before any write if
a jump-port or thread-name already exists. --dry-run previews the full plan.
Output hands `roots: <csv>` to nc_regression for bulk env-A-vs-B testing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New mutating tool, built on the proven journal/rollback foundation. Curated
safe field set only (rejects anything else; never creates a missing field).
Edits are line-number-anchored to the target thread's protocol block via
nc-parse (a shared port/host value in another thread is never touched),
brace-balance-checked before an atomic write, journaled for byte-identical
rollback. Flags: --dry-run (no write), --confirm yes, --site, --netconfig.
Copy-tested: PORT + HOST applied surgically, rollback byte-identical.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tested all mutating tools (nc_table/nc_add_route/nc_insert_protocol/
nc_create_thread/nc_make_jump/nc_tclgen) on a throwaway copy: every change is
journaled and rolls back byte-identical across --session/--entry/--target/
--last granularities. Fixed nc-create-thread --host brace-collision (emitted
invalid TCL { HOST x} }; now balanced { HOST x }, and { HOST {} } when omitted)
and lessons.sh:142 printf option-injection. Read fixture verified untouched.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ran every read/analysis tool against the real 24-site integrator (lib + wired
dispatch). Fixed: nc-find --name (GNU sed \+ → POSIX; 0 rows on BSD/macOS),
nc-find tsv/jsonl exit-1-on-success, nc-parse tclproc-refs dropping
digit-leading procs (3M_check_ack), nc-xlate diff missing --site,
nc-diff-interface + nc-smat-diff printf '-'-leading option-injection dropping
output, nc-status not-up crashing on --format, and nc-status not-up's gawk-only
\<up\> word-boundary → portable form (BSD/macOS). Test matrix in Deliverables.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wires nc_status, nc_engine, nc_xlate, nc_smat_diff, nc_tclgen as first-class
LLM tools (all 4 surfaces). nc_engine unlocks TPS testing (hcitps) + the
route-test driver. Fixes a real nc-engine.sh bug surfaced by the exposure:
the dispatcher treated every --flag as taking a value (--dry-run ate the next
token) and a set -u leak from journal.sh crashed start/stop/bounce on bash 3.2;
fixed with set +u + a multi-case parser (no over-shift on bare trailing flags).
Corrects stale CHANGELOG + nc_engine schema text that misstated the bug as live.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New tool: show how a thread/system/site changed over time by diffing
Cloverleaf NetConfig revision snapshots, annotated with who saved each and
when. Handles the non-zero-padded NetConfig<TS> revision dirs by parsing the
prologue date into a sortable key; scopes diffs to the requested thread/system
via nc-parse. Flags: <thread>[.<site>], --system, --site, --format
timeline|diff, --limit, --since. Wired as nc_revisions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Shared _sanitize_ctl (unconditional, nc-document) and _sanitize_ctl_tty
(strips only when stdout is a terminal) now live in cygwin-safe.sh. nc-msgs,
nc-parse, and the hl7-* tools route stdout through the tty-gated variant, so a
terminal is protected from raw HL7/NetConfig control bytes while pipes and
redirects stay byte-exact (the 0x1c framing route_test needs is preserved).
Exit codes propagate via PIPESTATUS. ssh-helper _read_hidden installs its
restore trap before stty -echo on every path and saves/restores the prior trap.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
nc-document.sh now sanitizes C0 control bytes (except tab/LF/CR) at the single
output sink, so raw ESC sequences embedded in NetConfig/.tcl content can no
longer flip the terminal's mode and break line editing when output isn't
redirected. ssh-helper.sh password prompts save/restore termios via stty -g +
trap so a ^C mid-prompt no longer leaves echo off. UTF-8 preserved; portable.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Default render is now plain text (no #/**/pipe-tables/fences) so it pastes
cleanly into OneNote. Tabular sections default to label:value hop blocks;
--onenote-table emits tab-separated rows for paste -> Insert>Table. Raw TCL
moved behind opt-in --raw-tcl (readable UPOC bits stay inline). Removed the
verbose "Filter / translation logic (surfaced deterministically...)" label.
Fixes a markdown leak in the proc-not-found fallback (Vera gate). Folds in
two prior deferred minors (dead counters; local _dest_hit/_d).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Resolves the downstream route chain via nc-paths, grabs N recent messages
from the START inbound's SMAT, walks each ENTRY node (START + post-==> remote
inbounds) running hciroutetest -a -d -f nl, chaining each step's selected
.out.<DEST> across cross-site hops. Generates per-chain commands.sh for the
engine box; --dry-run stubs the engine. Command syntax mined verbatim from
the v1/v2 route_test wrappers. Fixes --help sed range (header ends at 94).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Single-line `local a=… b="…$a…"` referenced the 1st var before it was bound
within the SAME `local` statement, aborting under set -u on Cygwin/MobaXterm
(and modern bash). Split larry.sh:6903 (/ssh-set-hciroot) and the same latent
pattern at larry.sh:6925 (/ssh) into set-u-safe declare-then-assign form.
Codebase-wide bug-class audit (larry.sh + all lib/*.sh + scripts): zero
remaining instances. Closed the v0.8.15 gate gap by driving the ACTUAL REPL
slash-dispatch handler bodies under set -u + BASH_COMPAT=3.2 (not just the
ssh-helper subcommand): /ssh-set-hciroot normal + empty-path-clear, /ssh, and
usage paths all pass; old code aborts under the same harness. No-traffic-bypass
line unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
MAJOR-1: regenerate MANIFEST (larry.sh, lib/ssh-helper.sh, VERSION,
CHANGELOG.md hashes now authoritative for the v0.8.15 bytes).
MINOR-1: print_help /sites line documents the --hciroot <path> pin
convenience and the pinned-vs-login resolution distinction.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
larry tools list / <name> [args] makes all 24 lib/ Cloverleaf+HL7 tools
discoverable and runnable by hand with no API/LLM; dispatches before
bootstrap/self-update/network. _diagnose_api_block recognizes a blocked
API (curl rc/stderr/body/headers, incl. Cisco Umbrella fingerprints) and
guides the operator to manual-tools mode + IT allowlisting instead of a
raw error dump. Graceful degradation + honest guidance only — NO traffic
masking/proxy-hiding/circumvention on a PHI box.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Root-cause fix for the live-session friction where "how many sites are on
qa?" stalled on repeated `export $HCIROOT` nags despite a working `qa` SSH
alias:
1. $HCIROOT login-shell fix: ssh-helper.sh `exec` now wraps remote commands in
`bash -lc` so the Cloverleaf login profile sources and $HCIROOT/$HCISITE/PATH
populate as for an interactive operator login. Escape hatch: NOLOGIN prefix
or LARRY_SSH_NO_LOGIN=1. pull-smat find/sample use the same wrapper.
2. Both-mode detection: startup surfaces a MODE= line (LOCAL / REMOTE / UNKNOWN)
and leads with what it found instead of asking for paths.
3. First-class list_sites tool + /sites [alias]: enumerates sites in both modes
(hcisitelist fast-path, NetConfig-walk fallback) via new ssh-helper discover.
4. System-prompt de-nagging: agents/larry.md + env-diff/regression prompts no
longer tell Larry to ask Bryan to export $HCIROOT for a reachable host.
5. Streaming slowness (dominant residual): new pure-bash _json_str_decode
un-escapes the common escape-free delta with zero forks, halving per-turn
jq forks on top of v0.8.12. Round-trip verified.
6. pull-smat path capture hardened (Vera Minor #1): resolved path now emitted
behind a SMATDB_PATH: sentinel and selected by pattern not position, so a
login-shell MOTD/banner on stdout can't be mistaken for the path; falls back
to prior tail -1 when no sentinel present. Selection logic unit-verified.
Vera gate: PASS-WITH-NOTES (v0.8.13). bash -n clean on larry.sh + ssh-helper.sh;
MANIFEST regenerated (48 entries) and --check clean.
Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
Crash/slowness/cost pass on the API-key rail (Vera-QA-passed). Full diagnosis:
Deliverables/2026-05-27-cloverleaf-larry-v0812-crash-slowness-cost.md.
- Crash fix: CR-coerce at 3 response-path accounting sites (non-streaming cost,
streaming cost, _record_ctx_used) via coerce_int promoted to lib/cygwin-safe.sh
— guards CRLF-tainted jq usage counts on Cygwin/MobaXterm (v0.7.5 Anomaly-#4
recurrence on the response path).
- Slowness: collapsed per-delta jq forks in the SSE hot path (3 -> 2 forks/event
on text, 1 on ignored deltas) — dominant fix for the laggy per-turn render on
Windows fork emulation. Plus per-session PHI sidecar /health probe caching.
- Cost: prompt caching wired in — system sent as block array + last tool marked
cache_control: ephemeral, billing the ~12.7K static prefix at the cache-read
rate after turn 1 (~90% prefix cut, lower TTFB).
- PHI tier-5 notice now default-silent (LARRY_PHI_NOTICE=1 to re-enable).
Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
Closes the last gap in the rate-limit-diagnosis pipeline: anthropic-ratelimit-*
headers captured on the MobaXterm work-box now flow to the Mac memory daemon
(Tier 4 Hindsight + Tier 7 mem0) automatically.
- lib/headers-sync.sh: incremental, offset-tracked, idempotent push of
headers.log to ~/.cloverleaf/headers-<hostname>.jsonl on the Mac, riding the
existing authenticated SSH ControlMaster. No new auth; password never in
argv/env. No-op when nothing new; re-seed on local rotation/shrink. Fully
graceful (no target / closed master / transport error → warn + continue;
never crashes the session).
- /headers-sync on|off|status|target <alias>|now slash command + TAB-completion
+ /help. Config persisted to $LARRY_HOME/.env. Auto-sync fires on REPL exit.
- Security: headers.log carries only anthropic-* headers + status lines — NO
PHI per Vera audit V7; transport reused unchanged (not weakened).
Layered cleanly on top of Clover #8's v0.8.5 (4f1ea86) — edits isolated to new
lib + help/array/trap/dispatch hunks; no overlap with the streaming parser,
retry/backoff, error-display, or phi-notice regions.
Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
Hardens the installer + auto-updater against the Gitea private-repo trap
(Clover #5 diagnosis): an unauthenticated raw-file read of a sign-in-gated
Gitea returns the HTML Sign-In page at HTTP 200, which `curl -fsSL` treats as
success — so the old code parsed HTML as VERSION/MANIFEST/larry.sh content and
silently aborted (or overwrote real files with HTML). This stranded a work-box
at v0.7.3 until the REQUIRE_SIGNIN_VIEW=false flip.
- New lib/fetch-safe.sh: fetch_validate URL DEST KIND [MAX_TIME]. Detects the
HTML-login trap (DOCTYPE/<html/"Sign In - Gitea"/<title>Sign In markers, or
text/html Content-Type) and validates content shape per file type (semver
VERSION, path-list MANIFEST, shebang larry.sh, non-HTML .sh). On failure:
actionable error + non-zero, target file left untouched.
- install-larry.sh (curl|bash bootstrap) and larry.sh self_update() each carry
a byte-identical inline copy (both run before lib/ can be sourced).
- Every remote-content fetch routed through the validator: install fetch();
agent fetch; sync_from_manifest MANIFEST + per-file; _fetch_with_fallback.
- Optional LARRY_GITEA_TOKEN / GITEA_TOKEN env var adds Authorization: token
<PAT> for authenticated fetch against private repos. Never hardcoded/logged.
Documented in --help + MANUAL.md.
Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
The only path that closes V1 (free-text PHI gap — the dominant real-world
failure mode per Vera). Opt-in install; larry runs in v0.8.1 mode on hosts
without Presidio (MobaXterm/Cygwin per Bryan's accepted tradeoff).
New files:
- lib/phi-presidio-sidecar.py — FastAPI service on 127.0.0.1:$LARRY_PHI_PORT
(default 41189). Presidio AnalyzerEngine + AnonymizerEngine over spaCy
en_core_web_sm + 3 HL7-specific custom recognizers (HL7_MRN, HL7_CARET_NAME,
HL7_PHONE_BARE). POST /redact and GET /health.
- lib/phi-sidecar.sh — lifecycle (start/stop/status/health/ensure). ensure
is idempotent; called backgrounded from main_loop so it never blocks the
first prompt. Honors LARRY_PHI_VENV.
- lib/phi-client.sh — bash client (phi_client_available / phi_redact_text /
phi_redact_entities). CR-safe; 5s timeout bounds tier-5 stall.
larry.sh:
- auto_detect_phi gains tier-5: after tiers 1-4, before status summary,
source phi-client.sh, run Presidio on a token-masked copy of the input,
tokenize each entity through hl7-sanitize.sh tokenize-value (category
presidio_<TYPE>) so token IDs stay stable. Honors confirm + strict modes.
Removed the v0.7.3 early-return that skipped past tier-5 when tiers 1-4
found nothing — pure prose now always reaches tier-5.
- Token-safe substitution: existing [[...]] tokens are pulled to sentinels,
tier-5 value is replaced, sentinels restored — prevents the token-within-
token corruption that naive literal-replace caused on already-tokenized
text. Acronym guard drops HL7/clinical jargon (SSN/MRN/DOB/ADT) Presidio
over-tags as ORGANIZATION.
- Graceful degradation: sidecar unreachable → tier-5 no-ops with a one-time
stderr warning. /phi-sidecar slash command + completion table.
install-larry.sh:
- Probes python3 3.9+; offers to create $LARRY_HOME/phi-venv and install
presidio + fastapi + uvicorn + en_core_web_sm. Skips silently (with a
v0.8.1-mode note) on Cygwin/MobaXterm without python3, and on
non-interactive pipe installs. Sets LARRY_PHI_VENV in the larry shim.
MANIFEST: three new lib files added for auto-sync.
Prototype validation (Bryan's Mac, Apple Silicon, Python 3.14):
cold start (en_core_web_sm): ~9s (vs ~82s if Presidio auto-grabs _lg;
we pin _sm for the REPL budget)
warm analyzer latency: P50 20.6ms / P95 22.7ms
end-to-end HTTP round-trip: ~57ms warm; ~150ms first-post-startup
All comfortably under the 200ms-per-turn budget.
MobaXterm verdict: v0.8.2 is Mac/Linux-only. MobaXterm stays on v0.8.1 +
nudges, per Bryan's explicit acceptance. install-larry.sh enforces this
by platform detection; larry.sh tier-5 silently no-ops when the sidecar
is absent (which IS the MobaXterm path — no code is platform-gated).
Verification: bash -n clean on larry.sh + all 3 new lib scripts; python3
ast.parse clean on the sidecar; end-to-end tier-5 tested live against the
sidecar (pure prose, rule-pack+tier-5 combined with no token corruption,
!nophi bypass); strict-mode fail-closed abort tested; CR-taint, path-block,
and base64 round-trip batteries re-run green.
Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com>
Two REPL enhancements:
1. HL7 v2.x inline tab completion. Type a segment ID or SEG.field or
SEG.field.component in any prompt and TAB completes against a built-in
schema (18 segments fully fielded: MSH, PID, PV1, PV2, EVN, MSA, ERR,
NK1, GT1, IN1, IN2, OBR, OBX, ORC, AL1, DG1, PR1, ROL; component
breakdowns for MSH.9, PID.3, PID.5, PID.11, PV1.3, NK1.4, OBX.3, IN1.4).
New slash commands /hl7 <SEG> and /hl7-fields <SEG.N> print schema
without typing. Z-segments get a "site-specific" hint instead of a
guess. Exact-match wins over prefix siblings (PID.3 completes over
PID.30; MSH completes over MSH+MSA).
2. Mouse mode. /mouse on|off and LARRY_NO_MOUSE env kill switch enable
bracketed-paste + SGR mouse reporting (mode 1006). Click-to-position
cursor in the input line is intentionally NOT implemented in this
pass — it requires per-terminal escape parsing inside bind -x which
is not reliable across iTerm2 / macOS Terminal / MobaXterm / Cygwin
in a single pass. Documented as terminal-dependent.
New file: lib/hl7-schema.sh (sourced; bash assoc arrays for the segment
+field+component tables, plus helpers hl7_segments / hl7_fields_for /
hl7_components_for / hl7_field_name).
MANIFEST + install-larry.sh updated to fetch the new lib file on
install/self-update.
Regression-safe: v0.6.9 status line, slash completion, @file completion,
streaming SSE, header capture, and all 37 prior slash commands are
unchanged. Added 3 new slash commands (/hl7, /hl7-fields, /mouse).
Verification: 15/15 automated checks on the three completion paths
(segment, field, component) — including mid-buffer completion and
exact-match preference.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
NEW lib/ssh-helper.sh implements the full SSH command surface:
hosts/list show configured remote hosts
add <alias> <user@host[:port]> register a new host
remove <alias> remove + clean cred + socket
pass <alias> set/update password (hidden interactive)
setup <alias> open long-lived ControlMaster
close <alias> close ControlMaster
status [alias] show open masters + cred presence
exec <alias> <command...> run command via master
Architecture:
• $LARRY_HOME/.ssh-hosts.tsv — alias \t user@host \t port (3-col)
• $LARRY_HOME/.ssh-creds/<alias> — raw password, mode 0600
• $LARRY_HOME/.ssh-sockets/<alias>.sock — ControlMaster socket
The password is read from disk by sshpass via -f (file argument), so it
never lands in argv or environment. It is used ONCE to open the master;
all subsequent execs multiplex through the socket with no auth. Daily-
rotating passwords: just overwrite the cred file and re-run setup.
SLASH COMMANDS wired in larry.sh REPL: /ssh-hosts /ssh-add /ssh-remove
/ssh-pass /ssh-setup /ssh-close /ssh-status /ssh <alias> <cmd>.
LARRY TOOLS exposed to the LLM:
ssh_status — list aliases + open-master state
ssh_exec — run command on remote via the master socket
Both tool descriptions explicitly tell Larry the password is unreachable
and to ask Bryan to run /ssh-setup if a master is closed. Tool inputs
and outputs never contain the password. Output capped at max_lines
(default 500) with a "[ssh_exec: exit rc=N]" footer.
Bundle updated: MANIFEST + install-larry.sh both now include
lib/ssh-helper.sh. Auto-update will pull it on next launch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Self-update overhaul (no more manual reinstalls when lib/ changes):
- New MANIFEST file at repo root lists every file that should auto-sync
(top-level scripts, agents/, lib/, VERSION, MANUAL.md).
- larry.sh self_update() reworked into two phases:
Phase A — local sync: if $LARRY_HOME/.last-sync-version != $LARRY_VERSION,
fetch MANIFEST and refresh every listed file. Stamps version after.
Phase B — remote check: fetch $LARRY_BASE_URL/VERSION; if newer, pull
larry.sh, self-replace, relaunch with LARRY_JUST_UPDATED=1 so phase B
is skipped on the relaunch (phase A then pulls everything else).
- New LARRY_BASE_URL env var (the legacy LARRY_UPDATE_URL / LARRY_AGENTS_URL
still work as overrides).
- Bumped LARRY_VERSION and VERSION to 0.5.0.
OAuth fix (lib/oauth.sh):
- Anthropic's callback returns the code as 'CODE#STATE' (URL fragment, not
query). Previous prompt told users to copy "between code= and the next &"
which produced the wrong substring; the token endpoint then returned a
misleading 'rate_limit_error' on the malformed code.
- Now splits the pasted input on '#', verifies the returned state matches
the one we generated, sends only CODE to the token endpoint.
- Updated user-facing prompt and error hints to describe the real format
and explain the misleading rate_limit_error symptom.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>