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

@rschlaefli/manifest-mcp

v0.2.0

Published

Manifest — a calm, shared AI-agent Kanban board. MCP server + singleton local daemon; agents share one live board across tools, and humans steer it with comments.

Readme

Manifest — @rschlaefli/manifest-mcp

A calm, shared Kanban board for AI coding agents — one board across every agent on your machine. Add one MCP entry and the board "just works": the first agent to start spins up a local daemon (a singleton), opens it in your browser once, and every agent (Claude Code, Codex, Cursor, …) reads and writes the same live board through MCP tools. Agents own the cards and do the work; humans watch, and comment on a card to steer — agents reply and resolve.

Use it with your agents

Add the MCP server — one entry per client:

Claude Code.mcp.json in the project, or claude mcp add manifest -- npx -y @rschlaefli/manifest-mcp:

{
  "mcpServers": {
    "manifest": { "command": "npx", "args": ["-y", "@rschlaefli/manifest-mcp"] }
  }
}

Codex CLI~/.codex/config.toml (or a trusted project's .codex/config.toml):

[mcp_servers.manifest]
command = "npx"
args = ["-y", "@rschlaefli/manifest-mcp"]

Cursor~/.cursor/mcp.json (or .cursor/mcp.json in a project):

{
  "mcpServers": {
    "manifest": { "command": "npx", "args": ["-y", "@rschlaefli/manifest-mcp"] }
  }
}

That's it. On first tool call the daemon starts, the board opens at http://127.0.0.1:4317, and agents share state. Env knobs: MANIFEST_PORT (default 4317), MANIFEST_DATA_DIR (default ~/.manifest), MANIFEST_NO_OPEN=1 (don't auto-open a browser), MANIFEST_IDLE_MS (idle-shutdown window, default 30 min), MANIFEST_PLANS_DIR (base dir for resolving relative plan paths to local files).

Tip: for reproducible installs, pin a version — npx -y @rschlaefli/manifest-mcp@<version>.

A ready-made agent workflow skill lives in .agents/skills/manifest-board (.claude/skills symlinks to .agents/skills, so Claude Code discovers every skill there). Copy or symlink it into ~/.claude/skills/ to use it across projects.

Auto-sync hooks (onboarding)

So an onboarded repo keeps the board current without anyone remembering to, .claude/settings.json wires Claude Code lifecycle hooks to .agents/hooks/manifest-hook.mjs. The hook talks to the local daemon over HTTP and keys each workstream to the git branch:

| Event | Effect | | ---------------------------- | ------------------------------------------------------------------------------------------------------ | | Session start | Registers the repo's project on the board | | Plan approved (ExitPlanMode) | Upserts the branch's task to In Progress, writes .agents/plans/<branch>.md, links it as the plan | | Agent stops (end of turn) | On a feature branch with no task yet, creates one (In Progress) keyed by the branch — idempotent | | Session end | Moves the branch's In Progress task to In Review |

The stop hook is what catches work that never went through plan mode: the first time you pause on a feature branch, its task appears; later turns are a no-op (it only creates, never churns). It does nothing on a trunk branch (master/main/develop).

It's fail-soft — if the daemon isn't running, hooks no-op and never block the session. It never auto-sets Done/Blocked (those stay deliberate). Each transition is tunable via env — MANIFEST_HOOK_PLAN_STATUS=none, MANIFEST_HOOK_STOP=none, and MANIFEST_HOOK_SESSION_END=none disable the plan-approve, stop, and session-end behaviors respectively.

To onboard another repo, run manifest init inside it — one idempotent, non-clobbering command that writes the MCP entry (and gitignores .mcp.json for the machine-specific local-path default), copies the skill + lifecycle hook, and wires .claude/ (symlinks skills — skipped with a warning if it already exists — plus the hooks block):

cd /path/to/repo
npx -y @rschlaefli/manifest-mcp init   # writes the npx MCP entry, copies skill + hook, wires .claude

Run from npm (as above) it writes the portable npx MCP entry. Run from a local checkout instead — node /path/to/manifest/dist/index.mjs init — and it writes a local-path entry (node <abs>/dist/index.mjs) and gitignores .mcp.json, which is handy for same-machine development.

Flags: --npx (force the npx entry even from a local checkout), --agent <name> (attribution, default claude-code), --dry-run (preview). Re-running is safe; it never duplicates or clobbers existing config (existing hooks/skill files and a foreign .claude/skills are left as-is). Then run /mcp (or restart the client) to connect.

MCP tools

16 tools. Tasks, projects, comments, activity:

