Agent Todos

Mostre o plano de um agente enquanto ele trabalha. Cada etapa passa de backlog para a fazer, em andamento e concluída, com tempos ao vivo, falhas e as chamadas de ferramenta por trás dela. Uma pílula de status para acima do composer, mudanças no plano que você consegue ver e uma etapa de revisão para editar o plano antes de executá-lo.

Agentes de código como Claude Code, Codex e Gemini CLI mantêm um plano escrito enquanto trabalham, por meio de ferramentas como TodoWrite, update_plan e write_todos. O Agent Todos transforma esse plano em uma linha por etapa, com a etapa em andamento descrita por extenso ("Executando os testes") e quanto tempo cada uma levou.

Cada etapa passa de backlog para a fazer, em andamento e, depois, concluída, com falha ou cancelada. Quando o agente reescreve o plano, as novas etapas são destacadas, as removidas se recolhem e uma nota curta diz o que mudou. Uma etapa com falha mantém o motivo à vista, e longas sequências de etapas concluídas se dobram para que o trabalho atual continue na tela.

AgentTodosStatus coloca a etapa atual e uma contagem em uma linha acima do composer, e AgentTodosReview permite que as pessoas editem, reordenem, adicionem e removam etapas antes de qualquer execução e depois iniciem com ⌘↵. Os adaptadores leem o formato de ferramenta de cada agente, inclusive entradas que ainda estão em streaming, e o Tool Calls pode mostrar o trabalho dentro de qualquer etapa.

  1. Adicione o registro Pro ao components.json

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

    Crie um token na sua página de conta e coloque-o em .env.local como HEXTAUI_PRO_TOKEN.

  3. Adicione o bloco

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

Com o AI SDK

getTodosFromParts lê a chamada TodoWrite, update_plan ou write_todos mais recente em uma mensagem, mesmo enquanto sua entrada ainda está em streaming. Mostre a lista na mensagem e a pílula de status acima do composer.

"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 executar

Permita que as pessoas editem, reordenem, adicionem e removam etapas antes de o agente começar. Run plan ou ⌘↵ devolve a lista já ajustada.

"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} />
}

Com ferramentas de tarefa incrementais

Para ferramentas que criam e atualizam uma tarefa por vez, como TaskCreate e TaskUpdate do Claude Code, incorpore cada chamada à lista com 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} />
}

Anatomia

As partes que você compõe, de fora para dentro.

ParteDescrição
AgentTodosA lista completa com cabeçalho, barra de progresso, explicação e nota de mudança no plano.
AgentTodosStatusUma pílula de uma linha com a etapa atual, que abre a lista em um popover.
AgentTodosReviewO plano editável exibido antes de o agente começar.
TodoMarkA marca de status sozinha, para layouts personalizados.
getTodosFromParts, fromTodoWrite, fromUpdatePlan, fromWriteTodos, applyTaskEventAdaptadores do formato de ferramenta de cada agente para itens Todo.
PropTipoPadrão
todosEtapas em ordem.
Todo[]–
explanationPor que o plano está como está, exibido sob a barra de progresso. O Codex envia isso com update_plan.
string–
runningSe o agente está trabalhando. O padrão é true enquanto houver alguma etapa em andamento.
boolean–
titleRótulo do cabeçalho e nome da região.
string"Tasks"
collapsiblePermite que o cabeçalho dobre a lista.
booleantrue
defaultOpenComeça aberto.
booleantrue
foldAfterDobra as etapas concluídas no topo quando a lista for maior que isto.
number6
PropTipoPadrão
idId estável. As mudanças no plano são detectadas pelo id.
string–
contentO que fazer, no imperativo: "Execute os testes".
string–
activeFormO que está fazendo, exibido enquanto em andamento: "Executando os testes".
string–
statusO estágio. pending é lido como A fazer.
"backlog" | "pending" | "in_progress" | "completed" | "failed" | "cancelled"–
errorExibido sob uma etapa com falha.
string–
durationSegundos armazenados, para o histórico.
number–
detailsAbre sob a etapa, normalmente o Tool Calls.
ReactNode–
PropTipoPadrão
todosA mesma lista. Não renderiza nada quando vazia.
Todo[]–
runningAnima a marca e o shimmer enquanto for true.
boolean–
explanationPassado para a lista no popover.
string–
PropTipoPadrão
todosO plano proposto.
Todo[]–
onChangeChamado a cada edição.
(todos: Todo[]) => void–
onApproveChamado por Run plan ou ⌘↵, com as etapas vazias removidas e o texto aparado.
(todos: Todo[]) => void–
onCancelMostra Cancel e trata o Escape.
() => void–
titleTítulo.
string"Review the plan"
approveLabelRótulo do botão.
string"Run plan"

getTodosFromParts(parts)

Retorna { todos, explanation? } da chamada TodoWrite, update_plan ou write_todos mais recente em uma mensagem do AI SDK, ou null.

PropTipoPadrão
partsFunciona com partes de ferramenta estáticas e dinâmicas, inclusive entradas que ainda estão em streaming.
UIMessage["parts"]–

applyTaskEvent(todos, event)

Incorpora uma chamada TaskCreate ou TaskUpdate à lista. Um status deleted remove a tarefa.

PropTipoPadrão
eventA chamada, normalizada.
{ type: "create", id, subject, activeForm? } | { type: "update", id, status?, subject?, activeForm? }–
TeclaAção
EnterSpaceAbre ou dobra a lista a partir do cabeçalho, ou os detalhes de uma etapa.
⌘↵Executa o plano enquanto ele está em revisão, a menos que você esteja digitando em outro campo.
EnterNa revisão, adiciona uma etapa abaixo da atual.
⌫Na revisão, remove uma etapa vazia e vai para a de cima.
⌥↑Na revisão, move a etapa para cima. ⌥↓ a move para baixo.
↑↓Na revisão, move entre as etapas.
EscCancela a revisão quando onCancel está definido.
  • A lista é uma região rotulada com uma lista ordenada, e o nome de cada etapa inclui seu estágio, como "Executando os testes, Em andamento".
  • Uma progressbar oculta informa quantas etapas foram concluídas.
  • Iniciar, concluir e falhar uma etapa são anunciados por uma região live polite, junto com as mudanças no plano. Os ticks do cronômetro não são anunciados.
  • O rótulo da pílula de status lê a etapa atual e a contagem, e ela abre a lista em um popover com gerenciamento de foco.
  • Os campos da revisão são rotulados pelo número da etapa, as movimentações são anunciadas ("Movida para a etapa 2 de 5") e o Enter espera enquanto um método de entrada está compondo texto.
  • Com movimento reduzido, o anel de em andamento para de girar, os checks aparecem sem ser desenhados e as etapas deslizam para o lugar instantaneamente.

Construído com

Os componentes gratuitos do HextaUI de que Agent Todos é feito. Cada um é instalado separadamente.

Código

4 arquivos, adicionados a components/blocks/agent-todos.