mulmoterminal 0.1.4 → 0.3.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.
Files changed (41) hide show
  1. package/README.md +135 -5
  2. package/dist/assets/index-BKmWMFww.js +296 -0
  3. package/dist/assets/index-NQD6EPeb.css +1 -0
  4. package/dist/assets/{marp-BhLpsKq8.js → marp-Bux3kHcu.js} +1 -1
  5. package/dist/assets/material-symbols-outlined-gxDE5xnH.woff2 +0 -0
  6. package/dist/index.html +2 -2
  7. package/package.json +14 -12
  8. package/plugins/plugins.json +8 -1
  9. package/server/app-config.spec.ts +64 -0
  10. package/server/app-config.ts +45 -0
  11. package/server/backends/collections.spec.ts +221 -0
  12. package/server/backends/collections.ts +410 -0
  13. package/server/backends/files.spec.ts +67 -0
  14. package/server/backends/files.ts +126 -0
  15. package/server/backends/html.spec.ts +85 -0
  16. package/server/backends/html.ts +119 -0
  17. package/server/backends/shortcuts.spec.ts +124 -0
  18. package/server/backends/shortcuts.ts +139 -0
  19. package/server/backends/viewToken.spec.ts +111 -0
  20. package/server/backends/viewToken.ts +101 -0
  21. package/server/claude-args.spec.ts +59 -0
  22. package/server/claude-args.ts +37 -0
  23. package/server/config-routes.ts +48 -0
  24. package/server/cwd-presets.spec.ts +54 -0
  25. package/server/cwd-presets.ts +44 -0
  26. package/server/gitRemote.spec.ts +157 -0
  27. package/server/gitRemote.ts +79 -0
  28. package/server/host-tools.ts +4 -2
  29. package/server/index.ts +423 -87
  30. package/server/open-dir.spec.ts +14 -0
  31. package/server/open-dir.ts +46 -0
  32. package/server/pick-file.spec.ts +32 -0
  33. package/server/pick-file.ts +73 -0
  34. package/server/plugins-registry.ts +10 -1
  35. package/server/scripts.spec.ts +95 -0
  36. package/server/scripts.ts +63 -0
  37. package/server/transcript.spec.ts +137 -0
  38. package/server/transcript.ts +164 -0
  39. package/dist/assets/index-BCNbHKHg.js +0 -225
  40. package/dist/assets/index-C8IoNzUa.css +0 -1
  41. package/dist/assets/material-symbols-outlined-CogVgFVS.woff2 +0 -0
package/README.md CHANGED
@@ -10,6 +10,13 @@ WebSocket. A sidebar lists every Claude session for the project and reflects, in
10
10
  real time, which sessions are **working** (Claude is thinking) and which **need
