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

scope-kanban

v0.10.0

Published

Local-first kanban for projects, epics, stories, and bugs — built for agents. CLI + web UI.

Readme

scope

Local-first kanban for epics, stories, and bugs — built so coding agents and humans can plan and track work without leaving the command line.

Ships as a CLI, a GitHub-Projects-style web UI, and a hub daemon (scope serve) that fans changes out to every viewer over SSE. Everything lives in a .scope/ directory in your repo (SQLite, WAL mode), so it works offline, syncs through git if you want, and needs no external service.

brew install briannadoubt/tap/scope     # macOS / Linuxbrew
npm install -g scope-kanban             # any platform with Node ≥20
npx scope-kanban --help                 # one-shot, no install

Quick start

cd ~/my-app
scope init                               # prompts for workspace key + name on a TTY
scope workspace set --key MA --name "My App" --description "Short blurb"
scope auth login                         # browser-approved login for Scope Cloud
scope connect                            # creates/joins a hosted project and syncs
scope ticket create "Auth refactor" -t epic -p high
scope ticket create "OAuth login"   -t story --parent MA-1
scope serve                              # → https://localhost:4321 (also https://scope.local:4321)
scope ca trust                           # one-time: trust the local CA so browsers stop warning

scope init accepts --key MA --name "My App" if you want to skip the prompts (e.g. from another agent or a non-interactive shell). New workspaces keep their event log and SQLite cache in machine-local Scope storage by default, with a small committed .scope/workspace.json marker in the repo. Use scope init --git-events only when you intentionally want the event log carried through git.

LAN security

scope serve listens on HTTPS with a leaf cert signed by a local certificate authority (generated on first run, persisted to ~/.scope-hub/ca/). Authentication is layered:

  • Browser path — a bearer token stored in a cookie. Bookmark https://scope.local:4321/?token=… once (printed at startup) and the cookie does the rest. Loopback connections from the same machine bypass the token check entirely so scope CLI commands work without configuration.
  • Native path (SwiftUI app, etc.) — clients pair with scope pair and get a client certificate signed by the local CA. mTLS replaces the bearer token for those connections; see scope devices list.

To clear the browser cert warning, trust the local CA once:

scope ca trust            # System keychain, sudo (recommended)
scope ca trust --user     # login keychain, no sudo (per-user only)
scope ca fingerprint      # print SHA-256 for out-of-band verification
scope ca untrust          # reverses `scope ca trust`

The CA's private key lives at ~/.scope-hub/ca/ca.key (mode 0600) and never leaves the machine. The cert at ~/.scope-hub/ca/ca.crt is what gets trusted by the keychain.

What ships today

  • CLI — workspace / ticket / epic / artifact / link / status / branch / pr / board with --json output on every command for agent consumption.
  • Web UI — configurable kanban columns, drag-and-drop, ticket drawer with inline edit, agent execution state, and sandboxed agent-authored HTML visualizations; a visual coordination center for agent presence, durable conversations, delivery state, leases, attempts, and conflicts; workspace overview, epic filter, swimlanes (group by epic / assignee / priority / type), and live updates via SSE.
  • scope serve hub — one long-lived process that serves the UI, the REST API, and the SSE event stream on https://localhost:4321 (loopback HTTP also bound for CLI traffic). Multiple agents and a human in the browser all share the workspace's SQLite DB; writes from any source push to every viewer over Server-Sent Events within ~100ms.
  • Self-healing federated hub — every scope serve invocation auto-discovers a running hub (default port 4321, walks forward to 4330 if taken by a non-scope process) and registers its local .scope/ workspace with it. First one to start binds the port; the rest idle with a watchdog that promotes a survivor if the hub-owning process dies. Concurrent Claude Code sessions / previews / repos all converge on the same UI, no port flags required. Each repo keeps its own .scope/scope.db (so it travels with git clone).
  • iOS app — SwiftUI client that discovers the hub over Bonjour, pairs via mTLS, and renders the same board + ticket detail + live updates. Lives in App/ in this repo.

The web UI

scope serve

The Group by picker in the topbar turns the board into swimlanes — one horizontal row per epic, assignee, priority, or type, each with its own status columns. Manage columns in that same popover lets you add, rename, reorder, recolor, and classify workspace-specific statuses. State (group choice, collapsed lanes) persists in localStorage.

The little dot next to the refresh button is your live indicator: green = SSE connected, blue flash = just applied a change, gray = paused (drawer/modal/input/drag), red = disconnected. Clicking refresh during a red indicator triggers an active hub re-probe and rebuilds the SSE connection.

