# Migración Cursor → Claude Code CLI — hu-developer-bot > Documento técnico de plan de migración. Listo para pegar en Notion. --- ## Contexto Hoy `hu-developer-bot` usa Cursor como agente de código: - **Cursor CLI** (`cursor-agent`) spawneado por `src/cursor/cli-client.ts` para triage, fix, follow-up, respuestas a comentarios de PR, mentions de PR y mentions de Slack. - **Cursor Cloud HTTP API** (`api.cursor.com/v0/agents`) consumido por `src/cursor/cloud-client.ts` y expuesto por debug routes en `src/server/routes/debug.ts`. - **Reglas** sincronizadas como symlinks `.mdc` dentro de `.cursor/rules/` en cada repo target (vía `src/repo/rules-sync.ts`). **Por qué migramos.** Consolidar en Claude (alineado con el stack interno y con el modelo que ya nos cobran vía Cursor proxy → `claude-opus-4-7-thinking-high`), eliminar la dependencia del binario `cursor-agent` y de `api.cursor.com`, y tener control directo sobre `ANTHROPIC_API_KEY` / SSM. ### Decisiones tomadas (cerradas) 1. **Runtime**: `claude` CLI spawneado igual que `cursor-agent` (mismo shape `Bun.spawn`, `--output-format stream-json`, bypass de permisos). No usamos Claude Agent SDK ni Anthropic API directa. 2. **Cloud client**: se elimina por completo (`cloud-client.ts`, debug routes `/api/cursor/*`, health check `checkCursor`). No hay equivalente público de Anthropic. 3. **Reglas**: se mantienen los symlinks `.cursor/rules/*.mdc` (para humanos que sigan usando Cursor en los repos target) **+** se inyecta un `CLAUDE.md` raíz en cada repo target para que el CLI de Claude lo auto-cargue. 4. **Rollout**: big-bang en un solo PR. Sin feature flag, sin runtime dual. --- ## Resumen ejecutivo | Área | Cambio | |---|---| | Carpeta | `src/cursor/` → `src/agent/` (nombre vendor-neutral) | | Clases | `CursorCliClient` → `ClaudeCliClient`; `CursorCloudClient` → eliminada | | Errores | `CursorAgentError` → `AgentError` (`AGENT_ERROR`) | | Constantes | `CURSOR_AGENT` → `CLAUDE_AGENT` (drop `CLOUD_BASE_URL`) | | Env vars | `CURSOR_API_KEY` → `ANTHROPIC_API_KEY`; `CURSOR_MODEL` → `CLAUDE_MODEL` | | Binario | `~/.local/bin/cursor-agent` → `claude` (vía `npm i -g @anthropic-ai/claude-code`) | | CLI flags | `--api-key/--yolo/--print/--trust/--output-format stream-json/--stream-partial-output` → `--print/--model/--output-format stream-json/--verbose/--dangerously-skip-permissions/--include-partial-messages` | | Reglas | Mantener `.cursor/rules/*.mdc` symlinks + escribir `CLAUDE.md` raíz | | Debug API | Eliminar `/api/cursor/models`, `/api/cursor/agents`, `/api/cursor/agents/conversation` y branch `cursor` de `/api/health/full` | | Logs | Prefijos `[cursor:*]` → `[claude:*]`; keys Pino `cursor_cli_*` → `claude_cli_*` (impacta monitores Datadog) | | Infra | Terraform env block + parámetro SSM `/hu-agent/cursor-api-key` → `/hu-agent/anthropic-api-key` | --- ## Fases de implementación ### Fase 0 — Pre-flight (sin cambios de código) - `git status` limpio. - `bun install --frozen-lockfile` ok. - `bun run check && bun run test` verde en `develop`. - Tener un Jira ticket de prueba listo para Fase 11. - **Ops**: pre-crear el SSM parameter `/hu-agent/anthropic-api-key` (SecureString, mismo KMS que el viejo) antes del deploy en Fase 12. --- ### Fase 1 — Constants + Errors + Types Renombres fundacionales para que las fases siguientes compilen incrementalmente. **Modificar:** - `src/core/constants.ts` — `CURSOR_AGENT` → `CLAUDE_AGENT`, drop `CLOUD_BASE_URL`. Mantener timeouts: `TRIAGE_TIMEOUT_MS=16min`, `INITIAL_TIMEOUT_MS=30min`, `FOLLOWUP_TIMEOUT_MS=15min`. - `src/core/errors.ts` — `CursorAgentError` → `AgentError`, code `"CURSOR_AGENT_ERROR"` → `"AGENT_ERROR"`. - `src/types/index.ts`: - Eliminar bloque `// ── Cursor ───` (HTTP types: `AgentStatus`, `CursorAgent`, `LaunchAgentRequest`, `FollowUpRequest`, `ConversationMessage`, `AgentConversation`). - Mantener `CliRunResult` y `StreamJsonEvent` (genéricos). - `AppConfig.cursorApiKey` → `anthropicApiKey`; `cursorModel` → `claudeModel`. - `test/core/errors.test.ts` — actualizar al nuevo `AgentError`. > ✅ **Verificación**: `bun run typecheck` rompe en sitios esperados (se arreglan en fases siguientes); `errors.test.ts` y `constants.test.ts` deben pasar aislados. --- ### Fase 2 — Nuevo módulo `src/agent/` **Recomendación**: nombrar la carpeta `src/agent/` (no `src/claude/`). Es el "agent runner" interno, no una integración nueva con un vendor; el nombre neutral nos protege del próximo swap. **Crear:** - `src/agent/cli-client.ts` — `ClaudeCliClient`. Constructor `(apiKey, model, logger)` y método público `runAgent(prompt, workDir, timeoutMs?, options?): Promise` idénticos al actual. **Cambios internos:** - `CLAUDE_BIN = "claude"` (resuelve por PATH; npm-global del Dockerfile). - Argv nuevo: ```ts [ "claude", "--print", effectivePrompt, "--model", this.model, "--output-format", "stream-json", "--verbose", // requerido con stream-json en --print "--dangerously-skip-permissions", // equivalente a --yolo --trust "--include-partial-messages", // equivalente a --stream-partial-output ] ``` - **Env del proceso**: dropear `CURSOR_API_KEY`, `CURSOR_CLI_COMPAT`. Setear `ANTHROPIC_API_KEY: this.apiKey`. Mantener `GIT_EDITOR/GIT_SEQUENCE_EDITOR=true`, `GIT_MERGE_AUTOEDIT=no`, `GIT_TERMINAL_PROMPT=0`. - **Stream events a parsear**: - `{type:"system", subtype:"init"}` — log único. - `{type:"assistant", message:{content:[{type:"text"|"thinking"|"tool_use", ...}]}}` — `text` → stderr; `tool_use` → `[claude:tool ...]` truncado. - `{type:"user", message:{content:[{type:"tool_result", tool_use_id, content, is_error}]}}` → `[claude:result ...]` truncado. - `{type:"result", subtype:"success"|"error_max_turns"|..., result, is_error, total_cost_usd, num_turns}` — captura `result` como `turnEndResult`. - `{type:"stream_event", event:{type:"content_block_delta", delta:{type:"text_delta", text}}}` — forward a stderr (parcial). - `extractAssistantText`: tomar el último `assistant` event, joinear text blocks; fallback a `result.result` del evento final. Eliminar la rama legacy `agent_turn_end` y el `event.content` string-fallback. - Throw `AgentError` (era `CursorAgentError`). - Mantener la lógica de overflow a `.hu-bot/prompt-*.md` para prompts >128 KB. - Renombrar log keys: `cursor_cli_started/finished/failed` → `claude_cli_*`. Prefijos `[cursor:*]` → `[claude:*]`. - `src/agent/prompts.ts` — mover verbatim desde `src/cursor/prompts.ts`. Los marcadores (`PR_TITLE_START/END`, `PR_BODY_START/END`, `REPLY_START/END`, `VERDICT:/SUMMARY:/QUESTIONS:`) **NO** son cursor-específicos: se mantienen. Único ajuste cosmético: el comentario en `buildCardReadinessPrompt` que dice "Cursor agent receives this prompt as text only" → "Claude agent...". - `src/agent/client.ts` — barrel: ```ts export { ClaudeCliClient } from "./cli-client.js"; ``` > ✅ **Verificación**: `src/agent/*.ts` typechequea aislado. --- ### Fase 3 — Reglas: inyección de `CLAUDE.md` en repos target **Decisión**: mantener `.cursor/rules/*.mdc` (humanos en Cursor) + escribir un `CLAUDE.md` raíz fino que apunte a `.cursor/rules/`. **No duplicar contenido.** **Razón**: el CLI de Claude auto-carga `CLAUDE.md` del cwd. **NO** auto-carga `.claude/rules/` (eso es Cursor-ism). Mirrorear no aporta y agrega drift. **Modificar:** - `src/repo/rules-sync.ts` — después del loop de symlinks `mdcFiles`, agregar: ```ts function writeClaudeIndex(targetRepoDir: string, logger: Logger) { const path = join(targetRepoDir, "CLAUDE.md"); const body = `# Repository rules for the Claude Code agent Read every file in \`.cursor/rules/\` (project_rules and any \`.mdc\`) before doing any work. Treat each \`.mdc\` as authoritative on PR templates, linting, and do-not-touch lists. hu-developer-bot conventions: - Output PR_TITLE_START/END and PR_BODY_START/END markers when finishing a fix task. - Output REPLY_START/END markers for PR-comment / mention replies. - English-only in commits, branches, PRs, and code comments. `; // idempotente: solo escribir si difiere del actual } ``` - Llamar `writeClaudeIndex(repoDir, logger)` para cada repo target después de los symlinks. - `src/repo/manager.ts` — extender `isBotManagedPath` (líneas 22–26) para incluir `filePath === "CLAUDE.md"`. Sin esto, el archivo aparecería en cada PR diff y rompería las safety checks. **Tests opcionales** (este PR): - `test/repo/rules-sync.test.ts` — un test mínimo: dado un workdir + 1 repo, después de `syncRemoteRulesRepos`, `//CLAUDE.md` existe y contiene el marker. > ✅ **Verificación**: `ls workdir//CLAUDE.md` existe; `git status` lo lista untracked; jamás aparece en el diff de un PR del bot. --- ### Fase 4 — Pipelines + DI rewire La signature de `runAgent` no cambia, así que cada pipeline cambia solo imports + nombres. **Modificar:** - `src/pipeline/fix-pipeline.ts`: - `import { CURSOR_AGENT }` → `import { CLAUDE_AGENT }`. - `import type { CursorCliClient } from "@/cursor/cli-client.js"` → `from "@/agent/cli-client.js"` (renombrar a `ClaudeCliClient`). - `import { ... } from "@/cursor/prompts.js"` → `from "@/agent/prompts.js"`. - `FixPipelineDeps.cursorCli: CursorCliClient` → `agentCli: ClaudeCliClient`. - 4 call sites: `this.deps.cursorCli.runAgent(...)` → `this.deps.agentCli.runAgent(...)`. - Constantes: `CURSOR_AGENT.{TRIAGE,INITIAL,FOLLOWUP}_TIMEOUT_MS` → `CLAUDE_AGENT.*`. - Log keys / mensajes: `cursor_cli_failed` → `claude_cli_failed`; `"Cursor CLI exited..."` → `"Claude CLI exited..."`. - `src/pipeline/pr-comment-pipeline.ts`, `src/pipeline/pr-mention-pipeline.ts`, `src/pipeline/slack-mention-pipeline.ts`: mismo patrón (1 call site cada uno + renombre de imports/deps). - `src/utils/config.ts`: - Schema Zod: drop `CURSOR_API_KEY`, `CURSOR_MODEL`. Add: ```ts ANTHROPIC_API_KEY: z.string().min(1), CLAUDE_MODEL: z.string().min(1).default("opus"), ``` - Returned config: `cursorApiKey` → `anthropicApiKey`; `cursorModel` → `claudeModel`. - `src/index.ts`: - Drop `import { CursorCliClient, CursorCloudClient } from "@/cursor/client.js"`. - Add `import { ClaudeCliClient } from "@/agent/client.js"`. - Reemplazar bloque de construcción: ```ts const agentCli = new ClaudeCliClient( config.anthropicApiKey, config.claudeModel, logger.child({ module: "claude-cli" }), ); ``` - Eliminar `cursorCloudClient = new CursorCloudClient(...)`. - En cada `new XxxPipeline({...})`: pasar `agentCli` en lugar de `cursorCli`. - En `createApp({...})`: drop `cursorCloudClient`. - `src/server/app.ts` — drop `cursorCloudClient` field de `AppDeps`. > ✅ **Verificación**: `bun run typecheck` verde al final de la fase. --- ### Fase 5 — Eliminar cloud client + debug routes Cursor **Modificar:** - `src/server/routes/debug.ts`: - Drop import de `CursorCloudClient`. - Drop field `cursorCloudClient` de `DebugDeps`. - Eliminar las 3 routes (`GET /api/cursor/models`, `GET /api/cursor/agents`, `GET /api/cursor/agents/conversation`). - Eliminar `checkCursor()` interno y removerlo de `Promise.allSettled([...])` y del `checks` literal en `/api/health/full`. - Actualizar el comment de `hideThinking`: "cursor agent thinking blocks" → "Claude agent thinking blocks". - Mantener `/api/prompts/...` (es agnóstico de agente). **Eliminar (`git rm`):** - `src/cursor/cli-client.ts` - `src/cursor/cloud-client.ts` - `src/cursor/prompts.ts` - `src/cursor/client.ts` - `src/cursor/` (carpeta vacía) > ✅ **Verificación**: `bun run typecheck && bun run lint` verde. --- ### Fase 6 — Tests **Mover/editar:** - `test/cursor/cli-client.test.ts` → `test/agent/cli-client.test.ts`: - Renombrar `CursorCliClient` → `ClaudeCliClient`. - Update import path a `@/agent/cli-client.js`. - Reemplazar assertions de spawn argv: dropear `--api-key/--yolo/--trust/--stream-partial-output/subcommand agent`; agregar `--print/--model/--output-format stream-json/--verbose/--dangerously-skip-permissions/--include-partial-messages`. - Env assertions: assert `ANTHROPIC_API_KEY`, drop `CURSOR_API_KEY/CURSOR_CLI_COMPAT`. - Fixtures de `extractAssistantText`: - Mantener tests con `assistant` events + `message.content[]`. - Agregar: `{type:"result", subtype:"success", result:"x"}` → retorna `"x"` cuando no hay assistant message. - Agregar: `stream_event/content_block_delta` no contamina el extracted text. - Reemplazar fixtures legacy `agent_turn_end` por `result`. - `test/cursor/prompts.test.ts` → `test/agent/prompts.test.ts`: solo update de import path. **Eliminar:** - `test/cursor/cloud-client.test.ts` — sin reemplazo. - `test/cursor/` (vacío). **Actualizar:** - `test/core/errors.test.ts` — `CursorAgentError` → `AgentError`. - `test/core/constants.test.ts` (si existe) — `CURSOR_AGENT` → `CLAUDE_AGENT`, drop `CLOUD_BASE_URL`. - `test/_coverage-sentinel.test.ts` — side-effect imports `@/cursor/...` → `@/agent/...`. - `test/utils/config.test.ts` (si existe) — env vars nuevas. - `bunfig.toml` — `coveragePathIgnorePatterns`: `"src/cursor/client.ts"` → `"src/agent/client.ts"`. > ✅ **Verificación**: `bun run test && bun run test:coverage` verde, ≥80%. --- ### Fase 7 — Dockerfile **Modificar `Dockerfile`** (línea 37): Reemplazar: ```dockerfile RUN curl https://cursor.com/install -fsS | bash ``` Por (recomendado, npm-global): ```dockerfile RUN . "$NVM_DIR/nvm.sh" \ && npm install -g @anthropic-ai/claude-code \ && claude --version ``` > ⚠️ El npm-global bin path ya está en `$PATH` vía nvm. `claude --version` actúa como sanity check del build. > ✅ **Verificación**: `docker build -t hu-agent:claude-test . && docker run --rm hu-agent:claude-test claude --version`. --- ### Fase 8 — Infraestructura **Modificar `infrastructure/app/main.tf`:** - Línea 122: `CURSOR_MODEL = "claude-opus-4-7-thinking-high"` → `CLAUDE_MODEL = ""`. - Línea 141: `CURSOR_API_KEY = "secret::...parameter/hu-agent/cursor-api-key"` → `ANTHROPIC_API_KEY = "secret::...parameter/hu-agent/anthropic-api-key"`. - Correr `terraform fmt infrastructure/app`. **Tareas de Ops** (call out en el PR): > 📌 **Ops handoff** > > 1. Pre-crear SSM parameter `/hu-agent/anthropic-api-key` (SecureString, mismo KMS) con la API key de Anthropic. > 2. Update IAM policies que limiten el ECS task role a ARNs específicos de SSM (buscar `hu-agent/cursor-api-key` literal en repo de infra). > 3. **Datadog**: re-apuntar monitores/dashboards que filtren por `cursor_cli_*` → `claude_cli_*`. Hand-off del rename map al on-call. > 4. Después de 24-48h healthy: borrar SSM `/hu-agent/cursor-api-key`. > ✅ **Verificación**: `terraform plan` muestra solo diff de env vars; `terraform fmt -check` exit 0. --- ### Fase 9 — `.env.example` Reemplazar: ```bash # Cursor Cloud Agent CURSOR_API_KEY=key_your-cursor-api-key CURSOR_MODEL=opus-4.6-thinking ``` Por: ```bash # Claude Code agent # API key from https://console.anthropic.com (must have Claude Code access). ANTHROPIC_API_KEY=sk-ant-... # Model alias or full id. Examples: opus, sonnet, claude-opus-4-7-thinking-high. CLAUDE_MODEL=opus ``` --- ### Fase 10 — Documentación **`AGENTS.md` / `CLAUDE.md`** (symlink): - Línea 12: "Cursor CLI agent" → "Claude Code CLI agent". - Línea 79: `CURSOR_AGENT` → `CLAUDE_AGENT`. - Línea 91: rewrite entry `cursor/` → `agent/` (mismo contenido descriptivo). - Línea 192: bullet "Cursor CLI" → "Claude Code CLI" + comando `claude --print --output-format stream-json --dangerously-skip-permissions ...` + install line del Dockerfile. - Línea 205: "every prompt the bot sends to Cursor" → "...to the Claude Code agent". - Sección 4 (test directory listing): row `cursor/` → `agent/`. **`README.md`:** - Línea 3, 39, 42, 61, 401: "Cursor CLI" → "Claude Code CLI". - Líneas 89-90: rewrite source-tree para `cursor/` → `agent/`; drop `cloud-client.ts`. - Líneas 121-142: reemplazar sección "Cursor Agent CLI" por "Claude Code CLI" (binario único `claude`, install via `npm install -g @anthropic-ai/claude-code`, auth via `ANTHROPIC_API_KEY`). - Líneas 233-234: tabla de env vars actualizada (`ANTHROPIC_API_KEY`, `CLAUDE_MODEL`). - Líneas 463-467: eliminar la sección **Cursor** del debug API. - Líneas 488-490: `CURSOR_AGENT.*` → `CLAUDE_AGENT.*`. **`docs/jira-fix-pipeline.md`:** - Línea 3: "Cursor agent" → "Claude Code agent". - Líneas 146, 179: `CURSOR_AGENT.*` → `CLAUDE_AGENT.*`. **`docs/github-pipeline.md`:** - Línea 3: idem. - Línea 24 (ascii-art): "Cursor CLI" → "Claude Code CLI". - Línea 148: `CursorCliClient.runAgent` → `ClaudeCliClient.runAgent`. - Línea 151: `CURSOR_AGENT.FOLLOWUP_TIMEOUT_MS` → `CLAUDE_AGENT.FOLLOWUP_TIMEOUT_MS`. - Línea 164: "all Cursor tools" → "all Claude Code tools". **`docs/parallelism-roadmap.md`:** - Línea 140: heading "Cursor CLI spawn cost" → "Claude Code CLI spawn cost". - Línea 142: `cursor-agent` subprocesses → `claude` subprocesses (revalidar estimado de 1-2 GB RSS; TODO si difiere). **`docs/label-resolver.md`:** - Línea 16: "Agent-suggested labels (from Cursor agent output)" → "...from Claude agent output" (o solo "agent"). **`docs/api-docs/collection.yaml`:** - Eliminar `fld_cursor` (líneas 68-71) y los 3 requests cursor (líneas 446-471). - Validar si `cursor_agent_id: bc_abc123` y `assignee_email: cursor.bot@humand.co` son vendor-related o nombres del bot account (confirmar con ops). **`rules/general.mdc`:** - Sin cambios obligatorios. Línea 79 referencia `.cursor/rules/pull-request-template.mdc` que sigue existiendo. - Polish opcional: agregar líneas-1 "this agent runs as Claude Code; treat any `.cursor/rules/*.mdc` files as authoritative". > ✅ **Verificación**: > ```bash > grep -rn -i "cursor\|CURSOR" docs/ README.md AGENTS.md CLAUDE.md # solo refs intencionales > grep -rn "CursorCloud\|CursorAgentError\|CURSOR_AGENT\|cursorApiKey\|cursorModel\|CURSOR_API_KEY\|CURSOR_MODEL\|/api/cursor/" src/ test/ infrastructure/ # 0 matches > ``` --- ### Fase 11 — Validación local ```bash bun install bun run check # typecheck + lint + format:check bun run test bun run test:coverage # ≥80% (bunfig.toml) terraform fmt -check infrastructure/app docker build -t hu-agent:claude-local . ``` **Smoke test manual (dev mode):** 1. `.env`: `ANTHROPIC_API_KEY=...`, `CLAUDE_MODEL=opus`, `JIRA_*`, `GITHUB_TOKEN`. `NODE_ENV` unset (dev mode → pollers off). 2. `bun run dev`. Esperar logs `hu-developer-bot v… starting` → `DEV mode active` → `Loaded repo registry`. 3. Trigger manual de fix: ```bash curl -s -X POST http://localhost:3000/hu-agent/api/jira/enqueue \ -H "content-type: application/json" \ -d '{"issueKey":""}' ``` 4. Stderr debe mostrar: `claude_cli_started`, `[claude:tool ...]`, `[claude:result ...]`, `claude_cli_finished`. 5. El agente emite markers `PR_TITLE_START/END` + `PR_BODY_START/END`; bot abre PR (o en dev sin push, `parsePrTitle/parsePrBody` retorna non-null en logs). 6. `ls workdir//CLAUDE.md` existe; no aparece en el PR diff. 7. `curl http://localhost:3000/hu-agent/api/health/full` retorna 200; key `cursor` ya no está en el JSON. --- ### Fase 12 — Deploy & cutover 1. PR abierto. CI corre `bun run check && bun run test:coverage`. Verde. 2. Reviewer + ops sign-off. Ops confirma SSM `/hu-agent/anthropic-api-key` existe. 3. Merge → `develop`. 4. ECS rollout: nueva task definition con imagen `claude` instalado y env block nuevo. Old tasks drenan. 5. Smoke test prod en ticket low-stakes. 6. 24-48h healthy → ops borra SSM viejo y entrada IAM asociada. --- ## Resumen de archivos ### Eliminados - `src/cursor/cli-client.ts` - `src/cursor/cloud-client.ts` - `src/cursor/prompts.ts` - `src/cursor/client.ts` - `src/cursor/` - `test/cursor/cloud-client.test.ts` - `test/cursor/` ### Movidos - `test/cursor/cli-client.test.ts` → `test/agent/cli-client.test.ts` - `test/cursor/prompts.test.ts` → `test/agent/prompts.test.ts` ### Creados - `src/agent/cli-client.ts` - `src/agent/prompts.ts` - `src/agent/client.ts` ### Renombres globales | Antes | Después | |---|---| | `CURSOR_AGENT` | `CLAUDE_AGENT` | | `CursorAgentError` / `CURSOR_AGENT_ERROR` | `AgentError` / `AGENT_ERROR` | | `CursorCliClient` | `ClaudeCliClient` | | `cursorCli` (DI var) | `agentCli` | | `cursorApiKey` / `CURSOR_API_KEY` | `anthropicApiKey` / `ANTHROPIC_API_KEY` | | `cursorModel` / `CURSOR_MODEL` | `claudeModel` / `CLAUDE_MODEL` | | `[cursor:*]` log prefix | `[claude:*]` | | `cursor_cli_*` Pino keys | `claude_cli_*` | --- ## Archivos críticos para la implementación - **`src/agent/cli-client.ts`** (nuevo) — mayor cambio semántico: argv/env/event-shape rewrite preservando signature pública. - **`src/repo/rules-sync.ts`** — añade write de `CLAUDE.md` por target repo junto a symlinks `.cursor/rules/`. - **`src/index.ts`** — DI rewire (drop `CursorCloudClient`, swap `CursorCliClient` → `ClaudeCliClient`). - **`src/server/routes/debug.ts`** — borrar `/api/cursor/*` y branch `checkCursor` de health. - **`Dockerfile`** — swap install command. - **`infrastructure/app/main.tf`** — env vars + SSM rename. --- ## Open questions / supuestos a confirmar 1. **Modelo default**: el plan usa `"opus"` como default de `CLAUDE_MODEL`. Si querés un id pinneado (`claude-opus-4-7-thinking-high`), avisá. 2. **Auth**: asumido `ANTHROPIC_API_KEY` directo (no Bedrock/Vertex/OAuth token). Confirmar. 3. **Flag exacto**: el flag `--include-partial-messages` está documentado pero conviene re-validar contra la doc CLI vigente al implementar. 4. **SSM rename**: requiere coordinación con ops antes del deploy. El TF edit por sí solo no crea el parámetro. 5. **Datadog monitors**: cualquier monitor que filtre por `cursor_cli_*` literal hay que actualizarlo. Sugerido: hand-off al on-call con un rename map.