editprompt 1.6.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +112 -9
  2. package/dist/index.js +470 -52
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -22,7 +22,7 @@ A CLI tool that lets you write prompts for CLI tools using your favorite text ed
22
22
  ## ✨ Features
23
23
 
24
24
  - 🖊️ **Editor Integration**: Use your preferred text editor to write prompts
25
- - 🖥️ **Multiplexer Support**: Send prompts directly to tmux, WezTerm, or Herdr sessions
25
+ - 🖥️ **Multiplexer Support**: Send prompts directly to tmux, WezTerm, Herdr, or Rex sessions
26
26
  - 🖥️ **Universal Terminal Support**: Works with any terminal via clipboard - no multiplexer required
27
27
  - 📤 **Send Without Closing**: Iterate on prompts without closing your editor
28
28
  - 📋 **Quote Buffering**: Collect text selections and send them as quoted replies
@@ -187,6 +187,92 @@ Run the binding from a target pane. editprompt opens a regular bottom split for
187
187
 
188
188
  Inside a Herdr pane, editprompt automatically selects the Herdr backend when `HERDR_SOCKET_PATH` and a pane ID are available. An explicit `--mux` option or `EDITPROMPT_MUX` value takes precedence, so the binding above keeps `--mux herdr` to make its intent clear.
189
189
 
