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:
- Analizar qué test falló
- Corregir el código o el test
- 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-commandcreado y funcional - Skill
/add-testscreado 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
pytestonpm test - Tests pasan en limpio (no dependen de estado previo)
Rúbrica de autoevaluación
Usa esta tabla para evaluar tu propio trabajo:
| Criterio | Peso | Tu puntuación (1-5) | Notas |
|---|---|---|---|
| CLAUDE.md | 20% | ___ | |
| Skills | 15% | ___ | |
| Hooks | 15% | ___ | |
| Workflow E→P→C | 20% | ___ | |
| CLI funcional | 20% | ___ | |
| Tests | 10% | ___ |
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
- ¿Cuánto tiempo te tomó el proyecto completo? ¿Fue más o menos de lo esperado?
- ¿Qué fase fue la más valiosa — Setup, Explore/Plan, Build, o Test?
- ¿Habrías obtenido el mismo resultado sin el ciclo Explore → Plan → Code?
- ¿En qué momento Claude Code te sorprendió positivamente?
- ¿En qué momento tuviste que corregir a Claude?
Sobre las herramientas
- ¿CLAUDE.md marcó diferencia en la calidad del output de Claude?
- ¿Los skills ahorraron tiempo vs escribir instrucciones en cada prompt?
- ¿Los hooks atraparon algún error que no habrías visto?
- ¿Usaste subagents (Explore) durante la implementación?
Sobre tu proceso
- ¿Cómo compara este workflow con tu proceso de desarrollo habitual?
- ¿Qué cambiarías si volvieras a hacer el proyecto?
- ¿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.jsonnode_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
| Aspecto | Tú | Claude Code |
|---|---|---|
| Identificar edge cases | Piensas en los obvios | Analiza el código y encuentra más |
| Boilerplate | Lo escribes cada vez | Lo genera desde el skill |
| Consistencia | Varía entre tests | Siempre sigue el template del skill |
| Cobertura | A veces olvidas casos | Genera mínimo 3 tests por función |
Lo que tú haces mejor que Claude en testing
| Aspecto | Claude Code | Tú |
|---|---|---|
| Priorizar qué testear | Todo igual | Sabes qué es crítico |
| Tests de integración complejos | A veces se confunde | Entiendes el flujo de negocio |
| Definir qué es "correcto" | Asume | Tú defines los criterios |
| Tests de UX | No puede | Puedes 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
- Configuraste un entorno profesional — CLAUDE.md, skills, hooks. No eres un usuario casual.
- Seguiste el workflow E→P→C — analizaste, planificaste, y ejecutaste en orden. No improvisaste.
- Construiste algo real — una CLI funcional con 3 comandos que puedes usar y mostrar.
- Automatizaste validaciones — los hooks ejecutaron linter y tests sin que tú intervinieras.
- Generaste tests — Claude Code no solo escribió código, también lo validó.
- 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ápsula | Fase | Qué hiciste | Tiempo |
|---|---|---|---|
| 01 | Intro | Entendiste el proyecto, criterios, y timeline | 10 min |
| 02 | Setup | CLAUDE.md + 2 skills + 2 hooks + entry point | 25 min |
| 03 | Explore + Plan | Analizar setup, diseñar CLI, iterar plan | 15 min |
| 04 | Build | Implementar 3 comandos con Claude Code | 35 min |
| 05 | Test + Commit | Tests, review, commit, entrega | 15 min |
| Total | Proyecto 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.