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

@cr8rcho/alkahest

v0.1.104

Published

Screen-graph CLI that reverse-engineers a product from code (React/Next static analysis → product map)

Readme

Alkahest

English · 한국어

Reverse-engineer the product from the code, so people can make product decisions.

Alkahest is a CLI that statically analyzes a UI codebase and builds a Product Map. It extracts screens as nodes, and the navigation between screens plus the API/data calls each screen makes as edges — then draws it as an interactive graph on the hosted viewer (alkahest.app) and helps you write PRDs and requirements. Scanning is local and free; viewing a map happens on the hosted viewer — sign up, create a token, and publish for a shareable link.

Platforms are pluggable via adapters — 21 today across React (Next.js, React Router, Remix), Vue (Nuxt, Vue Router), Angular, Svelte, Astro, React Native, SwiftUI/UIKit, Jetpack Compose, Flutter, and server-rendered Django/Flask/Rails (+ plain HTML). The data model is platform-agnostic, so a new framework is just a new adapter.

Where the references (graphify, codegraph, Understand-Anything) build code-symbol graphs, Alkahest aims one level up — screen-level product understanding — for a target audience of PMs / product folks.

Screen ──navigate (Link / router.push / redirect)──▶ Screen
   │
   └──call (fetch / useQuery / server action)──▶ Resource (API endpoint / data)

Two-layer graph

  • Nodes: Screen (route/page) · Resource (the API/data a screen calls)
  • Edges: Transition (screen → screen) · Call (screen → resource)
  • When several screens call the same resource they share one node — so the graph reveals "which screens use /api/orders together" (data dependencies & change impact).

No API key — the agent reasons

The core output map.json is deterministic and needs no LLM key. Alkahest never calls an LLM itself. When you want narrative output (summaries, PRDs, requirements), your agent does the reasoning: connect Alkahest as an MCP server, and Claude Code / Codex / Cursor query the product map and write the prose themselves — no key, no SDK.

You ── "write a PRD for the checkout screen" ──▶ Agent (Claude Code / Codex)
                                                    │  get_screen / who_calls (MCP)
                                                    ▼
                                                 Alkahest  →  map.json (deterministic, no key)

Install

npm i -g @cr8rcho/alkahest      # the command is `alkahest`
# or run without installing:  npx @cr8rcho/alkahest <command>

From source (contributors):

git clone https://github.com/cr8rcho/alkahest-cli.git
cd alkahest-cli && npm install && npm run build && npm link

Quickstart (Claude Code)

The full flow for a Claude Code user, from zero to a shared graph + PRDs on the hosted viewer:

# 1. Install alkahest:  npm i -g @cr8rcho/alkahest   (or build from source above + `npm link`)

# 2. In your project root, build the product map
cd ~/my-next-app
alkahest scan                 # → .alkahest/map.json

# 3. Publish it — this is where the map becomes viewable
#    (sign up at alkahest.app → Account → Create token)
alkahest login --token alk_xxxxx
alkahest publish              # → https://alkahest.app/p/<project>  (shareable, no login to view)

# 4. Register the MCP server with Claude Code (project scope = shared via .mcp.json)
claude mcp add alkahest -s project -- alkahest mcp
#   verify it's connected:  run `claude` then `/mcp`  → "alkahest" should be listed

Now just talk to Claude Code — it uses the alkahest MCP tools for you:

You:  "Give me an overview of this product's screens."
        → Claude calls overview → summarizes the structure.

You:  "Write a PRD for the checkout screen and the cart screen."
        → Claude calls get_screen / who_calls to read the structure,
          writes each PRD, and calls set_prd to save it into map.json.

You:  "publish it"
        → Claude calls publish and returns the link. Open it — click a
          screen node and the panel shows the Summary + PRD it just wrote.

That's it: scan → publish → register MCP → ask Claude. No API key — Claude does the writing, alkahest stores it in map.json, and the hosted viewer renders it after each publish.

Usage

Run it from the root of the project you want to analyze; outputs land in that project's .alkahest/ folder.

alkahest scan          # analyze → .alkahest/map.json (incremental by default)
alkahest scan --full   # ignore the baseline and rescan everything
alkahest hook install  # run scan automatically on commit/merge (diff-driven refresh)
alkahest mcp           # run the MCP server (agents query the product map; no key)
alkahest login         # save your API token (Account → API tokens on alkahest.app); bare = show current state
alkahest status        # are you signed in, and where does this checkout publish?
alkahest logout        # forget the saved token (local only — revoke it on the web to cut it off)
alkahest publish       # upload the map to the hosted viewer → shareable link
alkahest projects      # list your account's workspaces & projects (recover a slug after a move, etc.)
alkahest history       # a code map's publish timeline — when each publish happened & what changed
alkahest comments pull # pull comments left on the published map → .alkahest/comments.json
alkahest comments promote <ids…>  # promote the given comments into ONE alkahest issue on the issue map (targeted at their node) + link it back (--title, --map, --type, --status, --force)
alkahest comments resolve <id>  # mark a comment resolved (or --reopen) — author or project owner only
alkahest issues pull   # pull the project's Issue Map (graph-shaped issue tracker) → .alkahest/issues.json
alkahest issues add <title>     # create an issue (--parent epic, --target s:…/r:…//route, --type/--status, --priority/--due/--assignee)
alkahest issues done <id>       # mark an issue finished (other ops: status / priority / due / assign / archive / restore / link / rm)
alkahest issues edit <id>       # edit title / body / type / code-map target / props / parent epic in one call (--parent none detaches; 'none' clears --body/--target)
alkahest issues map <id>        # place an issue on an issue map (--map), or take it off with --remove (never deletes the issue)
alkahest notes add <title>      # create a markdown note on the Note Map (--body, --folder, --map; --props '{"k":v}' sets property values)
alkahest notes list             # list a note map's notes (--q to search); show <slug> for one note with connections
alkahest notes update <slug>    # edit a note in place (--title/--body/--rename; --props '{"k":v}' sets property values, null deletes a key) — update, don't re-add
alkahest notes delete <slug>    # move a note to the Trash (soft delete; --reason "<why>" required, restorable for 30 days); restore <slug> undoes
alkahest notes map <slug>       # place a pool note on a note map (--map), or take it off with --remove (never deletes the note)
alkahest notes import <dir>     # import an Obsidian-style folder of .md files: one note per file (--map, --exclude, --dry-run; re-run to refresh — idempotent by source_path prop, then title, so retitled docs update in place; files already current make no write call)
alkahest notes props            # edit a note map's property schema — --define <key:type[:opt,opt]…> registers, --remove <key…> unregisters (values kept, shown "unregistered"; --map)
alkahest preset list            # list the installable presets — packaged conventions your agent follows (as-built docs, llm-wiki)
alkahest preset install <id>    # install one: account skills + note maps, plus the repo half when the preset has one (docs/ scaffold, reference sync script, CLAUDE.md snippet; --force, --claude-md, --slug) — everything rides public primitives, so your own setup loses nothing by skipping it. `alkahest presets` / `alkahest docs init` are aliases
alkahest preset update [id]     # bring installed preset skills / reference script / CLAUDE.md snippet up to the current version: unedited copies are replaced, edited ones merged (your edits kept; where an edit and a preset change hit the same lines the preset's win and the replaced lines are listed; a copy that can't be merged safely — your own rewrite, or a merge that wouldn't run — is left untouched and the preset's change printed). The as-built sync script is the preset's ENGINE — always replaced; your settings live in `scripts/sync-docs-maps.config.mjs`, which updates never touch (an edited old script switches only once the engine stages the same notes; `--adopt` forces it). --dry-run, --json, --adopt
alkahest skills list            # list your skills — named markdown instructions an agent follows when writing for you (personal + team, with defaults)
alkahest skills show <name>     # print one skill's body (pipe it into a prompt; --team <workspace> picks a team copy on a name collision)
alkahest map overview           # ask the product map from the shell (the MCP overview / get_screen / who_calls twins): screens + resources with edge counts; JSON output
alkahest map screen <screen>    # one screen in full (features, navigation in/out, resource calls, summary/PRD); --slug <project> reads the PUBLISHED map instead of the local one
alkahest map who-calls <res>    # which screens call a resource — the impact of changing it
alkahest search <q>             # one query across this project's notes and issues and your tasks (the MCP search tool's twin; --json)
alkahest tasks list             # your open tasks with pending thread notes / open questions flagged (--all, --project, --q; writes stay on MCP/web)
alkahest update        # update to the latest GitHub release (--check to only check)

Multiple maps per project. A hosted project is a container that can hold several code maps and several issue maps (e.g. web / api in a monorepo) — all equal, no privileged default. alkahest publish --map <slug> targets a code map (remembered per checkout in .alkahest/project.json); issues add/pull --map <slug> and comments add/pull --map <slug> pick the issue / code map. Omit --map and the CLI uses the project's sole map of that type — the server asks you to pass --map only when there's more than one. Each map is shared at alkahest.app/p/<project>/<map>.

Lost a project's link? A checkout remembers its project slug in .alkahest/project.json, but a fresh clone (or a project moved between workspaces) can lose it. alkahest projects lists every workspace & project on your account so you can re-publish with --slug <slug> (which also re-links the checkout). And publish won't blindly create a duplicate: when the checkout has no link — or its remembered project was deleted on the web (the old slug 404s) — it finds the project that matches this one and asks before overwriting. CI / non-interactive runs stop with the candidates and point you at --slug rather than guessing.

Map interactions (hosted viewer)

Your published map at alkahest.app/p/<project>/<map>:

  • Force-directed layout — nodes settle naturally by their connections. A fixed seed keeps the layout the same every time.
  • Start point is marked with a ▶ prefix on its label (app entry point / root route).
  • Hover a node to highlight its connected edges and neighbors by color (preview).
  • Click a screen to pin it — the right panel shows its features, transitions, and calls.
  • Drag a node to move it (connected neighbors follow), wheel/pinch to zoom, drag empty space to pan.
  • Top right: 🌗 light/dark toggle, ⤢ Fit to frame the whole graph.
  • Edges: solid = navigate, short dashes = contains, long dashes = call.

Agent (MCP) integration

Register the MCP server once, then the agent reads the map and writes summaries / PRDs / requirements itself — no key needed.

# Claude Code (recommended): project scope writes a shared .mcp.json
claude mcp add alkahest -s project -- alkahest mcp

Or add it to any MCP-capable agent's config directly:

{
  "mcpServers": {
    "alkahest": { "command": "alkahest", "args": ["mcp"] }
  }
}

Tools exposed:

| Tool | What it does | |---|---| | scan | (re)build the product map for the project | | overview | list all screens & resources at a glance | | get_screen | one screen's full structure (features, navigation, calls, source) | | who_calls | which screens call a given API/resource (impact analysis) | | set_summary | save a one-line summary onto a screen → shown in the hosted viewer's panel after publish | | set_prd | save a PRD/requirements markdown onto a screen → rendered in the panel after publish | | publish | upload the map to the hosted viewer → shareable link (needs a token, see below) | | comments | list comments left on the published map, each joined to where to act: a screen comment → its source file/route + on-screen elements with line numbers; a resource comment → the screens that call that endpoint (file + line). Lets the agent address feedback in-editor (needs a token) | | resolve_comment | mark a map comment resolved (or reopen) after addressing it (needs a token) | | add_comment / reply_comment | leave a new comment on a map node / reply under an existing one (needs a token) | | promote_comment | promote one or more map comments into ONE alkahest issue on the issue map (targeted at their node) and link it back onto each, so the hosted viewer shows a "promoted" badge (needs a token) | | issues | read the project's Issue Map — a dependency-first issue tracker drawn as a graph. Each issue comes with derived state: done and actionable (nothing unfinished blocks it), so the agent can pick what to work on next (needs a token) | | add_issue | create an issue while planning with the user — parent_id groups under an epic, target ties it to the code map (existing node key, or a planned /route that converges when the screen ships); props sets issue properties (reserved key tags) (needs a token) | | update_issue | move status (e.g. to done when the work ships — progress gets painted onto the map), edit fields, archive/restore (archived: true|false), or delete (needs a token) | | link_issues | add/remove an edge between issues: blocks (dependency), contains (epic→task), relates (needs a token) | | map_issue | place an issue on an issue map, or take it off with remove — issue maps are lenses over the project's issue pool, so an issue can appear on several maps; removing it from one never deletes the issue (needs a token) | | list_tasks | read your personal tasks — a private checklist item (only you see it); open by default, status: all includes done, project filters by a project tag (needs a token) | | add_task | add a personal task to the user's list — private, shows in their home Tasks band + activity feed (chipped as the agent). No project or publish required — omit project for a personal Inbox task, or tag one with project (slug); add free tags. Promotable to an issue later; for shared/team work use add_issue. Put the context — why it matters, what "done" looks like, and a [label](url) link to the evidence — in body, which renders as markdown; there is no separate link field. dedup_key keeps re-scans idempotent (needs a token) | | promote_task | turn a personal task into a full issue — the same verb as the web's "Promote to issue": issue from the task's title/body, lineage kept (the task leaves the open list), the task's thread moves onto the issue, one Promoted row in the feed. Use this instead of add_issue + complete_task when a task grows teeth. Needs a project-filed task + editor access; map places it on an issue map (default: pool) | | notes | read the project's Note Map — markdown notes drawn as a mindmap, with per-project slug addresses and hand-drawn edges. Check it before adding: update an existing note instead of creating a near-duplicate (needs a token) | | get_note | one note in full: markdown body, connections and backlinks, plus its issue/code links (needs a token) | | add_note | create a markdown note to record durable knowledge — a decision, a convention, a constraint; parent_id draws a mindmap child edge (needs a token) | | update_note | edit a note in place when knowledge on a topic evolves — update, don't re-add; delete: true + a one-line reason soft-deletes it to the Trash (restorable 30 days), restore: true brings it back (needs a token) | | link_notes | connect a note to another note, an issue (issue:<uuid>), or a code-map node (code:s:…/code:r:…) — all links are explicit; bodies are never parsed (needs a token) | | map_note | place a pool note on a note map, or take it off with remove — note maps are lenses over the project's note pool, so a note can sit on several maps; removing it from one never deletes the note (needs a token) | | check_version | report installed vs latest GitHub release (so the agent can suggest alkahest update) |

The agent reads with get_screen / who_calls and writes back with set_summary / set_prd; both write into map.json — publish and the hosted map reflects the latest.

Publishing from the agent (optional). scan / read / write-back need no key. publish does — it uploads the map to your account on the hosted viewer. Get a token at alkahest.app → Account → Create token, then put it in the MCP config so the server can authenticate:

claude mcp add alkahest -s project \
  -e ALKAHEST_TOKEN=alk_xxxxx \
  -- alkahest mcp
{
  "mcpServers": {
    "alkahest": {
      "command": "alkahest",
      "args": ["mcp"],
      "env": {
        "ALKAHEST_TOKEN": "alk_xxxxx"
      }
    }
  }
}

The token is all you need — the CLI defaults to the hosted service (alkahest.app). If you've already run alkahest login, even the token env is optional (saved credentials are used). Then just ask the agent to "publish this" and it returns the link.

ALKAHEST_API_URL (or alkahest login --api <url>) points the CLI at a different backend deployment. The backend is not part of this repo — the MIT scope is the scanner, the adapters and the MCP server — so unless you are developing against your own Supabase instance, leave it unset and the CLI talks to alkahest.app.

Working without a checkout — notes, issues, maps. Only the code map comes from a folder. Notes, issues and maps are addressed by project slug, so the agent can write to a project the current folder knows nothing about — and the project never has to have been published:

notes / add_note / issues / add_issue / maps / create_map / search / …
  → project: "my-wiki-2772c2"      # a slug from the list_projects tool

alkahest publish binds a checkout to a project (.alkahest/project.json), and inside such a folder the parameter is redundant. Everywhere else, name the project. To make one MCP server permanently point at a single project — a personal wiki, say — set it once in the config:

{ "env": { "ALKAHEST_TOKEN": "alk_xxxxx", "ALKAHEST_PROJECT": "my-wiki-2772c2" } }

The env var is the last fallback: a linked checkout still wins over it, so a wiki-configured server won't hijack a real code project. On the CLI the same address is --slug <slug> (alkahest projects lists them).

Output — .alkahest/

.alkahest/
└─ map.json       # the canonical ProductMap (source of every output)

map.json is the only artifact the scan produces — it's what publish uploads (source code never leaves the machine) and what the MCP tools read and write. Add .alkahest/ to your .gitignore.

Incremental & auto-refresh

scan is incremental by default — it compares file hashes against map.json, re-parses only the changed screens, and preserves everything else. Run alkahest hook install to wire a git hook so the map refreshes automatically on every commit/merge.

Scope & limitations

Adapters (21, under src/core/adapters/), grouped by framework:

  • React — Next.js (app & pages router), React Router (Vite/CRA), Remix, plain JSX
  • Vue — Nuxt, Vue Router, Vue SFC · Angular · Svelte (SvelteKit) · Astro
  • React Native — Expo Router, React Navigation
  • iOS — SwiftUI, UIKit · Android — Jetpack Compose · Flutter
  • Server-rendered — Django, Flask, Rails · plain multi-page HTML

How a couple map source → graph:

| Adapter | Screen | Navigate | Call | |---|---|---|---| | Next.js app-router | app/**/page.tsx | <Link> · router.push · redirect | fetch · query hooks | | SwiftUI | struct X: View | NavigationLink · .sheet · .fullScreenCover · navigationDestination | URL(string:) · URLRequest |

  • Limitations: parsing is per file/view — features/calls inside imported child components aren't traced yet. Dynamic targets (router.push(variable), a useQuery hook's URL, etc.) are marked "unresolved".
  • The data model is platform-agnostic — a new platform is just one adapter under src/core/adapters/.

Development

npm install
npm run build
node dist/cli.js scan examples/sample-next        # try it on the bundled fixture
node dist/cli.js scan examples/next-shell-mini   # app shell: layout sidebar + nav table + thin pages
npm run typecheck

The single source of truth for the design is ALKAHEST.md.

License

MIT — see LICENSE. This covers everything in this repository (the scanner, the adapters, and the MCP server). The hosted service at alkahest.app — including the map viewer — is a separate, closed codebase.