190
+ ### Rex Integration
191
+
192
+ [Rex](https://www.superlogical.com/rex/docs) is configured in Lua, and its configuration file can both run commands and build layouts, so the binding does the splitting and editprompt only opens the editor. Add this to `~/.config/rex/init.lua`:
193
+
194
+ ```lua
195
+ -- Height of the editor pane, in rows. new_split takes a fraction, so it is
196
+ -- derived from the target pane's current height; a fixed fraction would make
197
+ -- the editor grow with the window. PANE_CHROME_ROWS is what a pane spends on
198
+ -- its header and border, which hold no text.
199
+ local EDITOR_ROWS = 12
200
+ local PANE_CHROME_ROWS = 3
201
+
202
+ local function editor_ratio(block_id)
203
+ local size = rex.block.call("com.superlogical.terminal", "size", { block_id = block_id })
204
+ local rows = size and size.rows
205
+ if not rows or rows <= 1 then
206
+ return 0.8
207
+ end
208
+ -- Never let the editor take more than half, for a pane too short to spare the
209
+ -- rows, and stay inside the fraction new_split accepts.
210
+ return math.max(0.5, math.min(0.9, 1 - (EDITOR_ROWS + PANE_CHROME_ROWS) / rows))
211
+ end
212
+
213
+ -- Start the editor in the directory the target pane last reported. A shell that
214
+ -- uses OSC 7, such as nushell, reports it as a file:// URL.
215
+ local function pane_cwd(block_id)
216
+ local result = rex.block.call("com.superlogical.terminal", "pwd", { block_id = block_id })
217
+ local pwd = result and result.pwd
218
+ if not pwd then
219
+ return nil
220
+ end
221
+ return (pwd:gsub("^file://[^/]*", ""))
222
+ end
223
+
224
+ rex.bind("alt+q", function(ctx)
225
+ -- Focus an editor pane that is already open, or, when pressed from the editor
226
+ -- pane, go back to the target pane.
227
+ local resumed, err = rex.exec({
228
+ "editprompt", "resume", "--mux", "rex", "--target-pane", ctx.block_id,
229
+ })
230
+ if not resumed then
231
+ -- The command never ran at all, which is not the same as having no editor
232
+ -- pane to go back to. Say so rather than opening a split that would fail
233
+ -- the same way.
234
+ rex.log("error", "editprompt could not be run: " .. tostring(err))
235
+ return true
236
+ end
237
+ if resumed.ok then
238
+ return true
239
+ end
240
+
241
+ rex.session.new_split{
242
+ anchor_id = ctx.block_id,
243
+ direction = "vertical",
244
+ side = "after",
245
+ ratio = editor_ratio(ctx.block_id),
246
+ focus = true,
247
+ layout = rex.layout.block{
248
+ flavor = "com.superlogical.terminal.shell",
249
+ options = {
250
+ command = {
251
+ "editprompt", "open", "--mux", "rex",
252
+ "--target-pane", ctx.block_id, "--editor", "nvim", "--always-copy",
253
+ },
254
+ cwd = pane_cwd(ctx.block_id),
255
+ },
256
+ },
257
+ }
258
+ return true
259
+ end)
260
+ ```
261
+
262
+ Reload it with `rex config reload`; Rex does not watch the file for changes. `rex config check` reports mistakes without applying them.
263
+
264
+ `ratio` is the share the `before` part keeps, which here is the target pane, so a larger value leaves the editor less room. `direction = "vertical"` stacks the panes and `side = "after"` puts the new one below. `EDITOR_ROWS` is the knob worth tuning: it keeps the editor the same height whatever the window's size, the way `--pane-rows` does for Herdr.
265
+
266
+ **Turn off the app's "Autobalance Splits" setting.** With it on, Rex equalises every pane as soon as a block is created, which discards the `ratio` the binding asked for and leaves the editor half the window. Setting the border afterwards with `session.resize_pane` does not help either: the equalisation lands after it. `focus` defaults to false, so the binding asks for it explicitly. The pane closes once the editor exits, which is Rex's default for a terminal started with a `command`; it stays open after a quick failure instead, so a startup error remains readable.
267
+
268
+ editprompt locates the `rex` CLI through `REX_BIN_DIR`, which Rex sets in every terminal it opens, and falls back to the PATH. That matters because the login shell can rebuild PATH and drop the directory Rex added. On NixOS and nix-darwin it does, and the fix Rex documents for that runs in interactive shells only, which a terminal started from `init.lua` is not.
269
+
270
+ `rex.exec` runs in the Rex server's environment rather than a shell's, so give `editprompt` an absolute path there if the server's PATH does not include it.
271
+
272
+ Name the editor with `--editor`. A terminal Rex starts gets the login shell's environment, not the interactive one, so `$EDITOR` is whatever that environment sets rather than what your prompt uses — on nix-darwin, for instance, it is `nano`.
273
+
274
+ Inside a Rex pane, editprompt automatically selects the Rex backend when `REX_BLOCK` is set. Running tmux inside a Rex pane sets both, and Rex wins, so pass `--mux tmux` or set `EDITPROMPT_MUX` for that case.
275
+
190
276
  ### Editor Integration (Send Without Closing)
191
277
 
192
278
  While editprompt is running, you can send content to the target pane or clipboard without closing the editor. This allows you to iterate quickly on your prompts.
@@ -205,7 +291,7 @@ editprompt input --auto-send -- "your content here"
205
291
  editprompt input --auto-send --send-key "C-m" -- "your content here"
206
292
  # Customize the key to send after content (tmux format example)
207
293
  # WezTerm example: --send-key "\r"
208
- # Herdr example: --send-key "enter"
294
+ # Herdr and Rex example: --send-key "enter"
209
295
  ```
210
296
 
211
297
  This sends the content to the target pane (or clipboard) while keeping your editor open, so you can continue editing and send multiple times.
@@ -217,6 +303,7 @@ This sends the content to the target pane (or clipboard) while keeping your edit
217
303
  - tmux format: `Enter` (default), `C-a`, etc.
218
304
  - WezTerm format: `\r` (default), `\x01`, etc.
219
305
  - Herdr format: `enter` (default), `ctrl+a`, etc.
306
+ - Rex format: `enter` (default), `ctrl+a`, etc.
220
307
 
221
308
  #### Neovim Integration
222
309
 
@@ -236,7 +323,7 @@ editprompt press -- Up
236
323
  editprompt press -- C-m # Enter
237
324
  editprompt press -- C-c # Ctrl+C
238
325
 
239
- # Herdr examples
326
+ # Herdr and Rex examples
240
327
  editprompt press -- enter
241
328
  editprompt press -- ctrl+c
242
329
 
@@ -252,12 +339,12 @@ editprompt press --delay 500 -- Tab
252
339
 
253
340
  **Key notation** depends on your multiplexer:
254
341
 
255
- | Key | tmux | WezTerm | Herdr |
256
- | ------------- | ------------- | ------------------- | ------------- |
257
- | Enter | `C-m` | `\r` | `enter` |
258
- | Tab | `Tab` | `\t` | `tab` |
259
- | Escape | `Escape` | `\x1b` | `esc` |
260
- | Arrow Up/Down | `Up` / `Down` | `\x1b[A` / `\x1b[B` | `up` / `down` |
342
+ | Key | tmux | WezTerm | Herdr | Rex |
343
+ | ------------- | ------------- | ------------------- | ------------- | ------------- |
344
+ | Enter | `C-m` | `\r` | `enter` | `enter` |
345
+ | Tab | `Tab` | `\t` | `tab` | `tab` |
346
+ | Escape | `Escape` | `\x1b` | `esc` | `escape` |
347
+ | Arrow Up/Down | `Up` / `Down` | `\x1b[A` / `\x1b[B` | `up` / `down` | `up` / `down` |
261
348
 
262
349
  ### Quote Workflow Setup
263
350
 
@@ -327,6 +414,22 @@ editprompt collect --mux herdr --target-pane "$HERDR_PANE_ID" -- "selected text"
327
414
 
328
415
  This can be called from a script or editor integration that has access to the selected text. Herdr quote buffers are stored in editprompt's configuration, associated with the target pane.
329
416
 
417
+ #### Collecting Quotes in Rex
418
+
419
+ Pass selected text as an argument together with the target block ID:
420
+
421
+ ```bash
422
+ editprompt collect --mux rex --target-pane "$REX_BLOCK" -- "selected text"
423
+ ```
424
+
425
+ From `init.lua`, call it with `rex.exec`, which takes an argv table so the text needs no quoting:
426
+
427
+ ```lua
428
+ rex.exec({ "editprompt", "collect", "--mux", "rex", "--target-pane", ctx.block_id, "--", text })
429
+ ```
430
+
431
+ As with Herdr, Rex quote buffers live in editprompt's configuration, associated with the target pane.
432
+
330
433
  #### Capturing Collected Quotes
331
434
 
332
435
  Run this command from within your editor pane to retrieve all collected quotes:
package/dist/index.js CHANGED
@@ -7,10 +7,10 @@ import { createConnection } from "node:net";
7
7
  import Conf from "conf";
8
8
  import { exec, spawn } from "node:child_process";
9
9
  import { promisify } from "node:util";
10
+ import { join } from "node:path";
10
11
  import clipboardy from "clipboardy";
11
12
  import { mkdir, readFile, writeFile } from "node:fs/promises";
12
13
  import { tmpdir } from "node:os";
13
- import { join } from "node:path";
14
14
 
15
15
  //#region \0rolldown/runtime.js
16
16
  var __defProp = Object.defineProperty;
@@ -90,7 +90,7 @@ function setupLogger(options = {}) {
90
90
 
91
91
  //#endregion
92
92
  //#region package.json
93
- var version = "1.6.0";
93
+ var version = "1.8.1";
94
94
 
95
95
  //#endregion
96
96
  //#region src/config/constants.ts
@@ -102,6 +102,9 @@ const TMUX_SEND_CHUNK_BYTES = TMUX_IMSG_LIMIT_BYTES / 2;
102
102
  const WEZTERM_SEND_CHUNK_BYTES = TMUX_SEND_CHUNK_BYTES;
103
103
  const HERDR_REQUEST_LIMIT_BYTES = 1024 * 1024;
104
104
  const HERDR_SEND_CHUNK_BYTES = HERDR_REQUEST_LIMIT_BYTES / 8;
105
+ const NIWATERM_SEND_CHUNK_BYTES = WEZTERM_SEND_CHUNK_BYTES;
106
+ const REX_CONTROL_LIMIT_BYTES = 64 * 1024;
107
+ const REX_SEND_CHUNK_BYTES = REX_CONTROL_LIMIT_BYTES / 8;
105
108
 
106
109
  //#endregion
107
110
  //#region src/utils/contentChunker.ts
@@ -139,28 +142,28 @@ const conf = new Conf({ projectName });
139
142
  //#endregion
140
143
  //#region src/modules/herdr.ts
141
144
  var herdr_exports = /* @__PURE__ */ __exportAll({
142
- appendToQuoteText: () => appendToQuoteText$1,
145
+ appendToQuoteText: () => appendToQuoteText$2,
143
146
  calculateEditorSplitRatio: () => calculateEditorSplitRatio,
144
- checkPaneExists: () => checkPaneExists$2,
145
- clearEditorPaneId: () => clearEditorPaneId$2,
146
- clearQuoteText: () => clearQuoteText$1,
147
+ checkPaneExists: () => checkPaneExists$4,
148
+ clearEditorPaneId: () => clearEditorPaneId$4,
149
+ clearQuoteText: () => clearQuoteText$2,
147
150
  closePane: () => closePane,
148
- focusPane: () => focusPane$2,
149
- getCurrentPaneId: () => getCurrentPaneId$2,
150
- getEditorPaneId: () => getEditorPaneId$2,
151
- getQuoteText: () => getQuoteText$1,
151
+ focusPane: () => focusPane$4,
152
+ getCurrentPaneId: () => getCurrentPaneId$4,
153
+ getEditorPaneId: () => getEditorPaneId$4,
154
+ getQuoteText: () => getQuoteText$2,
152
155
  getStorageNamespace: () => getStorageNamespace,
153
- getTargetPaneIds: () => getTargetPaneIds$2,
156
+ getTargetPaneIds: () => getTargetPaneIds$4,
154
157
  inputToHerdrPane: () => inputToHerdrPane,
155
- isEditorPaneFromConf: () => isEditorPaneFromConf$1,
156
- markAsEditorPane: () => markAsEditorPane$2,
157
- resumeEditorPane: () => resumeEditorPane,
158
+ isEditorPaneFromConf: () => isEditorPaneFromConf$2,
159
+ markAsEditorPane: () => markAsEditorPane$4,
160
+ resumeEditorPane: () => resumeEditorPane$1,
158
161
  runCommandInPane: () => runCommandInPane,
159
- saveEditorPaneId: () => saveEditorPaneId$2,
162
+ saveEditorPaneId: () => saveEditorPaneId$4,
160
163
  sendKeyToHerdrPane: () => sendKeyToHerdrPane,
161
164
  splitEditorPane: () => splitEditorPane
162
165
  });
163
- const logger$13 = getLogger(["editprompt", "herdr"]);
166
+ const logger$15 = getLogger(["editprompt", "herdr"]);
164
167
  const HERDR_REQUEST_TIMEOUT_MS = 6e3;
165
168
  let requestSequence = 0;
166
169
  function getSocketPath() {
@@ -241,27 +244,27 @@ async function sendRequest(method, params) {
241
244
  function getStorageNamespace() {
242
245
  return `herdr.session_${createHash("sha256").update(getSocketPath()).digest("hex").slice(0, 16)}`;
243
246
  }
244
- function targetPaneKey(paneId) {
247
+ function targetPaneKey$1(paneId) {
245
248
  return `${getStorageNamespace()}.targetPane.pane_${paneId}`;
246
249
  }
247
- function editorPaneKey(paneId) {
250
+ function editorPaneKey$1(paneId) {
248
251
  return `${getStorageNamespace()}.editorPane.pane_${paneId}`;
249
252
  }
250
- async function getCurrentPaneId$2() {
253
+ async function getCurrentPaneId$4() {
251
254
  const paneId = process.env.HERDR_PANE_ID?.trim() || process.env.HERDR_ACTIVE_PANE_ID?.trim();
252
255
  if (!paneId) throw new Error("HERDR_PANE_ID or HERDR_ACTIVE_PANE_ID is not set");
253
256
  return paneId;
254
257
  }
255
- async function checkPaneExists$2(paneId) {
258
+ async function checkPaneExists$4(paneId) {
256
259
  try {
257
260
  await sendRequest("pane.get", { pane_id: paneId });
258
261
  return true;
259
262
  } catch (error) {
260
- logger$13.debug("checkPaneExists failed: {error}", { error });
263
+ logger$15.debug("checkPaneExists failed: {error}", { error });
261
264
  return false;
262
265
  }
263
266
  }
264
- async function focusPane$2(paneId) {
267
+ async function focusPane$4(paneId) {
265
268
  await sendRequest("pane.focus", { pane_id: paneId });
266
269
  }
267
270
  function calculateEditorSplitRatio(paneHeight, editorPaneRows) {
@@ -284,14 +287,14 @@ async function splitEditorPane(targetPaneId, editorPaneRows, cwd, env = {}) {
284
287
  if (typeof editorPaneId !== "string" || editorPaneId === "") throw new Error("Herdr pane.split did not return the new pane ID");
285
288
  return editorPaneId;
286
289
  }
287
- function quoteShellWord(value) {
290
+ function quoteShellWord$1(value) {
288
291
  return `'${value.replace(/'/g, "'\\''")}'`;
289
292
  }
290
293
  async function runCommandInPane(paneId, argv) {
291
294
  if (argv.length === 0) throw new Error("Cannot run an empty command in a Herdr pane");
292
295
  await sendRequest("pane.send_input", {
293
296
  pane_id: paneId,
294
- text: `exec ${argv.map(quoteShellWord).join(" ")}`,
297
+ text: `exec ${argv.map(quoteShellWord$1).join(" ")}`,
295
298
  keys: ["Enter"]
296
299
  });
297
300
  }
@@ -304,7 +307,7 @@ async function inputToHerdrPane(paneId, content) {
304
307
  pane_id: paneId,
305
308
  text: chunk
306
309
  });
307
- logger$13.debug("Content sent to Herdr pane: {paneId} ({chunkCount} chunks)", {
310
+ logger$15.debug("Content sent to Herdr pane: {paneId} ({chunkCount} chunks)", {
308
311
  paneId,
309
312
  chunkCount: chunks.length
310
313
  });
@@ -316,6 +319,264 @@ async function sendKeyToHerdrPane(paneId, key, delay = 1e3) {
316
319
  keys: [key]
317
320
  });
318
321
  }
322
+ async function saveEditorPaneId$4(targetPaneId, editorPaneId) {
323
+ const data = conf.get(targetPaneKey$1(targetPaneId));
324
+ const existing = typeof data === "object" && data !== null ? data : {};
325
+ conf.set(targetPaneKey$1(targetPaneId), {
326
+ ...existing,
327
+ editorPaneId
328
+ });
329
+ }
330
+ async function getEditorPaneId$4(targetPaneId) {
331
+ const data = conf.get(targetPaneKey$1(targetPaneId));
332
+ if (typeof data === "object" && data !== null && "editorPaneId" in data) return String(data.editorPaneId);
333
+ return "";
334
+ }
335
+ async function clearEditorPaneId$4(targetPaneId, expectedEditorPaneId) {
336
+ const editorPaneId = await getEditorPaneId$4(targetPaneId);
337
+ if (expectedEditorPaneId && editorPaneId !== expectedEditorPaneId) {
338
+ conf.delete(editorPaneKey$1(expectedEditorPaneId));
339
+ return;
340
+ }
341
+ const data = conf.get(targetPaneKey$1(targetPaneId));
342
+ if (typeof data === "object" && data !== null && "editorPaneId" in data) {
343
+ const { editorPaneId: _, ...remaining } = data;
344
+ if (Object.keys(remaining).length === 0) conf.delete(targetPaneKey$1(targetPaneId));
345
+ else conf.set(targetPaneKey$1(targetPaneId), remaining);
346
+ }
347
+ if (editorPaneId) conf.delete(editorPaneKey$1(editorPaneId));
348
+ }
349
+ async function markAsEditorPane$4(editorPaneId, targetPaneIds) {
350
+ const uniqueTargetPaneIds = [...new Set(targetPaneIds)];
351
+ conf.set(editorPaneKey$1(editorPaneId), { targetPaneIds: uniqueTargetPaneIds });
352
+ for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId$4(targetPaneId, editorPaneId);
353
+ }
354
+ async function getTargetPaneIds$4(editorPaneId) {
355
+ const data = conf.get(editorPaneKey$1(editorPaneId));
356
+ if (typeof data === "object" && data !== null && "targetPaneIds" in data) {
357
+ const targetPaneIds = data.targetPaneIds;
358
+ if (Array.isArray(targetPaneIds)) return targetPaneIds.map((id) => String(id));
359
+ }
360
+ return [];
361
+ }
362
+ function isEditorPaneFromConf$2(paneId) {
363
+ return conf.has(editorPaneKey$1(paneId));
364
+ }
365
+ async function resumeEditorPane$1(targetPaneId) {
366
+ const currentPaneId = await getCurrentPaneId$4();
367
+ const editorPaneId = isEditorPaneFromConf$2(currentPaneId) ? currentPaneId : isEditorPaneFromConf$2(targetPaneId) ? targetPaneId : "";
368
+ if (editorPaneId) {
369
+ const targetPaneIds = await getTargetPaneIds$4(editorPaneId);
370
+ for (const paneId of targetPaneIds) if (await checkPaneExists$4(paneId)) {
371
+ await focusPane$4(paneId);
372
+ return true;
373
+ }
374
+ return false;
375
+ }
376
+ const registeredEditorPaneId = await getEditorPaneId$4(targetPaneId);
377
+ if (registeredEditorPaneId === "") return false;
378
+ if (!await checkPaneExists$4(registeredEditorPaneId)) {
379
+ await clearEditorPaneId$4(targetPaneId);
380
+ return false;
381
+ }
382
+ await focusPane$4(registeredEditorPaneId);
383
+ return true;
384
+ }
385
+ async function appendToQuoteText$2(paneId, content) {
386
+ const data = conf.get(targetPaneKey$1(paneId));
387
+ const existing = typeof data === "object" && data !== null ? data : {};
388
+ const existingQuoteText = "quote_text" in existing ? String(existing.quote_text) : "";
389
+ const quoteText = existingQuoteText.trim() !== "" ? `${existingQuoteText}\n\n${content}` : content;
390
+ conf.set(targetPaneKey$1(paneId), {
391
+ ...existing,
392
+ quote_text: quoteText
393
+ });
394
+ }
395
+ async function getQuoteText$2(paneId) {
396
+ const data = conf.get(targetPaneKey$1(paneId));
397
+ if (typeof data === "object" && data !== null && "quote_text" in data) return String(data.quote_text);
398
+ return "";
399
+ }
400
+ async function clearQuoteText$2(paneId) {
401
+ const data = conf.get(targetPaneKey$1(paneId));
402
+ if (typeof data === "object" && data !== null && "quote_text" in data) {
403
+ const { quote_text: _, ...remaining } = data;
404
+ if (Object.keys(remaining).length === 0) conf.delete(targetPaneKey$1(paneId));
405
+ else conf.set(targetPaneKey$1(paneId), remaining);
406
+ }
407
+ }
408
+
409
+ //#endregion
410
+ //#region src/modules/niwaterm.ts
411
+ const execAsync$2 = promisify(exec);
412
+ const logger$14 = getLogger(["editprompt", "niwaterm"]);
413
+ const VAR_EDITOR_PANE = "editprompt_editor_pane";
414
+ const VAR_IS_EDITOR = "editprompt_is_editor";
415
+ const VAR_TARGET_PANES = "editprompt_target_panes";
416
+ const VAR_QUOTE = "editprompt_quote";
417
+ function quoteShellWord(value) {
418
+ return `'${value.replace(/'/g, "'\\''")}'`;
419
+ }
420
+ async function getCurrentPaneId$3() {
421
+ const tabId = process.env.NIWATERM_TAB_ID?.trim();
422
+ if (!tabId) throw new Error("NIWATERM_TAB_ID is not set");
423
+ return tabId;
424
+ }
425
+ async function getTabVar(tabId, key) {
426
+ try {
427
+ const { stdout } = await execAsync$2(`niwa tab var get -t ${quoteShellWord(tabId)} ${quoteShellWord(key)}`);
428
+ return stdout.trim();
429
+ } catch (error) {
430
+ logger$14.debug("getTabVar failed: {error}", { error });
431
+ return "";
432
+ }
433
+ }
434
+ async function setTabVar(tabId, key, value) {
435
+ await execAsync$2(`niwa tab var set -t ${quoteShellWord(tabId)} ${quoteShellWord(key)} ${quoteShellWord(value)}`);
436
+ }
437
+ async function checkPaneExists$3(paneId) {
438
+ try {
439
+ await execAsync$2(`niwa tab var list -t ${quoteShellWord(paneId)}`);
440
+ return true;
441
+ } catch (error) {
442
+ logger$14.debug("checkPaneExists failed: {error}", { error });
443
+ return false;
444
+ }
445
+ }
446
+ async function focusPane$3(paneId) {
447
+ await execAsync$2(`niwa tab focus -t ${quoteShellWord(paneId)}`);
448
+ }
449
+ async function saveEditorPaneId$3(targetPaneId, editorPaneId) {
450
+ await setTabVar(targetPaneId, VAR_EDITOR_PANE, editorPaneId);
451
+ }
452
+ async function clearEditorPaneId$3(targetPaneId) {
453
+ await setTabVar(targetPaneId, VAR_EDITOR_PANE, "");
454
+ }
455
+ async function getEditorPaneId$3(targetPaneId) {
456
+ return getTabVar(targetPaneId, VAR_EDITOR_PANE);
457
+ }
458
+ async function markAsEditorPane$3(editorPaneId, targetPaneIds) {
459
+ const uniqueTargetPaneIds = [...new Set(targetPaneIds)];
460
+ await setTabVar(editorPaneId, VAR_IS_EDITOR, "1");
461
+ await setTabVar(editorPaneId, VAR_TARGET_PANES, uniqueTargetPaneIds.join(","));
462
+ for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId$3(targetPaneId, editorPaneId);
463
+ }
464
+ async function getTargetPaneIds$3(editorPaneId) {
465
+ const value = await getTabVar(editorPaneId, VAR_TARGET_PANES);
466
+ if (value === "") return [];
467
+ return value.split(",").map((id) => id.trim());
468
+ }
469
+ async function isEditorPane$1(paneId) {
470
+ return await getTabVar(paneId, VAR_IS_EDITOR) === "1";
471
+ }
472
+ async function getQuoteVariableContent$1(paneId) {
473
+ return getTabVar(paneId, VAR_QUOTE);
474
+ }
475
+ async function appendToQuoteVariable$1(paneId, content) {
476
+ const existingContent = await getQuoteVariableContent$1(paneId);
477
+ await setTabVar(paneId, VAR_QUOTE, existingContent.trim() !== "" ? `${existingContent}\n${content}` : content);
478
+ }
479
+ async function clearQuoteVariable$1(paneId) {
480
+ await setTabVar(paneId, VAR_QUOTE, "");
481
+ }
482
+ async function sendKeyToNiwatermPane(paneId, key, delay = 1e3) {
483
+ await new Promise((resolve) => setTimeout(resolve, delay));
484
+ await execAsync$2(`niwa tab send-keys -t ${quoteShellWord(paneId)} ${quoteShellWord(key)}`);
485
+ }
486
+ async function inputToNiwatermPane(paneId, content) {
487
+ const chunks = splitByByteSize(content, NIWATERM_SEND_CHUNK_BYTES);
488
+ for (const chunk of chunks) await execAsync$2(`niwa tab send-keys -t ${quoteShellWord(paneId)} ${quoteShellWord(chunk)}`);
489
+ logger$14.debug("Content sent to niwaterm pane: {paneId} ({chunks} chunk(s))", {
490
+ paneId,
491
+ chunks: chunks.length
492
+ });
493
+ }
494
+
495
+ //#endregion
496
+ //#region src/modules/rex.ts
497
+ const logger$13 = getLogger(["editprompt", "rex"]);
498
+ const REX_COMMAND = "rex";
499
+ const REX_GLOBAL_ARGS = ["--autostart=false"];
500
+ function rexCommand() {
501
+ const binDir = process.env.REX_BIN_DIR?.trim();
502
+ return binDir ? join(binDir, REX_COMMAND) : REX_COMMAND;
503
+ }
504
+ function targetPaneKey(paneId) {
505
+ return `rex.targetPane.pane_${paneId}`;
506
+ }
507
+ function editorPaneKey(paneId) {
508
+ return `rex.editorPane.pane_${paneId}`;
509
+ }
510
+ function rexArgs(args) {
511
+ return [...REX_GLOBAL_ARGS, ...args];
512
+ }
513
+ function runRex(args, input) {
514
+ const command = rexCommand();
515
+ const argv = rexArgs(args);
516
+ return new Promise((resolve, reject) => {
517
+ const child = spawn(command, argv, { stdio: [
518
+ input === void 0 ? "ignore" : "pipe",
519
+ "pipe",
520
+ "pipe"
521
+ ] });
522
+ let stdout = "";
523
+ let stderr = "";
524
+ child.stdout?.setEncoding("utf8");
525
+ child.stdout?.on("data", (chunk) => {
526
+ stdout += chunk;
527
+ });
528
+ child.stderr?.setEncoding("utf8");
529
+ child.stderr?.on("data", (chunk) => {
530
+ stderr += chunk;
531
+ });
532
+ child.on("error", (error) => {
533
+ reject(/* @__PURE__ */ new Error(`Failed to run rex: ${error.message}`));
534
+ });
535
+ child.on("close", (code) => {
536
+ if (code === 0) {
537
+ resolve(stdout);
538
+ return;
539
+ }
540
+ reject(/* @__PURE__ */ new Error(`rex ${argv.join(" ")} exited with ${code}: ${stderr.trim()}`));
541
+ });
542
+ if (input !== void 0) {
543
+ if (!child.stdin) {
544
+ reject(/* @__PURE__ */ new Error("rex was started without a standard input to write to"));
545
+ return;
546
+ }
547
+ child.stdin.on("error", (error) => {
548
+ reject(/* @__PURE__ */ new Error(`Failed to write to rex: ${error.message}`));
549
+ });
550
+ child.stdin.end(input);
551
+ }
552
+ });
553
+ }
554
+ async function getCurrentPaneId$2() {
555
+ const paneId = process.env.REX_BLOCK?.trim();
556
+ if (!paneId) throw new Error("REX_BLOCK is not set");
557
+ return paneId;
558
+ }
559
+ async function checkPaneExists$2(paneId) {
560
+ try {
561
+ await runRex([
562
+ "block",
563
+ "inspect",
564
+ paneId,
565
+ "--json"
566
+ ]);
567
+ return true;
568
+ } catch (error) {
569
+ logger$13.debug("checkPaneExists failed: {error}", { error });
570
+ return false;
571
+ }
572
+ }
573
+ async function focusPane$2(paneId) {
574
+ await runRex([
575
+ "block",
576
+ "focus",
577
+ paneId
578
+ ]);
579
+ }
319
580
  async function saveEditorPaneId$2(targetPaneId, editorPaneId) {
320
581
  const data = conf.get(targetPaneKey(targetPaneId));
321
582
  const existing = typeof data === "object" && data !== null ? data : {};
@@ -329,12 +590,8 @@ async function getEditorPaneId$2(targetPaneId) {
329
590
  if (typeof data === "object" && data !== null && "editorPaneId" in data) return String(data.editorPaneId);
330
591
  return "";
331
592
  }
332
- async function clearEditorPaneId$2(targetPaneId, expectedEditorPaneId) {
593
+ async function clearEditorPaneId$2(targetPaneId) {
333
594
  const editorPaneId = await getEditorPaneId$2(targetPaneId);
334
- if (expectedEditorPaneId && editorPaneId !== expectedEditorPaneId) {
335
- conf.delete(editorPaneKey(expectedEditorPaneId));
336
- return;
337
- }
338
595
  const data = conf.get(targetPaneKey(targetPaneId));
339
596
  if (typeof data === "object" && data !== null && "editorPaneId" in data) {
340
597
  const { editorPaneId: _, ...remaining } = data;
@@ -402,6 +659,27 @@ async function clearQuoteText$1(paneId) {
402
659
  else conf.set(targetPaneKey(paneId), remaining);
403
660
  }
404
661
  }
662
+ async function inputToRexPane(paneId, content) {
663
+ const chunks = splitByByteSize(content, REX_SEND_CHUNK_BYTES);
664
+ for (const chunk of chunks) await runRex([
665
+ "send",
666
+ "-b",
667
+ paneId
668
+ ], chunk);
669
+ logger$13.debug("Content sent to Rex pane: {paneId} ({chunks} chunk(s))", {
670
+ paneId,
671
+ chunks: chunks.length
672
+ });
673
+ }
674
+ async function sendKeyToRexPane(paneId, key, delay = 1e3) {
675
+ await new Promise((resolve) => setTimeout(resolve, delay));
676
+ await runRex([
677
+ "send-key",
678
+ "-b",
679
+ paneId,
680
+ key
681
+ ]);
682
+ }
405
683
 
406
684
  //#endregion
407
685
  //#region src/modules/tmux.ts
@@ -768,10 +1046,12 @@ function processQuoteText(text, options) {
768
1046
  const SUPPORTED_MUXES = [
769
1047
  "tmux",
770
1048
  "wezterm",
771
- "herdr"
1049
+ "herdr",
1050
+ "niwaterm",
1051
+ "rex"
772
1052
  ];
773
1053
  function isMuxType(value) {
774
- return value === "tmux" || value === "wezterm" || value === "herdr";
1054
+ return value === "tmux" || value === "wezterm" || value === "herdr" || value === "niwaterm" || value === "rex";
775
1055
  }
776
1056
  function hasValue(value) {
777
1057
  return typeof value === "string" && value.trim() !== "";
@@ -779,8 +1059,14 @@ function hasValue(value) {
779
1059
  function isHerdrEnvironment(env = process.env) {
780
1060
  return hasValue(env.HERDR_SOCKET_PATH) && (hasValue(env.HERDR_PANE_ID) || hasValue(env.HERDR_ACTIVE_PANE_ID));
781
1061
  }
1062
+ function isNiwatermEnvironment(env = process.env) {
1063
+ return hasValue(env.NIWATERM_TAB_ID);
1064
+ }
1065
+ function isRexEnvironment(env = process.env) {
1066
+ return hasValue(env.REX_BLOCK);
1067
+ }
782
1068
  function resolveMux(explicitMux, env = process.env) {
783
- const muxValue = explicitMux || env.EDITPROMPT_MUX || (isHerdrEnvironment(env) ? "herdr" : "tmux");
1069
+ const muxValue = explicitMux || env.EDITPROMPT_MUX || (isHerdrEnvironment(env) ? "herdr" : isNiwatermEnvironment(env) ? "niwaterm" : isRexEnvironment(env) ? "rex" : "tmux");
784
1070
  if (!isMuxType(muxValue)) {
785
1071
  const displayValue = typeof muxValue === "string" ? muxValue : `<${typeof muxValue}>`;
786
1072
  throw new Error(`Invalid multiplexer type '${displayValue}'. Supported values: ${SUPPORTED_MUXES.join(", ")}`);
@@ -793,7 +1079,7 @@ function resolveMux(explicitMux, env = process.env) {
793
1079
  const logger$10 = getLogger(["editprompt"]);
794
1080
  const ARG_MUX = {
795
1081
  short: "m",
796
- description: "Multiplexer type (tmux, wezterm, or herdr; auto-detects Herdr, otherwise tmux)",
1082
+ description: "Multiplexer type (tmux, wezterm, herdr, niwaterm, or rex; auto-detects Herdr, niwaterm and Rex, otherwise tmux)",
797
1083
  type: "string"
798
1084
  };
799
1085
  const ARG_TARGET_PANE_SINGLE = {
@@ -905,7 +1191,9 @@ async function runCollectMode(mux, targetPaneId, rawContent, outputs = ["buffer"
905
1191
  const processedText = processQuoteText(selection, { withQuote });
906
1192
  for (const output of outputs) if (output === "buffer") if (mux === "tmux") await appendToQuoteVariable(targetPaneId, processedText);
907
1193
  else if (mux === "wezterm") await appendToQuoteText(targetPaneId, processedText);
908
- else await appendToQuoteText$1(targetPaneId, processedText);
1194
+ else if (mux === "niwaterm") await appendToQuoteVariable$1(targetPaneId, processedText);
1195
+ else if (mux === "rex") await appendToQuoteText$1(targetPaneId, processedText);
1196
+ else await appendToQuoteText$2(targetPaneId, processedText);
909
1197
  else if (output === "stdout") process.stdout.write(processedText);
910
1198
  } catch (error) {
911
1199
  logger$9.error(`${error instanceof Error ? error.message : "Unknown error"}`);
@@ -935,7 +1223,7 @@ const collectCommand = define({
935
1223
  const outputs = normalizeCollectOutputs(ctx.values.output);
936
1224
  const withQuote = !ctx.values["no-quote"];
937
1225
  let rawContent;
938
- if (mux === "wezterm" || mux === "herdr") {
1226
+ if (mux === "wezterm" || mux === "herdr" || mux === "niwaterm" || mux === "rex") {
939
1227
  rawContent = extractRawContent(ctx.rest, ctx.positionals);
940
1228
  if (rawContent === void 0) {
941
1229
  logger$9.error(`Text content is required for collect mode with ${mux}. Use: editprompt collect --mux ${mux} --target-pane <id> -- "<text>"`);
@@ -974,9 +1262,15 @@ async function runDumpMode() {
974
1262
  } else if (config.mux === "wezterm") {
975
1263
  currentPaneId = await getCurrentPaneId();
976
1264
  isEditor = isEditorPaneFromConf(currentPaneId);
977
- } else {
1265
+ } else if (config.mux === "niwaterm") {
1266
+ currentPaneId = await getCurrentPaneId$3();
1267
+ isEditor = await isEditorPane$1(currentPaneId);
1268
+ } else if (config.mux === "rex") {
978
1269
  currentPaneId = await getCurrentPaneId$2();
979
1270
  isEditor = isEditorPaneFromConf$1(currentPaneId);
1271
+ } else {
1272
+ currentPaneId = await getCurrentPaneId$4();
1273
+ isEditor = isEditorPaneFromConf$2(currentPaneId);
980
1274
  }
981
1275
  if (!isEditor) {
982
1276
  logger$8.error("Current pane is not an editor pane");
@@ -985,7 +1279,9 @@ async function runDumpMode() {
985
1279
  let targetPanes;
986
1280
  if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
987
1281
  else if (config.mux === "wezterm") targetPanes = await getTargetPaneIds(currentPaneId);
988
- else targetPanes = await getTargetPaneIds$2(currentPaneId);
1282
+ else if (config.mux === "niwaterm") targetPanes = await getTargetPaneIds$3(currentPaneId);
1283
+ else if (config.mux === "rex") targetPanes = await getTargetPaneIds$2(currentPaneId);
1284
+ else targetPanes = await getTargetPaneIds$4(currentPaneId);
989
1285
  if (targetPanes.length === 0) {
990
1286
  logger$8.error("No target panes registered for this editor pane");
991
1287
  process.exit(1);
@@ -999,9 +1295,15 @@ async function runDumpMode() {
999
1295
  } else if (config.mux === "wezterm") {
1000
1296
  content = await getQuoteText(targetPane);
1001
1297
  await clearQuoteText(targetPane);
1002
- } else {
1298
+ } else if (config.mux === "niwaterm") {
1299
+ content = await getQuoteVariableContent$1(targetPane);
1300
+ await clearQuoteVariable$1(targetPane);
1301
+ } else if (config.mux === "rex") {
1003
1302
  content = await getQuoteText$1(targetPane);
1004
1303
  await clearQuoteText$1(targetPane);
1304
+ } else {
1305
+ content = await getQuoteText$2(targetPane);
1306
+ await clearQuoteText$2(targetPane);
1005
1307
  }
1006
1308
  if (content.trim() !== "") quoteContents.push(content);
1007
1309
  }
@@ -1048,13 +1350,17 @@ async function copyToClipboard(content) {
1048
1350
  async function inputContentToPane(content, mux, targetPaneId) {
1049
1351
  if (mux === "wezterm") await inputToWeztermPane(targetPaneId, content);
1050
1352
  else if (mux === "herdr") await inputToHerdrPane(targetPaneId, content);
1353
+ else if (mux === "niwaterm") await inputToNiwatermPane(targetPaneId, content);
1354
+ else if (mux === "rex") await inputToRexPane(targetPaneId, content);
1051
1355
  else await inputToTmuxPane(targetPaneId, content);
1052
1356
  }
1053
1357
  async function focusFirstSuccessPane(mux, targetPanes, failedPanes) {
1054
1358
  const firstSuccessPane = targetPanes.find((p) => !failedPanes.includes(p));
1055
1359
  if (firstSuccessPane) if (mux === "tmux") await focusPane$1(firstSuccessPane);
1056
1360
  else if (mux === "wezterm") await focusPane(firstSuccessPane);
1057
- else await focusPane$2(firstSuccessPane);
1361
+ else if (mux === "niwaterm") await focusPane$3(firstSuccessPane);
1362
+ else if (mux === "rex") await focusPane$2(firstSuccessPane);
1363
+ else await focusPane$4(firstSuccessPane);
1058
1364
  }
1059
1365
  async function handleContentDelivery(content, mux, targetPanes) {
1060
1366
  if (!content) return {
@@ -1131,9 +1437,15 @@ async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
1131
1437
  } else if (config.mux === "wezterm") {
1132
1438
  currentPaneId = await getCurrentPaneId();
1133
1439
  isEditor = isEditorPaneFromConf(currentPaneId);
1134
- } else {
1440
+ } else if (config.mux === "niwaterm") {
1441
+ currentPaneId = await getCurrentPaneId$3();
1442
+ isEditor = await isEditorPane$1(currentPaneId);
1443
+ } else if (config.mux === "rex") {
1135
1444
  currentPaneId = await getCurrentPaneId$2();
1136
1445
  isEditor = isEditorPaneFromConf$1(currentPaneId);
1446
+ } else {
1447
+ currentPaneId = await getCurrentPaneId$4();
1448
+ isEditor = isEditorPaneFromConf$2(currentPaneId);
1137
1449
  }
1138
1450
  if (!isEditor) {
1139
1451
  logger$6.error("Current pane is not an editor pane");
@@ -1142,13 +1454,15 @@ async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
1142
1454
  let targetPanes;
1143
1455
  if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
1144
1456
  else if (config.mux === "wezterm") targetPanes = await getTargetPaneIds(currentPaneId);
1145
- else targetPanes = await getTargetPaneIds$2(currentPaneId);
1457
+ else if (config.mux === "niwaterm") targetPanes = await getTargetPaneIds$3(currentPaneId);
1458
+ else if (config.mux === "rex") targetPanes = await getTargetPaneIds$2(currentPaneId);
1459
+ else targetPanes = await getTargetPaneIds$4(currentPaneId);
1146
1460
  if (targetPanes.length === 0) {
1147
1461
  logger$6.error("No target panes registered for this editor pane");
1148
1462
  process.exit(1);
1149
1463
  }
1150
1464
  if (autoSend) {
1151
- const key = sendKey || (config.mux === "wezterm" ? "\\r" : config.mux === "herdr" ? "enter" : "C-m");
1465
+ const key = sendKey || (config.mux === "wezterm" ? "\\r" : config.mux === "herdr" || config.mux === "rex" ? "enter" : "C-m");
1152
1466
  const delay = /\.(png|webp|avif|jpe?g|gif)\b/i.test(content) ? sendKeyDelay ?? 1e3 : 200;
1153
1467
  let successCount = 0;
1154
1468
  for (const targetPane of targetPanes) try {
@@ -1158,6 +1472,12 @@ async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
1158
1472
  } else if (config.mux === "herdr") {
1159
1473
  await inputToHerdrPane(targetPane, content);
1160
1474
  await sendKeyToHerdrPane(targetPane, key, delay);
1475
+ } else if (config.mux === "niwaterm") {
1476
+ await inputToNiwatermPane(targetPane, content);
1477
+ await sendKeyToNiwatermPane(targetPane, key, delay);
1478
+ } else if (config.mux === "rex") {
1479
+ await inputToRexPane(targetPane, content);
1480
+ await sendKeyToRexPane(targetPane, key, delay);
1161
1481
  } else {
1162
1482
  await inputToTmuxPane(targetPane, content);
1163
1483
  await sendKeyToTmuxPane(targetPane, key, delay);
@@ -1325,8 +1645,16 @@ async function runOpenEditorMode(options) {
1325
1645
  await markAsEditorPane(currentPaneId, options.targetPanes);
1326
1646
  } catch {}
1327
1647
  else if (options.targetPanes.length > 0 && options.mux === "herdr") try {
1328
- const currentPaneId = await getCurrentPaneId$2();
1648
+ const currentPaneId = await getCurrentPaneId$4();
1329
1649
  herdrEditorPaneId = currentPaneId;
1650
+ await markAsEditorPane$4(currentPaneId, options.targetPanes);
1651
+ } catch {}
1652
+ else if (options.targetPanes.length > 0 && options.mux === "niwaterm") try {
1653
+ const currentPaneId = await getCurrentPaneId$3();
1654
+ await markAsEditorPane$3(currentPaneId, options.targetPanes);
1655
+ } catch {}
1656
+ else if (options.targetPanes.length > 0 && options.mux === "rex") try {
1657
+ const currentPaneId = await getCurrentPaneId$2();
1330
1658
  await markAsEditorPane$2(currentPaneId, options.targetPanes);
1331
1659
  } catch {}
1332
1660
  try {
@@ -1363,7 +1691,13 @@ async function runOpenEditorMode(options) {
1363
1691
  for (const targetPane of options.targetPanes) await clearEditorPaneId(targetPane);
1364
1692
  } catch {}
1365
1693
  else if (options.targetPanes.length > 0 && options.mux === "herdr") try {
1366
- for (const targetPane of options.targetPanes) await clearEditorPaneId$2(targetPane, herdrEditorPaneId);
1694
+ for (const targetPane of options.targetPanes) await clearEditorPaneId$4(targetPane, herdrEditorPaneId);
1695
+ } catch {}
1696
+ else if (options.targetPanes.length > 0 && options.mux === "niwaterm") try {
1697
+ for (const targetPane of options.targetPanes) await clearEditorPaneId$3(targetPane);
1698
+ } catch {}
1699
+ else if (options.targetPanes.length > 0 && options.mux === "rex") try {
1700
+ for (const targetPane of options.targetPanes) await clearEditorPaneId$2(targetPane);
1367
1701
  } catch {}
1368
1702
  }
1369
1703
  }
@@ -1419,6 +1753,18 @@ async function runRegisterMode(options) {
1419
1753
  process.exit(1);
1420
1754
  }
1421
1755
  } else if (options.mux === "herdr") {
1756
+ editorPaneId = await getCurrentPaneId$4();
1757
+ if (!isEditorPaneFromConf$2(editorPaneId)) {
1758
+ logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1759
+ process.exit(1);
1760
+ }
1761
+ } else if (options.mux === "niwaterm") {
1762
+ editorPaneId = await getCurrentPaneId$3();
1763
+ if (!await isEditorPane$1(editorPaneId)) {
1764
+ logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1765
+ process.exit(1);
1766
+ }
1767
+ } else if (options.mux === "rex") {
1422
1768
  editorPaneId = await getCurrentPaneId$2();
1423
1769
  if (!isEditorPaneFromConf$1(editorPaneId)) {
1424
1770
  logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
@@ -1432,11 +1778,15 @@ async function runRegisterMode(options) {
1432
1778
  let existingPanes = [];
1433
1779
  if (options.mux === "tmux") existingPanes = await getTargetPaneIds$1(editorPaneId);
1434
1780
  else if (options.mux === "wezterm") existingPanes = await getTargetPaneIds(editorPaneId);
1435
- else if (options.mux === "herdr") existingPanes = await getTargetPaneIds$2(editorPaneId);
1781
+ else if (options.mux === "herdr") existingPanes = await getTargetPaneIds$4(editorPaneId);
1782
+ else if (options.mux === "niwaterm") existingPanes = await getTargetPaneIds$3(editorPaneId);
1783
+ else if (options.mux === "rex") existingPanes = await getTargetPaneIds$2(editorPaneId);
1436
1784
  const mergedTargetPanes = [...new Set([...existingPanes, ...options.targetPanes])];
1437
1785
  if (options.mux === "tmux") await markAsEditorPane$1(editorPaneId, mergedTargetPanes);
1438
1786
  else if (options.mux === "wezterm") await markAsEditorPane(editorPaneId, mergedTargetPanes);
1439
- else if (options.mux === "herdr") await markAsEditorPane$2(editorPaneId, mergedTargetPanes);
1787
+ else if (options.mux === "herdr") await markAsEditorPane$4(editorPaneId, mergedTargetPanes);
1788
+ else if (options.mux === "niwaterm") await markAsEditorPane$3(editorPaneId, mergedTargetPanes);
1789
+ else if (options.mux === "rex") await markAsEditorPane$2(editorPaneId, mergedTargetPanes);
1440
1790
  logger$4.info(`Editor pane ${editorPaneId} registered with target panes: ${mergedTargetPanes.join(", ")}`);
1441
1791
  } catch (error) {
1442
1792
  logger$4.error(`${error instanceof Error ? error.message : "Unknown error"}`);
@@ -1477,11 +1827,61 @@ const registerCommand = define({
1477
1827
  const logger$3 = getLogger(["editprompt", "resume"]);
1478
1828
  async function runResumeMode(targetPane, mux) {
1479
1829
  if (mux === "herdr") try {
1480
- process.exit(await resumeEditorPane(targetPane) ? 0 : 1);
1830
+ process.exit(await resumeEditorPane$1(targetPane) ? 0 : 1);
1481
1831
  } catch (error) {
1482
1832
  logger$3.debug("Herdr resume failed: {error}", { error });
1483
1833
  process.exit(1);
1484
1834
  }
1835
+ if (mux === "rex") try {
1836
+ process.exit(await resumeEditorPane(targetPane) ? 0 : 1);
1837
+ } catch (error) {
1838
+ logger$3.debug("Rex resume failed: {error}", { error });
1839
+ process.exit(1);
1840
+ }
1841
+ if (mux === "niwaterm") {
1842
+ const currentPaneId = await getCurrentPaneId$3();
1843
+ if (await isEditorPane$1(currentPaneId)) {
1844
+ logger$3.debug("Current pane is an editor pane");
1845
+ const originalTargetPaneIds = await getTargetPaneIds$3(currentPaneId);
1846
+ if (originalTargetPaneIds.length === 0) {
1847
+ logger$3.debug("No target pane IDs found for editor pane");
1848
+ process.exit(1);
1849
+ }
1850
+ let focused = false;
1851
+ for (const paneId of originalTargetPaneIds) if (await checkPaneExists$3(paneId)) {
1852
+ await focusPane$3(paneId);
1853
+ focused = true;
1854
+ break;
1855
+ }
1856
+ if (!focused) {
1857
+ logger$3.debug("All target panes do not exist");
1858
+ process.exit(1);
1859
+ }
1860
+ process.exit(0);
1861
+ }
1862
+ logger$3.debug("Current pane is not an editor pane");
1863
+ const editorPaneId = await getEditorPaneId$3(targetPane);
1864
+ logger$3.debug("niwaterm editorPaneId: {editorPaneId}", { editorPaneId });
1865
+ if (editorPaneId === "") {
1866
+ logger$3.debug("Editor pane ID not found");
1867
+ process.exit(1);
1868
+ }
1869
+ if (!await checkPaneExists$3(editorPaneId)) {
1870
+ logger$3.debug("Editor pane does not exist");
1871
+ await clearEditorPaneId$3(targetPane);
1872
+ process.exit(1);
1873
+ }
1874
+ try {
1875
+ await focusPane$3(editorPaneId);
1876
+ process.exit(0);
1877
+ } catch (error) {
1878
+ logger$3.debug("Can't focus editorPaneId: {editorPaneId}, error: {error}", {
1879
+ editorPaneId,
1880
+ error
1881
+ });
1882
+ process.exit(1);
1883
+ }
1884
+ }
1485
1885
  if (mux === "wezterm") {
1486
1886
  const currentPaneId = await getCurrentPaneId();
1487
1887
  if (isEditorPaneFromConf(currentPaneId)) {
@@ -1585,9 +1985,15 @@ async function runPressMode(key, delay = 0) {
1585
1985
  } else if (config.mux === "wezterm") {
1586
1986
  currentPaneId = await getCurrentPaneId();
1587
1987
  isEditor = isEditorPaneFromConf(currentPaneId);
1588
- } else {
1988
+ } else if (config.mux === "niwaterm") {
1989
+ currentPaneId = await getCurrentPaneId$3();
1990
+ isEditor = await isEditorPane$1(currentPaneId);
1991
+ } else if (config.mux === "rex") {
1589
1992
  currentPaneId = await getCurrentPaneId$2();
1590
1993
  isEditor = isEditorPaneFromConf$1(currentPaneId);
1994
+ } else {
1995
+ currentPaneId = await getCurrentPaneId$4();
1996
+ isEditor = isEditorPaneFromConf$2(currentPaneId);
1591
1997
  }
1592
1998
  if (!isEditor) {
1593
1999
  logger$2.error("Current pane is not an editor pane");
@@ -1596,7 +2002,9 @@ async function runPressMode(key, delay = 0) {
1596
2002
  let targetPanes;
1597
2003
  if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
1598
2004
  else if (config.mux === "wezterm") targetPanes = await getTargetPaneIds(currentPaneId);
1599
- else targetPanes = await getTargetPaneIds$2(currentPaneId);
2005
+ else if (config.mux === "niwaterm") targetPanes = await getTargetPaneIds$3(currentPaneId);
2006
+ else if (config.mux === "rex") targetPanes = await getTargetPaneIds$2(currentPaneId);
2007
+ else targetPanes = await getTargetPaneIds$4(currentPaneId);
1600
2008
  if (targetPanes.length === 0) {
1601
2009
  logger$2.error("No target panes registered for this editor pane");
1602
2010
  process.exit(1);
@@ -1605,6 +2013,8 @@ async function runPressMode(key, delay = 0) {
1605
2013
  for (const targetPane of targetPanes) try {
1606
2014
  if (config.mux === "wezterm") await sendKeyToWeztermPane(targetPane, key, delay);
1607
2015
  else if (config.mux === "herdr") await sendKeyToHerdrPane(targetPane, key, delay);
2016
+ else if (config.mux === "niwaterm") await sendKeyToNiwatermPane(targetPane, key, delay);
2017
+ else if (config.mux === "rex") await sendKeyToRexPane(targetPane, key, delay);
1608
2018
  else await sendKeyToTmuxPane(targetPane, key, delay);
1609
2019
  successCount++;
1610
2020
  } catch (error) {
@@ -1698,9 +2108,15 @@ async function getTargetPaneForStash() {
1698
2108
  } else if (config.mux === "wezterm") {
1699
2109
  currentPaneId = await getCurrentPaneId();
1700
2110
  isEditor = isEditorPaneFromConf(currentPaneId);
1701
- } else {
2111
+ } else if (config.mux === "niwaterm") {
2112
+ currentPaneId = await getCurrentPaneId$3();
2113
+ isEditor = await isEditorPane$1(currentPaneId);
2114
+ } else if (config.mux === "rex") {
1702
2115
  currentPaneId = await getCurrentPaneId$2();
1703
2116
  isEditor = isEditorPaneFromConf$1(currentPaneId);
2117
+ } else {
2118
+ currentPaneId = await getCurrentPaneId$4();
2119
+ isEditor = isEditorPaneFromConf$2(currentPaneId);
1704
2120
  }
1705
2121
  if (!isEditor) {
1706
2122
  logger$1.error("Current pane is not an editor pane");
@@ -1709,7 +2125,9 @@ async function getTargetPaneForStash() {
1709
2125
  let targetPanes;
1710
2126
  if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
1711
2127
  else if (config.mux === "wezterm") targetPanes = await getTargetPaneIds(currentPaneId);
1712
- else targetPanes = await getTargetPaneIds$2(currentPaneId);
2128
+ else if (config.mux === "niwaterm") targetPanes = await getTargetPaneIds$3(currentPaneId);
2129
+ else if (config.mux === "rex") targetPanes = await getTargetPaneIds$2(currentPaneId);
2130
+ else targetPanes = await getTargetPaneIds$4(currentPaneId);
1713
2131
  if (targetPanes.length === 0) {
1714
2132
  logger$1.error("No target panes registered for this editor pane");
1715
2133
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "editprompt",
3
- "version": "1.6.0",
3
+ "version": "1.8.1",
4
4
  "description": "A CLI tool that lets you write prompts for CLI tools using your favorite text editor",
5
5
  "keywords": [
6
6
  "cli",