editprompt 0.6.0 → 0.8.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 +255 -199
  2. package/dist/index.js +432 -139
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,16 +1,21 @@
1
+ <p align="center">
2
+ <a href="https://www.npmjs.com/package/editprompt"><img src="https://img.shields.io/npm/v/editprompt?color=CB0200" alt="link to npm.js" /></a>
3
+ </p>
4
+
1
5
  # 📝 editprompt
2
6
 
3
7
  A CLI tool that lets you write prompts for CLI tools using your favorite text editor. Works seamlessly with Claude Code, Codex CLI, Gemini CLI, and any other CLI process.
4
8
 
5
- https://github.com/user-attachments/assets/01bcda7c-7771-4b33-bf5c-629812d45cc4
9
+ ![send without closing editor](https://github.com/user-attachments/assets/b0e486af-78d7-4b70-8c82-64d330c22ba1)
6
10
 
7
11
 
8
12
  ## 🏆 Why editprompt?
9
13
 
10
14
  - **🎯 Your Editor, Your Way**: Write prompts in your favorite editor with full syntax highlighting, plugins, and customizations
11
15
  - **🚫 No Accidental Sends**: Never accidentally hit Enter and send an incomplete prompt again
12
- - 🔄 **Reusable Prompts**: Save and iterate on prompts with `--always-copy`
13
- - 📝 **Multi-line Commands**: Complex SQL queries, JSON payloads
16
+ - **🔄 Iterate Efficiently**: Keep your editor open and send multiple prompts without reopening
17
+ - **💬 Quote and Reply**: Collect multiple text selections and reply to specific parts of AI responses
18
+ - **📝 Multi-line Commands**: Complex SQL queries, JSON payloads, and structured prompts
14
19
 
15
20
 
16
21
  ## ✨ Features
@@ -18,8 +23,9 @@ https://github.com/user-attachments/assets/01bcda7c-7771-4b33-bf5c-629812d45cc4
18
23
  - 🖊️ **Editor Integration**: Use your preferred text editor to write prompts
19
24
  - 🖥️ **Multiplexer Support**: Send prompts directly to tmux or WezTerm sessions
20
25
  - 🖥️ **Universal Terminal Support**: Works with any terminal via clipboard - no multiplexer required
26
+ - 📤 **Send Without Closing**: Iterate on prompts without closing your editor
27
+ - 📋 **Quote Buffering**: Collect text selections and send them as quoted replies
21
28
  - 📋 **Clipboard Fallback**: Automatically copies to clipboard if sending fails
22
- - 📋 **Always Copy Option**: Copy to clipboard even after successful tmux delivery (`--always-copy`)
23
29
 
24
30
 
25
31
  ## 📦 Installation
@@ -34,106 +40,61 @@ npx editprompt
34
40
 
35
41
  ## 🚀 Usage
36
42
 
37
- - Run `editprompt` to open a temporary Markdown file in your editor
38
- - Write your prompt comfortably with full editor features
39
- - Save and close - it automatically:
40
- - Sends to tmux/wezterm panes if detected
41
- - Falls back to clipboard otherwise (works with **any terminal**)
43
+ editprompt supports three main workflows to fit different use cases:
42
44
 
43
- ```sh
44
- editprompt
45
- ```
45
+ ### Workflow 1: Basic - Write and Send
46
46
 
47
- **Advanced usage:** You can also send content **without closing the editor** for faster iteration. See `Send Without Closing Editor` for details.
47
+ ![wrihte and send prompt by editprompt](https://github.com/user-attachments/assets/6587b0c4-8132-4d5c-be68-3aa32a8d4df2)
48
48
 
49
+ The simplest way to use editprompt:
49
50
 
50
- ### 🖥️ Tmux Integration
51
+ 1. Run `editprompt` to open your editor
52
+ 2. Write your prompt
53
+ 3. Save and close the editor
54
+ 4. Content is automatically sent to the target pane or clipboard
51
55
 
52
- **Split window version (with pane resume):**
53
- ```tmux
54
- bind -n M-q run-shell '\
55
- editprompt --resume --target-pane #{pane_id} || \
56
- tmux split-window -v -l 10 -c "#{pane_current_path}" \
57
- "editprompt --editor nvim --always-copy --target-pane #{pane_id}"'
58
- ```
56
+ Perfect for one-off prompts when you need more space than a terminal input line.
59
57
 
60
- **How it works:**
61
- - **First time**: Creates a new editor pane if one doesn't exist
62
- - **Subsequent times**: Focuses the existing editor pane instead of creating a new one
63
- - **Bidirectional**: Pressing the same keybinding from within the editor pane returns you to the original target pane
58
+ ### Workflow 2: Interactive - Iterate with Editor Open
64
59
 
65
- This allows you to toggle between your target pane and editor pane using the same keybinding (`M-q`).
60
+ ![send without closing editor](https://github.com/user-attachments/assets/b0e486af-78d7-4b70-8c82-64d330c22ba1)
66
61
 
67
- **Benefits:**
68
- - Prevents pane proliferation and keeps your window management simple
69
- - Switch between your work pane and editor pane while preserving your editing content
62
+ For iterating on prompts without constantly reopening the editor:
70
63
 
71
- **Popup version:**
72
- ```tmux
73
- bind -n M-q run-shell 'tmux display-popup -E \
74
- -d "#{pane_current_path}" \
75
- -w 80% -h 65% \
76
- "editprompt --editor nvim --always-copy --target-pane #{pane_id}"'
77
- ```
64
+ 1. Set up a keybinding to open editprompt with `--resume` mode
65
+ 2. Editor pane stays open between sends
66
+ 3. Write, send, refine, send again - all without closing the editor
67
+ 4. Use the same keybinding to toggle between your work pane and editor pane
78
68
 
69
+ Ideal for trial-and-error workflows with AI assistants.
79
70
 
80
- ### 🖼️ WezTerm Integration
71
+ ### Workflow 3: Quote - Collect and Reply
81
72
 
82
- ```lua
83
- {
84
- key = "q",
85
- mods = "OPT",
86
- action = wezterm.action_callback(function(window, pane)
87
- local target_pane_id = tostring(pane:pane_id())
88
-
89
- -- Try to resume existing editor pane
90
- local success, stdout, stderr = wezterm.run_child_process({
91
- "/bin/zsh",
92
- "-lc",
93
- string.format(
94
- "editprompt --resume --mux wezterm --target-pane %s",
95
- target_pane_id
96
- ),
97
- })
98
-
99
- -- If resume failed, create new editor pane
100
- if not success then
101
- window:perform_action(
102
- act.SplitPane({
103
- direction = "Down",
104
- size = { Cells = 10 },
105
- command = {
106
- args = {
107
- "/bin/zsh",
108
- "-lc",
109
- string.format(
110
- "editprompt --editor nvim --always-copy --mux wezterm --target-pane %s",
111
- target_pane_id
112
- ),
113
- },
114
- },
115
- }),
116
- pane
117
- )
118
- end
119
- end),
120
- },
73
+ ![quote and capture with editprompt](https://github.com/user-attachments/assets/33af0702-5c80-4ccf-80d9-0ae42052e6fa)
74
+
75
+ ```markdown
76
+ > Some AI agents include leading spaces in their output,which can make the copied text look a bit awkward.
77
+
78
+ <!-- Write your reply here -->
79
+
80
+ > Using editprompt’s quote mode or capture mode makes it easy to reply while quoting the AI agent’s output.
81
+
82
+ <!-- Write your reply here -->
121
83
  ```
122
84
 
123
- **How it works:**
124
- - **First time**: Creates a new editor pane if one doesn't exist
125
- - **Subsequent times**: Focuses the existing editor pane instead of creating a new one
126
- - **Bidirectional**: Pressing the same keybinding from within the editor pane returns you to the original target pane
85
+ For replying to specific parts of AI responses:
127
86
 
128
- This allows you to toggle between your target pane and editor pane using the same keybinding (`OPT-q`).
87
+ 1. Select text in your terminal (tmux copy mode or WezTerm selection) and trigger quote mode
88
+ 2. Repeat to collect multiple selections
89
+ 3. Run `editprompt --capture` to retrieve all collected quotes
90
+ 4. Edit and send your reply with context
129
91
 
130
- **Benefits:**
131
- - Prevents pane proliferation and keeps your window management simple
132
- - Switch between your work pane and editor pane while preserving your editing content
92
+ Perfect for addressing multiple points in long AI responses.
133
93
 
134
- **Note:** The `-lc` flag ensures your shell loads the full login environment, making `editprompt` available in your PATH.
135
94
 
136
- ### 💡 Basic Usage
95
+ ## ⚙️ Setup & Configuration
96
+
97
+ ### Basic Setup
137
98
 
138
99
  ```bash
139
100
  # Use with your default editor (from $EDITOR)
@@ -150,21 +111,91 @@ editprompt --always-copy
150
111
  editprompt --help
151
112
  ```
152
113
 
114
+ ### Tmux Integration
153
115
 
154
- ## 📤 Send Without Closing Editor
116
+ ```tmux
117
+ bind -n M-q run-shell '\
118
+ editprompt --resume --target-pane #{pane_id} || \
119
+ tmux split-window -v -l 10 -c "#{pane_current_path}" \
120
+ "editprompt --editor nvim --always-copy --target-pane #{pane_id}"'
121
+ ```
122
+
123
+
124
+ ### WezTerm Integration
125
+
126
+ ```lua
127
+ {
128
+ key = "q",
129
+ mods = "OPT",
130
+ action = wezterm.action_callback(function(window, pane)
131
+ local target_pane_id = tostring(pane:pane_id())
132
+
133
+ -- Try to resume existing editor pane
134
+ local success, stdout, stderr = wezterm.run_child_process({
135
+ "/bin/zsh",
136
+ "-lc",
137
+ string.format(
138
+ "editprompt --resume --mux wezterm --target-pane %s",
139
+ target_pane_id
140
+ ),
141
+ })
142
+
143
+ -- If resume failed, create new editor pane
144
+ if not success then
145
+ window:perform_action(
146
+ act.SplitPane({
147
+ direction = "Down",
148
+ size = { Cells = 10 },
149
+ command = {
150
+ args = {
151
+ "/bin/zsh",
152
+ "-lc",
153
+ string.format(
154
+ "editprompt --editor nvim --always-copy --mux wezterm --target-pane %s",
155
+ target_pane_id
156
+ ),
157
+ },
158
+ },
159
+ }),
160
+ pane
161
+ )
162
+ end
163
+ end),
164
+ },
165
+ ```
166
+
167
+ **Note:** The `-lc` flag ensures your shell loads the full login environment, making `editprompt` available in your PATH.
168
+
169
+
170
+ ### Editor Integration (Send Without Closing)
155
171
 
156
172
  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.
157
173
 
158
- ### Basic Usage
174
+ #### Command Line Usage
159
175
 
160
176
  ```bash
161
177
  # Run this command from within your editor session
162
178
  editprompt -- "your content here"
179
+ # Sends content to target pane and moves focus there
180
+
181
+ editprompt --auto-send -- "your content here"
182
+ # Sends content, automatically submits it (presses Enter), and returns focus to editor pane
183
+ # Perfect for iterating on prompts without leaving your editor
184
+
185
+ editprompt --auto-send --send-key "C-m" -- "your content here"
186
+ # Customize the key to send after content (tmux format example)
187
+ # WezTerm example: --send-key "\r" (default for WezTerm is \r, tmux default is Enter)
163
188
  ```
164
189
 
165
190
  This sends the content to the target pane (or clipboard) while keeping your editor open, so you can continue editing and send multiple times.
166
191
 
167
- ### Neovim Integration Example
192
+ **Options:**
193
+ - `--auto-send`: Automatically sends the content and returns focus to your editor pane (requires multiplexer)
194
+ - `--send-key <key>`: Customize the key to send after content (requires `--auto-send`)
195
+ - tmux format: `Enter` (default), `C-a`, etc.
196
+ - WezTerm format: `\r` (default), `\x01`, etc.
197
+
198
+ #### Neovim Integration Example
168
199
 
169
200
  You can set up a convenient keybinding to send your buffer content:
170
201
 
@@ -198,155 +229,180 @@ if vim.env.EDITPROMPT then
198
229
  end
199
230
  ```
200
231
 
201
- With this configuration:
202
- 1. Open editprompt using the tmux/wezterm keybinding mentioned above
232
+ **Usage:**
233
+ 1. Open editprompt using the tmux/wezterm keybinding
203
234
  2. Write your prompt in the editor
204
235
  3. Press `<Space>x` to send the content to the target pane
205
236
  4. The buffer is automatically cleared on success
206
237
  5. Continue editing to send more content
207
238
 
239
+ ### Quote Workflow Setup
208
240
 
209
- ## ⚙️ Configuration
241
+ #### Collecting Quotes in tmux Copy Mode
210
242
 
211
- ### 📝 Editor Selection
243
+ Add this keybinding to your `.tmux.conf` to collect selected text as quotes:
212
244
 
213
- editprompt respects the following editor priority:
214
-
215
- 1. `--editor/-e` command line option
216
- 2. `$EDITOR` environment variable
217
- 3. Default: `vim`
218
-
219
- ### 🌍 Environment Variables
220
-
221
- - `EDITOR`: Your preferred text editor
245
+ ```tmux
246
+ bind-key -T copy-mode-vi C-e { send-keys -X pipe "editprompt --quote --target-pane #{pane_id}" }
247
+ ```
222
248
 
223
- ### 🔧 Editor Integration with EDITPROMPT
249
+ **Usage:**
250
+ 1. Enter tmux copy mode (`prefix + [`)
251
+ 2. Select text using vi-mode keybindings
252
+ 3. Press `Ctrl-e` to add the selection as a quote
253
+ 4. Repeat to collect multiple quotes
254
+ 5. All quotes are stored in a pane variable associated with the target pane
224
255
 
225
- editprompt automatically sets `EDITPROMPT=1` when launching your editor. This allows you to detect when your editor is launched by editprompt and enable specific configurations or plugins.
256
+ #### Collecting Quotes in WezTerm
226
257
 
227
- #### 🔍 Example: Neovim Configuration
258
+ Add this event handler and keybinding to your `wezterm.lua` to collect selected text as quotes:
228
259
 
229
260
  ```lua
230
- -- In your Neovim config (e.g., init.lua)
231
- if vim.env.EDITPROMPT then
232
- vim.opt.wrap = true
233
- -- Load a specific colorscheme
234
- vim.cmd('colorscheme blue')
235
- end
261
+ local wezterm = require("wezterm")
262
+
263
+ wezterm.on("editprompt-quote", function(window, pane)
264
+ local text = window:get_selection_text_for_pane(pane)
265
+ local target_pane_id = tostring(pane:pane_id())
266
+
267
+ wezterm.run_child_process({
268
+ "/bin/zsh",
269
+ "-lc",
270
+ string.format(
271
+ "editprompt --quote --mux wezterm --target-pane %s -- %s",
272
+ target_pane_id,
273
+ wezterm.shell_quote_arg(text)
274
+ ),
275
+ })
276
+ end)
277
+
278
+ return {
279
+ keys = {
280
+ {
281
+ key = "e",
282
+ mods = "CTRL",
283
+ action = wezterm.action.EmitEvent("editprompt-quote"),
284
+ },
285
+ },
286
+ }
236
287
  ```
237
288
 
238
- #### 🛠️ Setting Custom Environment Variables
289
+ **Usage:**
290
+ 1. Select text in WezTerm (by dragging with mouse or using copy mode)
291
+ 2. Press `Ctrl-e` to add the selection as a quote
292
+ 3. Repeat to collect multiple quotes
293
+ 4. All quotes are stored in a configuration file associated with the target pane
239
294
 
240
- You can also pass custom environment variables to your editor:
295
+ #### Capturing Collected Quotes
241
296
 
242
- ```bash
243
- # Single environment variable
244
- editprompt --env THEME=dark
245
-
246
- # Multiple environment variables
247
- editprompt --env THEME=dark --env FOO=fooooo
297
+ Run this command from within your editor pane to retrieve all collected quotes:
248
298
 
249
- # Useful for editor-specific configurations
250
- editprompt --env NVIM_CONFIG=minimal
299
+ ```bash
300
+ editprompt --capture
251
301
  ```
252
302
 
253
- ---
303
+ This copies all collected quotes to the clipboard and clears the buffer, ready for your reply.
254
304
 
255
- ## 🔧 Development
305
+ **Complete workflow:**
306
+ 1. AI responds with multiple points
307
+ 2. Select each point in copy mode and press `Ctrl-e`
308
+ 3. Open your editor pane and run `editprompt --capture`
309
+ 4. Edit the quoted text with your responses
310
+ 5. Send to AI
256
311
 
257
- ```bash
258
- # Clone the repository
259
- git clone https://github.com/eetann/editprompt.git
260
- cd editprompt
312
+ **How quote buffering works:**
313
+ - **tmux**: Quotes are stored in pane variables, automatically cleaned up when the pane closes
314
+ - **WezTerm**: Quotes are stored in a configuration file associated with the pane
315
+ - Text is intelligently processed: removes common indentation, handles line breaks smartly
316
+ - Each quote is prefixed with `> ` in markdown quote format
317
+ - Multiple quotes are separated with blank lines
261
318
 
262
- # Install dependencies
263
- bun install
319
+ #### Neovim Integration Example
264
320
 
265
- # Build
266
- bun run build
321
+ You can set up a convenient keybinding to capture your quote content:
322
+ ```lua
323
+ vim.keymap.set("n", "<Space>X", function()
324
+ vim.cmd("update")
267
325
 
268
- # Run tests
269
- bun test
326
+ vim.system({ "editprompt", "--capture" }, { text = true }, function(obj)
327
+ vim.schedule(function()
328
+ if obj.code == 0 then
329
+ vim.cmd("silent write")
330
+ -- Split stdout by lines
331
+ local output_lines = vim.split(obj.stdout, "\n")
332
+
333
+ local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
334
+ local is_empty = #lines == 1 and lines[1] == ""
335
+
336
+ if is_empty then
337
+ -- If empty, overwrite from the beginning
338
+ vim.api.nvim_buf_set_lines(0, 0, -1, false, output_lines)
339
+ vim.cmd("normal 2j")
340
+ else
341
+ -- If not empty, append to the end
342
+ table.insert(output_lines, 1, "")
343
+ local line_count = vim.api.nvim_buf_line_count(0)
344
+ vim.api.nvim_buf_set_lines(
345
+ 0,
346
+ line_count,
347
+ line_count,
348
+ false,
349
+ output_lines
350
+ )
351
+ vim.cmd("normal 4j")
352
+ end
270
353
 
271
- # Development mode
272
- bun run dev
354
+ vim.cmd("silent write")
355
+ else
356
+ vim.notify(
357
+ "editprompt failed: " .. (obj.stderr or "unknown error"),
358
+ vim.log.levels.ERROR
359
+ )
360
+ end
361
+ end)
362
+ end)
363
+ end, { silent = true, desc = "Capture from editprompt quote mode" })
273
364
  ```
274
365
 
275
- ### 💻 Testing During Development
366
+ ### Environment Variables
276
367
 
277
- When developing, you can test the built `dist/index.js` directly:
368
+ #### Editor Selection
278
369
 
279
- #### Neovim Configuration
370
+ editprompt respects the following editor priority:
280
371
 
281
- ```diff
282
- - { "editprompt", "--", content },
283
- + { "node", vim.fn.expand("~/path/to/editprompt/dist/index.js"), "--", content },
284
- ```
372
+ 1. `--editor/-e` command line option
373
+ 2. `$EDITOR` environment variable
374
+ 3. Default: `vim`
285
375
 
286
- #### WezTerm Configuration
376
+ #### EDITPROMPT Environment Variable
287
377
 
288
- ```lua
289
- -- In your wezterm.lua
290
- local editprompt_cmd = "node " .. os.getenv("HOME") .. "/path/to/editprompt/dist/index.js"
378
+ editprompt automatically sets `EDITPROMPT=1` when launching your editor. This allows you to detect when your editor is launched by editprompt and enable specific configurations or plugins.
291
379
 
292
- {
293
- key = "e",
294
- mods = "OPT",
295
- action = wezterm.action_callback(function(window, pane)
296
- local target_pane_id = tostring(pane:pane_id())
297
-
298
- local success, stdout, stderr = wezterm.run_child_process({
299
- "/bin/zsh",
300
- "-lc",
301
- string.format(
302
- "%s --resume --mux wezterm --target-pane %s",
303
- editprompt_cmd,
304
- target_pane_id
305
- ),
306
- })
307
-
308
- if not success then
309
- window:perform_action(
310
- act.SplitPane({
311
- -- ...
312
- command = {
313
- args = {
314
- "/bin/zsh",
315
- "-lc",
316
- string.format(
317
- "%s --editor nvim --always-copy --mux wezterm --target-pane %s",
318
- editprompt_cmd,
319
- target_pane_id
320
- ),
321
- },
322
- },
323
- }),
324
- pane
325
- )
326
- end
327
- end),
328
- },
329
- ```
380
+ **Example: Neovim Configuration**
330
381
 
331
- #### tmux Configuration
382
+ ```lua
383
+ -- In your Neovim config (e.g., init.lua)
384
+ if vim.env.EDITPROMPT then
385
+ vim.opt.wrap = true
386
+ -- Load a specific colorscheme
387
+ vim.cmd('colorscheme blue')
388
+ end
389
+ ```
332
390
 
333
- ```tmux
334
- # In your .tmux.conf
335
- set-option -g @editprompt-cmd "node ~/path/to/editprompt/dist/index.js"
391
+ #### Custom Environment Variables
336
392
 
337
- bind-key -n M-q run-shell '\
338
- #{@editprompt-cmd} --resume --target-pane #{pane_id} || \
339
- tmux split-window -v -l 10 -c "#{pane_current_path}" \
340
- "#{@editprompt-cmd} --editor nvim --always-copy --target-pane #{pane_id}"'
341
- ```
393
+ You can also pass custom environment variables to your editor:
342
394
 
343
- This allows you to make changes, run `bun run build`, and test immediately without reinstalling globally.
395
+ ```bash
396
+ # Single environment variable
397
+ editprompt --env THEME=dark
344
398
 
345
- ## 🔍 Technical Details
399
+ # Multiple environment variables
400
+ editprompt --env THEME=dark --env FOO=fooooo
346
401
 
347
- ### 🔄 Fallback Strategy
402
+ # Useful for editor-specific configurations
403
+ editprompt --env NVIM_CONFIG=minimal
404
+ ```
348
405
 
349
- editprompt implements a robust fallback strategy:
406
+ #### Target Pane Environment Variable
350
407
 
351
- 1. **Tmux Integration**: Direct input to tmux panes (when available)
352
- 2. **Clipboard**: Copy content to clipboard with user notification
408
+ When using the send-without-closing feature or quote capture, editprompt sets `EDITPROMPT_TARGET_PANE` to the target pane ID. This is automatically used by `editprompt --` and `editprompt --capture` commands.
package/dist/index.js CHANGED
@@ -1,114 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { cli } from "gunshi";
3
3
  import { exec, spawn } from "node:child_process";
4
+ import { promisify } from "node:util";
5
+ import Conf from "conf";
4
6
  import { mkdir, readFile, writeFile } from "node:fs/promises";
5
7
  import { tmpdir } from "node:os";
6
8
  import { join } from "node:path";
7
- import { promisify } from "node:util";
8
- import Conf from "conf";
9
9
  import clipboardy from "clipboardy";
10
10
 
11
11
  //#region package.json
12
- var version = "0.6.0";
13
-
14
- //#endregion
15
- //#region src/config/constants.ts
16
- const TEMP_FILE_PREFIX = ".editprompt-";
17
- const TEMP_FILE_EXTENSION = ".md";
18
- const DEFAULT_EDITOR = "vim";
19
-
20
- //#endregion
21
- //#region src/utils/contentProcessor.ts
22
- function processContent(content) {
23
- let processed = content.replace(/\n$/, "");
24
- if (/@[^\n]*$/.test(processed)) processed += " ";
25
- return processed;
26
- }
27
-
28
- //#endregion
29
- //#region src/utils/envParser.ts
30
- /**
31
- * Parses environment variable strings into an object.
32
- * @param envStrings - An array of strings in the format ["KEY=VALUE", "FOO=bar"].
33
- * @returns An object of environment variable key-value pairs.
34
- */
35
- function parseEnvVars(envStrings) {
36
- if (!envStrings || envStrings.length === 0) return {};
37
- const result = {};
38
- for (const envString of envStrings) {
39
- const [key, ...valueParts] = envString.split("=");
40
- if (!key || valueParts.length === 0) throw new Error(`Invalid environment variable format: ${envString}`);
41
- result[key] = valueParts.join("=");
42
- }
43
- return result;
44
- }
45
-
46
- //#endregion
47
- //#region src/utils/tempFile.ts
48
- function getFormattedDateTime() {
49
- const now = /* @__PURE__ */ new Date();
50
- return `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}${String(now.getSeconds()).padStart(2, "0")}`;
51
- }
52
- async function createTempFile() {
53
- const tempDir = join(tmpdir(), "editprompt-prompts");
54
- await mkdir(tempDir, { recursive: true });
55
- const filePath = join(tempDir, `${TEMP_FILE_PREFIX}${getFormattedDateTime()}${TEMP_FILE_EXTENSION}`);
56
- await writeFile(filePath, "", "utf-8");
57
- return filePath;
58
- }
59
-
60
- //#endregion
61
- //#region src/modules/editor.ts
62
- function getEditor(editorOption) {
63
- return editorOption || process.env.EDITOR || DEFAULT_EDITOR;
64
- }
65
- async function launchEditor(editor, filePath, envVars, sendConfig) {
66
- return new Promise((resolve, reject) => {
67
- const configEnv = {};
68
- if (sendConfig) {
69
- if (sendConfig.targetPane) configEnv.EDITPROMPT_TARGET_PANE = sendConfig.targetPane;
70
- configEnv.EDITPROMPT_MUX = sendConfig.mux;
71
- configEnv.EDITPROMPT_ALWAYS_COPY = sendConfig.alwaysCopy ? "1" : "0";
72
- }
73
- const processEnv = {
74
- ...process.env,
75
- EDITPROMPT: "1",
76
- ...configEnv,
77
- ...envVars
78
- };
79
- const editorProcess = spawn(editor, [filePath], {
80
- stdio: "inherit",
81
- shell: true,
82
- env: processEnv
83
- });
84
- editorProcess.on("error", (error) => {
85
- reject(/* @__PURE__ */ new Error(`Failed to launch editor: ${error.message}`));
86
- });
87
- editorProcess.on("exit", (code) => {
88
- if (code === 0) resolve();
89
- else reject(/* @__PURE__ */ new Error(`Editor exited with code: ${code}`));
90
- });
91
- });
92
- }
93
- async function readFileContent(filePath) {
94
- try {
95
- return processContent(await readFile(filePath, "utf-8"));
96
- } catch (error) {
97
- throw new Error(`Failed to read file: ${error instanceof Error ? error.message : "Unknown error"}`);
98
- }
99
- }
100
- async function openEditorAndGetContent(editorOption, envVars, sendConfig) {
101
- const tempFilePath = await createTempFile();
102
- const editor = getEditor(editorOption);
103
- const parsedEnvVars = parseEnvVars(envVars);
104
- try {
105
- await launchEditor(editor, tempFilePath, parsedEnvVars, sendConfig);
106
- return await readFileContent(tempFilePath);
107
- } catch (error) {
108
- if (error instanceof Error) throw error;
109
- throw new Error("An unknown error occurred");
110
- }
111
- }
12
+ var version = "0.8.0";
112
13
 
113
14
  //#endregion
114
15
  //#region src/modules/tmux.ts
@@ -162,11 +63,38 @@ async function isEditorPane(paneId) {
162
63
  return false;
163
64
  }
164
65
  }
66
+ async function getQuoteVariableContent(paneId) {
67
+ try {
68
+ const { stdout } = await execAsync$2(`tmux show -pt '${paneId}' -v @editprompt_quote`);
69
+ return stdout;
70
+ } catch {
71
+ return "";
72
+ }
73
+ }
74
+ async function appendToQuoteVariable(paneId, content) {
75
+ let newContent = "";
76
+ const existingContent = await getQuoteVariableContent(paneId);
77
+ if (existingContent.trim() !== "") newContent = `${existingContent}\n${content}`;
78
+ else newContent = content;
79
+ await execAsync$2(`tmux set-option -pt '${paneId}' @editprompt_quote '${newContent.replace(/'/g, "'\\''")}' `);
80
+ }
81
+ async function clearQuoteVariable(targetPaneId) {
82
+ await execAsync$2(`tmux set-option -pt '${targetPaneId}' @editprompt_quote ""`);
83
+ }
84
+ async function sendKeyToTmuxPane(paneId, key) {
85
+ await execAsync$2(`tmux send-keys -t '${paneId}' ${key}`);
86
+ }
87
+ async function sendContentToTmuxPaneNoFocus(paneId, content) {
88
+ await execAsync$2(`tmux if-shell -t '${paneId}' '[ "#{pane_in_mode}" = "1" ]' "copy-mode -q -t '${paneId}'"`);
89
+ await execAsync$2(`tmux send-keys -t '${paneId}' -- '${content.replace(/'/g, "'\\''")}'`);
90
+ console.log(`Content sent to tmux pane: ${paneId}`);
91
+ }
165
92
 
166
93
  //#endregion
167
94
  //#region src/modules/wezterm.ts
168
95
  const execAsync$1 = promisify(exec);
169
- const conf = new Conf({ projectName: "editprompt" });
96
+ const projectName = process.env.NODE_ENV === "test" ? "editprompt-test" : "editprompt";
97
+ const conf = new Conf({ projectName });
170
98
  async function getCurrentPaneId$1() {
171
99
  try {
172
100
  const { stdout } = await execAsync$1("wezterm cli list --format json");
@@ -235,6 +163,186 @@ function isEditorPaneFromConf(paneId) {
235
163
  return false;
236
164
  }
237
165
  }
166
+ async function appendToQuoteText(paneId, content) {
167
+ try {
168
+ const data = conf.get(`wezterm.targetPane.pane_${paneId}`);
169
+ let newData;
170
+ if (typeof data === "object" && data !== null) {
171
+ const existingQuoteText = "quote_text" in data ? String(data.quote_text) : "";
172
+ const newQuoteText = existingQuoteText.trim() !== "" ? `${existingQuoteText}\n\n${content}` : content;
173
+ newData = {
174
+ ...data,
175
+ quote_text: newQuoteText
176
+ };
177
+ } else newData = { quote_text: content };
178
+ conf.set(`wezterm.targetPane.pane_${paneId}`, newData);
179
+ } catch (error) {
180
+ console.log(error);
181
+ }
182
+ }
183
+ async function getQuoteText(paneId) {
184
+ try {
185
+ const data = conf.get(`wezterm.targetPane.pane_${paneId}`);
186
+ if (typeof data === "object" && data !== null && "quote_text" in data) return String(data.quote_text);
187
+ return "";
188
+ } catch (error) {
189
+ console.log(error);
190
+ return "";
191
+ }
192
+ }
193
+ async function clearQuoteText(paneId) {
194
+ try {
195
+ const key = `wezterm.targetPane.pane_${paneId}.quote_text`;
196
+ if (conf.has(key)) conf.delete(key);
197
+ } catch (error) {
198
+ console.log(error);
199
+ }
200
+ }
201
+ async function sendKeyToWeztermPane(paneId, key) {
202
+ await execAsync$1(`wezterm cli send-text --no-paste --pane-id '${paneId}' $'${key}'`);
203
+ }
204
+ async function sendContentToWeztermPaneNoFocus(paneId, content) {
205
+ await execAsync$1(`wezterm cli send-text --no-paste --pane-id '${paneId}' -- '${content.replace(/'/g, "'\\''")}'`);
206
+ console.log(`Content sent to wezterm pane: ${paneId}`);
207
+ }
208
+
209
+ //#endregion
210
+ //#region src/utils/sendConfig.ts
211
+ const VALID_MUX_TYPES = ["tmux", "wezterm"];
212
+ function readSendConfig() {
213
+ const targetPane = process.env.EDITPROMPT_TARGET_PANE;
214
+ const muxValue = process.env.EDITPROMPT_MUX || "tmux";
215
+ if (!VALID_MUX_TYPES.includes(muxValue)) throw new Error(`Invalid EDITPROMPT_MUX value: ${muxValue}. Must be one of: ${VALID_MUX_TYPES.join(", ")}`);
216
+ return {
217
+ targetPane,
218
+ mux: muxValue,
219
+ alwaysCopy: process.env.EDITPROMPT_ALWAYS_COPY === "1"
220
+ };
221
+ }
222
+
223
+ //#endregion
224
+ //#region src/modes/capture.ts
225
+ async function runCaptureMode() {
226
+ try {
227
+ const config = readSendConfig();
228
+ if (!config.targetPane) {
229
+ console.error("Error: EDITPROMPT_TARGET_PANE environment variable is required in capture mode");
230
+ process.exit(1);
231
+ }
232
+ let quoteContent;
233
+ if (config.mux === "tmux") {
234
+ quoteContent = await getQuoteVariableContent(config.targetPane);
235
+ await clearQuoteVariable(config.targetPane);
236
+ } else {
237
+ quoteContent = await getQuoteText(config.targetPane);
238
+ await clearQuoteText(config.targetPane);
239
+ }
240
+ process.stdout.write(quoteContent.replace(/\n{3,}$/, "\n\n"));
241
+ process.exit(0);
242
+ } catch (error) {
243
+ console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
244
+ process.exit(1);
245
+ }
246
+ }
247
+
248
+ //#endregion
249
+ //#region src/config/constants.ts
250
+ const TEMP_FILE_PREFIX = ".editprompt-";
251
+ const TEMP_FILE_EXTENSION = ".md";
252
+ const DEFAULT_EDITOR = "vim";
253
+
254
+ //#endregion
255
+ //#region src/utils/contentProcessor.ts
256
+ function processContent(content) {
257
+ let processed = content.replace(/\n$/, "");
258
+ if (/@[^\n]*$/.test(processed)) processed += " ";
259
+ return processed;
260
+ }
261
+
262
+ //#endregion
263
+ //#region src/utils/envParser.ts
264
+ /**
265
+ * Parses environment variable strings into an object.
266
+ * @param envStrings - An array of strings in the format ["KEY=VALUE", "FOO=bar"].
267
+ * @returns An object of environment variable key-value pairs.
268
+ */
269
+ function parseEnvVars(envStrings) {
270
+ if (!envStrings || envStrings.length === 0) return {};
271
+ const result = {};
272
+ for (const envString of envStrings) {
273
+ const [key, ...valueParts] = envString.split("=");
274
+ if (!key || valueParts.length === 0) throw new Error(`Invalid environment variable format: ${envString}`);
275
+ result[key] = valueParts.join("=");
276
+ }
277
+ return result;
278
+ }
279
+
280
+ //#endregion
281
+ //#region src/utils/tempFile.ts
282
+ function getFormattedDateTime() {
283
+ const now = /* @__PURE__ */ new Date();
284
+ return `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}${String(now.getSeconds()).padStart(2, "0")}`;
285
+ }
286
+ async function createTempFile() {
287
+ const tempDir = join(tmpdir(), "editprompt-prompts");
288
+ await mkdir(tempDir, { recursive: true });
289
+ const filePath = join(tempDir, `${TEMP_FILE_PREFIX}${getFormattedDateTime()}${TEMP_FILE_EXTENSION}`);
290
+ await writeFile(filePath, "", "utf-8");
291
+ return filePath;
292
+ }
293
+
294
+ //#endregion
295
+ //#region src/modules/editor.ts
296
+ function getEditor(editorOption) {
297
+ return editorOption || process.env.EDITOR || DEFAULT_EDITOR;
298
+ }
299
+ async function launchEditor(editor, filePath, envVars, sendConfig) {
300
+ return new Promise((resolve, reject) => {
301
+ const configEnv = {};
302
+ if (sendConfig) {
303
+ if (sendConfig.targetPane) configEnv.EDITPROMPT_TARGET_PANE = sendConfig.targetPane;
304
+ configEnv.EDITPROMPT_MUX = sendConfig.mux;
305
+ configEnv.EDITPROMPT_ALWAYS_COPY = sendConfig.alwaysCopy ? "1" : "0";
306
+ }
307
+ const processEnv = {
308
+ ...process.env,
309
+ EDITPROMPT: "1",
310
+ ...configEnv,
311
+ ...envVars
312
+ };
313
+ const editorProcess = spawn(editor, [filePath], {
314
+ stdio: "inherit",
315
+ shell: true,
316
+ env: processEnv
317
+ });
318
+ editorProcess.on("error", (error) => {
319
+ reject(/* @__PURE__ */ new Error(`Failed to launch editor: ${error.message}`));
320
+ });
321
+ editorProcess.on("exit", (code) => {
322
+ if (code === 0) resolve();
323
+ else reject(/* @__PURE__ */ new Error(`Editor exited with code: ${code}`));
324
+ });
325
+ });
326
+ }
327
+ async function readFileContent(filePath) {
328
+ try {
329
+ return processContent(await readFile(filePath, "utf-8"));
330
+ } catch (error) {
331
+ throw new Error(`Failed to read file: ${error instanceof Error ? error.message : "Unknown error"}`);
332
+ }
333
+ }
334
+ async function openEditorAndGetContent(editorOption, envVars, sendConfig) {
335
+ const tempFilePath = await createTempFile();
336
+ const editor = getEditor(editorOption);
337
+ const parsedEnvVars = parseEnvVars(envVars);
338
+ try {
339
+ await launchEditor(editor, tempFilePath, parsedEnvVars, sendConfig);
340
+ return await readFileContent(tempFilePath);
341
+ } catch (error) {
342
+ if (error instanceof Error) throw error;
343
+ throw new Error("An unknown error occurred");
344
+ }
345
+ }
238
346
 
239
347
  //#endregion
240
348
  //#region src/modules/process.ts
@@ -270,6 +378,50 @@ async function sendContentToPane(content, mux, targetPaneId, alwaysCopy) {
270
378
  }
271
379
  }
272
380
 
381
+ //#endregion
382
+ //#region src/modes/sendOnly.ts
383
+ async function sendContentToPaneWithAutoSend(content, mux, targetPaneId, sendKey) {
384
+ if (mux === "wezterm") {
385
+ await sendContentToWeztermPaneNoFocus(targetPaneId, content);
386
+ await sendKeyToWeztermPane(targetPaneId, sendKey);
387
+ } else {
388
+ await sendContentToTmuxPaneNoFocus(targetPaneId, content);
389
+ await sendKeyToTmuxPane(targetPaneId, sendKey);
390
+ }
391
+ }
392
+ async function runSendOnlyMode(rawContent, autoSend, sendKey) {
393
+ const content = processContent(rawContent);
394
+ if (!content) {
395
+ console.log("No content to send. Exiting.");
396
+ return;
397
+ }
398
+ const config = readSendConfig();
399
+ if (!config.targetPane) {
400
+ console.error("Error: EDITPROMPT_TARGET_PANE environment variable is required in send-only mode");
401
+ process.exit(1);
402
+ }
403
+ if (autoSend) {
404
+ if (!config.mux) {
405
+ console.error("Error: --auto-send requires a multiplexer (tmux or wezterm)");
406
+ process.exit(1);
407
+ }
408
+ try {
409
+ const key = sendKey || (config.mux === "wezterm" ? "\\r" : "Enter");
410
+ await handleAutoSendDelivery(content, config.mux, config.targetPane, key);
411
+ } catch (error) {
412
+ console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
413
+ process.exit(1);
414
+ }
415
+ return;
416
+ }
417
+ try {
418
+ await handleContentDelivery(content, config.mux, config.targetPane, config.alwaysCopy);
419
+ } catch (error) {
420
+ console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
421
+ process.exit(1);
422
+ }
423
+ }
424
+
273
425
  //#endregion
274
426
  //#region src/modes/common.ts
275
427
  function outputContent(content) {
@@ -295,6 +447,17 @@ async function handleContentDelivery(content, mux, targetPane, alwaysCopy) {
295
447
  }
296
448
  outputContent(content);
297
449
  }
450
+ async function handleAutoSendDelivery(content, mux, targetPane, sendKey) {
451
+ if (!content || !targetPane) throw new Error("Content and target pane are required");
452
+ try {
453
+ await sendContentToPaneWithAutoSend(content, mux, targetPane, sendKey);
454
+ console.log("Content sent and submitted successfully!");
455
+ } catch (error) {
456
+ console.error(`Failed to send content: ${error instanceof Error ? error.message : "Unknown error"}`);
457
+ throw error;
458
+ }
459
+ outputContent(content);
460
+ }
298
461
 
299
462
  //#endregion
300
463
  //#region src/modes/openEditor.ts
@@ -336,6 +499,127 @@ async function runOpenEditorMode(options) {
336
499
  }
337
500
  }
338
501
 
502
+ //#endregion
503
+ //#region src/utils/quoteProcessor.ts
504
+ /**
505
+ * Calculate the minimum leading whitespace count across all non-empty lines
506
+ */
507
+ function getMinLeadingWhitespace(lines) {
508
+ let min = 99;
509
+ for (const line of lines) {
510
+ if (line.length === 0) continue;
511
+ const match = line.match(/^[ \t]*/);
512
+ const count = match ? match[0].length : 0;
513
+ if (count < min) min = count;
514
+ }
515
+ return min === 99 ? 0 : min;
516
+ }
517
+ /**
518
+ * Check if we need a space separator between two lines
519
+ */
520
+ function needsSpaceSeparator(prevLine, currentLine) {
521
+ if (prevLine.length === 0 || currentLine.length === 0) return false;
522
+ const lastChar = prevLine[prevLine.length - 1] ?? "";
523
+ const firstChar = currentLine[0] ?? "";
524
+ const isLastCharAlpha = /[a-zA-Z]/.test(lastChar);
525
+ const isFirstCharAlpha = /[a-zA-Z]/.test(firstChar);
526
+ return isLastCharAlpha && isFirstCharAlpha;
527
+ }
528
+ /**
529
+ * Determine if two lines should be merged
530
+ */
531
+ function shouldMergeLines(prevLine, currentLine) {
532
+ if (/^[-*+]\s/.test(currentLine)) return false;
533
+ const hasColon = (line) => line.includes(":") || line.includes(":");
534
+ if (hasColon(prevLine) && hasColon(currentLine)) return false;
535
+ return true;
536
+ }
537
+ /**
538
+ * Remove common leading whitespace and merge lines
539
+ */
540
+ function removeWhitespaceAndMergeLines(lines) {
541
+ const minWhitespace = getMinLeadingWhitespace(lines);
542
+ const trimmedLines = lines.map((line) => {
543
+ if (line.length === 0) return line;
544
+ return line.slice(minWhitespace);
545
+ });
546
+ const result = [];
547
+ let currentLine = "";
548
+ for (let i = 0; i < trimmedLines.length; i++) {
549
+ const line = trimmedLines[i] ?? "";
550
+ if (i === 0) {
551
+ currentLine = line;
552
+ continue;
553
+ }
554
+ if (line.length === 0) {
555
+ result.push(currentLine);
556
+ result.push("");
557
+ currentLine = "";
558
+ continue;
559
+ }
560
+ if (currentLine.length === 0) {
561
+ currentLine = line;
562
+ continue;
563
+ }
564
+ const prevLine = trimmedLines[i - 1] ?? "";
565
+ if (shouldMergeLines(prevLine, line)) {
566
+ const separator = needsSpaceSeparator(prevLine, line) ? " " : "";
567
+ currentLine += separator + line;
568
+ } else {
569
+ result.push(currentLine);
570
+ currentLine = line;
571
+ }
572
+ }
573
+ if (currentLine !== "") result.push(currentLine);
574
+ return result;
575
+ }
576
+ /**
577
+ * Processes text for quote buffering by:
578
+ * 1. Detecting if 2nd+ lines have no leading whitespace (Pattern A) or all lines have common leading whitespace (Pattern B)
579
+ * 2. Pattern A: Remove only leading whitespace, preserve all line breaks
580
+ * 3. Pattern B: Remove common leading whitespace and merge lines (with exceptions)
581
+ * 4. Adding quote prefix ("> ") to each line
582
+ * 5. Adding two newlines at the end
583
+ */
584
+ function processQuoteText(text) {
585
+ const lines = text.replace(/^\n+|\n+$/g, "").split("\n");
586
+ const hasNoLeadingWhitespaceInLaterLines = lines.slice(1).some((line) => line.length > 0 && !line.startsWith(" ") && !line.startsWith(" "));
587
+ let processedLines;
588
+ if (hasNoLeadingWhitespaceInLaterLines) processedLines = lines.map((line) => line.trimStart());
589
+ else processedLines = removeWhitespaceAndMergeLines(lines);
590
+ return `${processedLines.map((line) => `> ${line}`).join("\n")}\n\n`;
591
+ }
592
+
593
+ //#endregion
594
+ //#region src/modes/quote.ts
595
+ async function readStdin() {
596
+ return new Promise((resolve, reject) => {
597
+ const chunks = [];
598
+ process.stdin.on("data", (chunk) => {
599
+ chunks.push(chunk);
600
+ });
601
+ process.stdin.on("end", () => {
602
+ resolve(Buffer.concat(chunks).toString("utf8"));
603
+ });
604
+ process.stdin.on("error", (error) => {
605
+ reject(error);
606
+ });
607
+ });
608
+ }
609
+ async function runQuoteMode(mux, targetPaneId, rawContent) {
610
+ try {
611
+ let selection;
612
+ if (rawContent !== void 0) selection = rawContent;
613
+ else selection = await readStdin();
614
+ const processedText = processQuoteText(selection);
615
+ if (mux === "tmux") await appendToQuoteVariable(targetPaneId, processedText);
616
+ else if (mux === "wezterm") await appendToQuoteText(targetPaneId, processedText);
617
+ } catch (error) {
618
+ console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
619
+ process.exit(1);
620
+ }
621
+ }
622
+
339
623
  //#endregion
340
624
  //#region src/modes/resume.ts
341
625
  async function runResumeMode(targetPane, mux) {
@@ -393,41 +677,6 @@ async function runResumeMode(targetPane, mux) {
393
677
  process.exit(0);
394
678
  }
395
679
 
396
- //#endregion
397
- //#region src/utils/sendConfig.ts
398
- const VALID_MUX_TYPES = ["tmux", "wezterm"];
399
- function readSendConfig() {
400
- const targetPane = process.env.EDITPROMPT_TARGET_PANE;
401
- const muxValue = process.env.EDITPROMPT_MUX || "tmux";
402
- if (!VALID_MUX_TYPES.includes(muxValue)) throw new Error(`Invalid EDITPROMPT_MUX value: ${muxValue}. Must be one of: ${VALID_MUX_TYPES.join(", ")}`);
403
- return {
404
- targetPane,
405
- mux: muxValue,
406
- alwaysCopy: process.env.EDITPROMPT_ALWAYS_COPY === "1"
407
- };
408
- }
409
-
410
- //#endregion
411
- //#region src/modes/sendOnly.ts
412
- async function runSendOnlyMode(rawContent) {
413
- const content = processContent(rawContent);
414
- if (!content) {
415
- console.log("No content to send. Exiting.");
416
- return;
417
- }
418
- const config = readSendConfig();
419
- if (!config.targetPane) {
420
- console.error("Error: EDITPROMPT_TARGET_PANE environment variable is required in send-only mode");
421
- process.exit(1);
422
- }
423
- try {
424
- await handleContentDelivery(content, config.mux, config.targetPane, config.alwaysCopy);
425
- } catch (error) {
426
- console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
427
- process.exit(1);
428
- }
429
- }
430
-
431
680
  //#endregion
432
681
  //#region src/utils/argumentParser.ts
433
682
  /**
@@ -482,10 +731,30 @@ await cli(process.argv.slice(2), {
482
731
  "always-copy": {
483
732
  description: "Always copy content to clipboard, even if tmux pane is available",
484
733
  type: "boolean"
734
+ },
735
+ quote: {
736
+ description: "Quote buffering mode - add quoted text to pane variable",
737
+ type: "boolean"
738
+ },
739
+ capture: {
740
+ description: "Capture mode - copy pane variable to clipboard and clear",
741
+ type: "boolean"
742
+ },
743
+ "auto-send": {
744
+ description: "Automatically send content and return focus to editor pane",
745
+ type: "boolean"
746
+ },
747
+ "send-key": {
748
+ description: "Key to send after content (default: Enter, requires --auto-send)",
749
+ type: "string"
485
750
  }
486
751
  },
487
752
  async run(ctx) {
488
753
  try {
754
+ if (ctx.values["send-key"] && !ctx.values["auto-send"]) {
755
+ console.error("Error: --send-key requires --auto-send option");
756
+ process.exit(1);
757
+ }
489
758
  if (ctx.values.resume) {
490
759
  if (!ctx.values["target-pane"]) {
491
760
  console.error("Error: --target-pane is required when using --resume");
@@ -499,9 +768,33 @@ await cli(process.argv.slice(2), {
499
768
  await runResumeMode(ctx.values["target-pane"], mux$1);
500
769
  return;
501
770
  }
771
+ if (ctx.values.quote) {
772
+ if (!ctx.values["target-pane"]) {
773
+ console.error("Error: --target-pane is required when using --quote");
774
+ process.exit(1);
775
+ }
776
+ const muxValue$1 = ctx.values.mux || "tmux";
777
+ if (!isMuxType(muxValue$1)) {
778
+ console.error(`Error: Invalid mux type '${muxValue$1}'. Supported values: tmux, wezterm`);
779
+ process.exit(1);
780
+ }
781
+ if (muxValue$1 === "wezterm") {
782
+ const rawContent$1 = extractRawContent(ctx.rest, ctx.positionals);
783
+ if (rawContent$1 === void 0) {
784
+ console.error("Error: Text content is required for quote mode with wezterm. Use: editprompt --quote --mux wezterm --target-pane <id> -- \"<text>\"");
785
+ process.exit(1);
786
+ }
787
+ await runQuoteMode(muxValue$1, ctx.values["target-pane"], rawContent$1);
788
+ } else await runQuoteMode(muxValue$1, ctx.values["target-pane"]);
789
+ return;
790
+ }
791
+ if (ctx.values.capture) {
792
+ await runCaptureMode();
793
+ return;
794
+ }
502
795
  const rawContent = extractRawContent(ctx.rest, ctx.positionals);
503
796
  if (rawContent !== void 0) {
504
- await runSendOnlyMode(rawContent);
797
+ await runSendOnlyMode(rawContent, ctx.values["auto-send"], ctx.values["send-key"]);
505
798
  return;
506
799
  }
507
800
  const muxValue = ctx.values.mux || "tmux";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "editprompt",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "author": "eetann",
5
5
  "description": "A CLI tool that lets you write prompts for CLI tools using your favorite text editor",
6
6
  "license": "MIT",