editprompt 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +45 -10
  2. package/dist/index.js +816 -192
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,15 +2,33 @@
2
2
  import { configureSync, getAnsiColorFormatter, getLogger, getTextFormatter, parseLogLevel, withFilter } from "@logtape/logtape";
3
3
  import { cli, define } from "gunshi";
4
4
  import { appendFileSync } from "node:fs";
5
+ import { createHash, randomBytes } from "node:crypto";
6
+ import { createConnection } from "node:net";
7
+ import Conf from "conf";
5
8
  import { exec, spawn } from "node:child_process";
6
9
  import { promisify } from "node:util";
7
- import Conf from "conf";
8
10
  import clipboardy from "clipboardy";
9
11
  import { mkdir, readFile, writeFile } from "node:fs/promises";
10
- import { randomBytes } from "node:crypto";
11
12
  import { tmpdir } from "node:os";
12
13
  import { join } from "node:path";
13
14
 
15
+ //#region \0rolldown/runtime.js
16
+ var __defProp = Object.defineProperty;
17
+ var __exportAll = (all, no_symbols) => {
18
+ let target = {};
19
+ for (var name in all) {
20
+ __defProp(target, name, {
21
+ get: all[name],
22
+ enumerable: true
23
+ });
24
+ }
25
+ if (!no_symbols) {
26
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
27
+ }
28
+ return target;
29
+ };
30
+
31
+ //#endregion
14
32
  //#region src/modules/logger.ts
