Skip to content

Releases: manuelschipper/nah

v0.9.0

21 May 20:38

Choose a tag to compare

Added

  • Guarded Codex headless execnah run codex exec and nah run codex e
    now guard local headless Codex runs with deterministic PreToolUse
    enforcement, block unresolved asks by default, disable unsupported headless
    tool surfaces, and log headless fallback/sandbox metadata. (nah-936)
  • First-class Nix packaging — added default.nix, a flake, and Nix CI so
    users can install the full nah CLI with YAML config and OS keyring support
    through nix profile add github:manuelschipper/nah. Nix and
    pip install "nah[config,keys]" are now peer recommended CLI install paths;
    the Claude plugin remains a separate Claude-only distribution. Inspired by
    PR #82 from ryanswrt. (nah-937)
  • Trusted container exec unwrapping — opt-in trusted_containers config can
    make docker exec, docker container exec, and simple
    docker compose exec transparent for narrow read-like payloads inside exact
    trusted identities, while writes, package scripts, network/database/container
    actions, unknown tools, credential-marker payloads, and unsupported Docker
    flags still ask or block. (nah-924)
  • Codex authority prompt routingnah run codex now launches Codex with
    approval_policy="untrusted" and installs a nah-managed
    $CODEX_HOME/rules/nah-authority.rules file so Codex-known-safe command
    prefixes such as cat, git, rg, and shell wrappers still route through
    nah's PermissionRequest classifier before execution. nah codex setup
    creates or refreshes this authority file, nah codex doctor inspects
    approval-memory/MCP drift, and nah codex remove-setup removes only the
    nah-managed setup files. (nah-923)
  • Codex confirm-edits mode — safe project-local apply_patch add/update
    edits now allow by default after nah path and content checks, while
    nah run codex --confirm-edits keeps those safe edits on Codex's native
    approval path for users who want edit confirmations.
  • Lang-exec LLM review for heredoc scripts — LLM script-veto prompts now
    include the full inspected heredoc body for interpreter commands such as
    python3 <<'PY', and the review policy now allows plainly read-only local
    inspection scripts instead of escalating solely for ordinary config/log/state
    reads.
  • Codex PreToolUse observation for taint trackingnah run codex now
    injects Codex PreToolUse, PermissionRequest, and PostToolUse hooks via
    the canonical features.hooks flag. PreToolUse observes routine Bash, MCP,
    and apply_patch calls without LLM review so taint source reads can be
    tracked before execution, while PermissionRequest remains the enforcement
    hook and PostToolUse confirms execution outcomes. (nah-921)
  • Runtime-neutral session taint tracking — opt-in taint mode now tracks
    successful sensitive reads across Claude, Codex, and terminal guard sessions,
    propagates labels to local writes/repo state, and can audit or enforce
    activation/boundary policies without weakening existing nah decisions.
    Defaults remain off; terminal guard taint support is audit-only in v1.
    (nah-919)
  • Session provenance guard — opt-in provenance mode now tracks
    successful writes from guarded Claude/Codex runs and can pause later
    activation or boundary actions when they operate over session-written files
    or repo state. context policies build a bounded session-delta packet for
    LLM review; incomplete packets or uncertain reviews remain asks. (nah-929)
  • Runtime execution outcome logging — nah now records append-only
    runtime and execution metadata for Claude, Codex, and terminal guard
    decisions so audit logs can distinguish a pre-execution permission decision
    from an observed tool outcome. Claude PostToolUse/PostToolUseFailure and
    Codex PostToolUse hooks log successful or failed execution without changing
    permission policy, while terminal prompts report denied or approved-to-run
    states without claiming process completion. (nah-920)
  • Non-Git project config with exact-root trust — nah now loads
    ./.nah.yaml from the current directory outside Git while keeping Git-root
    config precedence inside repositories. Project config remains tighten-only by
    default; nah trust-project / nah untrust-project manage exact project
    roots whose config may loosen policy and activate project classify rules.
    (nah-918)

