editprompt 0.4.4 → 0.5.1

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 +61 -27
  2. package/dist/index.js +123 -38
  3. package/package.json +62 -62
package/README.md CHANGED
@@ -34,22 +34,17 @@ npx editprompt
34
34
 
35
35
  ## 🚀 Usage
36
36
 
37
- 1. Run `editprompt` to open a temporary Markdown file in your editor
38
- 2. Write your prompt and save the file
39
- 3. Your prompt is automatically sent to the target pane or copied to clipboard if no pane is found
40
-
41
- editprompt works with **any terminal** - no special setup required!
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**)
42
42
 
43
43
  ```sh
44
- # Just run it - content will be copied to clipboard
45
44
  editprompt
46
- # Then paste (Ctrl+V / Cmd+V) into any CLI tool:
47
- # - Claude Code
48
- # - Codex
49
- # - Any REPL or interactive prompt
50
45
  ```
51
46
 
52
- Optional integrations (Tmux/Wezterm) provide seamless auto-send.
47
+ **Advanced usage:** You can also send content **without closing the editor** for faster iteration. See `Send Without Closing Editor` for details.
53
48
 
54
49
 
55
50
  ### 🖥️ Tmux Integration
@@ -117,6 +112,60 @@ editprompt --help
117
112
  ```
118
113
 
119
114
 
115
+ ## 📤 Send Without Closing Editor
116
+
117
+ 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
+
119
+ ### Basic Usage
120
+
121
+ ```bash
122
+ # Run this command from within your editor session
123
+ editprompt "your content here"
124
+ ```
125
+
126
+ 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
+
128
+ ### Neovim Integration Example
129
+
130
+ You can set up a convenient keybinding to send your buffer content:
131
+
132
+ ```lua
133
+ -- Send buffer content while keeping the editor open
134
+ if vim.env.EDITPROMPT then
135
+ vim.keymap.set("n", "<Space>x", function()
136
+ vim.cmd("update")
137
+ -- Get buffer content
138
+ local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
139
+ local content = table.concat(lines, "\n")
140
+
141
+ -- Execute editprompt command
142
+ vim.system(
143
+ { "editprompt", content },
144
+ { text = true },
145
+ function(obj)
146
+ vim.schedule(function()
147
+ if obj.code == 0 then
148
+ -- Clear buffer on success
149
+ vim.api.nvim_buf_set_lines(0, 0, -1, false, {})
150
+ vim.cmd("silent write")
151
+ else
152
+ -- Show error notification
153
+ vim.notify("editprompt failed: " .. (obj.stderr or "unknown error"), vim.log.levels.ERROR)
154
+ end
155
+ end)
156
+ end
157
+ )
158
+ end, { silent = true, desc = "Send buffer content to editprompt" })
159
+ end
160
+ ```
161
+
162
+ With this configuration:
163
+ 1. Open editprompt using the tmux/wezterm keybinding mentioned above
164
+ 2. Write your prompt in the editor
165
+ 3. Press `<Space>x` to send the content to the target pane
166
+ 4. The buffer is automatically cleared on success
167
+ 5. Continue editing to send more content
168
+
120
169
 
121
170
  ## ⚙️ Configuration
122
171
 
@@ -126,7 +175,7 @@ editprompt respects the following editor priority:
126
175
 
127
176
  1. `--editor/-e` command line option
128
177
  2. `$EDITOR` environment variable
129
- 3. Default: `nvim`
178
+ 3. Default: `vim`
130
179
 
131
180
  ### 🌍 Environment Variables
132
181
 
@@ -184,21 +233,6 @@ bun test
184
233
  bun run dev
185
234
  ```
186
235
 
187
- ### 📁 Project Structure
188
-
189
- ```
190
- src/
191
- ├── config/
192
- │ └── constants.ts # Configuration constants
193
- ├── modules/
194
- │ ├── editor.ts # Editor launching and file handling
195
- │ ├── process.ts # Process detection and communication
196
- │ └── selector.ts # Interactive process selection
197
- ├── utils/
198
- │ └── tempFile.ts # Temporary file management
199
- └── index.ts # CLI entry point
200
- ```
201
-
202
236
  ## 🔍 Technical Details
203
237
 
204
238
  ### 🔄 Fallback Strategy
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { promisify } from "node:util";
8
8
  import clipboardy from "clipboardy";
9
9
 
10
10
  //#region package.json
11
- var version = "0.4.4";
11
+ var version = "0.5.1";
12
12
 
13
13
  //#endregion
14
14
  //#region src/config/constants.ts
@@ -16,12 +16,20 @@ const TEMP_FILE_PREFIX = ".editprompt-";
16
16
  const TEMP_FILE_EXTENSION = ".md";
17
17
  const DEFAULT_EDITOR = "vim";
18
18
 
