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

@bud.sh/app

v0.8.1

Published

The app, its config, screen and design APIs

Readme

@bud.sh/app

Bud is a local companion for reviewing a web project: the app itself, its config, screen, design and adapter APIs, and the companion server a host starts. Its Vite plugin, @bud.sh/app/vite, hosts it beside your Vite dev server, and the package's bud CLI runs it without Vite. Bud browses your live pages, shows your routes side by side on a canvas, lets you browse, edit and review files, and keeps discussions on pages, code and tasks through your selected storage adapters. The interface uses Remix 3; your project can use any framework that runs on Vite.

Features

  • Pages - Your configured routes beside a browser of the live site, with tabs and history per workspace tab, and comments on elements you pick
  • Canvas - Every configured route as an always-current screenshot in light and dark, with comments whose pins follow their element
  • Branches - Screenshots of main and every activated branch beside the working tree, each built from its latest commit outside your checkout
  • Pickers - Comments record the element, its block and, with bud:react-grab, bud:remix or your own picker, its component and source line
  • Files - File tree, editor, diffs, history, staging and conflict resolution
  • Code comments - Discussions on selected lines that follow edits and stay readable when outdated
  • Tasks - Comments become tasks on a board that people and agents change live, with Beads as the default store; task designs can add fields such as priority or assignee
  • Agents - Comments become tasks that a coding agent (OpenCode) finishes in a sandbox, a local worktree or boat.dev, on its own dev/ branch: Agent view shows the live preview beside the agent's terminal and chat, the Agents screen lists every session, and nothing is pushed until someone approves
  • Hosting - The canvas, Pages, tasks and agents for a whole team on Cloudflare, for a GitHub repository
  • Custom screens - Your own Remix screens beside the built-ins, any of which you can turn off
  • Storage adapters - Beads stores tasks and discussions by default; custom adapters and the compatible Git comment store are explicit selections

Installation

One package holds the app, its Vite plugin and the bud CLI:

npm install -D @bud.sh/app

To try this repository's version instead, build and pack it:

pnpm --filter @bud.sh/app build
pnpm --filter @bud.sh/app pack --out /tmp/bud-app.tgz
# In your project
npm install -D /tmp/bud-app.tgz

Node 26.12 or newer is required.

Usage

Add the Vite plugin:

// vite.config.ts
import { bud } from '@bud.sh/app/vite'

export default {
  plugins: [bud()],
}

Add exactly one config file:

// bud.config.ts (or bud.config.js)
import { defineConfig } from '@bud.sh/app'

export default defineConfig({
  name: 'My project',
  routes: ['/', { id: 'product', path: '/products/123', title: 'Product' }],
  // port: 4000,                     // Bud's own port; omit to pick a free one
  // target: { port: 3000 },         // detected from Vite by default
  // comments: { remote: 'origin' },
  // plugins: [reactGrab()],         // from @bud.sh/app/plugins; Bud's DOM picker by default
})

Run your usual Vite dev command and open the printed Bud URL. The plugin only runs in development, and config changes restart it. It starts Bud's companion when Vite starts listening, finds your dev server's address, refreshes previews on hot updates and adds the small dev bridge Pages uses to follow your live pages; the bridge does nothing outside Bud's frames.

Plugins, storage and designs

Stores, the picker, the task design, extra screens and the factory's sandboxes and agent come from plugins, which @bud.sh/app/plugins exports: beads(), gitComments(), memory(), reactGrab(), react(), remix(), picker(), taskDesign(), local(), boat(), opencode() and definePlugin for your own. Tasks and discussions are Beads' unless a plugin provides one; storage: { work: false } or { threads: false } turns one off. designs.pages sets how the Pages sidebar names pages: by path (the default) or by title with the path beside it.

import { defineConfig } from '@bud.sh/app'
import { definePlugin, gitComments, taskDesign } from '@bud.sh/app/plugins'
import { tracker } from './bud/tracker.server.ts'

