Fixed
- #462 / #474 — restricted shell mode no longer rejects
for/while/if
loops,caseblocks and subshells. The allowlist checker now expands a
compound command down to its leaf command segments and validates each segment
against the allowlist, so legitimate constructs (for f in *.rs; do cat $f; done,if test -f x; then ls; fi,( ls && pwd )) run under restricted mode
while injection attempts smuggled through the same constructs stay blocked. - #476 / #477 —
lean-ctx uninstall --helpno longer performs a real
uninstall. The--help/-hflag fell through to the uninstaller, which
removed the installation instead of printing usage. The CLI now short-circuits
uninstall --help/-hto print the usage text and exit without touching
processes, configs, data or the binary. - #356 — the "lean-ctx wants to access your Documents folder" prompt is now
closed even forbrew upgrade-only installs. The path guards + LaunchAgent
Seatbelt wrapper already made daemon/proxy boot promptless, andlean-ctx update/dev-installregenerate the plists with that wrapper. The remaining
hole was a user who only runsbrew upgrade(which bypasses lean-ctx's
updater), so their pre-Seatbelt plists were never regenerated. New belt-and-
suspenders: a launchd-standalone process (ppid 1) now re-execs itself
under the deny-~/DocumentsSeatbelt at startup if it is not already
wrapped (reexec_under_seatbelt_if_needed, called first thing inmain). A
sentinel env var baked into the plists (LEAN_CTX_SEATBELT) prevents any
double-wrap for current-code plists; terminal/editor children (host TCC grant)
and non-macOS are unaffected. Verified by the existingtcc_sandbox.sh
SIGKILL-on-access boot test. This makes the daemon/proxy promptless
independent of code signature, so no Apple Developer ID is required. - #451 —
ctx_shell/lean-ctx -cno longer run agent commands in a
non-POSIX interactive shell.$SHELLis the user's interactive shell; when
it is Nushell, Fish, Elvish, xonsh or PowerShell, an agent's bash/POSIX command
silently mis-executes.detect_shellnow honors$SHELLonly when it is
POSIX-compatible (bash/zsh/sh/dash/ash/ksh/mksh) and otherwise falls back to a
real POSIX shell. zsh/bash users are unaffected;LEAN_CTX_SHELLstill forces a
specific shell regardless of the gate. - Shell gotcha auto-learning now correlates fail→fix in CLI (
lean-ctx -c)
mode.pending_errorswere#[serde(skip)]and cleared on load, so a fix
spanning two separatelean-ctx -cprocesses never correlated (only the
long-lived daemon could). They are now persisted (bounded byMAX_PENDING+ a
15-min TTL, pruned on load), so a later process loads the pending error and
correlates the fix — the gotcha loop now works in the hybrid CLI-shell setup.
Added
- #668 — FinOps showback: readable project names. The savings ledger stores
only a truncated repo hash (never a path), so thefinops exportproject
column was opaque. An opt-in<config_dir>/finops-aliases.toml([projects]
<repo_hash> = "Team", also--aliases=FILE/$LEAN_CTX_FINOPS_ALIASES) now
maps hashes to human-readable names at export time only — the ledger, the
signed batch and the hash chain are never touched, so privacy guarantees and
signatures stay intact. Unmapped hashes fall back to the hash, so an incomplete
map never drops rows. Newcore/finops_export/aliases.rs; applies to all
targets (FOCUS / CBF / Vantage). - #674 — central, signed org policy distribution + admin.
lean-ctx policy org sign <pack.toml> --org <name>wraps a policy pack in an Ed25519-signed
artifact; endpointspolicy org trust <pubkey>(pin once, out-of-band) and
policy org install <artifact>, after which the runtime folds the org pack in
as an un-bypassable floor beneath the local.lean-ctx/policy.toml. The
local pack can only ever tighten it:deny_toolsunion,allow_tools
intersect,redactionunion (org patterns win clashes), the stricter filter
action, the tighter egress/max_context_tokenscaps, the longer
audit_retention_days. Two independent checks gate enforcement — the signature
must verify and the signer key must be pinned — so a forged or untrusted
artifact is ignored, never enforced, and never bricks the agent (fail-open);
with no key pinned nothing is enforced (opt-in).--advisorydistributes a
policy for preview without enforcing it;policy org statusshows the
effective floor andpolicy org verifychecks an artifact offline. Pluggable
source (LEANCTX_ORG_POLICY/LEANCTX_ORG_TRUST_KEYfor MDM). New
core::policy::org+core::policy::floor; contract
docs/contracts/org-policy-v1.md. - #677 — signed CISO compliance report.
lean-ctx compliance report --from <rfc3339> --to <rfc3339> [--framework eu-ai-act|iso42001|soc2]... [--pack <name|path>] [--format json|csv|pdf|text]composes the engine's evidence
surfaces into one Ed25519-signed artifact for a date range: OWASP
Top-10-for-Agents alignment, framework coverage (verified live against the
resolved pack), what enforcement blocked (ToolDenied) and redacted
(SecretDetected) over the period (folded from the append-only audit chain,
with the segment'shead_hashbound into the signed payload), and the
retention posture (packaudit_retention_daysintent vs. plan entitlement).
The signed JSON is always written and is offline-verifiable withlean-ctx compliance verify <report.json>(no audit trail, no LeanCTX needed);--format csv|pdfadditionally emits that human rendering — the PDF is a real,
dependency-free PDF 1.7. Honest by construction: a quiet period reports zero
blocks, and a broken local chain is reported (chain_valid = false), never
hidden. Newcore::compliance_reportmodule; contract
docs/contracts/compliance-report-v1.md. - #676 — egress / output DLP on agent writes & actions. A new
[egress]
policy-pack section governs what the agent emits (the output side of the
Great Filter), checked before dispatch ofctx_editwrites and
ctx_shell/ctx_executeactions — so a blocked write never touches disk and a
blocked command never runs.forbidden_patternsare regexes that refuse a
write/action on match (e.g. a prod-DB DSN or a destructive query);
block_secretsrefuses content carrying detected secrets (the pack's
[redaction]patterns) or PII (the #675 checksum-validated detectors);
max_writes_per_minis a per-process sliding-window rate limit on agent
writes/actions. Blocked egress returns[POLICY BLOCKED]and is audited
(ToolDenied) with a non-sensitive reason (forbidden-pattern:…,secret,
pii:…,rate-limit) — never the matched content. Egress obeys the same
opt-in / fail-open / Local-Free guarantees;forbidden_patternsaccumulate and
the scalars override down theextendschain. Newcore::egressmodule. - #675 — inbound content filters (PII / classification / prompt-injection).
A new[filters]policy-pack section adds net-new detectors that run inside the
enforcement pipeline before tool output reaches the agent (the input side of
the Great Filter). Each detector takes an action —off/warn/redact/
block:piifinds Swiss AHV (EAN-13), IBAN (mod-97), payment cards
(Luhn) and email, each checksum-validated to keep false positives low;
classificationgates files marked confidential/secret (banner lines or
aClassification:field, not prose mentions;blocked_labelsis
configurable);injectionmasks/blocks OWASP-LLM01 prompt-injection lines
(reusingoutput_sanitizer::detect_injection). Decisions are audit-logged
privacy-preservingly — only(class, count)pairs (e.g.pii:iban×2), never
the matched value. Filters obey the same opt-in / fail-open / Local-Free
guarantees as the rest of the pack; actions override andblocked_labels
accumulate down theextendschain. Newcore::input_filtersmodule. - #673 — context policy packs are now enforced at runtime. A project pack
(.lean-ctx/policy.toml, authored from any built-in vialean-ctx policy show <name> --toml) is applied at the MCP hot path:deny_tools/allow_tools
gate which tools the agent may call (denied calls return[POLICY DENIED]and
are audited asToolDenied),[redaction]patterns strip matches
([REDACTED:<name>]) from tool output before it reaches the model,
default_read_modesets thectx_readfallback when the caller omitsmode,
andmax_context_tokenstightens (never loosens) the session token ceiling.
Enforcement is opt-in (no pack → unchanged behavior), fail-open on an invalid
pack, and Local-Free — only the agent pipeline is constrained, never a human's
own reads. Thectx/ctx_session/ctx_policymeta tools are never gated, so
a pack can never lock the operator out. - #454 —
prefer_native_editorconfig to opt out of lean-ctx edit operations.
Setprefer_native_editor = true(orLEAN_CTX_PREFER_NATIVE_EDITOR=1) so the
lean-ctx edit tool (ctx_edit) is neither advertised inlist_toolsnor
dispatchable (direct or viactx_call); the agent falls back to the host's
built-in editor UI. Read / search / shell / memory tools are unaffected.
Colorized diffs are intentionally left to host extensions rather than the MCP
tool output, which must stay byte-stable for prompt caching (#498).
Upgrade
lean-ctx update # recommended (auto-downloads + refreshes shell hooks)
cargo install lean-ctx # or
npm update -g lean-ctx-bin # or
brew upgrade lean-ctxNote: After upgrading via cargo/npm/brew, run
lean-ctx setupto refresh shell aliases.lean-ctx updatedoes this automatically.
Full Changelog: v3.8.10...v3.8.10