Added
- Wildcard support in
classifyentries — classify entries now accept a trailing*wildcard on the last token.mcp__github*matches every tool under the github MCP server, letting one line cover a whole MCP server instead of enumerating each tool. Exact entries always beat wildcard entries at equal prefix length, so a specific override still wins over a server-wide rule. Invalid patterns (leading*, mid-string*, bare*, multi-*) are rejected atnah classifywrite time and skipped with a stderr warning if they appear in hand-edited YAML. FD-024 semantics — implicit prefix matching remains forbidden, wildcards must be written explicitly — are preserved. Requested in #76 (nah-875)
Fixed
- Atomic config writes —
_write_configinsrc/nah/remember.pynow writes to a sibling temp file andos.replaces it over the target. Previously it calledopen(path, "w")which truncates the file to zero bytes before writing; concurrent Claude Code sessions calling_read_configduring that window could observe an empty file, parse it as{}, and later persist a single rule as the whole config — a full config wipe was reported in production. The fix resolves symlinks on the target (preserving dotfile-managed links), preserves the file's existing mode (or defaults to0o644), writes with explicit UTF-8 encoding, fsyncs the tempfile before rename, and fsyncs the parent directory on POSIX as a durability hedge. All six_write_configcall sites (write_action,write_classify,write_trust_host,write_allow_path, etc.) inherit the fix without modification. Lost-update races where two writers both persist stale state are explicitly deferred — that requires advisory file locking. Reported by @0reo (#66, nah-876) - Intra-chain
$VARexpansion before sensitive-path checks — Bash classification now propagates literal env assignments across&&/||/;stages and expands$NAME/${NAME}in later consumer tokens, soBAD=/etc/shadow && cat "$BAD"blocks where it previously allowed. Pipe|clears the var map (subshell boundary); unsafe RHS values ($, backticks, command substitution) are never propagated; the executed command string is never mutated. Covers bare andexport NAME=valueassignment forms. Bypass identified by srgvg (#74, nah-874)