Fixed

  • Agent hook executable transport — Claude direct hooks and Codex hook
    overrides now call the installed nah executable instead of a raw Python
    interpreter plus import path. This fixes Nix and wrapper-based installs where
    the package is importable through the nah executable but not through the
    bare interpreter. nah update claude migrates old direct-hook settings even
    when the old shim file is missing. Reported in #83
    by ryanswrt. (nah-943)

Changed

  • Session provenance outside-project identity — session-written files
    outside the current project boundary now stay direct-path-only in provenance
    state instead of being aggregated under the current repo; exact path
    activation can still trigger provenance review, but base outside-project
    asks/blocks keep their authority. (nah-939)
  • Decision prompts no longer include auto-allow hints — ask/block output no
    longer appends remediation suggestions such as nah trust, nah allow,
    nah allow-path, or nah classify, because misleading shortcuts can loosen
    policy in the wrong place. Friendly safety reasons and diagnostic metadata
    remain. (nah-935)
  • Taint boundary sinks — taint tracking now treats network diagnostics,
    database reads, browser interaction/navigation/exec, container actions, git
    history rewrites, remote agent execution, and agent servers as boundary sinks
    by default. Users can tune category membership with
    taint.categories.*.add/remove.

Fixed

  • Bazel test label classification — local bazel test and
    bazelisk test target labels now classify as package_run, so valid
    Bazel labels such as //pkg:target no longer pause as unknown commands
    or get mistaken for filesystem paths. (#62)
  • jq read-only classificationjq now classifies as
    filesystem_read, so JSON inspection pipelines such as
    ... --json | jq '.metadata' no longer pause as unknown commands while
    sensitive-path reads still stay guarded.
  • Codex setup command surfacenah codex setup now backs up and fixes
    supported Codex approval-memory/MCP drift, so the separate pre-v1
    nah codex repair command has been removed. (nah-925)
  • YAML llm.mode: on/off parsing — PyYAML parses unquoted on and off
    as booleans, so nah now accepts boolean true/false anywhere it reads LLM
    mode, including target overrides and inline --config overrides.
  • Codex lifecycle guidance — bare nah install, nah update, and
    nah uninstall now explain that Codex is session-scoped through
    nah run codex, and nah update codex reports that there is no persistent
    Codex update target.

v0.8.3

06 May 20:16

Choose a tag to compare

Added

  • HTTP and REST API intent classification — visible HTTP API calls now
    classify by service intent: GET/HEAD/OPTIONS use context-resolved
    service_read, POST/PUT/PATCH use service_write, DELETE and destructive
    paths use service_destructive, and remote service actions still participate
    in network data-flow blocks such as curl ... | bash. (nah-910)
  • GraphQL operation intent classification — visible GraphQL operations now
    classify by action intent instead of HTTP method alone: queries and
    subscriptions use context-resolved service_read, mutations use
    service_write, destructive mutation names/root fields use
    service_destructive, and hidden or ambiguous documents stay on ask paths.
    (nah-911)
  • JSON-RPC and MCP method intent classification — visible JSON-RPC request
    bodies now classify by method intent before REST fallback: read-like methods
    use context-resolved service_read, write-like methods use service_write,
    destructive methods use service_destructive, and generic MCP tool
    invocation stays on an ask path unless a separate trusted tool classifier
    handles it. (nah-912)
  • gRPC CLI method intent classification — visible grpcurl calls now
    classify by method intent: read-like methods and reflection verbs use
    context-resolved service_read, write-like methods use service_write,
    destructive methods use service_destructive, and missing or unknown
    methods stay on ask paths. (nah-913)
  • WebSocket and Socket.IO event intent classification — visible wscat
    and websocat commands now distinguish connection-only traffic from sends,
    classify visible event names into service_read, service_write, or
    service_destructive, parse simple visible Socket.IO 42[...] event
    packets, and keep opaque sends on ask paths. (nah-914)
  • SQLite read-only CLI classification — explicit read-only sqlite3
    inspection commands now classify as db_read for simple SELECT, safe
    EXPLAIN, safe PRAGMA introspection, and safe dot commands; bare SQLite,
    script-fed SQL, mutating SQL, unsafe helpers, and ambiguous forms stay
    db_write. (nah-916)
  • Postgres read-only CLI classification — explicit one-shot psql
    inspection commands now classify as db_read when they set same-invocation
    PGOPTIONS to default_transaction_read_only, disable psql startup files,
    and use a narrow read-only SQL allowlist; bare, script-fed, mutating, or
    ambiguous Postgres commands stay on existing db_write ask paths. (nah-bqe)

Fixed

  • Package script argument boundary classificationnpm run <script> --,
    pnpm run <script> --, bun run <script> --, and explicit package exec
    payloads no longer treat child arguments such as -g, --global, or
    --target as package-manager global install flags; malformed or
    package-owned global flags still ask. (nah-917)
  • Curl host extraction skips body and option values — curl/wget-style host
    detection now ignores option values such as JSON bodies, config files, cert
    paths, and headers before selecting the actual request URL. (nah-909)

v0.8.2

06 May 02:29

Choose a tag to compare

Changed

  • Codex runtime simplified around workspace-writenah run codex now
    uses one protected local interactive preset: Codex workspace-write,
    on-request, user approvals, nah PermissionRequest hooks, and preflight.
    Normal Codex UI flags still pass through, while sandbox/approval overrides
    remain rejected because nah owns that safety boundary. (nah-908)

Removed

  • Removed Codex flow/edit mode surface — removed nah-owned --flow,
    --auto-edits, --no-sandbox, explicit --sandbox, and hook-side safe
    apply_patch auto-allow behavior from nah run codex. Safe project edits
    should flow through Codex workspace-write; risky apply_patch permission
    requests still ask or block after nah path/content checks. (nah-908)

v0.8.1

05 May 02:38

Choose a tag to compare

Changed

  • Project license restored to MIT — current source snapshots and future
    releases are MIT licensed again. The already-published v0.8.0 package
    metadata briefly reflected the short-lived license transition before this
    reversal.

v0.8.0

05 May 02:10

Choose a tag to compare

Breaking

  • Target-first install/update/uninstall commandsnah install, nah update, and nah uninstall now require an explicit target instead of defaulting to Claude Code. Use nah install claude, nah update claude, and nah uninstall claude for direct Claude hooks; shell and provider targets use the same shape. Bare nah install exits nonzero with a guided target list, and the old --agent lifecycle shape is no longer the documented product surface. (nah-882)

Added

  • Human-friendly safety explanations — terminal guard prompts, Claude Code permission reasons, nah test, and compact nah log output now show short nah paused: / nah blocked: messages such as “this can rewrite Git history” while preserving the technical reason, action type, hints, and JSON/log diagnostics alongside a new human_reason field. (nah-884)
  • Opt-in bash and zsh terminal guard — added nah install bash and nah install zsh to protect interactive shell sessions with managed rc-file snippets that classify complete single-line commands before execution. The guard supports status/doctor diagnostics, prompt-on-ask behavior, fail-closed handling for unsupported multiline/here-doc/continuation input, explicit bypass via nah-bypass <command> or NAH_TERMINAL_BYPASS=1, and terminal decision logging for blocks, denied asks, confirmed asks, bypasses, and errors while keeping allowed terminal commands out of the nah log by default. (nah-882)
  • Target-aware dry runs and config overrides — added nah test --target <target> and --json, plus target-scoped config under targets.<target> for runtime-specific policies. Bash and zsh targets default to LLM mode off even when a global provider is configured, unless explicitly enabled under their target override. (nah-882)
  • Codex native hook support — added nah run codex for local interactive Codex sessions using Codex PermissionRequest hooks instead of shell-wrapper prompts. The runner injects nah-owned hook, sandbox, approval, and dynamic-MCP-disabling overrides, rejects unsafe Codex launch modes, routes Bash and MCP permission requests through nah classification, and adds nah codex doctor / nah codex repair to block and repair remembered Codex approvals or MCP approval modes that could bypass nah. (nah-897, nah-898)
  • Guarded Codex edit auto-allow — added nah run codex --auto-edits to auto-allow safe project-local apply_patch add/update edits after nah path, content, and LLM checks, while default sessions still ask and dangerous patches remain blocked or ask-only. (nah-904)
  • Codex flow mode — added nah run codex --flow as a shortcut for no Codex filesystem sandbox plus safe apply_patch auto-allow. Use --no-sandbox separately when you want to remove the Codex sandbox without also auto-accepting edits. (nah-906)
  • MCP threat-model audit coverage — added mcp_permissions to nah audit-threat-model, covering core MCP permission/runtime tests for Claude Code matcher registration, global-only MCP classification, wildcard safety, database/browser MCP action typing, Codex MCP PermissionRequest hooks, and Codex MCP approval-mode preflight/repair. README and public docs now report 1,807 category coverage hits across 13 tested danger classes. (nah-905)
  • OS keyring-backed LLM secrets — added nah key set, nah key status, nah key import-env, and nah key rm for storing optional LLM
    provider keys outside the process environment. Keyring support remains an
    optional keys extra, with env-var fallback for existing setups. Thanks
    @ZhangJiaLong90524 for the initial PR and direction.
    (#65, nah-889)

Changed

  • License transition for future releases — future source snapshots and
    releases are fair-source under FSL-1.1-MIT and convert to MIT two years after
    they are made available. Versions and source snapshots made available under
    MIT before this transition, including v0.7.1 and earlier tagged releases,
    remain MIT.
  • Agent session launchers use nah run — the one-shot Claude Code launcher
    is now nah run claude, matching nah run codex. Legacy nah claude exits
    with a pointer to the new command.
  • README and public docs rebranded for coding agents — refreshed README, site install/CLI/privacy/how-it-works/getting-started docs, and package metadata around “Context aware safety guard for coding agents” instead of a Claude-only positioning, with the user terminal guard treated as an optional bonus. Updated examples to current nah paused: / nah blocked: copy and documented Codex doctor/repair plus nah log --llm. (nah-899)
  • Terminal guard documented as opt-in shell protection — README, install docs, configuration docs, privacy copy, and lifecycle target help now describe bash/zsh terminal protection as opt-in per shell without preview labeling.
  • Minimal taxonomy profile deprecatedprofile: minimal now warns and behaves like profile: full, leaving full and none as the two supported profile shapes. The old minimal classification data has been removed so custom classifiers are not evaluated against a weaker built-in baseline.
  • Install lifecycle targets stay runtime-only — removed the unreleased nah install openrouter / nah uninstall openrouter convenience path so lifecycle commands only install or remove nah from guarded runtimes (claude, bash, zsh). LLM providers remain configured through global config. (nah-882 follow-up)
  • Install docs now start with a chooser — README and site install docs now separate the Claude Code plugin, PyPI CLI/direct-hook, terminal guard, config extra, and LLM provider configuration more clearly, and stale setup examples now use explicit target-first lifecycle commands instead of bare nah install. (nah-885)

Fixed

  • Windows drive-letter paths route through trusted pathsnah trust C:/Projects and nah trust D:\work now write to trusted_paths instead of
    being mistaken for network hosts in known_registries. Thanks @enermark.
    (#69)
  • Sparse environments keep useful log user attribution — structured
    decision logs now fall back from USER to LOGNAME, USERNAME, and finally
    getpass.getuser() before leaving the user field empty.
  • Codex flow edit approval race — safe apply_patch edits in nah run codex --flow now trust direct Codex patch payloads and retry the Codex transcript briefly when direct patch text is unavailable, fixing cases where the PermissionRequest hook fell back to the native edit prompt before nah could inspect the patch. (nah-907)
  • Quoted output text no longer becomes a fake redirect — Bash classification
    now preserves > characters that appear inside quoted or backslash-escaped
    printf / echo text before redirect decomposition, so prose such as
    <key> or -> no longer prompts as a filesystem write while real unquoted
    redirects to sensitive paths still block. (nah-902)
  • Claude launcher rejects unsafe bypass modesnah run claude now refuses
    --dangerously-skip-permissions, --enable-auto-mode, and
    --permission-mode bypassPermissions because those modes can run tool calls
    outside the guarded permission path.
  • Shell control-flow bodies are classified by payloadfor ...; do ...; done, while ...; do ...; done, and if ...; then ...; fi now expose their executable inner commands to the classifier instead of stopping at reserved words like for, do, and done. Literal for item lists are expanded into the loop body so safe batch GitHub/GitLab CLI API reads can allow while sensitive paths still block; dynamic loop values and control-flow body command substitutions fail closed. Tracks #78.
  • Unwrapped shell bodies mirror top-level variable expansionbash -c 'BAD=/etc/shadow; rm "$BAD"' and control-flow variants now apply the same intra-chain $VAR expansion used for top-level Bash commands, closing a sensitive-path bypass inside shell wrappers.
  • GitLab API form writes ask cleanlyglab api --form ... now classifies as network_write, including multipart file-upload forms, and gh / glab API prompt copy no longer mistakes field values such as file=@image.png for network hosts.
  • Bash terminal ask denials clear the prompt line — denied ask decisions now cancel and return to an empty prompt instead of restoring the same command line and making the shell look stuck. (nah-882 follow-up)
  • Terminal guard reload hint replaces active snippetsnah install bash / nah update bash now print a reload command that clears terminal guard environment and replaces the shell, so an already-running shell can load the updated guard without running startup files inside the old in-memory Readline hook. (nah-882 follow-up)
  • Bash terminal guard preserves normal prompt redraws — bash now filters the Readline buffer and lets Bash execute accepted commands normally, instead of running commands inside the Readline callback. Confirmed commands run through normal Bash execution, preserving shell state such as cd and source. (nah-882 follow-up)
  • nah update no longer looks like a project file writenah install / nah update now classify as nah lifecycle commands instead of treating target names such as bash or update as filesystem paths like ~/bash or ~/update when the terminal guard runs outside a Git project. (nah-882 follow-up)
  • Bash rc reloads replace the active guard — sourcing .bashrc in an already-guarded bash shell now refreshes nah's active function and key bindings instead of skipping the snippet because NAH_TERMINAL_GUARD_ACTIVE was already set. The original pre-nah binding metadata is still captured only once for diagnostics. (nah-882 follow-up)
  • Bash ask prompts use the hidden decision helper — bash ask d...
Read more

v0.7.1

20 Apr 09:22

Choose a tag to compare

Added

  • Official Claude plugin marketplace submission prep — polished the generated Claude Code plugin metadata with repository, author URL, and discovery keywords, rewrote the copied plugin README so release artifacts no longer describe themselves as local-only scaffolds, and added an Anthropic marketplace submission packet covering source refs, runtime behavior, data handling, trust/safety notes, and validation commands. Tests now assert the generated plugin and marketplace artifacts keep this submission-ready metadata and avoid regressing to local-only wording. (nah-883)

v0.7.0

19 Apr 11:01

Choose a tag to compare

Added

  • Local Claude Code plugin distribution prep — added a local-only plugin scaffold and scripts/build_claude_plugin.py artifact builder that copies the stdlib-only nah runtime into an ignored dist/claude-plugin/nah directory, generates Claude hook matchers from the canonical agent matcher list, and keeps hook commands rooted at ${CLAUDE_PLUGIN_ROOT} with no package-manager or network bootstrap. CLI install/update/uninstall/nah claude paths now detect enabled nah plugins, avoid silent mixed plugin/direct-hook setups, and preserve plugin-managed state during uninstall. (nah-879)
  • Claude Code plugin marketplace artifact generationscripts/build_claude_plugin.py can now generate and freshness-check a full self-hosted marketplace root at dist/claude-marketplace, with .claude-plugin/marketplace.json pointing to the generated self-contained plugins/nah artifact instead of the source template. Added plugin beta install docs covering opt-in plugin mode, legacy direct-hook migration, rollback, and the intentional absence of bundled PyYAML. (nah-880)
  • Automated Claude plugin marketplace releases — the existing tagged release workflow now builds, freshness-checks, release-verifies, and Claude-validates the generated marketplace tree before publishing PyPI, then publishes that exact marketplace artifact to the claude-marketplace branch with immutable claude-plugin-vX.Y.Z tags. Added release scripts and tests so plugin metadata, source tag, package version, changelog, and bundled runtime cannot silently drift. (nah-881)

v0.6.4

18 Apr 18:14

Choose a tag to compare

Fixed

  • Conservative kubectl read classification with global flag supportkubectl -n <ns> logs ..., kubectl --namespace=<ns> get pods, and other known low-risk Kubernetes inspection commands now classify as container_read instead of falling through to unknown. The classifier strips recognized kubectl global flags before matching subcommands, while malformed flags, mutations, exec/copy/port-forward paths, detailed object dumps (-o yaml/json), secrets, configmaps, service accounts, and custom resources remain on the unknown ask path. Tracks #67, superseding the broad prefix-table approach from #51 and the global-flag stripping branch #68.
  • Explicit-delimiter mise wrappers preserve payload classificationmise exec -- <cmd>, mise x -- <cmd>, and mise watch -- <cmd> now classify and resolve context from the command after --, so safe Git/GitHub CLI reads allow, script and inline-code inspection use the inner payload, and unknown tools launched through mise still ask. Redirected literal content is inspected through the wrapper while preserving the outer redirect target guard. (nah-878)
  • GitHub CLI API reads no longer look like script executiongh api ... now uses a full-profile flag classifier instead of the generic lang_exec table entry, so read-only API calls such as gh api repos/owner/repo/contributors --jq length classify as git_safe and no longer ask with script not found: .../api. POST-like methods, request bodies, implicit POST field flags, typed --field key=@file payloads, and --input stay on the existing network_write ask path, while gh extension exec remains lang_exec. (nah-32c)
  • Direct script arguments no longer resolve as script pathsnah now treats tokens[0] as the inspected script for direct script invocations such as ./bin/release.sh 2.0.0 prerelease --label rc, instead of scanning positional arguments and asking on script not found: <project>/2.0.0. Missing direct scripts still fail closed, but the prompt now names the missing script rather than the first argument. Reported in #70; PR behavior integrated from #72 by @srgvg. (nah-877)
  • Windows hook shim and update compatibility — the generated nah_guard.py shim now includes an explicit UTF-8 source cookie and treats old non-UTF-8 hook files as stale during update, rewriting them safely instead of crashing while checking for identical content. nah update now handles both current string-style Claude hook matchers and legacy object-style {"tool_name": [...]} matchers, preserves object-style entries when present, and creates a missing hooks.PreToolUse list before adding new tool matchers. Reported in #58 by @zacbrown.

v0.6.3

17 Apr 08:31

Choose a tag to compare

Added

  • Wildcard support in classify entries — 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 at nah classify write 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_config in src/nah/remember.py now writes to a sibling temp file and os.replaces it over the target. Previously it called open(path, "w") which truncates the file to zero bytes before writing; concurrent Claude Code sessions calling _read_config during 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 to 0o644), 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_config call 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 $VAR expansion before sensitive-path checks — Bash classification now propagates literal env assignments across && / || / ; stages and expands $NAME / ${NAME} in later consumer tokens, so BAD=/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 and export NAME=value assignment forms. Bypass identified by srgvg (#74, nah-874)

v0.6.2

14 Apr 09:11

Choose a tag to compare

Added

  • Default-config dry runsnah test --defaults now ignores user/project config and uses packaged defaults for one dry-run classification, keeping /nah-demo base battery results stable under customized local configs while preserving --config for explicit variants (nah-jpv)

Fixed

  • find -exec shell-wrapper classification — Bash classification now unwraps find -exec / -execdir / -ok / -okdir payloads through the same inner-command pipeline as direct sh -c and bash -lc, so hidden network access and curl | sh composition no longer collapse to project-local filesystem paths while safe grep and project-local cleanup still allow (#52, nah-871)
  • Shell comment prefix bypass — Bash command classification now treats top-level newlines as command separators and strips shell comments before per-stage tokenization, so comment-prefixed commands such as # note\ncat /etc/shadow no longer collapse to ALLOW / empty command while quoted hashes and heredoc content remain intact (#71, nah-870)