mellos-mapping
v0.28.0
Published
A live layered dependency map for bottom-up development — MCP server + terminal pane. Ghost the design first, then light nodes up from the bottom as they are built and verified.
Maintainers
Readme
Mellos Mapping · 梅勒斯地图
English | 简体中文
给 Claude Code 、ChatGPT 桌面 Codex 模式与 Codex CLI 的自下而上 开发实况地图,原生运行在终端里。
Claude 为你构建系统时,对话旁边的分屏实时显示这个系统的分层依赖地图: 最底层是原语,依赖边只允许向下指;虚线幽灵节点是已设计未实现的部分, 转圈的是此刻正在构建的模块,实心绿色代表已构建且已验证。
真实会话:左边 Claude Code,右边地图面板。L0 已验证,L1 刚点亮,上面还是幽灵节点。
为什么
大多数进度汇报是一张任务清单——那是自上而下的世界观。梅勒斯地图反过来生长: 上层节点只能站在下层节点之上,这张图让纪律变得可见:
- 代码写出来之前,幽灵设计先出现。 Claude 先把完整的设计声明成虚线 幽灵节点;你可以在它还只是一张图的时候就否掉一个坏设计。
- spinner 在哪,Claude 的注意力就在哪。 一眼回答"它现在在做什么、 站在什么之上?"
- done 意味着已验证。 节点只有带着证据(一次通过的测试)才会变成 实心绿色。如果后续工作弄裂了地基,那个节点会变红——转圈的上层楼板 下面压着一块红色地基,是最诚实的状态汇报。
- 地图是账本,不是法官。 工具只拒绝结构性破坏(向上指的边、重复的 rank)。工作流是 Claude 的纪律,写在随插件捆绑的 skill 里;违反纪律 会被看见,而不是被悄悄拦截。
安装
克隆适合自己宿主的分支,运行一个安装命令即可;无需构建。
| 分支 | 使用对象 | 在克隆目录运行 |
| --- | --- | --- |
| main | 共用源码/任一宿主 | node install.mjs chatgpt-app 或 node install.mjs claude |
| claude | Claude Code | node install.mjs |
| chatgpt-app | ChatGPT 桌面 App 的 Codex 模式 | node install.mjs |
前置要求为 Node.js 18.17 及以上的 18.x,或 20.3 及以上版本,以及对应宿主的 CLI, 并确保命令在 PATH 中。支持 Windows 10+、macOS 13.0+ 和 glibc 2.28+ 的 Linux,均支持 x64 与 arm64;系统还须满足所选 Node.js 版本的更高要求。发行包自带原生锁绑定, 无需用户本地编译。安装器检查 发行文件和八个 MCP 工具,将运行时保留到克隆目录之外,完成宿主配置。 安装后开启新对话。详见发布与分支说明。
Claude Code 也可以通过插件市场安装:
在 Claude Code 对话里输入两行:
/plugin marketplace add GuangminJu/mellos-mapping
/plugin install mellos-mapping@mellos-mapping或者在终端里一条命令装完:
claude plugin marketplace add GuangminJu/mellos-mapping && claude plugin install mellos-mapping@mellos-mapping需要 PATH 上有符合安装要求的 Node.js;使用原生 Claude Code 安装包时也需
单独安装 Node.js。
没有构建步骤:dist/ 是提交进仓库的,克隆即用——dist/server.mjs(MCP
服务器)、dist/watch.mjs(面板)、dist/mmap.mjs(mmap 开关)、
dist/hook-session-start.mjs(由 hooks/hooks.json 注册的 SessionStart
钩子),以及 dist/store-paths.mjs(存储的路径词汇;纯 node 的面板启动
脚本从这里导入,而不是自己抄一份文件名)。
omp(Oh My Pi) 读的是 Claude Code 版本用的同一套插件布局——同一份市场
目录、.mcp.json、技能与斜杠命令——一份发行包同时服务两个宿主:
omp plugin marketplace add GuangminJu/mellos-mapping
omp plugin install mellos-mapping@mellos-mapping这样装跟的是 main,而发布就是它上面的版本号提升。要冻结版本,就改把某个
版本的检出目录加为市场——GitHub Release 里的 Claude Code 版本 ZIP,或按
tag 检出的克隆:
omp plugin marketplace add "<检出或解压出来的发行目录>"
omp plugin install mellos-mapping@mellos-mappingomp 不读 hooks/hooks.json,所以那段会话说明由插件的 omp 宿主适配器送达
(dist/omp-extension.mjs,在 package.json#omp.extensions 里声明):同一个
存储、同一段策略文字,Windows 上的 mmap shim 也由它安装。面板仍旧是会话
旁边的终端分屏。请把它当插件安装——omp plugin link 与 npm 包都不是插
件,用它们装会只剩适配器、没有工具。见
omp 安装与限制。
pi 则完全不读那套目录:它加载的是扩展,而且出于设计考虑自己没有 MCP。
所以这个插件自己跟自己讲 MCP——在 package.json#pi.extensions 里声明的扩
展(dist/pi-extension.mjs)会在会话所在目录启动同一个 dist/server.mjs,
八个 mmap_* 工具因此就是同样的八个、同名可用,会话说明也来自同一个存储、
同一段策略文字,与 Claude 钩子打印的一样:
pi install npm:mellos-mapping加 -l 只装到当前项目而不是所有项目,之后开一个新会话:pi 在会话开始时才
发现包。包里的 skills/ 与 commands/ 随之一并生效——同一套建图纪律,
/mmap 也还是同一个斜杠命令。见
pi 安装与行为。
装完之后的第一个会话只会问你一个问题——建图要多积极——并把答案记成 你以后打开的每一个项目的默认。此后钩子会自己把它带进每个新会话;再也没有 "每个项目设置一遍"这回事。见 Setup:选择什么时候建图。
mmap 终端命令(面板的开关切换)在 Windows 上会自己装好:还是这个钩子,
在会话启动时发现 shim 缺失或者还指向旧版本安装,就把 mmap.cmd(cmd、
PowerShell)和 mmap(git-bash)写进 %LOCALAPPDATA%\mellos-mapping\bin,
把这一个目录追加进你的用户 PATH,并通过助手告诉你这件事。PATH 改动
只对新进程生效——而且运行中的 Windows Terminal 连新标签页都继承旧环境,
所以第一次敲 mmap 之前要把终端应用整个关掉重开。PATH 的编辑保持安装器
原有的承诺:条目已经在里面
就什么都不做;遇到 setx 会损坏的 PATH(%VARIABLE% 被展平、超长被截
断),它干脆拒绝。
PATH 被拒绝不等于没命令可用:同样两个 shim 会再写一份到
%LOCALAPPDATA%\Microsoft\WindowsApps(或 ~/.local/bin)——一个你的 PATH
本来就有名字的目录——于是新终端里直接敲 mmap 就能用,完全不用改 PATH。
PATH 很长的机器正是这种情况,也最需要它。命令可以带要打开的页名
(mmap omp-host-support);面板已经开着时再敲一次就是关掉它。
它背后的那一步仍然是个独立命令,留给钩子管不到的情形——--uninstall,
或者 shim 还在、PATH 条目却被你删掉之后重新加回去:
node "<插件目录>/scripts/install-mmap-command.mjs" [--uninstall](--json 把安装结果打成一行 JSON 而不是散文——钩子自己调用它时用的就是
这个模式。)npm i -g mellos-mapping 通过 bin 提供同一个 mmap,不涉及
shim。
更新
claude plugin marketplace update mellos-mapping && claude plugin update mellos-mapping@mellos-mapping要两步是因为 plugin update 只对比本地缓存的 marketplace 克隆——真正
拉取本仓库的是第一条命令。重启 Claude Code 生效。发布即 main 分支
上的版本号提升。(在对话里输入 /plugin 也能打开同一个管理界面。)
omp 也有自己的两步:
omp plugin marketplace update mellos-mapping && omp plugin upgrade mellos-mapping@mellos-mapping先关面板(面板里按 q):watcher 跑的就是插件副本里的文件,而 Windows 不
允许改名一个正被打开的文件——开着面板升级会以 EPERM 失败并把插件缓存清空
(旧副本已删、新副本未就位),用 omp plugin install mellos-mapping@mellos-mapping
--force 修回来。然后:第一条刷新市场目录,第二条按目录重装。omp 还会在启动时尽力刷新超过一天没更新过
的目录条目——除非 marketplace.autoUpdate 设为 off;默认的 notify 模式只把
"有可用更新"写进 debug 日志,所以让更新看得见的是 marketplace update 这一
步。upgrade 不比较版本:它按目录里现在指的内容强制重装,同版本号也会重下——
因此版本号提升是给发行贴标签,而不是送达的门槛。之后重启 omp,并把仍显示旧
运行文件的面板关掉重开(面板里按 q,再 mmap_open)。
pi 更新的则是它装下的那个包,中间没有市场目录:
pi update npm:mellos-mapping这是 npm 来源;git 来源由 pi 自己的更新流程校准到你 settings 里写的 ref,带版本的
写法(npm:[email protected])会被跳过。之后重启会话:每个会话各自拥有它启动
的地图服务器进程,仍在运行的会话会一直用开始时的那份副本,直到会话结束。
从 0.19 升级
0.20 把地图存储从 .claude/ 挪到了 .mellos/——地图属于这个工具,不属于
某一个客户端。服务器和 watcher 都会在启动时做一次搬迁,并在 stderr 打一行
提示(mellos-mapping: moved the legacy .claude map store to .mellos/ — commit
the move.):
| 0.19 及更早 | 0.20 及之后 |
| --- | --- |
| .claude/mellos-mapping.json | .mellos/map.json |
| .claude/mellos-mapping.pages/ | .mellos/pages/ |
| .claude/mellos-mapping.config.json | .mellos/config.json |
搬迁不合并、也不覆盖:.mellos/ 里已经有东西(地图、页目录或配置)的项目
原样不动,无论旧目录里还剩什么。如果地图跟着 git 走,记得把这次搬迁提交
上去——git add -A .claude .mellos 会把它记成重命名,而不是一堆删除加一堆
未跟踪文件。
这一次搬迁是两个进程唯一会碰 .claude/ 的时刻。此后工具只往 .mellos/
里写,也绝不会写到启动时解析出的项目目录之外。
ChatGPT App · Codex 模式
本版用于 ChatGPT 桌面 App 的 Codex 模式(也称 Codex App)。在源码分支运行
以下命令;在 chatgpt-app 分支运行时省略宿主参数。它会一次配置桌面专用技能、
插件市场与八个 MCP 工具。
node install.mjs chatgpt-app安装完成后开启新对话。技能默认使用 web-terminal 网页终端:
mmap_open {surface: "web-terminal", page: "<slug>"} 启动本地服务后,
AI 自动把 URL 打开到当前对话右侧浏览器,直接显示 mmap 终端地图。
不需要粘贴命令或 Computer Use;网页有独立字号调节。
图形 SVG 网页、原生终端和 Markdown 仍然保留。旧 MCP 对话可使用
node "<插件根>/dist/web.mjs" "<项目>" --terminal --page <slug>。
面板排队打开不等于地图已经可见。
详见桌面安装与限制。
在 Windows Terminal 使用 Codex CLI 时(不指桌面 App 内置终端),运行
node <插件根>/scripts/open-pane.mjs <项目目录>——它会在承载本会话的
终端窗口右侧分屏,并保留左侧对话的键盘焦点。识别或聚焦失败时会明确报错,
不会改开其他窗口;--window 用于明确选择独立窗口。加 --page <slug> 指定打开哪一页;
当前会话的面板已经开着时,带 --page 重跑会让它切到那一页。其他会话
或独立窗口中的面板不算当前会话已分屏。面板默认自动跟随正在被写入的页——AI 此刻操作哪张图,
就看哪张图;按 f 开关(手动切页也会关掉),或用 --no-follow 启动。
其他环境在项目目录下的第二个终端(或任意分屏)运行
node <插件根>/dist/watch.mjs。两者接受同一套参数,见
面板参数。
桌面对话右侧:Markdown 地图
用户选择文档方式时,技能会使用 mmap_open 的
surface: "markdown",生成地图文档后交给宿主在当前对话右侧打开。
文档包含彩色 SVG 分层依赖图、模块状态、设计说明、验证记录和子图链接。
不需要网页服务或 Mermaid 支持,图像放大仍保持清晰。
首次打开后,本项目中成功的地图工具写入会自动重新生成预览。JSON 仍是
唯一数据源,.mellos/previews/ 是可重新生成的输出。图片里的节点没有
拖拽、悬停、双击下潜和动画;用文档链接打开子图。侧栏是否自动刷新由
桌面应用决定,生成成功不代表已显示。手动刷新或旧会话可以运行:
node "<插件根>/dist/preview.mjs" "<项目目录>" --page <页名>完整说明见 Codex 桌面地图。
可选:交互网页版
现有 Markdown/SVG 用法继续保留。需要缩放拖动、悬停/固定节点详情、依赖
高亮、搜索、状态筛选、分组概览、页面和子地图切换时,使用新增的
mmap_open {surface: "web", page: "<页名>"},将返回的网址交给宿主在右侧
浏览器打开。网页直接读取项目地图并自动更新,也支持明暗主题和确认后删页。
node "<插件根>/dist/web.mjs" "<项目目录>" --page <页名>
# 关闭该项目的网页服务:
node "<插件根>/dist/web.mjs" "<项目目录>" --stop服务仅在本机运行,不需要部署或下载网页依赖。Markdown 和网页共用原来的 JSON 数据,能够同时使用;手动切页会固定当前页面。详见 网页版功能与运行方式。
任意 MCP 客户端
服务器已发布到 npm,任何 MCP 客户端(Cursor、Windsurf、Zed、Gemini CLI……)都能用标准 stdio 条目接入:
npx -y mellos-mapping地图文件落在客户端会话的工作目录(.mellos/map.json)。在同
一项目里打开实况面板:
npx -y -p mellos-mapping mellos-mapping-watch服务器按这个顺序确定项目目录:MELLOS_MAPPING_CWD(显式覆盖,给那些会
在固定目录里拉起服务器的客户端用)、CLAUDE_PROJECT_DIR(Claude Code 为
插件 MCP 服务器设置的约定),最后才是服务器进程自己的工作目录。如果你的
客户端会在你实际工作的项目之外启动服务器,就设 MELLOS_MAPPING_CWD。
技能/纪律层是 Claude Code 与 Codex 专属的;其他客户端获得八个 mmap_*
工具和面板,提示词自备。
使用
- 让 Claude 构建一个非平凡的东西。捆绑的 skill 会让 Claude 先声明幽灵 设计,并在工作过程中保持地图与现实一致。
- 面板会自己打开。每一次写入都会告诉 Claude 到底有没有人在看
(见谁在看);没人看的时候,Claude 用
mmap_open自己 把面板开出来或切到该看的那一页——你不需要记着去开它。想自己开关, 在任意终端敲mmap,或者在对话里用/mellos-mapping:mmap(Windows 上是 Windows Terminal 分屏,tmux 里是 tmux 分屏,Pebrel 里 是 Pebrel 分屏,其他环境会打印一条命令让你在第二个终端里运行)。Windows 上即使开着多个 终端窗口,分屏也会落在你的会话所在的窗口;想让地图独占一个窗口 就加--window。字体缺少制表符字形时用--ascii。 - 看着节点从底部一路亮起。图让你不安的时候就打断它——这正是它存在的 意义。
在 Linux 和 macOS 上,mmap_open 与 mmap 会自动使用 tmux。启动器优先
定位继承的 TMUX/TMUX_PANE;工具进程丢失这些变量时,会自动发现默认
tmux 服务中唯一已连接的会话。默认在右侧分屏并保留输入焦点;--window
创建一个新的 tmux 窗口。再次打开会复用属于该源面板或会话窗口的 watcher。
如果有多个已连接的会话,或使用自定义 tmux socket,在 MCP 服务进程的 环境中设置以下变量(修改后重启该进程):
| 环境变量 | 含义 |
| --- | --- |
| MELLOS_MAPPING_TMUX_TARGET | 指定 tmux 会话或面板,例如 work:2.1 或 %7 |
| MELLOS_MAPPING_TMUX_SOCKET | socket 的绝对路径,例如 /tmp/my-tmux/socket |
在 Pebrel 里,mmap_open 与 mmap 通过 Pebrel 运行时 CLI
(PEBREL_CLI)分屏承载本会话的面板。启动器用进程树确认该面板所在的窗口,
把输入焦点还给对话,再把 watcher 命令输入新面板的 shell(PowerShell、cmd
或 POSIX shell);shell 随 watcher 一起退出,地图关闭时面板也随之关闭。
--window 会新开一个 Pebrel 窗口。在 tmux 里则由 tmux 负责分屏。
目标不明确、会话未连接或没有安装 tmux 时,启动器会返回具体原因,以及 包含项目、页面和正确引号的一条完整 watcher 命令,可以粘贴到可见终端。 地图仍可正常写入;打开失败后,助手只在环境变化或你要求重试时再次打开。
分屏支持鼠标(xterm SGR any-event 协议——htop 和 tmux 说的同一种话):
| 输入 | 动作 |
| --- | --- |
| 悬停节点 | 高亮它的关系线;在地图下方预览节点详情 |
| 点击节点 | 钉住——鼠标移开后详情仍然常驻 |
| 点击空白 / Esc | 取消钉住;无钉住时 Esc 从下潜返回 |
| 滚轮 / + - | 缩放,以焦点节点为锚(阶梯见下) |
| 按住左键拖动 | 地图超出面板时抓取平移 |
| shift+滚轮 | 垂直滚动 |
| 滚轮横向拨动 | 左右平移 |
| hjkl / 方向键 | 微移视口 |
| Tab / Shift+Tab / 1-9 / 点击标签 | 切换页(并行的多张地图) |
| 在标签行上滚轮 / 点击 ‹ › | 浏览放不下的标签栏,但不切页 |
| f | 开关自动跟随(见页) |
| x,或点击当前标签上的 × | 请求删除屏幕上这一页;在确认窗口内再按一次,它的文件就被删掉(见页) |
| 双击带 ⊞ 的节点 | 下潜进它的子图(一个子页面) |
| Backspace / Esc | 从上一次下潜爬回父图 |
| 拖动 ⋯ 分隔线 | 调整详情面板高度——向上拉,完整阅读长设计笔记 |
| 0 | 重置平移和缩放 |
| q / Ctrl+C | 退出面板 |
其他按键一律无效,这是有意的:面板不认识的转义序列(F 键、Home/End、 PgUp/PgDn、Insert/Delete、带修饰的方向键)会被整段吞掉、什么都不做, 而不是让它的载荷字节被当成热键读进来。
缩放先做几何缩小,只在阶梯两端才切换显示模式——每一级都能看到有意义的 数据:
细读+ ← 细读 ← 100% ← 85% ← 70% ← 55% ← 概览- 放大过 100%——
detail在盒子里展开验证证据和设计笔记的前三行;detail+把盒子撑成一张阅读卡(最多十二行笔记); - 85–55%——留白收紧、标签按比例截断,盒子还是盒子;
- 低于 55%——标签已短到无意义,此时地图聚合:每个已声明的分组
(层内的具名子系统)变成一个盒子,如
地基子系统 1/2,状态由成员推导, 边收拢到分组上,未分组节点保持原样。就像真地图缩小后显示省名,而不是 把城市变成无名光点。(没有声明分组的地图退化为纯字形星座 + 每层计数。) 页脚始终显示当前级别。
地图下方、提示行之上,是一块固定高度的详情面板:一条可拖动的分隔线、
一行按状态着色的表头、焦点节点的验证证据、两个方向的连线(uses → … ·
used by ← …,每个邻居各带自己的状态字形),以及自动折行的设计笔记。没有
焦点时显示整图的仪表盘。高度固定——详情不会悬浮遮挡地图,版面也不会跳。
字形
一个状态一个字形,画地图的地方都一样——面板里的盒子、标签栏、详情面板, 以及任何读同一份存储的客户端:
| Unicode | ASCII | 含义 |
| --- | --- | --- |
| · | . | planned——已声明,未开工 |
| ⠿ | * | in-progress 的静止形态——能做动画的盒子转的是盲文帧 ⠋⠙⠹…,ASCII 下是四帧的转杠 |
| ■ | # | done,且有证据 |
| □ | o | done,但没有记录证据——同一个断言,背后空无一物 |
| ✗ | X | regressed:曾经 done,现在坏了 |
| ⊞ | + | 徽标:这个节点链着子图,双击下潜 |
图下方的图例列出四个状态;□ done, no evidence 只在这张图里真的出现了
这种节点时才加入——四个状态是词汇表,那一个是此时此地正在被违反的规则。
文档型图种用节点 kind 的字形取代状态图例。
面板参数
面板启动脚本(scripts/open-pane.mjs <项目目录>)和 watcher
(dist/watch.mjs)接受同一套 watcher 参数;启动脚本原样转发它们,遇到
不认识的参数会报错,而不是悄悄丢掉。
| 参数 | 作用 |
| --- | --- |
| --page <slug> | 打开时定位到这一页;已经有面板在跑时,改为让那个面板切过去,而不是再开一个 |
| --ascii | 纯 ASCII 字形,给缺制表符字形的字体用 |
| --no-color | 不输出 ANSI 颜色 |
| --no-mouse | 关闭鼠标上报,把鼠标留给你的终端复用器 |
| --no-follow | 启动时就关掉自动跟随 |
| --interval <ms> | 轮询间隔,缺省 250,下限 50 |
只属于启动脚本的:--window 直接开到专属的 "mellos-mapping" 窗口而不是
在会话窗口里分屏,--force 即使本项目已有面板在跑也再开一个。只属于
watcher 的:--file <path> 指定默认页的状态文件(启动脚本会从项目目录
自己推导出来)。
内部 watcher 参数 --owner <token> 和面板报告的可选 owner 字段承载会话绑定。
启动器用源控制台进程及创建时间生成身份;手动运行 watcher 可省略。切页和关闭
请求按面板 PID 定向投递,同项目其他窗口不会抢走请求。普通 mmap 只切换当前
会话的面板;mmap --window 切换本项目的独立窗口。
mmap 命令
在终端里敲 mmap,它是当前会话地图的开关:本会话还没有面板就开一个,
已经有面板就把它关掉。
| 你敲的 | 发生什么 |
| --- | --- |
| mmap | 当前会话没有面板在跑 → 开一个;有 → 关掉它 |
| mmap <页 slug> | 打开时定位到这一页,或者让已开的面板切过去——永远不关 |
| mmap --window | 开到专属的 "mellos-mapping" 窗口,而不是把当前窗口分屏 |
| mmap --force | 即使已经有面板在跑也再开一个 |
项目是像 git 找仓库根那样找出来的:从当前目录往上走,找最近一个含
.mellos/ 存储的目录。站在一个还没有地图的项目里也没问题——面板会开在
待机画面上,等第一次 mmap_declare。
关闭走的是存储,不是信号:mmap 在地图旁边写一份一次性请求,面板在下一次
轮询(缺省 250 毫秒)时消费掉它并退出,并把终端原样还回去——关掉鼠标上报、
恢复光标。还停在待机画面上的面板也一样关得掉。请求读到即删;上一个面板死掉
留下的残留会在下一个面板启动时被清扫,所以过期的请求永远关不掉新面板。
上面每一个 watcher 参数在这里同样有效,原样转发;不认识的参数会报用法错误,
绝不悄悄丢掉。除非你装的是 npm 包,否则 mmap 需要
装一次。在 Claude Code 对话里,/mellos-mapping:mmap 打开的是
同一个面板。
页
一个项目可以并排保有多张地图——一个工作努力 = 一页。Claude 在任何
mmap_* 工具里传 page 参数即可定向到某页;出现第二页时面板顶部自动长出
标签栏。当前页加粗、按整图状态着色。每页记住自己的平移/缩放/钉住状态。
面板默认自动跟随正在被写入的那一页——AI 此刻在操作哪张图,就看哪张图,
于是 declare 和 update 自己就把观众带过去了。按 f 开关,手动切页也会关掉
它,--no-follow 则是一开始就关着;跟随关掉之后,后台页有变化时它的标签会
亮起状态色提示你,而不是抢走你的视线。显式的 --page 优先级高于跟随;请求
一个还不存在的页会一直挂着,等它出现的那一刻显示出来。
删除一页。 一件事做完了,它那一页不必留着。在面板里按 x——或者点击
当前标签上的 ×(开着鼠标时才画)——只是发问:页脚出现
press x again to delete <页>,三秒内再按一次,这一页的文件就被删掉。切页、
Esc、或者干脆等它过期,请求就收回了。× 只长在当前标签上,所以点一个
非当前标签是先切过去,下一帧它才带上自己的 ×。工具那边是
mmap_remove {pages: ["slug", …]},在这次调用的地图修改之后执行。两条
路都一样:文件是真的没了——地图是纯 JSON,提交进 git 是唯一的后悔药。
地图状态存在项目根目录下、属于本工具的 .mellos/ 目录里:
| 路径 | 是什么 |
| --- | --- |
| .mellos/map.json | 默认页——可选;工作全在命名页上的项目根本没有这个文件 |
| .mellos/pages/<slug>.json | 一个命名页一个文件 |
| .mellos/config.json | 本项目的建图策略(见 Setup) |
| .mellos/focus | 启动脚本发给运行中面板的一次性"切到这一页"请求;面板在一个轮询周期内消费并删除它 |
| .mellos/quit | mmap 开关发出的一次性"自己关掉"请求,以同样的方式被消费和删除 |
| .mellos/viewers/<pid>.json | 每个活着的面板一份报告——它正显示哪一页、自动跟随是否打开——运行期间每秒刷新一次(见谁在看) |
| <上面任一文件>.<pid>.<随机>.tmp | 正在落盘的一次写入;它要么被改名覆盖目标,要么被删掉。留下来说明那次写入失败(并已被报告),连清理都没能跑成 |
| .mellos/.write-lock | 操作系统项目锁使用的永久普通文件;不纳入 Git,正常运行时不得删除、改名或替换 |
地图文件是纯 JSON,想在 git 里留下地图的历史就把它们提交进去。focus、quit、
viewers/ 和 .write-lock 应加入 gitignore。前三者传递运行期消息;锁文件即使
没有进程持锁也应保留,不要当作临时文件清理。
并发模型,直说。 每次保存都是原子的——先写进一个私有的同级临时文件,
再改名覆盖目标——所以轮询存储的读者要么看到上一张完整的地图,要么看到新的
那张,绝不会读到写了一半的。所有写图入口(MCP、HTTP 查看器、watcher)都使用
同一把项目级、非阻塞的操作系统排他锁。锁使用独立的固定文件,与被原子替换的
地图文件分开;操作结束或进程退出后,操作系统释放锁,锁文件仍保留。锁被占用时
返回可重试的 BUSY。
这把锁串行执行每次操作的读取、修改和保存。基于先前读取计算的改动还应传入
expectedRevision:它在锁内比较,版本过旧返回 CONFLICT。省略它时,后续写入
仍可能覆盖同一字段或 context 这样的完整值。不同页面用于分开互不相关的工作;
共用一页时仍需带版本检查。底层 saveMapFile、手工编辑和仍在运行的旧进程不参与
这份约定。升级前须停掉该项目所有旧 MCP、查看器和 watcher 进程;残留的旧锁目录
需一次性迁移。完整边界和升级步骤见持久化地图 API 指南与
项目锁说明。
图种
缺省图种 dev 就是上文那本活的进度账本。同一套分层 DAG 机器也能画文档型
图:在 mmap_declare 里传 kind,该页即以中性方式渲染——素色实线盒子,
没有幽灵、没有 spinner、不数进度。
| 图种 | 读法 | 专属能力 |
| --- | --- | --- |
| architecture | 分层组件(也适合模块依赖、调用图) | 边标签标协议 |
| dataflow | 管线阶段即层,源头在最底 | 边标签标数据 |
| behavior-tree | 叶子(动作)在最底,根在顶(也适合思维导图、WBS) | 节点 kind selector sequence parallel decorator condition action 渲染为字形 |
| sequence | 经典的调用/返回时序:时间自上而下流,参与者是顶部泳道表头;每次进入和每次返回都是"当事参与者泳道里"的一个事件 | lanes 即参与者泳道;边标签即消息 |
节点 kind 和边标签在 dev 图上同样可用。状态机是有意不支持的:状态迁移
成环,而这里的边只许向下。
子图
节点可以用 submap: <页名> 链接一个子页面——面板给它戴上 ⊞ 徽标;双击
下潜进子图,Backspace 爬回父图。图中图,完全由页组合而成:没有新存储、
没有新不变量。一个节点值不值得配子图,由 AI 自行判断——大多数不需要。
子图是节点的内部细节,不是兄弟页:被别的页潜进去的页不占标签栏。
有两条修正保证标签栏不会把自己抹掉——节点指向自己所在页的,谁也不藏;
链接成环的一组页保留各自的标签,除非环外有页潜进来,因为环本身没有"外面"
可以爬回去。下潜
之后标签行变成面包屑——⌫ 父图 ▸ 节点——点击它(或按 Backspace)爬
回去。隐藏的子图在后台有变化时,底栏会提示。
MCP 工具
先用 mmap_read 查找并恢复已有页面;新对话不等于新任务。通过稳定 ID、过滤查询
和版本检查做增量修改。持久化地图接口说明包含完整增删改查、
混合事务、恢复摘要、源码哈希和 worktree 边界。
| 工具 | 用途 |
| --- | --- |
| mmap_declare | 生长地图:标题(传 null 删掉)、图种、层级横条、泳道、分组(子系统)、节点(可带 status、evidence、detail、kind、group、lane、submap)、边(可带标签);批量,全有或全无 |
| mmap_update | 记录进度并修订:状态(planned → in-progress → done 附证据、regressed)、改节点标签、把节点搬到另一层(layer)、加入/退出分组或泳道、设节点 kind 或 submap;给层改名和改 rank(layers)、给分组改标签(groups)、给泳道改标签(lanes);任何可清空的字段传 null 即清空 |
| mmap_remove | 修订:删除边、节点、分组、泳道、空层——以及用 pages 删掉整页,连文件一起(永久;在本次调用的地图修改之后执行) |
| mmap_view | 把当前地图渲染成文本,直接在对话里看(可选 zoom,-4…2);每次响应结尾都有一行 pages:,列出本项目有哪些页、以及你正在看哪一页 |
| mmap_read | 结构化页面发现、稳定 ID、过滤分页查询、恢复摘要与源码变化检查 |
| mmap_batch | 单页增删改混合事务与版本冲突检查 |
| mmap_setup | 查/设本项目的建图策略——什么时候开地图 |
| mmap_open | 把地图放到你屏幕上:开面板,或把已开的面板切到某一 page(window: true 用独立窗口)。它回答的是"事后有没有面板真的报到",而不只是"命令跑过了"——而且它永远关不掉面板 |
一个批次的施加顺序是 层 → 分组 → 泳道 → 节点更新;同一条节点更新里
layer 先于其他字段生效,所以一个节点可以在一条更新里搬层并加入新层上的
分组。
边界会拒绝下面这些,好让账本不会记下它并不想记的东西:
- 不认识的键,并把键名说出来——拼错的
evidance是错误,不是被悄悄 丢掉的字段;任何嵌套深度都一样; - 文本字段里的控制字符——藏在标签里的 ESC 序列,会让这张图重绘每一个
打开它的人的终端。
detail是例外:换行和制表符本来就是写笔记的方式, 其余(ESC、BEL、单独的 CR)照样拒绝。文件加载和公共库保存也校验控制字符, 同时兼容旧文件中的多行证据。直接通过公共库构造地图时,终端渲染仍会消除 控制字符的执行效果; - 可选字段上的空字符串——清空字段用
null,而不是一个渲染出来跟真盒子 分不清的空白; submap指向本次调用所针对的那一页的节点——那是个没有底的环,不是 指向父图的链接;- 删页请求指向本次调用自己所针对的那一页,或者指向本项目根本没有的 slug——一次调用不能一边改一张图一边删掉它;而对不上任何一页的名字, 是拼错的概率远大于"刚被别人删了",拒绝时会把真实存在的页列出来。
没有落盘的写入回答 save failed, nothing changed (retry):之前的文件完好
无损,重试一次就是全部的恢复手段。
谁在看
没人开在屏幕上的地图不是地图,是文件——而系统以前根本分不出这两者。 助手声明幽灵设计、一个个把节点点亮,然后把这一切写进一个你从来没打开过 面板的仓库里。
现在每个面板在运行期间都会发布一份小小的报告——.mellos/viewers/,
一个面板一个文件,每秒刷新一次——而每一次写入、每一次查看的结尾,都会
把这些报告说出来:
| 这一行 | 意思 |
| --- | --- |
| pane: CLOSED | 没人在看这张图;助手会用 mmap_open 自己开,而不是回过头来要求你开 |
| pane: running on this page | 活跃进程报告正在显示这一页,但终端可能被隐藏 |
| pane: open on <其他页>, auto-follow on | 面板跟随最后被写入的那一页,它自己会过来 |
| pane: open on <其他页>, auto-follow OFF | 那一页是你亲手钉住的:这次改动是真的,但不在你屏幕上。助手被要求把这件事说出来,而不是把你的视图搬走 |
同一批报告也回答了 mmap 和启动脚本要问的"面板是不是已经开着"——这个
问题以前得靠一次只有 Windows 才有的进程扫描,而且答不出屏幕上是哪一页。
超过五秒没被刷新的报告不再算数,超过一分钟就被读到它的人删掉,所以被强杀
的面板不会一直冒领观众。
心跳不等于屏幕可见。显式调用 tmux 打开操作时,启动器还会切回已有地图所在的
窗口,解除遮挡地图的面板缩放,并检查已连接会话的活动窗口后再报告可见。
缺少 TMUX 环境变量、焦点又落在地图面板上时,会沿用原来的归属,避免重复打开。
Setup:选择什么时候建图
建图要多积极,是一个人的工作习惯,不是某个仓库的属性——所以它只为你选 一次,就在你装完之后的第一个会话里:
always—— 任何有结构的任务都建图:流程、设计、架构、技术依赖。AI 会 主动开面板;你记下的这个答案就是它的长期授权,它不会再问。complex—— 同样的做法,但只用在中等或复杂任务上:牵涉多个模块、一个 新子系统,大约一小时以上的活。on-request—— 只在你明确要求时建图。在还没有地图的项目里,插件对此 一个字都不说——零噪音就是目的。
答案落在 <你的用户目录>/.mellos/config.json,并通过插件的 SessionStart
钩子进入每一个会话:钩子把它读出来,在你敲下第一个字之前就把对应的指令交给
AI。从此再也不需要按项目设置什么。
单个项目仍然可以不一样:mmap_setup {policy, scope: "project"} 把策略写进
那个项目的 .mellos/config.json,项目策略压过用户策略。想改主意时,
/mmap setup 会把这个问题按任一作用域重新问一遍。策略只是引导 AI;它从不
阻止工具本身——无论什么策略,明确要求建图永远有效。
没有钩子的宿主(Codex CLI、裸 MCP 客户端)用另一条路拿到这个问题:只要两个
作用域里都还没有策略,每一次 mmap_declare 的响应都会带一条提示,让 AI 来
问你。你在任何一个作用域答完之后,这条提示就永远闭嘴了——在每一个项目里。
工具强制的结构不变量:层按 rank 构成全序(rank 是 0..99 的整数,0 在最底, 一张图里不许重复);每个节点恰好属于一层;边严格向下——因此图从构造上 就是无环的;节点不能依赖同层兄弟(如果 A 需要兄弟 B,要么 B 其实是更低层的 概念,要么 A 和 B 本来就是一个节点);分组只在一层之内聚拢节点;节点 id 和 分组 id 共用同一个命名空间——一个 id 要么命名节点、要么命名分组,绝不 两者兼有,因为它们都渲染成盒子,一个 id 必须只意味着一个盒子。
开发
先阅读贡献说明和项目结构、分支与恢复指南。
npm ci
npm run verify开发环境使用 Node.js 22.12+。verify 依次运行 typecheck、test、build
(打包 dist/、产出带声明的 lib/)、check:package、check:codex 和
check:release,检查 npm 入口、Codex 包和两个可安装发行包,包括 MCP 握手。
这个仓库本身就是自下而上分层的,每一层都有自己的规格测试:
| 层 | 代码 | 规格 | 职责 |
| --- | --- | --- | --- |
| 0 domain | src/domain/ | ops.test.ts | 地图值、结构不变量、纯操作 |
| 1 format | src/store/format.ts | store.test.ts | 状态文件格式:重放校验的解析与序列化,零 I/O |
| 1 store | src/store/store.ts | store.test.ts、atomic-save.test.ts | Node 上的原子化状态文件持久化 |
| 1 semantics | src/semantics/ | semantics.test.ts | 媒介无关的视图语义:缩放阶梯、分组聚合、页集规则、时序翻转、共享字形词汇表 |
| 2 apply | src/server/apply.ts | apply.test.ts | 工具输入 → 事务性操作序列 |
| 3 server | src/server/server.ts | server.test.ts、save-failure.test.ts | stdio 上的八个 MCP 工具 |
| 4 render | src/render/ | render.test.ts、routing.test.ts | ASCII 渲染器与它的走线 |
| 4 pane | src/watch/ | watch.test.ts、pane-state.test.ts、input.test.ts | 轮询面板:页集、输入解析、详情面板与外框 |
| — 启动脚本 | scripts/ | open-pane.test.mjs、codex-register.test.mjs | 纯 node 的入口 |
| — 打包 | package.json | tests/lockfile.test.ts、browser-safe.test.ts | 发出去的是什么、发给谁 |
dist/ 是刻意提交的:插件安装就是克隆本仓库、不运行任何东西,所以入口
文件以打包形式随仓库分发。CI 会把提交的 dist/ 和一次全新构建做 diff,
所以改了源码却忘了重新构建会直接失败。
库
底部各层同时是一个库(npm run build 产出带类型声明的 lib/,npm 打包
收录)。子路径导出与源码结构一一对应:
| 子路径 | 内容 | 浏览器安全 |
| --- | --- | --- |
| mellos-mapping/domain/types | 地图值、id、rank、状态、错误 | 是 |
| mellos-mapping/domain/ops | 地图上的纯操作 | 是 |
| mellos-mapping/format | 状态文件的解析/序列化、页 id | 是 |
| mellos-mapping/semantics | 缩放阶梯、分组聚合、焦点与页集规则、共享字形词汇表 | 是 |
| mellos-mapping/render | 终端渲染器 | 同样受门禁守护(它是纯的),但产出是字符格——给终端宿主 |
| mellos-mapping/store | 文件系统持久化、原子保存、focus 文件、策略 | 仅 Node |
| mellos-mapping/server | 打包好的 MCP 服务器入口——用来拉起的进程,不是拿来 import 的模块 | 仅 Node |
浏览器安全的意思是 import 闭包里没有任何 Node 内建模块,由测试门禁 守护——图形客户端(web 面板、编辑器视图)可以直接解析状态文件,并复用与 终端面板完全一致的聚合、缩放与字形语义。
可选的 GitHub Star 提醒
仅 npm/npx 安装会在首次成功查看地图满 7 天、且已在至少 3 个不同 UTC 日期 成功查看地图后,显示一次轻提示。网页中是一行可关闭的文字,不抢焦点; 原生交互终端只在正常退出地图后提示。MCP 返回、AI 提示词、安装日志、CI 及管道输出中都不会加入提醒,插件版和源码运行也不触发。
Star 按钮会调用系统默认浏览器打开 GitHub,方便沿用已有登录状态。
若未能打开,会保留提示并提供复制地址按钮。
提醒按用户跨项目、版本及安装去重,一生效就记为已提醒。关闭、点击 Star
或直接离开后,都不会再次出现。想提前关闭,可在启动地图或 MCP 的环境中
设置 MELLOS_MAPPING_NO_STAR=1。
仅在 ~/.mellos/support/star-reminder.json 本地记录首次使用时间、最后一个
UTC 日期、最多 3 天的计数和已提醒标记,不上传数据,不查询是否点过 Star;
只有用户主动点击链接时才访问 GitHub。设置损坏、不可写或锁忙时静默跳过。
社区
本项目积极参与并认可 LINUX DO 社区。
许可证
MIT
