mellos-mapping 0.24.0 → 0.26.0

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 CHANGED
@@ -61,7 +61,11 @@ Clone the branch for your host, then run one command. No build is required.
61
61
  | `claude` | Claude Code | `node install.mjs` |
62
62
  | `chatgpt-app` | ChatGPT desktop, Codex mode | `node install.mjs` |
63
63
 
64
- Requires Node.js 18+ and the corresponding host CLI on PATH. The installer checks
64
+ Requires Node.js 18.17+ on the 18.x line, or 20.3+, and the corresponding host CLI
65
+ on PATH. Supported platforms are Windows 10+, macOS 13.0+ and Linux with glibc
66
+ 2.28+, each on x64 or arm64. The OS must also meet the selected Node.js version's
67
+ requirements. Native lock bindings ship with the release; no local compilation
68
+ is needed. The installer checks
65
69
  release integrity and all eight MCP tools, retains the runtime outside the clone,
66
70
  and configures the host. Start a new conversation after installation.
67
71
  See [release and branch instructions](docs/releasing.md).
@@ -81,13 +85,41 @@ Or one line in a terminal:
81
85
  claude plugin marketplace add GuangminJu/mellos-mapping && claude plugin install mellos-mapping@mellos-mapping
82
86
  ```
83
87
 
84
- Requires Node.js 18+ on PATH (install Node separately when using native Claude Code). No build step: `dist/` is committed, so a clone runs as-is —
88
+ Requires the same [Node.js and platform versions](#install) on PATH (install
89
+ Node separately when using native Claude Code). No build step: `dist/` is
90
+ committed, so a clone runs as-is —
85
91
  `dist/server.mjs` (the MCP server), `dist/watch.mjs` (the pane),
86
92
  `dist/mmap.mjs` (the `mmap` toggle), `dist/hook-session-start.mjs` (the
87
93
  `SessionStart` hook that `hooks/hooks.json` registers) and
88
94
  `dist/store-paths.mjs` (the store's path vocabulary, which the plain-node pane
89
95
  launcher imports instead of restating filenames).
90
96
 
97
+ **omp (Oh My Pi)** reads the same plugin layout the Claude Code edition ships —
98
+ the same marketplace catalog, `.mcp.json`, skill and slash command — so one
99
+ release serves both hosts:
100
+
101
+ ```
102
+ omp plugin marketplace add GuangminJu/mellos-mapping
103
+ omp plugin install mellos-mapping@mellos-mapping
104
+ ```
105
+
106
+ That install follows `main`, where a release is a version bump. To freeze a
107
+ version instead, add a checkout of one — the Claude Code edition ZIP from the
108
+ GitHub Release, or a clone at the release tag:
109
+
110
+ ```
111
+ omp plugin marketplace add "<checkout or extracted release directory>"
112
+ omp plugin install mellos-mapping@mellos-mapping
113
+ ```
114
+
115
+ omp never reads `hooks/hooks.json`, so the session paragraph arrives through the
116
+ plugin's omp host adapter (`dist/omp-extension.mjs`, declared in
117
+ `package.json#omp.extensions`): the same store, the same policy text, and the
118
+ same `mmap` shim install as the Claude hook. The pane is the same terminal
119
+ split beside the session. Install it *as a plugin*: neither `omp plugin link`
120
+ nor the npm package is a plugin, and both would leave the tools behind. See
121
+ [omp installation and limitations](docs/distributions/omp.md).
122
+
91
123
  The first session after installing asks you **one** question — how eager
92
124
  mapping should be — and records the answer for every project you will ever
93
125
  open. From then on the hook carries it into each new session by itself; there
@@ -104,8 +136,14 @@ Terminal inherits the old environment, so close the terminal app entirely and
104
136
  reopen it before the first `mmap`. The PATH edit keeps the
105
137
  installer's guarantees: nothing happens when the entry is already there, and
106
138
  a PATH that `setx` would damage (flattened `%VARIABLE%` references, truncation
107
- past its limit) is refused outright, with the entry to add by hand named
108
- instead.
139
+ past its limit) is refused outright.
140
+
141
+ A refused PATH is not a dead end: the same two shims are then written into
142
+ `%LOCALAPPDATA%\Microsoft\WindowsApps` (or `~/.local/bin`) — a directory your
143
+ PATH already names — so `mmap` is runnable in a new terminal with no PATH
144
+ change at all. Machines with a long PATH are exactly where that happens, and
145
+ where it matters most. The command takes the page to open as an argument
146
+ (`mmap omp-host-support`), and closes the pane when it is already open.
109
147
 
110
148
  The step behind it is still a command of its own, for the cases the hook does
111
149
  not cover — `--uninstall`, or re-adding a PATH entry you removed while the
@@ -130,6 +168,26 @@ marketplace clone — the first command is what actually pulls this repo.
130
168
  Restart Claude Code to apply. Releases are version bumps on `main`.
131
169
  (In-app, `/plugin` opens the same management UI.)
132
170
 
171
+ omp updates with its own two steps:
172
+
173
+ ```
174
+ omp plugin marketplace update mellos-mapping && omp plugin upgrade mellos-mapping@mellos-mapping
175
+ ```
176
+
177
+ Close the pane first (`q` in it): the watcher runs from the plugin copy, and on
178
+ Windows an open file cannot be renamed away, so an upgrade attempted while a
179
+ pane is open can fail with `EPERM` and leave the plugin cache empty — repaired
180
+ by `omp plugin install mellos-mapping@mellos-mapping --force`. Then: the first
181
+ command refreshes the catalog, the second reinstalls from it. omp also
182
+ refreshes a catalog entry it has not updated for a day at startup, unless
183
+ `marketplace.autoUpdate` is `off` — in the default `notify` mode that check
184
+ writes its finding to the debug log only, so `marketplace update` is the step
185
+ that makes an update visible. `upgrade` does not compare versions: it
186
+ force-reinstalls whatever the catalog names, which is why a release is
187
+ identified by its version bump rather than gated by it. Restart omp afterwards,
188
+ and reopen a pane still showing the old bundle (`q` in it, then `mmap_open`
189
+ again).
190
+
133
191
  ### Upgrading from 0.19
134
192
 
135
193
  0.20 moved the store out of `.claude/` — the map belongs to this tool, not to
@@ -446,19 +504,31 @@ State lives in the tool-owned `.mellos/` directory at the project root:
446
504
  | `.mellos/quit` | one-shot "close yourself" request from the `mmap` toggle, consumed and deleted the same way |
447
505
  | `.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)) |
448
506
  | `<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 |
507
+ | `.mellos/.write-lock` | permanent regular file for the OS project lock; keep it untracked and never remove, rename or replace it during normal operation |
449
508
 
450
509
  The map files are plain JSON, safe to commit if you want the maps' history in
451
- git. The other three are runtime chatter between a pane and whoever is talking
452
- to it gitignore `focus`, `quit` and `viewers/` if you commit the store.
510
+ git. Keep `focus`, `quit`, `viewers/` and `.write-lock` out of Git. The first
511
+ three carry runtime messages; the lock file must remain in place, including
512
+ when no process holds its lock.
453
513
 
454
514
  **Concurrency, stated plainly.** Every save is atomic — written to a private
455
515
  sibling temp file and renamed over the target — so a reader polling the store
456
- sees the previous complete map or the new one, never a torn write. There is
457
- no lost-update protection: two writers saving the *same* page race, and the
458
- last rename wins, silently discarding what the other computed from an older
459
- read. Pages are the isolation unit two sessions that must not clobber each
460
- other belong on two pages, which is also the answer to running several Claude
461
- sessions in one project.
516
+ sees the previous complete map or the new one, never a torn write. Every graph
517
+ writer (MCP, the HTTP viewer, the watcher) uses the same non-blocking OS
518
+ exclusive lock for the project. The lock has its own fixed file, separate from
519
+ the map files being replaced; it stays in place after the OS releases the lock
520
+ on completion or process exit. Contention returns a retryable `BUSY`.
521
+
522
+ The lock serializes each operation's read, change and save. To protect an edit
523
+ computed from an earlier read, pass `expectedRevision`: it is compared inside
524
+ the lock, and a stale revision returns `CONFLICT`. Without it, a later write
525
+ can still overwrite the same field or a whole value such as `context`. Pages
526
+ separate unrelated efforts; sharing a page still requires revision-checked
527
+ edits. Low-level `saveMapFile`, hand edits and older running processes do not
528
+ participate in this contract. Stop all old MCP, viewer and watcher processes
529
+ for the project before upgrading; an old lock directory needs a one-time
530
+ migration. See the [persistent-map API guide](docs/map-api.md) and
531
+ [project locking](docs/locking.md) for the boundary and upgrade steps.
462
532
 
463
533
  ### Diagram kinds
464
534
 
package/README.zh-CN.md CHANGED
@@ -54,7 +54,10 @@ Claude 为你构建系统时,对话旁边的分屏实时显示这个系统的*
54
54
  | `claude` | Claude Code | `node install.mjs` |
55
55
  | `chatgpt-app` | ChatGPT 桌面 App 的 Codex 模式 | `node install.mjs` |
56
56
 
57
- 前置要求为 Node.js 18+ 和对应宿主的 CLI,并确保命令在 PATH 中。安装器检查
57
+ 前置要求为 Node.js 18.17 及以上的 18.x,或 20.3 及以上版本,以及对应宿主的 CLI,
58
+ 并确保命令在 PATH 中。支持 Windows 10+、macOS 13.0+ 和 glibc 2.28+ 的 Linux,均支持
59
+ x64 与 arm64;系统还须满足所选 Node.js 版本的更高要求。发行包自带原生锁绑定,
60
+ 无需用户本地编译。安装器检查
58
61
  发行文件和八个 MCP 工具,将运行时保留到克隆目录之外,完成宿主配置。
59
62
  安装后开启新对话。详见[发布与分支说明](docs/releasing.md)。
60
63
 
@@ -73,13 +76,38 @@ Claude Code 也可以通过插件市场安装:
73
76
  claude plugin marketplace add GuangminJu/mellos-mapping && claude plugin install mellos-mapping@mellos-mapping
74
77
  ```
75
78
 
76
- 需要 PATH 上有 Node.js 18+(Claude Code 本身就依赖 Node,所以你已经有了)。
79
+ 需要 PATH 上有符合[安装要求](#安装)的 Node.js;使用原生 Claude Code 安装包时也需
80
+ 单独安装 Node.js。
77
81
  没有构建步骤:`dist/` 是提交进仓库的,克隆即用——`dist/server.mjs`(MCP
78
82
  服务器)、`dist/watch.mjs`(面板)、`dist/mmap.mjs`(`mmap` 开关)、
79
83
  `dist/hook-session-start.mjs`(由 `hooks/hooks.json` 注册的 `SessionStart`
80
84
  钩子),以及 `dist/store-paths.mjs`(存储的路径词汇;纯 node 的面板启动
81
85
  脚本从这里导入,而不是自己抄一份文件名)。
82
86
 
87
+ **omp(Oh My Pi)** 读的是 Claude Code 版本用的同一套插件布局——同一份市场
88
+ 目录、`.mcp.json`、技能与斜杠命令——一份发行包同时服务两个宿主:
89
+
90
+ ```
91
+ omp plugin marketplace add GuangminJu/mellos-mapping
92
+ omp plugin install mellos-mapping@mellos-mapping
93
+ ```
94
+
95
+ 这样装跟的是 `main`,而发布就是它上面的版本号提升。要冻结版本,就改把某个
96
+ 版本的检出目录加为市场——GitHub Release 里的 Claude Code 版本 ZIP,或按
97
+ tag 检出的克隆:
98
+
99
+ ```
100
+ omp plugin marketplace add "<检出或解压出来的发行目录>"
101
+ omp plugin install mellos-mapping@mellos-mapping
102
+ ```
103
+
104
+ omp 不读 `hooks/hooks.json`,所以那段会话说明由插件的 omp 宿主适配器送达
105
+ (`dist/omp-extension.mjs`,在 `package.json#omp.extensions` 里声明):同一个
106
+ 存储、同一段策略文字,Windows 上的 `mmap` shim 也由它安装。面板仍旧是会话
107
+ 旁边的终端分屏。请把它**当插件**安装——`omp plugin link` 与 npm 包都不是插
108
+ 件,用它们装会只剩适配器、没有工具。见
109
+ [omp 安装与限制](docs/distributions/omp.md)。
110
+
83
111
  装完之后的第一个会话只会问你**一个**问题——建图要多积极——并把答案记成
84
112
  你以后打开的每一个项目的默认。此后钩子会自己把它带进每个新会话;再也没有
85
113
  "每个项目设置一遍"这回事。见
@@ -93,7 +121,13 @@ PowerShell)和 `mmap`(git-bash)写进 `%LOCALAPPDATA%\mellos-mapping\bin`
93
121
  所以第一次敲 `mmap` 之前要把终端应用整个关掉重开。PATH 的编辑保持安装器
