editprompt 1.7.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 +365 -123
  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.7.0";
93
+ var version = "1.8.1";
94
94
 
95
95
  //#endregion
96
96
  //#region src/config/constants.ts
@@ -103,6 +103,8 @@ 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
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;
106
108
 
107
109
  //#endregion
108
110
  //#region src/utils/contentChunker.ts
@@ -140,28 +142,28 @@ const conf = new Conf({ projectName });
140
142
  //#endregion
141
143
  //#region src/modules/herdr.ts
142
144
  var herdr_exports = /* @__PURE__ */ __exportAll({
143
- appendToQuoteText: () => appendToQuoteText$1,
145
+ appendToQuoteText: () => appendToQuoteText$2,
144
146
  calculateEditorSplitRatio: () => calculateEditorSplitRatio,
145
- checkPaneExists: () => checkPaneExists$3,
146
- clearEditorPaneId: () => clearEditorPaneId$3,
147
- clearQuoteText: () => clearQuoteText$1,
147
+ checkPaneExists: () => checkPaneExists$4,
148
+ clearEditorPaneId: () => clearEditorPaneId$4,
149
+ clearQuoteText: () => clearQuoteText$2,
148
150
  closePane: () => closePane,
149
- focusPane: () => focusPane$3,
150
- getCurrentPaneId: () => getCurrentPaneId$3,
151
- getEditorPaneId: () => getEditorPaneId$3,
152
- getQuoteText: () => getQuoteText$1,
151
+ focusPane: () => focusPane$4,
152
+ getCurrentPaneId: () => getCurrentPaneId$4,
153
+ getEditorPaneId: () => getEditorPaneId$4,
154
+ getQuoteText: () => getQuoteText$2,
153
155
  getStorageNamespace: () => getStorageNamespace,
154
- getTargetPaneIds: () => getTargetPaneIds$3,
156
+ getTargetPaneIds: () => getTargetPaneIds$4,
155
157
  inputToHerdrPane: () => inputToHerdrPane,
156
- isEditorPaneFromConf: () => isEditorPaneFromConf$1,
157
- markAsEditorPane: () => markAsEditorPane$3,
158
- resumeEditorPane: () => resumeEditorPane,
158
+ isEditorPaneFromConf: () => isEditorPaneFromConf$2,
159
+ markAsEditorPane: () => markAsEditorPane$4,
160
+ resumeEditorPane: () => resumeEditorPane$1,
159
161
  runCommandInPane: () => runCommandInPane,
160
- saveEditorPaneId: () => saveEditorPaneId$3,
162
+ saveEditorPaneId: () => saveEditorPaneId$4,
161
163
  sendKeyToHerdrPane: () => sendKeyToHerdrPane,
162
164
  splitEditorPane: () => splitEditorPane
163
165
  });
164
- const logger$14 = getLogger(["editprompt", "herdr"]);
166
+ const logger$15 = getLogger(["editprompt", "herdr"]);
165
167
  const HERDR_REQUEST_TIMEOUT_MS = 6e3;
166
168
  let requestSequence = 0;
