Agent Todos

Muestra el plan de un agente mientras trabaja. Cada paso pasa de pendiente a por hacer, en curso y hecho, con tiempos en vivo, fallos y las llamadas a herramientas detrás. Una píldora de estado para sobre el compositor, cambios del plan que puedes ver y un paso de revisión para editar el plan antes de ejecutarlo.

Los agentes de programación como Claude Code, Codex y Gemini CLI mantienen un plan escrito mientras trabajan, mediante herramientas como TodoWrite, update_plan y write_todos. Agent Todos convierte ese plan en una línea por paso, con el paso en curso explicado en detalle («Ejecutando las pruebas») y cuánto tardó cada uno.

Cada paso pasa de backlog a por hacer, en curso y luego hecho, fallido o cancelado. Cuando el agente reescribe su plan, los pasos nuevos se resaltan, los eliminados se colapsan y una breve nota indica qué cambió. Un paso fallido mantiene su motivo a la vista, y las tandas largas de pasos terminados se pliegan para que el trabajo actual siga en pantalla.

AgentTodosStatus muestra el paso actual y un contador en una línea sobre el compositor, y AgentTodosReview permite editar, reordenar, añadir y quitar pasos antes de que se ejecute nada, y luego empezar con ⌘↵. Los adaptadores leen el formato de herramientas de cada agente, incluida la entrada que aún está en streaming, y Tool Calls puede mostrar el trabajo dentro de cualquier paso.

  1. Añade el registro Pro a components.json

    components.json
    {
      "registries": {
        "@hextaui-pro": {
          "url": "https://hextaui.com/r/pro/{name}.json",
          "headers": {
            "Authorization": "Bearer ${HEXTAUI_PRO_TOKEN}"
          }
        }
      }
    }
  2. Añade tu token

    Crea un token en tu página de cuenta y colócalo en .env.local como HEXTAUI_PRO_TOKEN.

  3. Añade el bloque

    pnpm dlx shadcn@latest add @hextaui-pro/agent-todos

Con AI SDK

getTodosFromParts lee la última llamada a TodoWrite, update_plan o write_todos de un mensaje, incluso mientras su entrada aún está en streaming. Muestra la lista en el mensaje y la píldora de estado sobre el compositor.

"use client"

import { useChat } from "@ai-sdk/react"

import { AgentTodos, AgentTodosStatus } from "@/components/blocks/agent-todos/agent-todos"
import { getTodosFromParts } from "@/components/blocks/agent-todos/todos"

export function Chat() {
  const { messages, status } = useChat()
  const busy = status === "submitted" || status === "streaming"
  const last = messages.at(-1)
  const current = last?.role === "assistant" ? getTodosFromParts(last.parts) : null

  return (
    <div className="flex flex-col gap-4">
      {messages.map((message) => {
        const plan = message.role === "assistant" ? getTodosFromParts(message.parts) : null
        return plan ? (
          <AgentTodos
            key={message.id}
            todos={plan.todos}
            explanation={plan.explanation}
            running={busy && message.id === last?.id}
          />
        ) : null
      })}
      {current ? <AgentTodosStatus todos={current.todos} running={busy} /> : null}
    </div>
  )
}

Revisar antes de ejecutar

Deja que se editen, reordenen, añadan y quiten pasos antes de que el agente empiece. Run plan o ⌘↵ devuelve la lista depurada.

"use client"

import * as React from "react"

import { AgentTodosReview } from "@/components/blocks/agent-todos/todo-review"
import type { Todo } from "@/components/blocks/agent-todos/todos"

export function PlanApproval({
  proposed,
  onRun,
}: {
  proposed: Todo[]
  onRun: (plan: Todo[]) => void
}) {
  const [draft, setDraft] = React.useState(proposed)

  return <AgentTodosReview todos={draft} onChange={setDraft} onApprove={onRun} />
}

Con herramientas de tareas incrementales

Para herramientas que crean y actualizan una tarea a la vez, como TaskCreate y TaskUpdate de Claude Code, incorpora cada llamada a la lista con applyTaskEvent.

"use client"

import * as React from "react"

import { AgentTodos } from "@/components/blocks/agent-todos/agent-todos"
import { applyTaskEvent, type TaskEvent, type Todo } from "@/components/blocks/agent-todos/todos"

export function useTaskList() {
  const [todos, setTodos] = React.useState<Todo[]>([])
  const apply = React.useCallback(
    (event: TaskEvent) => setTodos((current) => applyTaskEvent(current, event)),
    []
  )
  return { todos, apply }
}

export function Tasks({ todos, running }: { todos: Todo[]; running: boolean }) {
  return <AgentTodos todos={todos} running={running} />
}

Anatomía

Las partes que compones, de fuera hacia dentro.

ParteDescripción
AgentTodosLa lista completa con su encabezado, barra de progreso, explicación y nota de cambio del plan.
AgentTodosStatusUna píldora de una línea con el paso actual, que abre la lista en un popover.
AgentTodosReviewEl plan editable que se muestra antes de que el agente empiece.
TodoMarkLa marca de estado por sí sola, para diseños personalizados.
getTodosFromParts, fromTodoWrite, fromUpdatePlan, fromWriteTodos, applyTaskEventAdaptadores del formato de herramientas de cada agente a elementos Todo.
PropTipoPredeterminado
todosPasos en orden.
Todo[]–
explanationPor qué el plan tiene este aspecto, mostrado bajo la barra de progreso. Codex lo envía con update_plan.
string–
runningSi el agente está trabajando. Por defecto es true mientras algún paso esté en curso.
boolean–
titleEtiqueta del encabezado y nombre de la región.
string"Tasks"
collapsiblePermite que el encabezado pliegue la lista.
booleantrue
defaultOpenEmpieza abierto.
booleantrue
foldAfterPliega los pasos terminados al principio cuando la lista sea más larga que esto.
number6
PropTipoPredeterminado
idId estable. Los cambios del plan se detectan por id.
string–
contentQué hacer, en imperativo: «Ejecuta las pruebas».
string–
activeFormLo que está haciendo, mostrado mientras está en curso: «Ejecutando las pruebas».
string–
statusLa etapa. pending se lee como Por hacer.
"backlog" | "pending" | "in_progress" | "completed" | "failed" | "cancelled"–
errorSe muestra bajo un paso fallido.
string–
durationSegundos almacenados, para el historial.
number–
detailsSe abre bajo el paso, normalmente Tool Calls.
ReactNode–
PropTipoPredeterminado
todosLa misma lista. No renderiza nada si está vacía.
Todo[]–
runningAnima la marca y el shimmer mientras sea true.
boolean–
explanationSe pasa a la lista del popover.
string–
PropTipoPredeterminado
todosEl plan propuesto.
Todo[]–
onChangeSe llama en cada edición.
(todos: Todo[]) => void–
onApproveSe llama desde Run plan o ⌘↵, con los pasos vacíos eliminados y el texto recortado.
(todos: Todo[]) => void–
onCancelMuestra Cancel y gestiona Escape.
() => void–
titleEncabezado.
string"Review the plan"
approveLabelEtiqueta del botón.
string"Run plan"

getTodosFromParts(parts)

Devuelve { todos, explanation? } de la llamada más reciente a TodoWrite, update_plan o write_todos en un mensaje de AI SDK, o null.

PropTipoPredeterminado
partsFunciona con partes de herramienta estáticas y dinámicas, incluida la entrada que aún está en streaming.
UIMessage["parts"]–

applyTaskEvent(todos, event)

Incorpora una llamada TaskCreate o TaskUpdate a la lista. Un estado deleted elimina la tarea.

PropTipoPredeterminado
eventLa llamada, normalizada.
{ type: "create", id, subject, activeForm? } | { type: "update", id, status?, subject?, activeForm? }–
KeyAcción
EnterSpaceAbre o pliega la lista desde su encabezado, o los detalles de un paso.
⌘↵Ejecuta el plan mientras se revisa, salvo que estés escribiendo en otro campo.
EnterEn la revisión, añade un paso debajo del actual.
⌫En la revisión, elimina un paso vacío y pasa al anterior.
⌥↑En la revisión, sube el paso. ⌥↓ lo baja.
↑↓En la revisión, se mueve entre pasos.
EscCancela la revisión cuando onCancel está definido.
  • La lista es una región etiquetada con una lista ordenada, y el nombre de cada paso incluye su etapa, como «Ejecutando las pruebas, En curso».
  • Un progressbar oculto indica cuántos pasos están hechos.
  • Empezar, terminar y fallar un paso se anuncian mediante una región live polite, junto con los cambios del plan. Los avances del temporizador no se anuncian.
  • La etiqueta de la píldora de estado lee el paso actual y el contador, y abre la lista en un popover con gestión del foco.
  • Los campos de revisión se etiquetan por número de paso, los movimientos se anuncian («Movido al paso 2 de 5») y Enter espera mientras un método de entrada está componiendo.
  • Con movimiento reducido, el anillo en curso deja de girar, las marcas aparecen sin dibujarse y los pasos se colocan al instante.

Construido con

Los componentes gratuitos de HextaUI con los que está hecho Agent Todos. Cada uno se instala por separado.

Código

4 archivos, añadidos a components/blocks/agent-todos.