| Tool | Purpose | | ----------------------------------- | ------------------------------------------------------------------------ | | list_tasks | List tasks, filter by project / status / query | | get_task | One task by id | | create_task | Add a workstream (auto-registers an unknown project) | | update_task | Patch any field (status, priority, repo, branch, pr, plan…) | | set_status | Move a task to another column | | delete_task | Remove a task | | list_projects / add_project | Read / register projects | | update_project / delete_project | Rename or recolor a project · delete it (reassign or force its tasks) | | list_comments / add_comment | Read a task's comment threads · post a comment or threaded reply | | resolve_comment | Resolve (or reopen) a comment thread | | list_activity | Recent activity events — status moves, edits, comments, with attribution | | open_board | Return + open the board URL | | open_plan | Open a task's plan (local file → OS app, URL → browser) |

Adding tools means a new server version — after upgrading, run /mcp (or restart the client) so the agent re-reads the tool list.

How it works

  • Singleton daemon — the Next.js app, started detached by the first MCP process; others health-ping and attach. Idle-shuts-down; reclaims stale locks.
  • Shared state — a central ~/.manifest/ data dir, so every agent (and a future synced machine) sees one board.
  • Storage = NDJSON (~/.manifest/board.ndjson, atomic tmp+rename writes), schema from Zod (single source → types + validation + board.schema.json). See Storage format.
  • Live updates = SSE (/api/events). Any agent's write is pushed to open browsers instantly (no reload). The SSE connection count is also presence, so the browser opens exactly once and never duplicates tabs.

Stack

  • Next.js 16 (App Router) + React 19.2 + TypeScript 6 + Tailwind v4
  • MCP via @modelcontextprotocol/sdk, Zod 4 schemas, open for the browser
  • The MCP server (mcp/) is bundled with tsup; the daemon ships as a Next output: "standalone" build. Requires Node ≥ 20.9.

Develop

pnpm install
pnpm dev             # http://localhost:3000 (daemon UI in dev)
pnpm build:all       # standalone daemon + MCP bundle + board.schema.json
node scripts/mcp-smoke.mjs   # drive the built MCP server over stdio

next build uses --webpack (required for output: standalone; Turbopack doesn't emit it yet). Production single-build:

pnpm build:all && node dist/index.mjs

What's in the UI

  • Status board — five columns: Planned · In Progress · In Review · Done · Blocked
  • Swimlanes — same cards grouped by project instead of status (toggle in the top bar)
  • Project filter + search (/ or ⌘K to focus)
  • Comfortable / Compact density, dark / light theme (persisted)
  • Detail drawer — click any card; deep-links to repo, branch, PR/MR, and plan
  • Comments — the one human-write surface: a threaded composer in the drawer. Humans post; agents reply and resolve the thread over MCP. Cards show an open-thread badge.
  • Activity — a per-task timeline in the drawer (created · moved · edited · commented · resolved) with agent attribution
  • Drag a card to another column to change its status (persisted via the API)
  • Loading, empty, and error board states

Data layer

The board reads and writes through a small REST API backed by a process-local, NDJSON-backed in-memory store (lib/store.ts). Everything goes through a handful of functions, so replacing the store with Postgres/Prisma is self-contained.

| Method | Route | Purpose | | ------------ | ------------------------- | -------------------------------------------------------------------------- | | GET | /api/tasks | Board snapshot — { projects, statuses, tasks, commentCounts } | | POST | /api/tasks | Create a task (agent ingestion) | | GET | /api/tasks/:id | Single task | | PATCH | /api/tasks/:id | Partial update (status changes, edits) | | DELETE | /api/tasks/:id | Remove a task (cascades its comments) | | GET/POST | /api/tasks/:id/comments | List / add comments (a parentId makes a threaded reply) | | PATCH | /api/comments/:id | Resolve or reopen a thread — { resolved } | | GET | /api/activity | Recent activity events (optional ?task= / ?limit=) | | GET/POST | /api/projects | List / register projects | | PATCH | /api/projects/:key | Rename / recolor a project | | DELETE | /api/projects/:key | Delete a project (?reassignTo= moves its tasks, ?force=1 deletes them) | | GET | /api/events | SSE stream (live snapshots + presence) | | GET | /api/health | Daemon liveness { ok, viewers, version, … } |

The MCP tools proxy these. Agents normally use the tools; the REST API is the same surface for scripts (the daemon validates every payload with Zod). The mutating routes are localhost-only (a Host-header guard, against DNS-rebinding from a page in your browser).

Replace the host with your daemon URL — http://localhost:3000 under pnpm dev, http://127.0.0.1:4317 for the packaged daemon.

Example — an agent planning a workstream:

curl -X POST http://localhost:3000/api/tasks \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "Add OpenTelemetry spans to the RAG pipeline",
    "project": "ai-buddy",
    "priority": "P1",
    "platform": "github",
    "repo": "uzh-bf/ai-buddy",
    "branch": "feat/otel-rag",
    "plan": "docs/plans/otel-rag.md"
  }'

Example — moving it to review:

curl -X PATCH http://localhost:3000/api/tasks/<id> \
  -H 'Content-Type: application/json' \
  -d '{ "status": "In Review" }'

