# GLM context 보정 패치 v2 (Windows) — 옵션 3: 모델별 런타임 env 주입 # # 설치 파일: # %USERPROFILE%\.claude\statusline-glm-fix.js stdin 보정 wrapper (표시 fallback) # %USERPROFILE%\.claude\launch-claude.ps1 모델별 context 를 ollama에서 읽어 # CLAUDE_CODE_MAX_CONTEXT_TOKENS 로 주입하고 exec # settings.json: # statusLine -> statusline-glm-fix.js # env.CLAUDE_CODE_MAX_CONTEXT_TOKENS 제거 (launch-claude.ps1 가 모델별로 주입) # # 사용: powershell -ExecutionPolicy Bypass -File .\install-windows.ps1 # 이후 claude 실행: powershell -ExecutionPolicy Bypass -File ~/.claude/launch-claude.ps1 # (ollama launch claude 대신. 단 Windows 에서 ollama launch 통합이 있을 때.) # 되돌리기: settings.json statusLine.command 를 원래 dist/index.js 경로로. $ErrorActionPreference = 'Stop' $HC = Join-Path $env:USERPROFILE '.claude' $Wrapper = Join-Path $HC 'statusline-glm-fix.js' $Launch = Join-Path $HC 'launch-claude.ps1' $Settings = Join-Path $HC 'settings.json' $Patcher = Join-Path $env:TEMP "glm-patch-$([System.Guid]::NewGuid()).js" New-Item -ItemType Directory -Force -Path $HC | Out-Null # --- 1) statusline wrapper --- $wrapperJs = @' 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(); }); '@ Set-Content -Path $Wrapper -Value $wrapperJs -Encoding UTF8 # --- 2) launch-claude.ps1 --- $launchPs = @' $ErrorActionPreference = 'Stop' $Cfg = Join-Path $env:USERPROFILE '.ollama\config.json' if (-not (Test-Path $Cfg)) { Write-Host "⚠️ $Cfg 없음 — env 없이 실행" -ForegroundColor Yellow; & ollama launch claude; return } $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]||'');" if (-not $Model) { Write-Host "⚠️ claude 모델 못 읽음 — env 없이 실행" -ForegroundColor Yellow; & ollama launch claude; return } $line = ollama show $Model 2>$null | Select-String -Pattern 'context length' $Ctx = ($line -split '\s+')[-1] if (-not ($Ctx -match '^\d+$') -or $Ctx -eq '0') { Write-Host "⚠️ '$Model' context length 못 읽음 — env 없이 실행" -ForegroundColor Yellow; & ollama launch claude; return } Write-Host "▶ model=$Model context=$Ctx → CLAUDE_CODE_MAX_CONTEXT_TOKENS=$Ctx" -ForegroundColor Cyan $env:CLAUDE_CODE_MAX_CONTEXT_TOKENS = $Ctx & ollama launch claude '@ Set-Content -Path $Launch -Value $launchPs -Encoding UTF8 # --- 3) settings.json 패치: statusLine 교체 + 정적 env 제거 --- $patcherJs = @' 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 }; 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 || {})); '@ Set-Content -Path $Patcher -Value $patcherJs -Encoding UTF8 & node $Patcher $Settings $Wrapper Remove-Item -Path $Patcher -Force -ErrorAction SilentlyContinue Write-Host "" Write-Host "✅ 설치 완료." Write-Host " 이후 claude 실행은 ollama launch claude 대신" Write-Host " powershell -ExecutionPolicy Bypass -File ~/.claude/launch-claude.ps1"