# 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<CliRunResult>` 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 \`<name>.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`, `<workdir>/<repo>/CLAUDE.md` existe y contiene el marker.

> ✅ **Verificación**: `ls workdir/<repo>/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 = "<valor final>"`.
- 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":"<TEST-KEY>"}'
```

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/<test-repo>/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.