167
169
  function getSocketPath() {
@@ -242,27 +244,27 @@ async function sendRequest(method, params) {
242
244
  function getStorageNamespace() {
243
245
  return `herdr.session_${createHash("sha256").update(getSocketPath()).digest("hex").slice(0, 16)}`;
244
246
  }
245
- function targetPaneKey(paneId) {
247
+ function targetPaneKey$1(paneId) {
246
248
  return `${getStorageNamespace()}.targetPane.pane_${paneId}`;
247
249
  }
248
- function editorPaneKey(paneId) {
250
+ function editorPaneKey$1(paneId) {
249
251
  return `${getStorageNamespace()}.editorPane.pane_${paneId}`;
250
252
  }
251
- async function getCurrentPaneId$3() {
253
+ async function getCurrentPaneId$4() {
252
254
  const paneId = process.env.HERDR_PANE_ID?.trim() || process.env.HERDR_ACTIVE_PANE_ID?.trim();
253
255
  if (!paneId) throw new Error("HERDR_PANE_ID or HERDR_ACTIVE_PANE_ID is not set");
254
256
  return paneId;
255
257
  }
256
- async function checkPaneExists$3(paneId) {
258
+ async function checkPaneExists$4(paneId) {
257
259
  try {
258
260
  await sendRequest("pane.get", { pane_id: paneId });
259
261
  return true;
260
262
  } catch (error) {
261
- logger$14.debug("checkPaneExists failed: {error}", { error });
263
+ logger$15.debug("checkPaneExists failed: {error}", { error });
262
264
  return false;
263
265
  }
264
266
  }
265
- async function focusPane$3(paneId) {
267
+ async function focusPane$4(paneId) {
266
268
  await sendRequest("pane.focus", { pane_id: paneId });
267
269
  }
268
270
  function calculateEditorSplitRatio(paneHeight, editorPaneRows) {
@@ -305,7 +307,7 @@ async function inputToHerdrPane(paneId, content) {
305
307
  pane_id: paneId,
306
308
  text: chunk
307
309
  });
308
- logger$14.debug("Content sent to Herdr pane: {paneId} ({chunkCount} chunks)", {
310
+ logger$15.debug("Content sent to Herdr pane: {paneId} ({chunkCount} chunks)", {
309
311
  paneId,
310
312
  chunkCount: chunks.length
311
313
  });
@@ -317,97 +319,97 @@ async function sendKeyToHerdrPane(paneId, key, delay = 1e3) {
317
319
  keys: [key]
318
320
  });
319
321
  }
320
- async function saveEditorPaneId$3(targetPaneId, editorPaneId) {
321
- const data = conf.get(targetPaneKey(targetPaneId));
322
+ async function saveEditorPaneId$4(targetPaneId, editorPaneId) {
323
+ const data = conf.get(targetPaneKey$1(targetPaneId));
322
324
  const existing = typeof data === "object" && data !== null ? data : {};
323
- conf.set(targetPaneKey(targetPaneId), {
325
+ conf.set(targetPaneKey$1(targetPaneId), {
324
326
  ...existing,
325
327
  editorPaneId
326
328
  });
327
329
  }
328
- async function getEditorPaneId$3(targetPaneId) {
329
- const data = conf.get(targetPaneKey(targetPaneId));
330
+ async function getEditorPaneId$4(targetPaneId) {
331
+ const data = conf.get(targetPaneKey$1(targetPaneId));
330
332
  if (typeof data === "object" && data !== null && "editorPaneId" in data) return String(data.editorPaneId);
331
333
  return "";
332
334
  }
333
- async function clearEditorPaneId$3(targetPaneId, expectedEditorPaneId) {
334
- const editorPaneId = await getEditorPaneId$3(targetPaneId);
335
+ async function clearEditorPaneId$4(targetPaneId, expectedEditorPaneId) {
336
+ const editorPaneId = await getEditorPaneId$4(targetPaneId);
335
337
  if (expectedEditorPaneId && editorPaneId !== expectedEditorPaneId) {
336
- conf.delete(editorPaneKey(expectedEditorPaneId));
338
+ conf.delete(editorPaneKey$1(expectedEditorPaneId));
337
339
  return;
338
340
  }
339
- const data = conf.get(targetPaneKey(targetPaneId));
341
+ const data = conf.get(targetPaneKey$1(targetPaneId));
340
342
  if (typeof data === "object" && data !== null && "editorPaneId" in data) {
341
343
  const { editorPaneId: _, ...remaining } = data;
342
- if (Object.keys(remaining).length === 0) conf.delete(targetPaneKey(targetPaneId));
343
- else conf.set(targetPaneKey(targetPaneId), remaining);
344
+ if (Object.keys(remaining).length === 0) conf.delete(targetPaneKey$1(targetPaneId));
345
+ else conf.set(targetPaneKey$1(targetPaneId), remaining);
344
346
  }
345
- if (editorPaneId) conf.delete(editorPaneKey(editorPaneId));
347
+ if (editorPaneId) conf.delete(editorPaneKey$1(editorPaneId));
346
348
  }
347
- async function markAsEditorPane$3(editorPaneId, targetPaneIds) {
349
+ async function markAsEditorPane$4(editorPaneId, targetPaneIds) {
348
350
  const uniqueTargetPaneIds = [...new Set(targetPaneIds)];
349
- conf.set(editorPaneKey(editorPaneId), { targetPaneIds: uniqueTargetPaneIds });
350
- for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId$3(targetPaneId, editorPaneId);
351
+ conf.set(editorPaneKey$1(editorPaneId), { targetPaneIds: uniqueTargetPaneIds });
352
+ for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId$4(targetPaneId, editorPaneId);
351
353
  }
352
- async function getTargetPaneIds$3(editorPaneId) {
353
- const data = conf.get(editorPaneKey(editorPaneId));
354
+ async function getTargetPaneIds$4(editorPaneId) {
355
+ const data = conf.get(editorPaneKey$1(editorPaneId));
354
356
  if (typeof data === "object" && data !== null && "targetPaneIds" in data) {
355
357
  const targetPaneIds = data.targetPaneIds;
356
358
  if (Array.isArray(targetPaneIds)) return targetPaneIds.map((id) => String(id));
357
359
  }
358
360
  return [];
359
361
  }
360
- function isEditorPaneFromConf$1(paneId) {
361
- return conf.has(editorPaneKey(paneId));
362
+ function isEditorPaneFromConf$2(paneId) {
363
+ return conf.has(editorPaneKey$1(paneId));
362
364
  }
363
- async function resumeEditorPane(targetPaneId) {
364
- const currentPaneId = await getCurrentPaneId$3();
365
- const editorPaneId = isEditorPaneFromConf$1(currentPaneId) ? currentPaneId : isEditorPaneFromConf$1(targetPaneId) ? targetPaneId : "";
365
+ async function resumeEditorPane$1(targetPaneId) {
366
+ const currentPaneId = await getCurrentPaneId$4();
367
+ const editorPaneId = isEditorPaneFromConf$2(currentPaneId) ? currentPaneId : isEditorPaneFromConf$2(targetPaneId) ? targetPaneId : "";
366
368
  if (editorPaneId) {
367
- const targetPaneIds = await getTargetPaneIds$3(editorPaneId);
368
- for (const paneId of targetPaneIds) if (await checkPaneExists$3(paneId)) {
369
- await focusPane$3(paneId);
369
+ const targetPaneIds = await getTargetPaneIds$4(editorPaneId);
370
+ for (const paneId of targetPaneIds) if (await checkPaneExists$4(paneId)) {
371
+ await focusPane$4(paneId);
370
372
  return true;
371
373
  }
372
374
  return false;
373
375
  }
374
- const registeredEditorPaneId = await getEditorPaneId$3(targetPaneId);
376
+ const registeredEditorPaneId = await getEditorPaneId$4(targetPaneId);
375
377
  if (registeredEditorPaneId === "") return false;
376
- if (!await checkPaneExists$3(registeredEditorPaneId)) {
377
- await clearEditorPaneId$3(targetPaneId);
378
+ if (!await checkPaneExists$4(registeredEditorPaneId)) {
379
+ await clearEditorPaneId$4(targetPaneId);
378
380
  return false;
379
381
  }
380
- await focusPane$3(registeredEditorPaneId);
382
+ await focusPane$4(registeredEditorPaneId);
381
383
  return true;
382
384
  }
383
- async function appendToQuoteText$1(paneId, content) {
384
- const data = conf.get(targetPaneKey(paneId));
385
+ async function appendToQuoteText$2(paneId, content) {
386
+ const data = conf.get(targetPaneKey$1(paneId));
385
387
  const existing = typeof data === "object" && data !== null ? data : {};
386
388
  const existingQuoteText = "quote_text" in existing ? String(existing.quote_text) : "";
387
389
  const quoteText = existingQuoteText.trim() !== "" ? `${existingQuoteText}\n\n${content}` : content;
388
- conf.set(targetPaneKey(paneId), {
390
+ conf.set(targetPaneKey$1(paneId), {
389
391
  ...existing,
390
392
  quote_text: quoteText
391
393
  });
392
394
  }
393
- async function getQuoteText$1(paneId) {
394
- const data = conf.get(targetPaneKey(paneId));
395
+ async function getQuoteText$2(paneId) {
396
+ const data = conf.get(targetPaneKey$1(paneId));
395
397
  if (typeof data === "object" && data !== null && "quote_text" in data) return String(data.quote_text);
396
398
  return "";
397
399
  }
398
- async function clearQuoteText$1(paneId) {
399
- const data = conf.get(targetPaneKey(paneId));
400
+ async function clearQuoteText$2(paneId) {
401
+ const data = conf.get(targetPaneKey$1(paneId));
400
402
  if (typeof data === "object" && data !== null && "quote_text" in data) {
401
403
  const { quote_text: _, ...remaining } = data;
402
- if (Object.keys(remaining).length === 0) conf.delete(targetPaneKey(paneId));
403
- else conf.set(targetPaneKey(paneId), remaining);
404
+ if (Object.keys(remaining).length === 0) conf.delete(targetPaneKey$1(paneId));
405
+ else conf.set(targetPaneKey$1(paneId), remaining);
404
406
  }
405
407
  }
406
408
 
407
409
  //#endregion
408
410
  //#region src/modules/niwaterm.ts
409
411
  const execAsync$2 = promisify(exec);
410
- const logger$13 = getLogger(["editprompt", "niwaterm"]);
412
+ const logger$14 = getLogger(["editprompt", "niwaterm"]);
411
413
  const VAR_EDITOR_PANE = "editprompt_editor_pane";
412
414
  const VAR_IS_EDITOR = "editprompt_is_editor";
413
415
  const VAR_TARGET_PANES = "editprompt_target_panes";
@@ -415,7 +417,7 @@ const VAR_QUOTE = "editprompt_quote";
415
417
  function quoteShellWord(value) {
416
418
  return `'${value.replace(/'/g, "'\\''")}'`;
417
419
  }
418
- async function getCurrentPaneId$2() {
420
+ async function getCurrentPaneId$3() {
419
421
  const tabId = process.env.NIWATERM_TAB_ID?.trim();
420
422
  if (!tabId) throw new Error("NIWATERM_TAB_ID is not set");
421
423
  return tabId;
@@ -425,41 +427,41 @@ async function getTabVar(tabId, key) {
425
427
  const { stdout } = await execAsync$2(`niwa tab var get -t ${quoteShellWord(tabId)} ${quoteShellWord(key)}`);
426
428
  return stdout.trim();
427
429
  } catch (error) {
428
- logger$13.debug("getTabVar failed: {error}", { error });
430
+ logger$14.debug("getTabVar failed: {error}", { error });
429
431
  return "";
430
432
  }
431
433
  }
432
434
  async function setTabVar(tabId, key, value) {
433
435
  await execAsync$2(`niwa tab var set -t ${quoteShellWord(tabId)} ${quoteShellWord(key)} ${quoteShellWord(value)}`);
434
436
  }
435
- async function checkPaneExists$2(paneId) {
437
+ async function checkPaneExists$3(paneId) {
436
438
  try {
437
439
  await execAsync$2(`niwa tab var list -t ${quoteShellWord(paneId)}`);
438
440
  return true;
439
441
  } catch (error) {
440
- logger$13.debug("checkPaneExists failed: {error}", { error });
442
+ logger$14.debug("checkPaneExists failed: {error}", { error });
441
443
  return false;
442
444
  }
443
445
  }
444
- async function focusPane$2(paneId) {
446
+ async function focusPane$3(paneId) {
445
447
  await execAsync$2(`niwa tab focus -t ${quoteShellWord(paneId)}`);
446
448
  }
447
- async function saveEditorPaneId$2(targetPaneId, editorPaneId) {
449
+ async function saveEditorPaneId$3(targetPaneId, editorPaneId) {
448
450
  await setTabVar(targetPaneId, VAR_EDITOR_PANE, editorPaneId);
449
451
  }
450
- async function clearEditorPaneId$2(targetPaneId) {
452
+ async function clearEditorPaneId$3(targetPaneId) {
451
453
  await setTabVar(targetPaneId, VAR_EDITOR_PANE, "");
452
454
  }
453
- async function getEditorPaneId$2(targetPaneId) {
455
+ async function getEditorPaneId$3(targetPaneId) {
454
456
  return getTabVar(targetPaneId, VAR_EDITOR_PANE);
455
457
  }
456
- async function markAsEditorPane$2(editorPaneId, targetPaneIds) {
458
+ async function markAsEditorPane$3(editorPaneId, targetPaneIds) {
457
459
  const uniqueTargetPaneIds = [...new Set(targetPaneIds)];
458
460
  await setTabVar(editorPaneId, VAR_IS_EDITOR, "1");
459
461
  await setTabVar(editorPaneId, VAR_TARGET_PANES, uniqueTargetPaneIds.join(","));
460
- for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId$2(targetPaneId, editorPaneId);
462
+ for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId$3(targetPaneId, editorPaneId);
461
463
  }
462
- async function getTargetPaneIds$2(editorPaneId) {
464
+ async function getTargetPaneIds$3(editorPaneId) {
463
465
  const value = await getTabVar(editorPaneId, VAR_TARGET_PANES);
464
466
  if (value === "") return [];
465
467
  return value.split(",").map((id) => id.trim());
@@ -484,12 +486,201 @@ async function sendKeyToNiwatermPane(paneId, key, delay = 1e3) {
484
486
  async function inputToNiwatermPane(paneId, content) {
485
487
  const chunks = splitByByteSize(content, NIWATERM_SEND_CHUNK_BYTES);
486
488
  for (const chunk of chunks) await execAsync$2(`niwa tab send-keys -t ${quoteShellWord(paneId)} ${quoteShellWord(chunk)}`);
487
- logger$13.debug("Content sent to niwaterm pane: {paneId} ({chunks} chunk(s))", {
489
+ logger$14.debug("Content sent to niwaterm pane: {paneId} ({chunks} chunk(s))", {
488
490
  paneId,
489
491
  chunks: chunks.length
490
492
  });
491
493
  }
492
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
+ }
580
+ async function saveEditorPaneId$2(targetPaneId, editorPaneId) {
581
+ const data = conf.get(targetPaneKey(targetPaneId));
582
+ const existing = typeof data === "object" && data !== null ? data : {};
583
+ conf.set(targetPaneKey(targetPaneId), {
584
+ ...existing,
585
+ editorPaneId
586
+ });
587
+ }
588
+ async function getEditorPaneId$2(targetPaneId) {
589
+ const data = conf.get(targetPaneKey(targetPaneId));
590
+ if (typeof data === "object" && data !== null && "editorPaneId" in data) return String(data.editorPaneId);
591
+ return "";
592
+ }
593
+ async function clearEditorPaneId$2(targetPaneId) {
594
+ const editorPaneId = await getEditorPaneId$2(targetPaneId);
595
+ const data = conf.get(targetPaneKey(targetPaneId));
596
+ if (typeof data === "object" && data !== null && "editorPaneId" in data) {
597
+ const { editorPaneId: _, ...remaining } = data;
598
+ if (Object.keys(remaining).length === 0) conf.delete(targetPaneKey(targetPaneId));
599
+ else conf.set(targetPaneKey(targetPaneId), remaining);
600
+ }
601
+ if (editorPaneId) conf.delete(editorPaneKey(editorPaneId));
602
+ }
603
+ async function markAsEditorPane$2(editorPaneId, targetPaneIds) {
604
+ const uniqueTargetPaneIds = [...new Set(targetPaneIds)];
605
+ conf.set(editorPaneKey(editorPaneId), { targetPaneIds: uniqueTargetPaneIds });
606
+ for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId$2(targetPaneId, editorPaneId);
607
+ }
608
+ async function getTargetPaneIds$2(editorPaneId) {
609
+ const data = conf.get(editorPaneKey(editorPaneId));
610
+ if (typeof data === "object" && data !== null && "targetPaneIds" in data) {
611
+ const targetPaneIds = data.targetPaneIds;
612
+ if (Array.isArray(targetPaneIds)) return targetPaneIds.map((id) => String(id));
613
+ }
614
+ return [];
615
+ }
616
+ function isEditorPaneFromConf$1(paneId) {
617
+ return conf.has(editorPaneKey(paneId));
618
+ }
619
+ async function resumeEditorPane(targetPaneId) {
620
+ const currentPaneId = await getCurrentPaneId$2();
621
+ const editorPaneId = isEditorPaneFromConf$1(currentPaneId) ? currentPaneId : isEditorPaneFromConf$1(targetPaneId) ? targetPaneId : "";
622
+ if (editorPaneId) {
623
+ const targetPaneIds = await getTargetPaneIds$2(editorPaneId);
624
+ for (const paneId of targetPaneIds) if (await checkPaneExists$2(paneId)) {
625
+ await focusPane$2(paneId);
626
+ return true;
627
+ }
628
+ return false;
629
+ }
630
+ const registeredEditorPaneId = await getEditorPaneId$2(targetPaneId);
631
+ if (registeredEditorPaneId === "") return false;
632
+ if (!await checkPaneExists$2(registeredEditorPaneId)) {
633
+ await clearEditorPaneId$2(targetPaneId);
634
+ return false;
635
+ }
636
+ await focusPane$2(registeredEditorPaneId);
637
+ return true;
638
+ }
639
+ async function appendToQuoteText$1(paneId, content) {
640
+ const data = conf.get(targetPaneKey(paneId));
641
+ const existing = typeof data === "object" && data !== null ? data : {};
642
+ const existingQuoteText = "quote_text" in existing ? String(existing.quote_text) : "";
643
+ const quoteText = existingQuoteText.trim() !== "" ? `${existingQuoteText}\n\n${content}` : content;
644
+ conf.set(targetPaneKey(paneId), {
645
+ ...existing,
646
+ quote_text: quoteText
647
+ });
648
+ }
649
+ async function getQuoteText$1(paneId) {
650
+ const data = conf.get(targetPaneKey(paneId));
651
+ if (typeof data === "object" && data !== null && "quote_text" in data) return String(data.quote_text);
652
+ return "";
653
+ }
654
+ async function clearQuoteText$1(paneId) {
655
+ const data = conf.get(targetPaneKey(paneId));
656
+ if (typeof data === "object" && data !== null && "quote_text" in data) {
657
+ const { quote_text: _, ...remaining } = data;
658
+ if (Object.keys(remaining).length === 0) conf.delete(targetPaneKey(paneId));
659
+ else conf.set(targetPaneKey(paneId), remaining);
660
+ }
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
+ }
683
+
493
684
  //#endregion
494
685
  //#region src/modules/tmux.ts
495
686
  const execAsync$1 = promisify(exec);
@@ -856,10 +1047,11 @@ const SUPPORTED_MUXES = [
856
1047
  "tmux",
857
1048
  "wezterm",
858
1049
  "herdr",
859
- "niwaterm"
1050
+ "niwaterm",
1051
+ "rex"
860
1052
  ];
861
1053
  function isMuxType(value) {
862
- return value === "tmux" || value === "wezterm" || value === "herdr" || value === "niwaterm";
1054
+ return value === "tmux" || value === "wezterm" || value === "herdr" || value === "niwaterm" || value === "rex";
863
1055
  }
864
1056
  function hasValue(value) {
865
1057
  return typeof value === "string" && value.trim() !== "";
@@ -870,8 +1062,11 @@ function isHerdrEnvironment(env = process.env) {
870
1062
  function isNiwatermEnvironment(env = process.env) {
871
1063
  return hasValue(env.NIWATERM_TAB_ID);
872
1064
  }
1065
+ function isRexEnvironment(env = process.env) {
1066
+ return hasValue(env.REX_BLOCK);
1067
+ }
873
1068
  function resolveMux(explicitMux, env = process.env) {
874
- const muxValue = explicitMux || env.EDITPROMPT_MUX || (isHerdrEnvironment(env) ? "herdr" : isNiwatermEnvironment(env) ? "niwaterm" : "tmux");
1069
+ const muxValue = explicitMux || env.EDITPROMPT_MUX || (isHerdrEnvironment(env) ? "herdr" : isNiwatermEnvironment(env) ? "niwaterm" : isRexEnvironment(env) ? "rex" : "tmux");
875
1070
  if (!isMuxType(muxValue)) {
876
1071
  const displayValue = typeof muxValue === "string" ? muxValue : `<${typeof muxValue}>`;
877
1072
  throw new Error(`Invalid multiplexer type '${displayValue}'. Supported values: ${SUPPORTED_MUXES.join(", ")}`);
@@ -884,7 +1079,7 @@ function resolveMux(explicitMux, env = process.env) {
884
1079
  const logger$10 = getLogger(["editprompt"]);
885
1080
  const ARG_MUX = {
886
1081
  short: "m",
887
- description: "Multiplexer type (tmux, wezterm, herdr, or niwaterm; auto-detects Herdr and niwaterm, otherwise tmux)",
1082
+ description: "Multiplexer type (tmux, wezterm, herdr, niwaterm, or rex; auto-detects Herdr, niwaterm and Rex, otherwise tmux)",
888
1083
  type: "string"
889
1084
  };
890
1085
  const ARG_TARGET_PANE_SINGLE = {
@@ -997,7 +1192,8 @@ async function runCollectMode(mux, targetPaneId, rawContent, outputs = ["buffer"
997
1192
  for (const output of outputs) if (output === "buffer") if (mux === "tmux") await appendToQuoteVariable(targetPaneId, processedText);
998
1193
  else if (mux === "wezterm") await appendToQuoteText(targetPaneId, processedText);
999
1194
  else if (mux === "niwaterm") await appendToQuoteVariable$1(targetPaneId, processedText);
1000
- else await appendToQuoteText$1(targetPaneId, processedText);
1195
+ else if (mux === "rex") await appendToQuoteText$1(targetPaneId, processedText);
1196
+ else await appendToQuoteText$2(targetPaneId, processedText);
1001
1197
  else if (output === "stdout") process.stdout.write(processedText);
1002
1198
  } catch (error) {
1003
1199
  logger$9.error(`${error instanceof Error ? error.message : "Unknown error"}`);
@@ -1027,7 +1223,7 @@ const collectCommand = define({
1027
1223
  const outputs = normalizeCollectOutputs(ctx.values.output);
1028
1224
  const withQuote = !ctx.values["no-quote"];
1029
1225
  let rawContent;
1030
- if (mux === "wezterm" || mux === "herdr" || mux === "niwaterm") {
1226
+ if (mux === "wezterm" || mux === "herdr" || mux === "niwaterm" || mux === "rex") {
1031
1227
  rawContent = extractRawContent(ctx.rest, ctx.positionals);
1032
1228
  if (rawContent === void 0) {
1033
1229
  logger$9.error(`Text content is required for collect mode with ${mux}. Use: editprompt collect --mux ${mux} --target-pane <id> -- "<text>"`);
@@ -1067,11 +1263,14 @@ async function runDumpMode() {
1067
1263
  currentPaneId = await getCurrentPaneId();
1068
1264
  isEditor = isEditorPaneFromConf(currentPaneId);
1069
1265
  } else if (config.mux === "niwaterm") {
1070
- currentPaneId = await getCurrentPaneId$2();
1071
- isEditor = await isEditorPane$1(currentPaneId);
1072
- } else {
1073
1266
  currentPaneId = await getCurrentPaneId$3();
1267
+ isEditor = await isEditorPane$1(currentPaneId);
1268
+ } else if (config.mux === "rex") {
1269
+ currentPaneId = await getCurrentPaneId$2();
1074
1270
  isEditor = isEditorPaneFromConf$1(currentPaneId);
1271
+ } else {
1272
+ currentPaneId = await getCurrentPaneId$4();
1273
+ isEditor = isEditorPaneFromConf$2(currentPaneId);
1075
1274
  }
1076
1275
  if (!isEditor) {
1077
1276
  logger$8.error("Current pane is not an editor pane");
@@ -1080,8 +1279,9 @@ async function runDumpMode() {
1080
1279
  let targetPanes;
1081
1280
  if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
1082
1281
  else if (config.mux === "wezterm") targetPanes = await getTargetPaneIds(currentPaneId);
1083
- else if (config.mux === "niwaterm") targetPanes = await getTargetPaneIds$2(currentPaneId);
1084
- else targetPanes = await getTargetPaneIds$3(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);
1085
1285
  if (targetPanes.length === 0) {
1086
1286
  logger$8.error("No target panes registered for this editor pane");
1087
1287
  process.exit(1);
@@ -1098,9 +1298,12 @@ async function runDumpMode() {
1098
1298
  } else if (config.mux === "niwaterm") {
1099
1299
  content = await getQuoteVariableContent$1(targetPane);
1100
1300
  await clearQuoteVariable$1(targetPane);
1101
- } else {
1301
+ } else if (config.mux === "rex") {
1102
1302
  content = await getQuoteText$1(targetPane);
1103
1303
  await clearQuoteText$1(targetPane);
1304
+ } else {
1305
+ content = await getQuoteText$2(targetPane);
1306
+ await clearQuoteText$2(targetPane);
1104
1307
  }
1105
1308
  if (content.trim() !== "") quoteContents.push(content);
1106
1309
  }
@@ -1148,14 +1351,16 @@ async function inputContentToPane(content, mux, targetPaneId) {
1148
1351
  if (mux === "wezterm") await inputToWeztermPane(targetPaneId, content);
1149
1352
  else if (mux === "herdr") await inputToHerdrPane(targetPaneId, content);
1150
1353
  else if (mux === "niwaterm") await inputToNiwatermPane(targetPaneId, content);
1354
+ else if (mux === "rex") await inputToRexPane(targetPaneId, content);
1151
1355
  else await inputToTmuxPane(targetPaneId, content);
1152
1356
  }
1153
1357
  async function focusFirstSuccessPane(mux, targetPanes, failedPanes) {
1154
1358
  const firstSuccessPane = targetPanes.find((p) => !failedPanes.includes(p));
1155
1359
  if (firstSuccessPane) if (mux === "tmux") await focusPane$1(firstSuccessPane);
1156
1360
  else if (mux === "wezterm") await focusPane(firstSuccessPane);
1157
- else if (mux === "niwaterm") await focusPane$2(firstSuccessPane);
1158
- else await focusPane$3(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);
1159
1364
  }
1160
1365
  async function handleContentDelivery(content, mux, targetPanes) {
1161
1366
  if (!content) return {
@@ -1233,11 +1438,14 @@ async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
1233
1438
  currentPaneId = await getCurrentPaneId();
1234
1439
  isEditor = isEditorPaneFromConf(currentPaneId);
1235
1440
  } else if (config.mux === "niwaterm") {
1236
- currentPaneId = await getCurrentPaneId$2();
1237
- isEditor = await isEditorPane$1(currentPaneId);
1238
- } else {
1239
1441
  currentPaneId = await getCurrentPaneId$3();
1442
+ isEditor = await isEditorPane$1(currentPaneId);
1443
+ } else if (config.mux === "rex") {
1444
+ currentPaneId = await getCurrentPaneId$2();
1240
1445
  isEditor = isEditorPaneFromConf$1(currentPaneId);
1446
+ } else {
1447
+ currentPaneId = await getCurrentPaneId$4();
1448
+ isEditor = isEditorPaneFromConf$2(currentPaneId);
1241
1449
  }
1242
1450
  if (!isEditor) {
1243
1451
  logger$6.error("Current pane is not an editor pane");
@@ -1246,14 +1454,15 @@ async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
1246
1454
  let targetPanes;
1247
1455
  if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
1248
1456
  else if (config.mux === "wezterm") targetPanes = await getTargetPaneIds(currentPaneId);
1249
- else if (config.mux === "niwaterm") targetPanes = await getTargetPaneIds$2(currentPaneId);
1250
- else targetPanes = await getTargetPaneIds$3(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);
1251
1460
  if (targetPanes.length === 0) {
1252
1461
  logger$6.error("No target panes registered for this editor pane");
1253
1462
  process.exit(1);
1254
1463
  }
1255
1464
  if (autoSend) {
1256
- 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");
1257
1466
  const delay = /\.(png|webp|avif|jpe?g|gif)\b/i.test(content) ? sendKeyDelay ?? 1e3 : 200;
1258
1467
  let successCount = 0;
1259
1468
  for (const targetPane of targetPanes) try {
@@ -1266,6 +1475,9 @@ async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
1266
1475
  } else if (config.mux === "niwaterm") {
1267
1476
  await inputToNiwatermPane(targetPane, content);
1268
1477
  await sendKeyToNiwatermPane(targetPane, key, delay);
1478
+ } else if (config.mux === "rex") {
1479
+ await inputToRexPane(targetPane, content);
1480
+ await sendKeyToRexPane(targetPane, key, delay);
1269
1481
  } else {
1270
1482
  await inputToTmuxPane(targetPane, content);
1271
1483
  await sendKeyToTmuxPane(targetPane, key, delay);
@@ -1433,11 +1645,15 @@ async function runOpenEditorMode(options) {
1433
1645
  await markAsEditorPane(currentPaneId, options.targetPanes);
1434
1646
  } catch {}
1435
1647
  else if (options.targetPanes.length > 0 && options.mux === "herdr") try {
1436
- const currentPaneId = await getCurrentPaneId$3();
1648
+ const currentPaneId = await getCurrentPaneId$4();
1437
1649
  herdrEditorPaneId = currentPaneId;
1438
- await markAsEditorPane$3(currentPaneId, options.targetPanes);
1650
+ await markAsEditorPane$4(currentPaneId, options.targetPanes);
1439
1651
  } catch {}
1440
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 {
1441
1657
  const currentPaneId = await getCurrentPaneId$2();
1442
1658
  await markAsEditorPane$2(currentPaneId, options.targetPanes);
1443
1659
  } catch {}
@@ -1475,9 +1691,12 @@ async function runOpenEditorMode(options) {
1475
1691
  for (const targetPane of options.targetPanes) await clearEditorPaneId(targetPane);
1476
1692
  } catch {}
1477
1693
  else if (options.targetPanes.length > 0 && options.mux === "herdr") try {
1478
- for (const targetPane of options.targetPanes) await clearEditorPaneId$3(targetPane, herdrEditorPaneId);
1694
+ for (const targetPane of options.targetPanes) await clearEditorPaneId$4(targetPane, herdrEditorPaneId);
1479
1695
  } catch {}
1480
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 {
1481
1700
  for (const targetPane of options.targetPanes) await clearEditorPaneId$2(targetPane);
1482
1701
  } catch {}
1483
1702
  }
@@ -1534,17 +1753,23 @@ async function runRegisterMode(options) {
1534
1753
  process.exit(1);
1535
1754
  }
1536
1755
  } else if (options.mux === "herdr") {
1537
- editorPaneId = await getCurrentPaneId$3();
1538
- if (!isEditorPaneFromConf$1(editorPaneId)) {
1756
+ editorPaneId = await getCurrentPaneId$4();
1757
+ if (!isEditorPaneFromConf$2(editorPaneId)) {
1539
1758
  logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1540
1759
  process.exit(1);
1541
1760
  }
1542
1761
  } else if (options.mux === "niwaterm") {
1543
- editorPaneId = await getCurrentPaneId$2();
1762
+ editorPaneId = await getCurrentPaneId$3();
1544
1763
  if (!await isEditorPane$1(editorPaneId)) {
1545
1764
  logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1546
1765
  process.exit(1);
1547
1766
  }
1767
+ } else if (options.mux === "rex") {
1768
+ editorPaneId = await getCurrentPaneId$2();
1769
+ if (!isEditorPaneFromConf$1(editorPaneId)) {
1770
+ logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1771
+ process.exit(1);
1772
+ }
1548
1773
  } else {
1549
1774
  logger$4.error("Unsupported multiplexer");
1550
1775
  process.exit(1);
@@ -1553,13 +1778,15 @@ async function runRegisterMode(options) {
1553
1778
  let existingPanes = [];
1554
1779
  if (options.mux === "tmux") existingPanes = await getTargetPaneIds$1(editorPaneId);
1555
1780
  else if (options.mux === "wezterm") existingPanes = await getTargetPaneIds(editorPaneId);
1556
- else if (options.mux === "herdr") existingPanes = await getTargetPaneIds$3(editorPaneId);
1557
- else if (options.mux === "niwaterm") 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);
1558
1784
  const mergedTargetPanes = [...new Set([...existingPanes, ...options.targetPanes])];
1559
1785
  if (options.mux === "tmux") await markAsEditorPane$1(editorPaneId, mergedTargetPanes);
1560
1786
  else if (options.mux === "wezterm") await markAsEditorPane(editorPaneId, mergedTargetPanes);
1561
- else if (options.mux === "herdr") await markAsEditorPane$3(editorPaneId, mergedTargetPanes);
1562
- else if (options.mux === "niwaterm") 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);
1563
1790
  logger$4.info(`Editor pane ${editorPaneId} registered with target panes: ${mergedTargetPanes.join(", ")}`);
1564
1791
  } catch (error) {
1565
1792
  logger$4.error(`${error instanceof Error ? error.message : "Unknown error"}`);
@@ -1600,23 +1827,29 @@ const registerCommand = define({
1600
1827
  const logger$3 = getLogger(["editprompt", "resume"]);
1601
1828
  async function runResumeMode(targetPane, mux) {
1602
1829
  if (mux === "herdr") try {
1603
- process.exit(await resumeEditorPane(targetPane) ? 0 : 1);
1830
+ process.exit(await resumeEditorPane$1(targetPane) ? 0 : 1);
1604
1831
  } catch (error) {
1605
1832
  logger$3.debug("Herdr resume failed: {error}", { error });
1606
1833
  process.exit(1);
1607
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
+ }
1608
1841
  if (mux === "niwaterm") {
1609
- const currentPaneId = await getCurrentPaneId$2();
1842
+ const currentPaneId = await getCurrentPaneId$3();
1610
1843
  if (await isEditorPane$1(currentPaneId)) {
1611
1844
  logger$3.debug("Current pane is an editor pane");
1612
- const originalTargetPaneIds = await getTargetPaneIds$2(currentPaneId);
1845
+ const originalTargetPaneIds = await getTargetPaneIds$3(currentPaneId);
1613
1846
  if (originalTargetPaneIds.length === 0) {
1614
1847
  logger$3.debug("No target pane IDs found for editor pane");
1615
1848
  process.exit(1);
1616
1849
  }
1617
1850
  let focused = false;
1618
- for (const paneId of originalTargetPaneIds) if (await checkPaneExists$2(paneId)) {
1619
- await focusPane$2(paneId);
1851
+ for (const paneId of originalTargetPaneIds) if (await checkPaneExists$3(paneId)) {
1852
+ await focusPane$3(paneId);
1620
1853
  focused = true;
1621
1854
  break;
1622
1855
  }
@@ -1627,19 +1860,19 @@ async function runResumeMode(targetPane, mux) {
1627
1860
  process.exit(0);
1628
1861
  }
1629
1862
  logger$3.debug("Current pane is not an editor pane");
1630
- const editorPaneId = await getEditorPaneId$2(targetPane);
1863
+ const editorPaneId = await getEditorPaneId$3(targetPane);
1631
1864
  logger$3.debug("niwaterm editorPaneId: {editorPaneId}", { editorPaneId });
1632
1865
  if (editorPaneId === "") {
1633
1866
  logger$3.debug("Editor pane ID not found");
1634
1867
  process.exit(1);
1635
1868
  }
1636
- if (!await checkPaneExists$2(editorPaneId)) {
1869
+ if (!await checkPaneExists$3(editorPaneId)) {
1637
1870
  logger$3.debug("Editor pane does not exist");
1638
- await clearEditorPaneId$2(targetPane);
1871
+ await clearEditorPaneId$3(targetPane);
1639
1872
  process.exit(1);
1640
1873
  }
1641
1874
  try {
1642
- await focusPane$2(editorPaneId);
1875
+ await focusPane$3(editorPaneId);
1643
1876
  process.exit(0);
1644
1877
  } catch (error) {
1645
1878
  logger$3.debug("Can't focus editorPaneId: {editorPaneId}, error: {error}", {
@@ -1753,11 +1986,14 @@ async function runPressMode(key, delay = 0) {
1753
1986
  currentPaneId = await getCurrentPaneId();
1754
1987
  isEditor = isEditorPaneFromConf(currentPaneId);
1755
1988
  } else if (config.mux === "niwaterm") {
1756
- currentPaneId = await getCurrentPaneId$2();
1757
- isEditor = await isEditorPane$1(currentPaneId);
1758
- } else {
1759
1989
  currentPaneId = await getCurrentPaneId$3();
1990
+ isEditor = await isEditorPane$1(currentPaneId);
1991
+ } else if (config.mux === "rex") {
1992
+ currentPaneId = await getCurrentPaneId$2();
1760
1993
  isEditor = isEditorPaneFromConf$1(currentPaneId);
1994
+ } else {
1995
+ currentPaneId = await getCurrentPaneId$4();
1996
+ isEditor = isEditorPaneFromConf$2(currentPaneId);
1761
1997
  }
1762
1998
  if (!isEditor) {
1763
1999
  logger$2.error("Current pane is not an editor pane");
@@ -1766,8 +2002,9 @@ async function runPressMode(key, delay = 0) {
1766
2002
  let targetPanes;
1767
2003
  if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
1768
2004
  else if (config.mux === "wezterm") targetPanes = await getTargetPaneIds(currentPaneId);
1769
- else if (config.mux === "niwaterm") targetPanes = await getTargetPaneIds$2(currentPaneId);
1770
- else targetPanes = await getTargetPaneIds$3(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);
1771
2008
  if (targetPanes.length === 0) {
1772
2009
  logger$2.error("No target panes registered for this editor pane");
1773
2010
  process.exit(1);
@@ -1777,6 +2014,7 @@ async function runPressMode(key, delay = 0) {
1777
2014
  if (config.mux === "wezterm") await sendKeyToWeztermPane(targetPane, key, delay);
1778
2015
  else if (config.mux === "herdr") await sendKeyToHerdrPane(targetPane, key, delay);
1779
2016
  else if (config.mux === "niwaterm") await sendKeyToNiwatermPane(targetPane, key, delay);
2017
+ else if (config.mux === "rex") await sendKeyToRexPane(targetPane, key, delay);
1780
2018
  else await sendKeyToTmuxPane(targetPane, key, delay);
1781
2019
  successCount++;
1782
2020
  } catch (error) {
@@ -1871,11 +2109,14 @@ async function getTargetPaneForStash() {
1871
2109
  currentPaneId = await getCurrentPaneId();
1872
2110
  isEditor = isEditorPaneFromConf(currentPaneId);
1873
2111
  } else if (config.mux === "niwaterm") {
1874
- currentPaneId = await getCurrentPaneId$2();
1875
- isEditor = await isEditorPane$1(currentPaneId);
1876
- } else {
1877
2112
  currentPaneId = await getCurrentPaneId$3();
2113
+ isEditor = await isEditorPane$1(currentPaneId);
2114
+ } else if (config.mux === "rex") {
2115
+ currentPaneId = await getCurrentPaneId$2();
1878
2116
  isEditor = isEditorPaneFromConf$1(currentPaneId);
2117
+ } else {
2118
+ currentPaneId = await getCurrentPaneId$4();
2119
+ isEditor = isEditorPaneFromConf$2(currentPaneId);
1879
2120
  }
1880
2121
  if (!isEditor) {
1881
2122
  logger$1.error("Current pane is not an editor pane");
@@ -1884,8 +2125,9 @@ async function getTargetPaneForStash() {
1884
2125
  let targetPanes;
1885
2126
  if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
1886
2127
  else if (config.mux === "wezterm") targetPanes = await getTargetPaneIds(currentPaneId);
1887
- else if (config.mux === "niwaterm") targetPanes = await getTargetPaneIds$2(currentPaneId);
1888
- else targetPanes = await getTargetPaneIds$3(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);
1889
2131
  if (targetPanes.length === 0) {
1890
2132
  logger$1.error("No target panes registered for this editor pane");
1891
2133
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "editprompt",
3
- "version": "1.7.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",