---
title: "Ctrl+J: el salto de línea que las TUIs de agentes no te cuentan"
description: "Enter envía el prompt. Ctrl+J inserta el salto de línea en kiro-cli y Cursor Agent; en la terminal de VS Code el mismo atajo oculta el panel."
date: 2026-09-05
locale: es
tags: [tips]
url: https://escribano.dev/es/blog/016-ctrl-j-terminal-newline/
---
Quieres un prompt de varias líneas en `kiro-cli` o en la TUI de `agent`: una lista de requisitos, un diff explicado, contexto que no cabe en una frase. Pulsas Enter y se envía a medias.

**Ctrl+J** inserta la línea en casi cualquier terminal, incluido tmux. Hasta que lo pulsas dentro de la terminal integrada de VS Code (o de un derivado como Cursor o Kiro IDE) y desaparece el panel.

Este post es corto a propósito. Complementa el [mapa de herramientas IAG](/blog/015-spec-driven-development-iag/) con un detalle de teclado que esas CLIs documentan mal.

## El truco

Enter envía. Ctrl+J inserta el salto de línea.

| Herramienta | Nueva línea (siempre) | Otras, si el terminal las reporta |
|-------------|----------------------|-----------------------------------|
| OpenCode | Shift+Enter / Ctrl+Enter (la app las intercepta) | (ninguna) |
| Kiro CLI | Ctrl+J | Shift+Enter, Alt+Enter; `/editor` abre `$EDITOR` |
| Cursor Agent (`agent`) | Ctrl+J; `\` y luego Enter | Shift+Enter; Option+Enter tras `/setup-terminal` |

Ctrl+J no es un atajo inventado por Kiro. Es el carácter de control que el terminal lleva décadas emitiendo. **No hay otro `Ctrl+letra` que haga lo mismo**: J es la décima letra del alfabeto latino, y Ctrl+J es el único byte ASCII 10 (LF). Ctrl+M es CR (el Enter que envía). Ctrl+I es Tab. No existe un «Ctrl+K pero para newline».

Lo que sí existe son **otras combinaciones que la TUI interpreta como “nueva línea”** sin emitir LF:

| Combinación | Qué viaja por el cable | Dónde funciona de serie |
|-------------|------------------------|-------------------------|
| Ctrl+J | LF (`\n`, ASCII 10) | Cualquier terminal real; en VS Code el workbench se lo queda |
| `\` + Enter | barra invertida y luego CR; la TUI lo traduce a newline | Cursor Agent, también en tmux y en la terminal de VS Code |
| Shift+Enter | secuencia con modificador (no es LF) | iTerm2, Ghostty, Kitty, Warp, Zed |
| Alt+Enter / Option+Enter | suele ser ESC+CR u otra secuencia | Terminal.app, Ghostty; VS Code solo tras configurar |

Shift+Enter y Alt+Enter **no son Ctrl+J con otra etiqueta**. Dependen de que el emulador distinga el modificador. En gnome-terminal, en muchos IDEs y dentro de tmux, Shift+Enter llega como un Enter normal y envía el prompt. Por eso las docs de Cursor y Kiro llaman a Ctrl+J (y, en Agent, a `\`+Enter) la opción universal.

Kiro documenta `/settings terminal` para auto-configurar Shift+Enter. Cursor documenta `/setup-terminal`. Ambos acaban escribiendo un keymap del emulador (en VS Code, un `sendSequence`) para que esa combinación deje de ser un Enter disfrazado.

## Por qué funciona

Un `Ctrl+letra` no es magia del sistema operativo. En el modelo teletype / VT100, **Ctrl más una letra produce el byte `letra & 0x1F`**: el carácter de control correspondiente.

| Atajo | Carácter | ASCII | Equivalente |
|-------|----------|-------|-------------|
| Ctrl+J | LF | 10 | `\n` |
| Ctrl+M | CR | 13 | Enter |
| Ctrl+I | HT | 9 | Tab |
| Ctrl+[ | ESC | 27 | Escape |
| Ctrl+D | EOT | 4 | EOF / cerrar input |
| Ctrl+L | FF | 12 | Limpiar pantalla (a menudo) |
| Ctrl+H | BS | 8 | Backspace (a veces) |

Enter es **CR** (Ctrl+M). Ctrl+J es **LF**. Kiro y Agent, en la TUI, tratan LF como newline: si el editor se come Ctrl+J antes, no llega nada al input. OpenCode intercepta Shift+Enter a nivel de aplicación y no depende de ese byte.

Hay tres capas, y cada una puede quedarse el evento:

```
Teclado → terminal (bytes) → TUI (interpretación)
```

En un emulador de verdad (WezTerm, Alacritty, iTerm2, GNOME Terminal) las tres capas colaboran y Ctrl+J es LF. En la terminal integrada de un IDE la capa del medio es el *workbench*: decide si el atajo es suyo o del proceso que corre dentro.

Linux y macOS se comportan igual en terminal real. En Windows, Windows Terminal + WSL, Git Bash o SSH también. PowerShell, cmd y cualquier campo de texto de una GUI **no** siguen estas reglas: ahí Ctrl+J no es LF.

## El conflicto en VS Code (y derivados)

En Visual Studio Code, **Ctrl+J** (Cmd+J en macOS) está ligado a `workbench.action.togglePanel`: mostrar u ocultar el panel inferior. Cursor, Kiro IDE y el resto de forks heredan el mapa de teclas. Si ejecutas `kiro-cli` o `agent` **dentro** de esa terminal, Ctrl+J no llega al TUI. Llega al workbench. El panel se pliega.

En macOS el atajo del panel suele ser **Cmd+J**, así que **Ctrl+J puede funcionar** en la terminal integrada. El roce feo es Linux y Windows, donde Ctrl+J *es* el toggle del panel.

No hay un segundo `Ctrl+…` de fábrica que inserte LF y deje Ctrl+J para el panel. En **Cursor Agent** no hace falta: escribe `\` y pulsa Enter. Lo he comprobado en la terminal integrada de Cursor: inserta la línea, no oculta el panel y no toca el keymap. (La documentación de Agent lo da también por bueno en tmux y por SSH.) En **Kiro**, sin remapear, queda `/editor` (abre `$EDITOR`) o pegar texto ya multilínea.

Si quieres Shift+Enter en esa terminal, no es gratis: hay que configurar el IDE (`/setup-terminal` en Agent, `/settings terminal` en Kiro), que acaba en un `sendSequence`. El resto de esta sección es eso: recuperar el LF de Ctrl+J, o mandar el byte 10 con otra tecla.

### Dejar que Ctrl+J llegue al TUI

VS Code intercepta una lista de comandos cuando la terminal tiene el foco (`terminal.integrated.commandsToSkipShell`). `workbench.action.togglePanel` está en esa lista. Si lo sacas, Ctrl+J vuelve a ser LF para el proceso:

```json
{
  "terminal.integrated.commandsToSkipShell": [
    "-workbench.action.togglePanel"
  ]
}
```

El panel sigue ocultándose con ``Ctrl+` `` (el atajo de siempre de la terminal). Solo cedes Ctrl+J mientras el cursor está en la terminal.

