mellos-mapping 0.20.0 → 0.20.2
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 +360 -63
- package/README.zh-CN.md +314 -54
- package/dist/hook-session-start.mjs +239 -0
- package/dist/mmap.mjs +338 -0
- package/dist/server.mjs +1614 -809
- package/dist/store-paths.mjs +107 -0
- package/dist/watch.mjs +1391 -760
- package/lib/domain/ops.d.ts +71 -12
- package/lib/domain/ops.js +145 -14
- package/lib/domain/types.d.ts +47 -6
- package/lib/domain/types.js +34 -3
- package/lib/render/canvas.d.ts +50 -0
- package/lib/render/canvas.js +210 -0
- package/lib/render/draw.d.ts +37 -0
- package/lib/render/draw.js +111 -0
- package/lib/render/layout.d.ts +89 -0
- package/lib/render/layout.js +200 -0
- package/lib/render/options.d.ts +39 -0
- package/lib/render/options.js +10 -0
- package/lib/render/render.d.ts +32 -46
- package/lib/render/render.js +58 -789
- package/lib/render/routing.d.ts +56 -0
- package/lib/render/routing.js +244 -0
- package/lib/render/skins.d.ts +54 -0
- package/lib/render/skins.js +99 -0
- package/lib/render/width.d.ts +24 -0
- package/lib/render/width.js +139 -0
- package/lib/render/zoom-geometry.d.ts +52 -0
- package/lib/render/zoom-geometry.js +56 -0
- package/lib/semantics/semantics.d.ts +53 -4
- package/lib/semantics/semantics.js +130 -6
- package/lib/semantics/vocabulary.d.ts +79 -0
- package/lib/semantics/vocabulary.js +112 -0
- package/lib/store/format.d.ts +17 -0
- package/lib/store/format.js +185 -66
- package/lib/store/store.d.ts +220 -20
- package/lib/store/store.js +491 -38
- package/package.json +12 -4
- package/scripts/codex-register.mjs +89 -20
- package/scripts/install-mmap-command.mjs +293 -0
- package/scripts/mmap.mjs +213 -0
- package/scripts/open-pane.mjs +115 -254
- package/scripts/pane-core.mjs +418 -0
package/README.md
CHANGED
|
@@ -93,7 +93,43 @@ claude plugin marketplace add GuangminJu/mellos-mapping && claude plugin install
|
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
Requires Node.js 18+ on PATH (Claude Code itself requires Node, so you
|
|
96
|
-
already have it). No build step:
|
|
96
|
+
already have it). No build step: `dist/` is committed, so a clone runs as-is —
|
|
97
|
+
`dist/server.mjs` (the MCP server), `dist/watch.mjs` (the pane),
|
|
98
|
+
`dist/mmap.mjs` (the `mmap` toggle), `dist/hook-session-start.mjs` (the
|
|
99
|
+
`SessionStart` hook that `hooks/hooks.json` registers) and
|
|
100
|
+
`dist/store-paths.mjs` (the store's path vocabulary, which the plain-node pane
|
|
101
|
+
launcher imports instead of restating filenames).
|
|
102
|
+
|
|
103
|
+
The first session after installing asks you **one** question — how eager
|
|
104
|
+
mapping should be — and records the answer for every project you will ever
|
|
105
|
+
open. From then on the hook carries it into each new session by itself; there
|
|
106
|
+
is no per-project setup step. See
|
|
107
|
+
[Setup: choose when maps open](#setup-choose-when-maps-open).
|
|
108
|
+
|
|
109
|
+
The `mmap` terminal command (the pane's open/close toggle) installs itself on
|
|
110
|
+
Windows: the same hook notices on session start when the shim is missing or
|
|
111
|
+
points at an older install, writes `mmap.cmd` (cmd, PowerShell) and `mmap`
|
|
112
|
+
(git-bash) into `%LOCALAPPDATA%\mellos-mapping\bin`, appends that one
|
|
113
|
+
directory to your **user** PATH, and tells you so through the assistant. The
|
|
114
|
+
PATH change reaches only new processes — and a new tab of a running Windows
|
|
115
|
+
Terminal inherits the old environment, so close the terminal app entirely and
|
|
116
|
+
reopen it before the first `mmap`. The PATH edit keeps the
|
|
117
|
+
installer's guarantees: nothing happens when the entry is already there, and
|
|
118
|
+
a PATH that `setx` would damage (flattened `%VARIABLE%` references, truncation
|
|
119
|
+
past its limit) is refused outright, with the entry to add by hand named
|
|
120
|
+
instead.
|
|
121
|
+
|
|
122
|
+
The step behind it is still a command of its own, for the cases the hook does
|
|
123
|
+
not cover — `--uninstall`, or re-adding a PATH entry you removed while the
|
|
124
|
+
shims stayed put:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
node "<plugin dir>/scripts/install-mmap-command.mjs" [--uninstall]
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
(`--json` prints the install outcome as one JSON line instead of prose — the
|
|
131
|
+
mode the hook itself calls it in.) `npm i -g mellos-mapping` provides the
|
|
132
|
+
same `mmap` via `bin`, no shims involved.
|
|
97
133
|
|
|
98
134
|
## Update
|
|
99
135
|
|
|
@@ -106,6 +142,30 @@ marketplace clone — the first command is what actually pulls this repo.
|
|
|
106
142
|
Restart Claude Code to apply. Releases are version bumps on `master`.
|
|
107
143
|
(In-app, `/plugin` opens the same management UI.)
|
|
108
144
|
|
|
145
|
+
### Upgrading from 0.19
|
|
146
|
+
|
|
147
|
+
0.20 moved the store out of `.claude/` — the map belongs to this tool, not to
|
|
148
|
+
one client — into `.mellos/`. The server and the watcher each perform the move
|
|
149
|
+
once at startup, and print exactly one line on stderr when they do
|
|
150
|
+
(`mellos-mapping: moved the legacy .claude map store to .mellos/ — commit the
|
|
151
|
+
move.`):
|
|
152
|
+
|
|
153
|
+
| 0.19 and earlier | 0.20 and later |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| `.claude/mellos-mapping.json` | `.mellos/map.json` |
|
|
156
|
+
| `.claude/mellos-mapping.pages/` | `.mellos/pages/` |
|
|
157
|
+
| `.claude/mellos-mapping.config.json` | `.mellos/config.json` |
|
|
158
|
+
|
|
159
|
+
Nothing is merged and nothing is overwritten: a project that already has a
|
|
160
|
+
`.mellos/` store (map, pages or config) is left untouched, whatever the legacy
|
|
161
|
+
directory still holds. If you keep your maps in git, commit the move —
|
|
162
|
+
`git add -A .claude .mellos` records it as renames rather than as a pile of
|
|
163
|
+
deletions plus untracked files.
|
|
164
|
+
|
|
165
|
+
That one move is the only time either process touches `.claude/`. Afterwards
|
|
166
|
+
the tools write nowhere but `.mellos/`, and never outside the project
|
|
167
|
+
directory they resolved at startup.
|
|
168
|
+
|
|
109
169
|
## Codex CLI
|
|
110
170
|
|
|
111
171
|
The same repo doubles as a Codex plugin (codex-cli 0.147+). Three lines:
|
|
@@ -134,8 +194,9 @@ rerunning with `--page` retargets it instead of opening another. The pane
|
|
|
134
194
|
auto-follows the page being written — the map the agent is operating on
|
|
135
195
|
right now; press `f` to toggle that (a manual page switch also turns it
|
|
136
196
|
off), or start with `--no-follow`. Elsewhere run
|
|
137
|
-
`node <plugin root>/dist/watch.mjs`
|
|
138
|
-
|
|
197
|
+
`node <plugin root>/dist/watch.mjs` from the project directory in a second
|
|
198
|
+
terminal (or any terminal split). Both take the same flags — see
|
|
199
|
+
[Pane flags](#pane-flags).
|
|
139
200
|
|
|
140
201
|
## Any MCP client
|
|
141
202
|
|
|
@@ -153,19 +214,30 @@ The map file lands in the client session's working directory
|
|
|
153
214
|
npx -y -p mellos-mapping mellos-mapping-watch
|
|
154
215
|
```
|
|
155
216
|
|
|
217
|
+
The server picks its project directory in this order: `MELLOS_MAPPING_CWD`
|
|
218
|
+
(an explicit override, for clients that spawn servers from a fixed
|
|
219
|
+
directory), then `CLAUDE_PROJECT_DIR` (what Claude Code sets for plugin MCP
|
|
220
|
+
servers), then the server process's own working directory. Set
|
|
221
|
+
`MELLOS_MAPPING_CWD` when your client would otherwise start the server
|
|
222
|
+
somewhere other than the project you are working in.
|
|
223
|
+
|
|
156
224
|
The skill/discipline layer is Claude Code + Codex specific; other clients
|
|
157
|
-
get the
|
|
225
|
+
get the five `mmap_*` tools and the pane, and bring their own prompting.
|
|
158
226
|
|
|
159
227
|
## Use
|
|
160
228
|
|
|
161
229
|
1. Ask Claude to build something non-trivial. The bundled skill has Claude
|
|
162
230
|
declare the ghost design and keep the map current as it works.
|
|
163
|
-
2.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
231
|
+
2. The pane opens itself. Every write tells Claude whether anybody is
|
|
232
|
+
actually looking (see [Who is watching](#who-is-watching)), and Claude
|
|
233
|
+
opens or retargets the pane with `mmap_open` when nobody is — you never
|
|
234
|
+
have to remember to. Open or close it yourself with `mmap` in any
|
|
235
|
+
terminal, or `/mellos-mapping:mmap` in the conversation (Windows
|
|
236
|
+
Terminal split on Windows, tmux split inside tmux, or a printed command
|
|
237
|
+
to run in any second terminal). On Windows the pane opens in the terminal
|
|
238
|
+
window hosting YOUR session, even with several windows open; pass
|
|
239
|
+
`--window` to put the map in its own dedicated window instead. Prefer
|
|
240
|
+
`--ascii` if your font lacks box-drawing glyphs.
|
|
169
241
|
3. Watch nodes light up from the bottom. Interrupt when the picture worries
|
|
170
242
|
you — that is what it is for.
|
|
171
243
|
|
|
@@ -180,22 +252,33 @@ htop and tmux speak):
|
|
|
180
252
|
| wheel / `+` `-` | zoom, anchored on the focused node (see the ladder below) |
|
|
181
253
|
| left-drag | grab and pan when the map outgrows the pane |
|
|
182
254
|
| shift+wheel | scroll vertically |
|
|
255
|
+
| wheel tilt (horizontal) | pan sideways |
|
|
183
256
|
| `hjkl` / arrows | nudge the view |
|
|
184
257
|
| `Tab` / `Shift+Tab` / `1-9` / click a tab | switch pages (parallel maps) |
|
|
258
|
+
| wheel on the tab row / click `‹` `›` | browse an overflowing tab strip without switching pages |
|
|
259
|
+
| `f` | toggle auto-follow (see [Pages](#pages)) |
|
|
260
|
+
| `x`, or click the `×` on the active tab | ask to delete the page on screen; press again inside the window and its file is removed (see [Pages](#pages)) |
|
|
185
261
|
| double-click a `⊞` node | dive into its sub-map (a child page) |
|
|
186
262
|
| `Backspace` / `Esc` | climb back out of the last dive |
|
|
187
263
|
| drag the `⋯` divider | resize the detail panel — pull it up to read long design notes in full |
|
|
188
264
|
| `0` | reset pan and zoom |
|
|
189
|
-
| `q` | quit the pane |
|
|
265
|
+
| `q` / `Ctrl+C` | quit the pane |
|
|
266
|
+
|
|
267
|
+
Every other key is inert, on purpose: an escape sequence the pane does not
|
|
268
|
+
know (F-keys, Home/End, PgUp/PgDn, Insert/Delete, modified arrows) is
|
|
269
|
+
consumed whole and does nothing, rather than having its payload bytes read
|
|
270
|
+
as hotkeys.
|
|
190
271
|
|
|
191
272
|
Zooming scales the picture first and switches display mode only at the ends
|
|
192
273
|
of the ladder, so every level still shows meaningful data:
|
|
193
274
|
|
|
194
275
|
```
|
|
195
|
-
detail ← 100% ← 85% ← 70% ← 55% ← overview
|
|
276
|
+
detail+ ← detail ← 100% ← 85% ← 70% ← 55% ← overview
|
|
196
277
|
```
|
|
197
278
|
|
|
198
|
-
- **zoom in past 100%** — evidence and
|
|
279
|
+
- **zoom in past 100%** — `detail` unfolds evidence and the first three lines
|
|
280
|
+
of the design notes inside the boxes; `detail+` widens them into reading
|
|
281
|
+
cards (up to twelve note rows);
|
|
199
282
|
- **85–55%** — whitespace tightens and labels truncate proportionally, boxes
|
|
200
283
|
stay boxes;
|
|
201
284
|
- **below 55%** — labels would stop meaning anything, so the map AGGREGATES:
|
|
@@ -206,27 +289,133 @@ detail ← 100% ← 85% ← 70% ← 55% ← overview
|
|
|
206
289
|
no groups falls back to a pure glyph constellation with per-band counts.)
|
|
207
290
|
The footer always names the level.
|
|
208
291
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
(`uses → … · used by ←
|
|
292
|
+
Below the map, between it and the hint line, sits a fixed-height detail
|
|
293
|
+
panel: a separator you can drag, a status-colored header, the focused node's
|
|
294
|
+
evidence, both wire directions (`uses → … · used by ← …`, each neighbour
|
|
295
|
+
carrying its own status glyph) and its design notes, word-wrapped. With
|
|
296
|
+
nothing focused it shows the map's dashboard instead. Fixed height — details
|
|
297
|
+
never float over the map and the layout never jumps.
|
|
298
|
+
|
|
299
|
+
### Glyphs
|
|
300
|
+
|
|
301
|
+
One status, one glyph, everywhere a map is drawn — the pane's boxes, its tab
|
|
302
|
+
strip and detail panel, and any other client reading the same store:
|
|
303
|
+
|
|
304
|
+
| Unicode | ASCII | Meaning |
|
|
305
|
+
| --- | --- | --- |
|
|
306
|
+
| `·` | `.` | planned — declared, not started |
|
|
307
|
+
| `⠿` | `*` | in-progress, at rest — a box that can animate spins through the braille frames (`⠋⠙⠹…`) instead, or a four-bar cycle in ASCII |
|
|
308
|
+
| `■` | `#` | done, with evidence |
|
|
309
|
+
| `□` | `o` | done, with **no** evidence recorded — same claim, nothing behind it |
|
|
310
|
+
| `✗` | `X` | regressed: was done, now broken |
|
|
311
|
+
| `⊞` | `+` | badge: the node links a sub-map; double-click dives in |
|
|
312
|
+
|
|
313
|
+
The legend under the picture names the four statuses; `□ done, no evidence`
|
|
314
|
+
joins it only on a map that actually contains one — the four are the
|
|
315
|
+
vocabulary, that one is a rule being broken here and now. Documentation
|
|
316
|
+
diagram kinds replace the status legend with their node-kind glyphs.
|
|
212
317
|
|
|
213
|
-
|
|
214
|
-
|
|
318
|
+
### Pane flags
|
|
319
|
+
|
|
320
|
+
Both the pane launcher (`scripts/open-pane.mjs <project dir>`) and the
|
|
321
|
+
watcher (`dist/watch.mjs`) take the same watcher flags; the launcher forwards
|
|
322
|
+
them verbatim and rejects anything it does not know rather than dropping it.
|
|
323
|
+
|
|
324
|
+
| Flag | Effect |
|
|
325
|
+
| --- | --- |
|
|
326
|
+
| `--page <slug>` | open on this page; with a pane already running, retarget that pane instead of opening another |
|
|
327
|
+
| `--ascii` | pure-ASCII repertoire, for fonts without box-drawing glyphs |
|
|
328
|
+
| `--no-color` | no ANSI color |
|
|
329
|
+
| `--no-mouse` | no mouse reporting, if your terminal multiplexer wants the mouse for itself |
|
|
330
|
+
| `--no-follow` | start with auto-follow off |
|
|
331
|
+
| `--interval <ms>` | poll interval; default 250, floored at 50 |
|
|
332
|
+
|
|
333
|
+
Launcher-only: `--window` opens the dedicated "mellos-mapping" window instead
|
|
334
|
+
of splitting the session's window, and `--force` opens another pane even
|
|
335
|
+
though one is already running for this project. Watcher-only: `--file <path>`
|
|
336
|
+
names the default page's state file (the launcher derives it from the project
|
|
337
|
+
directory).
|
|
338
|
+
|
|
339
|
+
### The mmap command
|
|
340
|
+
|
|
341
|
+
`mmap`, typed in any terminal, is a **toggle**: it opens the map pane for the
|
|
342
|
+
project you are standing in, or closes the one that is already open.
|
|
343
|
+
|
|
344
|
+
| You type | What it does |
|
|
345
|
+
| --- | --- |
|
|
346
|
+
| `mmap` | nothing watching this project → open the pane; something watching → close it |
|
|
347
|
+
| `mmap <page-slug>` | open on that page, or retarget an already-open pane to it — never closes |
|
|
348
|
+
| `mmap --window` | open in the dedicated "mellos-mapping" window instead of splitting this one |
|
|
349
|
+
| `mmap --force` | open another pane even though one is already running |
|
|
350
|
+
|
|
351
|
+
The project is found the way git finds its root: from the current directory
|
|
352
|
+
upwards, to the nearest one holding a `.mellos/` store. Standing in a project
|
|
353
|
+
that has no map yet is fine — the pane opens on its standby screen and says so
|
|
354
|
+
until the first `mmap_declare`.
|
|
355
|
+
|
|
356
|
+
Closing goes through the store rather than through a signal: `mmap` writes a
|
|
357
|
+
one-shot request beside the map, the pane consumes it on its next poll (250 ms
|
|
358
|
+
by default) and exits, handing the terminal back exactly as it found it — mouse
|
|
359
|
+
reporting off, cursor visible. A pane still on the standby screen closes the
|
|
360
|
+
same way. The request is deleted as it is read, and a leftover from a pane that
|
|
361
|
+
died is swept when the next one starts, so a stale request can never close a
|
|
362
|
+
fresh pane.
|
|
363
|
+
|
|
364
|
+
Every watcher flag above works here too, forwarded verbatim; an unknown one is
|
|
365
|
+
a usage error, never dropped in silence. `mmap` needs
|
|
366
|
+
[installing once](#install) unless you have the npm package. Inside a Claude
|
|
367
|
+
Code conversation, `/mellos-mapping:mmap` opens the same pane.
|
|
215
368
|
|
|
216
369
|
### Pages
|
|
217
370
|
|
|
218
371
|
A project can keep several maps side by side — **one effort = one page**.
|
|
219
372
|
Claude targets a page by passing `page` to any `mmap_*` tool; the pane grows
|
|
220
373
|
a tab bar as soon as a second page exists. The active tab is bold in its
|
|
221
|
-
map's aggregate status color
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
374
|
+
map's aggregate status color. Each page remembers its own pan, zoom and
|
|
375
|
+
pinned node.
|
|
376
|
+
|
|
377
|
+
By default the pane **follows the page being written** — the map the agent is
|
|
378
|
+
operating on right now — so declares and updates bring the audience along by
|
|
379
|
+
themselves. `f` toggles that, a manual page switch turns it off, and
|
|
380
|
+
`--no-follow` starts it off; with follow off, a background page's change
|
|
381
|
+
lights its tab in status color instead of stealing your view. An explicit
|
|
382
|
+
`--page` outranks follow, and a page requested before it exists is shown the
|
|
383
|
+
moment it appears.
|
|
384
|
+
|
|
385
|
+
**Deleting a page.** An effort ends; its page does not have to stay. In the
|
|
386
|
+
pane, `x` — or the `×` the active tab carries when the mouse is on — *asks*:
|
|
387
|
+
the footer says `press x again to delete <page>`, and a second press within
|
|
388
|
+
three seconds removes that page's file. Switching page, `Esc` or simply
|
|
389
|
+
waiting takes the request back. The `×` sits on the active tab only, so
|
|
390
|
+
clicking an inactive one switches to it first and offers its `×` on the next
|
|
391
|
+
frame. From a tool call it is `mmap_remove {pages: ["slug", …]}`, applied
|
|
392
|
+
after that call's map edits. Either way the file is gone for good — the maps
|
|
393
|
+
are plain JSON, so committing them is the only undo there is.
|
|
394
|
+
|
|
395
|
+
State lives in the tool-owned `.mellos/` directory at the project root:
|
|
396
|
+
|
|
397
|
+
| Path | What it is |
|
|
398
|
+
| --- | --- |
|
|
399
|
+
| `.mellos/map.json` | the default page — optional; a project whose work lives on named pages has none |
|
|
400
|
+
| `.mellos/pages/<slug>.json` | one file per named page |
|
|
401
|
+
| `.mellos/config.json` | the project's mapping policy (see [Setup](#setup-choose-when-maps-open)) |
|
|
402
|
+
| `.mellos/focus` | one-shot "show this page" request from a launcher to a running pane; the pane consumes it and deletes it within a poll tick |
|
|
403
|
+
| `.mellos/quit` | one-shot "close yourself" request from the `mmap` toggle, consumed and deleted the same way |
|
|
404
|
+
| `.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)) |
|
|
405
|
+
| `<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 |
|
|
406
|
+
|
|
407
|
+
The map files are plain JSON, safe to commit if you want the maps' history in
|
|
408
|
+
git. The other three are runtime chatter between a pane and whoever is talking
|
|
409
|
+
to it — gitignore `focus`, `quit` and `viewers/` if you commit the store.
|
|
410
|
+
|
|
411
|
+
**Concurrency, stated plainly.** Every save is atomic — written to a private
|
|
412
|
+
sibling temp file and renamed over the target — so a reader polling the store
|
|
413
|
+
sees the previous complete map or the new one, never a torn write. There is
|
|
414
|
+
no lost-update protection: two writers saving the *same* page race, and the
|
|
415
|
+
last rename wins, silently discarding what the other computed from an older
|
|
416
|
+
read. Pages are the isolation unit — two sessions that must not clobber each
|
|
417
|
+
other belong on two pages, which is also the answer to running several Claude
|
|
418
|
+
sessions in one project.
|
|
230
419
|
|
|
231
420
|
### Diagram kinds
|
|
232
421
|
|
|
@@ -252,8 +441,11 @@ A node can link a child page with `submap: <page-slug>` — the pane badges it
|
|
|
252
441
|
map of maps, built entirely from pages: no new storage, no new invariants.
|
|
253
442
|
Whether a node deserves a sub-map is the AI's judgment call — most don't.
|
|
254
443
|
|
|
255
|
-
Sub-maps are interior detail, not siblings: a page
|
|
256
|
-
never occupies a tab.
|
|
444
|
+
Sub-maps are interior detail, not siblings: a page some *other* page dives
|
|
445
|
+
into never occupies a tab. Two refinements keep the tab strip from erasing
|
|
446
|
+
itself — a page whose own node names itself hides nobody, and a link cycle
|
|
447
|
+
keeps its tabs unless a page outside the cycle dives in, because a cycle has
|
|
448
|
+
no outside to climb back to. Inside a dive the tab row becomes a breadcrumb —
|
|
257
449
|
`⌫ parent map ▸ node` — and clicking it (or `Backspace`) climbs back out.
|
|
258
450
|
When a hidden sub-map changes in the background, the footer says so.
|
|
259
451
|
|
|
@@ -261,64 +453,169 @@ When a hidden sub-map changes in the background, the footer says so.
|
|
|
261
453
|
|
|
262
454
|
| Tool | Purpose |
|
|
263
455
|
| --- | --- |
|
|
264
|
-
| `mmap_declare` | Grow the map: title, diagram kind, layer bands, lanes, groups (subsystems), nodes
|
|
265
|
-
| `mmap_update` | Record progress
|
|
266
|
-
| `mmap_remove` | Revise: drop edges, nodes, groups, lanes, empty bands |
|
|
267
|
-
| `mmap_view` | Render the current map as text inline (optional `zoom`) |
|
|
456
|
+
| `mmap_declare` | Grow the map: title (`null` removes it), diagram kind, layer bands, lanes, groups (subsystems), nodes — with `status`, `evidence`, `detail`, `kind`, `group`, `lane`, `submap` — and edges, optionally labeled (all-or-nothing batch) |
|
|
457
|
+
| `mmap_update` | Record progress **and revise**: status (`planned → in-progress → done` +evidence, `regressed`), relabel a node, move it to another band (`layer`), join/leave a group or lane, set a node kind or a `submap`; rename and re-rank bands (`layers`), relabel groups (`groups`) and lanes (`lanes`); `null` clears any clearable field |
|
|
458
|
+
| `mmap_remove` | Revise: drop edges, nodes, groups, lanes, empty bands — and, with `pages`, whole pages, file and all (permanent; applied after this call's map edits) |
|
|
459
|
+
| `mmap_view` | Render the current map as text inline (optional `zoom`, `-4`…`2`), ending with a `pages:` line naming every page the project has and which one you are looking at |
|
|
268
460
|
| `mmap_setup` | Get/set the project's mapping policy — when maps open |
|
|
461
|
+
| `mmap_open` | Put the map on your screen: open the pane, or retarget an open one to a `page` (`window: true` for the dedicated window). It answers with whether a pane actually reported in afterwards, not merely that a command ran — and it can never close one |
|
|
462
|
+
|
|
463
|
+
A batch applies bands → groups → lanes → node updates, and within one node
|
|
464
|
+
update `layer` moves the node before its other fields, so a node can move and
|
|
465
|
+
join a group on its new band in one item.
|
|
466
|
+
|
|
467
|
+
What the boundary refuses, so the ledger never records something it did not
|
|
468
|
+
mean:
|
|
469
|
+
|
|
470
|
+
- **an unknown key**, naming it — a misspelled `evidance` is an error, not a
|
|
471
|
+
silently dropped field, at every nesting depth;
|
|
472
|
+
- **control characters** in text fields — an ESC sequence stored in a label
|
|
473
|
+
would let a map repaint the terminal of everyone who opens it. `detail` is
|
|
474
|
+
the exception: newlines and tabs are how a note is written, everything else
|
|
475
|
+
(ESC, BEL, lone CR) is still refused;
|
|
476
|
+
- **an empty string** where a field is optional — `null` is how a field is
|
|
477
|
+
cleared, never a blank that renders as a box nobody can tell from a real
|
|
478
|
+
one;
|
|
479
|
+
- **a node whose `submap` names the page the call itself targets** — a link
|
|
480
|
+
with no bottom, not a parent link;
|
|
481
|
+
- **a page deletion aimed at the page the same call targets, or at a slug the
|
|
482
|
+
project does not have** — one call must not edit a map it is deleting, and
|
|
483
|
+
a name matching no page is a typo far more often than a race; the refusal
|
|
484
|
+
lists the pages that do exist.
|
|
485
|
+
|
|
486
|
+
A write that does not land answers `save failed, nothing changed (retry)`:
|
|
487
|
+
the previous file is intact and calling again is the whole recovery.
|
|
488
|
+
|
|
489
|
+
### Who is watching
|
|
490
|
+
|
|
491
|
+
A map nobody has on screen is a file, not a map — and nothing in the system
|
|
492
|
+
used to be able to tell the difference. An assistant would declare a design,
|
|
493
|
+
light nodes up as it built them, and report all of it into a store you had
|
|
494
|
+
never opened a pane for.
|
|
495
|
+
|
|
496
|
+
Every pane now publishes a small report while it runs — `.mellos/viewers/`,
|
|
497
|
+
one file per pane, refreshed once a second — and every write and every view
|
|
498
|
+
ends with what those reports say:
|
|
499
|
+
|
|
500
|
+
| The line | What it means |
|
|
501
|
+
| --- | --- |
|
|
502
|
+
| `pane: CLOSED` | nobody is seeing this map; the assistant opens one with `mmap_open` instead of asking you to |
|
|
503
|
+
| `pane: open on this page` | you are watching this land |
|
|
504
|
+
| `pane: open on <other>, auto-follow on` | the pane follows the page last written, so it arrives here by itself |
|
|
505
|
+
| `pane: open on <other>, auto-follow OFF` | you pinned that page by hand: the change is real and NOT on your screen. The assistant is told to say so rather than move your view |
|
|
506
|
+
|
|
507
|
+
The same reports answer "is a pane already open?" for `mmap` and for the
|
|
508
|
+
launcher — a question that used to cost a Windows-only process scan and could
|
|
509
|
+
not say which page was on screen. A report whose pane stopped refreshing it is
|
|
510
|
+
ignored after five seconds and deleted after a minute, so a killed pane cannot
|
|
511
|
+
go on claiming an audience.
|
|
269
512
|
|
|
270
513
|
### Setup: choose when maps open
|
|
271
514
|
|
|
272
|
-
|
|
273
|
-
|
|
515
|
+
How eager mapping should be is a habit, not a property of a repository — so it
|
|
516
|
+
is chosen **once, for you**, in the first session after you install:
|
|
274
517
|
|
|
275
518
|
- `always` — map every structured task: workflows, designs, architecture,
|
|
276
|
-
technical dependencies.
|
|
277
|
-
|
|
278
|
-
- `
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
519
|
+
technical dependencies. The assistant opens the pane on its own initiative;
|
|
520
|
+
your recorded answer is its standing consent, so it stops asking.
|
|
521
|
+
- `complex` — the same, but only for medium or complex tasks: several modules,
|
|
522
|
+
a new subsystem, roughly an hour of work or more.
|
|
523
|
+
- `on-request` — map only when you explicitly ask. In a project with no map,
|
|
524
|
+
the plugin then says nothing at all — zero noise is the point.
|
|
525
|
+
|
|
526
|
+
The answer lands in `<your home>/.mellos/config.json` and reaches every session
|
|
527
|
+
through the plugin's `SessionStart` hook, which reads it and hands the
|
|
528
|
+
assistant the matching instruction before you have typed anything. Nothing has
|
|
529
|
+
to be set up per project, ever again.
|
|
530
|
+
|
|
531
|
+
One project can still differ: `mmap_setup {policy, scope: "project"}` records
|
|
532
|
+
a policy in that project's `.mellos/config.json`, and a project policy
|
|
533
|
+
overrides the user one. `/mmap setup` re-runs the question for either scope
|
|
534
|
+
whenever you want to change your mind. The policy guides the assistant; it
|
|
535
|
+
never blocks the tools, and asking for a map explicitly always works under any
|
|
536
|
+
policy.
|
|
537
|
+
|
|
538
|
+
Hosts without hooks (Codex CLI, a bare MCP client) get the question another
|
|
539
|
+
way: while no policy exists in either scope, every `mmap_declare` reply carries
|
|
540
|
+
a note telling the assistant to ask you. That note goes quiet for good — in
|
|
541
|
+
every project — the moment you have answered anywhere.
|
|
283
542
|
|
|
284
543
|
Structural invariants enforced by the tools: layers form a total order by
|
|
285
|
-
rank
|
|
286
|
-
downward** — which makes the graph
|
|
287
|
-
depend on same-layer siblings (if A
|
|
288
|
-
lower concept or A and B are one node)
|
|
544
|
+
rank (an integer in 0..99, 0 = bottom, unique per map); every node lives in
|
|
545
|
+
exactly one layer; edges point **strictly downward** — which makes the graph
|
|
546
|
+
acyclic by construction; nodes may not depend on same-layer siblings (if A
|
|
547
|
+
needs sibling B, either B is really a lower concept or A and B are one node);
|
|
548
|
+
a group clusters nodes within one band; and node ids and group ids share
|
|
549
|
+
**one namespace** — an id names a node or a group, never both, because both
|
|
550
|
+
render as boxes and one id must mean one box.
|
|
289
551
|
|
|
290
552
|
## Development
|
|
291
553
|
|
|
292
554
|
```
|
|
293
555
|
npm install
|
|
294
|
-
npm run verify
|
|
556
|
+
npm run verify
|
|
295
557
|
```
|
|
296
558
|
|
|
559
|
+
`verify` is five steps, in this order: `typecheck` (the repo's own sources),
|
|
560
|
+
`typecheck:packages` (the dsh plugin packages' framework-free modules, with
|
|
561
|
+
`mellos-mapping/*` pointed at these sources), `test`, `build` (bundles
|
|
562
|
+
`dist/`, emits `lib/` with declarations, cleaning both first), and
|
|
563
|
+
`check:package` — which packs the tarball through the real `prepack`
|
|
564
|
+
lifecycle and fails if any `exports` or `bin` target is missing from it.
|
|
565
|
+
|
|
297
566
|
The repo is itself layered bottom-up, and each layer has its spec:
|
|
298
567
|
|
|
299
|
-
| Layer | Code | Owns |
|
|
300
|
-
| --- | --- | --- |
|
|
301
|
-
| 0 domain | `src/domain/` | the map value, structural invariants, pure ops |
|
|
302
|
-
| 1 format | `src/store/format.ts` | the state-file format: replay-validated parse, serialize — I/O-free |
|
|
303
|
-
| 1 store | `src/store/store.ts` | atomic state-file persistence on Node |
|
|
304
|
-
| 1 semantics | `src/semantics/` | medium-neutral view semantics: zoom ladder, group aggregation, sequence flip |
|
|
305
|
-
| 2 apply | `src/server/apply.ts` | tool inputs → transactional op sequences |
|
|
306
|
-
| 3 server | `src/server/server.ts` | the
|
|
307
|
-
| 4 render | `src/render
|
|
568
|
+
| Layer | Code | Spec | Owns |
|
|
569
|
+
| --- | --- | --- | --- |
|
|
570
|
+
| 0 domain | `src/domain/` | `ops.test.ts` | the map value, structural invariants, pure ops |
|
|
571
|
+
| 1 format | `src/store/format.ts` | `store.test.ts` | the state-file format: replay-validated parse, serialize — I/O-free |
|
|
572
|
+
| 1 store | `src/store/store.ts` | `store.test.ts`, `atomic-save.test.ts` | atomic state-file persistence on Node |
|
|
573
|
+
| 1 semantics | `src/semantics/` | `semantics.test.ts` | medium-neutral view semantics: zoom ladder, group aggregation, page-set rules, sequence flip, the shared glyph vocabulary |
|
|
574
|
+
| 2 apply | `src/server/apply.ts` | `apply.test.ts` | tool inputs → transactional op sequences |
|
|
575
|
+
| 3 server | `src/server/server.ts` | `server.test.ts`, `save-failure.test.ts` | the five MCP tools over stdio |
|
|
576
|
+
| 4 render | `src/render/` | `render.test.ts`, `routing.test.ts` | the ASCII renderer and its wire routing |
|
|
577
|
+
| 4 pane | `src/watch/` | `watch.test.ts`, `pane-state.test.ts`, `input.test.ts` | the polling pane: page set, input parsing, panel and chrome |
|
|
578
|
+
| — launchers | `scripts/` | `open-pane.test.mjs`, `codex-register.test.mjs` | plain-node entry points |
|
|
579
|
+
| — packaging | `package.json`, `packages/` | `tests/lockfile.test.ts`, `tests/packages.test.ts`, `browser-safe.test.ts` | what ships, and to whom |
|
|
308
580
|
|
|
309
581
|
`dist/` is committed deliberately: plugin installation clones this repo and
|
|
310
|
-
runs nothing, so entry points ship bundled.
|
|
582
|
+
runs nothing, so entry points ship bundled. CI diffs the committed `dist/`
|
|
583
|
+
against a fresh build, so a source change that forgets the rebuild fails.
|
|
584
|
+
|
|
585
|
+
### The dsh packages
|
|
586
|
+
|
|
587
|
+
`packages/dsh` and `packages/dsh-client` are the DeepSeek Harness surface: a
|
|
588
|
+
host plugin that reads and watches a workspace's `.mellos/` store, and the
|
|
589
|
+
browser map panel that draws it with these same semantics. They are
|
|
590
|
+
*developed* inside a dsh workspace checkout (its toolchain builds them) and
|
|
591
|
+
*published* from here — sources, specs and `lib/` committed, refreshed by
|
|
592
|
+
`node scripts/sync-dsh-plugin.mjs <path-to-deepseek-harness>`, which rewrites
|
|
593
|
+
the dsh-internal package names to the published ones. This repo cannot build
|
|
594
|
+
them, so it proves what it can: `typecheck:packages` and the framework-free
|
|
595
|
+
specs run in CI, and `tests/packages.test.ts` guards the src↔lib structure,
|
|
596
|
+
the shared version line, and the MCP row's spawn form. Specs that need the
|
|
597
|
+
`@deepseek-ai` framework or a DOM are named, with their reason, in
|
|
598
|
+
`vitest.config.ts`. See [`packages/dsh/README.md`](packages/dsh/README.md).
|
|
311
599
|
|
|
312
600
|
### Library
|
|
313
601
|
|
|
314
602
|
The lower layers are also a library (`npm run build` emits `lib/` with type
|
|
315
603
|
declarations; npm packs it). Subpath exports mirror the source:
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
604
|
+
|
|
605
|
+
| Subpath | Contents | Browser-safe |
|
|
606
|
+
| --- | --- | --- |
|
|
607
|
+
| `mellos-mapping/domain/types` | the map value, ids, ranks, statuses, errors | yes |
|
|
608
|
+
| `mellos-mapping/domain/ops` | pure operations over a map | yes |
|
|
609
|
+
| `mellos-mapping/format` | state-file parse / serialize, page ids | yes |
|
|
610
|
+
| `mellos-mapping/semantics` | zoom ladder, group aggregation, focus and page-set rules, the shared glyph vocabulary | yes |
|
|
611
|
+
| `mellos-mapping/render` | the terminal renderer | gated the same way (it is pure), but its output is character cells — for terminal hosts |
|
|
612
|
+
| `mellos-mapping/store` | filesystem persistence, atomic saves, focus file, policy | Node only |
|
|
613
|
+
| `mellos-mapping/server` | the bundled MCP server entry — a spawn target, not a module to import | Node only |
|
|
614
|
+
|
|
615
|
+
**Browser-safe** means no Node builtins anywhere in the import closure, gated
|
|
616
|
+
by a test, so a graphical client (a web panel, an editor view) can parse state
|
|
617
|
+
files and reuse the exact aggregation, zoom and glyph semantics the terminal
|
|
618
|
+
pane draws with. `packages/dsh-client` is that client.
|
|
322
619
|
|
|
323
620
|
## License
|
|
324
621
|
|