Módulo 8: Proyecto integrador: Tu primer proyecto con Claude Code

Tests, Commit y Entregar: Cierre del Proyecto

Tests, Commit y Entregar: Cierre del Proyecto

Objetivo del proyecto

Construir una herramienta CLI funcional usando el workflow completo de Claude Code. En esta cápsula completas la Fase 4: Test, Commit, Deliver — generarás tests, revisarás el código, harás commit, y cerrarás el ciclo del proyecto integrador.


Qué construiste en módulos anteriores

  • Cápsula 02: Setup completo (CLAUDE.md, skills, hooks)
  • Cápsula 03: Explore + Plan de implementación
  • Cápsula 04: 3 comandos implementados y funcionales

Tu CLI funciona. Ahora falta validar, versionar, y entregar.


Qué agregarás en este módulo

Al terminar esta cápsula tendrás:

  • Tests para los 3 comandos (generados por Claude Code)
  • Código revisado y limpio
  • Commit con mensaje descriptivo
  • PR creado (opcional, si usas GitHub)
  • Proyecto completo y evaluable

Paso a paso guiado

Paso 1: Generar tests con Claude Code

Usa el skill /add-tests que creaste en la Cápsula 02. Este skill le dice a Claude exactamente cómo generar tests para tus comandos.

Generar tests para el primer comando

/add-tests src/commands/add.py

Genera tests para el comando add. Incluye:
- Test de agregar nota con texto básico
- Test de agregar nota con tag
- Test de agregar nota sin tag (tag debe ser None)
- Test de texto vacío (debe fallar o manejar el caso)

Claude lee el skill .claude/skills/add-tests/SKILL.md, analiza el comando, y genera el archivo de tests.

Output esperado de Claude:

Creé tests/test_add.py con 4 tests:
- test_add_basic: agrega nota con texto simple
- test_add_with_tag: agrega nota con tag
- test_add_without_tag: verifica que tag es None por defecto
- test_add_empty_text: verifica manejo de texto vacío

Ejecutando: pytest tests/test_add.py -v

Output de pytest esperado:

tests/test_add.py::TestAdd::test_add_basic PASSED
tests/test_add.py::TestAdd::test_add_with_tag PASSED
tests/test_add.py::TestAdd::test_add_without_tag PASSED
tests/test_add.py::TestAdd::test_add_empty_text PASSED

4 passed in 0.12s

Ejemplo de test generado (Python)

Claude debería generar algo similar a esto:

"""Tests para el comando add."""

import json
from pathlib import Path

from click.testing import CliRunner

from src.cli import cli


class TestAdd:
    """Tests para el comando add."""

    def setup_method(self) -> None:
        self.runner = CliRunner()

    def test_add_basic(self, tmp_path: Path) -> None:
        """Agrega una nota con texto básico."""
        with self.runner.isolated_filesystem(temp_dir=tmp_path):
            result = self.runner.invoke(cli, ["add", "Mi nota de prueba"])
            assert result.exit_code == 0
            assert "#1" in result.output

    def test_add_with_tag(self, tmp_path: Path) -> None:
        """Agrega una nota con tag."""
        with self.runner.isolated_filesystem(temp_dir=tmp_path):
            result = self.runner.invoke(
                cli, ["add", "Nota con tag", "--tag", "work"]
            )
            assert result.exit_code == 0

            notes_file = Path(".notes.json")
            data = json.loads(notes_file.read_text())
            assert data["notes"][0]["tag"] == "work"

    def test_add_without_tag(self, tmp_path: Path) -> None:
        """Verifica que tag es None por defecto."""
        with self.runner.isolated_filesystem(temp_dir=tmp_path):
            result = self.runner.invoke(cli, ["add", "Nota sin tag"])
            assert result.exit_code == 0

            notes_file = Path(".notes.json")
            data = json.loads(notes_file.read_text())
            assert data["notes"][0]["tag"] is None

    def test_add_empty_text(self, tmp_path: Path) -> None:
        """Manejo de texto vacío."""
        with self.runner.isolated_filesystem(temp_dir=tmp_path):
            result = self.runner.invoke(cli, ["add", ""])
            assert result.exit_code != 0 or "empty" in result.output.lower()