### Enviar LF con otra tecla

Si no quieres tocar el toggle, manda el byte a mano. `\u000a` es LF (el mismo 10 que Ctrl+J). **No uses `\u000d`**: eso es CR, el Enter que envía el prompt.

```json
{
  "key": "ctrl+enter",
  "command": "workbench.action.terminal.sendSequence",
  "args": { "text": "\u000a" },
  "when": "terminalFocus"
}
```

Elige una combinación que el IDE no reserve ya. Ctrl+Enter a veces está ocupado; Alt+J o Ctrl+Shift+J suelen estar libres. Misma receta en Cursor y Kiro IDE: el comando se llama igual.

También puedes reasignar Ctrl+J **solo con la terminal enfocada** a ese `sendSequence`. Entonces Ctrl+J en el editor sigue plegando el panel, y en la terminal inserta la línea. Sigue siendo un cambio de keymap; es el más preciso si vives en el IDE.

### No pelear con el IDE

Si `\`+Enter no te basta o estás en Kiro: abre la TUI en WezTerm, Alacritty o el emulador que ya uses. Ahí Ctrl+J es LF y no hay panel que ocultar. Pegar texto multilínea desde el portapapeles también funciona en casi todas las TUIs.

La TUI del agente y el chat del sidebar de Cursor no son la misma superficie. Este conflicto solo aparece cuando el agente corre **en la terminal integrada**.

## Cierre

Enter envía porque es CR. Ctrl+J inserta una línea porque es LF: no hay otro `Ctrl+letra` equivalente. Shift+Enter y Alt+Enter son atajos de la TUI, no del terminal, y fallan donde el emulador no reporta el modificador. En Agent, `\`+Enter cubre ese hueco sin pelear con VS Code. En el resto, o le devuelves el LF al TUI, o escribes el prompt donde el editor no pisa el teclado.