15
33
  function resolveLogFilePath(options) {
16
34
  if (options.logFile) return options.logFile;
@@ -72,7 +90,7 @@ function setupLogger(options = {}) {
72
90
 
73
91
  //#endregion
74
92
  //#region package.json
75
- var version = "1.5.0";
93
+ var version = "1.7.0";
76
94
 
77
95
  //#endregion
78
96
  //#region src/config/constants.ts
@@ -82,6 +100,9 @@ const DEFAULT_EDITOR = "vim";
82
100
  const TMUX_IMSG_LIMIT_BYTES = 16 * 1024;
83
101
  const TMUX_SEND_CHUNK_BYTES = TMUX_IMSG_LIMIT_BYTES / 2;
84
102
  const WEZTERM_SEND_CHUNK_BYTES = TMUX_SEND_CHUNK_BYTES;
103
+ const HERDR_REQUEST_LIMIT_BYTES = 1024 * 1024;
104
+ const HERDR_SEND_CHUNK_BYTES = HERDR_REQUEST_LIMIT_BYTES / 8;
105
+ const NIWATERM_SEND_CHUNK_BYTES = WEZTERM_SEND_CHUNK_BYTES;
85
106
 
86
107
  //#endregion
87
108
  //#region src/utils/contentChunker.ts
@@ -111,10 +132,368 @@ function splitByByteSize(content, maxBytes) {
111
132
  return chunks;
112
133
  }
113
134
 
135
+ //#endregion
136
+ //#region src/modules/conf.ts
137
+ const projectName = process.env.NODE_ENV === "test" ? "editprompt-test" : "editprompt";
138
+ const conf = new Conf({ projectName });
139
+
140
+ //#endregion
141
+ //#region src/modules/herdr.ts
142
+ var herdr_exports = /* @__PURE__ */ __exportAll({
143
+ appendToQuoteText: () => appendToQuoteText$1,
144
+ calculateEditorSplitRatio: () => calculateEditorSplitRatio,
145
+ checkPaneExists: () => checkPaneExists$3,
146
+ clearEditorPaneId: () => clearEditorPaneId$3,
147
+ clearQuoteText: () => clearQuoteText$1,
148
+ closePane: () => closePane,
149
+ focusPane: () => focusPane$3,
150
+ getCurrentPaneId: () => getCurrentPaneId$3,
151
+ getEditorPaneId: () => getEditorPaneId$3,
152
+ getQuoteText: () => getQuoteText$1,
153
+ getStorageNamespace: () => getStorageNamespace,
154
+ getTargetPaneIds: () => getTargetPaneIds$3,
155
+ inputToHerdrPane: () => inputToHerdrPane,
156
+ isEditorPaneFromConf: () => isEditorPaneFromConf$1,
157
+ markAsEditorPane: () => markAsEditorPane$3,
158
+ resumeEditorPane: () => resumeEditorPane,
159
+ runCommandInPane: () => runCommandInPane,
160
+ saveEditorPaneId: () => saveEditorPaneId$3,
161
+ sendKeyToHerdrPane: () => sendKeyToHerdrPane,
162
+ splitEditorPane: () => splitEditorPane
163
+ });
164
+ const logger$14 = getLogger(["editprompt", "herdr"]);
165
+ const HERDR_REQUEST_TIMEOUT_MS = 6e3;
166
+ let requestSequence = 0;
167
+ function getSocketPath() {
168
+ const socketPath = process.env.HERDR_SOCKET_PATH?.trim();
169
+ if (!socketPath) throw new Error("HERDR_SOCKET_PATH is not set");
170
+ return socketPath;
171
+ }
172
+ async function sendRequest(method, params) {
173
+ const id = `editprompt_${process.pid}_${++requestSequence}`;
174
+ const request = `${JSON.stringify({
175
+ id,
176
+ method,
177
+ params
178
+ })}\n`;
179
+ return new Promise((resolve, reject) => {
180
+ const socket = createConnection(getSocketPath());
181
+ let buffer = "";
182
+ let settled = false;
183
+ const finish = (callback) => {
184
+ if (settled) return;
185
+ settled = true;
186
+ socket.end();
187
+ callback();
188
+ };
189
+ socket.setEncoding("utf8");
190
+ socket.setTimeout(HERDR_REQUEST_TIMEOUT_MS);
191
+ socket.on("connect", () => {
192
+ socket.write(request);
193
+ });
194
+ socket.on("data", (chunk) => {
195
+ buffer += chunk;
196
+ const newlineIndex = buffer.indexOf("\n");
197
+ if (newlineIndex === -1) return;
198
+ const line = buffer.slice(0, newlineIndex);
199
+ let response;
200
+ try {
201
+ response = JSON.parse(line);
202
+ } catch (error) {
203
+ finish(() => {
204
+ reject(/* @__PURE__ */ new Error(`Invalid response from Herdr: ${error instanceof Error ? error.message : "Unknown error"}`));
205
+ });
206
+ return;
207
+ }
208
+ if (response.id !== id) {
209
+ finish(() => {
210
+ reject(/* @__PURE__ */ new Error(`Unexpected response ID from Herdr: ${response.id}`));
211
+ });
212
+ return;
213
+ }
214
+ if (response.error) {
215
+ finish(() => {
216
+ reject(/* @__PURE__ */ new Error(`Herdr ${response.error?.code}: ${response.error?.message}`));
217
+ });
218
+ return;
219
+ }
220
+ finish(() => {
221
+ resolve(response.result);
222
+ });
223
+ });
224
+ socket.on("error", (error) => {
225
+ finish(() => {
226
+ reject(/* @__PURE__ */ new Error(`Failed to communicate with Herdr: ${error.message}`));
227
+ });
228
+ });
229
+ socket.on("timeout", () => {
230
+ if (settled) return;
231
+ settled = true;
232
+ socket.destroy();
233
+ reject(/* @__PURE__ */ new Error(`Timed out waiting for Herdr response after ${HERDR_REQUEST_TIMEOUT_MS}ms`));
234
+ });
235
+ socket.on("end", () => {
236
+ if (!settled) finish(() => {
237
+ reject(/* @__PURE__ */ new Error("Herdr closed the socket before sending a response"));
238
+ });
239
+ });
240
+ });
241
+ }
242
+ function getStorageNamespace() {
243
+ return `herdr.session_${createHash("sha256").update(getSocketPath()).digest("hex").slice(0, 16)}`;
244
+ }
245
+ function targetPaneKey(paneId) {
246
+ return `${getStorageNamespace()}.targetPane.pane_${paneId}`;
247
+ }
248
+ function editorPaneKey(paneId) {
249
+ return `${getStorageNamespace()}.editorPane.pane_${paneId}`;
250
+ }
251
+ async function getCurrentPaneId$3() {
252
+ const paneId = process.env.HERDR_PANE_ID?.trim() || process.env.HERDR_ACTIVE_PANE_ID?.trim();
253
+ if (!paneId) throw new Error("HERDR_PANE_ID or HERDR_ACTIVE_PANE_ID is not set");
254
+ return paneId;
255
+ }
256
+ async function checkPaneExists$3(paneId) {
257
+ try {
258
+ await sendRequest("pane.get", { pane_id: paneId });
259
+ return true;
260
+ } catch (error) {
261
+ logger$14.debug("checkPaneExists failed: {error}", { error });
262
+ return false;
263
+ }
264
+ }
265
+ async function focusPane$3(paneId) {
266
+ await sendRequest("pane.focus", { pane_id: paneId });
267
+ }
268
+ function calculateEditorSplitRatio(paneHeight, editorPaneRows) {
269
+ if (!Number.isInteger(paneHeight) || paneHeight <= 1) throw new Error(`Invalid Herdr pane height: ${paneHeight}`);
270
+ if (!Number.isInteger(editorPaneRows) || editorPaneRows <= 0) throw new Error(`Invalid Herdr editor pane rows: ${editorPaneRows}`);
271
+ return Math.min(.9, Math.max(.1, 1 - editorPaneRows / paneHeight));
272
+ }
273
+ async function splitEditorPane(targetPaneId, editorPaneRows, cwd, env = {}) {
274
+ const paneHeight = ((await sendRequest("pane.layout", { pane_id: targetPaneId })).layout?.panes?.find((pane) => pane.pane_id === targetPaneId))?.rect?.height;
275
+ if (typeof paneHeight !== "number") throw new Error(`Herdr pane.layout did not return a height for pane ${targetPaneId}`);
276
+ const params = {
277
+ target_pane_id: targetPaneId,
278
+ direction: "down",
279
+ ratio: calculateEditorSplitRatio(paneHeight, editorPaneRows),
280
+ focus: true,
281
+ env
282
+ };
283
+ if (cwd?.trim()) params.cwd = cwd;
284
+ const editorPaneId = (await sendRequest("pane.split", params)).pane?.pane_id;
285
+ if (typeof editorPaneId !== "string" || editorPaneId === "") throw new Error("Herdr pane.split did not return the new pane ID");
286
+ return editorPaneId;
287
+ }
288
+ function quoteShellWord$1(value) {
289
+ return `'${value.replace(/'/g, "'\\''")}'`;
290
+ }
291
+ async function runCommandInPane(paneId, argv) {
292
+ if (argv.length === 0) throw new Error("Cannot run an empty command in a Herdr pane");
293
+ await sendRequest("pane.send_input", {
294
+ pane_id: paneId,
295
+ text: `exec ${argv.map(quoteShellWord$1).join(" ")}`,
296
+ keys: ["Enter"]
297
+ });
298
+ }
299
+ async function closePane(paneId) {
300
+ await sendRequest("pane.close", { pane_id: paneId });
301
+ }
302
+ async function inputToHerdrPane(paneId, content) {
303
+ const chunks = splitByByteSize(content, HERDR_SEND_CHUNK_BYTES);
304
+ for (const chunk of chunks) await sendRequest("pane.send_text", {
305
+ pane_id: paneId,
306
+ text: chunk
307
+ });
308
+ logger$14.debug("Content sent to Herdr pane: {paneId} ({chunkCount} chunks)", {
309
+ paneId,
310
+ chunkCount: chunks.length
311
+ });
312
+ }
313
+ async function sendKeyToHerdrPane(paneId, key, delay = 1e3) {
314
+ await new Promise((resolve) => setTimeout(resolve, delay));
315
+ await sendRequest("pane.send_keys", {
316
+ pane_id: paneId,
317
+ keys: [key]
318
+ });
319
+ }
320
+ async function saveEditorPaneId$3(targetPaneId, editorPaneId) {
321
+ const data = conf.get(targetPaneKey(targetPaneId));
322
+ const existing = typeof data === "object" && data !== null ? data : {};
323
+ conf.set(targetPaneKey(targetPaneId), {
324
+ ...existing,
325
+ editorPaneId
326
+ });
327
+ }
328
+ async function getEditorPaneId$3(targetPaneId) {
329
+ const data = conf.get(targetPaneKey(targetPaneId));
330
+ if (typeof data === "object" && data !== null && "editorPaneId" in data) return String(data.editorPaneId);
331
+ return "";
332
+ }
333
+ async function clearEditorPaneId$3(targetPaneId, expectedEditorPaneId) {
334
+ const editorPaneId = await getEditorPaneId$3(targetPaneId);
335
+ if (expectedEditorPaneId && editorPaneId !== expectedEditorPaneId) {
336
+ conf.delete(editorPaneKey(expectedEditorPaneId));
337
+ return;
338
+ }
339
+ const data = conf.get(targetPaneKey(targetPaneId));
340
+ if (typeof data === "object" && data !== null && "editorPaneId" in data) {
341
+ const { editorPaneId: _, ...remaining } = data;
342
+ if (Object.keys(remaining).length === 0) conf.delete(targetPaneKey(targetPaneId));
343
+ else conf.set(targetPaneKey(targetPaneId), remaining);
344
+ }
345
+ if (editorPaneId) conf.delete(editorPaneKey(editorPaneId));
346
+ }
347
+ async function markAsEditorPane$3(editorPaneId, targetPaneIds) {
348
+ const uniqueTargetPaneIds = [...new Set(targetPaneIds)];
349
+ conf.set(editorPaneKey(editorPaneId), { targetPaneIds: uniqueTargetPaneIds });
350
+ for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId$3(targetPaneId, editorPaneId);
351
+ }
352
+ async function getTargetPaneIds$3(editorPaneId) {
353
+ const data = conf.get(editorPaneKey(editorPaneId));
354
+ if (typeof data === "object" && data !== null && "targetPaneIds" in data) {
355
+ const targetPaneIds = data.targetPaneIds;
356
+ if (Array.isArray(targetPaneIds)) return targetPaneIds.map((id) => String(id));
357
+ }
358
+ return [];
359
+ }
360
+ function isEditorPaneFromConf$1(paneId) {
361
+ return conf.has(editorPaneKey(paneId));
362
+ }
363
+ async function resumeEditorPane(targetPaneId) {
364
+ const currentPaneId = await getCurrentPaneId$3();
365
+ const editorPaneId = isEditorPaneFromConf$1(currentPaneId) ? currentPaneId : isEditorPaneFromConf$1(targetPaneId) ? targetPaneId : "";
366
+ 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);
370
+ return true;
371
+ }
372
+ return false;
373
+ }
374
+ const registeredEditorPaneId = await getEditorPaneId$3(targetPaneId);
375
+ if (registeredEditorPaneId === "") return false;
376
+ if (!await checkPaneExists$3(registeredEditorPaneId)) {
377
+ await clearEditorPaneId$3(targetPaneId);
378
+ return false;
379
+ }
380
+ await focusPane$3(registeredEditorPaneId);
381
+ return true;
382
+ }
383
+ async function appendToQuoteText$1(paneId, content) {
384
+ const data = conf.get(targetPaneKey(paneId));
385
+ const existing = typeof data === "object" && data !== null ? data : {};
386
+ const existingQuoteText = "quote_text" in existing ? String(existing.quote_text) : "";
387
+ const quoteText = existingQuoteText.trim() !== "" ? `${existingQuoteText}\n\n${content}` : content;
388
+ conf.set(targetPaneKey(paneId), {
389
+ ...existing,
390
+ quote_text: quoteText
391
+ });
392
+ }
393
+ async function getQuoteText$1(paneId) {
394
+ const data = conf.get(targetPaneKey(paneId));
395
+ if (typeof data === "object" && data !== null && "quote_text" in data) return String(data.quote_text);
396
+ return "";
397
+ }
398
+ async function clearQuoteText$1(paneId) {
399
+ const data = conf.get(targetPaneKey(paneId));
400
+ if (typeof data === "object" && data !== null && "quote_text" in data) {
401
+ const { quote_text: _, ...remaining } = data;
402
+ if (Object.keys(remaining).length === 0) conf.delete(targetPaneKey(paneId));
403
+ else conf.set(targetPaneKey(paneId), remaining);
404
+ }
405
+ }
406
+
407
+ //#endregion
408
+ //#region src/modules/niwaterm.ts
409
+ const execAsync$2 = promisify(exec);
410
+ const logger$13 = getLogger(["editprompt", "niwaterm"]);
411
+ const VAR_EDITOR_PANE = "editprompt_editor_pane";
412
+ const VAR_IS_EDITOR = "editprompt_is_editor";
413
+ const VAR_TARGET_PANES = "editprompt_target_panes";
414
+ const VAR_QUOTE = "editprompt_quote";
415
+ function quoteShellWord(value) {
416
+ return `'${value.replace(/'/g, "'\\''")}'`;
417
+ }
418
+ async function getCurrentPaneId$2() {
419
+ const tabId = process.env.NIWATERM_TAB_ID?.trim();
420
+ if (!tabId) throw new Error("NIWATERM_TAB_ID is not set");
421
+ return tabId;
422
+ }
423
+ async function getTabVar(tabId, key) {
424
+ try {
425
+ const { stdout } = await execAsync$2(`niwa tab var get -t ${quoteShellWord(tabId)} ${quoteShellWord(key)}`);
426
+ return stdout.trim();
427
+ } catch (error) {
428
+ logger$13.debug("getTabVar failed: {error}", { error });
429
+ return "";
430
+ }
431
+ }
432
+ async function setTabVar(tabId, key, value) {
433
+ await execAsync$2(`niwa tab var set -t ${quoteShellWord(tabId)} ${quoteShellWord(key)} ${quoteShellWord(value)}`);
434
+ }
435
+ async function checkPaneExists$2(paneId) {
436
+ try {
437
+ await execAsync$2(`niwa tab var list -t ${quoteShellWord(paneId)}`);
438
+ return true;
439
+ } catch (error) {
440
+ logger$13.debug("checkPaneExists failed: {error}", { error });
441
+ return false;
442
+ }
443
+ }
444
+ async function focusPane$2(paneId) {
445
+ await execAsync$2(`niwa tab focus -t ${quoteShellWord(paneId)}`);
446
+ }
447
+ async function saveEditorPaneId$2(targetPaneId, editorPaneId) {
448
+ await setTabVar(targetPaneId, VAR_EDITOR_PANE, editorPaneId);
449
+ }
450
+ async function clearEditorPaneId$2(targetPaneId) {
451
+ await setTabVar(targetPaneId, VAR_EDITOR_PANE, "");
452
+ }
453
+ async function getEditorPaneId$2(targetPaneId) {
454
+ return getTabVar(targetPaneId, VAR_EDITOR_PANE);
455
+ }
456
+ async function markAsEditorPane$2(editorPaneId, targetPaneIds) {
457
+ const uniqueTargetPaneIds = [...new Set(targetPaneIds)];
458
+ await setTabVar(editorPaneId, VAR_IS_EDITOR, "1");
459
+ await setTabVar(editorPaneId, VAR_TARGET_PANES, uniqueTargetPaneIds.join(","));
460
+ for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId$2(targetPaneId, editorPaneId);
461
+ }
462
+ async function getTargetPaneIds$2(editorPaneId) {
463
+ const value = await getTabVar(editorPaneId, VAR_TARGET_PANES);
464
+ if (value === "") return [];
465
+ return value.split(",").map((id) => id.trim());
466
+ }
467
+ async function isEditorPane$1(paneId) {
468
+ return await getTabVar(paneId, VAR_IS_EDITOR) === "1";
469
+ }
470
+ async function getQuoteVariableContent$1(paneId) {
471
+ return getTabVar(paneId, VAR_QUOTE);
472
+ }
473
+ async function appendToQuoteVariable$1(paneId, content) {
474
+ const existingContent = await getQuoteVariableContent$1(paneId);
475
+ await setTabVar(paneId, VAR_QUOTE, existingContent.trim() !== "" ? `${existingContent}\n${content}` : content);
476
+ }
477
+ async function clearQuoteVariable$1(paneId) {
478
+ await setTabVar(paneId, VAR_QUOTE, "");
479
+ }
480
+ async function sendKeyToNiwatermPane(paneId, key, delay = 1e3) {
481
+ await new Promise((resolve) => setTimeout(resolve, delay));
482
+ await execAsync$2(`niwa tab send-keys -t ${quoteShellWord(paneId)} ${quoteShellWord(key)}`);
483
+ }
484
+ async function inputToNiwatermPane(paneId, content) {
485
+ const chunks = splitByByteSize(content, NIWATERM_SEND_CHUNK_BYTES);
486
+ 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))", {
488
+ paneId,
489
+ chunks: chunks.length
490
+ });
491
+ }
492
+
114
493
  //#endregion
115
494
  //#region src/modules/tmux.ts
116
495
  const execAsync$1 = promisify(exec);
117
- const logger$11 = getLogger(["editprompt", "tmux"]);
496
+ const logger$12 = getLogger(["editprompt", "tmux"]);
118
497
  async function getCurrentPaneId$1() {
119
498
  const envPaneId = process.env.TMUX_PANE?.trim();
120
499
  if (envPaneId) return envPaneId;
@@ -196,20 +575,15 @@ async function inputToTmuxPane(paneId, content) {
196
575
  await execAsync$1(`tmux if-shell -t '${paneId}' '[ "#{pane_in_mode}" = "1" ]' "copy-mode -q -t '${paneId}'"`);
197
576
  const chunks = splitByByteSize(content, TMUX_SEND_CHUNK_BYTES);
198
577
  for (const chunk of chunks) await execAsync$1(`tmux send-keys -t '${paneId}' -- '${chunk.replace(/'/g, "'\\''")}'`);
199
- logger$11.debug("Content sent to tmux pane: {paneId} ({chunks} chunk(s))", {
578
+ logger$12.debug("Content sent to tmux pane: {paneId} ({chunks} chunk(s))", {
200
579
  paneId,
201
580
  chunks: chunks.length
202
581
  });
203
582
  }
204
583
 
205
- //#endregion
206
- //#region src/modules/conf.ts
207
- const projectName = process.env.NODE_ENV === "test" ? "editprompt-test" : "editprompt";
208
- const conf = new Conf({ projectName });
209
-
210
584
  //#endregion
211
585
  //#region src/modules/wezterm.ts
212
- const logger$10 = getLogger(["editprompt", "wezterm"]);
586
+ const logger$11 = getLogger(["editprompt", "wezterm"]);
213
587
  const execAsync = promisify(exec);
214
588
  async function getCurrentPaneId() {
215
589
  try {
@@ -217,26 +591,26 @@ async function getCurrentPaneId() {
217
591
  const activePane = JSON.parse(stdout).find((pane) => pane.is_active === true);
218
592
  return String(activePane?.pane_id);
219
593
  } catch (error) {
220
- logger$10.debug("getCurrentPaneId failed: {error}", { error });
594
+ logger$11.debug("getCurrentPaneId failed: {error}", { error });
221
595
  return "";
222
596
  }
223
597
  }
224
598
  async function checkPaneExists(paneId) {
225
599
  try {
226
600
  const { stdout } = await execAsync("wezterm cli list --format json");
227
- logger$10.debug("wezterm cli list output: {stdout}", { stdout });
601
+ logger$11.debug("wezterm cli list output: {stdout}", { stdout });
228
602
  return JSON.parse(stdout).some((pane) => String(pane.pane_id) === paneId);
229
603
  } catch (error) {
230
- logger$10.debug("checkPaneExists failed: {error}", { error });
604
+ logger$11.debug("checkPaneExists failed: {error}", { error });
231
605
  return false;
232
606
  }
233
607
  }
234
608
  async function saveEditorPaneId(targetPaneId, editorPaneId) {
235
- logger$10.debug("Saving editor pane ID to conf key: wezterm.targetPane.pane_{targetPaneId}", { targetPaneId });
609
+ logger$11.debug("Saving editor pane ID to conf key: wezterm.targetPane.pane_{targetPaneId}", { targetPaneId });
236
610
  try {
237
611
  conf.set(`wezterm.targetPane.pane_${targetPaneId}`, { editorPaneId });
238
612
  } catch (error) {
239
- logger$10.debug("saveEditorPaneId failed: {error}", { error });
613
+ logger$11.debug("saveEditorPaneId failed: {error}", { error });
240
614
  }
241
615
  }
242
616
  async function getEditorPaneId(targetPaneId) {
@@ -245,7 +619,7 @@ async function getEditorPaneId(targetPaneId) {
245
619
  if (typeof data === "object" && data !== null && "editorPaneId" in data) return String(data.editorPaneId);
246
620
  return "";
247
621
  } catch (error) {
248
- logger$10.debug("getEditorPaneId failed: {error}", { error });
622
+ logger$11.debug("getEditorPaneId failed: {error}", { error });
249
623
  return "";
250
624
  }
251
625
  }
@@ -255,7 +629,7 @@ async function clearEditorPaneId(targetPaneId) {
255
629
  conf.delete(`wezterm.targetPane.pane_${targetPaneId}`);
256
630
  if (editorPaneId) conf.delete(`wezterm.editorPane.pane_${editorPaneId}`);
257
631
  } catch (error) {
258
- logger$10.debug("clearEditorPaneId failed: {error}", { error });
632
+ logger$11.debug("clearEditorPaneId failed: {error}", { error });
259
633
  }
260
634
  }
261
635
  async function focusPane(paneId) {
@@ -267,7 +641,7 @@ async function markAsEditorPane(editorPaneId, targetPaneIds) {
267
641
  conf.set(`wezterm.editorPane.pane_${editorPaneId}`, { targetPaneIds: uniqueTargetPaneIds });
268
642
  for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId(targetPaneId, editorPaneId);
269
643
  } catch (error) {
270
- logger$10.debug("markAsEditorPane failed: {error}", { error });
644
+ logger$11.debug("markAsEditorPane failed: {error}", { error });
271
645
  }
272
646
  }
273
647
  async function getTargetPaneIds(editorPaneId) {
@@ -279,7 +653,7 @@ async function getTargetPaneIds(editorPaneId) {
279
653
  }
280
654
  return [];
281
655
  } catch (error) {
282
- logger$10.debug("getTargetPaneIds failed: {error}", { error });
656
+ logger$11.debug("getTargetPaneIds failed: {error}", { error });
283
657
  return [];
284
658
  }
285
659
  }
@@ -287,7 +661,7 @@ function isEditorPaneFromConf(paneId) {
287
661
  try {
288
662
  return conf.has(`wezterm.editorPane.pane_${paneId}`);
289
663
  } catch (error) {
290
- logger$10.debug("isEditorPaneFromConf failed: {error}", { error });
664
+ logger$11.debug("isEditorPaneFromConf failed: {error}", { error });
291
665
  return false;
292
666
  }
293
667
  }
@@ -305,7 +679,7 @@ async function appendToQuoteText(paneId, content) {
305
679
  } else newData = { quote_text: content };
306
680
  conf.set(`wezterm.targetPane.pane_${paneId}`, newData);
307
681
  } catch (error) {
308
- logger$10.debug("appendToQuoteText failed: {error}", { error });
682
+ logger$11.debug("appendToQuoteText failed: {error}", { error });
309
683
  }
310
684
  }
311
685
  async function getQuoteText(paneId) {
@@ -314,7 +688,7 @@ async function getQuoteText(paneId) {
314
688
  if (typeof data === "object" && data !== null && "quote_text" in data) return String(data.quote_text);
315
689
  return "";
316
690
  } catch (error) {
317
- logger$10.debug("getQuoteText failed: {error}", { error });
691
+ logger$11.debug("getQuoteText failed: {error}", { error });
318
692
  return "";
319
693
  }
320
694
  }
@@ -323,7 +697,7 @@ async function clearQuoteText(paneId) {
323
697
  const key = `wezterm.targetPane.pane_${paneId}.quote_text`;
324
698
  if (conf.has(key)) conf.delete(key);
325
699
  } catch (error) {
326
- logger$10.debug("clearQuoteText failed: {error}", { error });
700
+ logger$11.debug("clearQuoteText failed: {error}", { error });
327
701
  }
328
702
  }
