editprompt 0.5.2 β 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +249 -99
- package/dist/index.js +475 -24
- package/package.json +2 -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/
|
|
9
|
+

|
|
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
|
-
-
|
|
13
|
-
-
|
|
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,67 +40,61 @@ npx editprompt
|
|
|
34
40
|
|
|
35
41
|
## π Usage
|
|
36
42
|
|
|
37
|
-
|
|
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
|
-
|
|
44
|
-
editprompt
|
|
45
|
-
```
|
|
45
|
+
### Workflow 1: Basic - Write and Send
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+

|
|
48
48
|
|
|
49
|
+
The simplest way to use editprompt:
|
|
49
50
|
|
|
50
|
-
|
|
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
|
-
|
|
53
|
-
```tmux
|
|
54
|
-
bind -n M-q run-shell 'tmux split-window -v -l 20 \
|
|
55
|
-
-c "#{pane_current_path}" \
|
|
56
|
-
"editprompt --editor nvim --always-copy --target-pane #{pane_id}"'
|
|
57
|
-
```
|
|
56
|
+
Perfect for one-off prompts when you need more space than a terminal input line.
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
```tmux
|
|
61
|
-
bind -n M-q run-shell 'tmux display-popup -E \
|
|
62
|
-
-d "#{pane_current_path}" \
|
|
63
|
-
-w 80% -h 65% \
|
|
64
|
-
"editprompt --editor nvim --always-copy --target-pane #{pane_id}"'
|
|
65
|
-
```
|
|
58
|
+
### Workflow 2: Interactive - Iterate with Editor Open
|
|
66
59
|
|
|
60
|
+

|
|
67
61
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
),
|
|
90
|
-
window:active_pane()
|
|
91
|
-
)
|
|
92
|
-
end)
|
|
93
|
-
end),
|
|
94
|
-
},
|
|
62
|
+
For iterating on prompts without constantly reopening the editor:
|
|
63
|
+
|
|
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
|
|
68
|
+
|
|
69
|
+
Ideal for trial-and-error workflows with AI assistants.
|
|
70
|
+
|
|
71
|
+
### Workflow 3: Quote - Collect and Reply
|
|
72
|
+
|
|
73
|
+

|
|
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 -->
|
|
95
83
|
```
|
|
96
84
|
|
|
97
|
-
|
|
85
|
+
For replying to specific parts of AI responses:
|
|
86
|
+
|
|
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
|
|
91
|
+
|
|
92
|
+
Perfect for addressing multiple points in long AI responses.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
## βοΈ Setup & Configuration
|
|
96
|
+
|
|
97
|
+
### Basic Setup
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
100
|
# Use with your default editor (from $EDITOR)
|
|
@@ -111,12 +111,67 @@ editprompt --always-copy
|
|
|
111
111
|
editprompt --help
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
+
### Tmux Integration
|
|
115
|
+
|
|
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
|
+
|
|
114
123
|
|
|
115
|
-
|
|
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)
|
|
116
171
|
|
|
117
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.
|
|
118
173
|
|
|
119
|
-
|
|
174
|
+
#### Command Line Usage
|
|
120
175
|
|
|
121
176
|
```bash
|
|
122
177
|
# Run this command from within your editor session
|
|
@@ -125,7 +180,7 @@ editprompt -- "your content here"
|
|
|
125
180
|
|
|
126
181
|
This sends the content to the target pane (or clipboard) while keeping your editor open, so you can continue editing and send multiple times.
|
|
127
182
|
|
|
128
|
-
|
|
183
|
+
#### Neovim Integration Example
|
|
129
184
|
|
|
130
185
|
You can set up a convenient keybinding to send your buffer content:
|
|
131
186
|
|
|
@@ -159,33 +214,155 @@ if vim.env.EDITPROMPT then
|
|
|
159
214
|
end
|
|
160
215
|
```
|
|
161
216
|
|
|
162
|
-
|
|
163
|
-
1. Open editprompt using the tmux/wezterm keybinding
|
|
217
|
+
**Usage:**
|
|
218
|
+
1. Open editprompt using the tmux/wezterm keybinding
|
|
164
219
|
2. Write your prompt in the editor
|
|
165
220
|
3. Press `<Space>x` to send the content to the target pane
|
|
166
221
|
4. The buffer is automatically cleared on success
|
|
167
222
|
5. Continue editing to send more content
|
|
168
223
|
|
|
224
|
+
### Quote Workflow Setup
|
|
225
|
+
|
|
226
|
+
#### Collecting Quotes in tmux Copy Mode
|
|
169
227
|
|
|
170
|
-
|
|
228
|
+
Add this keybinding to your `.tmux.conf` to collect selected text as quotes:
|
|
171
229
|
|
|
172
|
-
|
|
230
|
+
```tmux
|
|
231
|
+
bind-key -T copy-mode-vi C-e { send-keys -X pipe "editprompt --quote --target-pane #{pane_id}" }
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Usage:**
|
|
235
|
+
1. Enter tmux copy mode (`prefix + [`)
|
|
236
|
+
2. Select text using vi-mode keybindings
|
|
237
|
+
3. Press `Ctrl-e` to add the selection as a quote
|
|
238
|
+
4. Repeat to collect multiple quotes
|
|
239
|
+
5. All quotes are stored in a pane variable associated with the target pane
|
|
240
|
+
|
|
241
|
+
#### Collecting Quotes in WezTerm
|
|
242
|
+
|
|
243
|
+
Add this event handler and keybinding to your `wezterm.lua` to collect selected text as quotes:
|
|
244
|
+
|
|
245
|
+
```lua
|
|
246
|
+
local wezterm = require("wezterm")
|
|
247
|
+
|
|
248
|
+
wezterm.on("editprompt-quote", function(window, pane)
|
|
249
|
+
local text = window:get_selection_text_for_pane(pane)
|
|
250
|
+
local target_pane_id = tostring(pane:pane_id())
|
|
251
|
+
|
|
252
|
+
wezterm.run_child_process({
|
|
253
|
+
"/bin/zsh",
|
|
254
|
+
"-lc",
|
|
255
|
+
string.format(
|
|
256
|
+
"editprompt --quote --mux wezterm --target-pane %s -- %s",
|
|
257
|
+
target_pane_id,
|
|
258
|
+
wezterm.shell_quote_arg(text)
|
|
259
|
+
),
|
|
260
|
+
})
|
|
261
|
+
end)
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
keys = {
|
|
265
|
+
{
|
|
266
|
+
key = "e",
|
|
267
|
+
mods = "CTRL",
|
|
268
|
+
action = wezterm.action.EmitEvent("editprompt-quote"),
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Usage:**
|
|
275
|
+
1. Select text in WezTerm (by dragging with mouse or using copy mode)
|
|
276
|
+
2. Press `Ctrl-e` to add the selection as a quote
|
|
277
|
+
3. Repeat to collect multiple quotes
|
|
278
|
+
4. All quotes are stored in a configuration file associated with the target pane
|
|
279
|
+
|
|
280
|
+
#### Capturing Collected Quotes
|
|
281
|
+
|
|
282
|
+
Run this command from within your editor pane to retrieve all collected quotes:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
editprompt --capture
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
This copies all collected quotes to the clipboard and clears the buffer, ready for your reply.
|
|
289
|
+
|
|
290
|
+
**Complete workflow:**
|
|
291
|
+
1. AI responds with multiple points
|
|
292
|
+
2. Select each point in copy mode and press `Ctrl-e`
|
|
293
|
+
3. Open your editor pane and run `editprompt --capture`
|
|
294
|
+
4. Edit the quoted text with your responses
|
|
295
|
+
5. Send to AI
|
|
296
|
+
|
|
297
|
+
**How quote buffering works:**
|
|
298
|
+
- **tmux**: Quotes are stored in pane variables, automatically cleaned up when the pane closes
|
|
299
|
+
- **WezTerm**: Quotes are stored in a configuration file associated with the pane
|
|
300
|
+
- Text is intelligently processed: removes common indentation, handles line breaks smartly
|
|
301
|
+
- Each quote is prefixed with `> ` in markdown quote format
|
|
302
|
+
- Multiple quotes are separated with blank lines
|
|
303
|
+
|
|
304
|
+
#### Neovim Integration Example
|
|
305
|
+
|
|
306
|
+
You can set up a convenient keybinding to capture your quote content:
|
|
307
|
+
```lua
|
|
308
|
+
vim.keymap.set("n", "<Space>X", function()
|
|
309
|
+
vim.cmd("update")
|
|
310
|
+
|
|
311
|
+
vim.system({ "editprompt", "--capture" }, { text = true }, function(obj)
|
|
312
|
+
vim.schedule(function()
|
|
313
|
+
if obj.code == 0 then
|
|
314
|
+
vim.cmd("silent write")
|
|
315
|
+
-- Split stdout by lines
|
|
316
|
+
local output_lines = vim.split(obj.stdout, "\n")
|
|
317
|
+
|
|
318
|
+
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
|
|
319
|
+
local is_empty = #lines == 1 and lines[1] == ""
|
|
320
|
+
|
|
321
|
+
if is_empty then
|
|
322
|
+
-- If empty, overwrite from the beginning
|
|
323
|
+
vim.api.nvim_buf_set_lines(0, 0, -1, false, output_lines)
|
|
324
|
+
vim.cmd("normal 2j")
|
|
325
|
+
else
|
|
326
|
+
-- If not empty, append to the end
|
|
327
|
+
table.insert(output_lines, 1, "")
|
|
328
|
+
local line_count = vim.api.nvim_buf_line_count(0)
|
|
329
|
+
vim.api.nvim_buf_set_lines(
|
|
330
|
+
0,
|
|
331
|
+
line_count,
|
|
332
|
+
line_count,
|
|
333
|
+
false,
|
|
334
|
+
output_lines
|
|
335
|
+
)
|
|
336
|
+
vim.cmd("normal 4j")
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
vim.cmd("silent write")
|
|
340
|
+
else
|
|
341
|
+
vim.notify(
|
|
342
|
+
"editprompt failed: " .. (obj.stderr or "unknown error"),
|
|
343
|
+
vim.log.levels.ERROR
|
|
344
|
+
)
|
|
345
|
+
end
|
|
346
|
+
end)
|
|
347
|
+
end)
|
|
348
|
+
end, { silent = true, desc = "Capture from editprompt quote mode" })
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Environment Variables
|
|
352
|
+
|
|
353
|
+
#### Editor Selection
|
|
173
354
|
|
|
174
355
|
editprompt respects the following editor priority:
|
|
175
356
|
|
|
176
357
|
1. `--editor/-e` command line option
|
|
177
|
-
2. `$EDITOR` environment variable
|
|
358
|
+
2. `$EDITOR` environment variable
|
|
178
359
|
3. Default: `vim`
|
|
179
360
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
- `EDITOR`: Your preferred text editor
|
|
183
|
-
|
|
184
|
-
### π§ Editor Integration with EDITPROMPT
|
|
361
|
+
#### EDITPROMPT Environment Variable
|
|
185
362
|
|
|
186
363
|
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.
|
|
187
364
|
|
|
188
|
-
|
|
365
|
+
**Example: Neovim Configuration**
|
|
189
366
|
|
|
190
367
|
```lua
|
|
191
368
|
-- In your Neovim config (e.g., init.lua)
|
|
@@ -196,7 +373,7 @@ if vim.env.EDITPROMPT then
|
|
|
196
373
|
end
|
|
197
374
|
```
|
|
198
375
|
|
|
199
|
-
####
|
|
376
|
+
#### Custom Environment Variables
|
|
200
377
|
|
|
201
378
|
You can also pass custom environment variables to your editor:
|
|
202
379
|
|
|
@@ -211,33 +388,6 @@ editprompt --env THEME=dark --env FOO=fooooo
|
|
|
211
388
|
editprompt --env NVIM_CONFIG=minimal
|
|
212
389
|
```
|
|
213
390
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
## π§ Development
|
|
217
|
-
|
|
218
|
-
```bash
|
|
219
|
-
# Clone the repository
|
|
220
|
-
git clone https://github.com/eetann/editprompt.git
|
|
221
|
-
cd editprompt
|
|
222
|
-
|
|
223
|
-
# Install dependencies
|
|
224
|
-
bun install
|
|
225
|
-
|
|
226
|
-
# Build
|
|
227
|
-
bun run build
|
|
228
|
-
|
|
229
|
-
# Run tests
|
|
230
|
-
bun test
|
|
231
|
-
|
|
232
|
-
# Development mode
|
|
233
|
-
bun run dev
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
## π Technical Details
|
|
237
|
-
|
|
238
|
-
### π Fallback Strategy
|
|
239
|
-
|
|
240
|
-
editprompt implements a robust fallback strategy:
|
|
391
|
+
#### Target Pane Environment Variable
|
|
241
392
|
|
|
242
|
-
|
|
243
|
-
2. **Clipboard**: Copy content to clipboard with user notification
|
|
393
|
+
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,14 +1,234 @@
|
|
|
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
9
|
import clipboardy from "clipboardy";
|
|
9
10
|
|
|
10
11
|
//#region package.json
|
|
11
|
-
var version = "0.
|
|
12
|
+
var version = "0.7.0";
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/modules/tmux.ts
|
|
16
|
+
const execAsync$2 = promisify(exec);
|
|
17
|
+
async function getCurrentPaneId() {
|
|
18
|
+
const { stdout } = await execAsync$2("tmux display-message -p \"#{pane_id}\"");
|
|
19
|
+
return stdout.trim();
|
|
20
|
+
}
|
|
21
|
+
async function saveEditorPaneId(targetPaneId, editorPaneId) {
|
|
22
|
+
await execAsync$2(`tmux set-option -pt '${targetPaneId}' @editprompt_editor_pane '${editorPaneId}'`);
|
|
23
|
+
}
|
|
24
|
+
async function clearEditorPaneId(targetPaneId) {
|
|
25
|
+
await execAsync$2(`tmux set-option -pt '${targetPaneId}' @editprompt_editor_pane ""`);
|
|
26
|
+
}
|
|
27
|
+
async function getEditorPaneId(targetPaneId) {
|
|
28
|
+
try {
|
|
29
|
+
const { stdout } = await execAsync$2(`tmux show -pt '${targetPaneId}' -v @editprompt_editor_pane`);
|
|
30
|
+
return stdout.trim();
|
|
31
|
+
} catch {
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function checkPaneExists(paneId) {
|
|
36
|
+
try {
|
|
37
|
+
const { stdout } = await execAsync$2("tmux list-panes -a -F \"#{pane_id}\"");
|
|
38
|
+
return stdout.split("\n").map((id) => id.trim()).includes(paneId);
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function focusPane(paneId) {
|
|
44
|
+
await execAsync$2(`tmux select-pane -t '${paneId}'`);
|
|
45
|
+
}
|
|
46
|
+
async function markAsEditorPane(editorPaneId, targetPaneId) {
|
|
47
|
+
await execAsync$2(`tmux set-option -pt '${editorPaneId}' @editprompt_is_editor 1`);
|
|
48
|
+
await execAsync$2(`tmux set-option -pt '${editorPaneId}' @editprompt_target_pane '${targetPaneId}'`);
|
|
49
|
+
}
|
|
50
|
+
async function getTargetPaneId(editorPaneId) {
|
|
51
|
+
try {
|
|
52
|
+
const { stdout } = await execAsync$2(`tmux show -pt '${editorPaneId}' -v @editprompt_target_pane`);
|
|
53
|
+
return stdout.trim();
|
|
54
|
+
} catch {
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function isEditorPane(paneId) {
|
|
59
|
+
try {
|
|
60
|
+
const { stdout } = await execAsync$2(`tmux show -pt '${paneId}' -v @editprompt_is_editor`);
|
|
61
|
+
return stdout.trim() === "1";
|
|
62
|
+
} catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
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
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/modules/wezterm.ts
|
|
87
|
+
const execAsync$1 = promisify(exec);
|
|
88
|
+
const projectName = process.env.NODE_ENV === "test" ? "editprompt-test" : "editprompt";
|
|
89
|
+
const conf = new Conf({ projectName });
|
|
90
|
+
async function getCurrentPaneId$1() {
|
|
91
|
+
try {
|
|
92
|
+
const { stdout } = await execAsync$1("wezterm cli list --format json");
|
|
93
|
+
const activePane = JSON.parse(stdout).find((pane) => pane.is_active === true);
|
|
94
|
+
return String(activePane?.pane_id);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.log(error);
|
|
97
|
+
return "";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function checkPaneExists$1(paneId) {
|
|
101
|
+
try {
|
|
102
|
+
const { stdout } = await execAsync$1("wezterm cli list --format json");
|
|
103
|
+
console.log(stdout);
|
|
104
|
+
return JSON.parse(stdout).some((pane) => String(pane.pane_id) === paneId);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.log(error);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function getEditorPaneId$1(targetPaneId) {
|
|
111
|
+
try {
|
|
112
|
+
const data = conf.get(`wezterm.targetPane.pane_${targetPaneId}`);
|
|
113
|
+
if (typeof data === "object" && data !== null && "editorPaneId" in data) return String(data.editorPaneId);
|
|
114
|
+
return "";
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.log(error);
|
|
117
|
+
return "";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async function clearEditorPaneId$1(targetPaneId) {
|
|
121
|
+
try {
|
|
122
|
+
const editorPaneId = await getEditorPaneId$1(targetPaneId);
|
|
123
|
+
conf.delete(`wezterm.targetPane.pane_${targetPaneId}`);
|
|
124
|
+
if (editorPaneId) conf.delete(`wezterm.editorPane.pane_${editorPaneId}`);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.log(error);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function focusPane$1(paneId) {
|
|
130
|
+
await execAsync$1(`wezterm cli activate-pane --pane-id '${paneId}'`);
|
|
131
|
+
}
|
|
132
|
+
async function markAsEditorPane$1(editorPaneId, targetPaneId) {
|
|
133
|
+
try {
|
|
134
|
+
conf.set(`wezterm.targetPane.pane_${targetPaneId}`, { editorPaneId });
|
|
135
|
+
conf.set(`wezterm.editorPane.pane_${editorPaneId}`, { targetPaneId });
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.log(error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async function getTargetPaneId$1(editorPaneId) {
|
|
141
|
+
try {
|
|
142
|
+
const data = conf.get(`wezterm.editorPane.pane_${editorPaneId}`);
|
|
143
|
+
if (typeof data === "object" && data !== null && "targetPaneId" in data) return String(data.targetPaneId);
|
|
144
|
+
return "";
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.log(error);
|
|
147
|
+
return "";
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function isEditorPaneFromConf(paneId) {
|
|
151
|
+
try {
|
|
152
|
+
return conf.has(`wezterm.editorPane.pane_${paneId}`);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.log(error);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function appendToQuoteText(paneId, content) {
|
|
159
|
+
try {
|
|
160
|
+
const data = conf.get(`wezterm.targetPane.pane_${paneId}`);
|
|
161
|
+
let newData;
|
|
162
|
+
if (typeof data === "object" && data !== null) {
|
|
163
|
+
const existingQuoteText = "quote_text" in data ? String(data.quote_text) : "";
|
|
164
|
+
const newQuoteText = existingQuoteText.trim() !== "" ? `${existingQuoteText}\n\n${content}` : content;
|
|
165
|
+
newData = {
|
|
166
|
+
...data,
|
|
167
|
+
quote_text: newQuoteText
|
|
168
|
+
};
|
|
169
|
+
} else newData = { quote_text: content };
|
|
170
|
+
conf.set(`wezterm.targetPane.pane_${paneId}`, newData);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.log(error);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async function getQuoteText(paneId) {
|
|
176
|
+
try {
|
|
177
|
+
const data = conf.get(`wezterm.targetPane.pane_${paneId}`);
|
|
178
|
+
if (typeof data === "object" && data !== null && "quote_text" in data) return String(data.quote_text);
|
|
179
|
+
return "";
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.log(error);
|
|
182
|
+
return "";
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async function clearQuoteText(paneId) {
|
|
186
|
+
try {
|
|
187
|
+
const key = `wezterm.targetPane.pane_${paneId}.quote_text`;
|
|
188
|
+
if (conf.has(key)) conf.delete(key);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.log(error);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/utils/sendConfig.ts
|
|
196
|
+
const VALID_MUX_TYPES = ["tmux", "wezterm"];
|
|
197
|
+
function readSendConfig() {
|
|
198
|
+
const targetPane = process.env.EDITPROMPT_TARGET_PANE;
|
|
199
|
+
const muxValue = process.env.EDITPROMPT_MUX || "tmux";
|
|
200
|
+
if (!VALID_MUX_TYPES.includes(muxValue)) throw new Error(`Invalid EDITPROMPT_MUX value: ${muxValue}. Must be one of: ${VALID_MUX_TYPES.join(", ")}`);
|
|
201
|
+
return {
|
|
202
|
+
targetPane,
|
|
203
|
+
mux: muxValue,
|
|
204
|
+
alwaysCopy: process.env.EDITPROMPT_ALWAYS_COPY === "1"
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region src/modes/capture.ts
|
|
210
|
+
async function runCaptureMode() {
|
|
211
|
+
try {
|
|
212
|
+
const config = readSendConfig();
|
|
213
|
+
if (!config.targetPane) {
|
|
214
|
+
console.error("Error: EDITPROMPT_TARGET_PANE environment variable is required in capture mode");
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
let quoteContent;
|
|
218
|
+
if (config.mux === "tmux") {
|
|
219
|
+
quoteContent = await getQuoteVariableContent(config.targetPane);
|
|
220
|
+
await clearQuoteVariable(config.targetPane);
|
|
221
|
+
} else {
|
|
222
|
+
quoteContent = await getQuoteText(config.targetPane);
|
|
223
|
+
await clearQuoteText(config.targetPane);
|
|
224
|
+
}
|
|
225
|
+
process.stdout.write(quoteContent.replace(/\n{3,}$/, "\n\n"));
|
|
226
|
+
process.exit(0);
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
12
232
|
|
|
13
233
|
//#endregion
|
|
14
234
|
//#region src/config/constants.ts
|
|
@@ -172,19 +392,158 @@ async function handleContentDelivery(content, mux, targetPane, alwaysCopy) {
|
|
|
172
392
|
//#endregion
|
|
173
393
|
//#region src/modes/openEditor.ts
|
|
174
394
|
async function runOpenEditorMode(options) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
395
|
+
if (options.targetPane && options.mux === "tmux") try {
|
|
396
|
+
const currentPaneId = await getCurrentPaneId();
|
|
397
|
+
await saveEditorPaneId(options.targetPane, currentPaneId);
|
|
398
|
+
await markAsEditorPane(currentPaneId, options.targetPane);
|
|
399
|
+
} catch {}
|
|
400
|
+
else if (options.targetPane && options.mux === "wezterm") try {
|
|
401
|
+
const currentPaneId = await getCurrentPaneId$1();
|
|
402
|
+
await markAsEditorPane$1(currentPaneId, options.targetPane);
|
|
403
|
+
} catch {}
|
|
404
|
+
try {
|
|
405
|
+
const sendConfig = {
|
|
406
|
+
targetPane: options.targetPane,
|
|
407
|
+
mux: options.mux,
|
|
408
|
+
alwaysCopy: options.alwaysCopy
|
|
409
|
+
};
|
|
410
|
+
console.log("Opening editor...");
|
|
411
|
+
const content = await openEditorAndGetContent(options.editor, options.env, sendConfig);
|
|
412
|
+
if (!content) {
|
|
413
|
+
console.log("No content entered. Exiting.");
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
try {
|
|
417
|
+
await handleContentDelivery(content, options.mux, options.targetPane, options.alwaysCopy);
|
|
418
|
+
} catch (error) {
|
|
419
|
+
console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
420
|
+
process.exit(1);
|
|
421
|
+
}
|
|
422
|
+
} finally {
|
|
423
|
+
if (options.targetPane && options.mux === "tmux") try {
|
|
424
|
+
await clearEditorPaneId(options.targetPane);
|
|
425
|
+
} catch {}
|
|
426
|
+
else if (options.targetPane && options.mux === "wezterm") try {
|
|
427
|
+
await clearEditorPaneId$1(options.targetPane);
|
|
428
|
+
} catch {}
|
|
185
429
|
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
//#endregion
|
|
433
|
+
//#region src/utils/quoteProcessor.ts
|
|
434
|
+
/**
|
|
435
|
+
* Calculate the minimum leading whitespace count across all non-empty lines
|
|
436
|
+
*/
|
|
437
|
+
function getMinLeadingWhitespace(lines) {
|
|
438
|
+
let min = 99;
|
|
439
|
+
for (const line of lines) {
|
|
440
|
+
if (line.length === 0) continue;
|
|
441
|
+
const match = line.match(/^[ \t]*/);
|
|
442
|
+
const count = match ? match[0].length : 0;
|
|
443
|
+
if (count < min) min = count;
|
|
444
|
+
}
|
|
445
|
+
return min === 99 ? 0 : min;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Check if we need a space separator between two lines
|
|
449
|
+
*/
|
|
450
|
+
function needsSpaceSeparator(prevLine, currentLine) {
|
|
451
|
+
if (prevLine.length === 0 || currentLine.length === 0) return false;
|
|
452
|
+
const lastChar = prevLine[prevLine.length - 1] ?? "";
|
|
453
|
+
const firstChar = currentLine[0] ?? "";
|
|
454
|
+
const isLastCharAlpha = /[a-zA-Z]/.test(lastChar);
|
|
455
|
+
const isFirstCharAlpha = /[a-zA-Z]/.test(firstChar);
|
|
456
|
+
return isLastCharAlpha && isFirstCharAlpha;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Determine if two lines should be merged
|
|
460
|
+
*/
|
|
461
|
+
function shouldMergeLines(prevLine, currentLine) {
|
|
462
|
+
if (/^[-*+]\s/.test(currentLine)) return false;
|
|
463
|
+
const hasColon = (line) => line.includes(":") || line.includes("οΌ");
|
|
464
|
+
if (hasColon(prevLine) && hasColon(currentLine)) return false;
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Remove common leading whitespace and merge lines
|
|
469
|
+
*/
|
|
470
|
+
function removeWhitespaceAndMergeLines(lines) {
|
|
471
|
+
const minWhitespace = getMinLeadingWhitespace(lines);
|
|
472
|
+
const trimmedLines = lines.map((line) => {
|
|
473
|
+
if (line.length === 0) return line;
|
|
474
|
+
return line.slice(minWhitespace);
|
|
475
|
+
});
|
|
476
|
+
const result = [];
|
|
477
|
+
let currentLine = "";
|
|
478
|
+
for (let i = 0; i < trimmedLines.length; i++) {
|
|
479
|
+
const line = trimmedLines[i] ?? "";
|
|
480
|
+
if (i === 0) {
|
|
481
|
+
currentLine = line;
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
if (line.length === 0) {
|
|
485
|
+
result.push(currentLine);
|
|
486
|
+
result.push("");
|
|
487
|
+
currentLine = "";
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
if (currentLine.length === 0) {
|
|
491
|
+
currentLine = line;
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
const prevLine = trimmedLines[i - 1] ?? "";
|
|
495
|
+
if (shouldMergeLines(prevLine, line)) {
|
|
496
|
+
const separator = needsSpaceSeparator(prevLine, line) ? " " : "";
|
|
497
|
+
currentLine += separator + line;
|
|
498
|
+
} else {
|
|
499
|
+
result.push(currentLine);
|
|
500
|
+
currentLine = line;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
if (currentLine !== "") result.push(currentLine);
|
|
504
|
+
return result;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Processes text for quote buffering by:
|
|
508
|
+
* 1. Detecting if 2nd+ lines have no leading whitespace (Pattern A) or all lines have common leading whitespace (Pattern B)
|
|
509
|
+
* 2. Pattern A: Remove only leading whitespace, preserve all line breaks
|
|
510
|
+
* 3. Pattern B: Remove common leading whitespace and merge lines (with exceptions)
|
|
511
|
+
* 4. Adding quote prefix ("> ") to each line
|
|
512
|
+
* 5. Adding two newlines at the end
|
|
513
|
+
*/
|
|
514
|
+
function processQuoteText(text) {
|
|
515
|
+
const lines = text.replace(/^\n+|\n+$/g, "").split("\n");
|
|
516
|
+
const hasNoLeadingWhitespaceInLaterLines = lines.slice(1).some((line) => line.length > 0 && !line.startsWith(" ") && !line.startsWith(" "));
|
|
517
|
+
let processedLines;
|
|
518
|
+
if (hasNoLeadingWhitespaceInLaterLines) processedLines = lines.map((line) => line.trimStart());
|
|
519
|
+
else processedLines = removeWhitespaceAndMergeLines(lines);
|
|
520
|
+
return `${processedLines.map((line) => `> ${line}`).join("\n")}\n\n`;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
//#endregion
|
|
524
|
+
//#region src/modes/quote.ts
|
|
525
|
+
async function readStdin() {
|
|
526
|
+
return new Promise((resolve, reject) => {
|
|
527
|
+
const chunks = [];
|
|
528
|
+
process.stdin.on("data", (chunk) => {
|
|
529
|
+
chunks.push(chunk);
|
|
530
|
+
});
|
|
531
|
+
process.stdin.on("end", () => {
|
|
532
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
533
|
+
});
|
|
534
|
+
process.stdin.on("error", (error) => {
|
|
535
|
+
reject(error);
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
async function runQuoteMode(mux, targetPaneId, rawContent) {
|
|
186
540
|
try {
|
|
187
|
-
|
|
541
|
+
let selection;
|
|
542
|
+
if (rawContent !== void 0) selection = rawContent;
|
|
543
|
+
else selection = await readStdin();
|
|
544
|
+
const processedText = processQuoteText(selection);
|
|
545
|
+
if (mux === "tmux") await appendToQuoteVariable(targetPaneId, processedText);
|
|
546
|
+
else if (mux === "wezterm") await appendToQuoteText(targetPaneId, processedText);
|
|
188
547
|
} catch (error) {
|
|
189
548
|
console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
190
549
|
process.exit(1);
|
|
@@ -192,17 +551,60 @@ async function runOpenEditorMode(options) {
|
|
|
192
551
|
}
|
|
193
552
|
|
|
194
553
|
//#endregion
|
|
195
|
-
//#region src/
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
554
|
+
//#region src/modes/resume.ts
|
|
555
|
+
async function runResumeMode(targetPane, mux) {
|
|
556
|
+
if (mux === "wezterm") {
|
|
557
|
+
const currentPaneId$1 = await getCurrentPaneId$1();
|
|
558
|
+
if (isEditorPaneFromConf(currentPaneId$1)) {
|
|
559
|
+
console.log("isEditor");
|
|
560
|
+
const originalTargetPaneId = await getTargetPaneId$1(currentPaneId$1);
|
|
561
|
+
if (!originalTargetPaneId) {
|
|
562
|
+
console.log("Not found originalTargetPaneId");
|
|
563
|
+
process.exit(1);
|
|
564
|
+
}
|
|
565
|
+
if (!await checkPaneExists$1(originalTargetPaneId)) {
|
|
566
|
+
console.log("Not exist originalTargetPaneId");
|
|
567
|
+
process.exit(1);
|
|
568
|
+
}
|
|
569
|
+
await focusPane$1(originalTargetPaneId);
|
|
570
|
+
process.exit(0);
|
|
571
|
+
}
|
|
572
|
+
console.log("not isEditor");
|
|
573
|
+
const editorPaneId$1 = await getEditorPaneId$1(targetPane);
|
|
574
|
+
console.log(`wezterm editorPaneId: ${editorPaneId$1}`);
|
|
575
|
+
if (editorPaneId$1 === "") {
|
|
576
|
+
console.log("Not found editorPaneId");
|
|
577
|
+
process.exit(1);
|
|
578
|
+
}
|
|
579
|
+
if (!await checkPaneExists$1(editorPaneId$1)) {
|
|
580
|
+
console.log("Not exist editorPaneId");
|
|
581
|
+
await clearEditorPaneId$1(targetPane);
|
|
582
|
+
process.exit(1);
|
|
583
|
+
}
|
|
584
|
+
try {
|
|
585
|
+
await focusPane$1(editorPaneId$1);
|
|
586
|
+
process.exit(0);
|
|
587
|
+
} catch (error) {
|
|
588
|
+
console.log(`Can't focus editorPaneId: ${editorPaneId$1}\nerror: ${error}`);
|
|
589
|
+
process.exit(1);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const currentPaneId = await getCurrentPaneId();
|
|
593
|
+
if (await isEditorPane(currentPaneId)) {
|
|
594
|
+
const originalTargetPaneId = await getTargetPaneId(currentPaneId);
|
|
595
|
+
if (!originalTargetPaneId) process.exit(1);
|
|
596
|
+
if (!await checkPaneExists(originalTargetPaneId)) process.exit(1);
|
|
597
|
+
await focusPane(originalTargetPaneId);
|
|
598
|
+
process.exit(0);
|
|
599
|
+
}
|
|
600
|
+
const editorPaneId = await getEditorPaneId(targetPane);
|
|
601
|
+
if (editorPaneId === "") process.exit(1);
|
|
602
|
+
if (!await checkPaneExists(editorPaneId)) {
|
|
603
|
+
await clearEditorPaneId(targetPane);
|
|
604
|
+
process.exit(1);
|
|
605
|
+
}
|
|
606
|
+
await focusPane(editorPaneId);
|
|
607
|
+
process.exit(0);
|
|
206
608
|
}
|
|
207
609
|
|
|
208
610
|
//#endregion
|
|
@@ -247,6 +649,10 @@ await cli(process.argv.slice(2), {
|
|
|
247
649
|
name: "editprompt",
|
|
248
650
|
description: "A CLI tool that lets you write prompts for Claude Code using your favorite text editor",
|
|
249
651
|
args: {
|
|
652
|
+
resume: {
|
|
653
|
+
description: "Resume existing editor pane instead of creating new one",
|
|
654
|
+
type: "boolean"
|
|
655
|
+
},
|
|
250
656
|
editor: {
|
|
251
657
|
short: "e",
|
|
252
658
|
description: "Editor to use (overrides $EDITOR)",
|
|
@@ -276,10 +682,55 @@ await cli(process.argv.slice(2), {
|
|
|
276
682
|
"always-copy": {
|
|
277
683
|
description: "Always copy content to clipboard, even if tmux pane is available",
|
|
278
684
|
type: "boolean"
|
|
685
|
+
},
|
|
686
|
+
quote: {
|
|
687
|
+
description: "Quote buffering mode - add quoted text to pane variable",
|
|
688
|
+
type: "boolean"
|
|
689
|
+
},
|
|
690
|
+
capture: {
|
|
691
|
+
description: "Capture mode - copy pane variable to clipboard and clear",
|
|
692
|
+
type: "boolean"
|
|
279
693
|
}
|
|
280
694
|
},
|
|
281
695
|
async run(ctx) {
|
|
282
696
|
try {
|
|
697
|
+
if (ctx.values.resume) {
|
|
698
|
+
if (!ctx.values["target-pane"]) {
|
|
699
|
+
console.error("Error: --target-pane is required when using --resume");
|
|
700
|
+
process.exit(1);
|
|
701
|
+
}
|
|
702
|
+
const mux$1 = ctx.values.mux || "tmux";
|
|
703
|
+
if (!isMuxType(mux$1)) {
|
|
704
|
+
console.error(`Error: Invalid mux type '${mux$1}'. Supported values: tmux, wezterm`);
|
|
705
|
+
process.exit(1);
|
|
706
|
+
}
|
|
707
|
+
await runResumeMode(ctx.values["target-pane"], mux$1);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
if (ctx.values.quote) {
|
|
711
|
+
if (!ctx.values["target-pane"]) {
|
|
712
|
+
console.error("Error: --target-pane is required when using --quote");
|
|
713
|
+
process.exit(1);
|
|
714
|
+
}
|
|
715
|
+
const muxValue$1 = ctx.values.mux || "tmux";
|
|
716
|
+
if (!isMuxType(muxValue$1)) {
|
|
717
|
+
console.error(`Error: Invalid mux type '${muxValue$1}'. Supported values: tmux, wezterm`);
|
|
718
|
+
process.exit(1);
|
|
719
|
+
}
|
|
720
|
+
if (muxValue$1 === "wezterm") {
|
|
721
|
+
const rawContent$1 = extractRawContent(ctx.rest, ctx.positionals);
|
|
722
|
+
if (rawContent$1 === void 0) {
|
|
723
|
+
console.error("Error: Text content is required for quote mode with wezterm. Use: editprompt --quote --mux wezterm --target-pane <id> -- \"<text>\"");
|
|
724
|
+
process.exit(1);
|
|
725
|
+
}
|
|
726
|
+
await runQuoteMode(muxValue$1, ctx.values["target-pane"], rawContent$1);
|
|
727
|
+
} else await runQuoteMode(muxValue$1, ctx.values["target-pane"]);
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
if (ctx.values.capture) {
|
|
731
|
+
await runCaptureMode();
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
283
734
|
const rawContent = extractRawContent(ctx.rest, ctx.positionals);
|
|
284
735
|
if (rawContent !== void 0) {
|
|
285
736
|
await runSendOnlyMode(rawContent);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "editprompt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"clipboardy": "^4.0.0",
|
|
59
|
+
"conf": "^15.0.2",
|
|
59
60
|
"gunshi": "^0.26.3"
|
|
60
61
|
},
|
|
61
62
|
"peerDependencies": {
|