fc667e2451
30 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| fc667e2451 |
v0.8.15: legacy/qa remote-enumeration fix — per-alias HCIROOT pin (sudo-gated profile bypass), hcisitelist-free NetConfig walk, ControlMaster banner+rotating-pw hardening; zero traffic-bypass primitives
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> |
|||
| fe2f67a1aa |
v0.8.13: $HCIROOT login-shell fix + both-mode detection + list_sites/sites + per-delta jq-fork slowness fix
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> |
|||
| a12f2416c4 |
v0.8.11: API-key default rail (OAuth-impersonation off, secure per-client /set-api-key) + manifest-hashing auto-update speedup
Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com> |
|||
| 578cefcc35 |
v0.8.6: work-box → Mac headers.log sync (tsk-2026-05-27-023)
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 (
|
|||
| 31ffae6f36 |
v0.8.4: installer/updater detects HTML-sign-in-page responses and fails loud
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> |
|||
| d4c382dc6d |
v0.8.3: tab-completion trailing-space no longer breaks command dispatch
The slash-command completer (__larry_complete_slash) intentionally appends a trailing space after a unique match for arg-command ergonomics, but the main_loop dispatcher matched exact `case` globs — so a completed `/quit ` missed the `/quit)` arm and fell through to "unknown command". Latent since v0.6.6 (tab completion). Fixed by rtrimming the dispatch key once at the `case "$input"` boundary, which also transitively protects the sub-command dispatchers (/origin, /phi-auto, /phi-sidecar, /mouse) that consume the same $input via _slash_args. Interior `/load FILE` spacing is preserved. Added a shared rtrim() helper to lib/cygwin-safe.sh next to strip_cr. Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com> |
|||
| 60b8f0e1c8 |
v0.8.2: Presidio sidecar for free-text NER (tier-5) — closes V1
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>
|
|||
| 9dd5821436 |
v0.7.5: OAuth CR-taint fix + mouse opt-in + CR-safety sweep
- Fix bash arithmetic crash on MobaXterm/Cygwin: $(date +%s) was returning CR-tainted values landing in $(( )) operands - Mouse mode off by default; opt in via LARRY_MOUSE=1 or /mouse on - Comprehensive CR-safety sweep across lib/*.sh and larry.sh — every command-substitution result, file read, and user input that feeds an arithmetic context, case dispatcher, or path/header is now CR-stripped at the source New shared helper lib/cygwin-safe.sh defines three primitives: coerce_int VAL [DEFAULT] — for arithmetic / integer-test operands strip_cr VAL — for case patterns, regex tests, paths, headers read_clean VAR [PROMPT] — read -r wrapper that strips CR pre-assign Hardened call sites (14 files, 60+ patch points): - larry.sh: status-line date/tput, 3 y/N approvals, auth menu, API key - lib/oauth.sh: cmd_login + cmd_refresh date+%s captures - lib/nc-engine.sh: 5 y/N action prompts + find|wc arithmetic - lib/nc-msgs.sh: parse_time_ms (4 date sites) + meta-TSV time + MSG_COUNT - lib/nc-regression.sh: tr|wc count + hl7-diff ?-fallback arithmetic - lib/nc-smat-diff.sh: A_COUNT/B_COUNT/DIFFS_TOTAL - lib/nc-insert-protocol.sh: every awk-emitted line number → head/tail math - lib/journal.sh: _next_seq wc -l arithmetic - lib/lessons.sh: _next_id/_count + 2 y/N prompts - lib/hl7-sanitize.sh: cmd_count + clear-table y/N - lib/ssh-helper.sh: 4 local+remote wc -c integer compares - lib/nc-find.sh, lib/nc-table.sh, lib/nc-document.sh, larry-rollback.sh Reproduces the exact error Bryan hit: bash: ...: arithmetic syntax error: invalid arithmetic operator (error token is "") lib/cygwin-safe.sh added to MANIFEST so it auto-syncs on next launch. Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com> |
|||
| 58e6bf4e03 |
v0.7.3: automatic PHI detection (tiered detection + blacklist contexts)
Adds automatic PHI tokenization on two surfaces: user input and HL7-shaped tool results. Supersedes Bryan's reverted |
|||
| 0927238dcd |
v0.7.1: status line moves from above-prompt to between-turn (post-input, pre-response)
render_status_line is no longer called before printf 'you[model]>'. It is
now invoked after read_user_input returns and after @file/PHI preprocessing
complete, immediately before add_user_text/agent_turn. The visual effect is
that the dim status divider sits BETWEEN turns — summarising the cost of
the just-completed turn as the user heads into the next one.
The slash-command and empty-input paths all 'continue' before the new call
site, so no status line renders on /help, /status, /clear, /quit, etc.
First-turn suppression continues to live inside render_status_line (it
returns silently while STATUS_* globals are empty and _LARRY_TURNS=0), so
the very first prompt of a session still has nothing above the response.
/status on-demand command is unchanged; LARRY_NO_STATUS=1 still disables
entirely. Comments updated at render_status_line, the STATUS_* globals
header, the help block, and the LARRY_NO_STATUS env doc.
Supersedes the earlier combined v0.7.1 (
|
|||
| af2ffe883c |
v0.7.1: status line below prompt + automatic PHI detection + session-artifact upload
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>
|
|||
| 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> |
|||
| 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>
|
|||
| 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> |
|||
| 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>
|
|||
| 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>
|
|||
| 61f1500492 |
v0.3.1: OAuth subscription auth + offline manual cheat sheet
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>
|
|||
| 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> |