94
122
  原有的承诺:条目已经在里面
95
123
  就什么都不做;遇到 `setx` 会损坏的 PATH(`%VARIABLE%` 被展平、超长被截
96
- 断),它干脆拒绝,改为把要手动添加的条目说清楚。
124
+ 断),它干脆拒绝。
125
+
126
+ PATH 被拒绝不等于没命令可用:同样两个 shim 会再写一份到
127
+ `%LOCALAPPDATA%\Microsoft\WindowsApps`(或 `~/.local/bin`)——一个你的 PATH
128
+ 本来就有名字的目录——于是**新终端里直接敲 `mmap` 就能用,完全不用改 PATH**。
129
+ PATH 很长的机器正是这种情况,也最需要它。命令可以带要打开的页名
130
+ (`mmap omp-host-support`);面板已经开着时再敲一次就是关掉它。
97
131
 
98
132
  它背后的那一步仍然是个独立命令,留给钩子管不到的情形——`--uninstall`,
99
133
  或者 shim 还在、PATH 条目却被你删掉之后重新加回去:
@@ -116,6 +150,22 @@ claude plugin marketplace update mellos-mapping && claude plugin update mellos-m
116
150
  拉取本仓库的是第一条命令。重启 Claude Code 生效。发布即 `main` 分支
117
151
  上的版本号提升。(在对话里输入 `/plugin` 也能打开同一个管理界面。)
118
152
 
153
+ omp 也有自己的两步:
154
+
155
+ ```
156
+ omp plugin marketplace update mellos-mapping && omp plugin upgrade mellos-mapping@mellos-mapping
157
+ ```
158
+
159
+ **先关面板**(面板里按 `q`):watcher 跑的就是插件副本里的文件,而 Windows 不
160
+ 允许改名一个正被打开的文件——开着面板升级会以 `EPERM` 失败并把插件缓存清空
161
+ (旧副本已删、新副本未就位),用 `omp plugin install mellos-mapping@mellos-mapping
162
+ --force` 修回来。然后:第一条刷新市场目录,第二条按目录重装。omp 还会在启动时尽力刷新超过一天没更新过
163
+ 的目录条目——除非 `marketplace.autoUpdate` 设为 `off`;默认的 `notify` 模式只把
164
+ "有可用更新"写进 debug 日志,所以让更新**看得见**的是 `marketplace update` 这一
165
+ 步。`upgrade` 不比较版本:它按目录里现在指的内容强制重装,同版本号也会重下——
166
+ 因此版本号提升是给发行**贴标签**,而不是送达的门槛。之后重启 omp,并把仍显示旧
167
+ 运行文件的面板关掉重开(面板里按 `q`,再 `mmap_open`)。
168
+
119
169
  ### 从 0.19 升级
120
170
 
121
171
  0.20 把地图存储从 `.claude/` 挪到了 `.mellos/`——地图属于这个工具,不属于