The connected-agents button in the topbar opens Agent coordination. It shows heartbeat presence, pending delivery counts, active leases, attempt and conflict health, and every durable conversation available to the selected agent. Threads can be filtered by ticket, acknowledged, replied to, or started from the browser. Ticket cards show the current execution phase and agent; ticket drawers expand that into lease expiry, attempt status, verification, evidence, repository intent, handoffs, and conflicts. Message bodies remain durable workspace data. Agent rows and the message composer distinguish a live model-session connection from a durable mailbox that cannot currently wake its recipient.

Programmatic API

Beyond the CLI, scope-kanban publishes its data layer as an importable library. Install it as a dependency and drive a workspace from Node directly — no shelling out to the scope binary:

import { openWorkspace, TICKET_TYPES } from 'scope-kanban';

const ws = openWorkspace();          // opens the nearest existing .scope/ (like the CLI)
const epic  = ws.createTicket({ type: 'epic',  title: 'Auth refactor', priority: 'high' });
const story = ws.createTicket({ type: 'story', title: 'OAuth login', parent: epic.id });
ws.updateTicket(story.id, { status: 'in_progress' }, 'claude');

ws.listTickets({ status: 'in_progress' });   // → [{ id: 'MA-2', ... }]
ws.epicProgress(epic.id);                     // → { total, counts, done, percent }
ws.close();

openWorkspace() is the one entry point — every operation hangs off the returned handle, so there's a single obvious way to do things and the storage engine stays hidden. It mirrors exactly what the CLI does on each command (open the SQLite cache, ensure the append-only event log, replay the log if it's ahead), so writes persist to both the cache and the log — identical to scope ticket create. Each handle method forwards to the data layer with the workspace's db pre-bound; the raw db is not exposed, since writing to it directly would bypass the event log and be lost on the next cache rebuild.

Like the CLI, opening never creates a workspace implicitly — it throws if none is found, so library writes can't land in an accidental board. Pass { create: true } to bootstrap one:

const ws = openWorkspace('./.scope', { create: true });
ws.updateWorkspace({ key: 'MA', name: 'My App' });

Alongside the handle, the root also exports the domain vocabulary (TICKET_TYPES, STATUSES, PRIORITIES, RELATION_TYPES, DEFAULT_COLUMNS) — enough to build UIs and validate input without reaching into internals.

Published entry points

| Entry | For | |---|---| | scope-kanban | The library above — openWorkspace plus the domain vocabulary. | | scope-kanban/cli | run / buildProgram, for embedding the commander program. |

The surface is intentionally small: one runtime entry, not a function per table. Everything else under src/ is private, so internals can move without a breaking change.

Agent integration

Scope's primary API is an executable agent protocol. Start by discovering the runtime contract rather than assuming statuses, fields, or features:

scope --json capabilities
scope --json ready --capabilities node,postgres
scope --json claim --agent codex --capabilities node,postgres --files src/auth.js,test/auth.test.js

Every JSON response is a versioned envelope. Read data; on failure inspect error.code, error.retryable, and error.details. Mutations support two global reliability controls:

# Exactly-once retry: normally replays the original receipt; after an
# interrupted receipt write, the event-carried request fence prevents a repeat.
scope --json --request-id run-42 status MA-7 in_progress --by codex

# Compare-and-swap: reject the write if state changed since the last read.
scope --json --if-revision sha256:abc... ticket edit MA-7 --priority high

The normal execution lifecycle is ready → claim → context → discover/plan → complete. Claims atomically create a renewable lease and an attempt; completion atomically records verification/evidence, finishes the attempt, releases the lease, and moves the ticket to the workspace's done column. Contracts can make capabilities, evidence, or passing verification mandatory.

scope --json contract set MA-7 \
  --acceptance '["token expires correctly"]' \
  --verify '["npm test -- auth"]' \
  --policy '{"requireVerification":true}' --by planner
scope --json context MA-7 --budget 3000
scope --json lease renew LEASE_ID --agent codex
scope --json discover MA-7 fact "Expiry is enforced in middleware" --by codex
scope --json complete MA-7 --attempt 01... --agent codex \
  --verification '[{"command":"npm test -- auth","ok":true}]'

Capture data.lease.leaseId and data.attempt.attemptId from claim and keep them distinct from the ticket id. Stop renewing after completion, handoff, or release. A stale lease error means re-read execution state, not retry the old id; an invalid argument means correct the command shape before retrying.

When Codex or Claude can spawn native subagents, the host remains the harness and Scope is the shared coordination state. Ask Scope for a conflict-aware parallel plan, then atomically claim one ticket for each native child:

scope --json ready --plan --capabilities node,postgres
scope --json claim MA-7 --agent codex:worker-1 \
  --files src/auth.js,test/auth.test.js

