{
  "markdown": "# Mellos Mapping\n\n[![CI](https://github.com/GuangminJu/mellos-mapping/actions/workflows/ci.yml/badge.svg)](https://github.com/GuangminJu/mellos-mapping/actions/workflows/ci.yml)\n\nEnglish | [简体中文](README.zh-CN.md)\n\nA live, terminal-native map of bottom-up development for\n[Claude Code](https://claude.com/claude-code) and Codex CLI.\n\n<p align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: light)\" srcset=\"docs/demo-light.svg\">\n    <img alt=\"A Mellos map building itself: ghost design first, spinners climbing the layers, a cracked foundation spreading upward, honest recovery\" src=\"docs/demo.svg\" width=\"620\">\n  </picture>\n</p>\n\nWhile Claude builds your system, a split pane beside the conversation shows\nthe system's **layered dependency map**: primitive layers at the bottom,\ndependency edges that may only point downward, ghost nodes for what is\ndesigned, a spinner on what is being built right now, and solid green for\nwhat is built *and verified*.\n\n<p align=\"center\">\n  <img alt=\"A Claude Code session with the Mellos map pane beside it: a six-layer game engine design, L0 and L1 nodes solid green, the upper layers still dashed ghost nodes\" src=\"docs/session-claude-code.png\">\n</p>\n\n*A real session: Claude Code on the left, the map pane on the right. L0 is\nverified, L1 just lit up, everything above is still a ghost.*\n\n## Why\n\nMost progress reporting is a task list — a top-down worldview. A Mellos map\ngrows the other way: an upper node can only stand on nodes below it, and the\npicture makes the discipline visible:\n\n- **The ghost design appears before any code.** Claude declares the whole\n  intended structure as dashed ghost nodes first; you can veto a bad design\n  while it is still only a picture.\n- **The spinner is where Claude's attention is.** One glance answers \"what is\n  it doing right now, and on top of what?\"\n- **Done means verified.** A node turns solid green only with evidence (a\n  passing test run). If later work cracks a foundation, the node turns red —\n  a cracked foundation under a spinning upper floor is the most honest status\n  report there is.\n- **The map is a ledger, not a judge.** The tools refuse only structural\n  corruption (an edge pointing upward, a duplicate rank). Workflow is\n  Claude's discipline, defined in the bundled skill; violations are made\n  *visible*, never silently blocked.\n\n## Install\n\nTwo lines inside any Claude Code conversation:\n\n```\n/plugin marketplace add GuangminJu/mellos-mapping\n/plugin install mellos-mapping@mellos-mapping\n```\n\nOr one line in a terminal:\n\n```\nclaude plugin marketplace add GuangminJu/mellos-mapping && claude plugin install mellos-mapping@mellos-mapping\n```\n\nRequires Node.js 18+ on PATH (Claude Code itself requires Node, so you\nalready have it). No build step: `dist/` is committed, so a clone runs as-is —\n`dist/server.mjs` (the MCP server), `dist/watch.mjs` (the pane),\n`dist/mmap.mjs` (the `mmap` toggle), `dist/hook-session-start.mjs` (the\n`SessionStart` hook that `hooks/hooks.json` registers) and\n`dist/store-paths.mjs` (the store's path vocabulary, which the plain-node pane\nlauncher imports instead of restating filenames).\n\nThe first session after installing asks you **one** question — how eager\nmapping should be — and records the answer for every project you will ever\nopen. From then on the hook carries it into each new session by itself; there\nis no per-project setup step. See\n[Setup: choose when maps open](#setup-choose-when-maps-open).\n\nThe `mmap` terminal command (the pane's open/close toggle) installs itself on\nWindows: the same hook notices on session start when the shim is missing or\npoints at an older install, writes `mmap.cmd` (cmd, PowerShell) and `mmap`\n(git-bash) into `%LOCALAPPDATA%\\mellos-mapping\\bin`, appends that one\ndirectory to your **user** PATH, and tells you so through the assistant. The\nPATH change reaches only new processes — and a new tab of a running Windows\nTerminal inherits the old environment, so close the terminal app entirely and\nreopen it before the first `mmap`. The PATH edit keeps the\ninstaller's guarantees: nothing happens when the entry is already there, and\na PATH that `setx` would damage (flattened `%VARIABLE%` references, truncation\npast its limit) is refused outright, with the entry to add by hand named\ninstead.\n\nThe step behind it is still a command of its own, for the cases the hook does\nnot cover — `--uninstall`, or re-adding a PATH entry you removed while the\nshims stayed put:\n\n```\nnode \"<plugin dir>/scripts/install-mmap-command.mjs\" [--uninstall]\n```\n\n(`--json` prints the install outcome as one JSON line instead of prose — the\nmode the hook itself calls it in.) `npm i -g mellos-mapping` provides the\nsame `mmap` via `bin`, no shims involved.\n\n## Update\n\n```\nclaude plugin marketplace update mellos-mapping && claude plugin update mellos-mapping@mellos-mapping\n```\n\nTwo steps because `plugin update` compares against the locally cached\nmarketplace clone — the first command is what actually pulls this repo.\nRestart Claude Code to apply. Releases are version bumps on `master`.\n(In-app, `/plugin` opens the same management UI.)\n\n### Upgrading from 0.19\n\n0.20 moved the store out of `.claude/` — the map belongs to this tool, not to\none client — into `.mellos/`. The server and the watcher each perform the move\nonce at startup, and print exactly one line on stderr when they do\n(`mellos-mapping: moved the legacy .claude map store to .mellos/ — commit the\nmove.`):\n\n| 0.19 and earlier | 0.20 and later |\n| --- | --- |\n| `.claude/mellos-mapping.json` | `.mellos/map.json` |\n| `.claude/mellos-mapping.pages/` | `.mellos/pages/` |\n| `.claude/mellos-mapping.config.json` | `.mellos/config.json` |\n\nNothing is merged and nothing is overwritten: a project that already has a\n`.mellos/` store (map, pages or config) is left untouched, whatever the legacy\ndirectory still holds. If you keep your maps in git, commit the move —\n`git add -A .claude .mellos` records it as renames rather than as a pile of\ndeletions plus untracked files.\n\nThat one move is the only time either process touches `.claude/`. Afterwards\nthe tools write nowhere but `.mellos/`, and never outside the project\ndirectory they resolved at startup.\n\n## Codex CLI\n\nThe same repo doubles as a Codex plugin (codex-cli 0.147+). Three lines:\n\n```\ncodex plugin marketplace add GuangminJu/mellos-mapping\ncodex plugin add mellos-mapping@mellos-mapping\nnode ~/.codex/plugins/cache/mellos-mapping/mellos-mapping/<version>/scripts/codex-register.mjs\n```\n\nThe first two install the skill (the map discipline) as a Codex plugin. The\nthird registers the MCP server at user level — needed because Codex spawns\nplugin-bundled MCP servers inside the plugin cache with no way to see your\nworkspace, so a bundled server would write the map into the cache. A\nuser-level `codex mcp add` entry (which the script writes) inherits each\nsession's working directory instead: the state file lands in your project,\nsame as under Claude Code. The registered path is version-specific — re-run\nthe script after updating the plugin.\n\nTo watch the live pane beside a Codex session on Windows, run\n`node <plugin root>/scripts/open-pane.mjs <project dir>` — it splits the\nterminal window hosting the session (or falls back to a dedicated\n\"mellos-mapping\" window; `--window` picks that on purpose). Add\n`--page <slug>` to open on a particular page — and with a pane already open,\nrerunning with `--page` retargets it instead of opening another. The pane\nauto-follows the page being written — the map the agent is operating on\nright now; press `f` to toggle that (a manual page switch also turns it\noff), or start with `--no-follow`. Elsewhere run\n`node <plugin root>/dist/watch.mjs` from the project directory in a second\nterminal (or any terminal split). Both take the same flags — see\n[Pane flags](#pane-flags).\n\n## Any MCP client\n\nThe server ships on npm, so any MCP client (Cursor, Windsurf, Zed,\nGemini CLI, …) can run it with a standard stdio entry:\n\n```\nnpx -y mellos-mapping\n```\n\nThe map file lands in the client session's working directory\n(`.mellos/map.json`). Open the live pane from the same project:\n\n```\nnpx -y -p mellos-mapping mellos-mapping-watch\n```\n\nThe server picks its project directory in this order: `MELLOS_MAPPING_CWD`\n(an explicit override, for clients that spawn servers from a fixed\ndirectory), then `CLAUDE_PROJECT_DIR` (what Claude Code sets for plugin MCP\nservers), then the server process's own working directory. Set\n`MELLOS_MAPPING_CWD` when your client would otherwise start the server\nsomewhere other than the project you are working in.\n\nThe skill/discipline layer is Claude Code + Codex specific; other clients\nget the five `mmap_*` tools and the pane, and bring their own prompting.\n\n## Use\n\n1. Ask Claude to build something non-trivial. The bundled skill has Claude\n   declare the ghost design and keep the map current as it works.\n2. The pane opens itself. Every write tells Claude whether anybody is\n   actually looking (see [Who is watching](#who-is-watching)), and Claude\n   opens or retargets the pane with `mmap_open` when nobody is — you never\n   have to remember to. Open or close it yourself with `mmap` in any\n   terminal, or `/mellos-mapping:mmap` in the conversation (Windows\n   Terminal split on Windows, tmux split inside tmux, or a printed command\n   to run in any second terminal). On Windows the pane opens in the terminal\n   window hosting YOUR session, even with several windows open; pass\n   `--window` to put the map in its own dedicated window instead. Prefer\n   `--ascii` if your font lacks box-drawing glyphs.\n3. Watch nodes light up from the bottom. Interrupt when the picture worries\n   you — that is what it is for.\n\nThe pane is mouse-aware (xterm SGR any-event tracking — the same protocol\nhtop and tmux speak):\n\n| Input | Action |\n| --- | --- |\n| hover a node | spotlight its wires; preview its details below the map |\n| click a node | pin it — details stay resident after the mouse leaves |\n| click empty space / `Esc` | unpin; with nothing pinned, `Esc` climbs out of a dive |\n| wheel / `+` `-` | zoom, anchored on the focused node (see the ladder below) |\n| left-drag | grab and pan when the map outgrows the pane |\n| shift+wheel | scroll vertically |\n| wheel tilt (horizontal) | pan sideways |\n| `hjkl` / arrows | nudge the view |\n| `Tab` / `Shift+Tab` / `1-9` / click a tab | switch pages (parallel maps) |\n| wheel on the tab row / click `‹` `›` | browse an overflowing tab strip without switching pages |\n| `f` | toggle auto-follow (see [Pages](#pages)) |\n| `x`, or click the `×` on the active tab | ask to delete the page on screen; press again inside the window and its file is removed (see [Pages](#pages)) |\n| double-click a `⊞` node | dive into its sub-map (a child page) |\n| `Backspace` / `Esc` | climb back out of the last dive |\n| drag the `⋯` divider | resize the detail panel — pull it up to read long design notes in full |\n| `0` | reset pan and zoom |\n| `q` / `Ctrl+C` | quit the pane |\n\nEvery other key is inert, on purpose: an escape sequence the pane does not\nknow (F-keys, Home/End, PgUp/PgDn, Insert/Delete, modified arrows) is\nconsumed whole and does nothing, rather than having its payload bytes read\nas hotkeys.\n\nZooming scales the picture first and switches display mode only at the ends\nof the ladder, so every level still shows meaningful data:\n\n```\ndetail+ ← detail ← 100% ← 85% ← 70% ← 55% ← overview\n```\n\n- **zoom in past 100%** — `detail` unfolds evidence and the first three lines\n  of the design notes inside the boxes; `detail+` widens them into reading\n  cards (up to twelve note rows);\n- **85–55%** — whitespace tightens and labels truncate proportionally, boxes\n  stay boxes;\n- **below 55%** — labels would stop meaning anything, so the map AGGREGATES:\n  each declared group (a labeled subsystem within a band) becomes one box\n  named `foundation subsystem 1/2` with its status derived from the members, edges\n  collapse onto the groups, ungrouped nodes stay themselves. Like a real\n  map, zooming out shows province names — not anonymous dots. (A map with\n  no groups falls back to a pure glyph constellation with per-band counts.)\n  The footer always names the level.\n\nBelow the map, between it and the hint line, sits a fixed-height detail\npanel: a separator you can drag, a status-colored header, the focused node's\nevidence, both wire directions (`uses → … · used by ← …`, each neighbour\ncarrying its own status glyph) and its design notes, word-wrapped. With\nnothing focused it shows the map's dashboard instead. Fixed height — details\nnever float over the map and the layout never jumps.\n\n### Glyphs\n\nOne status, one glyph, everywhere a map is drawn — the pane's boxes, its tab\nstrip and detail panel, and any other client reading the same store:\n\n| Unicode | ASCII | Meaning |\n| --- | --- | --- |\n| `·` | `.` | planned — declared, not started |\n| `⠿` | `*` | in-progress, at rest — a box that can animate spins through the braille frames (`⠋⠙⠹…`) instead, or a four-bar cycle in ASCII |\n| `■` | `#` | done, with evidence |\n| `□` | `o` | done, with **no** evidence recorded — same claim, nothing behind it |\n| `✗` | `X` | regressed: was done, now broken |\n| `⊞` | `+` | badge: the node links a sub-map; double-click dives in |\n\nThe legend under the picture names the four statuses; `□ done, no evidence`\njoins it only on a map that actually contains one — the four are the\nvocabulary, that one is a rule being broken here and now. Documentation\ndiagram kinds replace the status legend with their node-kind glyphs.\n\n### Pane flags\n\nBoth the pane launcher (`scripts/open-pane.mjs <project dir>`) and the\nwatcher (`dist/watch.mjs`) take the same watcher flags; the launcher forwards\nthem verbatim and rejects anything it does not know rather than dropping it.\n\n| Flag | Effect |\n| --- | --- |\n| `--page <slug>` | open on this page; with a pane already running, retarget that pane instead of opening another |\n| `--ascii` | pure-ASCII repertoire, for fonts without box-drawing glyphs |\n| `--no-color` | no ANSI color |\n| `--no-mouse` | no mouse reporting, if your terminal multiplexer wants the mouse for itself |\n| `--no-follow` | start with auto-follow off |\n| `--interval <ms>` | poll interval; default 250, floored at 50 |\n\nLauncher-only: `--window` opens the dedicated \"mellos-mapping\" window instead\nof splitting the session's window, and `--force` opens another pane even\nthough one is already running for this project. Watcher-only: `--file <path>`\nnames the default page's state file (the launcher derives it from the project\ndirectory).\n\n### The mmap command\n\n`mmap`, typed in any terminal, is a **toggle**: it opens the map pane for the\nproject you are standing in, or closes the one that is already open.\n\n| You type | What it does |\n| --- | --- |\n| `mmap` | nothing watching this project → open the pane; something watching → close it |\n| `mmap <page-slug>` | open on that page, or retarget an already-open pane to it — never closes |\n| `mmap --window` | open in the dedicated \"mellos-mapping\" window instead of splitting this one |\n| `mmap --force` | open another pane even though one is already running |\n\nThe project is found the way git finds its root: from the current directory\nupwards, to the nearest one holding a `.mellos/` store. Standing in a project\nthat has no map yet is fine — the pane opens on its standby screen and says so\nuntil the first `mmap_declare`.\n\nClosing goes through the store rather than through a signal: `mmap` writes a\none-shot request beside the map, the pane consumes it on its next poll (250 ms\nby default) and exits, handing the terminal back exactly as it found it — mouse\nreporting off, cursor visible. A pane still on the standby screen closes the\nsame way. The request is deleted as it is read, and a leftover from a pane that\ndied is swept when the next one starts, so a stale request can never close a\nfresh pane.\n\nEvery watcher flag above works here too, forwarded verbatim; an unknown one is\na usage error, never dropped in silence. `mmap` needs\n[installing once](#install) unless you have the npm package. Inside a Claude\nCode conversation, `/mellos-mapping:mmap` opens the same pane.\n\n### Pages\n\nA project can keep several maps side by side — **one effort = one page**.\nClaude targets a page by passing `page` to any `mmap_*` tool; the pane grows\na tab bar as soon as a second page exists. The active tab is bold in its\nmap's aggregate status color. Each page remembers its own pan, zoom and\npinned node.\n\nBy default the pane **follows the page being written** — the map the agent is\noperating on right now — so declares and updates bring the audience along by\nthemselves. `f` toggles that, a manual page switch turns it off, and\n`--no-follow` starts it off; with follow off, a background page's change\nlights its tab in status color instead of stealing your view. An explicit\n`--page` outranks follow, and a page requested before it exists is shown the\nmoment it appears.\n\n**Deleting a page.** An effort ends; its page does not have to stay. In the\npane, `x` — or the `×` the active tab carries when the mouse is on — *asks*:\nthe footer says `press x again to delete <page>`, and a second press within\nthree seconds removes that page's file. Switching page, `Esc` or simply\nwaiting takes the request back. The `×` sits on the active tab only, so\nclicking an inactive one switches to it first and offers its `×` on the next\nframe. From a tool call it is `mmap_remove {pages: [\"slug\", …]}`, applied\nafter that call's map edits. Either way the file is gone for good — the maps\nare plain JSON, so committing them is the only undo there is.\n\nState lives in the tool-owned `.mellos/` directory at the project root:\n\n| Path | What it is |\n| --- | --- |\n| `.mellos/map.json` | the default page — optional; a project whose work lives on named pages has none |\n| `.mellos/pages/<slug>.json` | one file per named page |\n| `.mellos/config.json` | the project's mapping policy (see [Setup](#setup-choose-when-maps-open)) |\n| `.mellos/focus` | one-shot \"show this page\" request from a launcher to a running pane; the pane consumes it and deletes it within a poll tick |\n| `.mellos/quit` | one-shot \"close yourself\" request from the `mmap` toggle, consumed and deleted the same way |\n| `.mellos/viewers/<pid>.json` | one report per live pane — the page it is showing, whether auto-follow is on — refreshed every second while it runs (see [Who is watching](#who-is-watching)) |\n| `<any of the above>.<pid>.<random>.tmp` | a save in flight; it is renamed over its target or removed. A leftover means a write failed (and was reported) and even its cleanup could not run |\n\nThe map files are plain JSON, safe to commit if you want the maps' history in\ngit. The other three are runtime chatter between a pane and whoever is talking\nto it — gitignore `focus`, `quit` and `viewers/` if you commit the store.\n\n**Concurrency, stated plainly.** Every save is atomic — written to a private\nsibling temp file and renamed over the target — so a reader polling the store\nsees the previous complete map or the new one, never a torn write. There is\nno lost-update protection: two writers saving the *same* page race, and the\nlast rename wins, silently discarding what the other computed from an older\nread. Pages are the isolation unit — two sessions that must not clobber each\nother belong on two pages, which is also the answer to running several Claude\nsessions in one project.\n\n### Diagram kinds\n\nThe default kind, `dev`, is the living progress ledger described above. The\nsame layered-DAG machinery also draws documentation diagrams: pass `kind`\nin `mmap_declare` and the page renders neutrally — plain solid boxes, no\nghosts, no spinners, no progress counts.\n\n| Kind | Reading | Extras |\n| --- | --- | --- |\n| `architecture` | layered components (also module deps, call graphs) | edge labels for protocols |\n| `dataflow` | pipeline stages as layers, sources at the bottom | edge labels for the data |\n| `behavior-tree` | leaves (actions) at the bottom, root on top (also mind maps, WBS) | node kinds `selector` `sequence` `parallel` `decorator` `condition` `action` render as glyphs |\n| `sequence` | classic call/return: time flows top-down, participants as lane headers; every call and every return is an event in the acting participant's lane | `lanes` are participants; edge labels are messages |\n\nNode kinds and edge labels work on `dev` maps too. State machines are out\nof scope on purpose: transitions cycle, and edges here only point downward.\n\n### Sub-maps\n\nA node can link a child page with `submap: <page-slug>` — the pane badges it\n`⊞`; double-click dives into the child map, `Backspace` climbs back out. A\nmap of maps, built entirely from pages: no new storage, no new invariants.\nWhether a node deserves a sub-map is the AI's judgment call — most don't.\n\nSub-maps are interior detail, not siblings: a page some *other* page dives\ninto never occupies a tab. Two refinements keep the tab strip from erasing\nitself — a page whose own node names itself hides nobody, and a link cycle\nkeeps its tabs unless a page outside the cycle dives in, because a cycle has\nno outside to climb back to. Inside a dive the tab row becomes a breadcrumb —\n`⌫ parent map ▸ node` — and clicking it (or `Backspace`) climbs back out.\nWhen a hidden sub-map changes in the background, the footer says so.\n\n## MCP tools\n\n| Tool | Purpose |\n| --- | --- |\n| `mmap_declare` | Grow the map: title (`null` removes it), diagram kind, layer bands, lanes, groups (subsystems), nodes — with `status`, `evidence`, `detail`, `kind`, `group`, `lane`, `submap` — and edges, optionally labeled (all-or-nothing batch) |\n| `mmap_update` | Record progress **and revise**: status (`planned → in-progress → done` +evidence, `regressed`), relabel a node, move it to another band (`layer`), join/leave a group or lane, set a node kind or a `submap`; rename and re-rank bands (`layers`), relabel groups (`groups`) and lanes (`lanes`); `null` clears any clearable field |\n| `mmap_remove` | Revise: drop edges, nodes, groups, lanes, empty bands — and, with `pages`, whole pages, file and all (permanent; applied after this call's map edits) |\n| `mmap_view` | Render the current map as text inline (optional `zoom`, `-4`…`2`), ending with a `pages:` line naming every page the project has and which one you are looking at |\n| `mmap_setup` | Get/set the project's mapping policy — when maps open |\n| `mmap_open` | Put the map on your screen: open the pane, or retarget an open one to a `page` (`window: true` for the dedicated window). It answers with whether a pane actually reported in afterwards, not merely that a command ran — and it can never close one |\n\nA batch applies bands → groups → lanes → node updates, and within one node\nupdate `layer` moves the node before its other fields, so a node can move and\njoin a group on its new band in one item.\n\nWhat the boundary refuses, so the ledger never records something it did not\nmean:\n\n- **an unknown key**, naming it — a misspelled `evidance` is an error, not a\n  silently dropped field, at every nesting depth;\n- **control characters** in text fields — an ESC sequence stored in a label\n  would let a map repaint the terminal of everyone who opens it. `detail` is\n  the exception: newlines and tabs are how a note is written, everything else\n  (ESC, BEL, lone CR) is still refused;\n- **an empty string** where a field is optional — `null` is how a field is\n  cleared, never a blank that renders as a box nobody can tell from a real\n  one;\n- **a node whose `submap` names the page the call itself targets** — a link\n  with no bottom, not a parent link;\n- **a page deletion aimed at the page the same call targets, or at a slug the\n  project does not have** — one call must not edit a map it is deleting, and\n  a name matching no page is a typo far more often than a race; the refusal\n  lists the pages that do exist.\n\nA write that does not land answers `save failed, nothing changed (retry)`:\nthe previous file is intact and calling again is the whole recovery.\n\n### Who is watching\n\nA map nobody has on screen is a file, not a map — and nothing in the system\nused to be able to tell the difference. An assistant would declare a design,\nlight nodes up as it built them, and report all of it into a store you had\nnever opened a pane for.\n\nEvery pane now publishes a small report while it runs — `.mellos/viewers/`,\none file per pane, refreshed once a second — and every write and every view\nends with what those reports say:\n\n| The line | What it means |\n| --- | --- |\n| `pane: CLOSED` | nobody is seeing this map; the assistant opens one with `mmap_open` instead of asking you to |\n| `pane: open on this page` | you are watching this land |\n| `pane: open on <other>, auto-follow on` | the pane follows the page last written, so it arrives here by itself |\n| `pane: open on <other>, auto-follow OFF` | you pinned that page by hand: the change is real and NOT on your screen. The assistant is told to say so rather than move your view |\n\nThe same reports answer \"is a pane already open?\" for `mmap` and for the\nlauncher — a question that used to cost a Windows-only process scan and could\nnot say which page was on screen. A report whose pane stopped refreshing it is\nignored after five seconds and deleted after a minute, so a killed pane cannot\ngo on claiming an audience.\n\n### Setup: choose when maps open\n\nHow eager mapping should be is a habit, not a property of a repository — so it\nis chosen **once, for you**, in the first session after you install:\n\n- `always` — map every structured task: workflows, designs, architecture,\n  technical dependencies. The assistant opens the pane on its own initiative;\n  your recorded answer is its standing consent, so it stops asking.\n- `complex` — the same, but only for medium or complex tasks: several modules,\n  a new subsystem, roughly an hour of work or more.\n- `on-request` — map only when you explicitly ask. In a project with no map,\n  the plugin then says nothing at all — zero noise is the point.\n\nThe answer lands in `<your home>/.mellos/config.json` and reaches every session\nthrough the plugin's `SessionStart` hook, which reads it and hands the\nassistant the matching instruction before you have typed anything. Nothing has\nto be set up per project, ever again.\n\nOne project can still differ: `mmap_setup {policy, scope: \"project\"}` records\na policy in that project's `.mellos/config.json`, and a project policy\noverrides the user one. `/mmap setup` re-runs the question for either scope\nwhenever you want to change your mind. The policy guides the assistant; it\nnever blocks the tools, and asking for a map explicitly always works under any\npolicy.\n\nHosts without hooks (Codex CLI, a bare MCP client) get the question another\nway: while no policy exists in either scope, every `mmap_declare` reply carries\na note telling the assistant to ask you. That note goes quiet for good — in\nevery project — the moment you have answered anywhere.\n\nStructural invariants enforced by the tools: layers form a total order by\nrank (an integer in 0..99, 0 = bottom, unique per map); every node lives in\nexactly one layer; edges point **strictly downward** — which makes the graph\nacyclic by construction; nodes may not depend on same-layer siblings (if A\nneeds sibling B, either B is really a lower concept or A and B are one node);\na group clusters nodes within one band; and node ids and group ids share\n**one namespace** — an id names a node or a group, never both, because both\nrender as boxes and one id must mean one box.\n\n## Development\n\n```\nnpm install\nnpm run verify\n```\n\n`verify` is four steps, in this order: `typecheck`, `test`, `build` (bundles\n`dist/`, emits `lib/` with declarations, cleaning both first), and\n`check:package` — which packs the tarball through the real `prepack`\nlifecycle and fails if any `exports` or `bin` target is missing from it.\n\nThe repo is itself layered bottom-up, and each layer has its spec:\n\n| Layer | Code | Spec | Owns |\n| --- | --- | --- | --- |\n| 0 domain | `src/domain/` | `ops.test.ts` | the map value, structural invariants, pure ops |\n| 1 format | `src/store/format.ts` | `store.test.ts` | the state-file format: replay-validated parse, serialize — I/O-free |\n| 1 store | `src/store/store.ts` | `store.test.ts`, `atomic-save.test.ts` | atomic state-file persistence on Node |\n| 1 semantics | `src/semantics/` | `semantics.test.ts` | medium-neutral view semantics: zoom ladder, group aggregation, page-set rules, sequence flip, the shared glyph vocabulary |\n| 2 apply | `src/server/apply.ts` | `apply.test.ts` | tool inputs → transactional op sequences |\n| 3 server | `src/server/server.ts` | `server.test.ts`, `save-failure.test.ts` | the five MCP tools over stdio |\n| 4 render | `src/render/` | `render.test.ts`, `routing.test.ts` | the ASCII renderer and its wire routing |\n| 4 pane | `src/watch/` | `watch.test.ts`, `pane-state.test.ts`, `input.test.ts` | the polling pane: page set, input parsing, panel and chrome |\n| — launchers | `scripts/` | `open-pane.test.mjs`, `codex-register.test.mjs` | plain-node entry points |\n| — packaging | `package.json` | `tests/lockfile.test.ts`, `browser-safe.test.ts` | what ships, and to whom |\n\n`dist/` is committed deliberately: plugin installation clones this repo and\nruns nothing, so entry points ship bundled. CI diffs the committed `dist/`\nagainst a fresh build, so a source change that forgets the rebuild fails.\n\n### Library\n\nThe lower layers are also a library (`npm run build` emits `lib/` with type\ndeclarations; npm packs it). Subpath exports mirror the source:\n\n| Subpath | Contents | Browser-safe |\n| --- | --- | --- |\n| `mellos-mapping/domain/types` | the map value, ids, ranks, statuses, errors | yes |\n| `mellos-mapping/domain/ops` | pure operations over a map | yes |\n| `mellos-mapping/format` | state-file parse / serialize, page ids | yes |\n| `mellos-mapping/semantics` | zoom ladder, group aggregation, focus and page-set rules, the shared glyph vocabulary | yes |\n| `mellos-mapping/render` | the terminal renderer | gated the same way (it is pure), but its output is character cells — for terminal hosts |\n| `mellos-mapping/store` | filesystem persistence, atomic saves, focus file, policy | Node only |\n| `mellos-mapping/server` | the bundled MCP server entry — a spawn target, not a module to import | Node only |\n\n**Browser-safe** means no Node builtins anywhere in the import closure, gated\nby a test, so a graphical client (a web panel, an editor view) can parse state\nfiles and reuse the exact aggregation, zoom and glyph semantics the terminal\npane draws with.\n\n## License\n\nMIT\n",
  "bytes": 30622,
  "sha": "9550a9878f5bd28a09f2d55281af4cfec187e4717917a234b70845a45a94af84",
  "repo_slug": "guangminju/mellos-mapping",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_guangminju_mellos_mapping_677141eb/readme"
}