[Bug] FileChanged hook doesn't detect file modifications from Bash tool execution

Resolved 💬 2 comments Opened Apr 7, 2026 by NorthIsUp Closed May 21, 2026

Bug Description

we use mise to manage our dev environment. i'm trying to make hooks to run mise env -s bash before each bash command in case the environment has changed. I'm trying to use the FileChanged hook to do this.

here is mise-update-env.sh which runs for SessionStart and FileChanged

# set common variables

read \
    CLAUDE_CODE_SESSION_ID \
    CLAUDE_CODE_TRANSCRIPT_PATH \
    CLAUDE_CODE_CWD \
    CLAUDE_CODE_HOOK_EVENT_NAME \
        < <(jq -r '[.session_id, .transcript_path, .cwd, .hook_event_name] | @tsv' <&0)

export CLAUDE_CODE_SESSION_ID \
    CLAUDE_CODE_TRANSCRIPT_PATH \
    CLAUDE_CODE_CWD \
    CLAUDE_CODE_HOOK_EVENT_NAME


function export_mise_env() {
    if ! command -v mise >/dev/null 2>&1; then
        echo "mise is not installed, skipping mise environment setup." >&2
        return
    fi

    [[ -z "$CLAUDE_ENV_FILE" ]] && { echo "CLAUDE_ENV_FILE is not set, skipping mise environment setup." >&2; return; }
    [[ -f "$CLAUDE_ENV_FILE" ]] || touch "$CLAUDE_ENV_FILE"

    local mise_env_start="export __MISE_START__=''"
    local mise_env_end="export __MISE_END__=''"

    # Collapse multi-line values (e.g., RSA keys) to single lines.
    # Claude Code parses env files line-by-line; multi-line values break it.
    local exports
    exports=$(mise env -s bash 2>/dev/null | awk '
        /^export / { if (buf) print buf; buf=$0; next }
        { gsub(/\n/,"\\n"); buf=buf"\\n"$0 }
        END { if (buf) print buf }
    ') || {
        echo "mise env failed, skipping" >&2
        return
    }

    mise_iteration=${MISE_ITER:-0}
    sed -i '' "/$mise_env_start/,/$mise_env_end/d" "$CLAUDE_ENV_FILE"
    echo "$mise_env_start" >> "$CLAUDE_ENV_FILE"

    echo "export __CLAUDE_ENV_FILE='$CLAUDE_ENV_FILE'">> "$CLAUDE_ENV_FILE"
    echo "export __CLAUDE_PROJECT_DIR='$CLAUDE_PROJECT_DIR'" >> "$CLAUDE_ENV_FILE"
    echo "export __MISE_ITER='$((mise_iteration + 1))'" >> "$CLAUDE_ENV_FILE"

    echo "$exports" >> "$CLAUDE_ENV_FILE"
    
    echo "$mise_env_end" >> "$CLAUDE_ENV_FILE"

    #debugging
    echo "$exports\n\n----------------------" >> /tmp/mise_exports.log
}

hooks section of .claude/settings.json:

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/update-mise-env.sh",
            "statusMessage": "Activating mise environment...",
            "timeout": 5,
            "type": "command"
          }
        ]
      }
    ],
    "FileChanged": [
      {
        "hooks": [
          {
            "command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/update-mise-env.sh",
            "statusMessage": "Refreshing mise environment...",
            "timeout": 5,
            "type": "command"
          }
        ],
        "matcher": "mise.toml|mise.dev.toml"
      }
    ]
  },
}

claude:
Actually - mise rm argc ran via the Bash tool, not the Edit tool. The FileChanged hook watches for file changes made by Claude Code's file-writing tools (Edit/Write), not filesystem changes
from arbitrary Bash commands. That's why the FileChanged hook never fires when mise use/mise rm modify mise.toml through Bash.

Environment Info

  • Platform: darwin
  • Terminal: vscode
  • Version: 2.1.94
  • Feedback ID: b86ac5cb-dc47-4770-8f50-fcbd8d96a0d9

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