editprompt 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +44 -5
  2. package/dist/index.js +329 -164
  3. package/package.json +34 -28
package/README.md CHANGED
@@ -19,7 +19,6 @@ A CLI tool that lets you write prompts for CLI tools using your favorite text ed
19
19
  - **💬 Quote and Reply**: Collect multiple text selections and reply to specific parts of AI responses
20
20
  - **📝 Multi-line Commands**: Complex SQL queries, JSON payloads, and structured prompts
21
21
 
22
-
23
22
  ## ✨ Features
24
23
 
25
24
  - 🖊️ **Editor Integration**: Use your preferred text editor to write prompts
@@ -29,7 +28,6 @@ A CLI tool that lets you write prompts for CLI tools using your favorite text ed
29
28
  - 📋 **Quote Buffering**: Collect text selections and send them as quoted replies
30
29
  - 📋 **Clipboard Fallback**: Automatically copies to clipboard if sending fails
31
30
 
32
-
33
31
  ## 📦 Installation
34
32
 
35
33
  ```bash
@@ -97,7 +95,6 @@ For replying to specific parts of AI responses:
97
95
 
98
96
  Perfect for addressing multiple points in long AI responses.
99
97
 
100
-
101
98
  ## ⚙️ Setup & Configuration
102
99
 
103
100
  ### Basic Setup
@@ -127,7 +124,6 @@ bind -n M-q run-shell '\
127
124
  "editprompt open --editor nvim --always-copy --target-pane #{pane_id}"'
128
125
  ```
129
126
 
130
-
131
127
  ### WezTerm Integration
132
128
 
133
129
  ```lua
@@ -173,7 +169,6 @@ bind -n M-q run-shell '\
173
169
 
174
170
  **Note:** The `-lc` flag ensures your shell loads the full login environment, making `editprompt` available in your PATH.
175
171
 
176
-
177
172
  ### Editor Integration (Send Without Closing)
178
173
 
179
174
  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.
@@ -197,6 +192,7 @@ editprompt input --auto-send --send-key "C-m" -- "your content here"
197
192
  This sends the content to the target pane (or clipboard) while keeping your editor open, so you can continue editing and send multiple times.
198
193
 
199
194
  **Options:**
195
+
200
196
  - `--auto-send`: Automatically sends the content and returns focus to your editor pane (requires multiplexer)
201
197
  - `--send-key <key>`: Customize the key to send after content (requires `--auto-send`)
202
198
  - tmux format: `Enter` (default), `C-a`, etc.
@@ -217,6 +213,7 @@ bind-key -T copy-mode-vi C-e { send-keys -X pipe "editprompt collect --target-pa
217
213
  ```
218
214
 
219
215
  **Usage:**
216
+
220
217
  1. Enter tmux copy mode (`prefix + [`)
221
218
  2. Select text using vi-mode keybindings
222
219
  3. Press `Ctrl-e` to add the selection as a quote
