Last active
January 8, 2026 17:06
-
-
Save pybe/1b7f7d97097a20b0101a22e9cefbe5b7 to your computer and use it in GitHub Desktop.
Patch Claude Code cli.js to enable hooks on Termux/Android (PARTIAL FIX - PreToolUse known issue)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Claude Code Termux/Android Patch Script | |
| # Fixes hooks not firing on Android by patching platform detection and temp paths | |
| # | |
| # Usage: | |
| # patch-termux-hooks # Apply all patches (recommended) | |
| # patch-termux-hooks --platform-only # Only platform detection patch | |
| # patch-termux-hooks --tmp-only # Only /tmp path patch | |
| # patch-termux-hooks --rollback # Restore from backup | |
| # patch-termux-hooks --help # Show this help | |
| # | |
| # Repository: https://github.com/anthropics/claude-code/issues/16615 | |
| # Gist: https://gist.github.com/1b7f7d97097a20b0101a22e9cefbe5b7 | |
| # | |
| # ⚠️ KNOWN LIMITATION (Session 12 Deep Investigation - 2026-01-08): | |
| # | |
| # PreToolUse and PostToolUse hooks DO NOT FIRE during live tool execution, | |
| # even with all patches applied. This is NOT a configuration issue - it's | |
| # a deeper code path problem that requires an upstream fix. | |
| # | |
| # What works: | |
| # ✅ SessionStart hooks | |
| # ✅ Stop hooks | |
| # ✅ SubagentStop hooks | |
| # | |
| # What doesn't work: | |
| # ❌ PreToolUse hooks (never invoked during tool calls) | |
| # ❌ PostToolUse hooks (never invoked during tool calls) | |
| # | |
| # Technical Investigation Findings: | |
| # | |
| # The code path for PreToolUse exists in cli.js: | |
| # ZZ7 (tool execution) | |
| # → XZ7 (pre-tool hook wrapper) | |
| # → XM0 (executePreToolHooks) | |
| # → Ot (main hook generator) | |
| # → OM0 (getMatchingHooks) | |
| # → Debug: "Getting matching hook commands for PreToolUse" | |
| # | |
| # Debug logs show "Getting matching hook commands for X" for working hooks | |
| # (SessionStart, Stop, SubagentStop), but ZERO entries for PreToolUse. | |
| # The hook execution system simply never reaches the PreToolUse code path. | |
| # | |
| # Potential causes under investigation: | |
| # 1. Feature flag "tengu_streaming_tool_execution2" may route Android differently | |
| # 2. Async generator (XZ7) may not be properly consumed on Android | |
| # 3. Silent exception in tool execution path | |
| # | |
| # See GitHub issue #16615 for upstream tracking. | |
| # | |
| # What this script DOES fix: | |
| # 1. Platform detection (android → recognized as valid platform) | |
| # 2. Temp directory paths (/tmp → $PREFIX/tmp) | |
| # 3. Enables SessionStart/Stop/SubagentStop hooks | |
| # | |
| # ------------------------------------------------------------------------------ | |
| set -e | |
| CLI_PATH="/data/data/com.termux/files/usr/lib/node_modules/@anthropic-ai/claude-code/cli.js" | |
| BACKUP_PATH="${CLI_PATH}.backup" | |
| TERMUX_TMP="/data/data/com.termux/files/usr/tmp" | |
| # Colors | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| NC='\033[0m' | |
| show_help() { | |
| head -35 "$0" | tail -33 | |
| exit 0 | |
| } | |
| check_prerequisites() { | |
| if [[ ! -f "$CLI_PATH" ]]; then | |
| echo -e "${RED}Error: Claude Code CLI not found at $CLI_PATH${NC}" | |
| echo "Make sure Claude Code is installed: npm install -g @anthropic-ai/claude-code" | |
| exit 1 | |
| fi | |
| if [[ ! -d "$TERMUX_TMP" ]]; then | |
| echo -e "${YELLOW}Creating Termux tmp directory...${NC}" | |
| mkdir -p "$TERMUX_TMP" | |
| fi | |
| } | |
| create_backup() { | |
| if [[ ! -f "$BACKUP_PATH" ]]; then | |
| echo -e "${BLUE}Creating backup at $BACKUP_PATH${NC}" | |
| cp "$CLI_PATH" "$BACKUP_PATH" | |
| else | |
| echo -e "${YELLOW}Backup already exists${NC}" | |
| fi | |
| } | |
| apply_platform_patch() { | |
| echo -e "${BLUE}Applying platform detection patch...${NC}" | |
| # Count existing android patches | |
| local before=$(grep -c '==="android"' "$CLI_PATH" 2>/dev/null || echo "0") | |
| if [[ "$before" -ge 10 ]]; then | |
| echo -e "${GREEN}Platform patch already applied ($before occurrences)${NC}" | |
| return 0 | |
| fi | |
| # Patch patterns - add android to platform checks | |
| # Pattern 1: process.platform==="darwin"||process.platform==="linux" | |
| sed -i 's/process\.platform==="darwin"||process\.platform==="linux"/process.platform==="darwin"||process.platform==="linux"||process.platform==="android"/g' "$CLI_PATH" | |
| # Pattern 2: "darwin"===process.platform||"linux"===process.platform | |
| sed -i 's/"darwin"===process\.platform||"linux"===process\.platform/"darwin"===process.platform||"linux"===process.platform||"android"===process.platform/g' "$CLI_PATH" | |
| # Pattern 3: Variations with spaces | |
| sed -i 's/process\.platform === "darwin" || process\.platform === "linux"/process.platform === "darwin" || process.platform === "linux" || process.platform === "android"/g' "$CLI_PATH" | |
| local after=$(grep -c '==="android"\|=== "android"' "$CLI_PATH" 2>/dev/null || echo "0") | |
| echo -e "${GREEN}Platform patch applied ($after occurrences)${NC}" | |
| } | |
| apply_tmp_patch() { | |
| echo -e "${BLUE}Applying /tmp path patch...${NC}" | |
| # Count existing hardcoded /tmp paths | |
| local before=$(grep -c '"/tmp' "$CLI_PATH" 2>/dev/null || echo "0") | |
| if [[ "$before" -le 1 ]]; then | |
| echo -e "${GREEN}/tmp patch already applied or not needed${NC}" | |
| return 0 | |
| fi | |
| # Replace /tmp/claude with Termux path | |
| sed -i "s|\"/tmp/claude|\"$TERMUX_TMP/claude|g" "$CLI_PATH" | |
| # Replace /tmp/workspace with Termux path | |
| sed -i "s|\"/tmp/workspace|\"$TERMUX_TMP/workspace|g" "$CLI_PATH" | |
| # Replace remaining /tmp references (be careful not to break legitimate paths) | |
| sed -i "s|TMPDIR=/tmp|TMPDIR=$TERMUX_TMP|g" "$CLI_PATH" | |
| local after=$(grep -c '"/tmp' "$CLI_PATH" 2>/dev/null || echo "0") | |
| echo -e "${GREEN}/tmp patch applied (remaining /tmp refs: $after)${NC}" | |
| } | |
| rollback() { | |
| if [[ -f "$BACKUP_PATH" ]]; then | |
| echo -e "${BLUE}Restoring from backup...${NC}" | |
| cp "$BACKUP_PATH" "$CLI_PATH" | |
| echo -e "${GREEN}Rollback complete${NC}" | |
| else | |
| echo -e "${RED}No backup found at $BACKUP_PATH${NC}" | |
| exit 1 | |
| fi | |
| } | |
| show_status() { | |
| echo -e "\n${BLUE}=== Patch Status ===${NC}" | |
| local platform_count=$(grep -c '==="android"\|=== "android"' "$CLI_PATH" 2>/dev/null || echo "0") | |
| local tmp_count=$(grep -c '"/tmp' "$CLI_PATH" 2>/dev/null || echo "0") | |
| if [[ "$platform_count" -ge 10 ]]; then | |
| echo -e "Platform patch: ${GREEN}✓ Applied ($platform_count occurrences)${NC}" | |
| else | |
| echo -e "Platform patch: ${RED}✗ Not applied${NC}" | |
| fi | |
| if [[ "$tmp_count" -le 1 ]]; then | |
| echo -e "/tmp patch: ${GREEN}✓ Applied${NC}" | |
| else | |
| echo -e "/tmp patch: ${RED}✗ $tmp_count hardcoded /tmp paths remain${NC}" | |
| fi | |
| if [[ -f "$BACKUP_PATH" ]]; then | |
| echo -e "Backup: ${GREEN}✓ Available${NC}" | |
| else | |
| echo -e "Backup: ${YELLOW}○ Not created${NC}" | |
| fi | |
| echo -e "\n${YELLOW}⚠️ Note: PreToolUse/PostToolUse hooks require upstream fix${NC}" | |
| echo -e "${YELLOW} See: https://github.com/anthropics/claude-code/issues/16615${NC}" | |
| } | |
| # Main | |
| case "${1:-}" in | |
| --help|-h) | |
| show_help | |
| ;; | |
| --rollback) | |
| rollback | |
| ;; | |
| --platform-only) | |
| check_prerequisites | |
| create_backup | |
| apply_platform_patch | |
| show_status | |
| ;; | |
| --tmp-only) | |
| check_prerequisites | |
| create_backup | |
| apply_tmp_patch | |
| show_status | |
| ;; | |
| *) | |
| check_prerequisites | |
| create_backup | |
| apply_platform_patch | |
| apply_tmp_patch | |
| show_status | |
| echo -e "\n${GREEN}All patches applied. Restart Claude Code to take effect.${NC}" | |
| ;; | |
| esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment