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

noterai

v0.1.2

Published

Locally-run, browser-based AI agent dashboard for developers using LLM coding assistants

Readme

Noter

A locally-run, browser-based AI agent dashboard that monitors AI agent activity, summarizes context using a local or cloud LLM, and generates next-step task suggestions and implementation prompts.

npm package: noterai — install with npx noterai or npm install -g noterai. The CLI command is noter.

Architecture

┌─────────────────────────────────┐     ┌─────────────────────────────────┐
│  OpenCode (SSE port 4096)       │     │  Claude Code (MCP stdio)        │
└────────────┬────────────────────┘     └────────────┬────────────────────┘
             │ SSE                                   │ MCP
             └────────────────┬──────────────────────┘
                              │
                    ┌─────────▼──────────┐
                    │  Express Backend   │  port 3000
                    │  (packages/server) │
                    │                  │
                    │  • EventStore    │  200 events, FIFO ring buffer
                    │  • StateManager  │  summary, suggestions, notes, prompts
                    │  • Summarizer    │  Ollama + ai-sdk generateObject
                    │  • Suggester     │  Ollama + ai-sdk generateObject
                    │  • PromptBuilder │  Ollama / Anthropic / OpenAI / Gemini
                    │  • OllamaQueue   │  serialized inference queue
                    │  • OllamaCache   │  memoized provider instances
                    │  • wsServer      │  ping/pong heartbeat, 1MB msg limit
                    │  • MCP Server    │  stdio transport, send_event + get_state
                    └─────────┬────────┘
                              │ WebSocket + HTTP
                    ┌─────────▼──────────┐
                    │  React Frontend    │  port 5173 (dev) / served static
                    │  (packages/client) │
                    │                  │
                    │  • 2x2 grid layout│  Editorial Brutalism design system
                    │  • wsClient      │  auto-reconnect, exponential backoff
                    │  • 4 panels      │  Notes, Tasks, Context, Prompts
                    └──────────────────┘

Quick Start

From npm (no clone needed)

npx noterai              # Run without installing (opens browser automatically)
npx noterai serve         # Production server without auto-opening browser
npx noterai serve --open  # Production server + auto-open browser
npx noterai help          # Show all commands
npx noterai version       # Print version

# Or install globally:
npm install -g noterai
noter                     # Then run directly

From source (development)

git clone https://github.com/oniwakaa/noter.git && cd noter
pnpm install
pnpm run dev

The dashboard opens at http://localhost:5173. The backend runs on http://localhost:3000.

Note: pnpm run dev starts both the backend server (:3000) and the Vite dev client (:5173) concurrently.

Prerequisites

  • Node.js >= 20 (tested on v24.4.1)
  • pnpm >= 10 (tested on v10.30.3)
  • Ollama running locally with at least one model pulled (check ollama list)
    • Default model in .env.example is falcon-h1r:7b-q4_K_Mupdate .env to a model you have installed (e.g. deepseek-v4-flash:cloud or gemma4:e4b)
  • (Optional) OpenCode CLI for OpenCode SSE event ingestion
  • (Optional) API keys if using cloud LLM providers for prompt generation (Anthropic, OpenAI, Gemini)

Environment Variables

Copy .env.example to .env and adjust values:

cp .env.example .env

| Variable | Default | Description | |----------|---------|-------------| | PORT | 3000 | Backend HTTP/WebSocket server port | | OLLAMA_BASE_URL | http://localhost:11434 | Ollama API endpoint | | OLLAMA_MODEL | falcon-h1r:7b-q4_K_M | Model for summarization & suggestions. Must be a model you have pulled locally. | | CONTEXT_OFFLOAD_THRESHOLD | 0.8 | Auto-offload model when accumulated context exceeds this % of context window (0.0–1.0) | | VRAM_LIMIT_GB | 6 | VRAM display limit for the Ollama status memory bar | | OPENCODE_PORT | 4096 | OpenCode SSE stream port (only when ACTIVE_SOURCE includes opencode) | | ACTIVE_SOURCE | generic | Event source: opencode, claude-code, codex, factory-droid, pi, generic, auto, both, or all | | NOTER_EVENT_LOG | /tmp/noter-events.jsonl | Path to JSONL event log for file-based adapters (Droid, Pi) | | GENERIC_LOG_PATH | /tmp/noter-generic-events.jsonl | Path to JSONL event log for the generic adapter | | LLM_PROVIDER | ollama | Prompt generation provider: ollama, anthropic, openai, gemini | | ANTHROPIC_API_KEY | (empty) | Required when LLM_PROVIDER=anthropic | | OPENAI_API_KEY | (empty) | Required when LLM_PROVIDER=openai | | GEMINI_API_KEY | (empty) | Required when LLM_PROVIDER=gemini |

Scripts

Root (package.json)

| Script | Description | |--------|-------------| | pnpm run dev | Start both server and client concurrently | | pnpm run dev:server | Start backend only (Express + WebSocket on port 3000) | | pnpm run dev:client | Start frontend only (Vite dev server on port 5173) | | pnpm run build | Build shared + server + client + bin for production | | pnpm run typecheck | TypeScript strict mode check (both packages) | | pnpm run test | Run full Vitest suite (server + client workspaces) |

Sub-package scripts

# Client build
cd packages/client && pnpm run build   # tsc && vite build
cd packages/client && pnpm run preview  # preview production build

# Server build
cd packages/server && pnpm run build    # tsc
cd packages/server && pnpm run build:mcp  # tsc, outputs dist/mcp-server.js

CLI (bin/noter.ts / bin/noter.js)

The package exposes a noter CLI via the bin field:

noter --help             # Show all commands
noter version            # Print version
noter serve              # Production server (serves static frontend + API)
noter serve --open        # Production server + auto-open browser
noter dev                 # Development mode (hot-reload)
noter build               # Build client & server for production
PORT=8080 noter serve     # Custom port

Project Structure

noter/
├── bin/
│   ├── noter.ts              # CLI source (compiled to noter.js for publish)
│   ├── noter.js              # Compiled CLI entry point (gitignored, built on publish)
│   └── tsconfig.json         # Bin-specific tsconfig (node16 module resolution)
├── packages/
│   ├── server/               # Express + WebSocket backend
│   │   ├── src/
│   │   │   ├── adapters/
│   │   │   │   ├── opencodeAdapter.ts   # OpenCode SSE → AgentEvent
│   │   │   │   ├── claudeAdapter.ts     # Claude Code MCP → AgentEvent
│   │   │   │   ├── codexAdapter.ts      # OpenAI Codex CLI MCP → AgentEvent
│   │   │   │   ├── droidAdapter.ts      # Factory Droid hooks → AgentEvent
│   │   │   │   ├── piAdapter.ts         # Pi (pi.dev) hooks → AgentEvent
│   │   │   │   └── genericAdapter.ts    # Generic JSONL/webhook → AgentEvent
│   │   │   ├── llm/
│   │   │   │   ├── ollamaClient.ts      # Low-level Ollama client (60s timeout, AbortController)
│   │   │   │   ├── ollamaCache.ts       # Memoized createOllama() provider instances
│   │   │   │   ├── ollamaQueue.ts       # Serialized inference queues (suggester + inference)
│   │   │   │   ├── summarizer.ts        # ContextSummary generation from events
│   │   │   │   ├── suggester.ts         # TaskSuggestion[] generation from context + notes
│   │   │   │   └── promptBuilder.ts   # GeneratedPrompt from task + context + notes
│   │   │   ├── routes/
│   │   │   │   ├── notes.ts           # POST /api/notes → save + fire-and-forget suggest
│   │   │   │   ├── prompt.ts          # POST /api/prompt (by taskId) + GET /:taskId
│   │   │   │   ├── summarize.ts       # POST /api/summarize (mutex-guarded)
│   │   │   │   └── ollama.ts          # GET /api/ollama/status + POST /api/ollama/offload
│   │   │   ├── config.ts              # AppConfig, types, loadConfig(), Zod-like validation
│   │   │   ├── eventStore.ts          # In-memory FIFO ring buffer (200 max)
│   │   │   ├── stateManager.ts        # Shared state + task→prompt map
│   │   │   ├── wsServer.ts            # WebSocketServer wrapper (heartbeat, 1MB limit)
│   │   │   ├── mcp-server.ts          # MCP stdio server (send_event, get_state tools)
│   │   │   └── index.ts               # createApp() factory + listenServer() + startServer()
│   │   └── package.json
│   └── client/               # React + Vite frontend
│       ├── src/
│       │   ├── components/
│       │   │   ├── NotesPanel.tsx       # Debounced textarea, drag-to-delete
│       │   │   ├── SuggestionsPanel.tsx # Task cards, priority badges, conflict warnings
│       │   │   ├── ContextPanel.tsx     # Summary fields, Ollama status, activity log
│       │   │   ├── PromptsPanel.tsx     # Build → expand prompt, copy to clipboard
│       │   │   └── OllamaStatus.tsx    # Model name, connection, VRAM bar, offload
│       │   ├── styles/
│       │   │   └── tokens.css           # DESIGN.md tokens: monochrome, zero-radius, no shadows
│       │   ├── App.tsx                  # 2x2 grid, top nav, dark/light toggle
│       │   ├── wsClient.ts              # Singleton WebSocket client (reconnect backoff)
│       │   ├── localDeletions.ts        # Drag-delete tracking set
│       │   ├── types.ts                 # Shared TypeScript interfaces
│       │   └── test/                    # Component tests (RTL + jsdom)
│       └── package.json
├── .env.example              # All environment variables with comments
├── vitest.workspace.ts        # Server (node) + Client (jsdom) test workspaces
├── tsconfig.base.json         # Shared TypeScript strict config
├── pnpm-workspace.yaml        # Monorepo workspace definition
└── README.md                  # This file