@@ -257,6 +254,7 @@ return {
257
254
  ```
258
255
 
259
256
  **Usage:**
257
+
260
258
  1. Select text in WezTerm (by dragging with mouse or using copy mode)
261
259
  2. Press `Ctrl-e` to add the selection as a quote
262
260
  3. Repeat to collect multiple quotes
@@ -273,6 +271,7 @@ editprompt dump
273
271
  This copies all collected quotes to the clipboard and clears the buffer, ready for your reply.
274
272
 
275
273
  **Complete workflow:**
274
+
276
275
  1. AI responds with multiple points
277
276
  2. Select each point in copy mode and press `Ctrl-e`
278
277
  3. Open your editor pane and run `editprompt dump`
@@ -280,6 +279,7 @@ This copies all collected quotes to the clipboard and clears the buffer, ready f
280
279
  5. Send to AI
281
280
 
282
281
  **How quote buffering works:**
282
+
283
283
  - **tmux**: Quotes are stored in pane variables, automatically cleaned up when the pane closes
284
284
  - **WezTerm**: Quotes are stored in a configuration file associated with the pane
285
285
  - Text is intelligently processed: removes common indentation, handles line breaks smartly
@@ -358,3 +358,42 @@ editprompt open --env NVIM_CONFIG=minimal
358
358
  #### Target Pane Environment Variable
359
359
 
360
360
  When using the send-without-closing feature or dump, editprompt sets `EDITPROMPT_TARGET_PANE` to the target pane ID. This is automatically used by `editprompt input` and `editprompt dump` commands.
361
+
362
+ ### Logging Options
363
+
364
+ editprompt uses structured logging via [LogTape](https://logtape.org/). The following flags are available on all subcommands:
365
+
366
+ | Flag | Description |
367
+ | ------------------- | ------------------------------------------ |
368
+ | `--quiet` / `-q` | Suppress all log output |
369
+ | `--verbose` / `-v` | Enable debug-level log output |
370
+ | `--log-file <path>` | Write logs to the specified file (appends) |
371
+
372
+ You can also configure logging via environment variables:
373
+
374
+ | Environment Variable | Description |
375
+ | ---------------------- | ---------------------------------------------------- |
376
+ | `EDITPROMPT_LOG_FILE` | Path to log file (same as `--log-file`) |
377
+ | `EDITPROMPT_LOG_LEVEL` | Log level (e.g. `debug`, `info`, `warning`, `error`) |
378
+
379
+ **Log level resolution priority:**
380
+
381
+ 1. `--quiet` → suppresses all logs
382
+ 2. `--verbose` → sets level to `debug`
383
+ 3. `EDITPROMPT_LOG_LEVEL` → uses the specified level
384
+ 4. Default: `info`
385
+
386
+ ### Send-Key Delay
387
+
388
+ When using `--auto-send` with `editprompt input`, a delay is inserted before sending the key to allow the target process to finish processing the content.
389
+
390
+ | Environment Variable | Description | Default |
391
+ | --------------------------- | -------------------------------------------- | ------- |
392
+ | `EDITPROMPT_SEND_KEY_DELAY` | Delay in milliseconds before sending the key | `1000` |
393
+
394
+ **Auto-detection:** editprompt automatically adjusts the delay based on content:
395
+
396
+ - **Content with image paths** → uses `EDITPROMPT_SEND_KEY_DELAY` (default: `1000` ms)
397
+ - **Content without image paths** → uses `200` ms
398
+
399
+ Supported image extensions: `.png`, `.webp`, `.avif`, `.jpg`/`.jpeg`, `.gif` (case-insensitive)
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import { configureSync, getAnsiColorFormatter, getLogger, getTextFormatter, parseLogLevel, withFilter } from "@logtape/logtape";
2
3
  import { cli, define } from "gunshi";
4
+ import { appendFileSync } from "node:fs";
3
5
  import { exec, spawn } from "node:child_process";
4
6
  import { promisify } from "node:util";
5
7
  import Conf from "conf";
@@ -8,23 +10,86 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
8
10
  import { tmpdir } from "node:os";
9
11
  import { join } from "node:path";
10
12
 
13
+ //#region src/modules/logger.ts
14
+ function resolveLogFilePath(options) {
15
+ if (options.logFile) return options.logFile;
16
+ const envPath = process.env.EDITPROMPT_LOG_FILE;
17
+ if (envPath) return envPath;
18
+ }
19
+ function resolveFileLogLevel(options) {
20
+ if (options.verbose) return "debug";
21
+ const envLevel = process.env.EDITPROMPT_LOG_LEVEL;
22
+ if (envLevel) return parseLogLevel(envLevel);
23
+ return "info";
24
+ }
25
+ function resolveLogLevel(options) {
26
+ if (options.quiet) return null;
27
+ if (options.verbose) return "debug";
28
+ const envLevel = process.env.EDITPROMPT_LOG_LEVEL;
29
+ if (envLevel) return parseLogLevel(envLevel);
30
+ return "info";
31
+ }
32
+ function setupLogger(options = {}) {
33
+ const stderrLevel = resolveLogLevel(options);
34
+ const stderrFormatter = getAnsiColorFormatter({
35
+ timestamp: "time-timezone",
36
+ level: "ABBR"
37
+ });
38
+ const rawStderrSink = (record) => {
39
+ process.stderr.write(stderrFormatter(record));
40
+ };
41
+ const sinks = { stderr: stderrLevel === null ? () => {} : withFilter(rawStderrSink, stderrLevel) };
42
+ const loggerSinks = ["stderr"];
43
+ const logFilePath = resolveLogFilePath(options);
44
+ const fileLogLevel = resolveFileLogLevel(options);
45
+ if (logFilePath) try {
46
+ appendFileSync(logFilePath, "");
47
+ const fileFormatter = getTextFormatter({
48
+ timestamp: "date-time-timezone",
49
+ level: "ABBR"
50
+ });
51
+ const rawFileSink = (record) => {
52
+ appendFileSync(logFilePath, fileFormatter(record));
53
+ };
54
+ sinks.file = withFilter(rawFileSink, fileLogLevel);
55
+ loggerSinks.push("file");
56
+ } catch {
57
+ process.stderr.write(`Warning: Cannot write to log file '${logFilePath}', continuing without file logging.\n`);
58
+ }
59
+ configureSync({
60
+ sinks,
61
+ loggers: [{
62
+ category: ["editprompt"],
63
+ sinks: loggerSinks,
64
+ lowestLevel: "trace"
65
+ }, {
66
+ category: ["logtape", "meta"],
67
+ lowestLevel: null
68
+ }]
69
+ });
70
+ }
71
+
72
+ //#endregion
11
73
  //#region package.json
12
- var version = "1.2.0";
74
+ var version = "1.3.0";
13
75
 
14
76
  //#endregion
15
77
  //#region src/modules/tmux.ts
16
78
  const execAsync$1 = promisify(exec);
17
- async function getCurrentPaneId() {
79
+ const logger$10 = getLogger(["editprompt", "tmux"]);
80
+ async function getCurrentPaneId$1() {
81
+ const envPaneId = process.env.TMUX_PANE?.trim();
82
+ if (envPaneId) return envPaneId;
18
83
  const { stdout } = await execAsync$1("tmux display-message -p \"#{pane_id}\"");
19
84
  return stdout.trim();
20
85
  }
21
86
  async function saveEditorPaneId$1(targetPaneId, editorPaneId) {
22
87
  await execAsync$1(`tmux set-option -pt '${targetPaneId}' @editprompt_editor_pane '${editorPaneId}'`);
23
88
  }
24
- async function clearEditorPaneId(targetPaneId) {
89
+ async function clearEditorPaneId$1(targetPaneId) {
25
90
  await execAsync$1(`tmux set-option -pt '${targetPaneId}' @editprompt_editor_pane ""`);
26
91
  }
27
- async function getEditorPaneId(targetPaneId) {
92
+ async function getEditorPaneId$1(targetPaneId) {
28
93
  try {
29
94
  const { stdout } = await execAsync$1(`tmux show -pt '${targetPaneId}' -v @editprompt_editor_pane`);
30
95
  return stdout.trim();
@@ -32,7 +97,7 @@ async function getEditorPaneId(targetPaneId) {
32
97
  return "";
33
98
  }
34
99
  }
35
- async function checkPaneExists(paneId) {
100
+ async function checkPaneExists$1(paneId) {
36
101
  try {
37
102
  const { stdout } = await execAsync$1("tmux list-panes -a -F \"#{pane_id}\"");
38
103
  return stdout.split("\n").map((id) => id.trim()).includes(paneId);
@@ -40,16 +105,16 @@ async function checkPaneExists(paneId) {
40
105
  return false;
41
106
  }
42
107
  }
43
- async function focusPane(paneId) {
108
+ async function focusPane$1(paneId) {
44
109
  await execAsync$1(`tmux select-pane -t '${paneId}'`);
45
110
  }
46
- async function markAsEditorPane(editorPaneId, targetPaneIds) {
111
+ async function markAsEditorPane$1(editorPaneId, targetPaneIds) {
47
112
  await execAsync$1(`tmux set-option -pt '${editorPaneId}' @editprompt_is_editor 1`);
48
113
  const uniqueTargetPaneIds = [...new Set(targetPaneIds)];
49
114
  await execAsync$1(`tmux set-option -pt '${editorPaneId}' @editprompt_target_panes '${uniqueTargetPaneIds.join(",")}'`);
50
115
  for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId$1(targetPaneId, editorPaneId);
51
116
  }
52
- async function getTargetPaneIds(editorPaneId) {
117
+ async function getTargetPaneIds$1(editorPaneId) {
53
118
  try {
54
119
  const { stdout } = await execAsync$1(`tmux show -pt '${editorPaneId}' -v @editprompt_target_panes`);
55
120
  const value = stdout.trim();
@@ -85,14 +150,14 @@ async function appendToQuoteVariable(paneId, content) {
85
150
  async function clearQuoteVariable(targetPaneId) {
86
151
  await execAsync$1(`tmux set-option -pt '${targetPaneId}' @editprompt_quote ""`);
87
152
  }
88
- async function sendKeyToTmuxPane(paneId, key) {
89
- await new Promise((resolve) => setTimeout(resolve, 100));
153
+ async function sendKeyToTmuxPane(paneId, key, delay = 1e3) {
154
+ await new Promise((resolve) => setTimeout(resolve, delay));
90
155
  await execAsync$1(`tmux send-keys -t '${paneId}' '${key}'`);
91
156
  }
92
157
  async function inputToTmuxPane(paneId, content) {
93
158
  await execAsync$1(`tmux if-shell -t '${paneId}' '[ "#{pane_in_mode}" = "1" ]' "copy-mode -q -t '${paneId}'"`);
94
159
  await execAsync$1(`tmux send-keys -t '${paneId}' -- '${content.replace(/'/g, "'\\''")}'`);
95
- console.log(`Content sent to tmux pane: ${paneId}`);
160
+ logger$10.debug("Content sent to tmux pane: {paneId}", { paneId });
96
161
  }
97
162
 
98
163
  //#endregion
@@ -102,67 +167,68 @@ const conf = new Conf({ projectName });
102
167
 
103
168
  //#endregion
104
169
  //#region src/modules/wezterm.ts
170
+ const logger$9 = getLogger(["editprompt", "wezterm"]);
105
171
  const execAsync = promisify(exec);