The NDJSON store persists to ~/.manifest/board.ndjson (held in memory by the single daemon, written atomically on each change). A fresh install starts with an empty board; agents populate it over the API.

Storage format

~/.manifest/board.ndjson holds one record per line — a discriminated union on type (meta | project | task | comment | event). Human-readable and git-diffable:

{"type":"meta","version":2,"statuses":["Planned","In Progress","In Review","Done","Blocked"]}
{"type":"project","key":"manifest","name":"manifest","color":"#6e7bf2"}
{"type":"task","id":"sse-live","title":"SSE live updates + presence","slug":"sse-live","status":"Done","priority":"P1","project":"manifest","platform":null,"repo":null,"branch":null,"pr":null,"plan":null,"updatedAt":"2026-06-08T09:45:00.000Z","order":4}
{"type":"comment","id":"c0","taskId":"sse-live","parentId":null,"author":"human","body":"does this duplicate tabs?","createdAt":"2026-06-08T10:00:00.000Z","resolved":false}
{"type":"event","id":"e0","taskId":"sse-live","kind":"status","detail":{"from":"In Review","to":"Done"},"agent":"codex","at":"2026-06-08T10:01:00.000Z"}

meta.version is 2 (it grew the comment + event records). The bump is additive and backward-compatible: a v1 file loads unchanged, and an older daemon skips record types it doesn't know rather than crashing.

Every line is validated (Zod) on read; bad lines are skipped with a warning, never crashing the board. board.schema.json (generated by pnpm build:all, shipped in the npm package) is the JSON Schema for these records — point an editor or validator at it.

Project layout

app/
  layout.tsx            root layout + pre-paint theme bootstrap
  page.tsx              renders <ManifestApp />
  globals.css           Tailwind import + @theme token map + keyframes/scrollbar residue
  api/tasks/route.ts                 GET (snapshot) · POST (create)
  api/tasks/[id]/route.ts            GET · PATCH · DELETE  (async params)
  api/tasks/[id]/comments/route.ts   GET · POST (threaded comments)
  api/comments/[id]/route.ts         PATCH (resolve / reopen)
  api/activity/route.ts              GET (activity events)
  api/projects/route.ts              GET · POST
  api/projects/[key]/route.ts        PATCH (rename/recolor) · DELETE
  api/events/route.ts                SSE stream (live + presence)
  api/health/route.ts                daemon liveness
  api/open/route.ts                  open the board in the browser (daemon-side)
instrumentation.ts     daemon idle-shutdown + SIGTERM (MANIFEST_DAEMON only)
components/
  ManifestApp.tsx       client root: state, fetch + SSE, filtering, drag wiring
  TopBar.tsx  Board.tsx  Swimlanes.tsx  Column.tsx  Card.tsx  Drawer.tsx
  Comments.tsx  Activity.tsx  states.tsx  badges.tsx  icons.tsx
  ui.ts  use-fetched.ts  drag.ts
lib/
  schema.ts (Zod source) · store.ts (NDJSON + bus) · board-model.ts (pure view/policy helpers)
  board-file.ts · http.ts (localhost guard) · paths.ts · plan.ts · presence.ts
  status.ts · format.ts · types.ts (re-export)
mcp/
  index.ts (stdio bin) · daemon.ts (singleton spawn/attach/open) · client.ts · tools.ts
scripts/
  emit-schema.ts (board.schema.json) · mcp-smoke.mjs
tests/
  vitest unit tests — pure store/model helpers (comments, activity, project admin, …)
tsup.config.ts  next.config.mjs (standalone)  eslint.config.mjs  postcss.config.mjs

Styling

Component styling is Tailwind v4 utility classes in the JSX. Design tokens (colors, radius, fonts, animations) are declared once in app/globals.css:

  • :root / [data-theme="dark"|"light"] hold the OKLCH token values.
  • @theme maps them to utilities (bg-card, text-text-2, border-border, rounded-card, animate-slide-in, …) that point back at those vars, so the data-theme toggle keeps flipping the whole UI with no per-utility config.
  • A thin residual layer stays in CSS for things Tailwind can't express: ::-webkit-scrollbar, ::selection, the @keyframes bodies, and the token map.

@custom-variant dark is wired to [data-theme] (not prefers-color-scheme) so any dark: utilities track the in-app theme toggle.

Tooling notes

  • Node ≥ 20.9 (Next 16). next build/next dev use Turbopack by default.
  • Lint: ESLint flat config in eslint.config.mjs; run pnpm lint. ESLint is pinned to 9.x — eslint-config-next 16 isn't compatible with ESLint 10 yet. next build no longer lints (Next 16 removed next lint).
  • Install: pnpm install regenerates pnpm-lock.yaml. (In some sandboxed environments pnpm's network layer is blocked while npm works — use npm install there.)

Design source

Manifest started from a static design prototype (HTML/CSS + React via CDN). The handoff bundle is gitignored — not part of the repo or the build.