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

ai-site-pilot

v0.6.0

Published

AI chat widget that can control and navigate your website. Works with Google Gemini (direct) or any AI model via OpenRouter.

Readme

ai-site-pilot

npm version Ko-fi

AI chat widget that can control and navigate your website. Unlike typical chatbots that just answer questions, Site Pilot can take actions—scroll to sections, open modals, filter content, and more.

Works with any AI model (Gemini, GPT-4, Claude, Llama) via OpenRouter.

Features

  • 🎯 Tool System - Define custom actions the AI can take on your site
  • 🌊 Streaming - Real-time streaming responses
  • 🤖 Any Model - GPT-4, Claude, Gemini, Llama - just change one string
  • 🎤 Speech - Voice input and text-to-speech output
  • 🎨 Themeable - CSS variables for easy customization
  • 📱 Responsive - Works on all screen sizes

Installation

npm install ai-site-pilot

Setup

Tailwind CSS Configuration (Required)

Add ai-site-pilot to your Tailwind content config so classes are generated:

Tailwind v4 (CSS-based config):

/* In your main CSS file */
@import "tailwindcss";
@source "../../node_modules/ai-site-pilot/dist/**/*.{js,mjs}";

Tailwind v3 (JS config):

// tailwind.config.js
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx}',
    './components/**/*.{js,ts,jsx,tsx}',
    './node_modules/ai-site-pilot/dist/**/*.{js,mjs}',
  ],
}

Without this, the button text ("Ask AI") and other responsive styles won't work correctly.

Import Styles

Make sure to import the CSS file in your component:

import 'ai-site-pilot/styles.css';

Quick Start

1. Get an OpenRouter API Key

Sign up at openrouter.ai and get your API key. Add credits to your account for API usage.

2. Create the API Route

// app/api/chat/route.ts
import { createHandler } from 'ai-site-pilot/api';
import { defineTool } from 'ai-site-pilot/tools';

const navigateTool = defineTool({
  name: 'navigate',
  description: 'Navigate to a section of the page',
  parameters: {
    type: 'object',
    properties: {
      section: {
        type: 'string',
        description: 'Section to navigate to',
        enum: ['home', 'products', 'about', 'contact'],
      },
    },
    required: ['section'],
  },
});

export const POST = createHandler({
  model: 'google/gemini-2.5-flash-lite',  // Or use any OpenRouter model
  systemPrompt: `You are a helpful assistant for our website.
You can navigate users to different sections using the navigate tool.`,
  tools: [navigateTool],
});

3. Add the Component

// app/layout.tsx or components/ChatWidget.tsx
'use client';

import { SitePilot } from 'ai-site-pilot';
import 'ai-site-pilot/styles.css';

export function ChatWidget() {
  return (
    <SitePilot
      apiEndpoint="/api/chat"
      suggestions={[
        { text: 'Show me products', icon: '🛍️' },
        { text: 'Take me to contact', icon: '📧' },
      ]}
      onToolCall={(name, args) => {
        if (name === 'navigate') {
          document.getElementById(args.section as string)?.scrollIntoView({
            behavior: 'smooth',
          });
        }
      }}
    />
  );
}

4. Add Environment Variable

# .env.local
OPENROUTER_API_KEY=sk-or-...

That's it!

Available Models

Change the model string to use any model:

| Model | ID | Notes | |-------|-----|-------| | Gemini 2.5 Flash-Lite | google/gemini-2.5-flash-lite | Fast, affordable (default) | | GPT-4o | openai/gpt-4o | Best overall | | Claude 3.5 Sonnet | anthropic/claude-3.5-sonnet | Best for coding | | Llama 3.1 70B | meta-llama/llama-3.1-70b-instruct | Open source |

See all models at openrouter.ai/models

API Reference

createHandler()

Creates a Next.js API route handler. You can use either systemPrompt (manual) or siteContent (auto-generated).

Option 1: Auto-generated prompt with siteContent (Recommended)

import { createHandler } from 'ai-site-pilot/api';

export const POST = createHandler({
  model: 'google/gemini-2.5-flash-lite',
  siteContent: {
    name: 'Acme Dance Studio',
    type: 'dance studio',
    description: 'Premier dance education since 1995',
    personality: 'warm and encouraging',
    pages: ['home', 'classes', 'teachers', 'schedule', 'contact'],
    items: [
      { id: 'ballet', name: 'Ballet', category: 'class', description: 'Classical ballet for ages 3-adult', price: '$80/month' },
      { id: 'jazz', name: 'Jazz', category: 'class', description: 'High-energy jazz for ages 6+', price: '$75/month' },
      { id: 'sarah', name: 'Sarah Johnson', category: 'teacher', description: 'Owner & lead instructor, 15 years experience' },
    ],
    faqs: [
      { question: 'What should I wear?', answer: 'Leotard and ballet slippers for ballet, comfortable athletic wear for jazz.' },
    ],
    contact: {
      email: '[email protected]',
      phone: '555-1234',
      hours: 'Mon-Sat 9am-8pm',
    },
  },
  tools: [navigateTool, showClassTool],
});

The AI automatically knows about your content and can answer questions like "What classes do you offer?" with specific details.

Option 2: Manual systemPrompt

export const POST = createHandler({
  systemPrompt: 'You are a helpful assistant for Acme Dance Studio...',
  tools: [navigateTool],
});

Full options:

createHandler({
  // Content (use ONE of these)
  siteContent: { ... },        // Auto-generate prompt from your content
  systemPrompt: '...',         // Or write your own prompt

  // Optional
  apiKey: process.env.OPENROUTER_API_KEY,  // Uses env var by default
  model: 'google/gemini-2.5-flash-lite',
  tools: [myTool1, myTool2],
  temperature: 0.7,
  siteUrl: 'https://mysite.com',  // Shown in OpenRouter dashboard
  siteName: 'My Site',
});

<SitePilot />

Main chat widget component.

<SitePilot
  apiEndpoint="/api/chat"
  suggestions={[{ text: 'Help me', icon: '❓' }]}
  onToolCall={(name, args) => { /* handle tool calls */ }}
  theme={{ accent: 'pink' }}  // or accentColor: '#ec4899'
  features={{ speech: true, tts: true }}
  welcomeMessage="Hi! How can I help?"
  placeholder="Type a message..."
  defaultOpen={false}
/>

Theme Presets

Use the accent prop for easy theming:

| Preset | Color | |--------|-------| | amber | Default orange/amber | | pink | Hot pink | | blue | Primary blue | | green | Emerald green | | purple | Violet purple | | red | Coral red | | cyan | Teal cyan | | orange | Bright orange |

Or use accentColor with any hex color: accentColor: '#8b5cf6'

defineTool()

Helper for defining tools.

import { defineTool } from 'ai-site-pilot/tools';

const searchTool = defineTool({
  name: 'search_products',
  description: 'Search for products by query',
  parameters: {
    type: 'object',
    properties: {
      query: { type: 'string', description: 'Search terms' },
      category: { type: 'string', enum: ['electronics', 'clothing'] },
    },
    required: ['query'],
  },
});

Custom API Implementation

If you need to use a different AI provider, implement this SSE format:

data: {"type":"text","content":"Hello, "}
data: {"type":"text","content":"how can I help?"}
data: {"type":"tool","name":"navigate","args":{"section":"products"}}
data: {"type":"done"}

Use the built-in SSE helpers:

import { createSSEEncoder, getSSEHeaders } from 'ai-site-pilot/api';

export async function POST(req: Request) {
  const sse = createSSEEncoder();

  const stream = new ReadableStream({
    async start(controller) {
      controller.enqueue(sse.encodeText('Hello!'));
      controller.enqueue(sse.encodeTool('navigate', { section: 'products' }));
      controller.enqueue(sse.encodeDone());
      controller.close();
    },
  });

  return new Response(stream, { headers: getSSEHeaders() });
}

Handling Tool-Only Responses

When the AI calls tools without text, customize the fallback:

import { SitePilot, createFallbackMessageGenerator } from 'ai-site-pilot';

const generateFallback = createFallbackMessageGenerator({
  navigate: (args) => `Scrolled to **${args.section}**.`,
  filter: (args) => `Showing **${args.category}** items.`,
});

<SitePilot
  apiEndpoint="/api/chat"
  generateFallbackMessage={generateFallback}
/>

Requirements

  • React 18+ or React 19
  • Next.js 13+ (for API routes)
  • Tailwind CSS (for responsive styles)
  • OpenRouter API key (openrouter.ai)

Troubleshooting

Button only shows icon, no "Ask AI" text

Your Tailwind config isn't scanning the package.

Tailwind v4: Add to your CSS:

@source "../../node_modules/ai-site-pilot/dist/**/*.{js,mjs}";

Tailwind v3: Add to tailwind.config.js:

content: ['./node_modules/ai-site-pilot/dist/**/*.{js,mjs}']

Theme accent color not working

Make sure you're using the accent prop (preset) or accentColor prop (custom hex):

// Using preset
<SitePilot theme={{ accent: 'pink' }} />

// Using custom color
<SitePilot theme={{ accentColor: '#ec4899' }} />

Don't set CSS variables directly—use the component props.

Tools not executing

  1. Make sure you're handling tool calls in onToolCall:
<SitePilot
  onToolCall={(name, args) => {
    console.log('Tool called:', name, args);
    // Handle the tool...
  }}
/>
  1. Check browser console for errors

"I've made some changes" generic message

Use generateFallbackMessage to customize messages when the AI uses tools without text:

import { createFallbackMessageGenerator } from 'ai-site-pilot';

const generateFallback = createFallbackMessageGenerator({
  navigate: (args) => `Navigated to ${args.section}`,
  filter: (args) => `Filtered by ${args.category}`,
});

<SitePilot generateFallbackMessage={generateFallback} />

License

MIT