a12f2416c4
45 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 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> |
|||
| b80f2fb29d |
v0.8.9: manifest-sync live progress indicator — silent ~3-min relaunch no longer looks frozen
Root cause: sync_from_manifest fully downloads all 48 manifest entries sequentially (authenticated HTTPS via proxy + Cloudflare), then cmp-compares locally to find the few that changed — 48 silent round-trips, ~3 min, no output. Add _sync_progress/_sync_progress_done: live in-place "checking N/48 <file>" (switching to "downloading N/48 <file>" on real changes) via \r\033[K only — MobaXterm-safe (no scroll-region/cursor-save/abs-pos). Gates on [ -t 2 ]; non-TTY emits a plain heartbeat every 10 files (no \r). Current filename shown so a hang is visible by name; per-file curl --max-time bounds each stall. Hash-skip speedup deferred: MANIFEST is paths-only (no hashes), so local skip-unchanged needs a manifest-format + release-tooling change — filed for v0.9.x. Sync correctness unchanged. Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com> |
|||
| 5ed82db770 |
v0.8.8: force unconditional 429 header capture so headers.log always generates
Bryan's MobaXterm work-box 429s never wrote headers.log because the v0.8.5 gate only fired on (OAuth + unified-*) OR retry-after — and his bare burst 429s carry neither. Detect 429 from the HTTP status line in the -D dump and ALWAYS write the full raw header block, exempt from the OAuth 50-call cap (own STATUS_429_HEADER_LOG_LIMIT budget), with a live phi/rl> stderr pointer. Non-stream path already reached the parser (call_api -D dump); the bug was the write-gate, not the call. Streaming path shares the same function. Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com> |
|||
| 4a992d9668 |
v0.8.7: status line renders on MobaXterm — gate on turn count not data presence
Root cause: render_status_line suppressed the OAuth line whenever ctx_used, 5h_util, and 7d_util were ALL empty. On a rate-limited session ctx is never recorded (the error path returns before _record_ctx_used) and pre-v0.8.5 the unified-* headers weren't captured on errors — so all three stayed empty turn after turn and the line never appeared on Bryan's work-box. NOT a positioning bug: the line is a plain printf'd dim line (no scroll-region/cursor escapes) and is not coupled to streaming or mouse mode. Fix: suppress only before the first turn (_LARRY_TURNS==0); thereafter always render — empty fields show "—" placeholders, reset date fills in once headers populate. /status now renders on demand even pre-first-turn. CR-taint sweep: coerce_int the reset-epoch arithmetic comparisons + strip_cr the oauth-status color case (MobaXterm CRLF would otherwise crash/blank the line). Verify: bash -n clean; 7/7 unit tests (turn-0 suppressed, turn>=1 placeholders, reset date when populated, renders with LARRY_NO_STREAM=1 + mouse off, survives CR-tainted epoch, LARRY_NO_STATUS=1 still disables). 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 (
|
|||
| 4f1ea86051 |
v0.8.5: rate-limit backoff + actionable message, streaming single-send, ErrorPI CR fix, phi once-notice
Diagnose-don't-assume rate-limit cluster (Clover #8). The rate_limit_error on a work-box with 90% of the 5h Max quota free was a short-window BURST rail, not 5h exhaustion — tripped by a stream->non-stream double-send per turn with no backoff. - Rate-limit backoff honoring retry-after (else exp 2/4/8 cap 30) + actionable header-parsed message naming the tripped rail; headers.log now captures every 429 (was OAuth+unified-* only), tagged with retry-after + rail. - parse_stream_to_response detects a non-SSE JSON error body (429/overload) and returns a distinct code so agent_turn surfaces it WITH backoff instead of re-sending the whole prompt (single-send invariant). Auto LARRY_NO_STREAM=1 on MobaXterm/Cygwin/MSYS; explicit LARRY_NO_STREAM=0 still forces streaming on. - ErrorPI fix: strip_cr on err_type/err_msg in _humanize_api_error (a trailing CR broke the case match AND carriage-overprinted "API error"); err/warn/log now strip embedded CRs defensively. (v0.7.5 sweep missed the error-display path.) - phi tier-5 notice once-per-session via $LARRY_HOME/.phi-notice-shown SESSION_ID flag (old export flag died in the $(...) subshell -> per-turn nag). Same-pattern sweep fixed the identical subshell-flag bug in _auto_phi_b64_roundtrip. Deliverable: Deliverables/2026-05-27-cloverleaf-larry-v085-ratelimit-streaming-fixes.md Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com> |
|||
| 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>
|
|||
| 9fc38e743d |
v0.8.1: tool-result content-shape gating + base64 round-trip + review gate
Three changes expanding PHI safety envelope on the tool-result surface. Closes V2 + V12 + V2-sub from Vera's audit. No behavior change for users not interacting with HL7-shaped data. - Tool-name allow-list dropped. The v0.7.3 tool-result auto-PHI gate ran only on read_file (.hl7|.txt), nc_msgs, hl7_field, hl7_diff. v0.8.1 runs _auto_phi_looks_like_hl7 on EVERY tool result. On hit → route through lib/hl7-sanitize.sh. On miss → pass through unchanged. Closes V2: bash_exec / ssh_exec / grep_files / read_file of any extension all get scanned when their output is HL7-shaped. False- positive cost is negligible (extra regex pass on non-HL7 has zero behavioral impact). - Base64-wrapped HL7 round-trip. New _auto_phi_b64_roundtrip helper. Detects candidate base64 runs (length >= 200, [A-Za-z0-9+/=] only, length divisible by 4 — NOT entropy-based per Pax §V2-sub: HL7's repetitive prefixes survive base64 with LOW entropy, so entropy is the wrong signal). Speculatively decodes each candidate; if decoded bytes look like HL7, routes through hl7-sanitize.sh and re-encodes back into the result. Catches ssh_pull_smat sampled mode's TSV format. Requires python3 (installed everywhere larry runs); skipped with a one-time stderr warning when unavailable. Server-side TSV encoding kept (binary-safe transport); client-side unwrap handles the safety concern, no remote refactor needed. - Operator review gate for bash_exec/ssh_exec/ssh_pull/ssh_pull_smat results. When the tool produced HL7-shaped output OR the result exceeds LARRY_TOOL_RESULT_REVIEW_THRESHOLD bytes (default 8192), Larry prompts [Y/n/i] before passing the result back to the model. 'i' opens the full output in $PAGER then re-prompts. Default Y (zero friction). N substitutes a refusal JSON so the model surfaces that something was withheld. Skipped when LARRY_AUTO_PHI=off (opt-out consistency) OR no TTY (headless scripts unaffected). Override with LARRY_TOOL_RESULT_REVIEW=always for paranoid mode. Closes V12. Proactive same-pattern sweep. Searched for other call sites where tool output bypasses content-shape gating: only the one in agent_turn. The v0.8.0-c strict-mode tool-result branch was updated in lockstep so it now triggers on the broader (content-only) eligibility. Verification: bash -n clean; b64 round-trip unit-tested with three cases (real-world HL7 base64 → decoded contains tokenized PHI not clear-text PHI; plain text → passthrough; non-HL7 b64 → passthrough, no false positive). Co-Authored-By: Clover (Claude Opus 4.7) <noreply@anthropic.com> |
|||
| 7434e6e8b8 |
v0.8.0: PHI safety quick-wins — path-block + /load HL7 routing + strict mode
Three independent zero-risk patches closing V3/V4/V5/V6/V11 gaps from
Vera's static PHI-leak audit. Implemented per Pax's mitigation
recommendations. No new deps, no behavior change for users not handling PHI.
- tool_read_file / tool_grep_files / tool_glob_files / tool_list_dir now
refuse paths under $LARRY_HOME/{log,sanitize,sessions} and
$LARRY_HOME/{.oauth.json,.env} with a structured JSON error the model
must surface. Block-list evaluates at call time; comparison runs against
both the literal and realpath-canonicalized form of both PATH and
$LARRY_HOME. Closes V4 + V6 + V11 (de-sanitization key, OAuth tokens,
PHI clear-text audit log). The proactive same-pattern sweep extended
the block from read_file alone to grep_files/glob_files/list_dir.
- /load <file> pre-routes HL7-shaped content through lib/hl7-sanitize.sh
(segment-aware tokenizer) BEFORE the user_input auto-PHI pass. Closes
V3 — smat dumps loaded via /load no longer rely on the lighter per-word
classifier.
- LARRY_AUTO_PHI=strict (fourth value alongside off/on/confirm) is the
fail-closed mode. Aborts the turn when sanitizer is missing or returns
empty on HL7-shaped content, or when tokenize-value fails. On the
tool-result surface (can't kill an in-flight tool_use), substitutes
the result with a refusal sentinel so raw HL7 NEVER reaches the model.
Existing off/on/confirm semantics unchanged. /phi-auto strict toggle,
/help text, and tests updated. Closes V5.
Refs:
Deliverables/2026-05-27-cloverleaf-larry-phi-leak-audit.md (Vera)
Deliverables/2026-05-27-cloverleaf-larry-phi-mitigation-research.md (Pax)
Verification: bash -n clean; path-block unit-tested with 13 cases including
symlink resolution (file and dir), ../ traversal, nonexistent paths, and
the empty-LARRY_HOME edge case — all pass.
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> |
|||
| 6a12c3d0f9 |
v0.7.4: drop GitHub fallback from auto-update (single-source Gitea)
The v0.7.2 GitHub fallback is now functionally broken: the GitHub mirror
is being made private, so anonymous raw fetches return 401/403. Rather
than ship a silent-failure path to a dead URL, remove the fallback
entirely.
Changes:
larry.sh
- LARRY_BASE_URL_FALLBACK / LARRY_ORIGIN_DEFAULT_GITHUB removed
- sync_from_manifest_with_fallback and _fetch_with_fallback retained
by name (call-site compat) but are now single-source wrappers; on
origin unreachable they warn "auto-update skipped this launch" and
proceed with locally cached files (no crash)
- status-line _origin_badge collapses to "" (default) or "custom"
(user-pinned HTTPS URL); legacy github / fallback badges gone
- /origin slash command simplified:
/origin show effective origin + pin file
/origin gitea pin to the default Gitea URL
/origin auto clear the pin
/origin <https-url> pin to an arbitrary HTTPS mirror
/origin github returns a clear error (mirror is private)
- /help text updated to reflect single-source model
- LARRY_VERSION 0.7.3 -> 0.7.4
install-larry.sh
- LARRY_BASE_URL_FALLBACK removed; single-origin install path
- fetch() dies with a clear error when origin unreachable:
"install failed: cannot reach LARRY_BASE_URL=... — verify the URL
or set LARRY_BASE_URL to a reachable mirror"
- post-install fallback-warning block removed
VERSION: 0.7.3 -> 0.7.4
Migration: stale $LARRY_HOME/.origin files containing the legacy keyword
"github" are treated as invalid — Larry warns once at startup and reverts
to the default Gitea origin. We deliberately do NOT auto-rewrite the
file (so the user can choose) and do NOT translate it to the GitHub raw
URL (which would just 401 on the next fetch).
Verification:
- bash -n larry.sh / install-larry.sh: pass
- larry --version: prints 0.7.4
- LARRY_BASE_URL=https://invalid.example.invalid + stale .last-sync-version:
logs "warn: ... unreachable, auto-update skipped this launch", no crash
- /origin auto clears pin file; /origin (no arg) shows current effective
origin and pin file; /origin <https-url> persisted; /origin github
returns clear error; /origin gitea re-pins to default
- stale .origin containing "github" -> startup warn + revert to default
Co-Authored-By: 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 |
|||
| 81c4875ecf |
v0.7.2: Gitea becomes primary auto-update origin; GitHub demoted to fallback
Switches the canonical $LARRY_BASE_URL default from raw.githubusercontent.com
to the self-hosted Gitea mirror at git.bjnoela.com. GitHub stays in the
loop as $LARRY_BASE_URL_FALLBACK and is used automatically when the primary
fails (DNS, timeout, HTTP error, private repo).
What's new
- Origin defaults split into LARRY_BASE_URL (Gitea) +
LARRY_BASE_URL_FALLBACK (GitHub). Env vars still override either side.
- Every network call in self_update tries primary first, then fallback.
Emits "warn: gitea unreachable, falling back to github" on switch and
"warn: self-update skipped (both origins unreachable)" if both fail.
- New /origin slash-command family:
/origin — show current primary/fallback + which served last
/origin gitea — pin to Gitea (default state)
/origin github — swap so GitHub is primary, Gitea fallback
/origin auto — clear pin, revert to defaults
/origin <https://...> — pin to an arbitrary HTTPS base URL
Pin is persisted to $LARRY_HOME/.origin and re-read on next launch.
- Status line picks up a light origin badge when state is non-default
("github" pinned, "custom" pinned, or "gitea→github" on failover).
- install-larry.sh mirrors the same primary→fallback fetch logic so
first-contact installs still work even if Gitea is unreachable.
ACTION REQUIRED — Bryan, before this commit's auto-update path becomes
live you must set git.bjnoela.com/bryan/cloverleaf-larry repo visibility
to PUBLIC. Gitea defaults to private; until you toggle it, every client
will silently fall back to GitHub. Verify by running, from any box:
curl -fsSI https://git.bjnoela.com/bryan/cloverleaf-larry/raw/branch/main/VERSION
A 200 with the published VERSION means clients hit Gitea; a 404/403 means
they still ride the GitHub fallback.
Don't break
- v0.7.1 status-line position (between turns)
- v0.7.0 HL7 completion, mouse mode
- v0.6.9 status line state tracking, header capture
- v0.6.7 streaming, @file, slash completion, persistent history
- v0.6.6 CR-strip + slash TAB
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
|||
| 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> |
|||
| 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>
|
|||
| a527dc7172 |
v0.5.0: visible "✓ LARRY UPDATED" banner at startup
When self_update() actually did something (phase A synced files, or phase B
self-replaced + relaunched), print a green/bold box at REPL startup with
the from→to versions and file count. Stays silent when nothing happened.
Phase B now also passes LARRY_PREV_VERSION across the exec relaunch so the
new script can report the prior version, not just "updated to 0.5.0".
Verified locally against file:// base URL: first launch shows
═══════════════════════════════════════════════════════════════
✓ LARRY UPDATED
first-run sync at v0.5.0 (34 files synced from manifest)
═══════════════════════════════════════════════════════════════
Second launch is silent (.last-sync-version matches running LARRY_VERSION).
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>
|
|||
| 63d7bc6756 |
bump LARRY_VERSION constant to 0.3.0 to match VERSION file
The hardcoded constant in larry.sh was stale at 0.1.0. Cosmetic-only fix that makes `larry --version` agree with the VERSION file already at 0.3.0. 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> |