ready --plan groups tickets whose known repository intent is disjoint and defers work that overlaps an active lease. Each child reads context, owns one claimed ticket, and uses its native prompt from the parent. The host handles spawning, model-session wakeup, waiting, cancellation, sandboxing, and worktrees. Scope records the durable lease, attempt, observed files, discoveries, verification, outcome, structured handoff, and addressed messages:

scope --json handoff create MA-7 --agent codex:worker-1 \
  --summary "Middleware updated; expiry test still needed" \
  --remaining '["add the regression test"]'

Agents can register heartbeat presence and exchange durable threaded messages. When registration runs inside Codex or Claude, Scope binds the current session locally by default. The bridge owned by scope serve resumes the addressed session, retries transient failures, and acknowledges after the provider accepts the turn:

scope --json agent register codex:sol --provider openai --ttl 2m
scope --json message send --from codex:sol --to claude:opus \
  --ticket MA-7 --kind review_request --body "Review commit abc123"
scope --json bridge status

Install native Codex and Claude Code lifecycle hooks to register a private random identity automatically, reuse it on resume, renew presence, and mark it offline on session end:

scope bridge hooks install
scope --json bridge hooks status

The supported-host matrix and mailbox-only fallbacks are documented in docs/session-lifecycle.md.

Explicit binding and the provider-neutral listener/SSE adapter contract are documented in docs/agent-messaging.md.

Humans can inspect the same state visually from the connected-agents button in the scope serve topbar. The coordination center shows agent presence, ticket-linked threads, pending/acknowledged delivery, active leases, attempts, and conflicts; execution badges and details also appear on ticket cards and in the ticket drawer.

Claims and attempt outcomes derive ticket lifecycle automatically. Agent-readable ticket, board, context, and readiness JSON include a coherent execution projection, so a parent can verify durable state instead of trusting a child's final chat message.

Dependencies determine readiness; file/worktree/branch/base-SHA intent warns or blocks overlapping claims; expired leases are reclaimable; causal sibling writes become explicit conflicts; and scope watch --since <event-id> provides a resumable JSONL changefeed. scope doctor --json verifies the authoritative event log and cache, while --repair rebuilds only the disposable cache.