Frontend Panels

The UI is a fixed 2x2 viewport grid separated by 1px solid dividers (outline_variant).

| Panel | Grid Position | Description | |-------|--------------|-------------| | Notes | Top-left | Borderless textarea with debounced save (500ms POST to /api/notes). Supports drag-to-delete: drop a task card here to remove it. | | Suggested Tasks | Top-right | Task cards (TaskSuggestion[]) with priority badges (high/medium/low), conflict warnings, and a "Build →" action to generate a prompt. | | Agent Context | Bottom-left | ContextSummary fields: what's happening, files changed, current goal, blockers. Includes Ollama status (model, VRAM bar, offload button). Terminal-style activity log. | | Suggested Prompts | Bottom-right | List of generated prompts. Click "Build →" to expand a prompt row (animated from 0fr to 1fr). Each prompt has a copy-to-clipboard button. |

Design System: High-End Editorial Brutalism — zero border-radius, no box-shadow, monochrome palette (#131313 surface), Space Grotesk for labels, Geist Mono for data/code. See DESIGN.md for full spec.

WebSocket Message Types

All messages are typed as WsMessage union. The client auto-reconnects with exponential backoff (500ms → 30s max).

type WsMessage =
  | { type: 'sync';    payload: { events: AgentEvent[]; summary: ContextSummary | null; suggestions: TaskSuggestion[]; notes: HumanNotes | null } }
  | { type: 'event';   payload: AgentEvent }
  | { type: 'summary';  payload: ContextSummary }
  | { type: 'suggestions'; payload: TaskSuggestion[] }
  | { type: 'notes';   payload: HumanNotes };
  • sync — sent to every new WebSocket connection to hydrate frontend state.
  • event — broadcast when a new agent event is ingested.
  • summary — broadcast when the summarizer generates a new context summary.
  • suggestions — broadcast when new task suggestions are generated.
  • notes — broadcast when notes are updated.

Server-side protections: ping/pong heartbeat every 30s (stale connections terminated), maximum message size 1MB.

API Endpoints

GET /health

Returns {"status":"ok"} — used by services.yaml healthcheck and CLI startup verification.

POST /api/notes

Body: { content: string, updatedAt: number } (Zod-validated)

Saves notes, returns { saved: true } immediately, then fire-and-forget generates TaskSuggestion[] via the suggester queue. Suggestions are broadcast via WebSocket.

POST /api/prompt

Body: { taskId: string } (Zod-validated)

Generates a GeneratedPrompt for the given task using the configured LLM_PROVIDER. Uses shared INFERENCE_QUEUE to serialize with summarization. Returns 404 if taskId not found, 500 if API key missing or generation fails.

Also: GET /api/prompt/:taskId — retrieve a previously generated prompt for a task.

POST /api/summarize

Body: none required.

Triggers context summarization manually. Mutex-guarded: concurrent requests return 202 without duplicate Ollama calls. Returns ContextSummary with fields: whatIsHappening, filesChanged, currentGoal, blockers, rawEventCount.

GET /api/ollama/status

Returns Ollama /api/ps response formatted with models[] and memory{} (totalSize, totalVram, usedPercent).

POST /api/ollama/offload

Offloads all loaded Ollama models (sends keep_alive: 0).

MCP Integration

Noter exposes an MCP server via stdio transport for use with Claude Code, Cursor, VS Code Copilot, Continue.dev, Windsurf, and OpenAI Codex CLI.

Tools:

  • send_event — Push an agent event into the pipeline. Zod-validated against AgentEvent schema. Stored in eventStore, broadcast via WebSocket, and processed by the summarizer.
  • get_state — Returns current application state (summary, suggestions, notes) as JSON.

Configuration — add to your agent's MCP settings (e.g. ~/.claude/mcp.json, .cursor/mcp.json, or equivalent):

{
  "mcpServers": {
    "noter": {
      "command": "node",
      "args": ["packages/server/dist/mcp-server.js"]
    }
  }
}

Per-Harness Setup

Claude Code & OpenAI Codex CLI: Both use the same MCP protocol. Add the MCP server config above to your agent's settings, then set ACTIVE_SOURCE=claude-code (or codex, or all).

Factory Droid: Droid writes events to /tmp/noter-events.jsonl via its hooks system. The droidAdapter.ts watches this file automatically when ACTIVE_SOURCE includes factory-droid.

Pi (pi.dev): Pi extensions can emit events to the same JSONL file. Install a noter-bridge.ts extension in Pi that writes { type, sessionId, payload, timestamp } lines to NOTER_EVENT_LOG.

OpenCode: Run opencode with the --events flag pointing to http://localhost:${OPENCODE_PORT}. The opencodeAdapter.ts connects via SSE.

Generic (any harness): The generic adapter watches a JSONL file (default: /tmp/noter-generic-events.jsonl) for events from any agent harness. Zero-config — just write JSON events to the default path, or set GENERIC_LOG_PATH for a custom path. Set ACTIVE_SOURCE=generic (the default).

Ollama Integration

  • Provider caching: ollamaCache.ts memoizes createOllama() instances by baseURL so the same provider config is reused across summarizer, suggester, and prompt builder.
  • Queue serialization: ollamaQueue.ts provides two queues:
    • INFERENCE_QUEUE — shared between summarizer and prompt builder (prevents concurrent calls).
    • SUGGESTER_QUEUE — dedicated queue for suggestion generation (runs independently).
  • Timeout: ollamaClient.ts uses AbortController with a 60-second default timeout. Configurable via OllamaClientOptions.timeout.
  • Context auto-offload: Tracks accumulated token usage and automatically offloads models when the context window threshold (CONTEXT_OFFLOAD_THRESHOLD) is exceeded.

LLM Providers for Prompt Generation

| Provider | Model ID | Requires Key | |----------|---------|-------------| | ollama | Configured OLLAMA_MODEL | No | | anthropic | claude-sonnet-4-20250514 | ANTHROPIC_API_KEY | | openai | gpt-4o | OPENAI_API_KEY | | gemini | gemini-2.5-flash | GEMINI_API_KEY |

Event Sources

| Source | Adapter | Transport | Trigger | |--------|---------|-----------|---------| | OpenCode | opencodeAdapter.ts | SSE (http://localhost:${OPENCODE_PORT}) | session.idle, tool.before/after, message.updated | | Claude Code | claudeAdapter.ts | MCP stdio | send_event tool calls | | OpenAI Codex CLI | codexAdapter.ts | MCP stdio | send_event tool calls | | Factory Droid | droidAdapter.ts | File hooks (AGENTDESK_EVENT_LOG) | tool.before/after, session.start/end | | Pi (pi.dev) | piAdapter.ts | File hooks (AGENTDESK_EVENT_LOG) | tool_call, tool_result, agent_start/end | | Generic | genericAdapter.ts | JSONL file watcher + webhook | Any harness writing JSON events |

Set ACTIVE_SOURCE=all to ingest from all supported harnesses simultaneously.

Testing

# Full suite (server + client)
pnpm run test

# Server only (node environment)
pnpm vitest run --project server

# Client only (jsdom environment)
pnpm vitest run --project client

# With coverage
pnpm vitest run --coverage
  • Server tests: 30+ test files covering routes, adapters (5 harnesses), LLM pipeline, config validation, WebSocket server, MCP server, session management, and CLI.
  • Client tests: 10+ component test files using React Testing Library + jsdom.

TypeScript

Strict mode enabled across both packages. Root tsconfig.base.json provides shared compiler options.

pnpm run typecheck   # Both packages

Security

  • No hardcoded secrets in source files.
  • .env is in .gitignore.
  • API keys are read exclusively via loadConfig() in config.ts.

License

MIT