Feature 1 — Status line BELOW the prompt (was: above).
The dim status line now renders AFTER each completed agent_turn and BEFORE
the next prompt, sitting between turns as a footer to the just-finished
exchange. Shipped Option B from the spec — render_status_line moved to the
tail of the REPL loop, the call before printing the prompt was removed.
Option A (cursor manipulation under an active readline prompt) was rejected
because `read -e` takes exclusive control of the cursor and inserting a
repositioned footer below an active prompt is fragile on MobaXterm / Cygwin
(readline redisplay clobbers manual cursor moves). Visual outcome is
identical to "below the previous prompt cycle", and /status still forces a
re-render mid-conversation if needed.
Feature 2 — Automatic PHI detection.
New auto_detect_phi() runs BEFORE preprocess_phi_markers and tokenizes any
value matching PHI-shaped patterns (email, SSN, phone, DOB, MRN 6-12 digits,
HL7 caret-name, "Last, First", or loose "Title Case Title Case"). Uses the
existing hl7-sanitize.sh tokenize-value pipeline so canonicalization
(sort-unique-lowercase NAME tokens, ISO DOB, digits-only PHONE/SSN,
lowercase EMAIL) collapses different surface forms onto one token across
the session. Skipped: paths, URLs, already-tokenized values, manual @@/{{phi:}}
markers, timestamps (13+ digits or 10 digits starting with '1'), and a
built-in allowlist of common non-PHI two-word phrases ("Home Assistant",
"Mac Studio", etc.).
Modes: confirm (default — prompts Y/n on loose name-like matches once per
session), aggressive (silent always-tokenize), off. Env LARRY_AUTO_PHI;
runtime /auto-phi and /auto-phi-status slash commands. Per-turn override
with "!nophi " prefix. Manual markers always win. New normalize-value
subcommand on hl7-sanitize.sh exposes the canonicalization step so the
per-session memory cache uses canonical keys (so "John Smith" and
"JOHN SMITH" share one confirm decision). EMAIL + PHONE categories added
to normalize_value().
Feature 3 — Session-artifact upload at close.
New upload_session_artifacts() POSTs $LARRY_HOME/log/headers.log,
$LARRY_HOME/sessions/<id>.log.md, and <id>.messages.json to
$LARRY_MEMORY_UPLOAD_URL on session exit. Each request carries
X-Larry-Source (headers-log | session-log | session-messages),
X-Larry-Version, and X-Session-Id headers so the ingest side can route
appropriately. Fires from both the clean main_loop exit and the EXIT/INT/TERM
trap (idempotent via _LARRY_UPLOAD_FIRED guard). Unset URL = silent skip
with a one-line warn. Auth tokens are never logged: headers.log captures
only response headers matching ^anthropic-* or ^retry-after: (per v0.6.9
writer); the session log + messages contain post-tokenization content only.
No regressions to v0.7.0 work — HL7 tab completion, mouse mode toggles,
TOOLS_JSON heredoc, streaming, @file refs, status-line existence, slash
completion, and all v0.6.x machinery remain untouched. MANIFEST unchanged.
Co-Authored-By: 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>
Closes the gap between v0.6.7's ssh_exec/ssh_status primitives and the local
nc_* tools, so Bryan's two motivating workflows compose cleanly:
1. "Compare the ADT site NetConfig on qa to dev"
2. "Grab smat files from dev and bring to qa for regression testing"
ssh_pull, ssh_push (lib/ssh-helper.sh + larry.sh):
scp via the existing ControlMaster socket — no second auth, no second TCP
handshake. Master-not-open and missing-remote-file paths fail with explicit
messages ("open the master with /ssh-setup <alias> first"). Pull caches to
/tmp/larry-pulls/<alias>.<basename>.<hash-of-remote-path> when local_path is
omitted, so repeat pulls of the same remote file are idempotent. Validates
byte counts post-transfer to catch partial transfers.
ssh_pull_smat (lib/ssh-helper.sh + larry.sh):
Cloverleaf-aware smatdb pull. Full mode scp's the entire .smatdb;
sampled mode (days_back=N) runs sqlite3 server-side via ssh_exec to extract
up to 1000 recent messages as TSV with base64-encoded MessageContent blobs
(verified end-to-end with a synthetic smatdb fixture matching nc-msgs.sh's
smat_msgs schema). Avoids transferring multi-GB archives when only N
samples are needed.
nc_diff_interface tool (newly wired):
Promotes lib/nc-diff-interface.sh into the LLM-callable tool surface. Used
by the new /nc-diff-env slash command for workflow #1.
nc_regression cross-env (lib/nc-regression.sh + larry.sh):
source_ssh_alias / target_ssh_alias args. Phase 1 (discovery) and Phase 2
(sample) run via ssh_exec + ssh_pull / ssh_pull_smat against the source
alias. Phase 3/4 (route_test) push inputs over and pull outputs back via
ssh_push / ssh_pull. Phases 5/6 (diff + summary) stay local. Reports
reference the SSH alias names rather than raw user@host strings.
/nc-diff-env and /nc-regression-env slash commands (larry.sh):
Templated prompts to Larry-the-LLM that explicitly cite the motivating
workflows, call out ssh_status / ssh_pull / nc_diff_interface and the
nc_regression cross-env fields. Registered in _LARRY_SLASH_CMDS +
_LARRY_SLASH_CMDS_DESC + /help per v0.6.7 patterns.
Bug fix unearthed during cross-env work:
lib/nc-regression.sh phase_5 / phase_6 used printf 'FORMAT' where FORMAT
begins with '- '. bash 3.2 (macOS default) reads the leading '-' as a bad
option and emits nothing — silently dropping the entire "Configuration"
section of regression-summary.md. Switched the affected lines to
printf -- 'FORMAT' so the format string is unambiguous.
Tool/slash surface deltas vs v0.6.7:
Tools: 31 → 35 (+ssh_pull, +ssh_push, +ssh_pull_smat, +nc_diff_interface)
Slash commands: 34 → 36 (+/nc-diff-env, +/nc-regression-env)
Updated tool descriptions for read_file, grep_files, nc_msgs to point at
ssh_pull / ssh_pull_smat as the cross-env pre-step so Larry-the-LLM picks
the right chain on the first attempt.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bug 1 (PRIMARY — unblocks OAuth on MobaXterm): jqf now strips \r from every
jq output. Root cause of the multi-day "OAuth token unavailable" cascade was
CRLF-tainted .oauth.json: `fetched_at=$(jqf ... '.fetched_at')` captured
"1716826990\r", and `$((fetched_at + expires_in))` crashed with the cryptic
"invalid arithmetic operator (error token is \"\"") — bash trying to print
the embedded CR. Single-point fix in jqf covers every caller (ensure, refresh,
status, debug, login). Added belt-and-suspenders `printf '%d'` coercion on
every numeric capture so any future non-CR junk falls back to 0 instead of
crashing arithmetic. /oauth-debug now reports CR/LF byte counts so future
CRLF taint is visible at a glance.
Bug 2 (security): .oauth.json was landing at 0644 on Cygwin/MobaXterm even
though both cmd_login and cmd_refresh called `chmod 600`. Introduced
secure_install (install -m 600 → cp+chmod → mv+chmod fallback chain) so the
mode is set atomically at placement. Also added umask 077 to cmd_refresh
(only cmd_login had it) so the .new sidecar is created tight from the start,
plus a pre-mv chmod 600 on the sidecar for fs-where-install-doesn't-stick.
On a fully POSIX FS this is now triple-redundant; on Cygwin NTFS we get as
close to 0600 as the ACL emulation will allow.
Feature 1: TAB completion for slash commands. New _LARRY_SLASH_CMDS canonical
array near read_user_input, __larry_complete_slash uses `bind -x` to read
$READLINE_LINE / $READLINE_POINT and rewrites the buffer in-place. Prefix
matching is primary; subsequence fuzzy is the fallback. Non-slash lines and
mid-arg TABs fall back to literal-tab insertion so muscle memory isn't
broken. Heredoc continuation lines DO NOT get completion (binding only fires
on the first read). /help section documents the behavior with examples.
Smoke-tested on macOS:
- CRLF-tainted .oauth.json: ensure returns access_token cleanly, status &
debug print real numbers + human timestamps (no bash arith crash).
- secure_install: file ends at 0600 even when source was 0644.
- Completion: /h→/help, /ss→lists ssh-*, /ssh-h→/ssh-hosts, /q→/quit,
/oa→/oauth-debug, /sssp→/ssh-setup (fuzzy), /xyz→silent, non-slash and
"/cmd args"→literal tab.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
call_api was swallowing every byte of oauth.sh ensure's stderr with
`2>/dev/null`, so when ensure returned an empty token there was zero
diagnostic info — just "OAuth token unavailable". With Bryan hitting an
intermittent failure on MobaXterm we'd already burned two guess-fix
cycles; this ships the data instead of another guess.
Changes:
- call_api now captures ensure's stderr to a tempfile and surfaces it
via err() when the token comes back empty, pointing the user at
/oauth-debug for full state.
- cmd_ensure validates the file parses as JSON before destructuring,
validates .access_token is non-empty before emitting, and emits a
decision trace to stderr under LARRY_OAUTH_DEBUG=1.
- New cmd_debug subcommand (oauth.sh debug) dumps: file state (mode,
size, mtime, JSON validity), parsed fetched_at + expires_in + now +
computed expiry + would_refresh decision, jq binary path + version +
Unix/Windows-native flavor, cygpath -w translation when on Cygwin,
truncated previews of access/refresh tokens (first 20 chars + length
only — safe to share), and a live LARRY_OAUTH_DEBUG=1 ensure trace.
- New /oauth-debug slash command exposes it from the REPL, documented
in /help.
- cmd_login and cmd_refresh now write to .new sidecars, validate
required keys parse, then atomically mv — guards against the
corrupted-file failure mode that would silently break ensure on a
later run.
Happy path unchanged: when the file is valid and the token is in-window
ensure prints just the access_token on stdout with no stderr.
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>
Bryan asked for an easier-to-remember inline PHI marker than {{phi:VALUE}}
and for name forms like SMITH^JOHN / Smith, John / John Smith / JOHN SMITH
to all collapse to the same hash. Both shipped.
INLINE SYNTAX (in addition to the legacy {{phi:VALUE}} which still works):
@@VALUE unbracketed — VALUE has no whitespace
e.g. @@12345 @@SMITH^JOHN @@V789
@@VALUE@@ bracketed — VALUE may contain spaces
e.g. @@John Smith@@ @@Smith, John@@
Parser is 2-pass to disambiguate mixed forms in the same prompt: bracketed
markers are matched first (via grep -oE with a regex that excludes leading/
trailing whitespace inside the brackets), then the unbracketed pass scans
the remaining text. Verified against:
"look for @@12345 in PID.3 for @@John Smith@@ DOB @@01/15/1985 ..."
extracts 4 markers correctly and routes each to its category.
AUTO-CATEGORY DETECTION (lib/hl7-sanitize.sh: detect_category):
pure digits 4-15 → MRN
9 digits with dashes → SSN
date-shaped → DOB
caret or comma → NAME
2+ alpha tokens → NAME
else → MANUAL
CANONICALIZATION (lib/hl7-sanitize.sh: normalize_value):
NAME: lowercase, replace ',^/' with spaces, sort unique alpha tokens
SMITH^JOHN, Smith John, John Smith, JOHN SMITH → "john smith"
DOB: parse to YYYY-MM-DD (GNU date or BSD date fallback)
SSN: strip dashes/whitespace
MRN/MANUAL: trim outer whitespace only
TABLE SCHEMA bumped to 4 columns (token / category / canonical / original).
Legacy 3-column rows still read fine — lookups key on column 3 which is
"canonical" in new rows and "value" in legacy rows (mismatches just create
a new token, no corruption). Detokenize prefers column 4, falls back to
column 3 for legacy compat.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Symptom: OAuth login succeeded on the work box but cmd_status emitted three
'jq: error: Could not open file' lines and showed empty fields. Same pattern
would have hit every subsequent chat turn via larry.sh's MESSAGES_FILE reads.
Root cause: install-larry.sh fetches a Windows-native jq.exe on cygwin/
mobaxterm platforms. Windows jq can't resolve Cygwin paths like
/home/mobaxterm/.larry/.oauth.json when they come in as argv arguments
(it interprets the leading slash as a Windows root). Bash's `>` redirection
worked because bash itself does the path open and hands jq an fd — the
read-side calls were passing the path string directly.
Fix: every read-side jq call now uses stdin redirection (`jq '...' < file`),
where bash does the open. Universal:
- Linux/macOS native jq: identical behavior (was already file-open-from-bash)
- MobaXterm/Cygwin/Git Bash with Windows jq.exe: now works
- WSL: works (Linux-native jq, same as Linux)
- Native PowerShell/cmd: doesn't apply — larry-anywhere is a bash script
Changes:
- lib/oauth.sh: new jqf() helper; 10 sites converted. Refactored cmd_refresh
to drop --slurpfile (which can only take a path) — pre-reads the previous
refresh_token, then uses --arg.
- larry.sh: add_user_text / add_assistant_blocks / add_user_tool_results
now pipe $MESSAGES_FILE via stdin too.
Verified: cmd_status against a real token file produces clean output, no
jq errors. Syntax check passes both files.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Confirmed against live token endpoint (HTTP/2 200, valid sk-ant-oat01- and
sk-ant-ort01- tokens returned) that the v0.5.2 0.5.2 request body and
URLs were correct — the EXCHANGE itself works fine from my Mac. Bryan's
work-box launches still get 'rate_limit_error' from the same script.
Only meaningful differences in the working curl vs the failing one:
- Working: explicit User-Agent (claude-cli/2.1.85) + Accept: application/json
- Failing: defaults (curl/X.Y.Z, no Accept)
Anthropic's OAuth endpoint apparently checks User-Agent (or the Accept
header) and returns the misleading rate_limit_error for unrecognized
clients. Adding both headers to match what claude-cli and droidrun
send. Patched in cmd_login AND cmd_refresh.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Root cause of every prior 'rate_limit_error' on OAuth login: Anthropic
migrated all the Claude-subscription OAuth endpoints from
console.anthropic.com / claude.ai to platform.claude.com / claude.com.
The old endpoints aren't 404 — they accept the POST and return a generic
'rate_limit_error' for every request, which is what mis-led both me and
several public community implementations.
Confirmed against two current working clients (droidrun/mobilerun and
motiful/cc-gateway, both using the same Claude Code public client_id):
AUTHORIZE_URL: claude.ai/oauth/authorize
→ claude.com/cai/oauth/authorize
TOKEN_URL: console.anthropic.com/v1/oauth/token
→ platform.claude.com/v1/oauth/token
REDIRECT_URI: console.anthropic.com/oauth/code/callback
→ platform.claude.com/oauth/code/callback
SCOPE: org:create_api_key user:profile user:inference
→ ...plus user:sessions:claude_code user:mcp_servers user:file_upload
Also updated the error-hint text to mention the misleading-rate-limit
pattern for both 'malformed code' AND 'dead endpoint' cases, and to cite
the current TOKEN_URL — so if/when these move again, the next person
hitting the same trap finds the answer in the script's own output.
The CODE#STATE parsing from 0.5.0 was correct and stays. State IS sent
in the token-exchange body (verified against droidrun's working flow).
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>
Each Larry is independent. Bryan's question "how will Larry on Windows
talk to Larry on Linux for regression file transfer" answered: they don't.
File transfer is YOUR responsibility (scp / gh release / shared mount /
USB), but nc-regression now produces and consumes portable bundles that
make the split a one-command-on-each-side workflow.
Changes:
lib/nc-regression.sh
+ --phase env-a convenience for phases 1+2+3 (env-A side)
+ --phase env-b convenience for phases 4+5+6 (env-B side + diff)
+ --bundle-out PATH after env-A phases, tar inputs+outputs/env-a +
manifest.json + README.md + inbounds.txt
+ --bundle-in PATH at start, untar a bundle into $OUT; pulls scope
from the manifest so the env-B side just needs
--env-b and --route-test-cmd
MANUAL.md
+ New "Cross-environment Larry — how the boxes communicate" section
+ Bundle transport table (scp, gh release, NFS, USB, etc.)
+ Notes that the lesson loop uses the same local-capture / manual-
transport / central-merge model
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Five small Unix-style loop & format helpers, fully offline:
lib/each.sh
- replaces v1 `each`
- run a CMD per item: args, stdin lines, or {}-placeholder substitution
- example: tbn adt | awk '{print $2}' | each.sh 'route_test {}'
lib/each-site.sh
- replaces v1 each_site / each_site_hdr / each_site_tcl patterns
- iterates every site under $HCIROOT with HCISITE/HCISITEDIR auto-exported
- --filter REGEX limits which sites; --hdr prints a header before each
lib/len2nl.sh
- replaces v1 `len2nl`
- strict superset: handles length-prefixed (digits before MSH),
MLLP (\x0b...\x1c\x0d), and segment CRs (→ LF)
- works as stdin filter or with file arg
lib/csv-to-table.sh
- 2-column CSV → Cloverleaf .tbl format
- emits proper prologue (who, date, bidir, type, version)
- --has-header --default VALUE --bidir 0|1 --in-delim CHAR --user NAME --out PATH
lib/table-to-csv.sh
- reverse: .tbl → CSV
- --with-header --delim CHAR --include-meta
- confirmed clean round-trip: CSV → table → CSV byte-identical for the data rows
All 5 are pipeable, have --help, zero external deps beyond bash+awk+sed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
nc-parse.sh
+ chain <name> [--depth N] [--direction both|up|down]
BFS over sources+destinations from a starting thread; returns the
reachable cluster as TSV (depth, direction, thread).
nc-msgs.sh
+ Filter operator additions:
> >= < <= numeric or lexical (works for HL7 YYYYMMDDHHMMSS timestamps)
>< range "LO..HI" inclusive
+ Filter group additions:
--field AND group (must match; existing behavior)
--or-field OR group (at least one must match)
--not-field NOT group (none may match)
All three groups combine; bug fixed where empty AND group bypassed
OR/NOT checks in the count format.
+ SmatHistory walk:
--include-history also walks $HCISITEDIR/exec/processes/*/SmatHistory/
--all cheat-sheet alias for --include-history
Confirmed working against the real ancout test data:
- chain IB_ADT_muxS finds all 7 downstream destinations
- event=A08 OR event=A03 → 20 (19+1 of 22)
- visit>400000000 → 22 (all numeric in range)
- visit><400000000..400450000 → 22 (range inclusive)
- --include-history → 22 active + 34 history rows = 56 total
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Field path improvements (hl7-field.sh + every tool that uses it):
- Accept both `.` and `-` as separators:
PID.3 == PID-3
PV1.3.4 == PV1-3.4 == PV1-3-4 == PV1.3-4
- Field-name aliases (case-insensitive):
mrn → PID.3
account / account_number → PID.18
name / patient_name → PID.5
dob / birthdate → PID.7
ssn → PID.19
visit / encounter / csn → PV1.19
attending → PV1.7
event → MSH.9.2
control_id / msgid → MSH.10
...and ~40 more covering MSH/PID/PV1/EVN/NK1/GT1/IN1/OBR/OBX/DG1/ORC
- Aliases also accept component/subcomponent suffixes:
name.2 → PID.5.2
mrn.1 → PID.3.1
Filter operators (nc-msgs.sh --field):
PATH=VALUE exact equality
PATH!=VALUE not equal
PATH~VALUE contains (case-insensitive)
PATH!~VALUE does not contain (case-insensitive)
PATH=NULL /= null / empty / absent
PATH!=NULL present (any non-empty rep)
PATH=* wildcard — any non-empty value
Multiple --field flags AND; for OR, run two queries.
New output formats for nc-msgs.sh:
text (default) segments per line + metadata header per message
oneline one message per line, segments joined with a ⏎ marker
fields each non-empty field on its own line: "SEG.N: value"
mp alias for fields (matches v1 `mp` semantic)
labeled fields with friendly aliases: "MSH.9 (msg_type): ADT^A08"
raw, json, count — unchanged
MANUAL.md updated with the full operator + format reference.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bryan's ask: use Larry on prod data without PHI ever leaving the client box.
Added:
lib/hl7-sanitize.sh — tokenize PHI fields in HL7 messages
lib/hl7-desanitize.sh — reverse op (local view-time unmask)
Tokenization model:
- Replace PHI fields with [[CATEGORY_NNNN]] tokens (MRN, NAME, DOB,
ADDR, PHONE, ACCT, SSN, PROV, VISIT, etc.)
- Same value → same token across messages (deterministic via local
lookup table; analysis can still correlate patients).
- Lookup table at $LARRY_HOME/sanitize/lookup.tsv mode 0600 — never
leaves the client.
- Default PHI rule set covers PID, PV1, NK1, GT1, IN1, OBR, OBX,
DG1, ORC; --rules-file to extend.
- --strict also tokenizes unknown Z segments wholesale.
Prompt-side preprocessing in larry.sh:
- {{phi:VALUE}} inline marker, auto-category lookup
- {{phi:CATEGORY:VALUE}} explicit category
- Replaced with the token BEFORE the user input enters conversation
history. The original never reaches the API.
- Local feedback "phi> {{phi:...}} → [[TOKEN]]" printed to terminal only.
New REPL slash commands:
/phi <value> tokenize a single value, print the token
/unmask <token> show original (local terminal only, never API)
/tokens show full PHI ↔ token lookup table
New tools in larry.sh schema:
hl7_sanitize agent can sanitize a file before reading PHI
tokenize-value / detokenize-value (subcommands of hl7-sanitize.sh)
Persona update (agents/larry.md):
- Documented PHI mode and rules for proactive sanitize-first behavior
MANUAL.md updated with the full PHI section including limitations.
Brings total native tools to 29.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bryan's pivot: until bjnoela.com is back online, transfer learnings via
local file capture on the client + manual paste-back to home-Larry. NO
credentials required on the client box.
Capture flow:
- lib/lessons.sh records lessons to $LARRY_HOME/lessons/<date>.md
- lesson_record tool in larry.sh lets the agent record proactively
- /lesson, /lessons, /export REPL commands
- agents/larry.md updated: capture corrections, conventions, quirks
silently when Bryan teaches them
Export flow:
- lessons.sh export | bundle | --gh-issue (uses gh CLI if available)
- Bryan pastes the bundle to home-Larry on his dev machine
- home-Larry commits the refinement into cloverleaf-larry/agents/
- next launch on any client pulls updated persona via self-update
Brings total native tools to 28.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two additions:
1. OAuth subscription auth (lib/oauth.sh + larry-auth.sh)
- PKCE-based out-of-band flow against Claude.ai (no localhost server
needed; works behind any firewall).
- Uses the same client_id Claude Code uses, so calls bill against your
Max/Pro subscription quota instead of pay-as-you-go API metering.
- Tokens stored at $LARRY_HOME/.oauth.json (mode 0600), auto-refresh.
- larry.sh now detects oauth file at startup and uses Bearer auth.
- First-run flow now offers OAuth or API key; /login, /logout, /auth
slash commands in the REPL.
- Transparent fallback to API key if OAuth flow fails.
2. MANUAL.md — offline tool cheat sheet
- Documents every lib/*.sh script with copy-paste examples.
- Bryan's backup plan: when Anthropic is unreachable (no internet, on
a plane, etc.), all the underlying tools work standalone from the
shell. Larry just sequences them; they do not need Larry to run.
- Quick-recipe table at the bottom for the common day-to-day asks.
Files added:
- lib/oauth.sh
- larry-auth.sh
- MANUAL.md
Files modified:
- larry.sh — auth-mode detection, /auth /login /logout commands
- install-larry.sh — fetch new files
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>