106
- async function getCurrentPaneId$1() {
172
+ async function getCurrentPaneId() {
107
173
  try {
108
174
  const { stdout } = await execAsync("wezterm cli list --format json");
109
175
  const activePane = JSON.parse(stdout).find((pane) => pane.is_active === true);
110
176
  return String(activePane?.pane_id);
111
177
  } catch (error) {
112
- console.log(error);
178
+ logger$9.debug("getCurrentPaneId failed: {error}", { error });
113
179
  return "";
114
180
  }
115
181
  }
116
- async function checkPaneExists$1(paneId) {
182
+ async function checkPaneExists(paneId) {
117
183
  try {
118
184
  const { stdout } = await execAsync("wezterm cli list --format json");
119
- console.log(stdout);
185
+ logger$9.debug("wezterm cli list output: {stdout}", { stdout });
120
186
  return JSON.parse(stdout).some((pane) => String(pane.pane_id) === paneId);
121
187
  } catch (error) {
122
- console.log(error);
188
+ logger$9.debug("checkPaneExists failed: {error}", { error });
123
189
  return false;
124
190
  }
125
191
  }
126
192
  async function saveEditorPaneId(targetPaneId, editorPaneId) {
127
- console.log(`wezterm.targetPane.pane_${targetPaneId}`);
193
+ logger$9.debug("Saving editor pane ID to conf key: wezterm.targetPane.pane_{targetPaneId}", { targetPaneId });
128
194
  try {
129
195
  conf.set(`wezterm.targetPane.pane_${targetPaneId}`, { editorPaneId });
130
196
  } catch (error) {
131
- console.log(error);
197
+ logger$9.debug("saveEditorPaneId failed: {error}", { error });
132
198
  }
133
199
  }
134
- async function getEditorPaneId$1(targetPaneId) {
200
+ async function getEditorPaneId(targetPaneId) {
135
201
  try {
136
202
  const data = conf.get(`wezterm.targetPane.pane_${targetPaneId}`);
137
203
  if (typeof data === "object" && data !== null && "editorPaneId" in data) return String(data.editorPaneId);
138
204
  return "";
139
205
  } catch (error) {
140
- console.log(error);
206
+ logger$9.debug("getEditorPaneId failed: {error}", { error });
141
207
  return "";
142
208
  }
143
209
  }
144
- async function clearEditorPaneId$1(targetPaneId) {
210
+ async function clearEditorPaneId(targetPaneId) {
145
211
  try {
146
- const editorPaneId = await getEditorPaneId$1(targetPaneId);
212
+ const editorPaneId = await getEditorPaneId(targetPaneId);
147
213
  conf.delete(`wezterm.targetPane.pane_${targetPaneId}`);
148
214
  if (editorPaneId) conf.delete(`wezterm.editorPane.pane_${editorPaneId}`);
149
215
  } catch (error) {
150
- console.log(error);
216
+ logger$9.debug("clearEditorPaneId failed: {error}", { error });
151
217
  }
152
218
  }
153
- async function focusPane$1(paneId) {
219
+ async function focusPane(paneId) {
154
220
  await execAsync(`wezterm cli activate-pane --pane-id '${paneId}'`);
155
221
  }
156
- async function markAsEditorPane$1(editorPaneId, targetPaneIds) {
222
+ async function markAsEditorPane(editorPaneId, targetPaneIds) {
157
223
  try {
158
224
  const uniqueTargetPaneIds = [...new Set(targetPaneIds)];
159
225
  conf.set(`wezterm.editorPane.pane_${editorPaneId}`, { targetPaneIds: uniqueTargetPaneIds });
160
226
  for (const targetPaneId of uniqueTargetPaneIds) await saveEditorPaneId(targetPaneId, editorPaneId);
161
227
  } catch (error) {
162
- console.log(error);
228
+ logger$9.debug("markAsEditorPane failed: {error}", { error });
163
229
  }
164
230
  }
165
- async function getTargetPaneIds$1(editorPaneId) {
231
+ async function getTargetPaneIds(editorPaneId) {
166
232
  try {
167
233
  const data = conf.get(`wezterm.editorPane.pane_${editorPaneId}`);
168
234
  if (typeof data === "object" && data !== null && "targetPaneIds" in data) {
@@ -171,7 +237,7 @@ async function getTargetPaneIds$1(editorPaneId) {
171
237
  }
172
238
  return [];
173
239
  } catch (error) {
174
- console.log(error);
240
+ logger$9.debug("getTargetPaneIds failed: {error}", { error });
175
241
  return [];
176
242
  }
177
243
  }
@@ -179,7 +245,7 @@ function isEditorPaneFromConf(paneId) {
179
245
  try {
180
246
  return conf.has(`wezterm.editorPane.pane_${paneId}`);
181
247
  } catch (error) {
182
- console.log(error);
248
+ logger$9.debug("isEditorPaneFromConf failed: {error}", { error });
183
249
  return false;
184
250
  }
185
251
  }
@@ -197,7 +263,7 @@ async function appendToQuoteText(paneId, content) {
197
263
  } else newData = { quote_text: content };
198
264
  conf.set(`wezterm.targetPane.pane_${paneId}`, newData);
199
265
  } catch (error) {
200
- console.log(error);
266
+ logger$9.debug("appendToQuoteText failed: {error}", { error });
201
267
  }
202
268
  }
203
269
  async function getQuoteText(paneId) {
@@ -206,7 +272,7 @@ async function getQuoteText(paneId) {
206
272
  if (typeof data === "object" && data !== null && "quote_text" in data) return String(data.quote_text);
207
273
  return "";
208
274
  } catch (error) {
209
- console.log(error);
275
+ logger$9.debug("getQuoteText failed: {error}", { error });
210
276
  return "";
211
277
  }
212
278
  }
@@ -215,15 +281,16 @@ async function clearQuoteText(paneId) {
215
281
  const key = `wezterm.targetPane.pane_${paneId}.quote_text`;
216
282
  if (conf.has(key)) conf.delete(key);
217
283
  } catch (error) {
218
- console.log(error);
284
+ logger$9.debug("clearQuoteText failed: {error}", { error });
219
285
  }
220
286
  }
221
- async function sendKeyToWeztermPane(paneId, key) {
287
+ async function sendKeyToWeztermPane(paneId, key, delay = 1e3) {
288
+ await new Promise((resolve) => setTimeout(resolve, delay));
222
289
  await execAsync(`wezterm cli send-text --no-paste --pane-id '${paneId}' $'${key}'`);
223
290
  }
224
291
  async function inputToWeztermPane(paneId, content) {
225
292
  await execAsync(`wezterm cli send-text --no-paste --pane-id '${paneId}' -- '${content.replace(/'/g, "'\\''")}'`);
226
- console.log(`Content sent to wezterm pane: ${paneId}`);
293
+ logger$9.debug("Content sent to wezterm pane: {paneId}", { paneId });
227
294
  }
228
295
 
229
296
  //#endregion
@@ -237,8 +304,12 @@ async function inputToWeztermPane(paneId, content) {
237
304
  * @returns Raw content string or undefined if no content provided
238
305
  */
239
306
  function extractRawContent(rest, positionals) {
240
- if (rest.length > 0) return rest.join(" ");
241
- return positionals[0];
307
+ if (rest.length > 0) {
308
+ const joined = rest.join(" ");
309
+ if (joined.trim() !== "") return joined;
310
+ }
311
+ const first = positionals[0];
312
+ if (first !== void 0 && first.trim() !== "") return first;
242
313
  }
243
314
 
244
315
  //#endregion
@@ -361,6 +432,7 @@ function processQuoteText(text, options) {
361
432
 
362
433
  //#endregion
363
434
  //#region src/modes/common.ts
435
+ const logger$8 = getLogger(["editprompt", "delivery"]);
364
436
  function isMuxType(value) {
365
437
  return value === "tmux" || value === "wezterm";
366
438
  }
@@ -374,8 +446,8 @@ async function inputContentToPane(content, mux, targetPaneId) {
374
446
  }
375
447
  async function focusFirstSuccessPane(mux, targetPanes, failedPanes) {
376
448
  const firstSuccessPane = targetPanes.find((p) => !failedPanes.includes(p));
377
- if (firstSuccessPane) if (mux === "tmux") await focusPane(firstSuccessPane);
378
- else await focusPane$1(firstSuccessPane);
449
+ if (firstSuccessPane) if (mux === "tmux") await focusPane$1(firstSuccessPane);
450
+ else await focusPane(firstSuccessPane);
379
451
  }
380
452
  async function handleContentDelivery(content, mux, targetPanes) {
381
453
  if (!content) return {
@@ -388,9 +460,9 @@ async function handleContentDelivery(content, mux, targetPanes) {
388
460
  if (targetPanes.length === 0) {
389
461
  try {
390
462
  await copyToClipboard(content);
391
- console.log("Content copied to clipboard.");
463
+ logger$8.info("Content copied to clipboard.");
392
464
  } catch (error) {
393
- console.log(`Failed to copy to clipboard: ${error instanceof Error ? error.message : "Unknown error"}`);
465
+ logger$8.warn(`Failed to copy to clipboard: ${error instanceof Error ? error.message : "Unknown error"}`);
394
466
  }
395
467
  return {
396
468
  successCount: 0,
@@ -408,7 +480,7 @@ async function handleContentDelivery(content, mux, targetPanes) {
408
480
  success: true
409
481
  });
410
482
  } catch (error) {
411
- console.log(`Failed to send to pane ${targetPane}: ${error instanceof Error ? error.message : "Unknown error"}`);
483
+ logger$8.warn(`Failed to send to pane ${targetPane}: ${error instanceof Error ? error.message : "Unknown error"}`);
412
484
  results.push({
413
485
  pane: targetPane,
414
486
  success: false
@@ -418,13 +490,13 @@ async function handleContentDelivery(content, mux, targetPanes) {
418
490
  const failedPanes = results.filter((r) => !r.success).map((r) => r.pane);
419
491
  const allSuccess = successCount === targetPanes.length;
420
492
  const allFailed = successCount === 0;
421
- if (allSuccess) console.log("Content sent successfully to all panes!");
493
+ if (allSuccess) logger$8.info("Content sent successfully to all panes!");
422
494
  else if (allFailed) {
423
- console.error("Error: All target panes failed to receive content.");
424
- console.log("Falling back to clipboard...");
495
+ logger$8.error("All target panes failed to receive content.");
496
+ logger$8.info("Falling back to clipboard...");
425
497
  await copyToClipboard(content);
426
- console.log("Content copied to clipboard.");
427
- } else console.warn(`Warning: Content sent to ${successCount}/${targetPanes.length} panes. Failed panes: ${failedPanes.join(", ")}`);
498
+ logger$8.info("Content copied to clipboard.");
499
+ } else logger$8.warn(`Content sent to ${successCount}/${targetPanes.length} panes. Failed panes: ${failedPanes.join(", ")}`);
428
500
  return {
429
501
  successCount,
430
502
  totalCount: targetPanes.length,
@@ -436,6 +508,7 @@ async function handleContentDelivery(content, mux, targetPanes) {
436
508
 
437
509
  //#endregion
438
510
  //#region src/modes/args.ts
511
+ const logger$7 = getLogger(["editprompt"]);
439
512
  const ARG_MUX = {
440
513
  short: "m",
441
514
  description: "Multiplexer type (tmux or wezterm, default: tmux)",
@@ -470,17 +543,31 @@ const ARG_OUTPUT = {
470
543
  type: "string",
471
544
  multiple: true
472
545
  };
546
+ const ARG_LOG_FILE = {
547
+ description: "Write logs to the specified file (appends)",
548
+ type: "string"
549
+ };
550
+ const ARG_QUIET = {
551
+ short: "q",
552
+ description: "Suppress all log output",
553
+ type: "boolean"
554
+ };
555
+ const ARG_VERBOSE = {
556
+ short: "v",
557
+ description: "Enable debug-level log output",
558
+ type: "boolean"
559
+ };
473
560
  function validateMux(value) {
474
561
  const muxValue = value || "tmux";
475
562
  if (!isMuxType(muxValue)) {
476
- console.error(`Error: Invalid multiplexer type '${muxValue}'. Supported values: ${SUPPORTED_MUXES.join(", ")}`);
563
+ logger$7.error(`Invalid multiplexer type '${muxValue}'. Supported values: ${SUPPORTED_MUXES.join(", ")}`);
477
564
  process.exit(1);
478
565
  }
479
566
  return muxValue;
480
567
  }
481
568
  function validateTargetPane(value, commandName) {
482
569
  if (!value || typeof value !== "string") {
483
- console.error(`Error: --target-pane is required for ${commandName} command`);
570
+ logger$7.error(`--target-pane is required for ${commandName} command`);
484
571
  process.exit(1);
485
572
  }
486
573
  return value;
@@ -493,6 +580,7 @@ function normalizeTargetPanes(value) {
493
580
 
494
581
  //#endregion
495
582
  //#region src/modes/collect.ts
583
+ const logger$6 = getLogger(["editprompt", "collect"]);
496
584
  const SUPPORTED_OUTPUTS = ["buffer", "stdout"];
497
585
  async function readStdin() {
498
586
  return new Promise((resolve, reject) => {
@@ -516,7 +604,7 @@ function normalizeCollectOutputs(value) {
516
604
  const uniqueOutputs = [...new Set(outputs)];
517
605
  const invalid = uniqueOutputs.filter((v) => !SUPPORTED_OUTPUTS.includes(v));
518
606
  if (invalid.length > 0) {
519
- console.error(`Error: Invalid output(s) '${invalid.join(", ")}'. Supported values: ${SUPPORTED_OUTPUTS.join(", ")}`);
607
+ logger$6.error(`Invalid output(s) '${invalid.join(", ")}'. Supported values: ${SUPPORTED_OUTPUTS.join(", ")}`);
520
608
  process.exit(1);
521
609
  }
522
610
  return uniqueOutputs;
@@ -532,7 +620,7 @@ async function runCollectMode(mux, targetPaneId, rawContent, outputs = ["buffer"
532
620
  else if (mux === "wezterm") await appendToQuoteText(targetPaneId, processedText);
533
621
  } else if (output === "stdout") process.stdout.write(processedText);
534
622
  } catch (error) {
535
- console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
623
+ logger$6.error(`${error instanceof Error ? error.message : "Unknown error"}`);
536
624
  process.exit(1);
537
625
  }
538
626
  }
@@ -543,9 +631,17 @@ const collectCommand = define({
543
631
  mux: ARG_MUX,
544
632
  "target-pane": ARG_TARGET_PANE_SINGLE,
545
633
  output: ARG_OUTPUT,
546
- "no-quote": ARG_NO_QUOTE
634
+ "no-quote": ARG_NO_QUOTE,
635
+ "log-file": ARG_LOG_FILE,
636
+ quiet: ARG_QUIET,
637
+ verbose: ARG_VERBOSE
547
638
  },
548
639
  async run(ctx) {
640
+ setupLogger({
641
+ quiet: Boolean(ctx.values.quiet),
642
+ verbose: Boolean(ctx.values.verbose),
643
+ logFile: ctx.values["log-file"]
644
+ });
549
645
  const targetPane = validateTargetPane(ctx.values["target-pane"], "collect");
550
646
  const mux = validateMux(ctx.values.mux);
551
647
  const outputs = normalizeCollectOutputs(ctx.values.output);
@@ -554,7 +650,7 @@ const collectCommand = define({
554
650
  if (mux === "wezterm") {
555
651
  rawContent = extractRawContent(ctx.rest, ctx.positionals);
556
652
  if (rawContent === void 0) {
557
- console.error("Error: Text content is required for collect mode with wezterm. Use: editprompt collect --mux wezterm --target-pane <id> -- \"<text>\"");
653
+ logger$6.error("Text content is required for collect mode with wezterm. Use: editprompt collect --mux wezterm --target-pane <id> -- \"<text>\"");
558
654
  process.exit(1);
559
655
  }
560
656
  }
@@ -568,35 +664,41 @@ const VALID_MUX_TYPES = ["tmux", "wezterm"];
568
664
  function readSendConfig() {
569
665
  const muxValue = process.env.EDITPROMPT_MUX || "tmux";
570
666
  if (!VALID_MUX_TYPES.includes(muxValue)) throw new Error(`Invalid EDITPROMPT_MUX value: ${muxValue}. Must be one of: ${VALID_MUX_TYPES.join(", ")}`);
667
+ const mux = muxValue;
668
+ const alwaysCopy = process.env.EDITPROMPT_ALWAYS_COPY === "1";
669
+ const delayValue = process.env.EDITPROMPT_SEND_KEY_DELAY;
670
+ const parsedDelay = delayValue ? Number.parseInt(delayValue, 10) : NaN;
571
671
  return {
572
- mux: muxValue,
573
- alwaysCopy: process.env.EDITPROMPT_ALWAYS_COPY === "1"
672
+ mux,
673
+ alwaysCopy,
674
+ sendKeyDelay: Number.isNaN(parsedDelay) ? 1e3 : parsedDelay
574
675
  };
575
676
  }
576
677
 
577
678
  //#endregion
578
679
  //#region src/modes/dump.ts
680
+ const logger$5 = getLogger(["editprompt", "dump"]);
579
681
  async function runDumpMode() {
580
682
  try {
581
683
  const config = readSendConfig();
582
684
  let currentPaneId;
583
685
  let isEditor;
584
686
  if (config.mux === "tmux") {
585
- currentPaneId = await getCurrentPaneId();
687
+ currentPaneId = await getCurrentPaneId$1();
586
688
  isEditor = await isEditorPane(currentPaneId);
587
689
  } else {
588
- currentPaneId = await getCurrentPaneId$1();
690
+ currentPaneId = await getCurrentPaneId();
589
691
  isEditor = isEditorPaneFromConf(currentPaneId);
590
692
  }
591
693
  if (!isEditor) {
592
- console.error("Error: Current pane is not an editor pane");
694
+ logger$5.error("Current pane is not an editor pane");
593
695
  process.exit(1);
594
696
  }
595
697
  let targetPanes;
596
- if (config.mux === "tmux") targetPanes = await getTargetPaneIds(currentPaneId);
597
- else targetPanes = await getTargetPaneIds$1(currentPaneId);
698
+ if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
699
+ else targetPanes = await getTargetPaneIds(currentPaneId);
598
700
  if (targetPanes.length === 0) {
599
- console.error("Error: No target panes registered for this editor pane");
701
+ logger$5.error("No target panes registered for this editor pane");
600
702
  process.exit(1);
601
703
  }
602
704
  const quoteContents = [];
@@ -615,15 +717,24 @@ async function runDumpMode() {
615
717
  process.stdout.write(combinedContent.replace(/\n{3,}$/, "\n\n"));
616
718
  process.exit(0);
617
719
  } catch (error) {
618
- console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
720
+ logger$5.error(`${error instanceof Error ? error.message : "Unknown error"}`);
619
721
  process.exit(1);
620
722
  }
621
723
  }
622
724
  const dumpCommand = define({
623
725
  name: "dump",
624
726
  description: "Output and clear collected quoted text from environment variables",
625
- args: {},
626
- async run() {
727
+ args: {
728
+ "log-file": ARG_LOG_FILE,
729
+ quiet: ARG_QUIET,
730
+ verbose: ARG_VERBOSE
731
+ },
732
+ async run(ctx) {
733
+ setupLogger({
734
+ quiet: Boolean(ctx.values.quiet),
735
+ verbose: Boolean(ctx.values.verbose),
736
+ logFile: ctx.values["log-file"]
737
+ });
627
738
  await runDumpMode();
628
739
  }
629
740
  });
@@ -638,55 +749,57 @@ function processContent(content) {
638
749
 
639
750
  //#endregion
640
751
  //#region src/modes/input.ts
641
- async function runInputMode(rawContent, autoSend, sendKey) {
752
+ const logger$4 = getLogger(["editprompt", "input"]);
753
+ async function runInputMode(rawContent, autoSend, sendKey, sendKeyDelay) {
642
754
  const content = processContent(rawContent);
643
755
  if (!content) {
644
- console.log("No content to send. Exiting.");
756
+ logger$4.info("No content to send. Exiting.");
645
757
  return;
646
758
  }
647
759
  const config = readSendConfig();
648
760
  let currentPaneId;
649
761
  let isEditor;
650
762
  if (config.mux === "tmux") {
651
- currentPaneId = await getCurrentPaneId();
763
+ currentPaneId = await getCurrentPaneId$1();
652
764
  isEditor = await isEditorPane(currentPaneId);
653
765
  } else {
654
- currentPaneId = await getCurrentPaneId$1();
766
+ currentPaneId = await getCurrentPaneId();
655
767
  isEditor = isEditorPaneFromConf(currentPaneId);
656
768
  }
657
769
  if (!isEditor) {
658
- console.error("Error: Current pane is not an editor pane");
770
+ logger$4.error("Current pane is not an editor pane");
659
771
  process.exit(1);
660
772
  }
661
773
  let targetPanes;
662
- if (config.mux === "tmux") targetPanes = await getTargetPaneIds(currentPaneId);
663
- else targetPanes = await getTargetPaneIds$1(currentPaneId);
774
+ if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
775
+ else targetPanes = await getTargetPaneIds(currentPaneId);
664
776
  if (targetPanes.length === 0) {
665
- console.error("Error: No target panes registered for this editor pane");
777
+ logger$4.error("No target panes registered for this editor pane");
666
778
  process.exit(1);
667
779
  }
668
780
  if (autoSend) {
669
781
  const key = sendKey || (config.mux === "wezterm" ? "\\r" : "C-m");
782
+ const delay = /\.(png|webp|avif|jpe?g|gif)\b/i.test(content) ? sendKeyDelay ?? 1e3 : 200;
670
783
  let successCount = 0;
671
784
  for (const targetPane of targetPanes) try {
672
785
  if (config.mux === "wezterm") {
673
786
  await inputToWeztermPane(targetPane, content);
674
- await sendKeyToWeztermPane(targetPane, key);
787
+ await sendKeyToWeztermPane(targetPane, key, delay);
675
788
  } else {
676
789
  await inputToTmuxPane(targetPane, content);
677
- await sendKeyToTmuxPane(targetPane, key);
790
+ await sendKeyToTmuxPane(targetPane, key, delay);
678
791
  }
679
792
  successCount++;
680
793
  } catch (error) {
681
- console.error(`Failed to send to pane ${targetPane}: ${error instanceof Error ? error.message : "Unknown error"}`);
794
+ logger$4.error(`Failed to send to pane ${targetPane}: ${error instanceof Error ? error.message : "Unknown error"}`);
682
795
  }
683
796
  if (config.alwaysCopy) {
684
797
  await copyToClipboard(content);
685
- console.log("Also copied to clipboard.");
798
+ logger$4.info("Also copied to clipboard.");
686
799
  }
687
- if (successCount > 0) console.log("Content sent and submitted successfully!");
800
+ if (successCount > 0) logger$4.info("Content sent and submitted successfully!");
688
801
  else {
689
- console.error("Error: All target panes failed to receive content");
802
+ logger$4.error("All target panes failed to receive content");
690
803
  process.exit(1);
691
804
  }
692
805
  return;
@@ -695,12 +808,12 @@ async function runInputMode(rawContent, autoSend, sendKey) {
695
808
  const result = await handleContentDelivery(content, config.mux, targetPanes);
696
809
  if (config.alwaysCopy && !result.allFailed) {
697
810
  await copyToClipboard(content);
698
- console.log("Also copied to clipboard.");
811
+ logger$4.info("Also copied to clipboard.");
699
812
  }
700
813
  if (result.successCount > 0) await focusFirstSuccessPane(config.mux, targetPanes, result.failedPanes);
701
814
  if (result.allFailed) process.exit(1);
702
815
  } catch (error) {
703
- console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
816
+ logger$4.error(`${error instanceof Error ? error.message : "Unknown error"}`);
704
817
  process.exit(1);
705
818
  }
706
819
  }
@@ -715,21 +828,30 @@ const inputCommand = define({
715
828
  "send-key": {
716
829
  description: "Key to send after content (requires --auto-send)",
717
830
  type: "string"
718
- }
831
+ },
832
+ "log-file": ARG_LOG_FILE,
833
+ quiet: ARG_QUIET,
834
+ verbose: ARG_VERBOSE
719
835
  },
720
836
  async run(ctx) {
837
+ setupLogger({
838
+ quiet: Boolean(ctx.values.quiet),
839
+ verbose: Boolean(ctx.values.verbose),
840
+ logFile: ctx.values["log-file"]
841
+ });
721
842
  const rawContent = extractRawContent(ctx.rest, ctx.positionals);
722
843
  if (rawContent === void 0) {
723
- console.error("Error: Content is required for input command");
724
- console.error("Usage: editprompt input \"your content\"");
725
- console.error(" or: editprompt input -- \"your content\"");
844
+ logger$4.error("Content is required for input command");
845
+ logger$4.error("Usage: editprompt input \"your content\"");
846
+ logger$4.error(" or: editprompt input -- \"your content\"");
726
847
  process.exit(1);
727
848
  }
728
849
  if (ctx.values["send-key"] && !ctx.values["auto-send"]) {
729
- console.error("Error: --send-key requires --auto-send to be enabled");
850
+ logger$4.error("--send-key requires --auto-send to be enabled");
730
851
  process.exit(1);
731
852
  }
732
- await runInputMode(rawContent, Boolean(ctx.values["auto-send"]), ctx.values["send-key"]);
853
+ const config = readSendConfig();
854
+ await runInputMode(rawContent, Boolean(ctx.values["auto-send"]), ctx.values["send-key"], config.sendKeyDelay);
733
855
  }
734
856
  });
735
857
 
@@ -825,23 +947,25 @@ async function openEditorAndGetContent(editorOption, envVars, sendConfig) {
825
947
 
826
948
  //#endregion
827
949
  //#region src/modes/openEditor.ts
950
+ const logger$3 = getLogger(["editprompt", "open"]);
828
951
  async function runOpenEditorMode(options) {
829
952
  if (options.targetPanes.length > 0 && options.mux === "tmux") try {
830
- await markAsEditorPane(await getCurrentPaneId(), options.targetPanes);
953
+ await markAsEditorPane$1(await getCurrentPaneId$1(), options.targetPanes);
831
954
  } catch {}
832
955
  else if (options.targetPanes.length > 0 && options.mux === "wezterm") try {
833
- const currentPaneId = await getCurrentPaneId$1();
834
- await markAsEditorPane$1(currentPaneId, options.targetPanes);
956
+ const currentPaneId = await getCurrentPaneId();
957
+ await markAsEditorPane(currentPaneId, options.targetPanes);
835
958
  } catch {}
836
959
  try {
837
960
  const sendConfig = {
838
961
  mux: options.mux,
839
- alwaysCopy: options.alwaysCopy
962
+ alwaysCopy: options.alwaysCopy,
963
+ sendKeyDelay: Number.parseInt(process.env.EDITPROMPT_SEND_KEY_DELAY || "", 10) || 1e3
840
964
  };
841
- console.log("Opening editor...");
965
+ logger$3.info("Opening editor...");
842
966
  const content = await openEditorAndGetContent(options.editor, options.env, sendConfig);
843
967
  if (!content) {
844
- console.log("No content entered. Exiting.");
968
+ logger$3.info("No content entered. Exiting.");
845
969
  return;
846
970
  }
847
971
  try {
@@ -850,20 +974,20 @@ async function runOpenEditorMode(options) {
850
974
  console.log(content);
851
975
  if (options.alwaysCopy && !result.allFailed) {
852
976
  await copyToClipboard(content);
853
- console.log("Also copied to clipboard.");
977
+ logger$3.info("Also copied to clipboard.");
854
978
  }
855
979
  if (options.targetPanes.length > 0 && result.successCount > 0) await focusFirstSuccessPane(options.mux, options.targetPanes, result.failedPanes);
856
980
  if (!result.allSuccess) process.exit(1);
857
981
  } catch (error) {
858
- console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
982
+ logger$3.error(`${error instanceof Error ? error.message : "Unknown error"}`);
859
983
  process.exit(1);
860
984
  }
861
985
  } finally {
862
986
  if (options.targetPanes.length > 0 && options.mux === "tmux") try {
863
- for (const targetPane of options.targetPanes) await clearEditorPaneId(targetPane);
987
+ for (const targetPane of options.targetPanes) await clearEditorPaneId$1(targetPane);
864
988
  } catch {}
865
989
  else if (options.targetPanes.length > 0 && options.mux === "wezterm") try {
866
- for (const targetPane of options.targetPanes) await clearEditorPaneId$1(targetPane);
990
+ for (const targetPane of options.targetPanes) await clearEditorPaneId(targetPane);
867
991
  } catch {}
868
992
  }
869
993
  }
@@ -875,6 +999,9 @@ const openCommand = define({
875
999
  "target-pane": ARG_TARGET_PANE_MULTI,
876
1000
  editor: ARG_EDITOR,
877
1001
  "always-copy": ARG_ALWAYS_COPY,
1002
+ "log-file": ARG_LOG_FILE,
1003
+ quiet: ARG_QUIET,
1004
+ verbose: ARG_VERBOSE,
878
1005
  env: {
879
1006
  short: "E",
880
1007
  description: "Environment variables to set (e.g., KEY=VALUE)",
@@ -883,6 +1010,11 @@ const openCommand = define({
883
1010
  }
884
1011
  },
885
1012
  async run(ctx) {
1013
+ setupLogger({
1014
+ quiet: Boolean(ctx.values.quiet),
1015
+ verbose: Boolean(ctx.values.verbose),
1016
+ logFile: ctx.values["log-file"]
1017
+ });
886
1018
  await runOpenEditorMode({
887
1019
  mux: validateMux(ctx.values.mux),
888
1020
  targetPanes: normalizeTargetPanes(ctx.values["target-pane"]),
@@ -895,39 +1027,40 @@ const openCommand = define({
895
1027
 
896
1028
  //#endregion
897
1029
  //#region src/modes/register.ts
1030
+ const logger$2 = getLogger(["editprompt", "register"]);
898
1031
  async function runRegisterMode(options) {
899
1032
  if (options.targetPanes.length === 0) {
900
- console.error("Error: --target-pane is required for register command");
1033
+ logger$2.error("--target-pane is required for register command");
901
1034
  process.exit(1);
902
1035
  }
903
1036
  let editorPaneId;
904
1037
  if (options.editorPane) editorPaneId = options.editorPane;
905
1038
  else if (options.mux === "tmux") {
906
- editorPaneId = await getCurrentPaneId();
1039
+ editorPaneId = await getCurrentPaneId$1();
907
1040
  if (!await isEditorPane(editorPaneId)) {
908
- console.error("Error: Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1041
+ logger$2.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
909
1042
  process.exit(1);
910
1043
  }
911
1044
  } else if (options.mux === "wezterm") {
912
- editorPaneId = await getCurrentPaneId$1();
1045
+ editorPaneId = await getCurrentPaneId();
913
1046
  if (!isEditorPaneFromConf(editorPaneId)) {
914
- console.error("Error: Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
1047
+ logger$2.error("Current pane is not an editor pane. Please run this command from an editor pane or specify --editor-pane.");
915
1048
  process.exit(1);
916
1049
  }
917
1050
  } else {
918
- console.error("Error: Unsupported multiplexer");
1051
+ logger$2.error("Unsupported multiplexer");
919
1052
  process.exit(1);
920
1053
  }
921
1054
  try {
922
1055
  let existingPanes = [];
923
- if (options.mux === "tmux") existingPanes = await getTargetPaneIds(editorPaneId);
924
- else if (options.mux === "wezterm") existingPanes = await getTargetPaneIds$1(editorPaneId);
1056
+ if (options.mux === "tmux") existingPanes = await getTargetPaneIds$1(editorPaneId);
1057
+ else if (options.mux === "wezterm") existingPanes = await getTargetPaneIds(editorPaneId);
925
1058
  const mergedTargetPanes = [...new Set([...existingPanes, ...options.targetPanes])];
926
- if (options.mux === "tmux") await markAsEditorPane(editorPaneId, mergedTargetPanes);
927
- else if (options.mux === "wezterm") await markAsEditorPane$1(editorPaneId, mergedTargetPanes);
928
- console.log(`Editor pane ${editorPaneId} registered with target panes: ${mergedTargetPanes.join(", ")}`);
1059
+ if (options.mux === "tmux") await markAsEditorPane$1(editorPaneId, mergedTargetPanes);
1060
+ else if (options.mux === "wezterm") await markAsEditorPane(editorPaneId, mergedTargetPanes);
1061
+ logger$2.info(`Editor pane ${editorPaneId} registered with target panes: ${mergedTargetPanes.join(", ")}`);
929
1062
  } catch (error) {
930
- console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
1063
+ logger$2.error(`${error instanceof Error ? error.message : "Unknown error"}`);
931
1064
  process.exit(1);
932
1065
  }
933
1066
  }
@@ -941,9 +1074,17 @@ const registerCommand = define({
941
1074
  short: "e",
942
1075
  description: "Editor pane ID (defaults to current pane)",
943
1076
  type: "string"
944
- }
1077
+ },
1078
+ "log-file": ARG_LOG_FILE,
1079
+ quiet: ARG_QUIET,
1080
+ verbose: ARG_VERBOSE
945
1081
  },
946
1082
  async run(ctx) {
1083
+ setupLogger({
1084
+ quiet: Boolean(ctx.values.quiet),
1085
+ verbose: Boolean(ctx.values.verbose),
1086
+ logFile: ctx.values["log-file"]
1087
+ });
947
1088
  await runRegisterMode({
948
1089
  mux: validateMux(ctx.values.mux),
949
1090
  targetPanes: normalizeTargetPanes(ctx.values["target-pane"]),
@@ -954,68 +1095,72 @@ const registerCommand = define({
954
1095
 
955
1096
  //#endregion
956
1097
  //#region src/modes/resume.ts
1098
+ const logger$1 = getLogger(["editprompt", "resume"]);
957
1099
  async function runResumeMode(targetPane, mux) {
958
1100
  if (mux === "wezterm") {
959
- const currentPaneId$1 = await getCurrentPaneId$1();
960
- if (isEditorPaneFromConf(currentPaneId$1)) {
961
- console.log("isEditor");
962
- const originalTargetPaneIds = await getTargetPaneIds$1(currentPaneId$1);
1101
+ const currentPaneId = await getCurrentPaneId();
1102
+ if (isEditorPaneFromConf(currentPaneId)) {
1103
+ logger$1.debug("Current pane is an editor pane");
1104
+ const originalTargetPaneIds = await getTargetPaneIds(currentPaneId);
963
1105
  if (originalTargetPaneIds.length === 0) {
964
- console.log("Not found originalTargetPaneIds");
1106
+ logger$1.debug("No target pane IDs found for editor pane");
965
1107
  process.exit(1);
966
1108
  }
967
1109
  let focused = false;
968
- for (const paneId of originalTargetPaneIds) if (await checkPaneExists$1(paneId)) {
969
- await focusPane$1(paneId);
1110
+ for (const paneId of originalTargetPaneIds) if (await checkPaneExists(paneId)) {
1111
+ await focusPane(paneId);
970
1112
  focused = true;
971
1113
  break;
972
1114
  }
973
1115
  if (!focused) {
974
- console.log("All target panes do not exist");
1116
+ logger$1.debug("All target panes do not exist");
975
1117
  process.exit(1);
976
1118
  }
977
1119
  process.exit(0);
978
1120
  }
979
- console.log("not isEditor");
980
- const editorPaneId$1 = await getEditorPaneId$1(targetPane);
981
- console.log(`wezterm editorPaneId: ${editorPaneId$1}`);
982
- if (editorPaneId$1 === "") {
983
- console.log("Not found editorPaneId");
1121
+ logger$1.debug("Current pane is not an editor pane");
1122
+ const editorPaneId = await getEditorPaneId(targetPane);
1123
+ logger$1.debug("wezterm editorPaneId: {editorPaneId}", { editorPaneId });
1124
+ if (editorPaneId === "") {
1125
+ logger$1.debug("Editor pane ID not found");
984
1126
  process.exit(1);
985
1127
  }
986
- if (!await checkPaneExists$1(editorPaneId$1)) {
987
- console.log("Not exist editorPaneId");
988
- await clearEditorPaneId$1(targetPane);
1128
+ if (!await checkPaneExists(editorPaneId)) {
1129
+ logger$1.debug("Editor pane does not exist");
1130
+ await clearEditorPaneId(targetPane);
989
1131
  process.exit(1);
990
1132
  }
991
1133
  try {
992
- await focusPane$1(editorPaneId$1);
1134
+ await focusPane(editorPaneId);
993
1135
  process.exit(0);
994
1136
  } catch (error) {
995
- console.log(`Can't focus editorPaneId: ${editorPaneId$1}\nerror: ${error}`);
1137
+ logger$1.debug("Can't focus editorPaneId: {editorPaneId}, error: {error}", {
1138
+ editorPaneId,
1139
+ error
1140
+ });
996
1141
  process.exit(1);
997
1142
  }
998
1143
  }
999
- const currentPaneId = await getCurrentPaneId();
1144
+ const currentPaneId = await getCurrentPaneId$1();
1000
1145
  if (await isEditorPane(currentPaneId)) {
1001
- const originalTargetPaneIds = await getTargetPaneIds(currentPaneId);
1146
+ const originalTargetPaneIds = await getTargetPaneIds$1(currentPaneId);
1002
1147
  if (originalTargetPaneIds.length === 0) process.exit(1);
1003
1148
  let focused = false;
1004
- for (const paneId of originalTargetPaneIds) if (await checkPaneExists(paneId)) {
1005
- await focusPane(paneId);
1149
+ for (const paneId of originalTargetPaneIds) if (await checkPaneExists$1(paneId)) {
1150
+ await focusPane$1(paneId);
1006
1151
  focused = true;
1007
1152
  break;
1008
1153
  }
1009
1154
  if (!focused) process.exit(1);
1010
1155
  process.exit(0);
1011
1156
  }
1012
- const editorPaneId = await getEditorPaneId(targetPane);
1157
+ const editorPaneId = await getEditorPaneId$1(targetPane);
1013
1158
  if (editorPaneId === "") process.exit(1);
1014
- if (!await checkPaneExists(editorPaneId)) {
1015
- await clearEditorPaneId(targetPane);
1159
+ if (!await checkPaneExists$1(editorPaneId)) {
1160
+ await clearEditorPaneId$1(targetPane);
1016
1161
  process.exit(1);
1017
1162
  }
1018
- await focusPane(editorPaneId);
1163
+ await focusPane$1(editorPaneId);
1019
1164
  process.exit(0);
1020
1165
  }
1021
1166
  const resumeCommand = define({
@@ -1023,15 +1168,24 @@ const resumeCommand = define({
1023
1168
  description: "Resume existing editor pane or focus back to target pane",
1024
1169
  args: {
1025
1170
  mux: ARG_MUX,
1026
- "target-pane": ARG_TARGET_PANE_SINGLE
1171
+ "target-pane": ARG_TARGET_PANE_SINGLE,
1172
+ "log-file": ARG_LOG_FILE,
1173
+ quiet: ARG_QUIET,
1174
+ verbose: ARG_VERBOSE
1027
1175
  },
1028
1176
  async run(ctx) {
1177
+ setupLogger({
1178
+ quiet: Boolean(ctx.values.quiet),
1179
+ verbose: Boolean(ctx.values.verbose),
1180
+ logFile: ctx.values["log-file"]
1181
+ });
1029
1182
  await runResumeMode(validateTargetPane(ctx.values["target-pane"], "resume"), validateMux(ctx.values.mux));
1030
1183
  }
1031
1184
  });
1032
1185
 
1033
1186
  //#endregion
1034
1187
  //#region src/modes/stash.ts
1188
+ const logger = getLogger(["editprompt", "stash"]);
1035
1189
  function getStashKey(mux, targetPaneId) {
1036
1190
  return `${mux}.targetPane.pane_${targetPaneId}.stash`;
1037
1191
  }
@@ -1078,21 +1232,21 @@ async function getTargetPaneForStash() {
1078
1232
  let currentPaneId;
1079
1233
  let isEditor;
1080
1234
  if (config.mux === "tmux") {
1081
- currentPaneId = await getCurrentPaneId();
1235
+ currentPaneId = await getCurrentPaneId$1();
1082
1236
  isEditor = await isEditorPane(currentPaneId);
1083
1237
  } else {
1084
- currentPaneId = await getCurrentPaneId$1();
1238
+ currentPaneId = await getCurrentPaneId();
1085
1239
  isEditor = isEditorPaneFromConf(currentPaneId);
1086
1240
  }
1087
1241
  if (!isEditor) {
1088
- console.error("Error: Current pane is not an editor pane");
1242
+ logger.error("Current pane is not an editor pane");
1089
1243
  process.exit(1);
1090
1244
  }
1091
1245
  let targetPanes;
1092
- if (config.mux === "tmux") targetPanes = await getTargetPaneIds(currentPaneId);
1093
- else targetPanes = await getTargetPaneIds$1(currentPaneId);
1246
+ if (config.mux === "tmux") targetPanes = await getTargetPaneIds$1(currentPaneId);
1247
+ else targetPanes = await getTargetPaneIds(currentPaneId);
1094
1248
  if (targetPanes.length === 0) {
1095
- console.error("Error: No target panes registered for this editor pane");
1249
+ logger.error("No target panes registered for this editor pane");
1096
1250
  process.exit(1);
1097
1251
  }
1098
1252
  return {
@@ -1103,13 +1257,13 @@ async function getTargetPaneForStash() {
1103
1257
  async function runPush(rest, positionals) {
1104
1258
  const rawContent = extractRawContent(rest, positionals);
1105
1259
  if (rawContent === void 0 || rawContent.trim() === "") {
1106
- console.error("Error: Content is required for stash push");
1107
- console.error("Usage: editprompt stash push -- \"your content\"");
1260
+ logger.error("Content is required for stash push");
1261
+ logger.error("Usage: editprompt stash push -- \"your content\"");
1108
1262
  process.exit(1);
1109
1263
  }
1110
1264
  const { mux, targetPaneId } = await getTargetPaneForStash();
1111
1265
  const key = await pushStash(mux, targetPaneId, rawContent);
1112
- console.log(`Stashed with key: ${key}`);
1266
+ logger.info("Stashed with key: {key}", { key });
1113
1267
  }
1114
1268
  async function runList() {
1115
1269
  const { mux, targetPaneId } = await getTargetPaneForStash();
@@ -1120,8 +1274,8 @@ async function runApply(key) {
1120
1274
  const { mux, targetPaneId } = await getTargetPaneForStash();
1121
1275
  const content = getStashContent(mux, targetPaneId, key);
1122
1276
  if (content === "") {
1123
- if (key) console.error(`Error: No stash entry found with key: ${key}`);
1124
- else console.error("Error: No stash entries found");
1277
+ if (key) logger.error("No stash entry found with key: {key}", { key });
1278
+ else logger.error("No stash entries found");
1125
1279
  process.exit(1);
1126
1280
  }
1127
1281
  process.stdout.write(content);
@@ -1129,18 +1283,18 @@ async function runApply(key) {
1129
1283
  async function runDrop(key) {
1130
1284
  const { mux, targetPaneId } = await getTargetPaneForStash();
1131
1285
  if (!dropStash(mux, targetPaneId, key)) {
1132
- if (key) console.error(`Error: No stash entry found with key: ${key}`);
1133
- else console.error("Error: No stash entries found");
1286
+ if (key) logger.error("No stash entry found with key: {key}", { key });
1287
+ else logger.error("No stash entries found");
1134
1288
  process.exit(1);
1135
1289
  }
1136
- console.log("Stash entry dropped");
1290
+ logger.info("Stash entry dropped");
1137
1291
  }
1138
1292
  async function runPop(key) {
1139
1293
  const { mux, targetPaneId } = await getTargetPaneForStash();
1140
1294
  const content = getStashContent(mux, targetPaneId, key);
1141
1295
  if (content === "") {
1142
- if (key) console.error(`Error: No stash entry found with key: ${key}`);
1143
- else console.error("Error: No stash entries found");
1296
+ if (key) logger.error("No stash entry found with key: {key}", { key });
1297
+ else logger.error("No stash entries found");
1144
1298
  process.exit(1);
1145
1299
  }
1146
1300
  process.stdout.write(content);
@@ -1169,8 +1323,17 @@ function parseKeyOption(args) {
1169
1323
  const stashCommand = define({
1170
1324
  name: "stash",
1171
1325
  description: "Stash prompts for later use",
1172
- args: {},
1326
+ args: {
1327
+ "log-file": ARG_LOG_FILE,
1328
+ quiet: ARG_QUIET,
1329
+ verbose: ARG_VERBOSE
1330
+ },
1173
1331
  async run(ctx) {
1332
+ setupLogger({
1333
+ quiet: Boolean(ctx.values.quiet),
1334
+ verbose: Boolean(ctx.values.verbose),
1335
+ logFile: ctx.values["log-file"]
1336
+ });
1174
1337
  const args = ctx.positionals.slice(1);
1175
1338
  if (args.length === 0 || args[0] === "-h" || args[0] === "--help") {
1176
1339
  showHelp();
@@ -1195,8 +1358,8 @@ const stashCommand = define({
1195
1358
  await runPop(parseKeyOption(subArgs));
1196
1359
  break;
1197
1360
  default:
1198
- console.error(`Error: Unknown subcommand '${subcommand}'`);
1199
- console.error("");
1361
+ logger.error("Unknown subcommand '{subcommand}'", { subcommand });
1362
+ logger.error("");
1200
1363
  showHelp();
1201
1364
  process.exit(1);
1202
1365
  }
@@ -1210,16 +1373,18 @@ await cli(process.argv.slice(2), {
1210
1373
  description: "A CLI tool that lets you write prompts for CLI tools using your favorite text editor",
1211
1374
  args: {},
1212
1375
  async run() {
1213
- console.error("Error: Subcommand is required");
1214
- console.error("");
1215
- console.error("Migration guide from old to new syntax:");
1216
- console.error(" editprompt → editprompt open");
1217
- console.error(" editprompt --resume → editprompt resume");
1218
- console.error(" editprompt -- \"text\" → editprompt input \"text\"");
1219
- console.error(" editprompt --quote → editprompt collect");
1220
- console.error(" editprompt --capture → editprompt dump");
1221
- console.error("");
1222
- console.error("For details: https://github.com/eetann/editprompt/?tab=readme-ov-file");
1376
+ setupLogger();
1377
+ const logger = getLogger(["editprompt"]);
1378
+ logger.error("Subcommand is required");
1379
+ logger.error("");
1380
+ logger.error("Migration guide from old to new syntax:");
1381
+ logger.error(" editprompt → editprompt open");
1382
+ logger.error(" editprompt --resume → editprompt resume");
1383
+ logger.error(" editprompt -- \"text\" → editprompt input \"text\"");
1384
+ logger.error(" editprompt --quote → editprompt collect");
1385
+ logger.error(" editprompt --capture editprompt dump");
1386
+ logger.error("");
1387
+ logger.error("For details: https://github.com/eetann/editprompt/?tab=readme-ov-file");
1223
1388
  process.exit(1);
1224
1389
  }
1225
1390
  }, {
package/package.json CHANGED
@@ -1,31 +1,33 @@
1
1
  {
2
2
  "name": "editprompt",
3
- "version": "1.2.0",
4
- "author": "eetann",
3
+ "version": "1.3.0",
5
4
  "description": "A CLI tool that lets you write prompts for CLI tools using your favorite text editor",
5
+ "keywords": [
6
+ "cli",
7
+ "clipboard",
8
+ "command-line",
9
+ "editor",
10
+ "prompt",
11
+ "text-editor",
12
+ "tmux"
13
+ ],
14
+ "homepage": "https://github.com/eetann/editprompt",
15
+ "bugs": {
16
+ "url": "https://github.com/eetann/editprompt/issues"
17
+ },
6
18
  "license": "MIT",
19
+ "author": "eetann",
7
20
  "repository": {
8
21
  "type": "git",
9
22
  "url": "https://github.com/eetann/editprompt.git"
10
23
  },
11
- "homepage": "https://github.com/eetann/editprompt",
12
- "bugs": {
13
- "url": "https://github.com/eetann/editprompt/issues"
24
+ "bin": {
25
+ "editprompt": "./dist/index.js"
14
26
  },
15
- "keywords": [
16
- "cli",
17
- "editor",
18
- "prompt",
19
- "tmux",
20
- "clipboard",
21
- "command-line",
22
- "text-editor"
27
+ "files": [
28
+ "dist"
23
29
  ],
24
- "publishConfig": {
25
- "access": "public"
26
- },
27
30
  "type": "module",
28
- "files": ["dist"],
29
31
  "main": "./dist/index.js",
30
32
  "module": "./dist/index.js",
31
33
  "types": "./dist/index.d.ts",
@@ -33,31 +35,35 @@
33
35
  ".": "./dist/index.js",
34
36
  "./package.json": "./package.json"
35
37
  },
36
- "bin": {
37
- "editprompt": "./dist/index.js"
38
+ "publishConfig": {
39
+ "access": "public"
38
40
  },
39
41
  "scripts": {
40
42
  "build": "tsdown",
41
43
  "dev": "tsdown --watch",
42
- "lint": "biome check",
43
- "format": "biome format --write",
44
+ "lint": "oxlint --type-aware && oxfmt --check",
45
+ "lint:fix": "oxlint --type-aware --fix && oxfmt",
46
+ "format": "oxfmt --write .",
44
47
  "typecheck": "tsgo --noEmit",
45
48
  "release": "bun run lint && bun run typecheck && bun run test && bun run build && bumpp",
46
49
  "test": "bun test",
47
50
  "test:watch": "bun test --watch"
48
51
  },
52
+ "dependencies": {
53
+ "@logtape/logtape": "^2.0.2",
54
+ "clipboardy": "^4.0.0",
55
+ "conf": "^15.0.2",
56
+ "gunshi": "^0.29.2"
57
+ },
49
58
  "devDependencies": {
50
- "@biomejs/biome": "1.9.4",
51
59
  "@types/bun": "^1.3.2",
52
60
  "@types/node": "^24.10.1",
53
61
  "@typescript/native-preview": "^7.0.0-dev.20251119.1",
54
62
  "bumpp": "^10.3.1",
55
- "tsdown": "latest"
56
- },
57
- "dependencies": {
58
- "clipboardy": "^4.0.0",
59
- "conf": "^15.0.2",
60
- "gunshi": "v0.27.0-beta.3"
63
+ "oxfmt": "^0.35.0",
64
+ "oxlint": "^1.50.0",
65
+ "oxlint-tsgolint": "^0.15.0",
66
+ "tsdown": "^0.20.3"
61
67
  },
62
68
  "peerDependencies": {
63
69
  "typescript": "^5.8.3"