KManager / scripts /kiro_fix_lags.py
StarrySkyWorld's picture
Initial commit
494c89b
#!/usr/bin/env python3
"""
Kiro Lag Fixer - автоматическая диагностика и исправление лагов.
Проверяет:
1. Hardware acceleration (SwiftShader = медленно)
2. Тяжёлые расширения
3. Утечки памяти в webview
4. Файловые watcher'ы (node_modules и т.д.)
5. Telemetry и другие фоновые процессы
Usage:
python kiro_fix_lags.py --diagnose # Только диагностика
python kiro_fix_lags.py --fix # Применить исправления
"""
import argparse
import json
import os
import subprocess
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
try:
import psutil
except ImportError:
print("❌ Установи psutil: pip install psutil")
sys.exit(1)
@dataclass
class DiagnosticResult:
"""Результат диагностики."""
issue: str
severity: str # "critical", "warning", "info"
description: str
fix: Optional[str] = None
auto_fixable: bool = False
class KiroLagDiagnostic:
"""Диагностика и исправление лагов Kiro."""
def __init__(self):
self.kiro_data = Path.home() / "AppData" / "Roaming" / "Kiro"
self.settings_file = self.kiro_data / "User" / "settings.json"
self.argv_file = self.kiro_data / "argv.json"
self.results: list[DiagnosticResult] = []
def get_kiro_processes(self) -> list[psutil.Process]:
"""Получает все процессы Kiro."""
processes = []
for proc in psutil.process_iter(['name', 'cmdline', 'memory_info', 'cpu_times']):
try:
if proc.info['name'] and 'kiro' in proc.info['name'].lower():
processes.append(proc)
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
return processes
def check_memory_usage(self):
"""Проверка потребления памяти."""
processes = self.get_kiro_processes()
total_mem_gb = sum(p.memory_info().rss for p in processes) / (1024**3)
if total_mem_gb > 8:
self.results.append(DiagnosticResult(
issue="Критическое потребление памяти",
severity="critical",
description=f"Kiro использует {total_mem_gb:.1f} GB RAM. Это ненормально!",
fix="Перезапусти Kiro или закрой лишние окна/расширения",
auto_fixable=False
))
elif total_mem_gb > 4:
self.results.append(DiagnosticResult(
issue="Высокое потребление памяти",
severity="warning",
description=f"Kiro использует {total_mem_gb:.1f} GB RAM",
fix="Рекомендуется перезапустить Extension Host (Ctrl+Shift+P → Restart Extension Host)",
auto_fixable=False
))
def check_gpu_acceleration(self):
"""Проверка hardware acceleration."""
processes = self.get_kiro_processes()
for proc in processes:
try:
cmdline = " ".join(proc.cmdline()).lower()
if "--type=gpu-process" in cmdline and "swiftshader" in cmdline:
self.results.append(DiagnosticResult(
issue="Software rendering (SwiftShader)",
severity="critical",
description="GPU acceleration отключён! Kiro использует медленный software rendering",
fix="Включить hardware acceleration в argv.json",
auto_fixable=True
))
return
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
def check_settings(self):
"""Проверка settings.json на проблемные настройки."""
if not self.settings_file.exists():
return
try:
with open(self.settings_file, 'r', encoding='utf-8') as f:
settings = json.load(f)
# Hardware acceleration
if settings.get("disable-hardware-acceleration") is True:
self.results.append(DiagnosticResult(
issue="Hardware acceleration отключён",
severity="critical",
description="В настройках отключено аппаратное ускорение",
fix='Удалить "disable-hardware-acceleration": true из settings.json',
auto_fixable=True
))
# Telemetry
if settings.get("telemetry.telemetryLevel") != "off":
self.results.append(DiagnosticResult(
issue="Telemetry включена",
severity="info",
description="Телеметрия может замедлять работу",
fix='Установить "telemetry.telemetryLevel": "off"',
auto_fixable=True
))
# File watchers
watcher_exclude = settings.get("files.watcherExclude", {})
if "**/node_modules/**" not in watcher_exclude:
self.results.append(DiagnosticResult(
issue="node_modules не исключены из watcher",
severity="warning",
description="Kiro следит за изменениями в node_modules (медленно!)",
fix='Добавить "**/node_modules/**": true в files.watcherExclude',
auto_fixable=True
))
except Exception as e:
print(f"⚠️ Не удалось прочитать settings.json: {e}")
def check_argv(self):
"""Проверка argv.json."""
if not self.argv_file.exists():
return
try:
with open(self.argv_file, 'r', encoding='utf-8') as f:
argv = json.load(f)
# Проверка на disable-gpu
if argv.get("disable-gpu") is True:
self.results.append(DiagnosticResult(
issue="GPU полностью отключён",
severity="critical",
description="В argv.json установлен disable-gpu",
fix='Удалить "disable-gpu": true из argv.json',
auto_fixable=True
))
except Exception as e:
print(f"⚠️ Не удалось прочитать argv.json: {e}")
def check_extension_host(self):
"""Проверка Extension Host на утечки памяти."""
processes = self.get_kiro_processes()
for proc in processes:
try:
cmdline = " ".join(proc.cmdline()).lower()
if "node.mojom.nodeservice" in cmdline:
mem_mb = proc.memory_info().rss / (1024**2)
if mem_mb > 1500:
self.results.append(DiagnosticResult(
issue="Extension Host утечка памяти",
severity="critical",
description=f"Extension Host использует {mem_mb:.0f} MB (PID {proc.pid})",
fix="Проверь расширения: Ctrl+Shift+P → Developer: Show Running Extensions",
auto_fixable=False
))
elif mem_mb > 800:
self.results.append(DiagnosticResult(
issue="Extension Host тяжёлый",
severity="warning",
description=f"Extension Host использует {mem_mb:.0f} MB",
fix="Отключи ненужные расширения",
auto_fixable=False
))
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
def apply_fixes(self):
"""Применяет автоматические исправления."""
fixed = []
# Исправление settings.json
if self.settings_file.exists():
try:
with open(self.settings_file, 'r', encoding='utf-8') as f:
settings = json.load(f)
modified = False
# Удалить disable-hardware-acceleration
if settings.get("disable-hardware-acceleration") is True:
del settings["disable-hardware-acceleration"]
modified = True
fixed.append("✅ Включён hardware acceleration")
# Отключить telemetry
if settings.get("telemetry.telemetryLevel") != "off":
settings["telemetry.telemetryLevel"] = "off"
modified = True
fixed.append("✅ Отключена telemetry")
# Исключить node_modules из watcher
if "files.watcherExclude" not in settings:
settings["files.watcherExclude"] = {}
watcher_exclude = settings["files.watcherExclude"]
if "**/node_modules/**" not in watcher_exclude:
watcher_exclude["**/node_modules/**"] = True
watcher_exclude["**/.git/objects/**"] = True
watcher_exclude["**/.git/subtree-cache/**"] = True
watcher_exclude["**/node_modules/*/**"] = True
modified = True
fixed.append("✅ Исключены node_modules из file watcher")
if modified:
# Бэкап
backup = self.settings_file.with_suffix('.json.backup')
with open(backup, 'w', encoding='utf-8') as f:
json.dump(settings, f, indent=2)
# Сохранить
with open(self.settings_file, 'w', encoding='utf-8') as f:
json.dump(settings, f, indent=2)
print(f"📝 Бэкап сохранён: {backup}")
except Exception as e:
print(f"❌ Ошибка при исправлении settings.json: {e}")
# Исправление argv.json
if self.argv_file.exists():
try:
with open(self.argv_file, 'r', encoding='utf-8') as f:
argv = json.load(f)
if argv.get("disable-gpu") is True:
del argv["disable-gpu"]
with open(self.argv_file, 'w', encoding='utf-8') as f:
json.dump(argv, f, indent=2)
fixed.append("✅ Включён GPU в argv.json")
except Exception as e:
print(f"❌ Ошибка при исправлении argv.json: {e}")
return fixed
def run_diagnosis(self):
"""Запускает полную диагностику."""
print("🔍 Диагностика лагов Kiro...\n")
self.check_memory_usage()
self.check_gpu_acceleration()
self.check_settings()
self.check_argv()
self.check_extension_host()
return self.results
def print_results(self):
"""Выводит результаты диагностики."""
if not self.results:
print("✅ Проблем не обнаружено!")
return
critical = [r for r in self.results if r.severity == "critical"]
warnings = [r for r in self.results if r.severity == "warning"]
info = [r for r in self.results if r.severity == "info"]
if critical:
print("🔴 КРИТИЧЕСКИЕ ПРОБЛЕМЫ:")
for r in critical:
print(f"\n • {r.issue}")
print(f" {r.description}")
if r.fix:
print(f" 💡 {r.fix}")
if r.auto_fixable:
print(f" ⚡ Можно исправить автоматически")
if warnings:
print("\n🟡 ПРЕДУПРЕЖДЕНИЯ:")
for r in warnings:
print(f"\n • {r.issue}")
print(f" {r.description}")
if r.fix:
print(f" 💡 {r.fix}")
if info:
print("\n🔵 ИНФОРМАЦИЯ:")
for r in info:
print(f"\n • {r.issue}")
print(f" {r.description}")
if r.fix:
print(f" 💡 {r.fix}")
def main():
parser = argparse.ArgumentParser(description="Kiro Lag Diagnostic & Fixer")
parser.add_argument('--diagnose', action='store_true', help='Только диагностика')
parser.add_argument('--fix', action='store_true', help='Применить исправления')
args = parser.parse_args()
diagnostic = KiroLagDiagnostic()
diagnostic.run_diagnosis()
diagnostic.print_results()
if args.fix:
print("\n" + "="*60)
print("🔧 Применение исправлений...")
print("="*60 + "\n")
fixed = diagnostic.apply_fixes()
if fixed:
for fix in fixed:
print(fix)
print("\n⚠️ ВАЖНО: Перезапусти Kiro чтобы изменения вступили в силу!")
else:
print("Нет автоматических исправлений для применения")
elif not args.diagnose:
# По умолчанию только диагностика
auto_fixable = [r for r in diagnostic.results if r.auto_fixable]
if auto_fixable:
print("\n" + "="*60)
print(f"💡 Найдено {len(auto_fixable)} проблем с автоматическим исправлением")
print(" Запусти с --fix чтобы применить исправления")
print("="*60)
if __name__ == "__main__":
main()