npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@emblemvault/hustle-react

v1.5.1

Published

React hooks and components for Hustle AI chat integration

Readme

@emblemvault/hustle-react

React hooks and components for AI chat using the Hustle Incognito SDK.

Installation

npm install @emblemvault/hustle-react

Quick Start

Web Application (Recommended)

Use the React component with Emblem Auth for a complete authentication experience:

import { EmblemAuthProvider, ConnectButton } from '@emblemvault/emblem-auth-react';
import { HustleProvider, HustleChat } from '@emblemvault/hustle-react';

function App() {
  return (
    <EmblemAuthProvider appId="your-app-id">
      <HustleProvider>
        <ConnectButton />
        <HustleChat showSettings />
      </HustleProvider>
    </EmblemAuthProvider>
  );
}

With API Key (Deprecated)

⚠️ Deprecated: API key authentication is deprecated and will be removed in a future version. Please migrate to the Emblem Auth React component for better security and user experience.

import { HustleProvider, HustleChat } from '@emblemvault/hustle-react';

function App() {
  return (
    <HustleProvider apiKey="your-key" vaultId="your-vault">
      <HustleChat showSettings />
    </HustleProvider>
  );
}

API Reference

HustleProvider

Provides chat context to your app.

<HustleProvider
  // With EmblemAuthProvider (recommended) - uses authentication context
  hustleApiUrl="https://agenthustle.ai"  // optional

  // Multi-instance support
  instanceId="unique-id"  // Scopes settings per instance
>
  {children}
</HustleProvider>

Props:

  • hustleApiUrl (optional) - Hustle API endpoint URL. Defaults to https://agenthustle.ai
  • instanceId (optional) - Unique identifier for multi-instance setups. Scopes settings and plugins per instance.
  • ~~apiKey~~ (deprecated) - API key for standalone auth. Deprecated: Use EmblemAuthProvider instead.
  • ~~vaultId~~ (deprecated) - Vault ID for standalone auth. Deprecated: Use EmblemAuthProvider instead.

useHustle

Access chat state and methods.

const {
  // State
  isReady,         // boolean - client ready to chat
  isLoading,       // boolean
  error,           // Error | null
  models,          // Model[] - available AI models
  client,          // HustleIncognitoClient | null

  // Chat methods
  chat,            // (options) => Promise<ChatResponse>
  chatStream,      // (options) => AsyncIterable<StreamChunk>
  uploadFile,      // (file) => Promise<Attachment>
  loadModels,      // () => Promise<Model[]>

  // Settings (persisted to localStorage)
  selectedModel,      // string
  setSelectedModel,   // (model: string) => void
  systemPrompt,       // string
  setSystemPrompt,    // (prompt: string) => void
  skipServerPrompt,   // boolean
  setSkipServerPrompt, // (skip: boolean) => void
} = useHustle();

HustleChat

Complete chat UI component.

<HustleChat
  showSettings           // Show settings modal button
  showDebug              // Show tool call debug info
  placeholder="Message"  // Input placeholder
  initialSystemPrompt="" // Initial system prompt
  enableSpeechToText     // Enable voice input via microphone (default: false)

  // Callbacks
  onMessage={(msg) => {}}     // When user sends message
  onToolCall={(tool) => {}}   // When AI calls a tool
  onResponse={(content) => {}} // When AI responds
/>

HustleChatWidget

Floating chat widget for site-wide chatbot integration. Perfect for customer support or AI assistants that persist across page navigations.

import { EmblemAuthProvider } from '@emblemvault/emblem-auth-react';
import { HustleProvider, HustleChatWidget } from '@emblemvault/hustle-react';

// Add to your Next.js layout for site-wide availability
export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <EmblemAuthProvider appId="your-app-id">
          <HustleProvider>
            {children}
            <HustleChatWidget
              config={{
                position: 'bottom-right',  // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
                size: 'md',                // 'sm' | 'md' | 'lg' | 'xl' | 'full'
                title: 'Support',          // Header title
                defaultOpen: false,        // Start open?
                offset: { x: 24, y: 24 },  // Edge offset in pixels
                storageKey: 'chat-open',   // localStorage key for persistence (false to disable)
                showBadge: true,           // Show notification badge
                badgeContent: 3,           // Badge content
                onOpen: () => {},          // Called when widget opens
                onClose: () => {},         // Called when widget closes
              }}
              placeholder="How can we help?"
              showSettings
            />
          </HustleProvider>
        </EmblemAuthProvider>
      </body>
    </html>
  );
}

Widget Configuration

