mellos-mapping 0.26.1 → 0.27.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/dist/server.mjs CHANGED
@@ -24650,7 +24650,7 @@ function launcherViewerPid(run) {
24650
24650
 
24651
24651
  // src/server/server.ts
24652
24652
  var SERVER_NAME = "mellos-mapping";
24653
- var SERVER_VERSION = "0.26.1";
24653
+ var SERVER_VERSION = "0.27.0";
24654
24654
  function text(s, isError = false) {
24655
24655
  return { content: [{ type: "text", text: s }], ...isError ? { isError: true } : {} };
24656
24656
  }
@@ -4,8 +4,11 @@ header{display:flex;align-items:center;justify-content:space-between;gap:12px;pa
4
4
  .brand{color:#e5edf7;text-decoration:none;font-weight:650;white-space:nowrap}.brand span{font-weight:400;color:#97a7bd;font-size:12px;margin-left:12px}.brand:hover span{color:#81dbae}
5
5
  .tools{display:flex;align-items:center;gap:8px;font-size:12px;color:#aab8cb}button,select{font:inherit;color:#d9e2ed;background:#202b3a;border:1px solid #3b4b60;border-radius:5px;padding:4px 7px;cursor:pointer}button:hover,select:hover{border-color:#81dbae}button:focus-visible,select:focus-visible,a:focus-visible{outline:2px solid #81dbae;outline-offset:2px}
6
6
  #terminal{min-width:0;min-height:0;padding:0;overflow:hidden;user-select:none;background:#10151c}#terminal .xterm{height:100%;padding:8px 10px 0}#terminal .xterm-viewport{overscroll-behavior:contain}
7
+ #terminal .xterm-helper-textarea:disabled{pointer-events:none}
7
8
  footer{min-height:32px;display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;padding:5px 14px;border-top:1px solid #283343;color:#8c9eb5;font-size:11px}#connection{overflow-wrap:anywhere}#connection[data-state="connected"]{color:#81dbae}#connection[data-state="error"]{color:#ffaf87}.hint{margin-left:auto}[hidden]{display:none!important}
8
- @media(max-width:440px){.brand span{margin-left:6px}.hint{margin-left:0;width:100%}header{padding:0 10px}#terminal .xterm{padding-left:4px;padding-right:4px}}
9
+ button:disabled{opacity:.65;cursor:default}
10
+ @media(max-width:600px){.brand span{display:none}}
11
+ @media(max-width:440px){.tools label{display:none}.hint{margin-left:0;width:100%}header{padding:0 10px}#terminal .xterm{padding-left:4px;padding-right:4px}}
9
12
  dialog{max-width:min(420px,90vw);border:1px solid #3b4b60;border-radius:10px;background:#151c25;color:#d9e2ed;padding:24px}dialog::backdrop{background:#0009}dialog h2{font-size:18px;margin-top:0}dialog p{line-height:1.9;font-size:13px}dialog form{text-align:right}
10
13
 
11
14
  #star-reminder {
@@ -1,7 +1,7 @@
1
1
  <!doctype html>
2
2
  <html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Mellos · 终端地图</title><link rel="icon" href="data:,"><link rel="stylesheet" href="./xterm.css"><link rel="stylesheet" href="./terminal.css"><script type="module" src="./terminal.js"></script></head>
3
- <body><header><a class="brand" id="graphic" href="./" title="切换到图形地图">Mellos <span>图形地图 ↗</span></a><div class="tools"><label for="font-size">字号</label><select id="font-size" aria-label="终端字号"><option>10</option><option>11</option><option>12</option><option selected>13</option><option>14</option><option>15</option><option>16</option><option>18</option><option>20</option><option>24</option><option>28</option></select><button id="help" aria-label="操作帮助">?</button></div></header>
3
+ <body><header><a class="brand" id="graphic" href="./" title="切换到图形地图">Mellos <span>图形地图 ↗</span></a><div class="tools"><button id="keyboard">启用快捷键</button><label for="font-size">字号</label><select id="font-size" aria-label="终端字号"><option>10</option><option>11</option><option>12</option><option selected>13</option><option>14</option><option>15</option><option>16</option><option>18</option><option>20</option><option>24</option><option>28</option></select><button id="help" aria-label="操作帮助">?</button></div></header>
4
4
  <main id="terminal" aria-label="Mellos 终端地图"></main>
5
- <footer><span id="connection" role="status" aria-live="polite">正在连接…</span><button id="restart" hidden>重新连接</button><span class="hint">点击地图启用快捷键 · 滚轮缩放 · 拖动平移 · ? 帮助</span><aside id="star-reminder" aria-label="支持 Mellos Mapping" hidden><span>地图有帮到你?欢迎在 GitHub 点个 Star。</span><a href="https://github.com/GuangminJu/mellos-mapping" target="_blank" rel="noopener noreferrer">去点 Star ↗</a><button type="button">不再提醒</button></aside></footer>
6
- <dialog id="help-dialog"><h2>终端地图操作</h2><p>滚轮 / + / −:总览 → 模块 → 细节<br>拖动:平移 · 单击:固定详情<br>双击带 ⊞ 的节点:进入子地图<br>Backspace:返回父地图<br>Tab / 数字 1–9:切换页面<br>f:自动跟随 · 0:重置视图<br>x 按两次:确认删除当前页<br>q:关闭地图,可用「重新连接」再次打开</p><p>右上角字号只影响此网页终端。</p><form method="dialog"><button>关闭帮助</button></form></dialog>
5
+ <footer><span id="connection" role="status" aria-live="polite">正在连接…</span><button id="restart" hidden>重新连接</button><span class="hint">滚轮缩放 · 拖动平移 · 快捷键需手动启用,离开地图即释放</span><aside id="star-reminder" aria-label="支持 Mellos Mapping" hidden><span>地图有帮到你?欢迎在 GitHub 点个 Star。</span><a href="https://github.com/GuangminJu/mellos-mapping" target="_blank" rel="noopener noreferrer">去点 Star ↗</a><button type="button">不再提醒</button></aside></footer>
6
+ <dialog id="help-dialog"><h2>终端地图操作</h2><p>鼠标操作可直接使用。键盘操作请先点击「启用快捷键」;Esc 或离开地图后释放,需再次启用。</p><p>滚轮 / + / −:总览 → 模块 → 细节<br>拖动:平移 · 单击:固定详情<br>双击带 ⊞ 的节点:进入子地图<br>Backspace:返回父地图<br>Tab / 数字 1–9:切换页面<br>f:自动跟随 · 0:重置视图<br>x 按两次:确认删除当前页<br>q:关闭地图,可用「重新连接」再次打开</p><p>右上角字号只影响此网页终端。</p><form method="dialog"><button>关闭帮助</button></form></dialog>
7
7
  </body></html>
@@ -9213,6 +9213,36 @@ function createStarNotice(base2) {
9213
9213
  };
9214
9214
  }
9215
9215
 
9216
+ // src/web/terminal-input.ts
9217
+ function bindTerminalInput(textarea, button, surface) {
9218
+ const listeners = new AbortController();
9219
+ const options = { signal: listeners.signal };
9220
+ const release = () => {
9221
+ textarea.blur();
9222
+ textarea.disabled = true;
9223
+ button.disabled = false;
9224
+ button.textContent = "\u542F\u7528\u5FEB\u6377\u952E";
9225
+ };
9226
+ button.addEventListener("click", () => {
9227
+ textarea.disabled = false;
9228
+ textarea.focus({ preventScroll: true });
9229
+ button.disabled = true;
9230
+ button.textContent = "\u5FEB\u6377\u952E\u5DF2\u542F\u7528";
9231
+ }, options);
9232
+ textarea.addEventListener("blur", release, options);
9233
+ window.addEventListener("blur", release, options);
9234
+ surface.addEventListener("pointerleave", release, options);
9235
+ document.documentElement.addEventListener("pointerleave", release, options);
9236
+ document.addEventListener("visibilitychange", () => {
9237
+ if (document.visibilityState !== "visible") release();
9238
+ }, options);
9239
+ release();
9240
+ return { release, dispose: () => {
9241
+ release();
9242
+ listeners.abort();
9243
+ } };
9244
+ }
9245
+
9216
9246
  // src/web/terminal-app.ts
9217
9247
  var element = (id) => document.getElementById(id);
9218
9248
  var container = element("terminal");
@@ -9246,6 +9276,8 @@ var terminal = new Dl({
9246
9276
  var fit = new o();
9247
9277
  terminal.loadAddon(fit);
9248
9278
  terminal.open(container);
9279
+ var keyboard = element("keyboard");
9280
+ var input = bindTerminalInput(terminal.textarea, keyboard, container);
9249
9281
  function setStatus(text, state) {
9250
9282
  status.textContent = text;
9251
9283
  status.dataset.state = state;
@@ -9293,15 +9325,20 @@ font.addEventListener("change", () => {
9293
9325
  var help = element("help-dialog");
9294
9326
  element("help").addEventListener("click", () => help.showModal());
9295
9327
  terminal.attachCustomKeyEventHandler((event) => {
9328
+ if (terminal.textarea.disabled || !document.hasFocus()) return false;
9296
9329
  if (event.isComposing || event.keyCode === 229) return true;
9297
- if (event.key === "?") {
9298
- if (event.type === "keydown" && !help.open) help.showModal();
9330
+ if (event.key === "Escape" || event.key === "?") {
9331
+ if (event.type === "keydown") {
9332
+ input.release();
9333
+ keyboard.focus();
9334
+ if (event.key === "?" && !help.open) help.showModal();
9335
+ }
9299
9336
  return false;
9300
9337
  }
9301
9338
  return true;
9302
9339
  });
9303
9340
  restart.addEventListener("click", () => {
9304
- terminal.focus();
9341
+ input.release();
9305
9342
  retries = 0;
9306
9343
  connect();
9307
9344
  });
@@ -9352,12 +9389,16 @@ function connect() {
9352
9389
  });
9353
9390
  }
9354
9391
  window.addEventListener("pagehide", (event) => {
9392
+ input.release();
9355
9393
  disposed = true;
9356
9394
  clearTimeout(retryTimer);
9357
9395
  cancelAnimationFrame(resizeFrame);
9358
9396
  observer.disconnect();
9359
9397
  socket?.close();
9360
- if (!event.persisted) terminal.dispose();
9398
+ if (!event.persisted) {
9399
+ input.dispose();
9400
+ terminal.dispose();
9401
+ }
9361
9402
  });
9362
9403
  window.addEventListener("pageshow", (event) => {
9363
9404
  if (event.persisted) {
package/docs/codex.md CHANGED
@@ -66,10 +66,13 @@ changes remeasure terminal cells rather than stretching an image, and do not
66
66
  change the host's global terminal font. The terminal uses xterm.js cell rendering;
67
67
  the graphical mode remains native SVG and supports SVG export.
68
68
 
69
- Click the map or Tab into it to use map keyboard shortcuts. Loading, automatic
70
- reconnection and page restoration preserve the current keyboard focus, so the
71
- viewer can remain open while typing in the conversation. Font selection keeps
72
- its own focus, and closing help returns to the control that opened it.
69
+ Mouse zoom, dragging and selection work immediately. To use keyboard shortcuts,
70
+ click **启用快捷键**, or Tab to that button and press Enter. The hidden terminal
71
+ input stays disabled until this explicit action. Escape, leaving the map, losing
72
+ focus or hiding the page releases keyboard input; enable it again when needed.
73
+ Loading, reconnecting and restoring the page do not activate keyboard input.
74
+ Font selection keeps its own focus; closing keyboard help returns to the
75
+ activation button without reactivating the terminal.
73
76
 
74
77
  Both modes use the same project maps. Terminal input/output goes over a local
75
78
  WebSocket to a dedicated mmap worker. Each connected browser owns its own
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mellos-mapping",
3
- "version": "0.26.1",
3
+ "version": "0.27.0",
4
4
  "mcpName": "io.github.GuangminJu/mellos-mapping",
5
5
  "description": "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.",
6
6
  "type": "module",
@@ -70,9 +70,22 @@
70
70
  "./dist/omp-extension.mjs"
71
71
  ]
72
72
  },
73
+ "pi": {
74
+ "extensions": [
75
+ "./dist/pi-extension.mjs"
76
+ ],
77
+ "skills": [
78
+ "./skills"
79
+ ],
80
+ "prompts": [
81
+ "./commands"
82
+ ]
83
+ },
73
84
  "files": [
74
85
  "dist",
75
86
  "lib",
87
+ "skills",
88
+ "commands",
76
89
  "README.zh-CN.md",
77
90
  "docs/codex.md",
78
91
  "docs/map-api.md",
@@ -0,0 +1,316 @@
1
+ ---
2
+ name: mellos-mapping
3
+ description: >-
4
+ Maintain a live layered dependency map (Mellos map) while doing bottom-up
5
+ development. Use when starting any non-trivial implementation or
6
+ architecture task — multiple modules, layers, or more than roughly an hour
7
+ of work. Resume existing maps first; declare missing design, then light nodes up
8
+ bottom-to-top as they are built and verified. Also use when the user asks
9
+ for a mellos map, /mmap, a dependency map, or wants to see development
10
+ progress as a picture. The user's mmap_setup policy decides how eager
11
+ mapping is — always / complex tasks only / on-request.
12
+ ---
13
+
14
+ # Mellos Mapping — the map discipline
15
+
16
+ Eight MCP tools (`mmap_declare`, `mmap_update`, `mmap_remove`, `mmap_view`,
17
+ `mmap_setup`, `mmap_open`, `mmap_read`, `mmap_batch`)
18
+ maintain a **Mellos map**: a layered dependency map of the system under
19
+ construction, persisted in `.mellos/map.json` (default page) plus
20
+ `.mellos/pages/<slug>.json` (named pages) and rendered live in
21
+ a terminal split pane, a desktop Markdown file panel, or an interactive local web viewer beside the conversation. To learn whether a map already
22
+ exists — and under which page slugs — call `mmap_view`: every response ends
23
+ with a `pages:` line naming the pages this project has and which one you are
24
+ looking at. Never probe the default file to decide: it is absent whenever all
25
+ work lives on named pages.
26
+
27
+ With current servers, prefer mmap_read {resource: "pages"}: it returns page
28
+ summaries, saved context and revisions. A new conversation or compaction is not
29
+ a new effort. Resume the matching page, read relevant nodes by ID/filter and
30
+ request detail/evidence only when needed. Use the IDs returned by mmap_read,
31
+ not display labels. Declare only missing structure. Keep context.summary and
32
+ context.next current so another conversation can continue without rebuilding.
33
+ Pass expectedRevision on writes; reread on CONFLICT. mmap_batch applies mixed
34
+ changes atomically to one page. mmap_read changes checks linked source hashes;
35
+ record sources[].sha256 only after verification. Older servers can fall back
36
+ to mmap_view discovery and the saved JSON for precise IDs.
37
+
38
+ The map is a **ledger, not a judge**: the tools only refuse structural
39
+ corruption; *when* to declare, start, or complete nodes is YOUR discipline,
40
+ spelled out here. Report honestly — an unflattering map is doing its job.
41
+
42
+ ## When to open a map
43
+
44
+ The USER chooses how eager mapping is — once, for themselves, not once per
45
+ project. Where the host runs a session adapter, the answer is already in this
46
+ session's context, stated before the conversation begins, so there is nothing to
47
+ probe: Claude Code runs the plugin's `SessionStart` hook, and omp (Oh My Pi) and
48
+ pi load the same paragraph through the plugin's host adapters
49
+ (`dist/omp-extension.mjs` and `dist/pi-extension.mjs`, declared in
50
+ `package.json#omp.extensions` and `package.json#pi.extensions`, read from the
51
+ same store). In hosts without one (Codex CLI, a bare MCP client) call
52
+ `mmap_setup` with no arguments before the first map decision of the session; the
53
+ reply names both scopes and which one governs.
54
+
55
+ - `always` — map every structured task: workflows, designs, architecture,
56
+ technical dependencies. Small effort, small map — but a map.
57
+ - `complex` — map only medium or complex tasks: several interacting modules,
58
+ a new subsystem, layered refactoring — roughly an hour of work or more.
59
+ Skip trivial edits; a map of one node is noise. When unsure, ask.
60
+ - `on-request` — open a map only when the user explicitly asks.
61
+
62
+ Under `always` and `complex`, the recorded policy is STANDING CONSENT: on a
63
+ task it covers, declare the ghost design and open the pane yourself, without
64
+ asking first. Re-asking for permission the user has already given is the
65
+ failure mode this policy exists to remove.
66
+
67
+ If the policy is not set anywhere, ask the USER to choose — present the three
68
+ options, never pick for them — then persist the answer with
69
+ `mmap_setup {policy, scope: "user"}`. That is asked ONCE EVER: the choice is
70
+ saved in the user's own `.mellos/config.json` and applies to every project.
71
+ A project that genuinely needs to differ takes
72
+ `mmap_setup {policy, scope: "project"}`, which overrides the user choice
73
+ there; `/mmap setup` reruns the question for either scope. Until they answer,
74
+ act as `complex`. Two things always outrank the policy: an explicit user
75
+ request for a map wins under any policy, and a map that is already open keeps
76
+ getting honest updates whatever the policy says.
77
+
78
+ Caught mid-implementation without a map on work that deserves one? Stop and
79
+ declare it with honest statuses (written-but-unverified is `in-progress`, not
80
+ `done`). If the mmap tools are missing from the session, explain that mapping
81
+ is unavailable and continue the user's implementation. In Codex, look for
82
+ tools by their `mmap_*` suffix: a host may add a server namespace. Installing
83
+ or repairing the plugin includes running
84
+ `node <plugin root>/scripts/codex-register.mjs`, then starting a new
85
+ conversation. Existing installation authorization covers this registration;
86
+ ordinary implementation work alone does not authorize changing host config.
87
+ The plugin root is two directories above this skill's directory. See
88
+ `<plugin root>/docs/codex.md` for Codex packaging and installation details.
89
+
90
+ ## Automatic desktop terminal in a browser
91
+
92
+ In ChatGPT desktop Codex mode, prefer `mmap_open {surface: "web-terminal", page}`
93
+ when no other surface was chosen. Pass its `hostOpen` object to `open_in_codex`
94
+ in the current conversation: right placement, browser target, returned URL.
95
+ The page starts the existing mmap terminal automatically, with independent font
96
+ size. No manual paste or Computer Use is needed. An older schema can use
97
+ `node "<plugin root>/dist/web.mjs" "<project>" --terminal --page <slug>`.
98
+ Respect manually pinned pages and report queued opening honestly. Native Claude
99
+ Code terminal placement remains the default for terminal hosts.
100
+
101
+ ## Desktop document panel
102
+
103
+ When the current host exposes a file side panel (for example Codex desktop's
104
+ `open_in_codex`) and the user requests a document or no browser panel is available, use the Markdown
105
+ surface. This replaces the terminal-placement instructions in step 2 below.
106
+
107
+ 1. Call `mmap_open {surface: "markdown", page: "<effort-slug>"}`. This generates
108
+ local Markdown with a colored SVG map and enables automatic preview updates
109
+ after successful map mutations in this project. It starts no terminal or web
110
+ server. Keep the JSON maps as the source; generated documents are not inputs.
111
+ 2. Pass the returned absolute `markdown:` path to the host's file-opening tool,
112
+ in the current conversation's right panel. With `open_in_codex`, use
113
+ `placement: "right"` and `target: {type: "file", path: "<returned path>"}`.
114
+ Do not set another conversation id unless the user asked for that placement.
115
+ 3. A generated file is not evidence that the user sees it. Report `queued`
116
+ opening as queued, and do not claim a live viewer or automatic UI refresh.
117
+ If the preview stays stale, regenerate with `mmap_open` and reopen the same
118
+ file. Avoid reopening after every write when the viewer already refreshes.
119
+ 4. `preview: STALE` means the map mutation succeeded but export failed. Fix
120
+ the export problem and regenerate; do not repeat the map mutation. A terminal
121
+ `pane:` report does not describe the desktop file panel.
122
+
123
+ If this conversation still has the previous MCP schema after a local upgrade,
124
+ use `node "<plugin root>/dist/preview.mjs" "<project directory>" --page <slug>`
125
+ to generate the same files. Until a new conversation loads the updated server,
126
+ rerun this command after map writes. Use the actual installed runtime path from
127
+ `docs/codex.md` when the skill cache differs from the runtime install.
128
+
129
+ The document contains a static vector image, module details, evidence and
130
+ links to existing child pages. Image nodes do not support dragging, hover
131
+ details, animated spinners or double-click navigation. Desktop rendering and
132
+ refresh behavior belong to the host, not the plugin.
133
+
134
+ ## Optional interactive web panel
135
+
136
+ Keep the user's chosen surface. Markdown/SVG remains available with its existing
137
+ workflow; adding the web viewer never disables or replaces it. When the user
138
+ asks for a web map or live interaction, call `mmap_open {surface: "web", page:
139
+ "<effort-slug>"}`. Pass the returned `web:` URL to the host's browser-opening
140
+ tool. In Codex use `open_in_codex` with `placement: "right"` and
141
+ `target: {type: "browser", url: "<returned URL>"}` in the current conversation.
142
+ Report queued opening honestly; a running local service does not prove visibility.
143
+
144
+ The viewer reads the same project maps and refreshes automatically, including
145
+ writes from older MCP clients. It supports zoom/pan, hover and pinned details,
146
+ dependency highlighting, search/status filters, page selection, child-map
147
+ navigation, groups, lanes, light/dark themes and confirmed page deletion.
148
+ Manual page selection disables auto-follow; do not override a pinned page.
149
+ Web page deletion also refreshes enabled Markdown previews.
150
+
151
+ If this conversation has an older schema, run
152
+ `node "<plugin root>/dist/web.mjs" "<project directory>" --page <slug>` and open
153
+ the JSON response's `url`. It reuses a local service for that project; no public
154
+ hosting or dependency download is required. Close an unused service with the
155
+ same command plus `--stop` instead of `--page <slug>`. It also exits after five
156
+ minutes without browser requests. Reopen with the tool/CLI after that, or after
157
+ a plugin update. A `web: configured` report describes a runtime record, not a
158
+ confirmed open desktop panel. See `docs/codex.md` for transport and lifecycle details.
159
+
160
+ ## The working loop
161
+
162
+ 1. **Resume first, then fill gaps.** Read existing pages and the matching effort's
163
+ records. For a new effort, declare its intended design with `mmap_declare`: `title`,
164
+ layer bands (rank 0 = most primitive, at the bottom), every planned node,
165
+ and the edges. Everything starts `planned` — the user can veto the ghost
166
+ design before any code exists.
167
+ 2. **Put the map on screen — that is YOUR job, not the user's.** Use the
168
+ desktop document flow above when applicable. For terminal hosts, at the first
169
+ map decision in a session, ensure the requested placement with `mmap_open`
170
+ even if a project viewer is already reported. Use no page for this initial
171
+ placement check so an existing pane pinned by the user keeps its view;
172
+ subsequent opens name the effort's page as usual. A project-wide viewer
173
+ report alone does not prove this session has its right split. Every
174
+ declare, update, remove and view answers with a `pane:` line telling you
175
+ who is actually looking. Read it and act on it:
176
+ - `pane: CLOSED` — nobody is. Call `mmap_open {page: "<slug>"}` at once,
177
+ without asking first: a recorded mapping policy IS the user's standing
178
+ consent to see the map. Confirm it is live only after the tool succeeds.
179
+ If automatic opening already failed, relay the reason and copyable command
180
+ once; retry only after the environment changes or the user asks.
181
+ - `pane: open on this page` — they are watching this land. Carry on.
182
+ - `pane: open on <other>, auto-follow on` — the pane follows the page
183
+ last written, so your next write brings the audience along by itself.
184
+ - `pane: open on <other>, auto-follow OFF` — the user pinned that page by
185
+ hand. Your changes are real and NOT on their screen: say so, and
186
+ retarget with `mmap_open {page}` only if they want it moved. A pane
187
+ with follow off is a deliberate choice — don't fight it.
188
+ Default open means a right split beside THIS conversation. A live viewer in
189
+ another window is not proof of that placement. If opening reports that the
190
+ source tab is inactive or Windows refused focus, relay it and ask the user
191
+ to activate this conversation's terminal tab before retrying. Do not retry
192
+ with `window: true` unless the user chose a separate window.
193
+ Always pass the page your effort lives on. Without it a fresh pane opens
194
+ on whichever page was written last, which after a gap is rarely the one
195
+ under discussion — and declaring on a named page makes YOU responsible
196
+ for the audience, rather than telling the user which tab to click.
197
+ `mmap_open {window: true}` puts the map in its own window instead of
198
+ splitting the conversation's, for users who want it separate. It never
199
+ CLOSES a pane: that is the user's (the `q` key, or `mmap` in any terminal
200
+ of the project, which toggles) — so a pane that vanishes is them, not a
201
+ fault.
202
+ On Linux/macOS, the same tool automatically opens tmux, using the inherited
203
+ session/pane or the single attached session when `TMUX` is missing. Multiple
204
+ attached sessions require `MELLOS_MAPPING_TMUX_TARGET`; custom sockets use
205
+ `MELLOS_MAPPING_TMUX_SOCKET` in the MCP server environment. `window: true`
206
+ requests a new tmux window. Do not guess between attached sessions.
207
+ Under omp (Oh My Pi) nothing about this changes: the same tool runs the same
208
+ launcher, splitting the Windows Terminal window hosting the omp session
209
+ (tmux split on Linux/macOS) and reusing the pane already bound to it. omp has
210
+ no pane of its own inside its TUI, so the split IS the map's home there —
211
+ never claim a map is visible when the tool reported it could not open one.
212
+ Where the tool cannot help — a client without it, or a machine without
213
+ Windows Terminal or an attached tmux session — relay the launcher's complete
214
+ quoted watcher command for a visible terminal, or use:
215
+ `node <plugin root>/dist/watch.mjs --file <project>/.mellos/map.json
216
+ --page <slug>` in a second terminal or tmux split (this skill file lives
217
+ under `<plugin root>/skills/mellos-mapping/`). `mmap_view` shows the map
218
+ inline anywhere.
219
+ 3. **Work bottom-up.** Set a node `in-progress` before implementing it, and
220
+ prefer finishing its lower dependencies first. Independent same-band
221
+ siblings need no artificial queue — building them together (several
222
+ spinners at once) is honest reporting, not a violation. If you
223
+ deliberately build above an unfinished dependency, say why in
224
+ conversation.
225
+ 4. **`done` requires evidence.** Mark `done` only when verification actually
226
+ passed, with what passed in `evidence` (e.g. `vitest: 23 passed`). No
227
+ evidence, no done.
228
+ 5. **Regression spreads upward.** If later work breaks a `done` node, set it
229
+ `regressed` (breakage in `evidence`) BEFORE fixing. Then walk every node
230
+ that uses it, directly or transitively: their `done` was proven against a
231
+ foundation that no longer holds. Default them to `regressed` too; keep one
232
+ green only with a stated reason (its own verification re-ran green, or it
233
+ never touches the broken behavior). After the fix, restore each node only
234
+ as its own verification passes again.
235
+ 6. **Revise the ghost honestly.** The design is a hypothesis, and everything
236
+ it declared can be revised. When a node splits, a primitive appears, or a
237
+ band was wrong, fix the map in the same turn you change the plan:
238
+ `mmap_update` moves a node to another band (`layer`), renames or re-ranks
239
+ a band, relabels a group or a lane, and clears any optional field with
240
+ `null` (never an empty string); it also edits title, kind and edge labels.
241
+ `mmap_declare` grows the map; `mmap_remove` drops edges, nodes, groups, lanes
242
+ and bands you have emptied. A map that no longer matches your intent is
243
+ the one failure mode this system cannot survive.
244
+ 7. **Clean up a finished effort.** Pages accumulate — one effort, one page —
245
+ so when an effort is over and its map has served its purpose, offer to
246
+ remove it: `mmap_remove {pages: ["slug"]}` deletes those page files for
247
+ good. Only with the user behind it, never on your own initiative, and
248
+ never a page someone might still be reading. (In the pane the user can do
249
+ it themselves: `x` twice, or the `×` on the active tab.)
250
+
251
+ ## Modeling guidance
252
+
253
+ - **Nodes are units of buildable, verifiable work** (a module, a contract, a
254
+ renderer) — not tasks like "write tests" and not files.
255
+ - **Declare groups when one band grows crowded** (`groups` in
256
+ `mmap_declare`, `group` on members): labeled subsystems within that band,
257
+ worth declaring once a single band holds roughly five or more nodes — the
258
+ far zoom renders groups, and a crowded band without them degrades into
259
+ anonymous glyphs. A group must be a strict subset of its band: a group
260
+ holding the whole band merely renames it. A map spread thin across many
261
+ bands needs no groups at all. Group status is derived from members —
262
+ never invent it.
263
+ - **Every node carries `detail`**: one to three sentences on responsibility,
264
+ contract, and the key decision. Update it when the design shifts — a stale
265
+ detail is a small lie on the map.
266
+ - **Push state to the top.** Lower layers take values in, give values out;
267
+ mutable state concentrates in the topmost orchestrator, which owns it
268
+ explicitly and hands it down as parameters. Stateless layers are cheap to
269
+ maintain: testable with values alone, rewritable without ceremony. A lower
270
+ node whose `detail` must describe state it keeps between calls is a design
271
+ smell — restructure before building on it.
272
+ - **Layers encode dependency direction, nothing else.** If A needs sibling B,
273
+ either B is really lower-layer or A and B are one node — restructure rather
274
+ than force an edge.
275
+ - **Edges mean "uses"** — the upper node genuinely calls, composes, or reads
276
+ the lower one. No aspirational edges.
277
+ - Ids are stable kebab-case slugs; labels are short display names (CJK fine),
278
+ renameable without breaking edges.
279
+ - **One effort = one page.** A genuinely separate effort (parallel session,
280
+ unrelated subsystem) gets its own page via the `page` parameter — don't mix
281
+ efforts or overwrite a finished map.
282
+ - **Sub-maps: dive, don't cram.** When a node's internals genuinely deserve
283
+ their own picture, declare a separate page and set `submap: <page-slug>` on
284
+ the node — the pane badges it ⊞; double-click dives in, Backspace climbs
285
+ back. Your judgment: most nodes need no sub-map; create one only when the
286
+ child map would carry a handful of nodes of its own.
287
+
288
+ ## Diagram kinds
289
+
290
+ The default kind is `dev` — the living progress ledger described above.
291
+ `mmap_declare` also accepts documentation kinds, rendered neutrally (no
292
+ ghosts, no spinners, no progress counts). Use them when the user asks for a
293
+ picture of a system rather than a picture of work; one diagram = one page.
294
+
295
+ - `architecture` — layered components; also module dependencies, call graphs.
296
+ - `dataflow` — pipeline stages as layers, sources at rank 0; label edges
297
+ with the data that flows.
298
+ - `behavior-tree` — leaves (actions) at rank 0, root on top; node `kind`
299
+ selector | sequence | parallel | decorator | condition | action renders
300
+ as a glyph. Also fits mind maps and WBS.
301
+ - `sequence` — the classic call/return diagram. rank = time step, rank 0 =
302
+ earliest; the pane draws sequence pages TOP-DOWN (earliest step on top,
303
+ under the participant headers). Declare participants as `lanes`; every
304
+ call AND every return is its own event node in the ACTING participant's
305
+ lane, so a round trip zigzags into the callee's lane and back out. Label
306
+ each edge with the message.
307
+ - State machines are out of scope: transitions cycle, and edges here only
308
+ point downward. Say so rather than forcing one in.
309
+
310
+ Edge labels and node kinds work on `dev` maps too.
311
+
312
+ ## Standing state
313
+
314
+ At any moment the pane should answer at a glance: what is designed, what is
315
+ built and verified, what is in progress RIGHT NOW, and whether any foundation
316
+ is cracked. If a glance would mislead on any of these, fix the map first.