The same coordination methods are available from the published Node workspace handle and under /api/agent/* on the hub. The generated command reference is docs/agent-protocol.md.

Mixed-version agents

All agents sharing a workspace must use a compatible Scope reader. Check scope --json capabilities: current builds write event format 2, read formats 1 and 2, and expose the requirement under data.eventFormat.minimumReaderVersion. Upgrading preserves and reads existing format-1 history without rewriting it. After a format-2 event is written, do not reopen or sync that workspace with a format-1-only binary; scope doctor reports the mismatch as an incompatible reader requirement rather than log corruption. See docs/event-log-format.md.

Ship the agent skill into Claude Code, Codex, or Cursor:

scope skills install                      # uses bundled copy from your install
curl -fsSL https://raw.githubusercontent.com/briannadoubt/scope/main/skills/install.sh | bash   # remote

Force a subset or target a specific repo:

scope skills install --tool claude
scope skills install --tool cursor --project /path/to/repo

The skill teaches agents to discover capabilities, claim leases, use compact context packs, publish typed discoveries, attach verification, and recover from stale revisions or contention.

Previewing in Claude Code

If you want the kanban available in Claude Code's preview pane, use scope preview --port <unique> in .claude/launch.json — never plain scope serve for previews:

{
  "version": "0.0.1",
  "configurations": [
    {
      "name": "scope-myproject",
      "runtimeExecutable": "scope",
      "runtimeArgs": ["preview", "--port", "4322"],
      "port": 4322,
      "autoPort": false
    }
  ]
}

Why: Claude Code's preview_start enforces one tracked server per port. If two projects both register port: 4321 (the hub), opening the preview in the second pane forcibly stops the first pane's tracked process — the iframe goes blank with "The preview server stopped." Even with unique server names this happens, because the collision is on port.

scope preview --port <N> works around this with a tiny per-pane reverse proxy: each project picks its own port (e.g. 4322, 4323, ...), and every proxy forwards to the single shared hub on 4321. Each pane gets its own preview-tracked server (no collision), all viewers see the same federated kanban. The first scope preview to run lazily starts the hub via the usual ensureHub() path; subsequent ones just proxy.

Pick a different port for every project (suggested range: 4322–4399).

Data model

| | | |---|---| | Hub | The scope serve daemon. Discovers and brokers traffic across one or more workspaces on a machine / LAN. | | Workspace | A .scope/ directory: owns the key prefix (e.g. MA), name, description, and overview. Each workspace is one SQLite database. | | Ticket | Epic, story, or bug. Belongs to one workspace. IDs are <KEY>-<n> (e.g. MA-3). | | Epic | High-level work. Parents stories and bugs. | | Story | Unit of work toward an epic. | | Bug | Defect. Can live under an epic. | | Status | backlog → todo → in_progress → in_review → done (+ cancelled) | | Priority | low / medium / high / urgent | | Relation | blocks, blocked_by, relates_to, duplicates, duplicate_of (inverse auto-created) |

Ticket IDs are immutable — once a ticket is created, its prefix is baked into its ID. Changing the workspace key after the fact leaves old tickets with the old prefix.

Collaboration and storage

Scope is event-sourced. The source of truth is an append-only log — one JSON file per change, named by a time-sortable ULID. By default, new workspaces store that log and the rebuildable scope.db cache under ~/.scope/workspaces/<id>/ so Scope does not flood your codebase with event files. The repo carries a tiny .scope/workspace.json marker plus optional .scope/remote.json; secrets never go there.

The normal sharing path is Scope Cloud:

scope auth login          # stores a machine-local credential
scope connect             # defaults to https://scope-hub.fly.dev
scope remote show         # explains cloud target, auth, and local storage

If you see two boards with the same name, check the subtitle in the board switcher. A local workspace is the editable repo marker on this machine; a cloud project is the hosted copy that remote clients and the iOS app read. scope remote show tells you whether the local workspace is bound to that cloud project.

Git-carried events remain available as an advanced mode:

scope init --git-events
scope events move-to-git
scope events move-to-local
scope events status

In git-events mode, the log lives at .scope/events/ and scope.db remains a cache that must never be committed. Because the log is append-only and every file name is globally unique, merging is just the union of new event files.

Conflicts resolve deterministically without coordination:

  • Concurrent field edits → last-writer-wins by timestamp (ULID breaks ties).
  • New tickets / comments / relations → grow-only union; both survive.
  • Ticket numbers (SCP-42) are display values de-collided at replay — the earliest creator keeps the number; a colliding offline create is bumped.

Git-events mode works over any dumb file sync — git, iCloud Drive, Dropbox, Syncthing — because all any of them has to do is deliver new files.

When you do want sub-second live updates, run scope serve: the hub brokers changes over SSE/mTLS on a machine or LAN. That's an optimization on top of the same log — the log remains the source of truth, so going offline and syncing later loses nothing. (Real-time across the open internet with zero infrastructure is the one thing that's out of scope — that always needs a meeting point.)

See docs/event-log-format.md and docs/adr/0001-decentralized-ticket-identity.md for the format and conflict semantics.

Command reference

| Command | What it does | |---|---| | scope init [--key KEY --name NAME] [--git-events] | Create .scope/ in the current directory. Defaults to machine-local event storage; --git-events opts into .scope/events. | | scope workspace show | Print the current workspace (key, name, description, overview). | | scope workspace set [--key KEY] [--name NAME] [--description ...] [--overview ...] | Edit workspace metadata. --key only affects future tickets. | | scope workspace rekey <KEY> | Change the key and reprefix every existing ticket (MA-1 → APP-1). The correct way to rename a key. | | scope workspace add / list / remove | Manage which workspaces the running hub knows about. | | scope ticket create <title> -t <type> [--parent <epic>] | New ticket in the current workspace. | | scope ticket list / show / edit / delete | Manage tickets. edit accepts a comma-separated id list (atomic). | | scope status <ids> <status> [--by <name>] | Move a ticket to any status id configured in the workspace columns. ids may be comma-separated to move several atomically. | | scope batch [-f ops.json] | Apply many ops as one atomic transaction (or pipe the JSON array on stdin). Supports $ref to reference a ticket created earlier in the batch. The supported path for bulk/compound edits — never edit scope.db directly. | | scope branch <id> [<name>] [--in-progress] | Get/set branch, optionally flip status. | | scope pr <id> [<url>] [--in-review\|--merged] | Get/set PR, optionally flip status. | | scope link add <from> <type> <to> | Relate two tickets. | | scope epic list / children <id> | Epic-focused views. | | scope comment <id> <body> [--by <name>] | Add a comment. | | scope history <id> | Change log for a ticket. | | scope board [--epic <id>] | Terminal kanban view. | | scope serve [-p <port>] | Run the hub (auto-attaches to a running hub if one exists). | | scope preview --port <N> | Run a per-pane proxy to the hub. For Claude Code's .claude/launch.json — each pane uses a unique port so preview_start doesn't make panes stop each other. | | scope auth login [--remote <url>] | Browser-approved hosted login for this machine. Stores the key outside the repo so future syncs just work. | | scope connect [--new NAME\|--project ID] | Connect the workspace to Scope Cloud by default, write safe .scope/remote.json, and run the first sync. | | scope events status / move-to-local / move-to-git | Inspect or migrate event storage between quiet local mode and git-events mode. | | scope ca fingerprint / trust / untrust / path | Manage the local certificate authority. | | scope pair | Pair a new native client (prints a one-time 6-digit code). | | scope devices list / rename | Inspect or rename paired native clients. | | scope skills install [--tool ...] [--project ...] | Install agent skill. |

Deprecated. scope project create / show / list / edit are kept as aliases that route to the scope workspace commands and print a yellow warning. scope project delete errors out — there's nothing to delete now that each workspace owns exactly one project. scope ticket create still accepts --project <KEY> but ignores it with a deprecation warning.

Every command accepts --json for machine-readable output.

Architecture

  • Storage — SQLite via better-sqlite3, in .scope/scope.db. WAL mode for safe multi-process writes; serialization happens at the SQLite layer. Each DB has a singleton workspace row (key, name, description, overview) and a tickets table — the old projects table has been folded into workspace. Existing DBs migrate on first open.
  • CLI — Node 20+ ES modules, commander for parsing.
  • Server — Express. Mounts the REST API and an SSE /events channel. GET /api/workspaces returns {id, scope_dir, label, key, name, description, overview, columns}; GET /api/board returns {columns, terminal_columns, buckets} so web, hosted, and iOS clients render the same workspace-defined status pipeline. GET /api/projects is kept as a back-compat shim that synthesizes one project per workspace for older clients.
  • Realtime — in-process EventEmitter bus emits on every mutation; fs.watch on .scope/ plus a PRAGMA data_version check catches writes from other processes (CLI, sibling serve processes) and feeds them into the same bus. UI subscribes via EventSource, debounces refresh, and diffs by hash to skip no-op renders.
  • Hub coordination — discovery file at ~/.scope-hub/hub.json, workspace registry at ~/.scope-hub/workspaces.json. The watchdog in every long-lived process polls /api/meta and re-runs ensureHub() if the current hub stops answering, so the UI never goes blank for surviving workspaces.

Releasing

npm run release checks that source and documentation are clean, bumps the patch version, refreshes the generated agent reference, commits the release-owned files, tags, and pushes. Active, untracked .scope/events/*.json and .scope/receipts/*.json runtime records do not block a release and are never staged by the wrapper; tracked changes and all other untracked files still block it. From there, .github/workflows/release.yml takes over:

  1. Runs the full Node 20/22/24 suite plus the live PostgreSQL integration suite.
  2. Verifies the tag matches package.json and inspects the npm payload.
  3. Uses a short-lived GitHub OIDC identity to run npm stage publish; no long-lived npm token is stored in GitHub.
  4. Waits for npm's malware scan and an explicit maintainer approval with 2FA before treating the package as released.
  5. Fetches the GitHub source tarball and computes its sha256.
  6. Patches Formula/scope.rb and pushes it into briannadoubt/homebrew-tap via an SSH deploy key.
  7. Creates a GitHub release with auto-generated notes.
  8. Calls the hosted deployment workflow only after the release succeeds.

The npm package is configured with a stage-only trusted publisher for briannadoubt/scope's release.yml workflow and the strict publishing-access policy. Direct token publishing is intentionally disabled. During a release, approve the scanned version from npm's Staged Packages view; the workflow will then continue automatically.

Bump types:

npm run release            # patch
npm run release minor
npm run release major
npm run release 1.0.0      # explicit

Repo layout

.
├── bin/scope.js              # CLI entrypoint
├── src/
│   ├── index.js             # public library API (openWorkspace + vocab)
│   ├── cli.js                # commander wiring
│   ├── db.js                 # SQLite schema, migrations, id generation
│   ├── repo.js               # data layer (emits change events)
│   ├── events.js             # in-process bus
│   ├── server.js             # Express: REST + SSE + UI
│   ├── hub.js                # auto-discovery + watchdog
│   ├── workspaces.js         # workspace registry
│   ├── format.js             # terminal table / board renderers
│   └── web/                  # vanilla-JS SPA (no build step)
├── App/                      # SwiftUI iOS client
├── skills/                   # agent skills (Claude / Codex / Cursor)
├── Formula/scope.rb          # Homebrew formula
├── .github/workflows/        # tag-driven release
└── scripts/release.sh        # local bump + tag + push wrapper

License

MIT — see LICENSE.