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

@useguano/guano

v0.1.13

Published

Self-hosted visual website builder: live canvas, layers tree, components, CMS collections, static publishing.

Downloads

1,383

Readme

guano

Self-hosted visual website builder: build pages on a live canvas with a layers tree, style with Tailwind classes, add interactions, reusable components, CMS collections and locales — then publish a fully static site (plain HTML + one CSS file + a ~1.5 KB runtime) served by the built-in server, downloaded as a zip, or pushed to GitHub.

npm create @useguano   # scaffolds a folder and installs
cd my-site && npm run dev
# or, without a project folder:
npx -y @useguano/guano dev

Open http://localhost:4174/admin, create the first (admin) account, build. Your published site is at http://localhost:4174/.

CLI

  • guano dev — start the server for local editing
  • guano start — production server (NODE_ENV=production)
  • guano build [--out dir] — export the current project as static files (default ./dist-site)
  • guano mcp — run the MCP server (stdio) so AI agents can edit the site (see MCP)
  • guano connect — issue an API token to a local instance, for guano mcp

Environment

| Variable | Default | Purpose | |---|---|---| | PORT | remembered, else 4174 | server port. Without it the port this data dir last landed on is preferred, so two sites on one machine keep their own addresses | | GUANO_DATA_DIR | ./data under node_modules, else server/data | users, media, drafts, published site — backup = copy this dir; on ephemeral hosts point it at a volume | | COOKIE_SECURE | 1 | session cookie Secure flag; set 0 only for plain-HTTP LAN setups | | PUBLISH_TOKEN | unset | when set, allows token-authenticated CI publishes | | MEDIA_QUOTA | 2 GiB | media library ceiling, bytes | | STORE_QUOTA | 512 MiB | project storage ceiling, bytes | | TRUST_PROXY | unset | set 1 only behind a reverse proxy — every rate limit then keys on the last X-Forwarded-For hop instead of the socket address | | PORT_STRICT | unset | 1 refuses to walk to the next free port when PORT is busy | | LOG_LEVEL | info | debug | info | warn | error; warn drops the per-request access line | | LOG_JSON | unset | 1 emits one JSON object per line, for a log shipper | | SHUTDOWN_GRACE_MS | 0 | how long to keep failing health checks before the socket closes, so a load balancer can drain first | | GUANO_URL | http://localhost:4174 | guano mcp only — the running instance to connect to | | GUANO_TOKEN | unset | guano mcp only — an API token (see MCP) | | GUANO_INSTANCE | unset | guano mcp only — which project this entry was connected to. guano connect writes it; a mismatch against the server's own id is refused by name instead of failing with a 401 |

The admin needs a persistent Node process with a writable disk (VPS, Railway, Fly.io, Render…). It cannot run on Vercel/Netlify — but your published site is fully static and can be hosted anywhere.

Updating

A site is an ordinary npm project, so the thing you update is the package:

cd my-site
npm update @useguano/guano   # within the pinned range: ^0.1.0 is 0.1.x
npm start                    # the running process keeps serving until you restart it
guano --version              # what you are now on

npm update @useguano does nothing — @useguano is the npm scope, not a package name. To cross a minor (0.1.x → 0.2.0), widen the range yourself with npm install @useguano/guano@latest.

Your site is not in the package: it lives in $GUANO_DATA_DIR (./data), which npm never touches. Two things to do around an update:

  • Back up data/ first, or take a snapshot in Settings → Snapshots. The server brings every project blob up to the current schema at boot, and a schema change is one-way.
  • Publish once afterwards. What visitors get is the HTML already on disk in data/site, exported by the version you just replaced, and the runtimes it loads (/assets/script.js, motion.js, slider.js) are copied out of the package at export time. Until you publish again, a fix in the exporter or in a runtime reaches nobody.

MCP (AI agents)

guano mcp exposes a running Guano instance to AI agents over the Model Context Protocol. The agent edits your site through the same authenticated HTTP API the editor uses and shares the editor's own document logic, so structure edits preserve element identity, styling, and interactions exactly as if you'd typed them.

Claude Desktop: already done by the scaffolder