Generar tests para los otros comandos

Repite el proceso para list y search:

/add-tests src/commands/list_notes.py

Genera tests para el comando list. Incluye:
- Test de listar sin notas (lista vacía)
- Test de listar con varias notas
- Test de filtrar por tag con --tag
- Test de output JSON con --json
/add-tests src/commands/search.py

Genera tests para el comando search. Incluye:
- Test de búsqueda con resultados
- Test de búsqueda sin resultados
- Test de búsqueda case-insensitive
- Test de búsqueda con texto parcial

Ejecutar todos los tests

Después de generar tests para los 3 comandos:

Ejecuta toda la suite de tests con output verbose:
pytest -v

Output esperado:

tests/test_add.py::TestAdd::test_add_basic PASSED
tests/test_add.py::TestAdd::test_add_with_tag PASSED
tests/test_add.py::TestAdd::test_add_without_tag PASSED
tests/test_add.py::TestAdd::test_add_empty_text PASSED
tests/test_list.py::TestList::test_list_empty PASSED
tests/test_list.py::TestList::test_list_with_notes PASSED
tests/test_list.py::TestList::test_list_filter_tag PASSED
tests/test_list.py::TestList::test_list_json_output PASSED
tests/test_search.py::TestSearch::test_search_with_results PASSED
tests/test_search.py::TestSearch::test_search_no_results PASSED
tests/test_search.py::TestSearch::test_search_case_insensitive PASSED
tests/test_search.py::TestSearch::test_search_partial PASSED

12 passed in 0.35s

Manejar tests que fallan

Si algún test falla, Claude lo verá en el output y debería ofrecer correcciones:

tests/test_search.py::TestSearch::test_search_case_insensitive FAILED

FAILED tests/test_search.py::TestSearch::test_search_case_insensitive
- AssertionError: assert 0 == 0
  E  assert 1 == 0

Pide a Claude que corrija:

El test test_search_case_insensitive falló. Investiga por qué
y corrígelo. Puede ser un bug en el código o en el test.

Claude analiza el error, identifica si el problema está en la implementación o en el test, y lo corrige. Después, ejecuta de nuevo:

Ejecuta el test que falló para confirmar que está arreglado:
pytest tests/test_search.py::TestSearch::test_search_case_insensitive -v

Paso 2: Revisar el código

Con los tests pasando, pide a Claude que haga una revisión completa del código.

Prompt de revisión

Revisa todo el código que escribimos. Analiza:
1. ¿Hay code smells o anti-patterns?
2. ¿El error handling es consistente?
3. ¿Las convenciones de CLAUDE.md se respetan?
4. ¿Hay código muerto o imports sin usar?
5. ¿Los docstrings están completos?
6. ¿Hay oportunidades de mejora que valgan la pena?

Solo reporta. No cambies nada todavía.

Output típico de revisión

## Revisión de código: notes-cli

### Archivos revisados
- src/cli.py ✅
- src/utils.py ✅
- src/commands/add.py ✅
- src/commands/list_notes.py ⚠️
- src/commands/search.py ✅

### Findings

#### 🟡 Sugerencia: list_notes.py
La función format_notes_table podría extraerse a utils.py
ya que search.py tiene una función similar. Evitaría duplicación.

#### 🟢 Bien hecho
- Error handling consistente con click.ClickException
- Type hints en todas las funciones públicas
- Docstrings presentes en todas las funciones de comandos
- Storage manager centralizado en utils.py
- snake_case consistente según CLAUDE.md

#### ✅ No se encontraron
- No hay código muerto
- No hay imports sin usar
- No hay anti-patterns evidentes

### Recomendación
El código está limpio y funcional. La sugerencia de extraer
format_notes_table es una mejora menor — implementar solo
si tienes tiempo.

Veredicto: ✅ Listo para commit

Implementar mejoras (opcional)

Si Claude encontró mejoras que valen la pena:

