📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-06-21 09:15:23 +00:00
parent 0e1bb1aef3
commit 3d137606c0
805 changed files with 93512 additions and 4532 deletions
@@ -0,0 +1,183 @@
---
name: claude-monitor
description: Monitor de performance do Claude Code e sistema local. Diagnostica lentidao, mede CPU/RAM/disco, verifica API latency e gera relatorios de saude do sistema.
risk: safe
source: community
date_added: '2026-03-06'
author: renat
tags:
- monitoring
- performance
- diagnostics
- system-health
tools:
- claude-code
- antigravity
- cursor
- gemini-cli
- codex-cli
---
# Claude Monitor — Diagnóstico de Performance
## Overview
Monitor de performance do Claude Code e sistema local. Diagnostica lentidao, mede CPU/RAM/disco, verifica API latency e gera relatorios de saude do sistema.
## When to Use This Skill
- When the user mentions "lento" or related topics
- When the user mentions "lentidao" or related topics
- When the user mentions "lag" or related topics
- When the user mentions "lagado" or related topics
- When the user mentions "travando" or related topics
- When the user mentions "claude lento" or related topics
## Do Not Use This Skill When
- The task is unrelated to claude monitor
- A simpler, more specific tool can handle the request
- The user needs general-purpose assistance without domain expertise
## How It Works
Skill para diagnosticar e resolver problemas de lentidão no Claude Code e no sistema.
Determina se o gargalo é local (PC) ou remoto (API Claude) e sugere ações corretivas.
## Quando Usar
- Usuário reclama que o Claude Code está lento ou travando
- Troca de sessões de conversa demora para carregar
- Respostas do Claude demoram muito
- PC parece lento enquanto usa o Claude Code
- Qualquer menção a performance, lag, lentidão
## 1. Diagnóstico Rápido (Health_Check.Py)
Rode SEMPRE como primeiro passo:
```bash
python C:\Users\renat\skills\claude-monitor\scripts\health_check.py
```
O script analisa em ~3 segundos:
- **CPU**: Uso atual e por core. >80% = gargalo provável
- **RAM**: Total, usada, disponível. >85% = pressão de memória
- **Browsers**: Processos e RAM por browser. >5GB total = excesso de abas
- **Claude Code**: Processos e RAM consumida
- **Disco**: Espaço livre. <10% = impacto em swap/performance
- **Rede**: Latência ao endpoint da API Claude
- **Diagnóstico**: Classificação automática do problema com sugestões
## 2. Interpretar O Resultado
O script retorna um JSON com `diagnosis` contendo:
- `bottleneck`: "cpu" | "ram" | "browsers" | "disk" | "network" | "claude_api" | "ok"
- `severity`: "critical" | "warning" | "ok"
- `suggestions`: Lista de ações recomendadas
- `summary`: Resumo em português para mostrar ao usuário
**Mostre o `summary` ao usuário** e ofereça executar as sugestões.
## 3. Ações Corretivas Automáticas
Baseado no diagnóstico, ofereça ao usuário:
#### Se CPU alta (>80%):
- Listar processos consumindo mais CPU
- Sugerir fechar processos pesados desnecessários
- Verificar se Windows Update está rodando em background
#### Se browsers pesados (>5GB RAM ou >40 processos):
```bash
python C:\Users\renat\skills\claude-monitor\scripts\health_check.py --browsers-detail
```
Mostra RAM por browser e sugere quais fechar. **Nunca fechar processos sem permissão explícita do usuário.**
#### Se disco cheio (>85%):
- Mostrar pastas maiores
- Sugerir limpeza de Temp, cache de browsers, lixeira
#### Se rede lenta (latência >500ms):
- Testar conexão com api.anthropic.com
- Sugerir verificar VPN, proxy, ou conexão WiFi
## 4. Monitor Contínuo (Opcional)
Se o usuário quiser monitoramento em background:
```bash
python C:\Users\renat\skills\claude-monitor\scripts\monitor.py --interval 30 --duration 300
```
Parâmetros:
- `--interval`: Segundos entre cada amostra (default: 30)
- `--duration`: Duração total em segundos (default: 300 = 5 min)
- `--output`: Caminho do arquivo de log (default: monitor_log.json)
- `--alert-cpu`: Threshold de CPU para alerta (default: 80)
- `--alert-ram`: Threshold de RAM % para alerta (default: 85)
O monitor salva snapshots periódicos e gera um relatório ao final com:
- Picos de CPU e RAM
- Tendência (melhorando/piorando/estável)
- Eventos de alerta detectados
- Recomendação final
## 5. Benchmark Da Api Claude (Opcional)
Para testar se a lentidão é da API:
```bash
python C:\Users\renat\skills\claude-monitor\scripts\api_bench.py
```
Mede o tempo de resposta do processo Claude Code local (não faz chamadas à API).
Compara com tempos típicos e indica se está dentro do esperado.
## Thresholds De Referência
| Métrica | OK | Warning | Critical |
|---------|-----|---------|----------|
| CPU % | <60% | 60-85% | >85% |
| RAM usada % | <70% | 70-85% | >85% |
| RAM browsers | <3 GB | 3-6 GB | >6 GB |
| Processos browser | <30 | 30-60 | >60 |
| Disco livre | >15% | 10-15% | <10% |
| Latência rede | <200ms | 200-500ms | >500ms |
## Dicas Para O Usuário
Quando apresentar o diagnóstico, inclua estas dicas contextuais:
- **Muitas abas = muito CPU/RAM**: Cada aba de browser é um processo separado.
50 abas = 50 processos competindo por recursos.
- **Claude Code é pesado**: Ele roda vários processos Electron. É normal consumir 3-5 GB.
Mas se estiver usando >6 GB com várias sessões, considere fechar sessões antigas.
- **Troca de sessão lenta**: Geralmente causada por CPU alta ou muitos processos competindo.
A sessão precisa carregar o histórico da conversa, e se o CPU está ocupado, demora.
- **Disco quase cheio**: Afeta a velocidade do swap (memória virtual) e pode causar
lentidão generalizada.
## Dependências
- Python 3.10+
- psutil (instalado automaticamente pelo script se não disponível)
- Nenhuma API key necessária
## Best Practices
- Provide clear, specific context about your project and requirements
- Review all suggestions before applying them to production code
- Combine with other complementary skills for comprehensive analysis
## Common Pitfalls
- Using this skill for tasks outside its domain expertise
- Applying recommendations without understanding your specific context
- Not providing enough project context for accurate analysis
## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
@@ -0,0 +1,251 @@
#!/usr/bin/env python3
"""
Claude Monitor — Benchmark de Conectividade API
Testa latência e conectividade com a API do Claude.
Não faz chamadas à API (não precisa de API key).
Apenas verifica se a rede está funcionando e se o endpoint responde.
Uso:
python api_bench.py # 5 testes de latência
python api_bench.py --samples 10 # 10 testes
python api_bench.py --json # Output JSON
"""
import json
import socket
import ssl
import subprocess
import sys
import time
from datetime import datetime
try:
import psutil
except ImportError:
subprocess.check_call([sys.executable, "-m", "pip", "install", "psutil", "--quiet"])
import psutil
ENDPOINTS = [
{"name": "Claude API", "host": "api.anthropic.com", "port": 443},
{"name": "Anthropic CDN", "host": "cdn.anthropic.com", "port": 443},
{"name": "Google DNS", "host": "8.8.8.8", "port": 53},
]
def create_tls_context():
"""Cria contexto TLS restringindo conexoes a TLS 1.2+."""
context = ssl.create_default_context()
if hasattr(ssl, "TLSVersion"):
context.minimum_version = ssl.TLSVersion.TLSv1_2
else:
context.options |= getattr(ssl, "OP_NO_TLSv1", 0)
context.options |= getattr(ssl, "OP_NO_TLSv1_1", 0)
return context
def test_tcp_latency(host, port, timeout=5):
"""Testa latência TCP para um host:port."""
try:
start = time.time()
sock = socket.create_connection((host, port), timeout=timeout)
latency = (time.time() - start) * 1000 # ms
sock.close()
return {"reachable": True, "latency_ms": round(latency, 1)}
except (socket.timeout, socket.error, OSError) as e:
return {"reachable": False, "latency_ms": None, "error": str(e)}
def test_tls_handshake(host, port=443, timeout=5):
"""Testa tempo do handshake TLS."""
try:
context = create_tls_context()
start = time.time()
with socket.create_connection((host, port), timeout=timeout) as sock:
with context.wrap_socket(sock, server_hostname=host) as ssock:
handshake_time = (time.time() - start) * 1000
return {
"success": True,
"handshake_ms": round(handshake_time, 1),
"tls_version": ssock.version(),
}
except Exception as e:
return {"success": False, "error": str(e)}
def test_dns(hostname):
"""Testa resolução DNS."""
try:
start = time.time()
ip = socket.gethostbyname(hostname)
dns_time = (time.time() - start) * 1000
return {"resolved": True, "ip": ip, "dns_ms": round(dns_time, 1)}
except socket.gaierror as e:
return {"resolved": False, "error": str(e)}
def check_network_interfaces():
"""Verifica interfaces de rede ativas."""
stats = psutil.net_if_stats()
active = []
for name, info in stats.items():
if info.isup and info.speed > 0:
active.append({
"name": name,
"speed_mbps": info.speed,
"mtu": info.mtu,
})
return active
def run_benchmark(samples=5):
"""Roda o benchmark completo."""
results = {
"timestamp": datetime.now().isoformat(),
"samples": samples,
"endpoints": [],
"dns": None,
"tls": None,
"network_interfaces": check_network_interfaces(),
}
# DNS
results["dns"] = test_dns("api.anthropic.com")
# TLS handshake
results["tls"] = test_tls_handshake("api.anthropic.com")
# Latência por endpoint
for ep in ENDPOINTS:
latencies = []
for _ in range(samples):
result = test_tcp_latency(ep["host"], ep["port"])
latencies.append(result)
time.sleep(0.2)
valid = [r["latency_ms"] for r in latencies if r["reachable"] and r["latency_ms"]]
ep_result = {
"name": ep["name"],
"host": ep["host"],
"port": ep["port"],
"tests": latencies,
}
if valid:
ep_result["avg_ms"] = round(sum(valid) / len(valid), 1)
ep_result["min_ms"] = round(min(valid), 1)
ep_result["max_ms"] = round(max(valid), 1)
ep_result["success_rate"] = round(len(valid) / samples * 100, 0)
else:
ep_result["avg_ms"] = None
ep_result["success_rate"] = 0
results["endpoints"].append(ep_result)
# Diagnóstico
api_ep = results["endpoints"][0]
if api_ep.get("avg_ms") is None:
results["diagnosis"] = {
"status": "critical",
"message": "API do Claude INACESSIVEL. Verifique sua conexao de internet.",
}
elif api_ep["avg_ms"] > 500:
results["diagnosis"] = {
"status": "warning",
"message": (
f"Latencia alta para API ({api_ep['avg_ms']}ms). "
f"Conexao lenta pode causar atrasos no Claude Code."
),
}
elif api_ep["avg_ms"] > 200:
results["diagnosis"] = {
"status": "ok",
"message": (
f"Latencia moderada ({api_ep['avg_ms']}ms). "
f"Dentro do aceitavel mas pode ser melhor."
),
}
else:
results["diagnosis"] = {
"status": "ok",
"message": (
f"Conexao excelente ({api_ep['avg_ms']}ms). "
f"A rede NAO e o gargalo."
),
}
return results
def format_results(results):
"""Formata resultados para exibição."""
lines = ["## Benchmark de Conectividade\n"]
# DNS
dns = results["dns"]
if dns.get("resolved"):
lines.append(f"- DNS: api.anthropic.com -> {dns['ip']} ({dns['dns_ms']}ms)")
else:
lines.append(f"- DNS: FALHOU ({dns.get('error', 'desconhecido')})")
# TLS
tls = results["tls"]
if tls.get("success"):
lines.append(f"- TLS: {tls['tls_version']} handshake em {tls['handshake_ms']}ms")
else:
lines.append(f"- TLS: FALHOU ({tls.get('error', 'desconhecido')})")
lines.append("")
# Endpoints
lines.append("### Latencia por Endpoint")
for ep in results["endpoints"]:
if ep.get("avg_ms"):
lines.append(
f"- **{ep['name']}**: {ep['avg_ms']}ms avg "
f"(min {ep['min_ms']}ms, max {ep['max_ms']}ms) "
f"[{ep['success_rate']:.0f}% sucesso]"
)
else:
lines.append(f"- **{ep['name']}**: INACESSIVEL")
# Interfaces
lines.append("\n### Interfaces de Rede")
for iface in results["network_interfaces"]:
speed = iface["speed_mbps"]
if speed >= 1000:
speed_str = f"{speed/1000:.0f} Gbps"
else:
speed_str = f"{speed} Mbps"
lines.append(f"- {iface['name']}: {speed_str}")
# Diagnóstico
lines.append(f"\n### Diagnostico")
diag = results["diagnosis"]
status_map = {"critical": "[!!!]", "warning": "[!]", "ok": "[OK]"}
lines.append(f"{status_map[diag['status']]} {diag['message']}")
return "\n".join(lines)
def main():
import argparse
parser = argparse.ArgumentParser(description="Claude Monitor - Benchmark de Conectividade")
parser.add_argument("--samples", type=int, default=5, help="Numero de testes por endpoint")
parser.add_argument("--json", action="store_true", help="Output JSON")
args = parser.parse_args()
print(f"Testando conectividade ({args.samples} amostras por endpoint)...\n")
results = run_benchmark(args.samples)
if args.json:
print(json.dumps(results, indent=2, ensure_ascii=False))
else:
print(format_results(results))
if __name__ == "__main__":
main()
@@ -0,0 +1,69 @@
"""
Configurações e thresholds para o Claude Monitor.
"""
# Thresholds de alerta
THRESHOLDS = {
"cpu": {
"ok": 60,
"warning": 85,
# acima de warning = critical
},
"ram_percent": {
"ok": 70,
"warning": 85,
},
"browsers_ram_gb": {
"ok": 3.0,
"warning": 6.0,
},
"browsers_processes": {
"ok": 30,
"warning": 60,
},
"disk_free_percent": {
"critical_below": 10,
"warning_below": 15,
},
"network_latency_ms": {
"ok": 200,
"warning": 500,
},
}
# Nomes de processos de browser conhecidos
BROWSER_NAMES = ["chrome", "msedge", "firefox", "brave", "opera", "vivaldi"]
# Nomes de processos do Claude Code
CLAUDE_NAMES = ["claude"]
# Endpoint para teste de latência
API_ENDPOINT = "api.anthropic.com"
# Monitor defaults
MONITOR_DEFAULTS = {
"interval": 30,
"duration": 300,
"alert_cpu": 80,
"alert_ram": 85,
}
def classify(value, metric_name):
"""Classifica um valor como 'ok', 'warning' ou 'critical'."""
t = THRESHOLDS.get(metric_name, {})
# Métricas onde "abaixo" é ruim (disco livre)
if "critical_below" in t:
if value < t["critical_below"]:
return "critical"
elif value < t["warning_below"]:
return "warning"
return "ok"
# Métricas onde "acima" é ruim (CPU, RAM, latência)
if value <= t.get("ok", 999999):
return "ok"
elif value <= t.get("warning", 999999):
return "warning"
return "critical"
@@ -0,0 +1,362 @@
#!/usr/bin/env python3
"""
Claude Monitor — Diagnóstico Rápido de Performance
Analisa CPU, RAM, browsers, disco e rede em ~3 segundos.
Identifica o gargalo principal e sugere ações corretivas.
Uso:
python health_check.py # Diagnóstico completo
python health_check.py --browsers-detail # Detalhe de browsers
python health_check.py --json # Output JSON puro
python health_check.py --quick # Só resumo (sem teste de rede)
"""
import json
import os
import socket
import subprocess
import sys
import time
from datetime import datetime
from pathlib import Path
# Garante que psutil está disponível
try:
import psutil
except ImportError:
print("Instalando psutil...")
subprocess.check_call([sys.executable, "-m", "pip", "install", "psutil", "--quiet"])
import psutil
# Importa config do mesmo diretório
sys.path.insert(0, str(Path(__file__).parent))
from config import (
BROWSER_NAMES, CLAUDE_NAMES, API_ENDPOINT,
THRESHOLDS, classify
)
def check_cpu():
"""Verifica uso de CPU."""
cpu_percent = psutil.cpu_percent(interval=1)
cpu_count = psutil.cpu_count()
per_cpu = psutil.cpu_percent(interval=0, percpu=True)
return {
"percent": cpu_percent,
"cores": cpu_count,
"per_core": per_cpu,
"status": classify(cpu_percent, "cpu"),
}
def check_ram():
"""Verifica uso de RAM."""
ram = psutil.virtual_memory()
swap = psutil.swap_memory()
return {
"total_gb": round(ram.total / 1024**3, 1),
"used_gb": round(ram.used / 1024**3, 1),
"available_gb": round(ram.available / 1024**3, 1),
"percent": ram.percent,
"swap_used_gb": round(swap.used / 1024**3, 1),
"swap_percent": swap.percent,
"status": classify(ram.percent, "ram_percent"),
}
def check_browsers(detail=False):
"""Verifica processos de browser e consumo de RAM."""
browsers = {}
all_procs = []
for proc in psutil.process_iter(["pid", "name", "memory_info"]):
try:
info = proc.info
name_lower = info["name"].lower()
ram_mb = info["memory_info"].rss / 1024**2
for bname in BROWSER_NAMES:
if bname in name_lower:
if bname not in browsers:
browsers[bname] = {"count": 0, "ram_mb": 0, "pids": []}
browsers[bname]["count"] += 1
browsers[bname]["ram_mb"] += ram_mb
if detail:
browsers[bname]["pids"].append({
"pid": info["pid"],
"ram_mb": round(ram_mb, 0)
})
break
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
total_ram_gb = sum(b["ram_mb"] for b in browsers.values()) / 1024
total_procs = sum(b["count"] for b in browsers.values())
# Formata para output
for bname in browsers:
browsers[bname]["ram_mb"] = round(browsers[bname]["ram_mb"], 0)
return {
"browsers": browsers,
"total_ram_gb": round(total_ram_gb, 1),
"total_processes": total_procs,
"ram_status": classify(total_ram_gb, "browsers_ram_gb"),
"process_status": classify(total_procs, "browsers_processes"),
}
def check_claude_processes():
"""Verifica processos do Claude Code."""
claude_procs = []
total_ram = 0
for proc in psutil.process_iter(["pid", "name", "memory_info", "cpu_percent"]):
try:
info = proc.info
name_lower = info["name"].lower()
for cname in CLAUDE_NAMES:
if cname in name_lower:
ram_mb = info["memory_info"].rss / 1024**2
claude_procs.append({
"pid": info["pid"],
"name": info["name"],
"ram_mb": round(ram_mb, 0),
})
total_ram += ram_mb
break
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
claude_procs.sort(key=lambda x: x["ram_mb"], reverse=True)
return {
"count": len(claude_procs),
"total_ram_gb": round(total_ram / 1024, 1),
"processes": claude_procs[:10], # Top 10
}
def check_disk():
"""Verifica espaço em disco."""
disk = psutil.disk_usage("C:/")
free_percent = 100 - disk.percent
return {
"total_gb": round(disk.total / 1024**3, 0),
"used_gb": round(disk.used / 1024**3, 0),
"free_gb": round(disk.free / 1024**3, 0),
"used_percent": disk.percent,
"free_percent": round(free_percent, 1),
"status": classify(free_percent, "disk_free_percent"),
}
def check_network():
"""Testa latência até a API do Claude."""
try:
start = time.time()
sock = socket.create_connection((API_ENDPOINT, 443), timeout=5)
latency_ms = round((time.time() - start) * 1000, 0)
sock.close()
return {
"latency_ms": latency_ms,
"endpoint": API_ENDPOINT,
"reachable": True,
"status": classify(latency_ms, "network_latency_ms"),
}
except (socket.timeout, socket.error, OSError) as e:
return {
"latency_ms": None,
"endpoint": API_ENDPOINT,
"reachable": False,
"status": "critical",
"error": str(e),
}
def check_top_processes(n=10):
"""Lista os N processos que mais consomem RAM."""
procs = []
for proc in psutil.process_iter(["pid", "name", "memory_info"]):
try:
info = proc.info
procs.append({
"name": info["name"],
"ram_mb": round(info["memory_info"].rss / 1024**2, 0),
"pid": info["pid"],
})
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
procs.sort(key=lambda x: x["ram_mb"], reverse=True)
return procs[:n]
def diagnose(results):
"""Analisa os resultados e gera diagnóstico."""
issues = []
suggestions = []
bottleneck = "ok"
severity = "ok"
cpu = results["cpu"]
ram = results["ram"]
browsers = results["browsers"]
disk = results["disk"]
network = results.get("network", {})
claude = results["claude"]
# CPU
if cpu["status"] == "critical":
issues.append(f"CPU a {cpu['percent']}% (CRITICO)")
suggestions.append("Fechar aplicativos pesados ou abas de browser desnecessarias")
suggestions.append("Verificar se Windows Update ou antivirus esta rodando em background")
bottleneck = "cpu"
severity = "critical"
elif cpu["status"] == "warning":
issues.append(f"CPU a {cpu['percent']}% (elevada)")
suggestions.append("Considerar fechar algumas abas de browser")
if severity != "critical":
bottleneck = "cpu"
severity = "warning"
# RAM
if ram["status"] == "critical":
issues.append(f"RAM a {ram['percent']}% ({ram['used_gb']} de {ram['total_gb']} GB)")
suggestions.append("Fechar browsers ou aplicativos para liberar memoria")
if severity != "critical":
bottleneck = "ram"
severity = "critical"
elif ram["status"] == "warning":
issues.append(f"RAM a {ram['percent']}% (monitorar)")
# Browsers
if browsers["ram_status"] == "critical":
issues.append(f"Browsers consumindo {browsers['total_ram_gb']} GB ({browsers['total_processes']} processos)")
suggestions.append("Fechar abas desnecessarias nos browsers")
browser_detail = []
for bname, info in browsers["browsers"].items():
browser_detail.append(f" - {bname}: {info['count']} processos, {info['ram_mb']:.0f} MB")
suggestions.append("Detalhamento:\n" + "\n".join(browser_detail))
if bottleneck == "ok":
bottleneck = "browsers"
if severity == "ok":
severity = "warning"
elif browsers["ram_status"] == "warning":
issues.append(f"Browsers usando {browsers['total_ram_gb']} GB (moderado)")
# Disco
if disk["status"] == "critical":
issues.append(f"Disco quase cheio: apenas {disk['free_gb']:.0f} GB livres ({disk['free_percent']}%)")
suggestions.append("Limpar arquivos temporarios, cache e lixeira")
suggestions.append("Verificar pasta Downloads e Temp por arquivos grandes")
if bottleneck == "ok":
bottleneck = "disk"
severity = "warning"
elif disk["status"] == "warning":
issues.append(f"Disco com {disk['free_gb']:.0f} GB livres ({disk['free_percent']}%)")
# Rede
if network.get("status") == "critical":
if not network.get("reachable"):
issues.append("API do Claude INACESSIVEL")
suggestions.append("Verificar conexao com internet")
suggestions.append("Verificar se VPN ou proxy esta bloqueando")
bottleneck = "network"
severity = "critical"
else:
issues.append(f"Latencia alta para API: {network['latency_ms']}ms")
suggestions.append("Verificar qualidade da conexao WiFi/cabo")
if bottleneck == "ok":
bottleneck = "network"
severity = "warning"
# Claude Code RAM
if claude["total_ram_gb"] > 8:
issues.append(f"Claude Code usando {claude['total_ram_gb']} GB ({claude['count']} processos)")
suggestions.append("Considerar fechar sessoes de conversa antigas no Claude Code")
# Tudo ok
if not issues:
issues.append("Sistema saudavel, sem gargalos detectados")
suggestions.append("A lentidao pode ser temporaria (pico na API do Claude)")
suggestions.append("Tente trocar de sessao novamente em alguns segundos")
# Gerar resumo em PT-BR
summary_lines = ["## Diagnostico de Performance\n"]
status_emoji = {"critical": "[!!!]", "warning": "[!]", "ok": "[OK]"}
summary_lines.append(f"**Status geral: {status_emoji[severity]} {severity.upper()}**\n")
if bottleneck != "ok":
summary_lines.append(f"**Gargalo principal: {bottleneck.upper()}**\n")
summary_lines.append("### Problemas detectados:")
for issue in issues:
summary_lines.append(f"- {issue}")
summary_lines.append("\n### Acoes recomendadas:")
for i, sug in enumerate(suggestions, 1):
if "\n" in sug:
summary_lines.append(f"{i}. {sug}")
else:
summary_lines.append(f"{i}. {sug}")
summary_lines.append(f"\n### Numeros-chave:")
summary_lines.append(f"- CPU: {cpu['percent']}% | RAM: {ram['percent']}% ({ram['used_gb']}/{ram['total_gb']} GB)")
summary_lines.append(f"- Browsers: {browsers['total_processes']} processos, {browsers['total_ram_gb']} GB")
summary_lines.append(f"- Claude Code: {claude['count']} processos, {claude['total_ram_gb']} GB")
summary_lines.append(f"- Disco C: {disk['free_gb']:.0f} GB livres ({disk['free_percent']}%)")
if network.get("latency_ms"):
summary_lines.append(f"- Latencia API: {network['latency_ms']}ms")
return {
"bottleneck": bottleneck,
"severity": severity,
"issues": issues,
"suggestions": suggestions,
"summary": "\n".join(summary_lines),
}
def main():
import argparse
parser = argparse.ArgumentParser(description="Claude Monitor - Diagnostico Rapido")
parser.add_argument("--browsers-detail", action="store_true", help="Mostra detalhes por browser")
parser.add_argument("--json", action="store_true", help="Output em JSON puro")
parser.add_argument("--quick", action="store_true", help="Pula teste de rede")
args = parser.parse_args()
results = {}
# Coleta dados
results["timestamp"] = datetime.now().isoformat()
results["cpu"] = check_cpu()
results["ram"] = check_ram()
results["browsers"] = check_browsers(detail=args.browsers_detail)
results["claude"] = check_claude_processes()
results["disk"] = check_disk()
results["top_processes"] = check_top_processes(15)
if not args.quick:
results["network"] = check_network()
# Diagnóstico
results["diagnosis"] = diagnose(results)
if args.json:
print(json.dumps(results, indent=2, ensure_ascii=False))
else:
print(results["diagnosis"]["summary"])
print(f"\n(Para output completo em JSON, use: python health_check.py --json)")
if __name__ == "__main__":
main()
@@ -0,0 +1,296 @@
#!/usr/bin/env python3
"""
Claude Monitor — Monitor Contínuo de Performance
Coleta snapshots periódicos de CPU, RAM e browsers.
Gera relatório com tendências e alertas ao final.
Uso:
python monitor.py # 5 min, amostras a cada 30s
python monitor.py --interval 10 --duration 120 # 2 min, amostras a cada 10s
python monitor.py --output meu_log.json # Salvar em arquivo específico
"""
import json
import os
import signal
import subprocess
import sys
import time
from datetime import datetime
from pathlib import Path
try:
import psutil
except ImportError:
subprocess.check_call([sys.executable, "-m", "pip", "install", "psutil", "--quiet"])
import psutil
sys.path.insert(0, str(Path(__file__).parent))
from config import BROWSER_NAMES, CLAUDE_NAMES, MONITOR_DEFAULTS
def take_snapshot():
"""Coleta um snapshot rápido do sistema."""
cpu = psutil.cpu_percent(interval=0.5)
ram = psutil.virtual_memory()
# Browser totals
browser_ram = 0
browser_count = 0
for proc in psutil.process_iter(["name", "memory_info"]):
try:
name = proc.info["name"].lower()
for bname in BROWSER_NAMES:
if bname in name:
browser_ram += proc.info["memory_info"].rss
browser_count += 1
break
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
# Claude totals
claude_ram = 0
claude_count = 0
for proc in psutil.process_iter(["name", "memory_info"]):
try:
name = proc.info["name"].lower()
for cname in CLAUDE_NAMES:
if cname in name:
claude_ram += proc.info["memory_info"].rss
claude_count += 1
break
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
return {
"timestamp": datetime.now().isoformat(),
"cpu_percent": cpu,
"ram_percent": ram.percent,
"ram_used_gb": round(ram.used / 1024**3, 2),
"ram_available_gb": round(ram.available / 1024**3, 2),
"browser_ram_gb": round(browser_ram / 1024**3, 2),
"browser_processes": browser_count,
"claude_ram_gb": round(claude_ram / 1024**3, 2),
"claude_processes": claude_count,
}
def analyze_snapshots(snapshots, alert_cpu, alert_ram):
"""Analisa os snapshots coletados e gera relatório."""
if not snapshots:
return {"error": "Nenhum snapshot coletado"}
n = len(snapshots)
cpu_values = [s["cpu_percent"] for s in snapshots]
ram_values = [s["ram_percent"] for s in snapshots]
browser_ram_values = [s["browser_ram_gb"] for s in snapshots]
# Alertas
alerts = []
for s in snapshots:
if s["cpu_percent"] >= alert_cpu:
alerts.append({
"time": s["timestamp"],
"type": "cpu",
"value": s["cpu_percent"],
"threshold": alert_cpu,
})
if s["ram_percent"] >= alert_ram:
alerts.append({
"time": s["timestamp"],
"type": "ram",
"value": s["ram_percent"],
"threshold": alert_ram,
})
# Tendência (compara primeira metade com segunda metade)
mid = n // 2
if mid > 0:
cpu_first = sum(cpu_values[:mid]) / mid
cpu_second = sum(cpu_values[mid:]) / (n - mid)
ram_first = sum(ram_values[:mid]) / mid
ram_second = sum(ram_values[mid:]) / (n - mid)
cpu_diff = cpu_second - cpu_first
ram_diff = ram_second - ram_first
if abs(cpu_diff) < 5 and abs(ram_diff) < 3:
trend = "estavel"
elif cpu_diff > 5 or ram_diff > 3:
trend = "piorando"
else:
trend = "melhorando"
else:
trend = "insuficiente"
cpu_diff = 0
ram_diff = 0
# Resumo
report = {
"samples": n,
"duration_seconds": round(
(datetime.fromisoformat(snapshots[-1]["timestamp"]) -
datetime.fromisoformat(snapshots[0]["timestamp"])).total_seconds(), 0
) if n > 1 else 0,
"cpu": {
"avg": round(sum(cpu_values) / n, 1),
"max": round(max(cpu_values), 1),
"min": round(min(cpu_values), 1),
},
"ram": {
"avg_percent": round(sum(ram_values) / n, 1),
"max_percent": round(max(ram_values), 1),
"avg_used_gb": round(sum(s["ram_used_gb"] for s in snapshots) / n, 1),
},
"browsers": {
"avg_ram_gb": round(sum(browser_ram_values) / n, 1),
"max_ram_gb": round(max(browser_ram_values), 1),
"avg_processes": round(sum(s["browser_processes"] for s in snapshots) / n, 0),
},
"trend": trend,
"trend_detail": {
"cpu_change": round(cpu_diff, 1),
"ram_change": round(ram_diff, 1),
},
"alerts_count": len(alerts),
"alerts": alerts[:20], # Máximo 20 alertas no relatório
}
# Recomendação final
if report["cpu"]["avg"] > alert_cpu:
report["recommendation"] = (
f"CPU consistentemente alta (media {report['cpu']['avg']}%). "
f"Fechar aplicativos pesados e abas de browser desnecessarias."
)
elif len(alerts) > n * 0.3:
report["recommendation"] = (
f"Alertas frequentes ({len(alerts)} de {n} amostras). "
f"Sistema sob pressao intermitente. Reduzir carga."
)
elif trend == "piorando":
report["recommendation"] = (
f"Tendencia de piora detectada (CPU {'+' if cpu_diff > 0 else ''}{cpu_diff:.0f}%, "
f"RAM {'+' if ram_diff > 0 else ''}{ram_diff:.0f}%). Monitorar."
)
else:
report["recommendation"] = "Sistema estavel durante o monitoramento."
return report
def format_report(report):
"""Formata o relatório para exibição."""
lines = ["## Relatorio de Monitoramento\n"]
lines.append(f"- **Amostras**: {report['samples']} em {report['duration_seconds']}s")
lines.append(f"- **Tendencia**: {report['trend'].upper()}")
lines.append(f"- **Alertas**: {report['alerts_count']}\n")
lines.append("### CPU")
lines.append(f"- Media: {report['cpu']['avg']}%")
lines.append(f"- Max: {report['cpu']['max']}% | Min: {report['cpu']['min']}%\n")
lines.append("### RAM")
lines.append(f"- Media: {report['ram']['avg_percent']}% ({report['ram']['avg_used_gb']} GB)")
lines.append(f"- Pico: {report['ram']['max_percent']}%\n")
lines.append("### Browsers")
lines.append(f"- Media RAM: {report['browsers']['avg_ram_gb']} GB")
lines.append(f"- Pico RAM: {report['browsers']['max_ram_gb']} GB")
lines.append(f"- Media processos: {report['browsers']['avg_processes']}\n")
lines.append(f"### Recomendacao")
lines.append(f"{report['recommendation']}")
return "\n".join(lines)
def main():
import argparse
parser = argparse.ArgumentParser(description="Claude Monitor - Monitor Continuo")
parser.add_argument("--interval", type=int, default=MONITOR_DEFAULTS["interval"],
help=f"Segundos entre amostras (default: {MONITOR_DEFAULTS['interval']})")
parser.add_argument("--duration", type=int, default=MONITOR_DEFAULTS["duration"],
help=f"Duracao total em segundos (default: {MONITOR_DEFAULTS['duration']})")
parser.add_argument("--output", type=str, default=None,
help="Arquivo de saida JSON")
parser.add_argument("--alert-cpu", type=int, default=MONITOR_DEFAULTS["alert_cpu"],
help=f"Threshold CPU para alerta (default: {MONITOR_DEFAULTS['alert_cpu']})")
parser.add_argument("--alert-ram", type=int, default=MONITOR_DEFAULTS["alert_ram"],
help=f"Threshold RAM para alerta (default: {MONITOR_DEFAULTS['alert_ram']})")
parser.add_argument("--json", action="store_true", help="Output em JSON")
args = parser.parse_args()
snapshots = []
start_time = time.time()
sample_count = 0
expected_samples = args.duration // args.interval
print(f"Monitorando por {args.duration}s (amostra a cada {args.interval}s)...")
print(f"Esperando {expected_samples} amostras. Ctrl+C para parar.\n")
# Permite interromper com Ctrl+C
interrupted = False
def handle_interrupt(sig, frame):
nonlocal interrupted
interrupted = True
print("\nInterrompido pelo usuario. Gerando relatorio...\n")
signal.signal(signal.SIGINT, handle_interrupt)
while not interrupted and (time.time() - start_time) < args.duration:
snapshot = take_snapshot()
snapshots.append(snapshot)
sample_count += 1
# Print inline progress
print(
f"[{sample_count}/{expected_samples}] "
f"CPU: {snapshot['cpu_percent']:5.1f}% | "
f"RAM: {snapshot['ram_percent']:5.1f}% | "
f"Browsers: {snapshot['browser_ram_gb']:.1f}GB ({snapshot['browser_processes']} proc) | "
f"Claude: {snapshot['claude_ram_gb']:.1f}GB ({snapshot['claude_processes']} proc)"
)
# Espera até a próxima amostra
elapsed = time.time() - start_time
next_sample_at = sample_count * args.interval
sleep_time = max(0, next_sample_at - elapsed)
if sleep_time > 0 and not interrupted:
time.sleep(sleep_time)
# Analisa
report = analyze_snapshots(snapshots, args.alert_cpu, args.alert_ram)
# Salva log
output_data = {
"config": {
"interval": args.interval,
"duration": args.duration,
"alert_cpu": args.alert_cpu,
"alert_ram": args.alert_ram,
},
"snapshots": snapshots,
"report": report,
}
if args.output:
output_path = args.output
else:
output_path = f"monitor_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(output_path, "w", encoding="utf-8") as f:
json.dump(output_data, f, indent=2, ensure_ascii=False)
print(f"\nLog salvo em: {output_path}\n")
if args.json:
print(json.dumps(report, indent=2, ensure_ascii=False))
else:
print(format_report(report))
if __name__ == "__main__":
main()