export default defineConfig({
  routes: ['/'],
  plugins: [
    definePlugin({ name: 'tracker', label: 'Tracker', work: tracker }),
    gitComments(),
    taskDesign('./bud/tasks.tsx'),
  ],
})

A plugin's store is a fully provided Effect layer using the WorkAdapter or ThreadAdapter service from @bud.sh/app/server, or a function making one for { project }; memoryAdapterLayer({ schema }) supplies both for fixtures. A shared layer is acquired once, and its resources close with the companion. Task designs are browser modules ending in .ts, .tsx, .js or .jsx inside the project, outside node_modules; Bud checks their paths before listening and their exports when opened. Only each store's public identity and connection state reach screens through project.storage; plugins and server configuration stay private.

Task designs default-export defineTaskDesign from @bud.sh/app/design. Its optional card, page, properties, actions, sections and column slots are Remix components. Omitted slots retain their built-in parts, which are exported for composition as TaskCardDefault, TaskPageDefault, SectionsDefault and ColumnDefault; properties and actions are empty until a design fills them. Slots receive validated work data, field rendering, navigation and explicit operations. Project context contains only its display id and name.

See the task design guide for the exported props and a compact-card example. The same browser-module registry and shared Remix import map serve screens and designs.

Screens

screens sets Bud's navigation, in order. Leave it out for Pages, Canvas, Tasks, Agents and Files (Agents once the factory is set up). Leave a built-in out to disable it, spread it to change its label or icon, and add your own screens as Remix component modules:

// bud.config.ts
import { canvas, defineConfig, files, pages } from '@bud.sh/app'

export default defineConfig({
  routes: ['/'],
  screens: [
    pages(),
    canvas(),
    { ...files(), label: 'Code' },
    { id: 'metrics', label: 'Metrics', icon: 'chart', module: './bud/metrics.tsx' },
  ],
})
// bud/metrics.tsx
import { Text, defineScreen } from '@bud.sh/app/screen'

export default defineScreen({
  load: async ({ project, signal }) => {
    let response = await fetch(new URL('/metrics.json', project.targetOrigin), { signal })
    return (await response.json()) as { visits: number }
  },
  render: (metrics) => <Text as="p">{metrics.visits} visits this week</Text>,
})

A custom screen's path defaults to /<id> and its icon to layers. Bud shows its loader until load settles and a retry card if it fails. Screens run only in the browser, inside Bud's page, and get the project with Bud's request capability, so treat them like the config. @bud.sh/app/screen also exports Bud's Button, Text, Loader, Spinner and field cards. Screens can't import CSS yet; Bud bundles each one with your project's Vite on first visit and again after you edit it.

Screens can own nested places. Keep the route map in a browser-safe module, then pass the same map as routes in both the config's screen and defineScreen:

// bud/metrics-routes.ts
import { get, route } from 'remix/routes'

export const metricsRoutes = route('/metrics', {
  index: get('/'),
  report: get('/reports/:id'),
})

metricsRoutes.report.href({ id: 'weekly' }) // /metrics/reports/weekly
import { Text, defineScreen } from '@bud.sh/app/screen'
import { metricsRoutes } from './metrics-routes.ts'

export default defineScreen({
  routes: metricsRoutes,
  render: (_data, context) => (
    <Text>{context.route === 'report' ? context.params.id : 'All reports'}</Text>
  ),
})

The config metadata allows startup validation without running screen modules on the server. Every route must stay under the screen's path. context.route narrows context.params; context.url carries the complete current location. Navigation within a screen preserves its component state and loaded data. Fetch route-specific updates from the component when its context changes; load runs only when the screen first opens.

Opened screens also stay mounted while hidden. context.active tells a screen when it is visible and owns the browser location. Background reconciliation can update its data, but should defer URL changes until it becomes active again.

Without Vite

npx bud [project-directory]                 # standalone, needs target.origin or target.port
npx bud doctor [project-directory]          # what's set up, and what the rest needs
npx bud comments migrate [project-directory] [--to bud:beads] [--dry-run]

