mellos-mapping 0.20.2 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -88
- package/README.zh-CN.md +122 -84
- package/dist/hook-session-start.mjs +25 -19
- package/dist/mmap.mjs +302 -170
- package/dist/preview.mjs +1418 -0
- package/dist/server.mjs +1198 -400
- package/dist/store-paths.mjs +40 -21
- package/dist/terminal-worker.mjs +3188 -0
- package/dist/watch.mjs +523 -280
- package/dist/web/TERMINAL-LICENSES.txt +70 -0
- package/dist/web/app.css +967 -0
- package/dist/web/app.js +1356 -0
- package/dist/web/index.html +9 -0
- package/dist/web/terminal.css +9 -0
- package/dist/web/terminal.html +7 -0
- package/dist/web/terminal.js +9293 -0
- package/dist/web/xterm.css +285 -0
- package/dist/web.mjs +5535 -0
- package/docs/codex.md +183 -0
- package/lib/domain/text.d.ts +9 -0
- package/lib/domain/text.js +43 -0
- package/lib/domain/types.js +10 -1
- package/lib/preview/index.d.ts +3 -0
- package/lib/preview/index.js +3 -0
- package/lib/preview/markdown.d.ts +8 -0
- package/lib/preview/markdown.js +54 -0
- package/lib/preview/presentation.d.ts +6 -0
- package/lib/preview/presentation.js +14 -0
- package/lib/preview/publisher.d.ts +23 -0
- package/lib/preview/publisher.js +143 -0
- package/lib/preview/svg.d.ts +3 -0
- package/lib/preview/svg.js +74 -0
- package/lib/preview/text.d.ts +4 -0
- package/lib/preview/text.js +13 -0
- package/lib/render/canvas.d.ts +1 -1
- package/lib/render/canvas.js +4 -2
- package/lib/render/draw.js +9 -6
- package/lib/render/render.d.ts +6 -0
- package/lib/render/render.js +50 -18
- package/lib/render/width.js +3 -1
- package/lib/store/atomic.d.ts +18 -0
- package/lib/store/atomic.js +86 -0
- package/lib/store/channels.d.ts +40 -0
- package/lib/store/channels.js +135 -0
- package/lib/store/format.js +3 -1
- package/lib/store/json-text.d.ts +9 -0
- package/lib/store/json-text.js +16 -0
- package/lib/store/maps.d.ts +12 -0
- package/lib/store/maps.js +42 -0
- package/lib/store/migration.d.ts +12 -0
- package/lib/store/migration.js +46 -0
- package/lib/store/pages.d.ts +46 -0
- package/lib/store/pages.js +89 -0
- package/lib/store/policy.d.ts +74 -0
- package/lib/store/policy.js +144 -0
- package/lib/store/store.d.ts +9 -256
- package/lib/store/store.js +10 -694
- package/lib/store/viewers.d.ts +81 -0
- package/lib/store/viewers.js +186 -0
- package/package.json +25 -6
- package/scripts/codex-cli.mjs +42 -0
- package/scripts/codex-register.mjs +27 -94
- package/scripts/mmap.mjs +26 -16
- package/scripts/open-pane.mjs +37 -18
- package/scripts/pane-core.mjs +57 -220
- package/scripts/terminal-session.mjs +137 -0
- package/scripts/tmux-session.mjs +90 -0
- package/scripts/watcher-command.mjs +16 -0
package/README.zh-CN.md
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
# Mellos Mapping · 梅勒斯地图
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/mellos-mapping)
|
|
4
|
+
[](https://www.npmjs.com/package/mellos-mapping)
|
|
5
|
+
[](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.GuangminJu/mellos-mapping)
|
|
6
|
+
[](https://github.com/GuangminJu/mellos-mapping/actions/workflows/ci.yml)
|
|
7
|
+
[](LICENSE)
|
|
4
8
|
|
|
5
9
|
[English](README.md) | 简体中文
|
|
6
10
|
|
|
7
|
-
给 [Claude Code](https://claude.com/claude-code)
|
|
11
|
+
给 [Claude Code](https://claude.com/claude-code) 、ChatGPT 桌面 Codex 模式与 Codex CLI 的自下而上
|
|
8
12
|
开发实况地图,原生运行在终端里。
|
|
9
13
|
|
|
10
14
|
<p align="center">
|
|
11
15
|
<picture>
|
|
12
16
|
<source media="(prefers-color-scheme: light)" srcset="docs/demo-light.svg">
|
|
13
|
-
<img alt="
|
|
17
|
+
<img alt="一次 declare 铺开整张幽灵设计,节点自下而上逐个点亮 —— 地基开裂向上传染,绿色再被挣回来" src="docs/demo.svg" width="620">
|
|
14
18
|
</picture>
|
|
15
19
|
</p>
|
|
16
20
|
|
|
@@ -18,41 +22,11 @@ Claude 为你构建系统时,对话旁边的分屏实时显示这个系统的*
|
|
|
18
22
|
最底层是原语,依赖边只允许向下指;虚线幽灵节点是已设计未实现的部分,
|
|
19
23
|
转圈的是此刻正在构建的模块,实心绿色代表已构建**且已验证**。
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 编排层
|
|
25
|
-
|
|
26
|
-
╭╌╌╌╌╌╌╌╌╌╌╌╌╌╌╮
|
|
27
|
-
╎ · MCP Server ╎
|
|
28
|
-
╰╌╌╌┬┬╌╌╌╌┬╌╌╌╌╯
|
|
29
|
-
││ │
|
|
30
|
-
└┼────┼───────────┐
|
|
31
|
-
│ └───┐ │
|
|
32
|
-
│ │ │
|
|
33
|
-
━━━━━━━┿━━━━━━━━┿━━━━━━━┿━━━ 契约层
|
|
34
|
-
│ │ │
|
|
35
|
-
┏━━━━┷━━━━━━━┓│ ╭╌╌╌╌╌┴╌╌╌╌╌╮
|
|
36
|
-
┃ ■ 状态存储 ┃│ ╎ · Watcher ╎
|
|
37
|
-
┗━━━━━┯━━━━━━┛│ ╰╌╌╌╌╌╌┬╌╌╌╌╯
|
|
38
|
-
│ │ │
|
|
39
|
-
│ ┌─────┘ │
|
|
40
|
-
│ │ │
|
|
41
|
-
━━━━━━━━┿━┿━━━━━━━━━━━━━━┿━━ 原语层
|
|
42
|
-
│ │ │
|
|
43
|
-
┏━━━━━┷━┷━━━━━━┓ ╭────┴────────╮
|
|
44
|
-
┃ ■ 图领域模型 ┃ │ ⠋ ASCII渲染 │
|
|
45
|
-
┗━━━━━━━━━━━━━━┛ ╰─────────────╯
|
|
46
|
-
|
|
47
|
-
· planned ⠋ in-progress ■ done ✗ regressed
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
在真实终端里,连线和层级横条以暗色渲染,节点盒子按状态发光、标签加粗——
|
|
51
|
-
像一块黑色电路板,元件是亮的。跨层的边会从中间层的盒子缝隙里穿过去
|
|
52
|
-
(看上图 状态存储 和 Watcher 之间下潜的那根线);互不重叠的走线段共享
|
|
53
|
-
轨道行,让层与层贴得更近。
|
|
25
|
+
<p align="center">
|
|
26
|
+
<img alt="Claude Code 会话旁边的梅勒斯地图面板:六层游戏引擎设计,L0 与 L1 节点实心绿色,上面几层仍是虚线幽灵节点" src="docs/session-claude-code.png">
|
|
27
|
+
</p>
|
|
54
28
|
|
|
55
|
-
|
|
29
|
+
*真实会话:左边 Claude Code,右边地图面板。L0 已验证,L1 刚点亮,上面还是幽灵节点。*
|
|
56
30
|
|
|
57
31
|
## 为什么
|
|
58
32
|
|
|
@@ -72,6 +46,20 @@ Claude 为你构建系统时,对话旁边的分屏实时显示这个系统的*
|
|
|
72
46
|
|
|
73
47
|
## 安装
|
|
74
48
|
|
|
49
|
+
克隆适合自己宿主的分支,运行一个安装命令即可;无需构建。
|
|
50
|
+
|
|
51
|
+
| 分支 | 使用对象 | 在克隆目录运行 |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| `main` | 共用源码/任一宿主 | `node install.mjs chatgpt-app` 或 `node install.mjs claude` |
|
|
54
|
+
| `claude` | Claude Code | `node install.mjs` |
|
|
55
|
+
| `chatgpt-app` | ChatGPT 桌面 App 的 Codex 模式 | `node install.mjs` |
|
|
56
|
+
|
|
57
|
+
前置要求为 Node.js 18+ 和对应宿主的 CLI,并确保命令在 PATH 中。安装器检查
|
|
58
|
+
发行文件和六个 MCP 工具,将运行时保留到克隆目录之外,完成宿主配置。
|
|
59
|
+
安装后开启新对话。详见[发布与分支说明](docs/releasing.md)。
|
|
60
|
+
|
|
61
|
+
Claude Code 也可以通过插件市场安装:
|
|
62
|
+
|
|
75
63
|
在 Claude Code 对话里输入两行:
|
|
76
64
|
|
|
77
65
|
```
|
|
@@ -125,7 +113,7 @@ claude plugin marketplace update mellos-mapping && claude plugin update mellos-m
|
|
|
125
113
|
```
|
|
126
114
|
|
|
127
115
|
要两步是因为 `plugin update` 只对比本地缓存的 marketplace 克隆——真正
|
|
128
|
-
拉取本仓库的是第一条命令。重启 Claude Code 生效。发布即 `
|
|
116
|
+
拉取本仓库的是第一条命令。重启 Claude Code 生效。发布即 `main` 分支
|
|
129
117
|
上的版本号提升。(在对话里输入 `/plugin` 也能打开同一个管理界面。)
|
|
130
118
|
|
|
131
119
|
### 从 0.19 升级
|
|
@@ -149,34 +137,71 @@ the move.`):
|
|
|
149
137
|
这一次搬迁是两个进程唯一会碰 `.claude/` 的时刻。此后工具只往 `.mellos/`
|
|
150
138
|
里写,也绝不会写到启动时解析出的项目目录之外。
|
|
151
139
|
|
|
152
|
-
## Codex
|
|
140
|
+
## ChatGPT App · Codex 模式
|
|
153
141
|
|
|
154
|
-
|
|
142
|
+
本版用于 ChatGPT 桌面 App 的 Codex 模式(也称 Codex App)。在源码分支运行
|
|
143
|
+
以下命令;在 `chatgpt-app` 分支运行时省略宿主参数。它会一次配置桌面专用技能、
|
|
144
|
+
插件市场与六个 MCP 工具。
|
|
155
145
|
|
|
156
146
|
```
|
|
157
|
-
|
|
158
|
-
codex plugin add mellos-mapping@mellos-mapping
|
|
159
|
-
node ~/.codex/plugins/cache/mellos-mapping/mellos-mapping/<版本>/scripts/codex-register.mjs
|
|
147
|
+
node install.mjs chatgpt-app
|
|
160
148
|
```
|
|
161
149
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
150
|
+
安装完成后开启新对话。技能默认使用 **web-terminal 网页终端**:
|
|
151
|
+
`mmap_open {surface: "web-terminal", page: "<slug>"}` 启动本地服务后,
|
|
152
|
+
AI 自动把 URL 打开到当前对话右侧浏览器,直接显示 mmap 终端地图。
|
|
153
|
+
不需要粘贴命令或 Computer Use;网页有独立字号调节。
|
|
154
|
+
图形 SVG 网页、原生终端和 Markdown 仍然保留。旧 MCP 对话可使用
|
|
155
|
+
`node "<插件根>/dist/web.mjs" "<项目>" --terminal --page <slug>`。
|
|
156
|
+
面板排队打开不等于地图已经可见。
|
|
157
|
+
详见[桌面安装与限制](docs/codex.md)。
|
|
168
158
|
|
|
169
|
-
|
|
159
|
+
在 Windows Terminal 使用 Codex CLI 时(不指桌面 App 内置终端),运行
|
|
170
160
|
`node <插件根>/scripts/open-pane.mjs <项目目录>`——它会在承载本会话的
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
161
|
+
终端窗口右侧分屏,并保留左侧对话的键盘焦点。识别或聚焦失败时会明确报错,
|
|
162
|
+
不会改开其他窗口;`--window` 用于明确选择独立窗口。加 `--page <slug>` 指定打开哪一页;
|
|
163
|
+
当前会话的面板已经开着时,带 `--page` 重跑会让它切到那一页。其他会话
|
|
164
|
+
或独立窗口中的面板不算当前会话已分屏。面板默认**自动跟随**正在被写入的页——AI 此刻操作哪张图,
|
|
175
165
|
就看哪张图;按 `f` 开关(手动切页也会关掉),或用 `--no-follow` 启动。
|
|
176
166
|
其他环境在项目目录下的第二个终端(或任意分屏)运行
|
|
177
167
|
`node <插件根>/dist/watch.mjs`。两者接受同一套参数,见
|
|
178
168
|
[面板参数](#面板参数)。
|
|
179
169
|
|
|
170
|
+
### 桌面对话右侧:Markdown 地图
|
|
171
|
+
|
|
172
|
+
用户选择文档方式时,技能会使用 `mmap_open` 的
|
|
173
|
+
`surface: "markdown"`,生成地图文档后交给宿主在当前对话右侧打开。
|
|
174
|
+
文档包含彩色 SVG 分层依赖图、模块状态、设计说明、验证记录和子图链接。
|
|
175
|
+
不需要网页服务或 Mermaid 支持,图像放大仍保持清晰。
|
|
176
|
+
|
|
177
|
+
首次打开后,本项目中成功的地图工具写入会自动重新生成预览。JSON 仍是
|
|
178
|
+
唯一数据源,`.mellos/previews/` 是可重新生成的输出。图片里的节点没有
|
|
179
|
+
拖拽、悬停、双击下潜和动画;用文档链接打开子图。侧栏是否自动刷新由
|
|
180
|
+
桌面应用决定,生成成功不代表已显示。手动刷新或旧会话可以运行:
|
|
181
|
+
|
|
182
|
+
```sh
|
|
183
|
+
node "<插件根>/dist/preview.mjs" "<项目目录>" --page <页名>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
完整说明见 [Codex 桌面地图](docs/codex.md#desktop-right-side-map)。
|
|
187
|
+
|
|
188
|
+
### 可选:交互网页版
|
|
189
|
+
|
|
190
|
+
现有 Markdown/SVG 用法继续保留。需要缩放拖动、悬停/固定节点详情、依赖
|
|
191
|
+
高亮、搜索、状态筛选、分组概览、页面和子地图切换时,使用新增的
|
|
192
|
+
`mmap_open {surface: "web", page: "<页名>"}`,将返回的网址交给宿主在右侧
|
|
193
|
+
浏览器打开。网页直接读取项目地图并自动更新,也支持明暗主题和确认后删页。
|
|
194
|
+
|
|
195
|
+
```sh
|
|
196
|
+
node "<插件根>/dist/web.mjs" "<项目目录>" --page <页名>
|
|
197
|
+
# 关闭该项目的网页服务:
|
|
198
|
+
node "<插件根>/dist/web.mjs" "<项目目录>" --stop
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
服务仅在本机运行,不需要部署或下载网页依赖。Markdown 和网页共用原来的
|
|
202
|
+
JSON 数据,能够同时使用;手动切页会固定当前页面。详见
|
|
203
|
+
[网页版功能与运行方式](docs/codex.md#optional-interactive-web-viewer)。
|
|
204
|
+
|
|
180
205
|
## 任意 MCP 客户端
|
|
181
206
|
|
|
182
207
|
服务器已发布到 npm,任何 MCP 客户端(Cursor、Windsurf、Zed、Gemini
|
|
@@ -198,7 +223,7 @@ npx -y -p mellos-mapping mellos-mapping-watch
|
|
|
198
223
|
插件 MCP 服务器设置的约定),最后才是服务器进程自己的工作目录。如果你的
|
|
199
224
|
客户端会在你实际工作的项目之外启动服务器,就设 `MELLOS_MAPPING_CWD`。
|
|
200
225
|
|
|
201
|
-
技能/纪律层是 Claude Code 与 Codex
|
|
226
|
+
技能/纪律层是 Claude Code 与 Codex 专属的;其他客户端获得六个 `mmap_*`
|
|
202
227
|
工具和面板,提示词自备。
|
|
203
228
|
|
|
204
229
|
## 使用
|
|
@@ -216,6 +241,23 @@ npx -y -p mellos-mapping mellos-mapping-watch
|
|
|
216
241
|
3. 看着节点从底部一路亮起。图让你不安的时候就打断它——这正是它存在的
|
|
217
242
|
意义。
|
|
218
243
|
|
|
244
|
+
在 Linux 和 macOS 上,`mmap_open` 与 `mmap` 会自动使用 tmux。启动器优先
|
|
245
|
+
定位继承的 `TMUX`/`TMUX_PANE`;工具进程丢失这些变量时,会自动发现默认
|
|
246
|
+
tmux 服务中唯一已连接的会话。默认在右侧分屏并保留输入焦点;`--window`
|
|
247
|
+
创建一个新的 tmux 窗口。再次打开会复用属于该源面板或会话窗口的 watcher。
|
|
248
|
+
|
|
249
|
+
如果有多个已连接的会话,或使用自定义 tmux socket,在 MCP 服务进程的
|
|
250
|
+
环境中设置以下变量(修改后重启该进程):
|
|
251
|
+
|
|
252
|
+
| 环境变量 | 含义 |
|
|
253
|
+
| --- | --- |
|
|
254
|
+
| `MELLOS_MAPPING_TMUX_TARGET` | 指定 tmux 会话或面板,例如 `work:2.1` 或 `%7` |
|
|
255
|
+
| `MELLOS_MAPPING_TMUX_SOCKET` | socket 的绝对路径,例如 `/tmp/my-tmux/socket` |
|
|
256
|
+
|
|
257
|
+
目标不明确、会话未连接或没有安装 tmux 时,启动器会返回具体原因,以及
|
|
258
|
+
包含项目、页面和正确引号的一条完整 watcher 命令,可以粘贴到可见终端。
|
|
259
|
+
地图仍可正常写入;打开失败后,助手只在环境变化或你要求重试时再次打开。
|
|
260
|
+
|
|
219
261
|
分屏支持鼠标(xterm SGR any-event 协议——htop 和 tmux 说的同一种话):
|
|
220
262
|
|
|
221
263
|
| 输入 | 动作 |
|
|
@@ -301,14 +343,19 @@ used by ← …`,每个邻居各带自己的状态字形),以及自动折
|
|
|
301
343
|
watcher 的:`--file <path>` 指定默认页的状态文件(启动脚本会从项目目录
|
|
302
344
|
自己推导出来)。
|
|
303
345
|
|
|
346
|
+
内部 watcher 参数 `--owner <token>` 和面板报告的可选 `owner` 字段承载会话绑定。
|
|
347
|
+
启动器用源控制台进程及创建时间生成身份;手动运行 watcher 可省略。切页和关闭
|
|
348
|
+
请求按面板 PID 定向投递,同项目其他窗口不会抢走请求。普通 `mmap` 只切换当前
|
|
349
|
+
会话的面板;`mmap --window` 切换本项目的独立窗口。
|
|
350
|
+
|
|
304
351
|
### mmap 命令
|
|
305
352
|
|
|
306
|
-
|
|
307
|
-
|
|
353
|
+
在终端里敲 `mmap`,它是当前会话地图的**开关**:本会话还没有面板就开一个,
|
|
354
|
+
已经有面板就把它关掉。
|
|
308
355
|
|
|
309
356
|
| 你敲的 | 发生什么 |
|
|
310
357
|
| --- | --- |
|
|
311
|
-
| `mmap` |
|
|
358
|
+
| `mmap` | 当前会话没有面板在跑 → 开一个;有 → 关掉它 |
|
|
312
359
|
| `mmap <页 slug>` | 打开时定位到这一页,或者让已开的面板切过去——永远不关 |
|
|
313
360
|
| `mmap --window` | 开到专属的 "mellos-mapping" 窗口,而不是把当前窗口分屏 |
|
|
314
361
|
| `mmap --force` | 即使已经有面板在跑也再开一个 |
|
|
@@ -420,7 +467,9 @@ Claude 会话的正确姿势。
|
|
|
420
467
|
丢掉的字段;任何嵌套深度都一样;
|
|
421
468
|
- 文本字段里的**控制字符**——藏在标签里的 ESC 序列,会让这张图重绘每一个
|
|
422
469
|
打开它的人的终端。`detail` 是例外:换行和制表符本来就是写笔记的方式,
|
|
423
|
-
其余(ESC、BEL、单独的 CR
|
|
470
|
+
其余(ESC、BEL、单独的 CR)照样拒绝。文件加载和公共库保存也校验控制字符,
|
|
471
|
+
同时兼容旧文件中的多行证据。直接通过公共库构造地图时,终端渲染仍会消除
|
|
472
|
+
控制字符的执行效果;
|
|
424
473
|
- 可选字段上的**空字符串**——清空字段用 `null`,而不是一个渲染出来跟真盒子
|
|
425
474
|
分不清的空白;
|
|
426
475
|
- **`submap` 指向本次调用所针对的那一页的节点**——那是个没有底的环,不是
|
|
@@ -445,7 +494,7 @@ Claude 会话的正确姿势。
|
|
|
445
494
|
| 这一行 | 意思 |
|
|
446
495
|
| --- | --- |
|
|
447
496
|
| `pane: CLOSED` | 没人在看这张图;助手会用 `mmap_open` 自己开,而不是回过头来要求你开 |
|
|
448
|
-
| `pane:
|
|
497
|
+
| `pane: running on this page` | 活跃进程报告正在显示这一页,但终端可能被隐藏 |
|
|
449
498
|
| `pane: open on <其他页>, auto-follow on` | 面板跟随最后被写入的那一页,它自己会过来 |
|
|
450
499
|
| `pane: open on <其他页>, auto-follow OFF` | 那一页是你亲手钉住的:这次改动是真的,但**不在**你屏幕上。助手被要求把这件事说出来,而不是把你的视图搬走 |
|
|
451
500
|
|
|
@@ -454,6 +503,10 @@ Claude 会话的正确姿势。
|
|
|
454
503
|
超过五秒没被刷新的报告不再算数,超过一分钟就被读到它的人删掉,所以被强杀
|
|
455
504
|
的面板不会一直冒领观众。
|
|
456
505
|
|
|
506
|
+
心跳不等于屏幕可见。显式调用 tmux 打开操作时,启动器还会切回已有地图所在的
|
|
507
|
+
窗口,解除遮挡地图的面板缩放,并检查已连接会话的活动窗口后再报告可见。
|
|
508
|
+
缺少 `TMUX` 环境变量、焦点又落在地图面板上时,会沿用原来的归属,避免重复打开。
|
|
509
|
+
|
|
457
510
|
### Setup:选择什么时候建图
|
|
458
511
|
|
|
459
512
|
建图要多积极,是一个人的工作习惯,不是某个仓库的属性——所以它**只为你选
|
|
@@ -488,16 +541,16 @@ AI。从此再也不需要按项目设置什么。
|
|
|
488
541
|
|
|
489
542
|
## 开发
|
|
490
543
|
|
|
544
|
+
先阅读[贡献说明](CONTRIBUTING.md)和[项目结构、分支与恢复指南](docs/project-maintenance.md)。
|
|
545
|
+
|
|
491
546
|
```
|
|
492
|
-
npm
|
|
547
|
+
npm ci
|
|
493
548
|
npm run verify
|
|
494
549
|
```
|
|
495
550
|
|
|
496
|
-
|
|
497
|
-
`
|
|
498
|
-
|
|
499
|
-
目录都先清空),以及 `check:package`——它按真实的 `prepack` 生命周期打出
|
|
500
|
-
tarball,只要 `exports` 或 `bin` 里有任何目标没被打进去就失败。
|
|
551
|
+
开发环境使用 Node.js 22.12+。`verify` 依次运行 `typecheck`、`test`、`build`
|
|
552
|
+
(打包 `dist/`、产出带声明的 `lib/`)、`check:package`、`check:codex` 和
|
|
553
|
+
`check:release`,检查 npm 入口、Codex 包和两个可安装发行包,包括 MCP 握手。
|
|
501
554
|
|
|
502
555
|
这个仓库本身就是自下而上分层的,每一层都有自己的规格测试:
|
|
503
556
|
|
|
@@ -508,30 +561,16 @@ tarball,只要 `exports` 或 `bin` 里有任何目标没被打进去就失败
|
|
|
508
561
|
| 1 store | `src/store/store.ts` | `store.test.ts`、`atomic-save.test.ts` | Node 上的原子化状态文件持久化 |
|
|
509
562
|
| 1 semantics | `src/semantics/` | `semantics.test.ts` | 媒介无关的视图语义:缩放阶梯、分组聚合、页集规则、时序翻转、共享字形词汇表 |
|
|
510
563
|
| 2 apply | `src/server/apply.ts` | `apply.test.ts` | 工具输入 → 事务性操作序列 |
|
|
511
|
-
| 3 server | `src/server/server.ts` | `server.test.ts`、`save-failure.test.ts` | stdio
|
|
564
|
+
| 3 server | `src/server/server.ts` | `server.test.ts`、`save-failure.test.ts` | stdio 上的六个 MCP 工具 |
|
|
512
565
|
| 4 render | `src/render/` | `render.test.ts`、`routing.test.ts` | ASCII 渲染器与它的走线 |
|
|
513
566
|
| 4 pane | `src/watch/` | `watch.test.ts`、`pane-state.test.ts`、`input.test.ts` | 轮询面板:页集、输入解析、详情面板与外框 |
|
|
514
567
|
| — 启动脚本 | `scripts/` | `open-pane.test.mjs`、`codex-register.test.mjs` | 纯 node 的入口 |
|
|
515
|
-
| — 打包 | `package.json
|
|
568
|
+
| — 打包 | `package.json` | `tests/lockfile.test.ts`、`browser-safe.test.ts` | 发出去的是什么、发给谁 |
|
|
516
569
|
|
|
517
570
|
`dist/` 是刻意提交的:插件安装就是克隆本仓库、不运行任何东西,所以入口
|
|
518
571
|
文件以打包形式随仓库分发。CI 会把提交的 `dist/` 和一次全新构建做 diff,
|
|
519
572
|
所以改了源码却忘了重新构建会直接失败。
|
|
520
573
|
|
|
521
|
-
### dsh 插件包
|
|
522
|
-
|
|
523
|
-
`packages/dsh` 与 `packages/dsh-client` 是 DeepSeek Harness 的那一面:一个
|
|
524
|
-
读取并监视工作区 `.mellos/` 存储的宿主插件,加上用同一套语义作画的浏览器
|
|
525
|
-
地图面板。它们在 dsh workspace 检出里*开发*(由那边的工具链构建),从这里
|
|
526
|
-
*发布*——源码、规格测试和 `lib/` 都提交在这儿,用
|
|
527
|
-
`node scripts/sync-dsh-plugin.mjs <deepseek-harness 检出路径>` 刷新,该脚本
|
|
528
|
-
会把 dsh 内部包名改写成发布用的名字。本仓库构建不了它们,所以只证明它能
|
|
529
|
-
证明的:`typecheck:packages` 和不依赖框架的规格测试在 CI 里跑,
|
|
530
|
-
`tests/packages.test.ts` 守住 src↔lib 的结构、共享版本线和 MCP 行的拉起
|
|
531
|
-
方式。需要 `@deepseek-ai` 框架或 DOM 的规格测试连同理由一起写在
|
|
532
|
-
`vitest.config.ts` 里。详见
|
|
533
|
-
[`packages/dsh/README.md`](packages/dsh/README.md)。
|
|
534
|
-
|
|
535
574
|
### 库
|
|
536
575
|
|
|
537
576
|
底部各层同时是一个库(`npm run build` 产出带类型声明的 `lib/`,npm 打包
|
|
@@ -549,8 +588,7 @@ tarball,只要 `exports` 或 `bin` 里有任何目标没被打进去就失败
|
|
|
549
588
|
|
|
550
589
|
**浏览器安全**的意思是 import 闭包里没有任何 Node 内建模块,由测试门禁
|
|
551
590
|
守护——图形客户端(web 面板、编辑器视图)可以直接解析状态文件,并复用与
|
|
552
|
-
|
|
553
|
-
客户端。
|
|
591
|
+
终端面板完全一致的聚合、缩放与字形语义。
|
|
554
592
|
|
|
555
593
|
## 许可证
|
|
556
594
|
|
|
@@ -3,15 +3,11 @@ import { createRequire } from 'node:module'; const require = createRequire(impor
|
|
|
3
3
|
|
|
4
4
|
// src/hook/session-start.ts
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
|
-
import { existsSync
|
|
6
|
+
import { existsSync, readFileSync as readFileSync2, realpathSync } from "node:fs";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
|
-
import { dirname as
|
|
8
|
+
import { dirname as dirname4, join as join4 } from "node:path";
|
|
9
9
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
10
10
|
|
|
11
|
-
// src/store/store.ts
|
|
12
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
13
|
-
import { basename, dirname, join } from "node:path";
|
|
14
|
-
|
|
15
11
|
// src/domain/types.ts
|
|
16
12
|
var ok = (value) => ({ ok: true, value });
|
|
17
13
|
var err = (error) => ({ ok: false, error });
|
|
@@ -19,23 +15,30 @@ var RANK_MIN = 0;
|
|
|
19
15
|
var RANK_MAX = 99;
|
|
20
16
|
var RANK_RULE_TEXT = `an integer in ${RANK_MIN}..${RANK_MAX}, 0 = bottom / most primitive`;
|
|
21
17
|
|
|
22
|
-
// src/store/
|
|
18
|
+
// src/store/pages.ts
|
|
19
|
+
import { basename, dirname, join } from "node:path";
|
|
20
|
+
var STORE_DIR_NAME = ".mellos";
|
|
21
|
+
var STATE_FILE_RELATIVE_PATH = join(STORE_DIR_NAME, "map.json");
|
|
22
|
+
var PAGES_DIR_NAME = "pages";
|
|
23
|
+
|
|
24
|
+
// src/store/json-text.ts
|
|
23
25
|
function isRecord(v) {
|
|
24
26
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
25
27
|
}
|
|
26
28
|
function stripBom(text) {
|
|
27
29
|
return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
28
30
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
|
|
32
|
+
// src/store/policy.ts
|
|
33
|
+
import { readFileSync } from "node:fs";
|
|
34
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
32
35
|
var CONFIG_FILE_NAME = "config.json";
|
|
33
36
|
var CONFIG_FILE_VERSION = 1;
|
|
34
37
|
function configFilePath(defaultFile) {
|
|
35
|
-
return
|
|
38
|
+
return join2(dirname2(defaultFile), CONFIG_FILE_NAME);
|
|
36
39
|
}
|
|
37
40
|
function userConfigFilePath(userBase) {
|
|
38
|
-
return
|
|
41
|
+
return join2(userBase, STORE_DIR_NAME, CONFIG_FILE_NAME);
|
|
39
42
|
}
|
|
40
43
|
var MAPPING_POLICIES = ["always", "complex", "on-request"];
|
|
41
44
|
function makeMappingPolicy(raw) {
|
|
@@ -84,7 +87,10 @@ function effectiveMappingPolicy(projectConfigFile, userConfigFile) {
|
|
|
84
87
|
const source = project.value !== void 0 ? "project" : user.value !== void 0 ? "user" : void 0;
|
|
85
88
|
return ok({ project: project.value, user: user.value, effective, source });
|
|
86
89
|
}
|
|
87
|
-
|
|
90
|
+
|
|
91
|
+
// src/store/migration.ts
|
|
92
|
+
import { dirname as dirname3, join as join3 } from "node:path";
|
|
93
|
+
var LEGACY_STATE_FILE_RELATIVE_PATH = join3(".claude", "mellos-mapping.json");
|
|
88
94
|
|
|
89
95
|
// src/hook/session-start.ts
|
|
90
96
|
function sessionStartContext(input) {
|
|
@@ -119,10 +125,10 @@ function sessionStartContext(input) {
|
|
|
119
125
|
].join("\n");
|
|
120
126
|
}
|
|
121
127
|
function hasMap(stateFile) {
|
|
122
|
-
return
|
|
128
|
+
return existsSync(stateFile) || existsSync(join4(dirname4(stateFile), PAGES_DIR_NAME));
|
|
123
129
|
}
|
|
124
130
|
function mmapShimFilePath(localAppData) {
|
|
125
|
-
return
|
|
131
|
+
return join4(localAppData, "mellos-mapping", "bin", "mmap.cmd");
|
|
126
132
|
}
|
|
127
133
|
function mmapShimCurrent(shimContent, mmapPath) {
|
|
128
134
|
return shimContent !== void 0 && shimContent.includes(`"${mmapPath}"`);
|
|
@@ -155,7 +161,7 @@ function ensureMmapCommand(pluginRoot) {
|
|
|
155
161
|
if (process.platform !== "win32") return void 0;
|
|
156
162
|
const localAppData = process.env["LOCALAPPDATA"];
|
|
157
163
|
if (localAppData === void 0 || localAppData === "") return void 0;
|
|
158
|
-
const mmapPath =
|
|
164
|
+
const mmapPath = join4(pluginRoot, "dist", "mmap.mjs");
|
|
159
165
|
let shim;
|
|
160
166
|
try {
|
|
161
167
|
shim = readFileSync2(mmapShimFilePath(localAppData), "utf8");
|
|
@@ -165,7 +171,7 @@ function ensureMmapCommand(pluginRoot) {
|
|
|
165
171
|
if (mmapShimCurrent(shim, mmapPath)) return void 0;
|
|
166
172
|
const run = spawnSync(
|
|
167
173
|
process.execPath,
|
|
168
|
-
[
|
|
174
|
+
[join4(pluginRoot, "scripts", "install-mmap-command.mjs"), "--json"],
|
|
169
175
|
{ encoding: "utf8", windowsHide: true, timeout: 15e3 }
|
|
170
176
|
);
|
|
171
177
|
if (run.status !== 0 || typeof run.stdout !== "string") return void 0;
|
|
@@ -199,10 +205,10 @@ async function readAll(stream) {
|
|
|
199
205
|
async function main() {
|
|
200
206
|
const raw = process.stdin.isTTY === true ? "" : await readAll(process.stdin);
|
|
201
207
|
const projectDir = parseHookInput(raw).cwd ?? process.cwd();
|
|
202
|
-
const stateFile =
|
|
208
|
+
const stateFile = join4(projectDir, STATE_FILE_RELATIVE_PATH);
|
|
203
209
|
const scopes = effectiveMappingPolicy(configFilePath(stateFile), userConfigFilePath(homedir()));
|
|
204
210
|
if (!scopes.ok) return;
|
|
205
|
-
const pluginRoot =
|
|
211
|
+
const pluginRoot = dirname4(dirname4(fileURLToPath(import.meta.url)));
|
|
206
212
|
const context = sessionStartContext({
|
|
207
213
|
policy: scopes.value.effective,
|
|
208
214
|
hasStore: hasMap(stateFile)
|