329
703
  async function sendKeyToWeztermPane(paneId, key, delay = 1e3) {
@@ -333,7 +707,7 @@ async function sendKeyToWeztermPane(paneId, key, delay = 1e3) {
333
707
  async function inputToWeztermPane(paneId, content) {
334
708
  const chunks = splitByByteSize(content, WEZTERM_SEND_CHUNK_BYTES);
335
709
  for (const chunk of chunks) await execAsync(`wezterm cli send-text --no-paste --pane-id '${paneId}' -- '${chunk.replace(/'/g, "'\\''")}'`);
336
- logger$10.debug("Content sent to wezterm pane: {paneId} ({chunks} chunk(s))", {
710
+ logger$11.debug("Content sent to wezterm pane: {paneId} ({chunks} chunk(s))", {
337
711
  paneId,
338
712
  chunks: chunks.length
339
713
  });
@@ -477,87 +851,40 @@ function processQuoteText(text, options) {
477
851
  }
478
852
 
479
853
  //#endregion
480
- //#region src/modes/common.ts
481
- const logger$9 = getLogger(["editprompt", "delivery"]);
854
+ //#region src/utils/mux.ts
855
+ const SUPPORTED_MUXES = [
856
+ "tmux",
857
+ "wezterm",
858
+ "herdr",
859
+ "niwaterm"
860
+ ];
482
861
  function isMuxType(value) {
483
- return value === "tmux" || value === "wezterm";
862
+ return value === "tmux" || value === "wezterm" || value === "herdr" || value === "niwaterm";
484
863
  }
485
- const SUPPORTED_MUXES = ["tmux", "wezterm"];
486
- async function copyToClipboard(content) {
487
- await clipboardy.write(content);
864
+ function hasValue(value) {
865
+ return typeof value === "string" && value.trim() !== "";
488
866
  }
489
- async function inputContentToPane(content, mux, targetPaneId) {
490
- if (mux === "wezterm") await inputToWeztermPane(targetPaneId, content);
491
- else await inputToTmuxPane(targetPaneId, content);
867
+ function isHerdrEnvironment(env = process.env) {
868
+ return hasValue(env.HERDR_SOCKET_PATH) && (hasValue(env.HERDR_PANE_ID) || hasValue(env.HERDR_ACTIVE_PANE_ID));
492
869
  }
493
- async function focusFirstSuccessPane(mux, targetPanes, failedPanes) {
494
- const firstSuccessPane = targetPanes.find((p) => !failedPanes.includes(p));
495
- if (firstSuccessPane) if (mux === "tmux") await focusPane$1(firstSuccessPane);
496
- else await focusPane(firstSuccessPane);
870
+ function isNiwatermEnvironment(env = process.env) {
871
+ return hasValue(env.NIWATERM_TAB_ID);
497
872
  }
498
- async function handleContentDelivery(content, mux, targetPanes) {
499
- if (!content) return {
500
- successCount: 0,
501
- totalCount: 0,
502
- allSuccess: true,
503
- allFailed: false,
504
- failedPanes: []
505
- };
506
- if (targetPanes.length === 0) {
507
- try {
508
- await copyToClipboard(content);
509
- logger$9.info("Content copied to clipboard.");
510
- } catch (error) {
511
- logger$9.warn(`Failed to copy to clipboard: ${error instanceof Error ? error.message : "Unknown error"}`);
512
- }
513
- return {
514
- successCount: 0,
515
- totalCount: 0,
516
- allSuccess: true,
517
- allFailed: false,
518
- failedPanes: []
519
- };
520
- }
521
- const results = [];
522
- for (const targetPane of targetPanes) try {
523
- await inputContentToPane(content, mux, targetPane);
524
- results.push({
525
- pane: targetPane,
526
- success: true
527
- });
528
- } catch (error) {
529
- logger$9.warn(`Failed to send to pane ${targetPane}: ${error instanceof Error ? error.message : "Unknown error"}`);
530
- results.push({
531
- pane: targetPane,
532
- success: false
533
- });
873
+ function resolveMux(explicitMux, env = process.env) {
874
+ const muxValue = explicitMux || env.EDITPROMPT_MUX || (isHerdrEnvironment(env) ? "herdr" : isNiwatermEnvironment(env) ? "niwaterm" : "tmux");
875
+ if (!isMuxType(muxValue)) {
876
+ const displayValue = typeof muxValue === "string" ? muxValue : `<${typeof muxValue}>`;
877
+ throw new Error(`Invalid multiplexer type '${displayValue}'. Supported values: ${SUPPORTED_MUXES.join(", ")}`);
534
878
  }
535
- const successCount = results.filter((r) => r.success).length;
536
- const failedPanes = results.filter((r) => !r.success).map((r) => r.pane);
537
- const allSuccess = successCount === targetPanes.length;
538
- const allFailed = successCount === 0;
539
- if (allSuccess) logger$9.info("Content sent successfully to all panes!");
540
- else if (allFailed) {
541
- logger$9.error("All target panes failed to receive content.");
542
- logger$9.info("Falling back to clipboard...");
543
- await copyToClipboard(content);
544
- logger$9.info("Content copied to clipboard.");
545
- } else logger$9.warn(`Content sent to ${successCount}/${targetPanes.length} panes. Failed panes: ${failedPanes.join(", ")}`);
546
- return {
547
- successCount,
548
- totalCount: targetPanes.length,
549
- allSuccess,
550
- allFailed,
551
- failedPanes
552
- };
879
+ return muxValue;
553
880
  }
554
881
 
555
882
  //#endregion
556
883
  //#region src/modes/args.ts
557
- const logger$8 = getLogger(["editprompt"]);
884
+ const logger$10 = getLogger(["editprompt"]);
558
885
  const ARG_MUX = {
559
886
  short: "m",
560
- description: "Multiplexer type (tmux or wezterm, default: tmux)",
887
+ description: "Multiplexer type (tmux, wezterm, herdr, or niwaterm; auto-detects Herdr and niwaterm, otherwise tmux)",
561
888
  type: "string"
562
889
  };
563
890
  const ARG_TARGET_PANE_SINGLE = {
@@ -603,17 +930,23 @@ const ARG_VERBOSE = {
603
930
  description: "Enable debug-level log output",
604
931
  type: "boolean"
605
932
  };
933
+ const ARG_ENV = {
934
+ short: "E",
935
+ description: "Environment variables to set (e.g., KEY=VALUE)",
936
+ type: "string",
937
+ multiple: true
938
+ };
606
939
  function validateMux(value) {
607
- const muxValue = value || "tmux";
608
- if (!isMuxType(muxValue)) {
609
- logger$8.error(`Invalid multiplexer type '${muxValue}'. Supported values: ${SUPPORTED_MUXES.join(", ")}`);
940
+ try {
941
+ return resolveMux(value);
942
+ } catch (error) {
943
+ logger$10.error(error instanceof Error ? error.message : String(error));
610
944
  process.exit(1);
611
945
  }
612
- return muxValue;
613
946
  }
614
947
  function validateTargetPane(value, commandName) {
615
948
  if (!value || typeof value !== "string") {
616
- logger$8.error(`--target-pane is required for ${commandName} command`);
949
+ logger$10.error(`--target-pane is required for ${commandName} command`);
617
950
  process.exit(1);
618
951
  }
619
952
  return value;
@@ -626,7 +959,7 @@ function normalizeTargetPanes(value) {
626
959
 
627
960
  //#endregion
628
961
  //#region src/modes/collect.ts
629
- const logger$7 = getLogger(["editprompt", "collect"]);
962
+ const logger$9 = getLogger(["editprompt", "collect"]);
630
963
  const SUPPORTED_OUTPUTS = ["buffer", "stdout"];
631
964
  async function readStdin() {
632
965
  return new Promise((resolve, reject) => {
@@ -650,7 +983,7 @@ function normalizeCollectOutputs(value) {
650
983
  const uniqueOutputs = [...new Set(outputs)];
651
984
  const invalid = uniqueOutputs.filter((v) => !SUPPORTED_OUTPUTS.includes(v));
652
985
  if (invalid.length > 0) {
653
- logger$7.error(`Invalid output(s) '${invalid.join(", ")}'. Supported values: ${SUPPORTED_OUTPUTS.join(", ")}`);
986
+ logger$9.error(`Invalid output(s) '${invalid.join(", ")}'. Supported values: ${SUPPORTED_OUTPUTS.join(", ")}`);
654
987
  process.exit(1);
655
988
  }
656
989
  return uniqueOutputs;
@@ -661,12 +994,13 @@ async function runCollectMode(mux, targetPaneId, rawContent, outputs = ["buffer"
661
994
  if (rawContent !== void 0) selection = rawContent;
662
995
  else selection = await readStdin();
663
996
  const processedText = processQuoteText(selection, { withQuote });
664
- for (const output of outputs) if (output === "buffer") {
665
- if (mux === "tmux") await appendToQuoteVariable(targetPaneId, processedText);
666
- else if (mux === "wezterm") await appendToQuoteText(targetPaneId, processedText);
667
- } else if (output === "stdout") process.stdout.write(processedText);
997
+ for (const output of outputs) if (output === "buffer") if (mux === "tmux") await appendToQuoteVariable(targetPaneId, processedText);
998
+ else if (mux === "wezterm") await appendToQuoteText(targetPaneId, processedText);
999
+ else if (mux === "niwaterm") await appendToQuoteVariable$1(targetPaneId, processedText);
1000
+ else await appendToQuoteText$1(targetPaneId, processedText);
1001
+ else if (output === "stdout") process.stdout.write(processedText);
668
1002
  } catch (error) {
669
- logger$7.error(`${error instanceof Error ? error.message : "Unknown error"}`);
1003
+ logger$9.error(`${error instanceof Error ? error.message : "Unknown error"}`);
670
1004
  process.exit(1);
671
1005
  }
672
1006
  }
@@ -693,10 +1027,10 @@ const collectCommand = define({
693
1027
  const outputs = normalizeCollectOutputs(ctx.values.output);
694
1028
  const withQuote = !ctx.values["no-quote"];
695
1029
  let rawContent;
696
- if (mux === "wezterm") {
1030
+ if (mux === "wezterm" || mux === "herdr" || mux === "niwaterm") {
697
1031
  rawContent = extractRawContent(ctx.rest, ctx.positionals);
698
1032
  if (rawContent === void 0) {
699
- logger$7.error("Text content is required for collect mode with wezterm. Use: editprompt collect --mux wezterm --target-pane <id> -- \"<text>\"");
1033
+ logger$9.error(`Text content is required for collect mode with ${mux}. Use: editprompt collect --mux ${mux} --target-pane <id> -- "<text>"`);
700
1034
  process.exit(1);
701
1035
  }
702
1036
  }
@@ -706,11 +1040,8 @@ const collectCommand = define({
706
1040
 
707
1041
  //#endregion
708
1042
  //#region src/utils/sendConfig.ts
709
- const VALID_MUX_TYPES = ["tmux", "wezterm"];
710
1043
  function readSendConfig() {
711
- const muxValue = process.env.EDITPROMPT_MUX || "tmux";
712
- if (!VALID_MUX_TYPES.includes(muxValue)) throw new Error(`Invalid EDITPROMPT_MUX value: ${muxValue}. Must be one of: ${VALID_MUX_TYPES.join(", ")}`);
713
- const mux = muxValue;
1044
+ const mux = resolveMux();
714
1045
  const alwaysCopy = process.env.EDITPROMPT_ALWAYS_COPY === "1";
715
1046
  const delayValue = process.env.EDITPROMPT_SEND_KEY_DELAY;
716
1047
  const parsedDelay = delayValue ? Number.parseInt(delayValue, 10) : NaN;
@@ -723,7 +1054,7 @@ function readSendConfig() {
723
1054
 
724
1055
  //#endregion
725
1056
  //#region src/modes/dump.ts
726
- const logger$6 = getLogger(["editprompt", "dump"]);
1057
+ const logger$8 = getLogger(["editprompt", "dump"]);
727
1058
  async function runDumpMode() {
728
1059
  try {
729
1060
  const config = readSendConfig();
@@ -732,19 +1063,27 @@ async function runDumpMode() {
732
1063
  if (config.mux === "tmux") {
733
1064
  currentPaneId = await getCurrentPaneId$1();
734
1065
  isEditor = await isEditorPane(currentPaneId);
735
- } else {
1066
+ } else if (config.mux === "wezterm") {
736
1067
  currentPaneId = await getCurrentPaneId();
737
1068
  isEditor = isEditorPaneFromConf(currentPaneId);
1069
+ } else if (config.mux === "niwaterm") {
1070
+ currentPaneId = await getCurrentPaneId$2();
1071
+ isEditor = await isEditorPane$1(currentPaneId);
1072
+ } else {
1073
+ currentPaneId = await getCurrentPaneId$3();
1074
+ isEditor = isEditorPaneFromConf$1(currentPaneId);
738
1075
  }
739
1076
  if (!isEditor) {
740
- logger$6.error("Current pane is not an editor pane");
1077
+ logger$8.error("Current pane is not an editor pane");
741
1078
  process.exit(1);
742
1079
  }
743
1080
  let targetPanes;
744
1081
  if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
745
- else targetPanes = await getTargetPaneIds(currentPaneId);
1082
+ 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);
746
1085
  if (targetPanes.length === 0) {
747
- logger$6.error("No target panes registered for this editor pane");
1086
+ logger$8.error("No target panes registered for this editor pane");
748
1087
  process.exit(1);
749
1088
  }
750
1089
  const quoteContents = [];
@@ -753,9 +1092,15 @@ async function runDumpMode() {
753
1092
  if (config.mux === "tmux") {
754
1093
  content = await getQuoteVariableContent(targetPane);
755
1094
  await clearQuoteVariable(targetPane);
756
- } else {
1095
+ } else if (config.mux === "wezterm") {
757
1096
  content = await getQuoteText(targetPane);
758
1097
  await clearQuoteText(targetPane);
1098
+ } else if (config.mux === "niwaterm") {
1099
+ content = await getQuoteVariableContent$1(targetPane);
1100
+ await clearQuoteVariable$1(targetPane);
1101
+ } else {
1102
+ content = await getQuoteText$1(targetPane);
1103
+ await clearQuoteText$1(targetPane);
759
1104
  }
760
1105
  if (content.trim() !== "") quoteContents.push(content);
761
1106
  }
@@ -763,7 +1108,7 @@ async function runDumpMode() {
763
1108
  process.stdout.write(combinedContent.replace(/\n{3,}$/, "\n\n"));
764
1109
  process.exit(0);
765
1110
  } catch (error) {
766
- logger$6.error(`${error instanceof Error ? error.message : "Unknown error"}`);
1111
+ logger$8.error(`${error instanceof Error ? error.message : "Unknown error"}`);
767
1112
  process.exit(1);
768
1113
  }
769
1114
  }
@@ -793,13 +1138,89 @@ function processContent(content) {
793
1138
  return processed;
794
1139
  }
795
1140
 
1141
+ //#endregion
1142
+ //#region src/modes/common.ts
1143
+ const logger$7 = getLogger(["editprompt", "delivery"]);
1144
+ async function copyToClipboard(content) {
1145
+ await clipboardy.write(content);
1146
+ }
1147
+ async function inputContentToPane(content, mux, targetPaneId) {
1148
+ if (mux === "wezterm") await inputToWeztermPane(targetPaneId, content);
1149
+ else if (mux === "herdr") await inputToHerdrPane(targetPaneId, content);
1150
+ else if (mux === "niwaterm") await inputToNiwatermPane(targetPaneId, content);
1151
+ else await inputToTmuxPane(targetPaneId, content);
1152
+ }
1153
+ async function focusFirstSuccessPane(mux, targetPanes, failedPanes) {
1154
+ const firstSuccessPane = targetPanes.find((p) => !failedPanes.includes(p));
1155
+ if (firstSuccessPane) if (mux === "tmux") await focusPane$1(firstSuccessPane);
1156
+ else if (mux === "wezterm") await focusPane(firstSuccessPane);
1157
+ else if (mux === "niwaterm") await focusPane$2(firstSuccessPane);
1158
+ else await focusPane$3(firstSuccessPane);
1159
+ }
1160
+ async function handleContentDelivery(content, mux, targetPanes) {
1161
+ if (!content) return {
1162
+ successCount: 0,
1163
+ totalCount: 0,
1164
+ allSuccess: true,
1165
+ allFailed: false,
1166
+ failedPanes: []
1167
+ };
1168
+ if (targetPanes.length === 0) {
1169
+ try {
1170
+ await copyToClipboard(content);
1171
+ logger$7.info("Content copied to clipboard.");
1172
+ } catch (error) {
1173
+ logger$7.warn(`Failed to copy to clipboard: ${error instanceof Error ? error.message : "Unknown error"}`);
1174
+ }
1175
+ return {
1176
+ successCount: 0,
1177
+ totalCount: 0,
1178
+ allSuccess: true,
1179
+ allFailed: false,
1180
+ failedPanes: []
1181
+ };
1182
+ }
1183
+ const results = [];
1184
+ for (const targetPane of targetPanes) try {
1185
+ await inputContentToPane(content, mux, targetPane);
1186
+ results.push({
1187
+ pane: targetPane,
1188
+ success: true
1189
+ });
1190
+ } catch (error) {
1191
+ logger$7.warn(`Failed to send to pane ${targetPane}: ${error instanceof Error ? error.message : "Unknown error"}`);
1192
+ results.push({
1193
+ pane: targetPane,
1194
+ success: false
1195
+ });
1196
+ }
1197
+ const successCount = results.filter((r) => r.success).length;
1198
+ const failedPanes = results.filter((r) => !r.success).map((r) => r.pane);
1199
+ const allSuccess = successCount === targetPanes.length;
1200
+ const allFailed = successCount === 0;
1201
+ if (allSuccess) logger$7.info("Content sent successfully to all panes!");
1202
+ else if (allFailed) {
1203
+ logger$7.error("All target panes failed to receive content.");
1204
+ logger$7.info("Falling back to clipboard...");
1205
+ await copyToClipboard(content);
1206
+ logger$7.info("Content copied to clipboard.");
1207
+ } else logger$7.warn(`Content sent to ${successCount}/${targetPanes.length} panes. Failed panes: ${failedPanes.join(", ")}`);
1208
+ return {
1209
+ successCount,
1210
+ totalCount: targetPanes.length,
1211
+ allSuccess,
1212
+ allFailed,
1213
+ failedPanes
1214
+ };
1215
+ }
1216
+
796
1217
  //#endregion
797
1218
  //#region src/modes/input.ts
798
- const logger$5 = getLogger(["editprompt", "input"]);
1219
+ const logger$6 = getLogger(["editprompt", "input"]);
799
1220
  async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
800
1221
  const content = processContent(rawContent);
801
1222
  if (!content) {
802
- logger$5.info("No content to send. Exiting.");
1223
+ logger$6.info("No content to send. Exiting.");
803
1224
  return;
804
1225
  }
805
1226
  const config = readSendConfig();
@@ -808,44 +1229,58 @@ async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
808
1229
  if (config.mux === "tmux") {
809
1230
  currentPaneId = await getCurrentPaneId$1();
810
1231
  isEditor = await isEditorPane(currentPaneId);
811
- } else {
1232
+ } else if (config.mux === "wezterm") {
812
1233
  currentPaneId = await getCurrentPaneId();
813
1234
  isEditor = isEditorPaneFromConf(currentPaneId);
1235
+ } else if (config.mux === "niwaterm") {
1236
+ currentPaneId = await getCurrentPaneId$2();
1237
+ isEditor = await isEditorPane$1(currentPaneId);
1238
+ } else {
1239
+ currentPaneId = await getCurrentPaneId$3();
1240
+ isEditor = isEditorPaneFromConf$1(currentPaneId);
814
1241
  }
815
1242
  if (!isEditor) {
816
- logger$5.error("Current pane is not an editor pane");
1243
+ logger$6.error("Current pane is not an editor pane");
817
1244
  process.exit(1);
818
1245
  }
819
1246
  let targetPanes;
820
1247
  if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
821
- else targetPanes = await getTargetPaneIds(currentPaneId);
1248
+ 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);
822
1251
  if (targetPanes.length === 0) {
823
- logger$5.error("No target panes registered for this editor pane");
1252
+ logger$6.error("No target panes registered for this editor pane");
824
1253
  process.exit(1);
825
1254
  }
826
1255
  if (autoSend) {
827
- const key = sendKey || (config.mux === "wezterm" ? "\\r" : "C-m");
1256
+ const key = sendKey || (config.mux === "wezterm" ? "\\r" : config.mux === "herdr" ? "enter" : "C-m");
828
1257
  const delay = /\.(png|webp|avif|jpe?g|gif)\b/i.test(content) ? sendKeyDelay ?? 1e3 : 200;
829
1258
  let successCount = 0;
830
1259
  for (const targetPane of targetPanes) try {
831
1260
  if (config.mux === "wezterm") {
832
1261
  await inputToWeztermPane(targetPane, content);
833
1262
  await sendKeyToWeztermPane(targetPane, key, delay);
1263
+ } else if (config.mux === "herdr") {
1264
+ await inputToHerdrPane(targetPane, content);
1265
+ await sendKeyToHerdrPane(targetPane, key, delay);
1266
+ } else if (config.mux === "niwaterm") {
1267
+ await inputToNiwatermPane(targetPane, content);
1268
+ await sendKeyToNiwatermPane(targetPane, key, delay);
834
1269
  } else {
835
1270
  await inputToTmuxPane(targetPane, content);
836
1271
  await sendKeyToTmuxPane(targetPane, key, delay);
837
1272
  }
838
1273
  successCount++;
839
1274
  } catch (error) {
840
- logger$5.error(`Failed to send to pane ${targetPane}: ${error instanceof Error ? error.message : "Unknown error"}`);
1275
+ logger$6.error(`Failed to send to pane ${targetPane}: ${error instanceof Error ? error.message : "Unknown error"}`);
841
1276
  }
842
1277
  if (config.alwaysCopy) {
843
1278
  await copyToClipboard(content);
844
- logger$5.info("Also copied to clipboard.");
1279
+ logger$6.info("Also copied to clipboard.");
845
1280
  }
846
- if (successCount > 0) logger$5.info("Content sent and submitted successfully!");
1281
+ if (successCount > 0) logger$6.info("Content sent and submitted successfully!");
847
1282
  else {
848
- logger$5.error("All target panes failed to receive content");
1283
+ logger$6.error("All target panes failed to receive content");
849
1284
  process.exit(1);
850
1285
  }
851
1286
  return;
@@ -854,12 +1289,12 @@ async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
854
1289
  const result = await handleContentDelivery(content, config.mux, targetPanes);
855
1290
  if (config.alwaysCopy && !result.allFailed) {
856
1291
  await copyToClipboard(content);
857
- logger$5.info("Also copied to clipboard.");
1292
+ logger$6.info("Also copied to clipboard.");
858
1293
  }
859
1294
  if (result.successCount > 0) await focusFirstSuccessPane(config.mux, targetPanes, result.failedPanes);
860
1295
  if (result.allFailed) process.exit(1);
861
1296
  } catch (error) {
862
- logger$5.error(`${error instanceof Error ? error.message : "Unknown error"}`);
1297
+ logger$6.error(`${error instanceof Error ? error.message : "Unknown error"}`);
863
1298
  process.exit(1);
864
1299
  }
865
1300
  }
@@ -887,13 +1322,13 @@ const inputCommand = define({
887
1322
  });
888
1323
  const rawContent = extractRawContent(ctx.rest, ctx.positionals);
889
1324
  if (rawContent === void 0) {
890
- logger$5.error("Content is required for input command");
891
- logger$5.error("Usage: editprompt input \"your content\"");
892
- logger$5.error(" or: editprompt input -- \"your content\"");
1325
+ logger$6.error("Content is required for input command");
1326
+ logger$6.error("Usage: editprompt input \"your content\"");
1327
+ logger$6.error(" or: editprompt input -- \"your content\"");
893
1328
  process.exit(1);
894
1329
  }
895
1330
  if (ctx.values["send-key"] && !ctx.values["auto-send"]) {
896
- logger$5.error("--send-key requires --auto-send to be enabled");
1331
+ logger$6.error("--send-key requires --auto-send to be enabled");
897
1332
  process.exit(1);
898
1333
  }
899
1334
  const config = readSendConfig();
@@ -987,8 +1422,9 @@ async function openEditorAndGetContent(editorOption, envVars, sendConfig) {
987
1422
 
988
1423
  //#endregion
989
1424
  //#region src/modes/openEditor.ts
990
- const logger$4 = getLogger(["editprompt", "open"]);
1425
+ const logger$5 = getLogger(["editprompt", "open"]);
991
1426
  async function runOpenEditorMode(options) {
1427
+ let herdrEditorPaneId;
992
1428
  if (options.targetPanes.length > 0 && options.mux === "tmux") try {
993
1429
  await markAsEditorPane$1(await getCurrentPaneId$1(), options.targetPanes);
994
1430
  } catch {}
@@ -996,16 +1432,25 @@ async function runOpenEditorMode(options) {
996
1432
  const currentPaneId = await getCurrentPaneId();
997
1433
  await markAsEditorPane(currentPaneId, options.targetPanes);
998
1434
  } catch {}
1435
+ else if (options.targetPanes.length > 0 && options.mux === "herdr") try {
1436
+ const currentPaneId = await getCurrentPaneId$3();
1437
+ herdrEditorPaneId = currentPaneId;
1438
+ await markAsEditorPane$3(currentPaneId, options.targetPanes);
1439
+ } catch {}
1440
+ else if (options.targetPanes.length > 0 && options.mux === "niwaterm") try {
1441
+ const currentPaneId = await getCurrentPaneId$2();
1442
+ await markAsEditorPane$2(currentPaneId, options.targetPanes);
1443
+ } catch {}
999
1444
  try {
1000
1445
  const sendConfig = {
1001
1446
  mux: options.mux,
1002
1447
  alwaysCopy: options.alwaysCopy,
1003
1448
  sendKeyDelay: Number.parseInt(process.env.EDITPROMPT_SEND_KEY_DELAY || "", 10) || 1e3
1004
1449
  };
1005
- logger$4.info("Opening editor...");
1450
+ logger$5.info("Opening editor...");
1006
1451
  const content = await openEditorAndGetContent(options.editor, options.env, sendConfig);
1007
1452
  if (!content) {
1008
- logger$4.info("No content entered. Exiting.");
1453
+ logger$5.info("No content entered. Exiting.");
1009
1454
  return;
1010
1455
  }
1011
1456
  try {
@@ -1014,12 +1459,12 @@ async function runOpenEditorMode(options) {
1014
1459
  console.log(content);
1015
1460
  if (options.alwaysCopy && !result.allFailed) {
1016
1461
  await copyToClipboard(content);
1017
- logger$4.info("Also copied to clipboard.");
1462
+ logger$5.info("Also copied to clipboard.");
1018
1463
  }
1019
1464
  if (options.targetPanes.length > 0 && result.successCount > 0) await focusFirstSuccessPane(options.mux, options.targetPanes, result.failedPanes);
1020
1465
  if (!result.allSuccess) process.exit(1);
1021
1466
  } catch (error) {
1022
- logger$4.error(`${error instanceof Error ? error.message : "Unknown error"}`);
1467
+ logger$5.error(`${error instanceof Error ? error.message : "Unknown error"}`);
1023
1468
  process.exit(1);
1024
1469
  }
1025
1470
  } finally {
@@ -1029,6 +1474,12 @@ async function runOpenEditorMode(options) {
1029
1474
  else if (options.targetPanes.length > 0 && options.mux === "wezterm") try {
1030
1475
  for (const targetPane of options.targetPanes) await clearEditorPaneId(targetPane);
1031
1476
  } catch {}
1477
+ else if (options.targetPanes.length > 0 && options.mux === "herdr") try {
1478
+ for (const targetPane of options.targetPanes) await clearEditorPaneId$3(targetPane, herdrEditorPaneId);
1479
+ } catch {}
1480
+ else if (options.targetPanes.length > 0 && options.mux === "niwaterm") try {
1481
+ for (const targetPane of options.targetPanes) await clearEditorPaneId$2(targetPane);
1482
+ } catch {}
1032
1483
  }
1033
1484
  }
1034
1485
  const openCommand = define({
@@ -1042,12 +1493,7 @@ const openCommand = define({
1042
1493
  "log-file": ARG_LOG_FILE,
1043
1494
  quiet: ARG_QUIET,
1044
1495
  verbose: ARG_VERBOSE,
1045
- env: {
1046
- short: "E",
1047
- description: "Environment variables to set (e.g., KEY=VALUE)",
1048
- type: "string",
1049
- multiple: true
1050
- }
1496
+ env: ARG_ENV
1051
1497
  },
1052
1498
  async run(ctx) {
1053
1499
  setupLogger({
@@ -1067,10 +1513,10 @@ const openCommand = define({
1067
1513
 
1068
1514
  //#endregion
1069
1515
  //#region src/modes/register.ts
1070
- const logger$3 = getLogger(["editprompt", "register"]);
1516
+ const logger$4 = getLogger(["editprompt", "register"]);
1071
1517
  async function runRegisterMode(options) {
1072
1518
  if (options.targetPanes.length === 0) {
1073
- logger$3.error("--target-pane is required for register command");
1519
+ logger$4.error("--target-pane is required for register command");
1074
1520
  process.exit(1);
1075
1521
  }
1076
1522
  let editorPaneId;
@@ -1078,29 +1524,45 @@ async function runRegisterMode(options) {
1078
1524
  else if (options.mux === "tmux") {
1079
1525
  editorPaneId = await getCurrentPaneId$1();
1080
1526
  if (!await isEditorPane(editorPaneId)) {
1081
- logger$3.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1527
+ logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1082
1528
  process.exit(1);
1083
1529
  }
1084
1530
  } else if (options.mux === "wezterm") {
1085
1531
  editorPaneId = await getCurrentPaneId();
1086
1532
  if (!isEditorPaneFromConf(editorPaneId)) {
1087
- logger$3.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1533
+ logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1534
+ process.exit(1);
1535
+ }
1536
+ } else if (options.mux === "herdr") {
1537
+ editorPaneId = await getCurrentPaneId$3();
1538
+ if (!isEditorPaneFromConf$1(editorPaneId)) {
1539
+ logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1540
+ process.exit(1);
1541
+ }
1542
+ } else if (options.mux === "niwaterm") {
1543
+ editorPaneId = await getCurrentPaneId$2();
1544
+ if (!await isEditorPane$1(editorPaneId)) {
1545
+ logger$4.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1088
1546
  process.exit(1);
1089
1547
  }
1090
1548
  } else {
1091
- logger$3.error("Unsupported multiplexer");
1549
+ logger$4.error("Unsupported multiplexer");
1092
1550
  process.exit(1);
1093
1551
  }
1094
1552
  try {
1095
1553
  let existingPanes = [];
1096
1554
  if (options.mux === "tmux") existingPanes = await getTargetPaneIds$1(editorPaneId);
1097
1555
  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);
1098
1558
  const mergedTargetPanes = [...new Set([...existingPanes, ...options.targetPanes])];
1099
1559
  if (options.mux === "tmux") await markAsEditorPane$1(editorPaneId, mergedTargetPanes);
1100
1560
  else if (options.mux === "wezterm") await markAsEditorPane(editorPaneId, mergedTargetPanes);
1101
- logger$3.info(`Editor pane ${editorPaneId} registered with target panes: ${mergedTargetPanes.join(", ")}`);
1561
+ else if (options.mux === "herdr") await markAsEditorPane$3(editorPaneId, mergedTargetPanes);
1562
+ else if (options.mux === "niwaterm") await markAsEditorPane$2(editorPaneId, mergedTargetPanes);
1563
+ logger$4.info(`Editor pane ${editorPaneId} registered with target panes: ${mergedTargetPanes.join(", ")}`);
1102
1564
  } catch (error) {
1103
- logger$3.error(`${error instanceof Error ? error.message : "Unknown error"}`);
1565
+ logger$4.error(`${error instanceof Error ? error.message : "Unknown error"}`);
1104
1566
  process.exit(1);
1105
1567
  }
1106
1568
  }
@@ -1135,15 +1597,65 @@ const registerCommand = define({
1135
1597
 
1136
1598
  //#endregion
1137
1599
  //#region src/modes/resume.ts
1138
- const logger$2 = getLogger(["editprompt", "resume"]);
1600
+ const logger$3 = getLogger(["editprompt", "resume"]);
1139
1601
  async function runResumeMode(targetPane, mux) {
1602
+ if (mux === "herdr") try {
1603
+ process.exit(await resumeEditorPane(targetPane) ? 0 : 1);
1604
+ } catch (error) {
1605
+ logger$3.debug("Herdr resume failed: {error}", { error });
1606
+ process.exit(1);
1607
+ }
1608
+ if (mux === "niwaterm") {
1609
+ const currentPaneId = await getCurrentPaneId$2();
1610
+ if (await isEditorPane$1(currentPaneId)) {
1611
+ logger$3.debug("Current pane is an editor pane");
1612
+ const originalTargetPaneIds = await getTargetPaneIds$2(currentPaneId);
1613
+ if (originalTargetPaneIds.length === 0) {
1614
+ logger$3.debug("No target pane IDs found for editor pane");
1615
+ process.exit(1);
1616
+ }
1617
+ let focused = false;
1618
+ for (const paneId of originalTargetPaneIds) if (await checkPaneExists$2(paneId)) {
1619
+ await focusPane$2(paneId);
1620
+ focused = true;
1621
+ break;
1622
+ }
1623
+ if (!focused) {
1624
+ logger$3.debug("All target panes do not exist");
1625
+ process.exit(1);
1626
+ }
1627
+ process.exit(0);
1628
+ }
1629
+ logger$3.debug("Current pane is not an editor pane");
1630
+ const editorPaneId = await getEditorPaneId$2(targetPane);
1631
+ logger$3.debug("niwaterm editorPaneId: {editorPaneId}", { editorPaneId });
1632
+ if (editorPaneId === "") {
1633
+ logger$3.debug("Editor pane ID not found");
1634
+ process.exit(1);
1635
+ }
1636
+ if (!await checkPaneExists$2(editorPaneId)) {
1637
+ logger$3.debug("Editor pane does not exist");
1638
+ await clearEditorPaneId$2(targetPane);
1639
+ process.exit(1);
1640
+ }
1641
+ try {
1642
+ await focusPane$2(editorPaneId);
1643
+ process.exit(0);
1644
+ } catch (error) {
1645
+ logger$3.debug("Can't focus editorPaneId: {editorPaneId}, error: {error}", {
1646
+ editorPaneId,
1647
+ error
1648
+ });
1649
+ process.exit(1);
1650
+ }
1651
+ }
1140
1652
  if (mux === "wezterm") {
1141
1653
  const currentPaneId = await getCurrentPaneId();
1142
1654
  if (isEditorPaneFromConf(currentPaneId)) {
1143
- logger$2.debug("Current pane is an editor pane");
1655
+ logger$3.debug("Current pane is an editor pane");
1144
1656
  const originalTargetPaneIds = await getTargetPaneIds(currentPaneId);
1145
1657
  if (originalTargetPaneIds.length === 0) {
1146
- logger$2.debug("No target pane IDs found for editor pane");
1658
+ logger$3.debug("No target pane IDs found for editor pane");
1147
1659
  process.exit(1);
1148
1660
  }
1149
1661
  let focused = false;
@@ -1153,20 +1665,20 @@ async function runResumeMode(targetPane, mux) {
1153
1665
  break;
1154
1666
  }
1155
1667
  if (!focused) {
1156
- logger$2.debug("All target panes do not exist");
1668
+ logger$3.debug("All target panes do not exist");
1157
1669
  process.exit(1);
1158
1670
  }
1159
1671
  process.exit(0);
1160
1672
  }
1161
- logger$2.debug("Current pane is not an editor pane");
1673
+ logger$3.debug("Current pane is not an editor pane");
1162
1674
  const editorPaneId = await getEditorPaneId(targetPane);
1163
- logger$2.debug("wezterm editorPaneId: {editorPaneId}", { editorPaneId });
1675
+ logger$3.debug("wezterm editorPaneId: {editorPaneId}", { editorPaneId });
1164
1676
  if (editorPaneId === "") {
1165
- logger$2.debug("Editor pane ID not found");
1677
+ logger$3.debug("Editor pane ID not found");
1166
1678
  process.exit(1);
1167
1679
  }
1168
1680
  if (!await checkPaneExists(editorPaneId)) {
1169
- logger$2.debug("Editor pane does not exist");
1681
+ logger$3.debug("Editor pane does not exist");
1170
1682
  await clearEditorPaneId(targetPane);
1171
1683
  process.exit(1);
1172
1684
  }
@@ -1174,7 +1686,7 @@ async function runResumeMode(targetPane, mux) {
1174
1686
  await focusPane(editorPaneId);
1175
1687
  process.exit(0);
1176
1688
  } catch (error) {
1177
- logger$2.debug("Can't focus editorPaneId: {editorPaneId}, error: {error}", {
1689
+ logger$3.debug("Can't focus editorPaneId: {editorPaneId}, error: {error}", {
1178
1690
  editorPaneId,
1179
1691
  error
1180
1692
  });
@@ -1225,10 +1737,10 @@ const resumeCommand = define({
1225
1737
 
1226
1738
  //#endregion
1227
1739
  //#region src/modes/press.ts
1228
- const logger$1 = getLogger(["editprompt", "press"]);
1740
+ const logger$2 = getLogger(["editprompt", "press"]);
1229
1741
  async function runPressMode(key, delay = 0) {
1230
1742
  if (!key) {
1231
- logger$1.error("Key is required");
1743
+ logger$2.error("Key is required");
1232
1744
  process.exit(1);
1233
1745
  }
1234
1746
  const config = readSendConfig();
@@ -1237,32 +1749,42 @@ async function runPressMode(key, delay = 0) {
1237
1749
  if (config.mux === "tmux") {
1238
1750
  currentPaneId = await getCurrentPaneId$1();
1239
1751
  isEditor = await isEditorPane(currentPaneId);
1240
- } else {
1752
+ } else if (config.mux === "wezterm") {
1241
1753
  currentPaneId = await getCurrentPaneId();
1242
1754
  isEditor = isEditorPaneFromConf(currentPaneId);
1755
+ } else if (config.mux === "niwaterm") {
1756
+ currentPaneId = await getCurrentPaneId$2();
1757
+ isEditor = await isEditorPane$1(currentPaneId);
1758
+ } else {
1759
+ currentPaneId = await getCurrentPaneId$3();
1760
+ isEditor = isEditorPaneFromConf$1(currentPaneId);
1243
1761
  }
1244
1762
  if (!isEditor) {
1245
- logger$1.error("Current pane is not an editor pane");
1763
+ logger$2.error("Current pane is not an editor pane");
1246
1764
  process.exit(1);
1247
1765
  }
1248
1766
  let targetPanes;
1249
1767
  if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
1250
- else targetPanes = await getTargetPaneIds(currentPaneId);
1768
+ 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);
1251
1771
  if (targetPanes.length === 0) {
1252
- logger$1.error("No target panes registered for this editor pane");
1772
+ logger$2.error("No target panes registered for this editor pane");
1253
1773
  process.exit(1);
1254
1774
  }
1255
1775
  let successCount = 0;
1256
1776
  for (const targetPane of targetPanes) try {
1257
1777
  if (config.mux === "wezterm") await sendKeyToWeztermPane(targetPane, key, delay);
1778
+ else if (config.mux === "herdr") await sendKeyToHerdrPane(targetPane, key, delay);
1779
+ else if (config.mux === "niwaterm") await sendKeyToNiwatermPane(targetPane, key, delay);
1258
1780
  else await sendKeyToTmuxPane(targetPane, key, delay);
1259
1781
  successCount++;
1260
1782
  } catch (error) {
1261
- logger$1.warn(`Failed to send key to pane ${targetPane}: ${error instanceof Error ? error.message : "Unknown error"}`);
1783
+ logger$2.warn(`Failed to send key to pane ${targetPane}: ${error instanceof Error ? error.message : "Unknown error"}`);
1262
1784
  }
1263
- if (successCount > 0) logger$1.info("Key sent successfully!");
1785
+ if (successCount > 0) logger$2.info("Key sent successfully!");
1264
1786
  else {
1265
- logger$1.error("All target panes failed to receive key");
1787
+ logger$2.error("All target panes failed to receive key");
1266
1788
  process.exit(1);
1267
1789
  }
1268
1790
  }
@@ -1286,8 +1808,8 @@ const pressCommand = define({
1286
1808
  });
1287
1809
  const key = extractRawContent(ctx.rest, ctx.positionals);
1288
1810
  if (key === void 0 || key === "") {
1289
- logger$1.error("Key is required for press command");
1290
- logger$1.error("Usage: editprompt press -- \"Tab\"");
1811
+ logger$2.error("Key is required for press command");
1812
+ logger$2.error("Usage: editprompt press -- \"Tab\"");
1291
1813
  process.exit(1);
1292
1814
  }
1293
1815
  await runPressMode(key, ctx.values.delay ? Number(ctx.values.delay) : 0);
@@ -1296,9 +1818,9 @@ const pressCommand = define({
1296
1818
 
1297
1819
  //#endregion
1298
1820
  //#region src/modes/stash.ts
1299
- const logger = getLogger(["editprompt", "stash"]);
1821
+ const logger$1 = getLogger(["editprompt", "stash"]);
1300
1822
  function getStashKey(mux, targetPaneId) {
1301
- return `${mux}.targetPane.pane_${targetPaneId}.stash`;
1823
+ return `${mux === "herdr" ? getStorageNamespace() : mux}.targetPane.pane_${targetPaneId}.stash`;
1302
1824
  }
1303
1825
  async function pushStash(mux, targetPaneId, content) {
1304
1826
  const key = (/* @__PURE__ */ new Date()).toISOString();
@@ -1345,19 +1867,27 @@ async function getTargetPaneForStash() {
1345
1867
  if (config.mux === "tmux") {
1346
1868
  currentPaneId = await getCurrentPaneId$1();
1347
1869
  isEditor = await isEditorPane(currentPaneId);
1348
- } else {
1870
+ } else if (config.mux === "wezterm") {
1349
1871
  currentPaneId = await getCurrentPaneId();
1350
1872
  isEditor = isEditorPaneFromConf(currentPaneId);
1873
+ } else if (config.mux === "niwaterm") {
1874
+ currentPaneId = await getCurrentPaneId$2();
1875
+ isEditor = await isEditorPane$1(currentPaneId);
1876
+ } else {
1877
+ currentPaneId = await getCurrentPaneId$3();
1878
+ isEditor = isEditorPaneFromConf$1(currentPaneId);
1351
1879
  }
1352
1880
  if (!isEditor) {
1353
- logger.error("Current pane is not an editor pane");
1881
+ logger$1.error("Current pane is not an editor pane");
1354
1882
  process.exit(1);
1355
1883
  }
1356
1884
  let targetPanes;
1357
1885
  if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
1358
- else targetPanes = await getTargetPaneIds(currentPaneId);
1886
+ 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);
1359
1889
  if (targetPanes.length === 0) {
1360
- logger.error("No target panes registered for this editor pane");
1890
+ logger$1.error("No target panes registered for this editor pane");
1361
1891
  process.exit(1);
1362
1892
  }
1363
1893
  return {
@@ -1368,13 +1898,13 @@ async function getTargetPaneForStash() {
1368
1898
  async function runPush(rest, positionals) {
1369
1899
  const rawContent = extractRawContent(rest, positionals);
1370
1900
  if (rawContent === void 0 || rawContent.trim() === "") {
1371
- logger.error("Content is required for stash push");
1372
- logger.error("Usage: editprompt stash push -- \"your content\"");
1901
+ logger$1.error("Content is required for stash push");
1902
+ logger$1.error("Usage: editprompt stash push -- \"your content\"");
1373
1903
  process.exit(1);
1374
1904
  }
1375
1905
  const { mux, targetPaneId } = await getTargetPaneForStash();
1376
1906
  const key = await pushStash(mux, targetPaneId, rawContent);
1377
- logger.info("Stashed with key: {key}", { key });
1907
+ logger$1.info("Stashed with key: {key}", { key });
1378
1908
  }
1379
1909
  async function runList() {
1380
1910
  const { mux, targetPaneId } = await getTargetPaneForStash();
@@ -1385,8 +1915,8 @@ async function runApply(key) {
1385
1915
  const { mux, targetPaneId } = await getTargetPaneForStash();
1386
1916
  const content = getStashContent(mux, targetPaneId, key);
1387
1917
  if (content === "") {
1388
- if (key) logger.error("No stash entry found with key: {key}", { key });
1389
- else logger.error("No stash entries found");
1918
+ if (key) logger$1.error("No stash entry found with key: {key}", { key });
1919
+ else logger$1.error("No stash entries found");
1390
1920
  process.exit(1);
1391
1921
  }
1392
1922
  process.stdout.write(content);
@@ -1394,18 +1924,18 @@ async function runApply(key) {
1394
1924
  async function runDrop(key) {
1395
1925
  const { mux, targetPaneId } = await getTargetPaneForStash();
1396
1926
  if (!dropStash(mux, targetPaneId, key)) {
1397
- if (key) logger.error("No stash entry found with key: {key}", { key });
1398
- else logger.error("No stash entries found");
1927
+ if (key) logger$1.error("No stash entry found with key: {key}", { key });
1928
+ else logger$1.error("No stash entries found");
1399
1929
  process.exit(1);
1400
1930
  }
1401
- logger.info("Stash entry dropped");
1931
+ logger$1.info("Stash entry dropped");
1402
1932
  }
1403
1933
  async function runPop(key) {
1404
1934
  const { mux, targetPaneId } = await getTargetPaneForStash();
1405
1935
  const content = getStashContent(mux, targetPaneId, key);
1406
1936
  if (content === "") {
1407
- if (key) logger.error("No stash entry found with key: {key}", { key });
1408
- else logger.error("No stash entries found");
1937
+ if (key) logger$1.error("No stash entry found with key: {key}", { key });
1938
+ else logger$1.error("No stash entries found");
1409
1939
  process.exit(1);
1410
1940
  }
1411
1941
  process.stdout.write(content);
@@ -1469,14 +1999,107 @@ const stashCommand = define({
1469
1999
  await runPop(parseKeyOption(subArgs));
1470
2000
  break;
1471
2001
  default:
1472
- logger.error("Unknown subcommand '{subcommand}'", { subcommand });
1473
- logger.error("");
2002
+ logger$1.error("Unknown subcommand '{subcommand}'", { subcommand });
2003
+ logger$1.error("");
1474
2004
  showHelp();
1475
2005
  process.exit(1);
1476
2006
  }
1477
2007
  }
1478
2008
  });
1479
2009
 
2010
+ //#endregion
2011
+ //#region src/modes/toggle.ts
2012
+ const logger = getLogger(["editprompt", "toggle"]);
2013
+ const DEFAULT_EDITOR_PANE_ROWS = 12;
2014
+ function buildOpenEditorArgv(options) {
2015
+ const invocation = options.invocation ?? {
2016
+ execPath: process.execPath,
2017
+ scriptPath: process.argv[1]
2018
+ };
2019
+ if (!invocation.scriptPath) throw new Error("Cannot determine the editprompt entrypoint");
2020
+ const argv = [
2021
+ invocation.execPath,
2022
+ invocation.scriptPath,
2023
+ "open",
2024
+ "--mux",
2025
+ options.mux,
2026
+ "--target-pane",
2027
+ options.targetPane
2028
+ ];
2029
+ if (options.editor) argv.push("--editor", options.editor);
2030
+ if (options.alwaysCopy) argv.push("--always-copy");
2031
+ for (const env of options.env ?? []) argv.push("--env", env);
2032
+ if (options.logFile) argv.push("--log-file", options.logFile);
2033
+ if (options.quiet) argv.push("--quiet");
2034
+ if (options.verbose) argv.push("--verbose");
2035
+ return argv;
2036
+ }
2037
+ async function runToggleMode(options, herdrApi = herdr_exports) {
2038
+ if (options.mux !== "herdr") throw new Error("toggle currently supports only the Herdr multiplexer");
2039
+ if (!Number.isInteger(options.paneRows) || options.paneRows <= 0) throw new Error("--pane-rows must be a positive integer");
2040
+ if (await herdrApi.resumeEditorPane(options.targetPane)) return;
2041
+ const launchEnv = {};
2042
+ if (process.env.NODE_NO_WARNINGS !== void 0) launchEnv.NODE_NO_WARNINGS = process.env.NODE_NO_WARNINGS;
2043
+ const editorPaneId = await herdrApi.splitEditorPane(options.targetPane, options.paneRows, process.env.HERDR_ACTIVE_PANE_CWD, launchEnv);
2044
+ try {
2045
+ await herdrApi.runCommandInPane(editorPaneId, buildOpenEditorArgv(options));
2046
+ } catch (error) {
2047
+ try {
2048
+ await herdrApi.closePane(editorPaneId);
2049
+ } catch (closeError) {
2050
+ logger.warn("Failed to close editor pane after launch failure: {error}", { error: closeError });
2051
+ }
2052
+ throw error;
2053
+ }
2054
+ }
2055
+ const toggleCommand = define({
2056
+ name: "toggle",
2057
+ description: "Open or focus an editor pane",
2058
+ args: {
2059
+ mux: ARG_MUX,
2060
+ "target-pane": ARG_TARGET_PANE_SINGLE,
2061
+ "pane-rows": {
2062
+ description: "Desired rows for the Herdr editor pane",
2063
+ type: "number",
2064
+ default: DEFAULT_EDITOR_PANE_ROWS
2065
+ },
2066
+ editor: ARG_EDITOR,
2067
+ "always-copy": ARG_ALWAYS_COPY,
2068
+ env: ARG_ENV,
2069
+ "log-file": ARG_LOG_FILE,
2070
+ quiet: ARG_QUIET,
2071
+ verbose: ARG_VERBOSE
2072
+ },
2073
+ async run(ctx) {
2074
+ const logFile = ctx.values["log-file"];
2075
+ const quiet = Boolean(ctx.values.quiet);
2076
+ const verbose = Boolean(ctx.values.verbose);
2077
+ setupLogger({
2078
+ quiet,
2079
+ verbose,
2080
+ logFile
2081
+ });
2082
+ const targetPane = validateTargetPane(ctx.values["target-pane"], "toggle");
2083
+ const mux = validateMux(ctx.values.mux);
2084
+ try {
2085
+ await runToggleMode({
2086
+ mux,
2087
+ targetPane,
2088
+ paneRows: Number(ctx.values["pane-rows"]),
2089
+ alwaysCopy: Boolean(ctx.values["always-copy"]),
2090
+ editor: ctx.values.editor,
2091
+ env: ctx.values.env,
2092
+ logFile,
2093
+ quiet,
2094
+ verbose
2095
+ });
2096
+ } catch (error) {
2097
+ logger.error(error instanceof Error ? error.message : String(error));
2098
+ process.exit(1);
2099
+ }
2100
+ }
2101
+ });
2102
+
1480
2103
  //#endregion
1481
2104
  //#region src/index.ts
1482
2105
  await cli(process.argv.slice(2), {
@@ -1505,6 +2128,7 @@ await cli(process.argv.slice(2), {
1505
2128
  open: openCommand,
1506
2129
  register: registerCommand,
1507
2130
  resume: resumeCommand,
2131
+ toggle: toggleCommand,
1508
2132
  input: inputCommand,
1509
2133
  press: pressCommand,
1510
2134
  collect: collectCommand,