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

@hgdc/agents

v0.7.0

Published

Sites lisibles par les agents IA : version Markdown des pages (Accept: text/markdown), 404 en Markdown, routes Build Output API pour les sites prérendus.

Readme

@hgdc/agents

Rendre un site lisible par les agents IA : chaque page existe en Markdown, servie quand la requête accepte text/markdown (acceptmarkdown.com), avec Vary: Accept. Une page introuvable reste en 404, en Markdown pour un agent. Le HTML servi aux navigateurs ne change pas.

Sans dépendance. Le convertisseur vise le HTML qu'un produit rend lui-même (Astro, React SSR) : il lit le <main> (sinon le <body>) et laisse de côté ce qui est caché aux lecteurs d'écran (aria-hidden, visually-hidden, hidden), les scripts, les styles, les SVG et les formulaires.

App rendue côté serveur (TanStack Start)

TanStack Start répond 406 à une requête sans text/html ni */*. Le point d'entrée serveur enrobe donc son handler, avant le routeur :

// src/server.ts
import handler, { createServerEntry } from "@tanstack/react-start/server-entry"
import { linksFooter, withMarkdown } from "@hgdc/agents"

const fetch = withMarkdown(handler.fetch, {
  footer: linksFooter([{ label: "Résumé pour les agents", href: "https://app.hgdc.ch/mail/llms.txt" }]),
})

export default createServerEntry({ fetch })

Pour un site servi à la racine de son hôte (Status, Portal, Account), les options ajoutent ce que les agents vérifient avant de recommander un site :

import { HGDC_TRUST_PAGES, productJsonLd, withMarkdown } from "@hgdc/agents"

withMarkdown(handler.fetch, {
  footer,
  redirects: HGDC_TRUST_PAGES, // /about, /contact, /privacy → pages de hgdc.ch (301)
  jsonLd: productJsonLd({ name: "Status", url: "https://status.hgdc.ch", description: "…" }),
  canonicalOrigin: "https://status.hgdc.ch", // <link rel="canonical"> des pages en 200
  apiPrefix: "/api", // 404 sous /api en application/problem+json
})

Le JSON-LD et l'adresse canonique s'insèrent en flux avant </head>, seulement si la page n'en porte pas déjà : le rendu SSR en streaming n'est pas retenu au-delà du <head>.

Pour la découverte (0.5.0) :

withMarkdown(handler.fetch, {
  links: ['</llms.txt>; rel="describedby"', '</.well-known/api-catalog>; rel="api-catalog"'], // en-tête Link des pages en 200
  apiCatalog: [
    { anchor: "https://status.hgdc.ch/", "service-desc": [{ href: "https://status.hgdc.ch/openapi.json" }] },
  ], // RFC 9727
  markdownFiles: { "/auth.md": AUTH_MD }, // servis tels quels en text/markdown
})

Une page est convertie en Markdown, pour un GET ou un HEAD, quand la requête le préfère (prefersMarkdown : text/markdown accepté avec une qualité au moins égale à text/html ; un joker ne compte pas), quand le User-Agent est celui d'un robot d'IA connu (isAgentUserAgent : GPTBot, ClaudeBot, PerplexityBot et leurs variantes « -User » ; Googlebot garde le HTML), avec ?mode=agent, ou par son jumeau .md (/index.md, /tarifs.md) quand l'app ne sert pas elle-même ce fichier. Les pages portent Vary: Accept, User-Agent et, en 200, un Link vers leur alternative. Une redirection, un fichier ou du JSON passent tels quels.

WebMCP (navigateur)

import { registerSiteTools } from "@hgdc/agents/webmcp"

registerSiteTools({ prefix: "portal", name: "Portal", info: { llms: "https://portal.hgdc.ch/llms.txt" } })

Inscrit portal_infos et portal_page (page du site en Markdown) auprès du navigateur qui prend WebMCP en charge ; en lecture seule, sans effet ailleurs. À appeler une fois, côté client (effet du composant racine).

Site prérendu (Astro, sortie Build Output API)

À la fin du build, @hgdc/agents/build écrit <page>.md à côté de chaque page et donne les routes qui les servent, sur HGDC Host comme sur Vercel :

import { markdownRoutes, writeMarkdownPages } from "@hgdc/agents/build"

// astro:build:done ({ dir }), après l'adaptateur Vercel qui a écrit .vercel/output/config.json
const pages = await writeMarkdownPages(dir, { site: "https://hgdc.ch", footer })
const { before, miss } = markdownRoutes(pages, { site: "https://hgdc.ch" })
config.routes.unshift(...before) // avant { handle: "filesystem" }
config.routes.splice(-1, 0, ...miss) // juste avant la page 404 de l'adaptateur

base (/agence) sert un site publié sous un chemin : son accueil s'écrit <base>/index.md et sa 404 Markdown reste sous le préfixe.

Référence

| Export | Rôle | | ----------------------------------------------------------------- | --------------------------------------------------------- | | htmlToMarkdown(html, url) | Markdown du contenu d'une page, liens rendus absolus | | prefersMarkdown(accept) | La requête préfère-t-elle le Markdown ? | | withMarkdown(fetch, { footer }) | Handler fetch qui sert le Markdown aux agents | | linksFooter(links) | Pied de page : liens séparés par « · » | | HGDC_TRUST_PAGES, HGDC_ORGANIZATION, productJsonLd(product) | Pages de confiance et données structurées de l'entreprise | | injectIntoHead(body, extra) | Insère des balises avant </head> d'un flux HTML | | varyOnAccept(headers), varyOn(headers, name) | Ajoute un en-tête à Vary | | isAgentUserAgent, markdownTwin, pageOfTwin | Robots d'IA, jumeau .md d'une page | | apiCatalogResponse, appendLinks | Catalogue d'API (RFC 9727), en-tête Link | | writeMarkdownPages(dir, options) (/build) | Écrit les .md d'un build prérendu | | markdownRoutes(pages, options) (/build) | Routes Build Output API v3 qui les servent |