Implementa la sugerencia de extraer format_notes_table a utils.py.
Actualiza list_notes.py y search.py para usarla desde utils.
Después, ejecuta los tests para verificar que no rompimos nada.

Claude refactoriza, y los tests confirman que todo sigue funcionando.


Paso 3: Actualizar CLAUDE.md

Ahora que el proyecto está completo, actualiza CLAUDE.md para reflejar el estado final:

Actualiza CLAUDE.md para reflejar los comandos reales
(add, list, search) en lugar de los placeholders
(command_one, command_two, command_three). No cambies las
convenciones ni las reglas — solo la estructura y los
nombres de comandos.

Claude actualiza la sección de estructura:

## Estructura
src/
├── __init__.py
├── cli.py              → Entry point, grupo de comandos Click
├── commands/
│   ├── __init__.py
│   ├── add.py           → Agregar nota con texto y tag
│   ├── list_notes.py    → Listar notas con filtros
│   └── search.py        → Buscar notas por texto
└── utils.py             → Storage JSON y formateo de output

tests/
├── __init__.py
├── test_add.py
├── test_list.py
└── test_search.py

Paso 4: Git commit

Este es el momento donde el hook PreToolUse entra en acción. Cuando Claude intente hacer git commit, el hook ejecutará los tests automáticamente.

Primer commit: proyecto completo

Crea un commit con todos los archivos del proyecto.
Usa un mensaje descriptivo que explique lo que construimos.

Claude ejecuta:

git add .
git commit -m "feat: build notes-cli with 3 commands (add, list, search)"

Pero antes del commit, el hook se dispara:

[Hook: PreToolUse] Detected: git commit
[Hook: PreToolUse] Running: pytest --tb=short -q

12 passed in 0.35s

[Hook: PreToolUse] Tests passed ✅ — proceeding with commit

Si los tests pasan, el commit procede:

[main (root-commit) a1b2c3d] feat: build notes-cli with 3 commands (add, list, search)
 15 files changed, 450 insertions(+)
 create mode 100644 CLAUDE.md
 create mode 100644 .claude/settings.json
 create mode 100644 .claude/skills/add-tests/SKILL.md
 create mode 100644 .claude/skills/create-command/SKILL.md
 create mode 100644 .gitignore
 create mode 100644 pyproject.toml
 create mode 100644 src/__init__.py
 create mode 100644 src/cli.py
 create mode 100644 src/commands/__init__.py
 create mode 100644 src/commands/add.py
 create mode 100644 src/commands/list_notes.py
 create mode 100644 src/commands/search.py
 create mode 100644 src/utils.py
 create mode 100644 tests/__init__.py
 create mode 100644 tests/test_add.py
 create mode 100644 tests/test_list.py
 create mode 100644 tests/test_search.py

Si los tests fallan antes del commit

[Hook: PreToolUse] Detected: git commit
[Hook: PreToolUse] Running: pytest --tb=short -q

FAILED tests/test_list.py::TestList::test_list_json_output
1 failed, 11 passed in 0.33s

[Hook: PreToolUse] Tests failed ❌ — commit blocked

Claude ve que el hook bloqueó el commit. Automáticamente debería:

  1. Analizar qué test falló
  2. Corregir el código o el test
  3. Reintentar el commit
El hook bloqueó el commit porque test_list_json_output falló.
Veo que el formato JSON no incluye indentación. Corrijo...

[Modifica list_notes.py]
[Hook: PostToolUse] ruff check: All checks passed!

Reintentando commit...

[Hook: PreToolUse] Running: pytest --tb=short -q
12 passed in 0.35s
[Hook: PreToolUse] Tests passed ✅

[main a1b2c3d] feat: build notes-cli with 3 commands

Esto es el poder de los hooks: el commit no puede pasar sin tests verdes. Es una red de seguridad automática.


Paso 5: Crear PR (opcional)

Si tu proyecto está en GitHub, puedes crear un Pull Request:

Crea un pull request con una descripción detallada de lo que
construimos. Incluye:
- Qué es la CLI
- Los 3 comandos con ejemplos de uso
- El stack técnico
- Cómo ejecutar los tests

Claude ejecuta:

# Crear repositorio en GitHub (si no existe)
gh repo create notes-cli --public --source=. --push

# Crear PR
gh pr create --title "feat: notes-cli - CLI tool built with Claude Code" \
  --body "## Summary
  CLI tool for managing notes from the terminal.
  
  ### Commands
  - \`add\` - Create a note with text and optional tag
  - \`list\` - List all notes with tag filter and JSON output
  - \`search\` - Search notes by text (case-insensitive)
  
  ### Tech Stack
  - Python 3.12 + Click 8.1
  - pytest for testing (12 tests, all passing)
  - Ruff for linting
  
  ### How to test
  \`\`\`bash
  pip install -e '.[dev]'
  pytest -v
  python -m src.cli --help
  \`\`\`
  
  ### Built with
  Claude Code workflow: CLAUDE.md → Skills → Hooks → Explore → Plan → Code → Tests"

Si no usas GitHub, no necesitas este paso. El commit local es suficiente.


Paso 6: Verificación final

Ejecuta la verificación completa del proyecto:

Haz una verificación final del proyecto completo. Ejecuta:
1. pytest -v (todos los tests)
2. ruff check src/ (linting)
3. python -m src.cli --help (CLI funcional)
4. git log --oneline (commits)
5. Verifica que CLAUDE.md, skills, y hooks existen

Output esperado:

## Verificación final

### Tests ✅
12 passed, 0 failed, 0 errors

### Linting ✅
All checks passed!

### CLI ✅
3 commands: add, list, search
--help funcional para todos

### Git ✅
1 commit: "feat: build notes-cli with 3 commands"
Working tree clean

### Setup ✅
- CLAUDE.md: 52 líneas, 6 secciones ✅
- Skills: create-command.md, add-tests.md ✅
- Hooks: PostToolUse (ruff), PreToolUse (pytest) ✅

### Resultado: Proyecto completo ✅

Checklist final del proyecto

Esta es la checklist definitiva. Cada item corresponde a un criterio de evaluación:

CLAUDE.md (20%)

  • Existe en la raíz del proyecto
  • Tiene las 6 secciones: Descripción, Stack, Estructura, Convenciones, Comandos, Reglas
  • Menos de 200 líneas
  • Convenciones específicas (no genéricas como "escribe buen código")
  • Comandos ejecutables (copy-paste funciona)
  • Reglas accionables (lo que NO hacer)
  • Refleja el estado actual del proyecto (no tiene placeholders)

Skills (15%)

  • Al menos 2 skills en .claude/skills/
  • Skill /create-command creado y funcional
  • Skill /add-tests creado y funcional
  • Skills tienen instrucciones claras, template, y reglas
  • Se usó al menos 1 skill durante la implementación

Hooks (15%)

  • Al menos 2 hooks en .claude/settings.json
  • Hook PostToolUse (Write → linter) configurado
  • Hook PreToolUse (Execute → tests antes de commit) configurado
  • Los hooks se dispararon durante la implementación
  • El hook de pre-commit bloqueó o permitió commits correctamente

Workflow Explore → Plan → Code (20%)

  • Se usó Explore para analizar el setup antes de implementar
  • Se usó Plan para diseñar la CLI antes de codificar
  • El plan se iteró al menos 1 vez con feedback
  • Se siguió el plan durante la implementación
  • Se verificó la implementación a mitad de camino

CLI funcional (20%)

  • CLI ejecutable con --help
  • 3 o más comandos funcionales
  • Cada comando tiene help text
  • Cada comando maneja errores correctamente
  • Los comandos producen output legible

Tests (10%)

  • Al menos 3 tests que pasan
  • Tests cubren los 3 comandos
  • Tests incluyen happy path y edge cases
  • Tests se ejecutan con pytest o npm test
  • Tests pasan en limpio (no dependen de estado previo)

Rúbrica de autoevaluación

Usa esta tabla para evaluar tu propio trabajo:

CriterioPesoTu puntuación (1-5)Notas
CLAUDE.md20%___
Skills15%___
Hooks15%___
Workflow E→P→C20%___
CLI funcional20%___
Tests10%___

Escala:

  • 5: Excelente — supera expectativas
  • 4: Bueno — cumple todos los requisitos
  • 3: Suficiente — cumple los mínimos
  • 2: Necesita mejora — falta algo importante
  • 1: Insuficiente — no cumple el criterio

Puntuación total: Suma ponderada. Ejemplo: si tienes 4 en todo = 4.0 / 5.0 = 80%.


Reflexión

Tómate un momento para responder estas preguntas. No hay respuestas correctas — es para que proceses lo que aprendiste:

Sobre el workflow

  1. ¿Cuánto tiempo te tomó el proyecto completo? ¿Fue más o menos de lo esperado?
  2. ¿Qué fase fue la más valiosa — Setup, Explore/Plan, Build, o Test?
  3. ¿Habrías obtenido el mismo resultado sin el ciclo Explore → Plan → Code?
  4. ¿En qué momento Claude Code te sorprendió positivamente?
  5. ¿En qué momento tuviste que corregir a Claude?

Sobre las herramientas

  1. ¿CLAUDE.md marcó diferencia en la calidad del output de Claude?
  2. ¿Los skills ahorraron tiempo vs escribir instrucciones en cada prompt?
  3. ¿Los hooks atraparon algún error que no habrías visto?
  4. ¿Usaste subagents (Explore) durante la implementación?

Sobre tu proceso

  1. ¿Cómo compara este workflow con tu proceso de desarrollo habitual?
  2. ¿Qué cambiarías si volvieras a hacer el proyecto?
  3. ¿Qué técnica de este módulo adoptarías en tu trabajo diario?

Troubleshooting

"Los tests fallan con import error"

# Asegúrate de instalar en modo editable
pip install -e ".[dev]"

# O ajusta PYTHONPATH
export PYTHONPATH="${PYTHONPATH}:$(pwd)"

"El hook de pre-commit no se dispara"

Verifica que settings.json está en .claude/:

cat .claude/settings.json

Verifica que la condición del hook es correcta. El matcher debe ser "Execute" y el command debe verificar que el input contiene git commit.

"pytest no encuentra los tests"

Verifica que pytest.ini o pyproject.toml tiene la configuración correcta:

[tool.pytest.ini_options]
testpaths = ["tests"]

Y que los archivos de test empiezan con test_:

ls tests/
# test_add.py  test_list.py  test_search.py

"git commit incluye archivos que no debería"

Verifica .gitignore:

cat .gitignore

Asegúrate de que incluye:

  • .notes.json (datos de prueba)
  • __pycache__/
  • .claude/settings.local.json
  • node_modules/ (si usas TypeScript)

Si ya hiciste commit con archivos incorrectos:

git rm --cached .notes.json
echo ".notes.json" >> .gitignore
git commit -m "chore: remove data file from tracking"

"La CLI funciona pero los tests fallan"

Esto suele pasar cuando los tests dependen de un archivo .notes.json que existe de sesiones anteriores. Los tests deben usar tmp_path o isolated_filesystem para crear un entorno limpio:

def test_example(self, tmp_path: Path) -> None:
    with self.runner.isolated_filesystem(temp_dir=tmp_path):
        # Aquí no hay .notes.json previo
        result = self.runner.invoke(cli, ["add", "Test"])
        assert result.exit_code == 0

"gh pr create falla"

# Verifica autenticación
gh auth status

# Si no estás autenticado
gh auth login

# Si el repo no existe en GitHub
gh repo create my-cli --public --source=. --push

Comparación: testing manual vs testing con Claude Code

Testing manual (sin Claude Code)

1. Piensas qué tests escribir
2. Escribes cada test a mano
3. Ejecutas y depuras
4. Repites para cada comando
5. Te olvidas de edge cases
6. Total: 30-45 minutos

Testing con Claude Code + skill

1. Invocas /add-tests src/commands/add.py
2. Claude analiza el comando y genera tests
3. Claude ejecuta los tests
4. Si fallan, Claude corrige
5. Repites para cada comando
6. Total: 5-10 minutos

Lo que Claude hace mejor que tú en testing

AspectoTúClaude Code
Identificar edge casesPiensas en los obviosAnaliza el código y encuentra más
BoilerplateLo escribes cada vezLo genera desde el skill
ConsistenciaVaría entre testsSiempre sigue el template del skill
CoberturaA veces olvidas casosGenera mínimo 3 tests por función

Lo que tú haces mejor que Claude en testing

AspectoClaude CodeTú
Priorizar qué testearTodo igualSabes qué es crítico
Tests de integración complejosA veces se confundeEntiendes el flujo de negocio
Definir qué es "correcto"AsumeTú defines los criterios
Tests de UXNo puedePuedes validar si el output se ve bien

La combinación ideal: Claude genera, tú revisas y priorizas.


Proyecto completado

Si llegaste hasta aquí con todos los items del checklist marcados: completaste el proyecto integrador.

Lo que lograste

  1. Configuraste un entorno profesional — CLAUDE.md, skills, hooks. No eres un usuario casual.
  2. Seguiste el workflow E→P→C — analizaste, planificaste, y ejecutaste en orden. No improvisaste.
  3. Construiste algo real — una CLI funcional con 3 comandos que puedes usar y mostrar.
  4. Automatizaste validaciones — los hooks ejecutaron linter y tests sin que tú intervinieras.
  5. Generaste tests — Claude Code no solo escribió código, también lo validó.
  6. Versionaste correctamente — commit con mensaje descriptivo, tests pasando, código limpio.

Lo que esto demuestra

No demostraste que sabes programar una CLI (eso lo puede hacer cualquiera con un tutorial). Demostraste que sabes trabajar con un agente de IA de forma profesional:

  • Configuras el entorno antes de implementar
  • Analizas antes de actuar
  • Planificas antes de codificar
  • Automatizas lo que se puede automatizar
  • Validas antes de entregar

Esa es la competencia que te diferencia.


Qué sigue

Completaste la guía Claude Code Foundations. Ahora tienes las bases para trabajar con Claude Code de forma profesional.

La siguiente guía del path Claude Code Agentic Development es:

Prompt Engineering with Claude Code

En esa guía profundizarás en cómo comunicarte con Claude Code de forma óptima:

  • Prompts como contratos de comportamiento — cómo escribir instrucciones que produzcan resultados predecibles
  • Zero-shot y few-shot prompting — cuándo dar ejemplos y cuándo no
  • Chain-of-thought — cómo pedirle a Claude que razone paso a paso
  • Evaluación de prompts — cómo medir si tus prompts son efectivos
  • Prompts en sistemas reales — CLAUDE.md, skills, y hooks optimizados con prompt engineering

La diferencia entre esta guía y la siguiente: aquí aprendiste las herramientas (CLAUDE.md, skills, hooks, workflow). Allá aprenderás el lenguaje (cómo hablarle a Claude Code para obtener los mejores resultados).


Resumen del módulo completo

CápsulaFaseQué hicisteTiempo
01IntroEntendiste el proyecto, criterios, y timeline10 min
02SetupCLAUDE.md + 2 skills + 2 hooks + entry point25 min
03Explore + PlanAnalizar setup, diseñar CLI, iterar plan15 min
04BuildImplementar 3 comandos con Claude Code35 min
05Test + CommitTests, review, commit, entrega15 min
TotalProyecto completo~1.5 hrs

El workflow que dominaste

CLAUDE.md → Skills → Hooks → Explore → Plan → Code → Tests → Commit
    │          │        │         │        │       │       │        │
    ▼          ▼        ▼         ▼        ▼       ▼       ▼        ▼
 Contexto  Atajos  Automáti-  Analizar  Diseñar  Hacer  Validar  Versionar
 persiste  reusa-  zación    antes de  antes    paso    antes    con
           bles    continua  actuar    de       a      de       confianza
                                      codificar paso   entregar

Esto no es solo un workflow para este proyecto. Es un workflow para cualquier proyecto donde trabajes con Claude Code — o con cualquier agente de IA.

Guía completada. Buen trabajo.