8661948cf6
25 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 8661948cf6 |
v0.7.0: HL7-aware tab completion + REPL mouse mode
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> |
|||
| c34b8900fb |
v0.6.9: persistent status line above the prompt
Adds a dim status line printed immediately above each `you[model]>` prompt
every turn, surfacing context-window usage + rate-limit visibility.
Two render modes auto-selected from $LARRY_AUTH_MODE:
OAuth: ─ ctx 12% (24K/1.0M) ─ 5h 1.8% reset 19:45 ─ 7d 73.7% reset Mon Jun 2 ─
API key: ─ ctx 12% (24K/200K) ─ $0.213 session ─ 14 turns ─
Implementation areas:
- call_api / call_api_stream now capture response headers via curl -D into
tempfiles. Streaming path drains its header file in the parent shell after
the SSE body completes (subshell-update problem avoided).
- New parser _parse_response_headers handles BOTH header families per Pax's
research (Deliverables/2026-05-27-anthropic-rate-limit-headers-research.md):
* API-key: RFC 3339 datetimes → converted to epoch
* OAuth: Unix epoch integer-as-string → used as-is
Both 5h and 7d buckets are displayed simultaneously; representative-claim
is honored for enforcement but not for what to render (anti-pattern noted
by Pax — Claude Code itself once shipped buggy logic that picked one).
- Static model-context-window lookup (Pax §4): opus-4-7 / sonnet-4-6 = 1M,
haiku-4-5 and legacy 4-5/4-1 families = 200K, unknown defaults to 200K.
- Safety net: first 50 OAuth response header blocks are logged to
$LARRY_HOME/log/headers.log so the empirical schema can be diff'd against
Pax's spec on Bryan's actual account. Auto-disables after limit reached.
- New /status slash command force-renders the line on demand. New env knob
LARRY_NO_STATUS=1 disables the status line entirely.
- parse_stream_to_response synthetic JSON now also carries
cache_read_input_tokens + cache_creation_input_tokens so the parent shell
can compute ctx_used = input + cache_creation + cache_read per Pax §5.
Fallback rules followed:
- First turn of a session: status line is NOT rendered (no zero-lies).
- Missing reset values: display "reset —" not a fabricated time.
- Reset already passed: display "— reset" (data stale).
- Narrow terminal (< 100 cols): drop the reset times, keep the percentages.
Verification (synthetic fixtures; no live OAuth session in this environment):
- 25 parser/renderer assertions pass (test-harness covering all 8 spec
scenarios + model lookup + token humanization).
- SSE parser still produces a valid synthetic response JSON, now including
cache fields (7 assertions pass).
- TOOLS_JSON heredoc still parses cleanly via jq.
- bash -n on larry.sh: clean.
- Pax's OAuth headers were NOT empirically observed against a live account
in this environment — only validated against the documented schema via
fixtures derived verbatim from Pax's research. The header-log safety net
is in place to verify on Bryan's account on first use.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
|||
| 1709655a9c |
v0.6.8: cross-env Cloverleaf workflows over SSH ControlMaster
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>
|
|||
| 67318cf0e6 |
v0.6.7: REPL quality-of-life pass — streaming + 11 polish items
Streaming API responses (marquee). Replaced the one-shot curl/jq pipeline
with an SSE-aware path that emits text deltas as they arrive. Server-sent
events are parsed line-by-line; content_block_delta with text_delta is
flushed to stderr immediately, while tool_use blocks accumulate
input_json_delta partials until content_block_stop, validate the assembled
JSON, then execute the tool exactly as before. A synthetic response is
rebuilt from streamed blocks to keep add_assistant_blocks + cost tracking
identical to the non-streaming path. Falls back to non-streaming on parse
failure or via LARRY_NO_STREAM=1. Cite: platform.claude.com/docs/en/api/
messages-streaming for SSE event types and tool-use streaming semantics.
Fuzzy slash completion polish + descriptions. Multi-match TAB now renders
each candidate on its own line with a one-line description, cyan command +
dim description. Backed by a new _LARRY_SLASH_CMDS_DESC associative array
that mirrors print_help's wording so the two stay in sync.
Persistent command history. HISTFILE=$LARRY_HOME/.history with
HISTSIZE=1000 and HISTCONTROL=ignoredups. history -r at REPL start;
history -a after each accepted input. Skips appending /login, /ssh-pass,
and /ssh-add lines so credentials never hit the history file.
/clear slash command. Clears the terminal via the ANSI 2J/H sequence
(works without external `clear`). Distinct from /reset (which clears the
conversation, not the screen). Listed in print_help and the canonical
_LARRY_SLASH_CMDS array.
Multi-line paste auto-detection. read_user_input now reads the first line
then non-blockingly polls stdin for buffered bytes within 50ms; if more
is present, it's slurped as continuation. Also: a trailing backslash on
the first line enters a multi-line mode that ends at a blank line. The
explicit '<<' / 'EOF' heredoc still works for users who prefer it.
/copy — copy last assistant response to clipboard. Tool detection cascade:
pbcopy (macOS), wl-copy (Wayland), xclip, xsel, /dev/clipboard (Cygwin),
clip.exe (WSL/Cygwin fallback). Falls back to printing the text to stdout
with a warning when no clipboard tool is found.
/cost — running token + dollar cost. Tracks input/output/cache-read/
cache-write tokens across both streaming and non-streaming responses.
Pricing constants inline as of 2026-05 (Sonnet $3/$15, Opus $15/$75,
Haiku $1/$5, cache writes 1.25x input, cache reads 0.1x input). The /cost
report shows per-stream subtotals and a session total. Refresh constants
periodically from platform.claude.com/docs/en/about-claude/pricing.
Model name in the prompt. The prompt now reads `you[sonnet-4.6]>` (or
`you[opus-4.7]>`, etc.) derived from $LARRY_MODEL via model_short_name —
strips the `claude-` prefix and converts the trailing -N-M to .N.M. The
prompt updates immediately after /model. Color scheme preserved.
Tool-call display polish. Cyan + bold tool name, dim args one key:value
per line, long values (>120 chars) truncated with a `(use /show-last-tool
for full args)` hint. New /show-last-tool slash command prints the full
last tool call JSON + the full result body for debugging.
Friendlier error messages. Audit pass through the most common API
failures: empty response surfaces as "Network error: empty response from
<URL>"; authentication_error invites /login when OAuth/token wording
appears; rate_limit_error and overloaded_error read as "Rate limited by
Anthropic — wait a few seconds"; not_found_error includes the current
LARRY_MODEL since that's almost always the cause; jq parse errors during
tool results get wrapped as "Tool returned malformed JSON; raw body: ..."
@file inline-file syntax. The user types @<path> in any prompt; Larry
resolves each ref before send-time and appends the file contents as a
fenced block keyed by extension. Grammar supports @bare-token and
@{bracketed path with spaces}; emails (bryan@x.com) are skipped via a
look-behind on the preceding character. Validation: missing → leave
literal with warning; directory → skip; binary (null-byte scan of first
8 KB) → skip; >250 KB → truncate with a footer note. Multiple refs are
deduped. Runs BEFORE PHI tokenization so PHI markers inside attached
files still get caught. TAB after @ completes against files (find -depth
4, fzf picker when on PATH). A one-time per-session tip prints the first
time the user types @.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
|||
| 9f97d15f9a |
v0.6.6: strip CR from jq output + 0600 oauth file + TAB slash completion
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>
|
|||
| dd44d361c3 |
v0.6.5: surface OAuth ensure stderr + add /oauth-debug diagnostic
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> |
|||
| 47452d3910 |
v0.6.4: cygpath -w wrap on every --rawfile/--slurpfile argv path
Bryan hit this on MobaXterm immediately after v0.6.3 shipped:
C:\Users\...\bin\jq.exe: Bad JSON in --rawfile c /tmp/tmp.AlsIcdX9aO:
Could not open /tmp/tmp.AlsIcdX9aO: No such file or directory
error: OAuth token unavailable; run 'larry-auth.sh login' to re-authenticate
error: empty response from API (timeout or network?)
Root cause is the same one v0.5.4 fixed for the OAuth file reads, but now
biting the new tempfile pipeline introduced for argv-overflow avoidance.
The Windows-native jq.exe shipped to MobaXterm via install-larry.sh
cannot resolve Cygwin paths like /tmp/tmp.X or /home/mobaxterm/.larry/...
when they come in as argv arguments to --rawfile / --slurpfile — it
interprets them as Windows paths and the open() fails.
v0.5.4 fixed this for stdin-read cases by piping via bash redirection
(`< "$file"`), since bash handles the cygwin→windows path open before
jq sees a file descriptor. But --rawfile / --slurpfile DO want a path
argument, so the stdin trick doesn't apply — we must give jq a path it
can actually open.
Fix: new jqpath() helper translates a cygwin path to its real Windows
equivalent via `cygpath -w` when cygpath exists (Cygwin / MobaXterm / MSYS).
On Linux and macOS cygpath is absent and the helper echoes the path
unchanged — true cross-platform no-op outside Cygwin.
Wrapped every --rawfile / --slurpfile argv path in larry.sh:
add_user_text --rawfile c ←
add_assistant_blocks --slurpfile b ←
add_user_tool_results --slurpfile b ←
agent_turn payload --rawfile system, --slurpfile messages,
--slurpfile tools ←
agent_turn tool-result aggregation --rawfile c ←
Verified on macOS: jqpath echoes paths unchanged, the 40KB prompt
smoke test from v0.6.3 still works end-to-end.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
|||
| 38d1eeede4 |
v0.6.3: route all large jq inputs through tempfiles, not argv
v0.6.2 fixed the TOOLS_JSON argv overflow but four other call sites had
the same risk pattern — any of them would have crashed under Cygwin's
~32KB argv cap with large user input, large agent responses, or large
tool results:
add_user_text --arg c "$content" ← multi-paragraph prompts
add_assistant_blocks --argjson b "$blocks" ← long assistant turns
add_user_tool_results --argjson b "$blocks" ← chained tool results
agent_turn loop --arg c "$result" ← tool output (up to 250KB
for read_file, 500 lines
for ssh_exec, etc.)
agent_turn loop --arg system "$system_prompt" ← agents/*.md
total ~25KB
All five are now passed via tempfile + --rawfile (for raw strings) or
--slurpfile (for pre-parsed JSON). Same proven pattern as the v0.6.2
TOOLS_JSON fix. Tempfiles are cleaned at every return path.
Verified by pushing a 60KB user prompt through the pipeline on macOS
(also has the larger 256KB argv cap that masked these bugs locally
before, but the codepath now uses files for the large values regardless
of platform). Messages file stored the full 60025-char prompt with no
warnings.
After this commit, the only --arg / --argjson calls remaining all carry
known-small values (UUIDs, version strings, port numbers, etc.).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
|||
| 0ebaacd221 |
v0.6.2: fix Cygwin argv overflow when passing TOOLS_JSON to jq
Bryan hit this on every chat turn from MobaXterm: jq: Argument list too long error: OAuth token unavailable; ... error: empty response from API warn: turn ended with error Root cause: agent_turn was building the API payload with jq -n ... --argjson tools "$TOOLS_JSON" ... which puts all 21KB of tool-definition JSON on the jq command line as argv. Cygwin/Windows argv limit is ~32KB total — combined with the other args this exceeded it and jq failed with E2BIG. Linux/macOS have a much larger limit (256KB+) so this never showed in local testing. The downstream errors were cascade noise: jq fails to write payload → call_api runs with empty/partial payload → various error paths fire. Fix: write TOOLS_JSON to a tempfile once per agent_turn and reference it via --slurpfile (same pattern already used for $MESSAGES_FILE). Tempfile is cleaned up at every return path. Verified locally — payload now builds cleanly with no argv warnings. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
|||
| 99f0b03c8c |
v0.6.1: fix TOOLS_JSON crash + slash robustness + backspace
THREE bugs that Bryan hit in v0.6.0:
(1) TOOLS_JSON: unbound variable crash on any unrecognised input.
Root cause: the TOOLS_JSON assignment was a single-quoted string spanning
~40 lines, and apostrophes in tool descriptions (Anthropic's, 'codametrix',
protocol's, etc.) closed the bash string prematurely. Bash then tried to
execute fragments of the JSON as shell commands ("NAME: command not found"
warnings) and TOOLS_JSON never got assigned. With set -u, the first
reference to $TOOLS_JSON crashed the whole script.
Fix: switch to a quoted-EOF heredoc — TOOLS_JSON=$(cat <<'TOOLS_END' ...
TOOLS_END). Heredoc with single-quoted delimiter preserves content
literally — apostrophes, backslashes, all of it. Verified: all 31 tool
defs now parse as valid JSON (including the previously-broken nc_msgs).
Also fixed the pre-existing \\" → \" escape error in nc_msgs.
(2) Slash command brittleness: /ssh-add\ * pattern matched only when args
were present; /ssh-add alone fell through to the catchall and reported
"unknown command". Also failed silently with no usage message on the
too-few-args path.
Fix: rewrote all SSH slash patterns as /ssh-foo* (matches both with and
without trailing args), with a _slash_args() helper that cleanly extracts
the arg portion. Every handler now validates and prints "usage: ..." on
missing args before continuing. New _run_ssh_helper() wrapper centralises
the installed-check and swallows helper exit codes so they don't propagate
into the main loop.
(3) Backspace not working in MobaXterm/Cygwin terminals.
Root cause: terminal sends ^? (DEL) for backspace but stty erase is often
set to ^H (BS) in MobaXterm, so backspace passes through as a literal
character.
Fix: stty erase '^?' at REPL startup (harmless if already correct), AND
switch read_user_input to use `read -e -r -p` which uses libreadline for
line editing — backspace, arrow keys, history all work via readline now,
bypassing the terminal's stty config entirely. Falls back to plain read
on environments without readline support.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
|||
| f58bcf711f |
v0.6.0: secure SSH ControlMaster — password hidden from Larry-the-LLM
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> |
|||
| c2bba7be90 |
v0.5.5: @@VALUE inline PHI syntax + name canonicalization
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>
|
|||
| af3f034337 |
v0.5.4: pipe files to jq via stdin (MobaXterm Windows-jq path-translation fix)
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> |
|||
| cbe15d548f |
v0.5.3: send User-Agent + Accept headers in OAuth token exchange
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> |
|||
| c42fd92292 |
v0.5.2: OAuth endpoint migration — console.anthropic.com → platform.claude.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>
|
|||
| 9b198f4e50 |
v0.5.1: run self_update BEFORE the auth prompt (chicken-and-egg fix)
In 0.5.0 (and every prior version), prompt_first_run_auth was called
unconditionally at script load time, BEFORE self_update. On a never-
authenticated box, this meant a broken lib/oauth.sh trapped the user:
1. larry starts
2. no creds → auth prompt fires
3. pick OAuth → old broken oauth.sh runs → rate_limit_error
4. Ctrl-C at the API-key fallback prompt
5. script exits — self_update never ran
6. relaunch → exact same trap, forever
Fix: defer the auth-prompt call to run AFTER self_update. The auth
function DEFINITION stays where it is; only the CALL site moves. Now
on a fresh box:
1. larry starts
2. self_update phase A pulls MANIFEST and refreshes everything,
including a patched lib/oauth.sh
3. THEN the auth prompt fires, using the now-correct OAuth code
Verified: with no ANTHROPIC_API_KEY and no .oauth.json, the manifest
sync log lines appear before the "First-run authentication setup" menu.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
|||
| 28622ca40b |
v0.5.0: MANIFEST-driven self-update + OAuth code#state parsing
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>
|
|||
| b141d54847 |
v0.4.3: cross-env bundle for regression — no direct peer protocol needed
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>
|
|||
| a0502e2ec6 |
v0.4.2: operational layer — engine ctrl, tables CRUD, xlate viz, smat-diff, create-thread, tclgen
Seven new lib tools — covers the remaining Bryan-requested gaps.
lib/nc-engine.sh
- Cloverleaf process control. Wraps shipped binaries (hcienginestop,
hcienginerun, hcienginerestart, hciengineroutetest). Every action
is Y/N confirmed AND journaled into engine-actions.tsv.
- Subcommands: stop, start, bounce/restart, status, resend-ib,
resend-ob, route-test, testxlate, tpstest.
lib/nc-status.sh
- Runtime status, v1-modelled. Subcommands: sites, threads, not-up,
connections, queued, raw. Auto-discovers hcienginestat / tstat /
connstatus binaries; falls back to file-presence heuristics.
lib/nc-table.sh
- Read+CRUD for .tbl lookup tables. Subcommands: list, show, pairs
(→csv/tsv), lookup, reverse-lookup, add, delete, create, replace.
- All modifications journal-backed. Composes csv-to-table /
table-to-csv for format conversion.
lib/nc-xlate.sh
- Visualize .xlt files. Parses the TCL nested-block ops format.
Subcommands: list, show, ops (TSV), tree (ASCII flow), summary
(counts + segments + tables touched), diff (cross-xlate).
- Confirmed working against Epic_ADT_CodaMetrix.xlt: identified
12 PATHCOPY + 1 COPY ops across MSH/EVN/PID/PV1/PV2/PD1/ZPD/ZPV/
AL1/GT1/IN1/IN2.
lib/nc-smat-diff.sh
- Cross-env smat content diff. Samples N msgs from each side,
pairs by configurable HL7 field (default MSH.10 = control ID),
hl7-diffs each pair with --ignore MSH.7. Outputs per-pair reports
+ master _summary.md with paired/A-only/B-only counts.
lib/nc-create-thread.sh
- High-level: create a new protocol + optionally splice a route from
an existing thread to the new one. Both writes journal-backed.
Confirmed end-to-end: created to_metrics_test outbound + routed
IB_ADT_muxS → to_metrics_test via journal entries 001+002.
lib/nc-tclgen.sh
- TCL UPOC scaffolding from intent. Templates: tps-presc, tps-postsc,
tps-iclkill, xlate-helper, trxid, ack, field-rewrite. Produces
clean syntax-correct TCL ready to edit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
|||
| 3eb88f86c8 |
v0.4.1: each / each-site / len2nl / csv-to-table / table-to-csv
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>
|
|||
| 47e44c2289 |
v0.4.0: chain walk, OR/NOT filter groups, numeric/range ops, smat history
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>
|
|||
| 8ffdeb4f5d |
v0.3.4: field-name aliases, dot/dash syntax, ops (=, !=, ~, !~), new formats
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>
|
|||
| b9415f3b57 |
v0.3.3: PHI sanitize/desanitize + {{phi:...}} prompt preprocessing
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>
|
|||
| 6060cd28c1 |
v0.3.2: lesson capture (local-first learning loop)
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>
|
|||
| e08f030df5 |
v0.3.0: initial release of Larry-Anywhere
Portable AI agent for Cloverleaf integration work. Pure bash + curl + jq. Zero dependency on v1 wrapper scripts or v2 cloverleaf-tools.pyz. 27 native Anthropic tools: NetConfig parsing (read) nc_list_protocols, nc_list_processes, nc_protocol_block, nc_protocol_field, nc_protocol_nested, nc_protocol_summary, nc_destinations, nc_sources, nc_xlate_refs, nc_tclproc_refs NetConfig modification (journal-backed writes with rollback) nc_insert_protocol, nc_add_route, larry_rollback_list Workflows nc_find_inbound, nc_make_jump (3-thread jump pattern), nc_find (tbn/tbp/tbh/tbpr/where replacements), nc_document, nc_diff_interface, nc_regression Messages hl7_field, nc_msgs (smat is SQLite!), hl7_diff (with --ignore MSH.7) File system read_file, list_dir, grep_files, glob_files, write_file, bash_exec Validated against a 22-site real Cloverleaf test install. Five worked examples end-to-end: jump-thread generation, smat MRN search, system documentation, interface+connected diff, HL7-aware regression diff. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |