editprompt 0.0.6 → 0.2.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 +78 -28
- package/dist/index.js +76 -21
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A CLI tool that lets you write prompts for CLI tools using your favorite text editor. Originally designed for [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview), but works with any CLI process.
|
|
4
4
|
|
|
5
|
+
https://github.com/user-attachments/assets/01bcda7c-7771-4b33-bf5c-629812d45cc4
|
|
6
|
+
|
|
5
7
|
## Features
|
|
6
8
|
|
|
7
9
|
- 🖊️ **Editor Integration**: Use your preferred text editor to write prompts
|
|
@@ -36,6 +38,14 @@ editprompt -e nvim
|
|
|
36
38
|
editprompt --process gemini
|
|
37
39
|
editprompt -p gemini
|
|
38
40
|
|
|
41
|
+
# Send content to a specific tmux pane
|
|
42
|
+
editprompt --target-pane %45
|
|
43
|
+
editprompt -t %45
|
|
44
|
+
|
|
45
|
+
# Set environment variables for the editor
|
|
46
|
+
editprompt --env THEME=dark
|
|
47
|
+
editprompt -E THEME=dark -E LANG=ja_JP.UTF-8
|
|
48
|
+
|
|
39
49
|
# Show help
|
|
40
50
|
editprompt --help
|
|
41
51
|
|
|
@@ -43,15 +53,39 @@ editprompt --help
|
|
|
43
53
|
editprompt --version
|
|
44
54
|
```
|
|
45
55
|
|
|
46
|
-
|
|
56
|
+
### Tmux Integration
|
|
57
|
+
|
|
58
|
+
editprompt offers two modes for tmux integration:
|
|
59
|
+
|
|
60
|
+
#### Recommended: Direct Pane Targeting
|
|
61
|
+
Use `--target-pane #{pane_id}` to automatically send content back to the pane where you triggered the command. This is useful when using Claude Code, etc. in multiple panes.
|
|
47
62
|
|
|
63
|
+
**Split window version:**
|
|
64
|
+
```tmux
|
|
65
|
+
bind -n M-q run-shell 'tmux split-window -v -l 20 \
|
|
66
|
+
-c "#{pane_current_path}" \
|
|
67
|
+
"editprompt --editor nvim --target-pane #{pane_id}"'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Popup version:**
|
|
71
|
+
```tmux
|
|
72
|
+
bind -n M-q run-shell 'tmux display-popup -E \
|
|
73
|
+
-d "#{pane_current_path}" \
|
|
74
|
+
-w 80% -h 65% \
|
|
75
|
+
"editprompt --editor nvim --target-pane #{pane_id}"'
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Alternative: Process Auto-detection
|
|
79
|
+
Let editprompt automatically detect and select target processes:
|
|
80
|
+
|
|
81
|
+
**Split window version:**
|
|
48
82
|
```tmux
|
|
49
83
|
bind -n M-q split-window -v -l 10 \
|
|
50
84
|
-c '#{pane_current_path}' \
|
|
51
85
|
'editprompt --editor nvim'
|
|
52
86
|
```
|
|
53
87
|
|
|
54
|
-
|
|
88
|
+
**Popup version:**
|
|
55
89
|
```tmux
|
|
56
90
|
bind -n M-q display-popup -E \
|
|
57
91
|
-d '#{pane_current_path}' \
|
|
@@ -62,25 +96,13 @@ bind -n M-q display-popup -E \
|
|
|
62
96
|
|
|
63
97
|
1. **Opens your editor** with a temporary markdown file
|
|
64
98
|
2. **Write your prompt** and save/exit the editor
|
|
65
|
-
3. **
|
|
66
|
-
|
|
67
|
-
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
When multiple processes are detected, you'll see an interactive selection menu:
|
|
99
|
+
3. **Sends the prompt** using one of two modes:
|
|
100
|
+
- 🎯 **Direct pane mode** (`--target-pane`): Sends directly to specified tmux pane
|
|
101
|
+
- 🔍 **Process detection mode**: Finds target processes and sends via tmux or clipboard
|
|
102
|
+
4. **Fallback strategy** ensures delivery:
|
|
103
|
+
- Tmux integration (preferred)
|
|
104
|
+
- Clipboard copy (fallback)
|
|
73
105
|
|
|
74
|
-
```
|
|
75
|
-
? Select a process:
|
|
76
|
-
1. PID: 12345 | Tmux: main:0.1 | Directory: /home/user/project1
|
|
77
|
-
2. PID: 67890 | Directory: /home/user/project2
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
The display shows:
|
|
81
|
-
- **PID**: Process ID
|
|
82
|
-
- **Tmux**: Session, window, and pane (if running in tmux)
|
|
83
|
-
- **Directory**: Working directory of the process
|
|
84
106
|
|
|
85
107
|
## Configuration
|
|
86
108
|
|
|
@@ -96,11 +118,37 @@ editprompt respects the following editor priority:
|
|
|
96
118
|
|
|
97
119
|
- `EDITOR`: Your preferred text editor
|
|
98
120
|
|
|
99
|
-
|
|
121
|
+
### Editor Integration with EDITPROMPT
|
|
122
|
+
|
|
123
|
+
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.
|
|
124
|
+
|
|
125
|
+
#### Example: Neovim Configuration
|
|
126
|
+
|
|
127
|
+
```lua
|
|
128
|
+
-- In your Neovim config (e.g., init.lua)
|
|
129
|
+
if vim.env.EDITPROMPT then
|
|
130
|
+
vim.opt.wrap = true
|
|
131
|
+
-- Load a specific colorscheme
|
|
132
|
+
vim.cmd('colorscheme blue')
|
|
133
|
+
end
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### Setting Custom Environment Variables
|
|
100
137
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
138
|
+
You can also pass custom environment variables to your editor:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Single environment variable
|
|
142
|
+
editprompt --env THEME=dark
|
|
143
|
+
|
|
144
|
+
# Multiple environment variables
|
|
145
|
+
editprompt --env THEME=dark --env FOO=fooooo
|
|
146
|
+
|
|
147
|
+
# Useful for editor-specific configurations
|
|
148
|
+
editprompt --env NVIM_CONFIG=minimal
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
104
152
|
|
|
105
153
|
## Development
|
|
106
154
|
|
|
@@ -141,12 +189,14 @@ src/
|
|
|
141
189
|
|
|
142
190
|
### Tmux Integration
|
|
143
191
|
|
|
144
|
-
|
|
192
|
+
editprompt supports two tmux integration modes:
|
|
193
|
+
|
|
194
|
+
- **Direct pane targeting** (`--target-pane`): Bypasses process detection and sends content directly to specified pane ID
|
|
195
|
+
- **Process-based targeting**: Detects target processes and links them to tmux panes for delivery
|
|
145
196
|
|
|
146
197
|
### Fallback Strategy
|
|
147
198
|
|
|
148
|
-
editprompt implements fallback strategy:
|
|
199
|
+
editprompt implements a robust fallback strategy:
|
|
149
200
|
|
|
150
201
|
1. **Tmux Integration**: Direct input to tmux panes (when available)
|
|
151
|
-
|
|
152
|
-
2. **Clipboard**: Copy content to clipboard with error notification
|
|
202
|
+
2. **Clipboard**: Copy content to clipboard with user notification
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import find from "find-process";
|
|
|
10
10
|
import inquirer from "inquirer";
|
|
11
11
|
|
|
12
12
|
//#region package.json
|
|
13
|
-
var version = "0.0
|
|
13
|
+
var version = "0.2.0";
|
|
14
14
|
|
|
15
15
|
//#endregion
|
|
16
16
|
//#region src/config/constants.ts
|
|
@@ -19,6 +19,25 @@ const TEMP_FILE_EXTENSION = ".md";
|
|
|
19
19
|
const DEFAULT_EDITOR = "vim";
|
|
20
20
|
const DEFAULT_PROCESS_NAME = "claude";
|
|
21
21
|
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/utils/envParser.ts
|
|
24
|
+
/**
|
|
25
|
+
* 環境変数文字列をパースしてオブジェクトに変換
|
|
26
|
+
* @param envStrings - ["KEY=VALUE", "FOO=bar"] 形式の配列
|
|
27
|
+
* @returns 環境変数のキーバリューオブジェクト
|
|
28
|
+
*/
|
|
29
|
+
function parseEnvVars(envStrings) {
|
|
30
|
+
if (!envStrings || envStrings.length === 0) return {};
|
|
31
|
+
const result = {};
|
|
32
|
+
for (const envString of envStrings) {
|
|
33
|
+
const [key, ...valueParts] = envString.split("=");
|
|
34
|
+
if (!key || valueParts.length === 0) throw new Error(`Invalid environment variable format: ${envString}`);
|
|
35
|
+
const value = valueParts.join("=");
|
|
36
|
+
result[key] = value;
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
22
41
|
//#endregion
|
|
23
42
|
//#region src/utils/tempFile.ts
|
|
24
43
|
function getFormattedDateTime() {
|
|
@@ -44,11 +63,17 @@ async function createTempFile() {
|
|
|
44
63
|
function getEditor(editorOption) {
|
|
45
64
|
return editorOption || process.env.EDITOR || DEFAULT_EDITOR;
|
|
46
65
|
}
|
|
47
|
-
async function launchEditor(editor, filePath) {
|
|
66
|
+
async function launchEditor(editor, filePath, envVars) {
|
|
48
67
|
return new Promise((resolve, reject) => {
|
|
68
|
+
const processEnv = {
|
|
69
|
+
...process.env,
|
|
70
|
+
EDITPROMPT: "1",
|
|
71
|
+
...envVars
|
|
72
|
+
};
|
|
49
73
|
const editorProcess = spawn(editor, [filePath], {
|
|
50
74
|
stdio: "inherit",
|
|
51
|
-
shell: true
|
|
75
|
+
shell: true,
|
|
76
|
+
env: processEnv
|
|
52
77
|
});
|
|
53
78
|
editorProcess.on("error", (error) => {
|
|
54
79
|
reject(new Error(`Failed to launch editor: ${error.message}`));
|
|
@@ -67,13 +92,13 @@ async function readFileContent(filePath) {
|
|
|
67
92
|
throw new Error(`Failed to read file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
68
93
|
}
|
|
69
94
|
}
|
|
70
|
-
async function openEditorAndGetContent(editorOption) {
|
|
95
|
+
async function openEditorAndGetContent(editorOption, envVars) {
|
|
71
96
|
const tempFilePath = await createTempFile();
|
|
72
97
|
const editor = getEditor(editorOption);
|
|
98
|
+
const parsedEnvVars = parseEnvVars(envVars);
|
|
73
99
|
try {
|
|
74
|
-
await launchEditor(editor, tempFilePath);
|
|
100
|
+
await launchEditor(editor, tempFilePath, parsedEnvVars);
|
|
75
101
|
const content = await readFileContent(tempFilePath);
|
|
76
|
-
if (!content) throw new Error("No content was entered in the editor");
|
|
77
102
|
return content;
|
|
78
103
|
} catch (error) {
|
|
79
104
|
if (error instanceof Error) throw error;
|
|
@@ -169,11 +194,20 @@ async function sendToTmuxPane(session, window, pane, content) {
|
|
|
169
194
|
const target = `${session}:${window}.${pane}`;
|
|
170
195
|
await execAsync(`tmux send-keys -t '${target}' '${content.replace(/'/g, "'\\''")}'`);
|
|
171
196
|
}
|
|
197
|
+
async function sendToSpecificPane(paneId, content) {
|
|
198
|
+
const tempContent = content.replace(/'/g, "'\\''");
|
|
199
|
+
await execAsync(`tmux load-buffer -b editprompt - <<< '${tempContent}'`);
|
|
200
|
+
await execAsync(`tmux paste-buffer -d -t '${paneId}' -b editprompt`);
|
|
201
|
+
}
|
|
172
202
|
async function copyToClipboard(content) {
|
|
173
203
|
await clipboardy.write(content);
|
|
174
204
|
}
|
|
175
|
-
async function sendContentToProcess(process$1, content) {
|
|
205
|
+
async function sendContentToProcess(process$1, content, targetPaneId) {
|
|
176
206
|
try {
|
|
207
|
+
if (targetPaneId) {
|
|
208
|
+
await sendToSpecificPane(targetPaneId, content);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
177
211
|
if (process$1.tmuxSession && process$1.tmuxWindow && process$1.tmuxPane) {
|
|
178
212
|
await sendToTmuxPane(process$1.tmuxSession, process$1.tmuxWindow, process$1.tmuxPane, content);
|
|
179
213
|
return;
|
|
@@ -226,30 +260,51 @@ await cli(argv, {
|
|
|
226
260
|
short: "p",
|
|
227
261
|
description: "Process name to target (default: claude)",
|
|
228
262
|
type: "string"
|
|
263
|
+
},
|
|
264
|
+
"target-pane": {
|
|
265
|
+
short: "t",
|
|
266
|
+
description: "Target tmux pane ID to send content to",
|
|
267
|
+
type: "string"
|
|
268
|
+
},
|
|
269
|
+
env: {
|
|
270
|
+
short: "E",
|
|
271
|
+
description: "Environment variables to set (e.g., KEY=VALUE)",
|
|
272
|
+
type: "string",
|
|
273
|
+
multiple: true
|
|
229
274
|
}
|
|
230
275
|
},
|
|
231
276
|
async run(ctx) {
|
|
232
277
|
try {
|
|
233
278
|
console.log("Opening editor...");
|
|
234
|
-
const content = await openEditorAndGetContent(ctx.values.editor);
|
|
279
|
+
const content = await openEditorAndGetContent(ctx.values.editor, ctx.values.env);
|
|
235
280
|
if (!content) {
|
|
236
281
|
console.log("No content entered. Exiting.");
|
|
237
282
|
return;
|
|
238
283
|
}
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const processInfo = [`PID ${selectedProcess.pid}`];
|
|
247
|
-
if (selectedProcess.tmuxSession) processInfo.push(`Tmux: ${selectedProcess.tmuxSession}:${selectedProcess.tmuxWindow}.${selectedProcess.tmuxPane}`);
|
|
248
|
-
if (selectedProcess.cwd) processInfo.push(`Directory: ${selectedProcess.cwd}`);
|
|
249
|
-
console.log(`Selected process: ${processInfo.join(" | ")}`);
|
|
250
|
-
console.log(`Sending content to ${processName} process...`);
|
|
251
|
-
await sendContentToProcess(selectedProcess, content);
|
|
284
|
+
const targetPane = ctx.values["target-pane"];
|
|
285
|
+
if (targetPane) {
|
|
286
|
+
console.log("Sending content to specified pane...");
|
|
287
|
+
await sendContentToProcess({
|
|
288
|
+
pid: 0,
|
|
289
|
+
name: "direct-pane"
|
|
290
|
+
}, content, targetPane);
|
|
252
291
|
console.log("Content sent successfully!");
|
|
292
|
+
} else {
|
|
293
|
+
const processName = ctx.values.process || DEFAULT_PROCESS_NAME;
|
|
294
|
+
console.log(`Searching for ${processName} processes...`);
|
|
295
|
+
const processes = await findTargetProcesses(processName);
|
|
296
|
+
if (processes.length === 0) console.log(`No ${processName} process found.`);
|
|
297
|
+
else {
|
|
298
|
+
const selectedProcess = await selectProcess(processes);
|
|
299
|
+
if (!selectedProcess) return;
|
|
300
|
+
const processInfo = [`PID ${selectedProcess.pid}`];
|
|
301
|
+
if (selectedProcess.tmuxSession) processInfo.push(`Tmux: ${selectedProcess.tmuxSession}:${selectedProcess.tmuxWindow}.${selectedProcess.tmuxPane}`);
|
|
302
|
+
if (selectedProcess.cwd) processInfo.push(`Directory: ${selectedProcess.cwd}`);
|
|
303
|
+
console.log(`Selected process: ${processInfo.join(" | ")}`);
|
|
304
|
+
console.log(`Sending content to ${processName} process...`);
|
|
305
|
+
await sendContentToProcess(selectedProcess, content);
|
|
306
|
+
console.log("Content sent successfully!");
|
|
307
|
+
}
|
|
253
308
|
}
|
|
254
309
|
} catch (error) {
|
|
255
310
|
console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "editprompt",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.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",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"build": "tsdown",
|
|
41
41
|
"dev": "tsdown --watch",
|
|
42
42
|
"lint": "biome check",
|
|
43
|
+
"formant": "biome format --write",
|
|
43
44
|
"typecheck": "tsgo --noEmit",
|
|
44
45
|
"release": "bun run lint && bun run typecheck && bun run test && bun run build && bumpp",
|
|
45
46
|
"test": "bun test",
|