npm create @useguano asks whether to connect Claude Desktop and the two permission questions a connected agent needs answered — may it edit the live project (Main)? and may it publish? — and does the connect right there, before the server has ever run: it mints an API token into ./data, writes the permissions beside it, and writes the mcpServers entry into Claude Desktop's config. The token is pending until you create the admin account at /admin, which binds it; the setup form shows the permissions you chose instead of asking again. Quit Claude Desktop before scaffolding (the app overwrites its config from memory on quit) — the scaffolder waits for you if it is open.

To connect later, or from another machine running the instance:

npx guano connect   # or `npm run connect` in a scaffolded project

The guano binary is a project dependency, not a global install, so it is reached through npx (or an npm script) rather than typed bare. It mints the token (through the running server, or straight into the data dir when the server is down — --offline never asks the network), asks the same two questions unless --main / --publish answer them (--yes skips them and leaves the stored policy alone), and writes Claude Desktop's config. When Main is allowed it also sets GUANO_MCP_TARGET=main, so the agent starts working instead of asking which target to use. Change either permission later in Settings → MCP → Agent permissions. guano connect --print emits the JSON snippet instead, for any other MCP client or a remote instance.

Manual setup (other clients / remote instances)

1. Create an API token — in the editor: My account → API tokens → create. Copy the guano_… token (shown once). It carries your role — admin or editor only (contributors can't).

2. Register the server — it talks to your instance over HTTP; point it at the URL and token. For example, with Claude Code:

claude mcp add guano -- npx -y @useguano/guano mcp

then set the environment in your MCP client config:

{
  "mcpServers": {
    "guano-my-site": {
      "command": "npx",
      "args": ["-y", "@useguano/guano", "mcp"],
      "env": {
        "GUANO_URL": "http://localhost:4174",
        "GUANO_TOKEN": "guano_your_token_here"
      }
    }
  }
}

Use your deployed URL (e.g. https://studio.example.com) for a hosted instance.

One entry per site. guano connect names it guano-<slug> for exactly this reason: a single shared key means connecting a second site overwrites the first one's entry, and since both would point at the same default port the survivor talks to whichever instance booted first — which answers its token with a 401. Give each site its own PORT and its own entry. guano connect also writes GUANO_INSTANCE, which lets guano mcp tell you that another project is holding the port instead of failing with "not connected"; add it by hand (the instance field of GET /api/health) if you write the config yourself.

3. Main vs. a draft — the agent asks you first

Writes go to a target you choose: Main (the live project) or a draft (a safe branch you later apply from the editor's Drafts panel). The store is latest-wins, so editing Main while you're also in the editor can clobber work — drafts are the safe mode. The agent must call set_target before any write, and its tool prompt tells it to ask you which to use. Creating a draft over MCP produces exactly the shape the editor opens, and never changes which branch your editor is viewing.

Tools

get_guide (the agent handbook — also served as MCP instructions) · get_status, set_target · list_pages, get_page, set_page_html, edit_structure, create_page, delete_page, set_page_seo · edit_elements (batch: classes, text content, media src/background, attributes, html id) · list_components, create_component, update_component, duplicate_component, detach_instance, delete_component, set_component_variants, create_components · get_settings, update_settings (design tokens, SEO defaults, fonts, custom head) · list_interactions, create_interactions, update_interaction, delete_interaction, bind_interaction, unbind_interaction · list_animations, create_animations, update_animation, delete_animation · list_collections, get_collection, create_collection, update_collection, delete_collection, upsert_entries, delete_entry · get_translation_worklist, set_translations · list_media, upload_media, list_icons · list_comments, create_comment, reply_to_comment · list_form_submissions, list_integrations · preview, publish, update_page.

There is no bundled component library: every component is the project's own, made in the editor or by an agent with create_component.

Pages are read and written as HTML — a strict subset where <Card> is a component instance and data-ref is a stable address, so one write carries structure, classes and text together. edit_structure changes part of a page (insert/replace/move/remove/wrap) without resending it.

Page/element edits are guarded by a version hash from get_page — a stale write (the page changed underneath) is rejected rather than clobbering. Markup that does not parse is refused with a line:col; a document that parses but has a problem (an unknown collection, a duplicate ref) saves and reports it.