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

@campfire-interactive/ai-chat

v0.4.2

Published

The platform AI chat panel for Campfire Suite apps: a docked, page-aware assistant opened from the header, answering from every app the user is granted, with typed answer blocks and confirm-to-write cards.

Readme

@campfire-interactive/ai-chat

Private and unvetted until the pm exercise. Do not adopt it in an app yet; see CLAUDE.md.

The platform AI chat: an icon in the header opens a docked panel beside the page. The chat knows which page is open, offers chips that fit it, answers with typed blocks (prose, tables, charts, KPI tiles, links, buttons, questions, citations), and proposes small changes as cards the user confirms. It talks to the hub's /api/v1/assistant/* API directly with the user's token.

A live playground with a scripted hub is the showcase's AI chat page (npm run showcase, then #ai-chat).

Entries

| Entry | What it holds | |---|---| | . | The wire-contract types and createAssistantClient (chatStream, confirmWrite, listActions). No UI. | | /host | AiChatProvider, AiChatSurface, AiChatButton, useAiChat. |

Peers: react / react-dom 18 or 19, lucide-react (>= 0.460), recharts 2.15+ or 3. No CSS import: the stylesheet (cfi-ac-*, in @layer components) is injected by the bundle. Charts load lazily.

Wiring a host (pm)

import { AiChatProvider, AiChatSurface, AiChatButton } from '@campfire-interactive/ai-chat/host';

// App.tsx: above <Routes>, beside <PmHelp>.
<AiChatProvider
  app="pm"
  hubUrl={aiEnabled ? runtime.aiHubBaseUrl : undefined}   // undefined = chat off
  getToken={getAccessToken}
  uploadFile={uploadToDms}                                 // pm's dms-client upload, as WorkAttachments does; returns { documentId }
  pageContext={usePmPageContext()}                        // rebuilt on every navigation
  starters={PM_STARTERS}                                   // { 'program.risks': ['Which risks are overdue?'], … }
  navigate={(link) => (link.app === 'pm' ? navigate(link.path) : (window.location.href = getAppUrl(link.app) + link.path))}
>
  <Routes>…</Routes>
</AiChatProvider>

// AppShell.tsx
<ShellHeader rightActions={<AiChatButton />} … />
<div className="flex flex-1 overflow-hidden">
  <SideNav … />
  <main className="min-w-0 flex-1 overflow-auto">…</main>
  <CampfireHelpSurfaces />
  <AiChatSurface />
</div>

AiChatButton and AiChatSurface render nothing when the provider has no hubUrl, so a host gates only the provider.

Behavior worth knowing

  • Session. The thread lives in React state; navigation and closing the panel keep it. A reload ends it. "Start over" clears the thread, the dropped files and the session id.
  • History. Each request sends the last 8 turns as text. Blocks are reduced to one-line traces; a confirmed write appends Confirmed: <summary> (succeeded|failed) to that answer.
  • Starters are keyed by pageContext.page ('*' is the fallback). {placeholders} fill from the page's entities by type, then from filters, then {title}. A fully filled chip sends; otherwise it loads into the composer. They show in the empty state, and again above the composer once the page differs from where the last question was asked.
  • Files are kept in memory under a fileRef and offered on every request until attached. uploadFile runs only when the user confirms an attach card, immediately before confirmWrite({ token, documentId }).
  • Writes. Cancel, or any new message, discards every pending write. Nothing is stored, so nothing needs cleaning up.
  • Panel open state and width (360 to 900 px) are remembered per browser in localStorage under cfi.ai-chat.<app>.*.