Communitygithub.com

joshi-chinmay-016/AgentProof

Mejores prácticas para vincular herramientas con widgets de interfaz frontend interactivos usando @Widget y el SDK @nitrostack/widgets (incluyendo sincronización de estado, invocación de herramientas, modos de visualización, consultas de medios y acciones de chat).

¿Qué es AgentProof?

Este skill describe cómo construir y conectar widgets React interactivos a herramientas backend en plataformas como Claude Code, Cursor o Codex utilizando el decorador `@Widget` y el SDK `@nitrostack/widgets`. Está diseñado para desarrolladores que crean asistentes de IA o paneles de control en NitroStudio, permitiendo que la salida de herramientas se renderice en componentes frontend personalizados. El skill cubre desde la definición backend con `@Widget` hasta la implementación frontend con hooks como `useWidgetSDK` y `useWidgetState`, facilitando la sincronización de estado, la invocación de herramientas desde el widget (`callTool`), y el control del layout (pantalla completa, modo inline, picture-in-picture). También incluye utilidades de accesibilidad y medios (`prefersReducedMotion`, `isPrimarilyTouchDevice`) y la capacidad de navegar por el chat (`openExternal`, `sendFollowUpMessage`). Las pruebas se realizan visualmente en NitroStudio. Es una guía práctica para cualquier skill o herramienta que requiera una interfaz de usuario enriquecida dentro de un cliente de IA.

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

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Cuándo Usarlo

Usa este skill al diseñar, construir o modificar widgets de interfaz de usuario interactivos que muestren contenido React personalizado dentro de clientes de IA o NitroStudio.


1. Definición en Backend (@Widget)

Para mostrar un widget basado en React para la salida de una herramienta, decora el método de la herramienta con @Widget.

Opciones:

  • Ruta de cadena: Una cadena simple que representa el identificador de la ruta en la aplicación React frontend (ej. 'product-card').
  • Ruta de objeto: Objeto que incluye:
    • route (obligatorio): La ruta.
    • domain (opcional): Dominio de sandbox permitido.
    • csp (opcional): Directrices de Política de Seguridad de Contenido.

Ejemplo:

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

export class CatalogTools {
  @Tool({
    name: 'fetch_product',
    description: 'Obtén información del producto por código de barras.',
    inputSchema: z.object({ barcode: z.string() }),
  })
  @Widget('product-details') // Se asigna al componente frontend "product-details"
  async fetchProduct(input: { barcode: string }) {
    return {
      name: 'Super Nitro Energy Drink',
      price: 2.99,
      sku: input.barcode,
    };
  }
}

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

En tu aplicación frontend de widget React (normalmente un componente cliente Next.js), usa el hook useWidgetSDK para recibir datos de entrada del host cliente.

Ejemplo 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 al host...</div>;
  }

  if (!data) {
    return <div className="error">No se recibieron datos del producto.</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. Gestión y Sincronización de Estado (useWidgetState)

Usa useWidgetState para gestionar y persistir el estado del widget del lado del cliente (ej. pestañas seleccionadas, valores de filtro, estados de entrada). Este estado se sincroniza automáticamente con el contexto de la aplicación host, persistiendo a través de los re-renderizados de la página.

Ejemplo:

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>Pestaña Actual: {state?.selectedTab}</p>
    </div>
  );
}

4. Invocar Herramientas del Núcleo desde Widgets (callTool)

Puedes invocar otras herramientas MCP del backend directamente desde el widget frontend usando callTool. Esto es útil para encadenar herramientas o desencadenar auditorías detalladas.

Ejemplo:

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('La ejecución del diagnóstico falló.');
    } finally {
      setIsRunning(false);
    }
  };

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

5. Control de Diseño y Visualización

Los widgets pueden solicitar dinámicamente cambios en el modo de tamaño (pantalla completa, en línea, picture-in-picture) y adaptar los diseños a las áreas seguras (islas dinámicas/notch) o restricciones de altura máxima.

Métodos Clave:

  • requestFullscreen(): Cambia la visualización del widget host a pantalla completa.
  • requestInline(): Cambia la visualización del widget host de nuevo a en línea.
  • requestPip(): Flota el widget en modo Picture-in-Picture.
  • requestClose(): Descarta el widget por completo.

Ejemplo:

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

export default function StatusBoard() {
  const { 
    requestFullscreen, 
    requestInline, 
    requestClose,
    displayMode,   // Propiedad reactiva ('fullscreen' | 'inline' | 'pip')
    maxHeight,     // Restricción reactiva de maxHeight (en píxeles)
    getSafeArea    // Datos de márgenes: { 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}>Pantalla Completa</button>
      <button onClick={requestInline}>Colapsar</button>
      <button onClick={requestClose}>Descartar Widget</button>
    </div>
  );
}

6. Navegación y Acciones de Chat

Los widgets pueden interactuar con el panel de chat del host usando enlaces externos del navegador y mensajes de seguimiento.

Métodos Clave:

  • openExternal(url): Abre la URL de destino de forma segura en el navegador externo principal del usuario.
  • sendFollowUpMessage(prompt): Inserta un mensaje en el flujo de chat, enviándolo automáticamente al agente LLM.

Ejemplo:

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

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

  return (
    <div>
      {/* Abrir documentación externa */}
      <button onClick={() => openExternal('https://docs.nitrostack.dev')}>
        Abrir Manual
      </button>

      {/* Preguntar directamente al agente LLM desde el widget */}
      <button onClick={() => sendFollowUpMessage('Analiza las fallos de misión de la semana pasada')}>
        Preguntar al Agente
      </button>
    </div>
  );
}

7. Consultas de Medios y Accesibilidad

El SDK proporciona utilidades auxiliares para consultar las capacidades del cliente objetivo para estilos o accesibilidad.

Utilidades Clave:

  • prefersReducedMotion(): Devuelve true si la configuración del cliente especifica movimiento reducido. Desactiva animaciones.
  • isPrimarilyTouchDevice(): Devuelve true si el dispositivo tiene un puntero grueso (ej. táctil/móvil). Aumenta el tamaño de los botones.
  • isHoverAvailable(): Devuelve true si el puntero admite estados de hover.
  • prefersDarkColorScheme(): Devuelve true si el tema del sistema es oscuro.

Ejemplo:

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ón Interactivo
      </button>
    </div>
  );
}

8. Pruebas de Widgets

  • Abre tu proyecto en NitroStudio para una vista previa visual.
  • Invoca la herramienta desde el chat de IA o el panel de pruebas para verificar que el widget se actualice instantáneamente con la estructura JSON devuelta.

Individual skills in this repo

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

Skills relacionados