19
+ //#endregion
20
+ //#region src/utils/contentProcessor.ts
21
+ function processContent(content) {
22
+ let processed = content.replace(/\n$/, "");
23
+ if (/@[^\n]*$/.test(processed)) processed += " ";
24
+ return processed;
25
+ }
26
+
19
27
  //#endregion
20
28
  //#region src/utils/envParser.ts
21
29
  /**
22
- * 環境変数文字列をパースしてオブジェクトに変換
23
- * @param envStrings - ["KEY=VALUE", "FOO=bar"] 形式の配列
24
- * @returns 環境変数のキーバリューオブジェクト
30
+ * Parses environment variable strings into an object.
31
+ * @param envStrings - An array of strings in the format ["KEY=VALUE", "FOO=bar"].
32
+ * @returns An object of environment variable key-value pairs.
25
33
  */
26
34
  function parseEnvVars(envStrings) {
27
35
  if (!envStrings || envStrings.length === 0) return {};
@@ -61,11 +69,18 @@ async function createTempFile() {
61
69
  function getEditor(editorOption) {
62
70
  return editorOption || process.env.EDITOR || DEFAULT_EDITOR;
63
71
  }
64
- async function launchEditor(editor, filePath, envVars) {
72
+ async function launchEditor(editor, filePath, envVars, sendConfig) {
65
73
  return new Promise((resolve, reject) => {
74
+ const configEnv = {};
75
+ if (sendConfig) {
76
+ if (sendConfig.targetPane) configEnv.EDITPROMPT_TARGET_PANE = sendConfig.targetPane;
77
+ configEnv.EDITPROMPT_MUX = sendConfig.mux;
78
+ configEnv.EDITPROMPT_ALWAYS_COPY = sendConfig.alwaysCopy ? "1" : "0";
79
+ }
66
80
  const processEnv = {
67
81
  ...process.env,
68
82
  EDITPROMPT: "1",
83
+ ...configEnv,
69
84
  ...envVars
70
85
  };
71
86
  const editorProcess = spawn(editor, [filePath], {
@@ -84,20 +99,18 @@ async function launchEditor(editor, filePath, envVars) {
84
99
  }
85
100
  async function readFileContent(filePath) {
86
101
  try {
87
- let content = await readFile(filePath, "utf-8");
88
- content = content.replace(/\n$/, "");
89
- if (/@[^\n]*$/.test(content)) content += " ";
90
- return content;
102
+ const content = await readFile(filePath, "utf-8");
103
+ return processContent(content);
91
104
  } catch (error) {
92
105
  throw new Error(`Failed to read file: ${error instanceof Error ? error.message : "Unknown error"}`);
93
106
  }
94
107
  }
95
- async function openEditorAndGetContent(editorOption, envVars) {
108
+ async function openEditorAndGetContent(editorOption, envVars, sendConfig) {
96
109
  const tempFilePath = await createTempFile();
97
110
  const editor = getEditor(editorOption);
98
111
  const parsedEnvVars = parseEnvVars(envVars);
99
112
  try {
100
- await launchEditor(editor, tempFilePath, parsedEnvVars);
113
+ await launchEditor(editor, tempFilePath, parsedEnvVars, sendConfig);
101
114
  const content = await readFileContent(tempFilePath);
102
115
  return content;
103
116
  } catch (error) {
@@ -121,11 +134,12 @@ async function sendToTmuxPane(paneId, content) {
121
134
  async function sendToWeztermPane(paneId, content) {
122
135
  await execAsync(`wezterm cli send-text --no-paste --pane-id '${paneId}' -- '${content.replace(/'/g, "'\\''")}'`);
123
136
  console.log(`Content sent to wezterm pane: ${paneId}`);
137
+ await execAsync(`wezterm cli activate-pane --pane-id '${paneId}'`);
124
138
  }
125
139
  async function copyToClipboard(content) {
126
140
  await clipboardy.write(content);
127
141
  }
128
- async function sendContentToPane(targetPaneId, content, mux = "tmux", alwaysCopy) {
142
+ async function sendContentToPane(content, mux, targetPaneId, alwaysCopy) {
129
143
  try {
130
144
  if (mux === "wezterm") await sendToWeztermPane(targetPaneId, content);
131
145
  else await sendToTmuxPane(targetPaneId, content);
@@ -139,6 +153,91 @@ async function sendContentToPane(targetPaneId, content, mux = "tmux", alwaysCopy
139
153
  }
140
154
  }
141
155
 
156
+ //#endregion
157
+ //#region src/modes/common.ts
158
+ function outputContent(content) {
159
+ console.log("---");
160
+ console.log(content);
161
+ }
162
+ async function handleContentDelivery(content, mux, targetPane, alwaysCopy) {
163
+ if (!content) return;
164
+ if (targetPane) try {
165
+ await sendContentToPane(content, mux, targetPane, alwaysCopy);
166
+ console.log("Content sent successfully!");
167
+ } catch (error) {
168
+ console.log(`Failed to send to pane: ${error instanceof Error ? error.message : "Unknown error"}`);
169
+ console.log("Falling back to clipboard...");
170
+ await copyToClipboard(content);
171
+ console.log("Content copied to clipboard.");
172
+ }
173
+ else try {
174
+ await copyToClipboard(content);
175
+ console.log("Content copied to clipboard.");
176
+ } catch (error) {
177
+ console.log(`Failed to copy to clipboard: ${error instanceof Error ? error.message : "Unknown error"}`);
178
+ }
179
+ outputContent(content);
180
+ }
181
+
182
+ //#endregion
183
+ //#region src/modes/openEditor.ts
184
+ async function runOpenEditorMode(options) {
185
+ const sendConfig = {
186
+ targetPane: options.targetPane,
187
+ mux: options.mux,
188
+ alwaysCopy: options.alwaysCopy
189
+ };
190
+ console.log("Opening editor...");
191
+ const content = await openEditorAndGetContent(options.editor, options.env, sendConfig);
192
+ if (!content) {
193
+ console.log("No content entered. Exiting.");
194
+ return;
195
+ }
196
+ try {
197
+ await handleContentDelivery(content, options.mux, options.targetPane, options.alwaysCopy);
198
+ } catch (error) {
199
+ console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
200
+ process.exit(1);
201
+ }
202
+ }
203
+
204
+ //#endregion
205
+ //#region src/utils/sendConfig.ts
206
+ const VALID_MUX_TYPES = ["tmux", "wezterm"];
207
+ function readSendConfig() {
208
+ const targetPane = process.env.EDITPROMPT_TARGET_PANE;
209
+ const muxValue = process.env.EDITPROMPT_MUX || "tmux";
210
+ if (!VALID_MUX_TYPES.includes(muxValue)) throw new Error(`Invalid EDITPROMPT_MUX value: ${muxValue}. Must be one of: ${VALID_MUX_TYPES.join(", ")}`);
211
+ const mux = muxValue;
212
+ const alwaysCopy = process.env.EDITPROMPT_ALWAYS_COPY === "1";
213
+ return {
214
+ targetPane,
215
+ mux,
216
+ alwaysCopy
217
+ };
218
+ }
219
+
220
+ //#endregion
221
+ //#region src/modes/sendOnly.ts
222
+ async function runSendOnlyMode(rawContent) {
223
+ const content = processContent(rawContent);
224
+ if (!content) {
225
+ console.log("No content to send. Exiting.");
226
+ return;
227
+ }
228
+ const config = readSendConfig();
229
+ if (!config.targetPane) {
230
+ console.error("Error: EDITPROMPT_TARGET_PANE environment variable is required in send-only mode");
231
+ process.exit(1);
232
+ }
233
+ try {
234
+ await handleContentDelivery(content, config.mux, config.targetPane, config.alwaysCopy);
235
+ } catch (error) {
236
+ console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
237
+ process.exit(1);
238
+ }
239
+ }
240
+
142
241
  //#endregion
143
242
  //#region src/index.ts
144
243
  const argv = process.argv.slice(2);
@@ -179,6 +278,11 @@ await cli(argv, {
179
278
  },
180
279
  async run(ctx) {
181
280
  try {
281
+ const rawContent = ctx.positionals[0];
282
+ if (rawContent !== void 0) {
283
+ await runSendOnlyMode(rawContent);
284
+ return;
285
+ }
182
286
  const muxValue = ctx.values.mux || "tmux";
183
287
  if (!isMuxType(muxValue)) {
184
288
  console.error(`Error: Invalid mux type '${muxValue}'. Supported values: tmux, wezterm`);
@@ -193,32 +297,13 @@ await cli(argv, {
193
297
  console.warn("Warning: --process option is deprecated and will be removed in future versions.");
194
298
  console.warn("Use --target-pane to specify the target pane directly.");
195
299
  }
196
- console.log("Opening editor...");
197
- const content = await openEditorAndGetContent(ctx.values.editor, ctx.values.env);
198
- if (!content) {
199
- console.log("No content entered. Exiting.");
200
- return;
201
- }
202
- const targetPane = ctx.values["target-pane"];
203
- const alwaysCopy = ctx.values["always-copy"];
204
- if (targetPane) try {
205
- console.log("Sending content to specified pane...");
206
- await sendContentToPane(targetPane, content, mux, alwaysCopy);
207
- console.log("Content sent successfully!");
208
- } catch (error) {
209
- console.log(`Failed to send to pane: ${error instanceof Error ? error.message : "Unknown error"}`);
210
- console.log("Falling back to clipboard...");
211
- await copyToClipboard(content);
212
- console.log("Content copied to clipboard.");
213
- }
214
- else try {
215
- await copyToClipboard(content);
216
- console.log("Content copied to clipboard.");
217
- } catch (error) {
218
- console.log(`Failed to copy to clipboard: ${error instanceof Error ? error.message : "Unknown error"}`);
219
- }
220
- console.log("---");
221
- console.log(content);
300
+ await runOpenEditorMode({
301
+ mux,
302
+ targetPane: ctx.values["target-pane"],
303
+ alwaysCopy: ctx.values["always-copy"] || false,
304
+ editor: ctx.values.editor,
305
+ env: ctx.values.env
306
+ });
222
307
  } catch (error) {
223
308
  console.error(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
224
309
  process.exit(1);
package/package.json CHANGED
@@ -1,64 +1,64 @@
1
1
  {
2
- "name": "editprompt",
3
- "version": "0.4.4",
4
- "author": "eetann",
5
- "description": "A CLI tool that lets you write prompts for CLI tools using your favorite text editor",
6
- "license": "MIT",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/eetann/editprompt.git"
10
- },
11
- "homepage": "https://github.com/eetann/editprompt",
12
- "bugs": {
13
- "url": "https://github.com/eetann/editprompt/issues"
14
- },
15
- "keywords": [
16
- "cli",
17
- "editor",
18
- "prompt",
19
- "tmux",
20
- "clipboard",
21
- "command-line",
22
- "text-editor"
23
- ],
24
- "publishConfig": {
25
- "access": "public"
26
- },
27
- "type": "module",
28
- "files": ["dist"],
29
- "main": "./dist/index.js",
30
- "module": "./dist/index.js",
31
- "types": "./dist/index.d.ts",
32
- "exports": {
33
- ".": "./dist/index.js",
34
- "./package.json": "./package.json"
35
- },
36
- "bin": {
37
- "editprompt": "./dist/index.js"
38
- },
39
- "scripts": {
40
- "build": "tsdown",
41
- "dev": "tsdown --watch",
42
- "lint": "biome check",
43
- "formant": "biome format --write",
44
- "typecheck": "tsgo --noEmit",
45
- "release": "bun run lint && bun run typecheck && bun run test && bun run build && bumpp",
46
- "test": "bun test",
47
- "test:watch": "bun test --watch"
48
- },
49
- "devDependencies": {
50
- "@biomejs/biome": "1.9.4",
51
- "@types/bun": "^1.2.16",
52
- "@types/node": "^24.0.1",
53
- "@typescript/native-preview": "^7.0.0-dev.20250712.1",
54
- "bumpp": "^10.2.0",
55
- "tsdown": "latest"
56
- },
57
- "dependencies": {
58
- "clipboardy": "^4.0.0",
59
- "gunshi": "^0.26.3"
60
- },
61
- "peerDependencies": {
62
- "typescript": "^5"
63
- }
2
+ "name": "editprompt",
3
+ "version": "0.5.1",
4
+ "author": "eetann",
5
+ "description": "A CLI tool that lets you write prompts for CLI tools using your favorite text editor",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/eetann/editprompt.git"
10
+ },
11
+ "homepage": "https://github.com/eetann/editprompt",
12
+ "bugs": {
13
+ "url": "https://github.com/eetann/editprompt/issues"
14
+ },
15
+ "keywords": [
16
+ "cli",
17
+ "editor",
18
+ "prompt",
19
+ "tmux",
20
+ "clipboard",
21
+ "command-line",
22
+ "text-editor"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "type": "module",
28
+ "files": ["dist"],
29
+ "main": "./dist/index.js",
30
+ "module": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "exports": {
33
+ ".": "./dist/index.js",
34
+ "./package.json": "./package.json"
35
+ },
36
+ "bin": {
37
+ "editprompt": "./dist/index.js"
38
+ },
39
+ "scripts": {
40
+ "build": "tsdown",
41
+ "dev": "tsdown --watch",
42
+ "lint": "biome check",
43
+ "formant": "biome format --write",
44
+ "typecheck": "tsgo --noEmit",
45
+ "release": "bun run lint && bun run typecheck && bun run test && bun run build && bumpp",
46
+ "test": "bun test",
47
+ "test:watch": "bun test --watch"
48
+ },
49
+ "devDependencies": {
50
+ "@biomejs/biome": "1.9.4",
51
+ "@types/bun": "^1.2.16",
52
+ "@types/node": "^24.0.1",
53
+ "@typescript/native-preview": "^7.0.0-dev.20250712.1",
54
+ "bumpp": "^10.2.0",
55
+ "tsdown": "latest"
56
+ },
57
+ "dependencies": {
58
+ "clipboardy": "^4.0.0",
59
+ "gunshi": "^0.26.3"
60
+ },
61
+ "peerDependencies": {
62
+ "typescript": "^5"
63
+ }
64
64
  }