The bud command comes with @bud.sh/app and needs target.origin or target.port in the config to serve. Previews opened this way can't be commented on; use the Vite plugin for that. Bud shows only what a project has set up; bud doctor says what that is and what the rest needs. bud capture is what a hosted Bud's builder runs.

Task links

The board lives at /tasks; each task has a typed /tasks/:id URL. Opening a task preserves the board's filters and scroll position, and Back returns to the same board. Copy link and modified clicks use the same route. Older ?issue= links redirect permanently after ID validation. Dots in hierarchical IDs are encoded by the route builder.

Comments

Comments use the selected thread adapter, which defaults to Beads. Initialize the project's Beads workspace explicitly before writing; Bud never creates one or chooses a hidden fallback. Authors come from your Git name and email on the server. One discussion can link to multiple tasks without duplicating its messages. Opening a discussion loads its messages; counts and truncation stay explicit when a backend returns a bounded view.

Writes use the discussion revision you last read. Conflicts retain the draft for review, and uncertain writes require checking the stored discussion before retrying. Sync is explicit and uses the selected adapter's existing remote configuration.

With gitComments(), discussions use the compatible bud/comments branch. Writes preserve your source branch, index and working files. Sync validates and merges fetched records, then pushes the exact validated commit without forcing. No other application storage selection writes this ref.

In Files, select lines and press Ctrl/⌘+Alt+M to comment. Save an editor draft first.

File and review links

File links name the place directly. The route maps shared by the screen and link helpers live in @bud.sh/ui/routes.

| Place | URL | | ------------------------ | --------------------------------------------------- | | File or directory | /files/src/app.ts | | Working or staged review | /files/review/working/src/app.ts | | All changed files | /files/review/working | | Commit comparison | /files/compare/HEAD...<full-object-id>/src/app.ts | | Selected source lines | /files/src/app.ts#L12-L20 | | Selected diff lines | /files/review/working/src/app.ts#L12-R14 |

In diffs, L is the old side and R the new side. A copied selection opens its one file, including when copied from an all-files review. Its optional at query value is a SHA-256 content digest used to detect changed source; it is not an addressable Git blob. Compare endpoints accept HEAD, full object IDs and full refs/... names. The typed builder encodes them safely.

Files whose first segment is review, compare, blob or path use /files/path/<actual-path> to avoid route collisions. Existing query links redirect after their project and coordinates are validated. Foreign legacy links stay rejected.

New links belong to the companion's origin, and requests still require its session capability. A bookmark does not assert project identity if a different project later reuses that origin. Project IDs continue to scope local preferences.

Discussions use /comments/:id. Bud reads the selected thread adapter and opens the owning canvas, file or task screen, including its saved location and conversation. Copy-link controls use this address; older ?thread= links redirect permanently after validation. The URL carries only the thread ID, and the preview never receives the API capability.

Migrating Git comments

bud comments migrate --to bud:beads --dry-run
bud comments migrate --to bud:beads

The command loads the project's config and uses its selected thread adapter unless --to overrides it. Initialize the target store first. Migration does not need a preview server or target.origin, and it never changes the config or source Git comments ref.

Canvas and code captures, statuses, message order, original authors, timestamps and reply ancestry are preserved. Complete history can exceed the browser's message display limit; the source and target still enforce their configured byte limits. Existing source identities are skipped, so rerunning the command resumes a partial migration without duplicating threads. An uncertain write stops the run and is never retried automatically. Keep the source Git history until you have reviewed the imported discussions.

Security

Bud binds to loopback. Requests need a per-session capability that is never sent to the previewed page. Canvas captures pages in its own headless browser. Pages shows them in sandboxed frames on the project's own origin, which differs from Bud's, and follows them only through the dev bridge's postMessage protocol (@bud.sh/app/bridge); a site served from Bud's own origin is never framed.

Development

pnpm dev       # Bud and the marketing site with HMR
pnpm verify    # all checks, including an installed-tarball browser test

See AGENTS.md for the app's structure.

Related Packages