@@ -405,17 +455,26 @@ watcher 的:`--file <path>` 指定默认页的状态文件(启动脚本会
405
455
  | `.mellos/quit` | `mmap` 开关发出的一次性"自己关掉"请求,以同样的方式被消费和删除 |
406
456
  | `.mellos/viewers/<pid>.json` | 每个活着的面板一份报告——它正显示哪一页、自动跟随是否打开——运行期间每秒刷新一次(见[谁在看](#谁在看)) |
407
457
  | `<上面任一文件>.<pid>.<随机>.tmp` | 正在落盘的一次写入;它要么被改名覆盖目标,要么被删掉。留下来说明那次写入失败(并已被报告),连清理都没能跑成 |
458
+ | `.mellos/.write-lock` | 操作系统项目锁使用的永久普通文件;不纳入 Git,正常运行时不得删除、改名或替换 |
408
459
 
409
- 地图文件是纯 JSON,想在 git 里留下地图的历史就把它们提交进去。另外三个是
410
- 面板和跟它说话的人之间的运行期闲聊——如果你要提交这个仓库,把 `focus`、
411
- `quit` 和 `viewers/` 加进 gitignore。
460
+ 地图文件是纯 JSON,想在 git 里留下地图的历史就把它们提交进去。`focus`、`quit`、
461
+ `viewers/` 和 `.write-lock` 应加入 gitignore。前三者传递运行期消息;锁文件即使
462
+ 没有进程持锁也应保留,不要当作临时文件清理。
412
463
 
413
464
  **并发模型,直说。** 每次保存都是原子的——先写进一个私有的同级临时文件,
414
465
  再改名覆盖目标——所以轮询存储的读者要么看到上一张完整的地图,要么看到新的
415
- 那张,绝不会读到写了一半的。但**没有丢失更新保护**:两个写者保存*同一页*
416
- 就是在赛跑,最后那次改名赢,另一个基于旧读取算出来的东西被静默丢弃。页就是
417
- 隔离单位——不许互相覆盖的两个会话,就该在两页上,这也是同一项目里跑多个
418
- Claude 会话的正确姿势。
466
+ 那张,绝不会读到写了一半的。所有写图入口(MCP、HTTP 查看器、watcher)都使用
467
+ 同一把项目级、非阻塞的操作系统排他锁。锁使用独立的固定文件,与被原子替换的
468
+ 地图文件分开;操作结束或进程退出后,操作系统释放锁,锁文件仍保留。锁被占用时
469
+ 返回可重试的 `BUSY`。
470
+
471
+ 这把锁串行执行每次操作的读取、修改和保存。基于先前读取计算的改动还应传入
472
+ `expectedRevision`:它在锁内比较,版本过旧返回 `CONFLICT`。省略它时,后续写入
473
+ 仍可能覆盖同一字段或 `context` 这样的完整值。不同页面用于分开互不相关的工作;
474
+ 共用一页时仍需带版本检查。底层 `saveMapFile`、手工编辑和仍在运行的旧进程不参与
475
+ 这份约定。升级前须停掉该项目所有旧 MCP、查看器和 watcher 进程;残留的旧锁目录
476
+ 需一次性迁移。完整边界和升级步骤见[持久化地图 API 指南](docs/map-api.md)与
477
+ [项目锁说明](docs/locking.md)。
419
478
 
420
479
  ### 图种
421
480
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
2
+ import { createRequire as __mellosCreateRequire } from 'node:module'; const require = __mellosCreateRequire(import.meta.url);
3
3
 
4
4
  // src/hook/session-start.ts
5
5
  import { spawnSync } from "node:child_process";
@@ -129,11 +129,47 @@ function sessionStartContext(input) {
129
129
  function hasMap(stateFile) {
130
130
  return existsSync(stateFile) || existsSync(join4(dirname4(stateFile), PAGES_DIR_NAME));
131
131
  }
132
+ function mmapBinDir(localAppData) {
133
+ return join4(localAppData, "mellos-mapping", "bin");
134
+ }
132
135
  function mmapShimFilePath(localAppData) {
133
- return join4(localAppData, "mellos-mapping", "bin", "mmap.cmd");
136
+ return join4(mmapBinDir(localAppData), "mmap.cmd");
137
+ }
138
+ function mmapFallbackDirs(localAppData, home) {
139
+ return [join4(localAppData, "Microsoft", "WindowsApps"), join4(home, ".local", "bin")];
140
+ }
141
+ function pathNames(rawPath, dir) {
142
+ const norm = (s) => s.trim().replace(/^"|"$/g, "").replace(/[\\/]+$/, "").toLowerCase();
143
+ return rawPath.split(";").some((entry) => entry.trim() !== "" && norm(entry) === norm(dir));
144
+ }
145
+ function readIfPresent(path) {
146
+ try {
147
+ return readFileSync2(path, "utf8");
148
+ } catch {
149
+ return void 0;
150
+ }
134
151
  }
135
152
  function mmapShimCurrent(shimContent, mmapPath) {
136
- return shimContent !== void 0 && shimContent.includes(`"${mmapPath}"`);
153
+ if (shimContent === void 0) return false;
154
+ return shimContent.includes(`"${mmapPath}"`) || shimContent.includes(`"${mmapPath.replaceAll("\\", "/")}"`);
155
+ }
156
+ function shimIsOurs(content) {
157
+ if (content.includes("mellos-mapping mmap shim")) return true;
158
+ const quoted = /"([^"]*mmap\.mjs)"/.exec(content)?.[1];
159
+ return quoted !== void 0 && /mellos-mapping/i.test(quoted);
160
+ }
161
+ function mmapCommandResolves(localAppData, mmapPath, environment, read, exists) {
162
+ if (pathNames(environment.path, mmapBinDir(localAppData))) return true;
163
+ if (environment.home === void 0 || environment.home === "") return false;
164
+ for (const dir of mmapFallbackDirs(localAppData, environment.home)) {
165
+ if (!exists(dir) || !pathNames(environment.path, dir)) continue;
166
+ const files = ["mmap.cmd", "mmap"].map((name) => read(join4(dir, name))).filter((content) => content !== void 0);
167
+ if (files.length === 0) continue;
168
+ const ours = files.filter((content) => shimIsOurs(content));
169
+ if (ours.length === 0) return true;
170
+ return ours.every((content) => mmapShimCurrent(content, mmapPath));
171
+ }
172
+ return false;
137
173
  }
138
174
  function installContextLine(outcome) {
139
175
  if (typeof outcome !== "object" || outcome === null) return void 0;
@@ -150,11 +186,22 @@ function installContextLine(outcome) {
150
186
  }
151
187
  if (o.path === "refused" || o.path === "error") {
152
188
  const reason = typeof o.reason === "string" ? o.reason : "the PATH edit failed";
189
+ const aliasDir = typeof o.alias === "object" && o.alias !== null && "dir" in o.alias && typeof o.alias.dir === "string" ? o.alias.dir : void 0;
190
+ if (aliasDir !== void 0) {
191
+ return [
192
+ `mellos-mapping: the \`mmap\` terminal command should work: its shims live in`,
193
+ `${o.binDir}, and \u2014 because the user PATH could not be changed (${reason}) \u2014 a second copy`,
194
+ `was written to ${aliasDir}, a directory PATH already names (so \`mmap\` resolves there unless`,
195
+ "something earlier in PATH claims the name first).",
196
+ "The user can therefore type `mmap` in a new terminal right now; the command opens the map",
197
+ "pane for the project it is typed in, or closes the open one."
198
+ ].join("\n");
199
+ }
153
200
  return [
154
201
  `mellos-mapping: the \`mmap\` command's launcher was written to ${o.binDir},`,
155
202
  `but the user PATH was NOT changed: ${reason}.`,
156
203
  "If the user wants the `mmap` pane-toggle command, tell them to add that directory to",
157
- 'their user PATH (Settings > "Edit environment variables for your account").'
204
+ 'their user PATH (Settings > "Edit environment variables for their account").'
158
205
  ].join("\n");
159
206
  }
160
207
  return void 0;
@@ -164,15 +211,18 @@ function ensureMmapCommand(pluginRoot) {
164
211
  const localAppData = process.env["LOCALAPPDATA"];
165
212
  if (localAppData === void 0 || localAppData === "") return void 0;
166
213
  const mmapPath = join4(pluginRoot, "dist", "mmap.mjs");
167
- let shim;
168
- try {
169
- shim = readFileSync2(mmapShimFilePath(localAppData), "utf8");
170
- } catch {
171
- shim = void 0;
172
- }
173
- if (mmapShimCurrent(shim, mmapPath)) return void 0;
214
+ const shim = readIfPresent(mmapShimFilePath(localAppData));
215
+ const resolves = mmapShimCurrent(shim, mmapPath) && mmapCommandResolves(
216
+ localAppData,
217
+ mmapPath,
218
+ { path: process.env["PATH"] ?? "", home: process.env["USERPROFILE"] ?? process.env["HOME"] },
219
+ readIfPresent,
220
+ existsSync
221
+ );
222
+ if (resolves) return void 0;
223
+ const runtime = "bun" in process.versions ? "node" : process.execPath;
174
224
  const run = spawnSync(
175
- process.execPath,
225
+ runtime,
176
226
  [join4(pluginRoot, "scripts", "install-mmap-command.mjs"), "--json"],
177
227
  { encoding: "utf8", windowsHide: true, timeout: 15e3 }
178
228
  );
@@ -236,12 +286,18 @@ if (launchedAsEntry(process.argv[1], import.meta.url)) {
236
286
  main().catch(() => process.exit(0));
237
287
  }
238
288
  export {
289
+ ensureMmapCommand,
239
290
  hasMap,
240
291
  hookOutput,
241
292
  installContextLine,
242
293
  launchedAsEntry,
294
+ mmapBinDir,
295
+ mmapCommandResolves,
296
+ mmapFallbackDirs,
243
297
  mmapShimCurrent,
244
298
  mmapShimFilePath,
245
299
  parseHookInput,
246
- sessionStartContext
300
+ pathNames,
301
+ sessionStartContext,
302
+ shimIsOurs
247
303
  };