| Option | Type | Default | Description | |--------|------|---------|-------------| | position | 'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left' | 'bottom-right' | Screen position | | size | 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full' | 'md' | Chat panel size | | title | string | 'Chat' | Panel header title | | defaultOpen | boolean | false | Whether widget starts open | | offset | { x: number, y: number } | { x: 24, y: 24 } | Offset from screen edge | | storageKey | string \| false | 'hustle-widget-open' | Key for persisting state | | showBadge | boolean | false | Show notification badge | | badgeContent | string \| number | - | Badge content | | launcherIcon | React.ReactNode | Chat bubble | Custom launcher icon | | launcherStyle | React.CSSProperties | - | Custom launcher styles | | panelStyle | React.CSSProperties | - | Custom panel styles | | zIndex | number | 9999 | Widget z-index | | onOpen | () => void | - | Called when widget opens | | onClose | () => void | - | Called when widget closes |

Plugins

Extend the AI with custom tools.

Using Built-in Plugins

import { usePlugins, availablePlugins } from '@emblemvault/hustle-react';

function PluginManager() {
  const { plugins, registerPlugin, enablePlugin, disablePlugin } = usePlugins();

  return (
    <div>
      {availablePlugins.map(plugin => (
        <button onClick={() => registerPlugin(plugin)}>
          Install {plugin.name}
        </button>
      ))}
    </div>
  );
}

Creating Custom Plugins

import type { HustlePlugin } from '@emblemvault/hustle-react';

const weatherPlugin: HustlePlugin = {
  name: 'weather',
  version: '1.0.0',
  description: 'Get weather information',
  enabled: true,
  tools: [{
    name: 'get_weather',
    description: 'Get weather for a city',
    parameters: {
      type: 'object',
      properties: {
        city: { type: 'string', description: 'City name' },
      },
      required: ['city'],
    },
  }],
  executors: {
    get_weather: async ({ city }) => {
      const response = await fetch(`/api/weather?city=${city}`);
      return response.json();
    },
  },
};

// Register it
const { registerPlugin } = usePlugins();
registerPlugin(weatherPlugin);

Plugin Hooks

const plugin: HustlePlugin = {
  // ... name, tools, executors

  // Modify messages before sending
  beforeRequest: (messages) => {
    return messages.map(m => ({
      ...m,
      content: m.content.replace(/secret/gi, '[REDACTED]'),
    }));
  },

  // Modify response after receiving
  afterResponse: (response) => {
    return {
      ...response,
      content: response.content + '\n\n---\nPowered by MyPlugin',
    };
  },
};

Multi-Instance Support

Run multiple isolated chat instances with separate settings and plugins:

import { EmblemAuthProvider } from '@emblemvault/emblem-auth-react';
import { HustleProvider, HustleChat } from '@emblemvault/hustle-react';

function App() {
  return (
    <EmblemAuthProvider appId="your-app-id">
      <HustleProvider instanceId="trading-bot">
        <HustleChat />
      </HustleProvider>

      <HustleProvider instanceId="support-bot">
        <HustleChat />
      </HustleProvider>
    </EmblemAuthProvider>
  );
}

Each instance has separate settings and plugins stored under its unique instanceId.

Speech-to-Text

Enable voice input in HustleChat via prop, settings modal, or use the hook directly for custom implementations.

Using HustleChat

Via prop:

<HustleChat enableSpeechToText />

Via settings modal: When showSettings is enabled, users can toggle "Voice Input" in the settings modal. This preference is persisted to localStorage.

<HustleChat showSettings />

When enabled, a microphone button appears next to the send button. Click to start listening, click again to stop. The button pulses red while actively listening.

Using the Hook Directly

import { useSpeechRecognition } from '@emblemvault/hustle-react';

function CustomInput() {
  const [message, setMessage] = useState('');

  const {
    isSupported,  // Browser supports Web Speech API
    isListening,  // Currently listening
    toggleListening,
  } = useSpeechRecognition({
    onTranscript: (text) => setMessage(prev => prev + ' ' + text),
    onInterimTranscript: (text) => console.log('Interim:', text),
    onError: (error) => console.error('Speech error:', error),
    continuous: true,   // Keep listening (default: true)
    language: 'en-US',  // Recognition language (default: 'en-US')
  });

  if (!isSupported) return <p>Speech not supported</p>;

  return (
    <div>
      <input value={message} onChange={e => setMessage(e.target.value)} />
      <button onClick={toggleListening}>
        {isListening ? 'Stop' : 'Speak'}
      </button>
    </div>
  );
}

Underlying SDK

This package wraps hustle-incognito for React. The SDK handles:

  • Chat API communication
  • Streaming responses
  • Tool execution
  • Model selection

For advanced usage, access the client directly via useHustle().client.

License

MIT