一键导入
refactoring
Guía para refactorizar código Python de forma segura y efectiva. Incluye técnicas de refactoring, detección de code smells, y mejoras.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Guía para refactorizar código Python de forma segura y efectiva. Incluye técnicas de refactoring, detección de code smells, y mejoras.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Entrevista a un experto de dominio (SME) para extraer su conocimiento técnico tácito y sintetizarlo en una Skill reutilizable. Se activa PROACTIVAMENTE ante 3 situaciones: (1) Frustración — el usuario se queja de código, arquitectura o falta de estándares; (2) Ambigüedad sin Reglas — se pide un refactor profundo pero no hay estándares definidos; (3) Fricción — el usuario ha corregido 2+ veces decisiones de diseño en la misma sesión. USE FOR: extraer estándares, crear reglas de arquitectura, definir anti-patrones, capturar conocimiento tácito, generar prompts reutilizables, entrevistar experto, sintetizar conocimiento técnico, crear skills desde cero.
Entrevista a un experto de dominio (SME) para extraer su conocimiento técnico y sintetizarlo en una Skill Maestra Abstracta (prompt reutilizable). Se activa PROACTIVAMENTE ante 3 situaciones: (1) Frustración — el usuario se queja de código, arquitectura o falta de estándares (ej. "vaya desastre", "arregla este espagueti", "los logs están mal"); (2) Ambigüedad sin Reglas — se pide un refactor profundo o pieza core pero no hay estándares definidos en el contexto; (3) Fricción — el usuario ha corregido 2 o más veces decisiones de diseño/arquitectura/formato en la misma sesión. USE FOR: extraer estándares, crear reglas de arquitectura, definir anti-patrones, refactor sin criterios claros, capturar conocimiento tácito, generar prompts reutilizables, entrevistar experto, sintetizar conocimiento técnico.
Skill para mantener la calidad del código: linting, formatting, type checking. Garantiza que el código cumple con los estándares del proyecto.
Skill para mantener la documentación sincronizada con el código. Incluye patrones de documentación, ubicaciones estándar, y checklists.
Skill para manejar el flujo de trabajo de Git: changelog, commits, push, y releases. Automatiza el proceso de versionado y publicación.
Skill para desarrollar, mantener y extender el servidor MCP de Obsidian. Incluye patrones de código, arquitectura, testing y gestión de paquetes.
| name | Refactoring |
| description | Guía para refactorizar código Python de forma segura y efectiva. Incluye técnicas de refactoring, detección de code smells, y mejoras. |
| tools | ["read","edit","grep_search","run_command"] |
Nunca refactorizar sin tests. Ejecuta
uv run pytest tests/antes y después de cada refactoring para verificar que no rompiste nada.
# Tests deben pasar
uv run pytest tests/ -v
# Sin errores de linting/tipos
uv run ruff check . && uv run pyright
Solo UN cambio a la vez. No mezclar refactorings.
uv run pytest tests/ -v
uv run ruff check . && uv run pyright
git add . && git commit -m "refactor(module): descripción breve"
Problema: Difícil de entender y testear.
Solución: Extract Method
# ❌ ANTES: Función monolítica
def process_vault(path: Path) -> str:
# 100 líneas de código...
pass
# ✅ DESPUÉS: Funciones pequeñas
def process_vault(path: Path) -> str:
"""Procesa el vault completo."""
notes = _find_notes(path)
filtered = _filter_forbidden(notes)
formatted = _format_results(filtered)
return formatted
def _find_notes(path: Path) -> List[Path]:
"""Busca notas en el vault."""
return list(path.rglob("*.md"))
def _filter_forbidden(notes: List[Path]) -> List[Path]:
"""Filtra notas prohibidas."""
return [n for n in notes if not is_forbidden(n)]
def _format_results(notes: List[Path]) -> str:
"""Formatea lista de notas."""
return "\n".join(str(n) for n in notes)
Problema: Cambios requieren editar múltiples lugares.
Solución: Extract Function o clase base.
# ❌ ANTES: Duplicado en cada tool
def tool1():
vault_path = get_vault_path()
if not vault_path:
return "❌ Error: La ruta del vault no está configurada."
# lógica...
def tool2():
vault_path = get_vault_path()
if not vault_path:
return "❌ Error: La ruta del vault no está configurada."
# lógica...
# ✅ DESPUÉS: Helper reutilizable
def _get_vault_or_error() -> Tuple[Optional[Path], Optional[str]]:
"""Obtiene vault path o mensaje de error."""
vault_path = get_vault_path()
if not vault_path:
return None, "❌ Error: La ruta del vault no está configurada."
return vault_path, None
def tool1():
vault_path, error = _get_vault_or_error()
if error:
return error
# lógica...
Problema: Difícil de usar y recordar el orden.
Solución: Parameter Object (dataclass/TypedDict)
# ❌ ANTES: Demasiados parámetros
def create_note(
title: str,
content: str,
folder: str,
tags: str,
template: str,
description: str,
author: str,
) -> str:
...
# ✅ DESPUÉS: Objeto de configuración
from dataclasses import dataclass
@dataclass
class NoteConfig:
title: str
content: str
folder: str = ""
tags: str = ""
template: str = ""
description: str = ""
author: str = ""
def create_note(config: NoteConfig) -> str:
...
Problema: Difícil de seguir la lógica.
Solución: Guard Clauses (early return)
# ❌ ANTES: Anidamiento profundo
def process(data):
if data:
if data.is_valid:
if data.has_permissions:
return do_work(data)
else:
return "Sin permisos"
else:
return "Datos inválidos"
else:
return "Sin datos"
# ✅ DESPUÉS: Guard clauses
def process(data):
if not data:
return "Sin datos"
if not data.is_valid:
return "Datos inválidos"
if not data.has_permissions:
return "Sin permisos"
return do_work(data)
Problema: Significado no claro, difícil de cambiar.
Solución: Constantes nombradas
# ❌ ANTES
if len(content) > 100:
content = content[:100] + "..."
# ✅ DESPUÉS
MAX_PREVIEW_LENGTH = 100
TRUNCATION_SUFFIX = "..."
if len(content) > MAX_PREVIEW_LENGTH:
content = content[:MAX_PREVIEW_LENGTH] + TRUNCATION_SUFFIX
Cambiar nombre para mejorar claridad.
# ❌ Nombre vago
def proc(d):
...
# ✅ Nombre descriptivo
def process_markdown_content(content: str) -> str:
...
Hacer explícito un cálculo complejo.
# ❌ ANTES
if path.suffix == ".md" and not str(path).startswith(".") and path.stat().st_size > 0:
...
# ✅ DESPUÉS
is_markdown = path.suffix == ".md"
is_not_hidden = not str(path).startswith(".")
is_not_empty = path.stat().st_size > 0
if is_markdown and is_not_hidden and is_not_empty:
...
Método en vez de variable temporal.
# ❌ ANTES
base_price = quantity * item_price
discount = base_price * 0.1
# ✅ DESPUÉS
def calculate_base_price() -> float:
return quantity * item_price
def calculate_discount() -> float:
return calculate_base_price() * 0.1
Mover función al módulo donde pertenece.
# ❌ Función de seguridad en navigation.py
# obsidian_mcp/tools/navigation.py
def is_path_safe(path: Path) -> bool:
...
# ✅ Mover a security.py
# obsidian_mcp/utils/security.py
def is_path_safe(path: Path) -> bool:
...
| Módulo | Responsabilidad |
|---|---|
config.py | Solo configuración de entorno |
vault_config.py | Config específica del vault |
utils/security.py | Validación de paths |
utils/vault.py | Operaciones de archivo |
tools/*.py | Herramientas MCP |
# Funciones largas
uv run ruff check . --select=C901
# Complejidad ciclomática alta
uv run radon cc obsidian_mcp/ -a
# Código duplicado (si radon disponible)
uv run radon raw obsidian_mcp/
Antes de refactorizar:
uv run pytest tests/)Durante el refactoring:
Después del refactoring: