Communitygithub.com

joshi-chinmay-016/AgentProof

Melhores práticas para conectar ferramentas a widgets interativos de frontend usando os SDKs @Widget e @nitrostack/widgets (incluindo sincronização de estado, chamada de ferramentas, modos de exibição, consultas de mídia e ações de chat).

O que é AgentProof?

Esta skill fornece um guia completo para desenvolvedores que desejam criar widgets React interativos que se integram perfeitamente com clientes de IA (como Claude, Cursor, Codex) e NitroStudio. Ela cobre desde a definição de ferramentas backend com o decorator `@Widget` até a construção de componentes frontend reativos usando o hook `useWidgetSDK` do pacote `@nitrostack/widgets`. A skill também aborda gerenciamento de estado persistente com `useWidgetState`, chamada de ferramentas diretamente do frontend (`callTool`), controle de layout e modos de exibição (tela cheia, inline, picture-in-picture), além de recursos avançados como navegação por chat, abertura de links externos e consultas de acessibilidade/mídia (`prefersReducedMotion`, `isPrimarilyTouchDevice`). Ela é útil para qualquer desenvolvedor que queira construir interfaces ricas e interativas que se comportam como widgets embutidos no fluxo de um agente de IA, garantindo sincronização de estado, responsividade e boas práticas de acessibilidade.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/joshi-chinmay-016/AgentProof/tree/HEAD/agentproof/.agents/skills/ui-widgets

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Quando Usar

Use esta skill ao projetar, construir ou modificar widgets de interface de usuário interativos que exibem conteúdo React personalizado dentro de clientes de IA ou NitroStudio.


1. Definição no Backend (@Widget)

Para exibir um widget baseado em React para a saída de uma ferramenta, decore o método da ferramenta com @Widget.

Opções:

  • Rota String: Uma string simples representando o identificador da rota no aplicativo React de frontend (ex.: 'product-card').
  • Rota Objeto: Objeto incluindo:
    • route (obrigatório): O caminho da rota.
    • domain (opcional): Domínio de sandbox permitido.
    • csp (opcional): Diretrizes de Política de Segurança de Conteúdo.

Exemplo:

import { Tool, Widget, z } from '@nitrostack/core';

export class CatalogTools {
  @Tool({
    name: 'fetch_product',
    description: 'Get product information by barcode.',
    inputSchema: z.object({ barcode: z.string() }),
  })
  @Widget('product-details') // Mapeia para o componente frontend "product-details"
  async fetchProduct(input: { barcode: string }) {
    return {
      name: 'Super Nitro Energy Drink',
      price: 2.99,
      sku: input.barcode,
    };
  }
}

2. Widget React no Frontend (@nitrostack/widgets)

Em seu aplicativo frontend de widget React (tipicamente um componente cliente Next.js), use o hook useWidgetSDK para receber dados de entrada do host cliente.

Exemplo de Componente React:

'use client';

import React from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';

interface ProductData {
  name: string;
  price: number;
  sku: string;
}

export default function ProductDetailsWidget() {
  const { isReady, getToolOutput, theme } = useWidgetSDK();
  const data = getToolOutput<ProductData>();

  if (!isReady) {
    return <div className="loading">Conectando ao host...</div>;
  }

  if (!data) {
    return <div className="error">Nenhum dado de produto recebido.</div>;
  }

  return (
    <div className={`product-card ${theme === 'dark' ? 'dark' : 'light'}`}>
      <h3>{data.name}</h3>
      <p className="price">${data.price.toFixed(2)}</p>
      <span className="sku">SKU: {data.sku}</span>
    </div>
  );
}

3. Gerenciamento e Sincronização de Estado (useWidgetState)

Use useWidgetState para gerenciar e persistir o estado do widget no lado do cliente (ex.: abas selecionadas, valores de filtro, estados de entrada). Esse estado é sincronizado automaticamente com o contexto do aplicativo host, persistindo entre re-renderizações de página.

Exemplo:

import React from 'react';
import { useWidgetState } from '@nitrostack/widgets';

export default function StationPanelWidget() {
  const [state, setState] = useWidgetState(() => ({
    selectedTab: 'overview',
    showExtendedInfo: false,
  }));

  return (
    <div>
      <button onClick={() => setState({ ...state, selectedTab: 'alerts' })}>
        Ver Alertas
      </button>
      <p>Aba Atual: {state?.selectedTab}</p>
    </div>
  );
}

4. Chamando Ferramentas Principais a partir de Widgets (callTool)

Você pode invocar outras ferramentas MCP do backend diretamente do widget frontend usando callTool. Isso é útil para encadeamento de ferramentas ou acionamento de auditorias detalhadas.

Exemplo:

import React, { useState } from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';

export default function SystemDiagnostics() {
  const { callTool, isReady } = useWidgetSDK();
  const [isRunning, setIsRunning] = useState(false);
  const [result, setResult] = useState<string | null>(null);

  const runDiagnostic = async () => {
    if (!isReady) return;
    setIsRunning(true);
    try {
      const response = await callTool('run_diagnostic', { system: 'oxygen_scrubber' });
      setResult(response.result as string);
    } catch (err) {
      setResult('Falha na execução do diagnóstico.');
    } finally {
      setIsRunning(false);
    }
  };

  return (
    <button onClick={runDiagnostic} disabled={isRunning}>
      {isRunning ? 'Executando...' : 'Executar Diagnóstico'}
    </button>
  );
}

5. Controles de Layout e Exibição

Os widgets podem solicitar dinamicamente mudanças no modo de tamanho (tela cheia, inline, picture-in-picture) e adaptar layouts a áreas seguras (ilhas dinâmicas/notches) ou restrições de altura máxima.

Métodos Principais:

  • requestFullscreen(): Muda a exibição do widget host para tela cheia.
  • requestInline(): Muda a exibição do widget host de volta para inline.
  • requestPip(): Flutua o widget em Picture-in-Picture.
  • requestClose(): Dispensa o widget completamente.

Exemplo:

import React from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';

export default function StatusBoard() {
  const { 
    requestFullscreen, 
    requestInline, 
    requestClose,
    displayMode,   // Propriedade reativa ('fullscreen' | 'inline' | 'pip')
    maxHeight,     // Restrição reativa de maxHeight (em pixels)
    getSafeArea    // Dados de insets: { top, right, bottom, left }
  } = useWidgetSDK();

  const safeArea = getSafeArea() || { top: 0, bottom: 0 };

  return (
    <div style={{ maxHeight: maxHeight || 400, paddingTop: safeArea.top }}>
      <h3>Modo: {displayMode}</h3>
      <button onClick={requestFullscreen}>Tela Cheia</button>
      <button onClick={requestInline}>Recolher</button>
      <button onClick={requestClose}>Fechar Widget</button>
    </div>
  );
}

6. Navegação e Ações no Chat

Os widgets podem interagir com o painel de chat do host usando links de navegador externos e prompts de acompanhamento.

Métodos Principais:

  • openExternal(url): Abre a URL de destino com segurança no navegador externo principal do usuário.
  • sendFollowUpMessage(prompt): Insere uma mensagem no fluxo de chat, enviando-a automaticamente para o agente LLM.

Exemplo:

import React from 'react';
import { useWidgetSDK } from '@nitrostack/widgets';

export default function MissionControl() {
  const { openExternal, sendFollowUpMessage } = useWidgetSDK();

  return (
    <div>
      {/* Abrir documentação externa */}
      <button onClick={() => openExternal('https://docs.nitrostack.dev')}>
        Abrir Manual
      </button>

      {/* Perguntar ao agente LLM diretamente do widget */}
      <button onClick={() => sendFollowUpMessage('Analyze mission failures from last week')}>
        Pedir Análise ao Agente
      </button>
    </div>
  );
}

7. Consultas de Mídia e Acessibilidade

O SDK fornece utilitários auxiliares para consultar as capacidades do cliente alvo para estilização ou acessibilidade.

Principais Utilitários:

  • prefersReducedMotion(): Retorna true se as configurações do cliente especificarem movimento reduzido. Desabilite animações.
  • isPrimarilyTouchDevice(): Retorna true se o dispositivo tiver um ponteiro grosseiro (ex.: toque/mobile). Aumente os tamanhos alvo dos botões.
  • isHoverAvailable(): Retorna true se o ponteiro suportar estados de hover.
  • prefersDarkColorScheme(): Retorna true se o tema do sistema for escuro.

Exemplo:

import React from 'react';
import { isPrimarilyTouchDevice, prefersReducedMotion } from '@nitrostack/widgets';

export default function AccessiblePanel() {
  const isTouch = isPrimarilyTouchDevice();
  const reducedMotion = prefersReducedMotion();

  return (
    <div className={reducedMotion ? 'no-animations' : 'smooth-transitions'}>
      <button style={{ padding: isTouch ? '16px' : '8px' }}>
        Botão Interativo
      </button>
    </div>
  );
}

8. Testando Widgets

  • Abra seu projeto no NitroStudio para uma pré-visualização visual.
  • Invoque a ferramenta a partir do chat de IA ou do painel de teste para verificar se o widget é atualizado instantaneamente com a estrutura JSON retornada.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Habilidades Relacionadas