11
11
  attention** (waiting for input, or finished with output you haven't seen).
12
12
 
13
+ **Inserting a file path** — like a native terminal, you can put a file's absolute
14
+ path into the prompt: **drag a file** onto the terminal (works where the browser
15
+ exposes the path via `file://` — Firefox/Safari), or click the **📎 file button**
16
+ in the terminal header, which asks the local server to open the OS file dialog and
17
+ inserts the chosen path (works in every browser, including Chrome). The path is
18
+ inserted at the cursor — it is not submitted, so you can review it first.
19
+
13
20
  ---
14
21
 
15
22
  ## Install & run
@@ -50,10 +57,13 @@ server, and opens the browser. For local development from a clone, see
50
57
  - [Tech stack](#tech-stack)
51
58
  - [Configuration](#configuration)
52
59
  - [Running](#running)
60
+ - [Scripts (Run menu)](#scripts-run-menu)
53
61
  - [Server API specification](#server-api-specification)
54
62
  - [HTTP: `GET /api/sessions`](#http-get-apisessions)
63
+ - [HTTP: `GET /api/scripts`](#http-get-apiscripts)
55
64
  - [HTTP: `POST /api/hook`](#http-post-apihook)
56
65
  - [WebSocket: `/ws` (terminal)](#websocket-ws-terminal)
66
+ - [WebSocket: `/ws/run` (command terminal)](#websocket-wsrun-command-terminal)
57
67
  - [Socket.IO: `/ws/pubsub` (activity pub/sub)](#socketio-wspubsub-activity-pubsub)
58
68
  - [Session model](#session-model)
59
69
  - [Session lifecycle](#session-lifecycle)
@@ -83,8 +93,11 @@ server, and opens the browser. For local development from a clone, see
83
93
  - **Session list** is fetched over HTTP (`/api/sessions`).
84
94
  - **Live activity** is pushed over a Socket.IO pub/sub channel (`/ws/pubsub`);
85
95
  the server learns of activity from **Claude hooks** that POST to `/api/hook`.
86
- - The Vite dev server proxies `/ws`, `/ws/pubsub`, and `/api` to the backend;
87
- in production the backend serves the built client from `dist/`.
96
+ - **Script commands** (`yarn dev`, tests, …), launched from a cell's directory
97
+ picker, run in their own ephemeral PTY over a separate WebSocket (`/ws/run`);
98
+ they are not Claude sessions. See [Scripts (Run menu)](#scripts-run-menu).
99
+ - The Vite dev server proxies `/ws` (covers `/ws/run`), `/ws/pubsub`, and `/api` to
100
+ the backend; in production the backend serves the built client from `dist/`.
88
101
 
89
102
  ---
90
103
 
@@ -136,6 +149,23 @@ Example `.env` (gitignored):
136
149
  CLAUDE_CWD=/Users/you/my-project
137
150
  ```
138
151
 
152
+ ### UI settings (`~/.mulmoterminal/config.json`)
153
+
154
+ The Settings modal (⚙) persists per-user UI choices to `~/.mulmoterminal/config.json`
155
+ (read/written via `GET`/`POST /api/config`):
156
+
157
+ | Field | Meaning |
158
+ | ------------ | ------- |
159
+ | `cwdPresets` | Quick-pick directories offered when launching a terminal. |
160
+ | `soundFile` | Absolute path to a custom **attention sound** (played when a session needs attention). Empty/unset uses the built-in synthesized chime. |
161
+
162
+ **Attention sound.** The default chime is generated with the Web Audio API — **no
163
+ audio file is bundled**, so the npm package stays light and has no media-licensing
164
+ concerns. To use your own sound, set `soundFile` in Settings (Browse / Test / Use
165
+ chime) or in the config file; the server streams that file at `GET /api/sound` and
166
+ the client decodes it (falling back to the chime if it's missing or not audio). It's
167
+ your own local file referenced by absolute path — nothing is added to the package.
168
+
139
169
  ---
140
170
 
141
171
  ## Running
@@ -164,6 +194,55 @@ In dev, open the Vite URL; its proxy forwards `/ws`, `/ws/pubsub`, and `/api` to
164
194
 
165
195
  ---
166
196
 
197
+ ## Scripts (Run menu)
198
+
199
+ An empty grid cell's launcher (the directory picker) offers a **run a script** row
200
+ that launches project scripts (a dev server, tests, a build, …) **in that cell, in
201
+ the directory the cell is pointed at** — so a whole workflow lives in one window
202
+ alongside the Claude sessions. Scripts are **per-directory**: the cell reads the
203
+ `script.json` of whatever directory you select, so different cells can offer
204
+ different projects' scripts.
205
+
206
+ Every running terminal's header also has a **▶ Run ▾** dropdown (next to the
207
+ connection status), in both the single view and each grid cell — but **only when the
208
+ open project has scripts** (no `script.json`, no button). It lists the **open
209
+ project's** `script.json` — the directory that terminal runs in — and launches the
210
+ picked script in a **spare grid cell** (reusing an open launcher, else a new one),
211
+ switching to the grid from the single view so you can watch it. So you can start a
212
+ dev server or tests for the project you're working in without disturbing the
213
+ session that's running.
214
+
215
+ The list is populated from a **`script.json`** at the chosen directory's root. It's
216
+ optional; a directory without one simply shows no scripts.
217
+
218
+ ```jsonc
219
+ // <dir>/script.json
220
+ {
221
+ "scripts": [
222
+ { "label": "Dev server", "command": "yarn dev" },
223
+ { "label": "Unit tests", "command": "yarn test" },
224
+ { "label": "Build", "command": "yarn build" },
225
+ // optional per-script working dir (relative to this file, or absolute):
226
+ { "label": "Sub server", "command": "yarn serve", "cwd": "packages/server" }
227
+ ]
228
+ }
229
+ ```
230
+
231
+ | Field | Required | Meaning |
232
+ | --------- | -------- | ------- |
233
+ | `label` | yes | What the launcher shows. |
234
+ | `command` | yes | Shell command, run via the login shell (`$SHELL -lc "<command>"`). |
235
+ | `cwd` | no | Working dir, relative to `script.json` or absolute. Defaults to the cell's directory. |
236
+
237
+ A command terminal is **not** a Claude session: it has no session id, no hooks, no
238
+ transcript, and **isn't persisted** — it's ephemeral, so a page reload drops it and
239
+ closing the cell (or reloading) kills the process. When the command exits, the cell
240
+ offers a **↻ re-run**. The browser only ever sends the script's **index** + its
241
+ directory; the server reads that directory's `script.json` and resolves the
242
+ command, so the file is the allowlist of what can run.
243
+
244
+ ---
245
+
167
246
  ## Server API specification
168
247
 
169
248
  Base URL: `http://localhost:$PORT` (default `http://localhost:3456`).
@@ -200,6 +279,28 @@ newest first, including freshly-created sessions that aren't yet written to disk
200
279
  - `500 { "error": string }` on an unexpected filesystem error. A missing project
201
280
  directory is **not** an error — it yields an empty `sessions` array.
202
281
 
282
+ ### HTTP: `GET /api/scripts`
283
+
284
+ The runnable entries from `<cwd>/script.json` for a cell's chosen directory
285
+ (`?cwd=<dir>`, falling back to `CLAUDE_CWD`); see
286
+ [Scripts (Run menu)](#scripts-run-menu). The resolved `cwd` is echoed back (the
287
+ server may fall back from a bad path), and each entry carries its `index` (the
288
+ position the client sends back to `/ws/run`).
289
+
290
+ ```jsonc
291
+ // GET /api/scripts?cwd=/Users/me/proj
292
+ {
293
+ "cwd": "/Users/me/proj",
294
+ "scripts": [
295
+ { "index": 0, "label": "Dev server", "command": "yarn dev" },
296
+ { "index": 1, "label": "Sub server", "command": "yarn serve", "cwd": "packages/server" }
297
+ ]
298
+ }
299
+ ```
300
+
301
+ A missing or invalid `script.json` is **not** an error — it yields an empty
302
+ `scripts` array.
303
+
203
304
  ### HTTP: `POST /api/hook`
204
305
 
205
306
  **Internal endpoint.** Claude hooks (injected per session — see
@@ -262,6 +363,27 @@ A non-JSON frame is written to the PTY verbatim (fallback).
262
363
  **kept alive** in the background; otherwise it's killed. See
263
364
  [Session lifecycle](#session-lifecycle).
264
365
 
366
+ ### WebSocket: `/ws/run` (command terminal)
367
+
368
+ A raw WebSocket carrying a one-off **Run-menu command** (see
369
+ [Scripts (Run menu)](#scripts-run-menu)) — a plain shell PTY, **not** a Claude
370
+ session, so there's no `session` message, no hooks, and no reattach.
371
+
372
+ **Connect**
373
+
374
+ - `ws://host/ws/run?index=<n>&cwd=<dir>` — run the script at position `<n>` in
375
+ `<dir>/script.json` (cwd falls back to `CLAUDE_CWD`). The server reads that
376
+ file and spawns `$SHELL -lc "<command>"` in the script's `cwd`. An out-of-range
377
+ index (or a missing/invalid `script.json`) yields
378
+ `{ "type": "error", "message": string }` and the socket closes.
379
+
380
+ The **output / input / resize / exit** frames are identical to `/ws`. There is no
381
+ `session` frame.
382
+
383
+ **Disconnect** — the terminal is **ephemeral**: when the socket closes (cell
384
+ closed, or page reloaded) the process is **killed**. There is no background
385
+ survival and no resume.
386
+
265
387
  ### Socket.IO: `/ws/pubsub` (activity pub/sub)
266
388
 
267
389
  A minimal Socket.IO pub/sub for live session-activity updates. Channel names are
@@ -401,7 +523,9 @@ appears, at which point the on-disk title takes over.
401
523
  ```
402
524
  server/
403
525
  index.js Express app, /api routes, terminal WebSocket, PTY lifecycle,
404
- session state, hook injection, session discovery
526
+ session state, hook injection, session discovery; also
527
+ /api/scripts + the /ws/run command-terminal relay
528
+ scripts.ts Loads/validates script.json; resolves a Run-menu command by index
405
529
  pubsub.js createPubSub(server) — socket.io pub/sub at /ws/pubsub
406
530
  fix-pty-perms.js postinstall: fixes node-pty prebuilt binary permissions
407
531
  src/
@@ -409,10 +533,16 @@ src/
409
533
  components/
410
534
  Sidebar.vue Session list; working dot + waiting bold; pub/sub driven
411
535
  Sidebar.spec.ts Vitest component tests
412
- Terminal.vue xterm.js terminal; /ws connection, reconnect on switch
536
+ Terminal.vue xterm.js terminal; /ws (or /ws/run); single-view ▶ Run menu
537
+ GridView.vue Grid toolbar (auto-layout, + Terminal); runs handed-off scripts
538
+ RunMenu.vue ▶ Run dropdown: lists a dir's script.json, emits the pick
539
+ TerminalCell.vue A cell: Claude launcher (dir picker + resume + run-a-script)
540
+ TerminalGrid.vue Grid of cells; auto-sizes by count; zoom lines up every tab
541
+ CommandCell.vue A grid cell that runs a script.json command (ephemeral)
413
542
  composables/
414
543
  usePubSub.ts socket.io-client pub/sub composable (subscribe/unsubscribe)
415
- vite.config.ts Dev proxy for /ws, /ws/pubsub, /api
544
+ usePendingScript.ts Hands a header-picked script to the grid to run
545
+ vite.config.ts Dev proxy for /ws (covers /ws/run), /ws/pubsub, /api
416
546
  vitest.config.ts jsdom test environment
417
547
  ```
418
548