Instalación y primera sesión
Pi se instala globalmente via npm. El flag --ignore-scripts evita scripts de lifecycle de dependencias que no son necesarios.
# Instalar Pi globalmente
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
# Autenticar con Anthropic (proveedor principal de CULTIVA)
export ANTHROPIC_API_KEY=sk-ant-...
# Abrir una sesión en un proyecto de cliente
cd /projects/cliente-acme/
pi
# One-shot: resumir el repo pasando archivos explícitamente
pi -p "Resume la arquitectura de este proyecto" @README.md @src/
# Modo JSON para pipelines CI
pi --mode json "Lista todos los archivos TypeScript"
# RPC para integración desde Python/Go
pi --mode rpc --no-session
--provider anthropic --model claude-sonnet-4-5 o configúralo en ~/.pi/agent/models.json para que todos los proyectos usen el mismo modelo sin flags extra.
Pasos para un desarrollador junior
-
1
Instalar Pi y verificar versión
npm install -g --ignore-scripts @earendil-works/pi-coding-agent→pi --version -
2
Exportar la API key del vault de equipoAñadir al
~/.zshrc:export ANTHROPIC_API_KEY=$(op read "op://CULTIVA/Anthropic/key") -
3
Crear AGENTS.md globalEditar
~/.pi/agent/AGENTS.mdcon contexto de CULTIVA IA y reglas de comportamiento del equipo -
4
Instalar la extensión cultiva-guardCopiar
cultiva-guard.tsa~/.pi/agent/extensions/y reiniciar Pi -
5
Instalar paquetes de subagentes y MCP
pi install npm:pi-subagents+pi install npm:pi-mcp-adaptery verificar con/subagents-doctor
Tabla de decisión: qué referencia leer
Antes de responder o generar código, Pi apunta al documento correcto según la intención del usuario.
Extensión cultiva-guard
Extensión TypeScript para CULTIVA IA que bloquea comandos destructivos, registra sesiones en log de auditoría por cliente, y añade el comando /contexto-cliente.
~/.pi/agent/extensions/cultiva-guard.ts
Las extensiones globales cargan en todos los proyectos sin requerir project trust. Las extensiones de proyecto en .pi/extensions/ requieren que el usuario confíe el directorio.
// ~/.pi/agent/extensions/cultiva-guard.ts
// Extensión de seguridad y auditoría para CULTIVA IA
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { appendFileSync, mkdirSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";
/** Patrones de comandos destructivos a confirmar */
const DANGEROUS_PATTERNS = [
/rm\s+-rf/,
/DROP\s+(TABLE|DATABASE|SCHEMA)/i,
/DELETE\s+FROM\s+\w+\s*;/i, // DELETE sin WHERE
/git\s+reset\s+--hard/,
/truncate\s+table/i,
/format\s+[a-z]:/i, // Windows format
];
/** Log de auditoría: ~/.cultiva-audit/YYYY-MM-DD.log */
function auditLog(entry: string) {
const dir = join(homedir(), ".cultiva-audit");
mkdirSync(dir, { recursive: true });
const date = new Date().toISOString().slice(0, 10);
appendFileSync(
join(dir, `${date}.log`),
`[${new Date().toISOString()}] ${entry}\n`
);
}
/** Detectar cliente desde cwd: /projects/<cliente>/... */
function detectCliente(cwd: string): string {
const match = cwd.match(/\/projects\/([^/]+)/);
return match?.[1] ?? "unknown";
}
export default function cultivaGuard(pi: ExtensionAPI) {
// 1. Log de inicio de sesión
pi.on("session_start", async (_evt, ctx) => {
const cliente = detectCliente(process.cwd());
auditLog(`SESSION_START cliente=${cliente} model=${ctx.model?.id ?? "?"}`);
if (ctx.mode === "tui")
ctx.ui.notify(`cultiva-guard activo — cliente: ${cliente}`, "info");
});
// 2. Bloqueo de comandos destructivos
pi.on("tool_call", async (event, ctx) => {
if (event.toolName !== "bash") return;
const cmd = event.input?.command ?? "";
const isDangerous = DANGEROUS_PATTERNS.some(p => p.test(cmd));
if (isDangerous) {
const cliente = detectCliente(process.cwd());
auditLog(`DANGEROUS_CMD cliente=${cliente} cmd="${cmd}"`);
if (ctx.hasUI) {
const ok = await ctx.ui.confirm(
"⚠️ Comando destructivo",
`¿Ejecutar "${cmd.slice(0, 80)}"?\n\nEsto se registrará en el log de auditoría.`
);
if (!ok) {
auditLog(`BLOCKED_CMD cliente=${cliente} cmd="${cmd}"`);
return { block: true, reason: "Bloqueado por cultiva-guard. Confirma en UI." };
}
auditLog(`ALLOWED_CMD cliente=${cliente} cmd="${cmd}"`);
} else {
// En modo RPC/JSON sin UI, bloquear por defecto
return { block: true, reason: "Bloqueado en modo no-interactivo (seguridad CULTIVA)" };
}
}
});
// 3. Log de tool results para trazabilidad
pi.on("tool_result", async (event) => {
if (event.toolName === "write" || event.toolName === "edit") {
const cliente = detectCliente(process.cwd());
auditLog(`FILE_WRITE cliente=${cliente} tool=${event.toolName}`);
}
});
// 4. Comando /contexto-cliente
pi.registerCommand("contexto-cliente", {
description: "Inyecta el briefing del cliente activo en el contexto de la sesión",
handler: async (_args, ctx) => {
const cliente = detectCliente(process.cwd());
const briefPath = join(process.cwd(), ".pi/cliente-brief.md");
try {
const { readFileSync } = await import("node:fs");
const brief = readFileSync(briefPath, "utf8");
// Inyectar como mensaje del sistema
await ctx.session.prompt(`[CONTEXTO CLIENTE: ${cliente}]\n\n${brief}`, {
source: "system"
});
ctx.ui.notify(`Contexto de "${cliente}" inyectado`, "success");
} catch {
ctx.ui.notify(`No se encontró .pi/cliente-brief.md en este proyecto`, "error");
}
}
});
}
Flujo de eventos de una extensión
| Evento | Cuándo se dispara | Uso en cultiva-guard |
|---|---|---|
session_start |
Al arrancar / cambiar sesión | Log de inicio, notificación TUI |
tool_call |
Antes de ejecutar cada tool | Bloqueo de comandos destructivos |
tool_result |
Tras ejecutar cada tool | Auditoría de escrituras de ficheros |
before_agent_start |
Antes de enviar el prompt al LLM | Inyectar system prompt de cliente |
context |
Antes de cada llamada al LLM | Añadir mensajes sin destruir historial |
session_shutdown |
Al cerrar Pi | Log de fin de sesión |
Workflow de subagentes: auditoría de repositorio
El caso de uso más frecuente en CULTIVA: recibir un repo de cliente y generar un plan de mejoras. Usando pi-subagents con el chain: scout → planner → worker → reviewer.
# Instalar paquete de subagentes
pi install npm:pi-subagents
# Verificar setup
/subagents-doctor
# Ejecutar el workflow de auditoría completo
/run-chain auditoria-repo -- "Auditar el repositorio y generar un plan de mejoras priorizado"
# O ejecutar paso a paso
/chain scout "Escanear el codebase: entry points, dependencias, riesgos" \
-> planner "Crear plan de mejoras priorizado por impacto y esfuerzo" \
-> worker "Implementar las mejoras quick wins (esfuerzo bajo, impacto alto)" \
-> reviewer[thinking=high] "Revisar cambios: correctitud, tests, regresiones"
# Revisión paralela (velocidad + cobertura)
/parallel-review
Agentes disponibles en pi-subagents
Chain file: auditoria-repo.chain.md
Guardar en .pi/chains/auditoria-repo.chain.md para reutilizar en cualquier proyecto de cliente.
# Auditoría de repositorio CULTIVA IA
## Workflow: scout → planner → worker → reviewer
## scout
phase: Reconocimiento
label: Escanear codebase
as: contexto
output: .pi/contexto-repo.md
outputMode: file-only
model: anthropic/claude-haiku-4-5
Escanea el repositorio completo. Documenta:
- Estructura de directorios y responsabilidades
- Entry points (main, index, server)
- Dependencias críticas y versiones
- Deuda técnica visible (TODOs, FIXMEs)
- Posibles vulnerabilidades de seguridad
- Cobertura de tests
Tarea: {task}
## planner
phase: Planificación
label: Generar plan de mejoras
as: plan
reads: .pi/contexto-repo.md
output: .pi/plan-mejoras.md
model: anthropic/claude-sonnet-4-5
thinking: high
Con el contexto del scout, genera un plan de mejoras priorizado por:
1. Impacto en el negocio del cliente
2. Esfuerzo de implementación (S/M/L)
3. Riesgo de regresión
Clasifica en: Quick Wins (alta prio), Mejoras Estratégicas, Deuda Técnica.
## worker
phase: Implementación
label: Implementar Quick Wins
reads: .pi/plan-mejoras.md
model: anthropic/claude-sonnet-4-5
Implementa solo los Quick Wins del plan (esfuerzo S, impacto alto).
NO modifiques arquitectura central. Crea tests para cada cambio.
## reviewer
phase: Revisión
label: Revisar cambios
model: anthropic/claude-sonnet-4-5
thinking: high
Revisa todos los cambios implementados:
- Correctitud lógica
- Tests añadidos / pasando
- Sin regresiones en funcionalidad existente
- Adherencia al plan original
- Calidad de código (naming, DRY, SOLID básico)
acceptance.verify con "command": "npm test" al paso worker para validar automáticamente que los tests pasan antes de avanzar al reviewer.
Integración SDK Node.js
Para embeber Pi en la plataforma interna de CULTIVA (Node.js) y automatizar workflows desde código.
npm install @earendil-works/pi-coding-agent
import {
AuthStorage,
createAgentSession,
ModelRegistry,
SessionManager,
} from "@earendil-works/pi-coding-agent";
async function runAuditoria(projectPath: string) {
const authStorage = AuthStorage.create();
const modelRegistry = ModelRegistry.create(authStorage);
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage,
modelRegistry,
cwd: projectPath, // contexto del proyecto del cliente
model: { provider: "anthropic", id: "claude-sonnet-4-5" },
tools: ["read", "grep", "find", "ls"], // solo lectura por seguridad
});
// Suscribirse a los eventos de streaming
let output = "";
session.subscribe((event) => {
if (
event.type === "message_update" &&
event.assistantMessageEvent.type === "text_delta"
) {
process.stdout.write(event.assistantMessageEvent.delta);
output += event.assistantMessageEvent.delta;
}
});
// Lanzar la auditoría
await session.prompt(
"Analiza este repositorio y genera un informe de calidad de código en Markdown."
);
await session.dispose();
return output;
}
// Uso desde la plataforma de CULTIVA
const informe = await runAuditoria("/projects/cliente-acme");
console.log(informe);
import { createAgentSessionRuntime, SessionManager } from "@earendil-works/pi-coding-agent";
// Usar Runtime cuando necesitas crear/reemplazar sesiones dinámicamente
const runtime = await createAgentSessionRuntime({
sessionManager: SessionManager.create(), // persistente en disco
authStorage,
modelRegistry,
});
// Sesión 1: Scout del proyecto A
await runtime.newSession({ cwd: "/projects/cliente-a" });
await runtime.session.prompt("Escanea el codebase");
// Fork para planner (hereda contexto del scout)
await runtime.fork();
await runtime.session.prompt("Con el contexto anterior, genera el plan");
// Re-suscribir tras cada fork/switch
runtime.session.subscribe(handleEvent);
SDK vs RPC — cuándo usar cada uno
| Criterio | SDK (Node.js) | RPC (proceso externo) |
|---|---|---|
| Lenguaje del cliente | Node.js / TypeScript | Python, Go, Ruby, cualquiera |
| Acceso al estado interno | ✅ Directo | Via eventos JSONL |
| Type safety | ✅ Total | Manual / generated |
| Aislamiento de proceso | Mismo proceso | ✅ Proceso separado |
| Custom tools in-process | ✅ customTools | Via extensiones externas |
| Overhead | Mínimo | Ligero (IPC) |
Integración MCP adapter
Pi-MCP-adapter expone un único tool proxy mcp() en vez de registrar miles de tokens de definiciones de herramientas MCP.
pi install npm:pi-mcp-adapter
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" },
"directTools": ["search_repositories", "get_file_contents", "create_issue"],
"lifecycle": "lazy"
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": { "POSTGRES_URL": "${DATABASE_URL}" },
"lifecycle": "eager"
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/projects"],
"lifecycle": "lazy"
}
}
}
directTools solo para 5-20 herramientas que el modelo invoca frecuentemente (GitHub search, get_file). El resto se descubre via el proxy mcp() bajo demanda, ahorrando 10,000+ tokens por servidor.
Sandboxing para repos de cliente
Pi no tiene sandbox built-in. Para repos de clientes no confiables o automatizaciones desatendidas, usar aislamiento explícito.
# Ejecutar Pi en Docker con el repo del cliente montado
docker run --rm -it \
-v /projects/cliente-acme:/workspace:ro \ # read-only
-e ANTHROPIC_API_KEY \
--network none \ # sin red
node:20-alpine sh -c \
"npm install -g @earendil-works/pi-coding-agent && \
cd /workspace && \
pi -p 'Analiza este repositorio' --mode json"
# Con red para repos que requieren npm install
docker run --rm -it \
-v /projects/cliente-acme:/workspace \
-e ANTHROPIC_API_KEY \
node:20-alpine sh -c "cd /workspace && pi"
~/.pi/agent/ en el contenedor de cliente (contiene credenciales). Pasar solo las API keys mínimas necesarias. Revisar siempre los diffs antes de copiar resultados al sistema host.
Gestión de secrets
Variable de entorno: export ANTHROPIC_API_KEY=sk-...
Auth storage de Pi: /login → guarda en ~/.pi/agent/auth.json
Vault del equipo: $(op read "op://CULTIVA/...")
No guardar secrets en .pi/settings.json
No hardcodear API keys en extensiones
No incluir .pi/ en el repo del cliente
{
"models": [
{
"provider": "anthropic",
"id": "claude-sonnet-4-5",
"label": "CULTIVA default",
"apiKeyCommand": "op read op://CULTIVA/Anthropic/key"
}
]
}