#!/bin/bash # GLM context 보정 패치 v2 (Linux) — 옵션 3: 모델별 런타임 env 주입 # # 설치 파일: # ~/.claude/statusline-glm-fix.js stdin 보정 wrapper (표시 fallback) # ~/.claude/launch-claude.sh 모델별 context 를 ollama에서 읽어 # CLAUDE_CODE_MAX_CONTEXT_TOKENS 로 주입하고 exec # settings.json: # statusLine -> statusline-glm-fix.js # env.CLAUDE_CODE_MAX_CONTEXT_TOKENS 제거 (launch-claude.sh 가 모델별로 주입) # # 사용: bash install-linux.sh # 이후 claude 실행: bash ~/.claude/launch-claude.sh (ollama launch claude 대신) # 되돌리기: settings.json statusLine.command 를 원래 dist/index.js 경로로. set -euo pipefail HC="$HOME/.claude" WRAPPER="$HC/statusline-glm-fix.js" LAUNCH="$HC/launch-claude.sh" SETTINGS="$HC/settings.json" PATCHER="$(mktemp -t glm-patch.XXXXXX.js)" trap 'rm -f "$PATCHER"' EXIT mkdir -p "$HC" # --- 1) statusline wrapper --- cat > "$WRAPPER" <<'WRAPPER_EOF' // Thin statusline wrapper: Claude Code reports context_window_size=200000 for // non-Anthropic models like glm-5.2:cloud, but GLM's real context window is 1M. // Patch the stdin before forwarding to the real claude-dashboard, so it shows // e.g. "0/1M" instead of "0/200K". Leaves the plugin cache untouched. // (launch-claude.sh 가 모델별 env를 주입하면 이 wrapper는 대부분 no-op 이지만, // env 없이 그냥 실행한 경우의 표시 fallback 역할.) // To revert: set settings.json statusLine.command back to the dist/index.js path. const { spawn } = require('child_process'); const fs = require('fs'); const os = require('os'); const path = require('path'); function resolveDashboard() { const base = path.join(os.homedir(), '.claude/plugins/cache/claude-dashboard/claude-dashboard'); let dirs; try { dirs = fs.readdirSync(base); } catch { dirs = []; } const ver = dirs.filter((d) => /^\d+\.\d+\.\d+$/.test(d)).sort((a, b) => { const pa = a.split('.').map(Number), pb = b.split('.').map(Number); for (let i = 0; i < 3; i++) if (pa[i] !== pb[i]) return pa[i] - pb[i]; return 0; }).pop(); return ver ? path.join(base, ver, 'dist/index.js') : path.join(base, '1.30.0/dist/index.js'); } const DASHBOARD = resolveDashboard(); let raw = ''; process.stdin.setEncoding('utf8'); process.stdin.on('data', (c) => { raw += c; }); process.stdin.on('end', () => { let payload = raw; try { const json = JSON.parse(raw); const cw = json?.context_window; const size = cw?.context_window_size; const modelId = String(json?.model?.id || '').toLowerCase(); if (modelId.includes('glm') && size === 200000) { cw.context_window_size = 1000000; const usage = cw.current_usage; if (usage) { const input = (usage.input_tokens || 0) + (usage.cache_creation_input_tokens || 0) + (usage.cache_read_input_tokens || 0); cw.used_percentage = Math.round((input / 1000000) * 100); if (typeof cw.remaining_percentage === 'number') cw.remaining_percentage = 100 - cw.used_percentage; } payload = JSON.stringify(json); } } catch { /* pass through */ } const child = spawn('node', [DASHBOARD], { stdio: ['pipe', 'inherit', 'inherit'] }); child.stdin.write(payload); child.stdin.end(); }); WRAPPER_EOF # --- 2) launch-claude.sh --- cat > "$LAUNCH" <<'LAUNCH_EOF' #!/bin/bash # claude를 모델별 실제 context window에 맞춰 실행. # config.json 의 claude 모델 -> ollama show 로 context length -> CLAUDE_CODE_MAX_CONTEXT_TOKENS 주입 -> exec. # compact 트리거 = min(AUTO_COMPACT_WINDOW, 모델 context) 이라 MAX_CONTEXT_TOKENS 만 주면 됨. set -euo pipefail CFG="$HOME/.ollama/config.json" [ ! -f "$CFG" ] && { echo "⚠️ $CFG 없음 — env 없이 실행" >&2; exec ollama launch claude; } MODEL=$(node -e 'const fs=require("fs"),os=require("os");const p=os.homedir()+"/.ollama/config.json";const d=JSON.parse(fs.readFileSync(p,"utf8"));const m=(d.integrations&&d.integrations.claude&&d.integrations.claude.models)||[];process.stdout.write(m[0]||"");' 2>/dev/null || true) if [ -z "$MODEL" ]; then echo "⚠️ claude 모델 못 읽음 — env 없이 실행" >&2; exec ollama launch claude; fi CTX=$(ollama show "$MODEL" 2>/dev/null | awk '/context length/{print $3; exit}' || true) if ! [[ "$CTX" =~ ^[0-9]+$ ]] || [ "$CTX" -eq 0 ]; then echo "⚠️ '$MODEL' context length 못 읽음 — env 없이 실행" >&2; exec ollama launch claude; fi echo "▶ model=$MODEL context=$CTX → CLAUDE_CODE_MAX_CONTEXT_TOKENS=$CTX" >&2 export CLAUDE_CODE_MAX_CONTEXT_TOKENS="$CTX" exec ollama launch claude LAUNCH_EOF chmod +x "$LAUNCH" # --- 3) settings.json 패치: statusLine 교체 + 정적 env 제거 --- cat > "$PATCHER" <<'PATCHER_EOF' const fs = require('fs'); const settingsPath = process.argv[2]; const wrapperPath = process.argv[3]; const s = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); const beforeSL = s.statusLine && s.statusLine.command; const beforeEnv = s.env && s.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS; s.statusLine = { type: 'command', command: 'node ' + wrapperPath }; // launch-claude.sh 가 모델별로 env를 주입하므로 정적 env는 제거 (충돌 방지). if (s.env) { delete s.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS; if (Object.keys(s.env).length === 0) delete s.env; } fs.writeFileSync(settingsPath, JSON.stringify(s, null, 2) + '\n'); console.log('statusLine before: ' + beforeSL); console.log('statusLine after : ' + s.statusLine.command); console.log('env removed (was): ' + (beforeEnv || '(none)')); console.log('env now : ' + JSON.stringify(s.env || {})); PATCHER_EOF node "$PATCHER" "$SETTINGS" "$WRAPPER" echo "" echo "✅ 설치 완료." echo " 이후 claude 실행은 ollama launch claude 대신" echo " bash ~/.claude/launch-claude.sh 로 하세요 (모델별 context 자동 주입)."