editprompt 1.1.0 → 1.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.
Files changed (3) hide show
  1. package/README.md +30 -95
  2. package/dist/index.js +185 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -40,6 +40,10 @@ npm install -g editprompt
40
40
  npx editprompt
41
41
  ```
42
42
 
43
+ ### Neovim Plugin
44
+
45
+ For Neovim users, [editprompt.nvim](https://github.com/eetann/editprompt.nvim) provides easy integration. For manual configuration, see [docs/neovim.md](docs/neovim.md).
46
+
43
47
  ## 🚀 Usage
44
48
 
45
49
  editprompt supports three main workflows to fit different use cases:
@@ -198,46 +202,9 @@ This sends the content to the target pane (or clipboard) while keeping your edit
198
202
  - tmux format: `Enter` (default), `C-a`, etc.
199
203
  - WezTerm format: `\r` (default), `\x01`, etc.
200
204
 
201
- #### Neovim Integration Example
202
-
203
- You can set up a convenient keybinding to send your buffer content:
204
-
205
- ```lua
206
- -- Send buffer content while keeping the editor open
207
- if vim.env.EDITPROMPT then
208
- vim.keymap.set("n", "<Space>x", function()
209
- vim.cmd("update")
210
- -- Get buffer content
211
- local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
212
- local content = table.concat(lines, "\n")
213
-
214
- -- Execute editprompt command
215
- vim.system(
216
- { "editprompt", "input", "--", content },
217
- { text = true },
218
- function(obj)
219
- vim.schedule(function()
220
- if obj.code == 0 then
221
- -- Clear buffer on success
222
- vim.api.nvim_buf_set_lines(0, 0, -1, false, {})
223
- vim.cmd("silent write")
224
- else
225
- -- Show error notification
226
- vim.notify("editprompt failed: " .. (obj.stderr or "unknown error"), vim.log.levels.ERROR)
227
- end
228
- end)
229
- end
230
- )
231
- end, { silent = true, desc = "Send buffer content to editprompt" })
232
- end
233
- ```
205
+ #### Neovim Integration
234
206
 
235
- **Usage:**
236
- 1. Open editprompt using the tmux/wezterm keybinding
237
- 2. Write your prompt in the editor
238
- 3. Press `<Space>x` to send the content to the target pane
239
- 4. The buffer is automatically cleared on success
240
- 5. Continue editing to send more content
207
+ For Neovim users, we recommend using [editprompt.nvim](https://github.com/eetann/editprompt.nvim) for easy setup. For manual configuration, see [docs/neovim.md](docs/neovim.md).
241
208
 
242
209
  ### Quote Workflow Setup
243
210
 
@@ -333,53 +300,32 @@ editprompt register --target-pane %1 --target-pane %2
333
300
 
334
301
  The content will be sent sequentially to all specified panes. This is useful when you want to send the same prompt to multiple CLI sessions.
335
302
 
336
- #### Neovim Integration Example
303
+ ### Prompt Stash
337
304
 
338
- You can set up a convenient keybinding to capture your quote content:
339
- ```lua
340
- vim.keymap.set("n", "<Space>X", function()
341
- vim.cmd("update")
342
-
343
- vim.system({ "editprompt", "dump" }, { text = true }, function(obj)
344
- vim.schedule(function()
345
- if obj.code == 0 then
346
- vim.cmd("silent write")
347
- -- Split stdout by lines
348
- local output_lines = vim.split(obj.stdout, "\n")
349
-
350
- local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
351
- local is_empty = #lines == 1 and lines[1] == ""
352
-
353
- if is_empty then
354
- -- If empty, overwrite from the beginning
355
- vim.api.nvim_buf_set_lines(0, 0, -1, false, output_lines)
356
- vim.cmd("normal 2j")
357
- else
358
- -- If not empty, append to the end
359
- table.insert(output_lines, 1, "")
360
- local line_count = vim.api.nvim_buf_line_count(0)
361
- vim.api.nvim_buf_set_lines(
362
- 0,
363
- line_count,
364
- line_count,
365
- false,
366
- output_lines
367
- )
368
- vim.cmd("normal 4j")
369
- end
370
-
371
- vim.cmd("silent write")
372
- else
373
- vim.notify(
374
- "editprompt failed: " .. (obj.stderr or "unknown error"),
375
- vim.log.levels.ERROR
376
- )
377
- end
378
- end)
379
- end)
380
- end, { silent = true, desc = "Capture from editprompt quote mode" })
305
+ Store prompts temporarily for later use, similar to `git stash`. This is useful when you want to save a prompt idea and use it later.
306
+
307
+ ```bash
308
+ # Save a prompt to stash (must be run from editor pane)
309
+ editprompt stash push -- "your prompt here"
310
+
311
+ # List all stashed prompts (JSON output)
312
+ editprompt stash list
313
+
314
+ # Get the latest stashed prompt (outputs to stdout)
315
+ editprompt stash apply
316
+
317
+ # Get a specific stashed prompt by key
318
+ editprompt stash apply --key "2025-01-07T12:34:56.789Z"
319
+
320
+ # Remove the latest stashed prompt
321
+ editprompt stash drop
322
+
323
+ # Get and remove the latest stashed prompt (apply + drop)
324
+ editprompt stash pop
381
325
  ```
382
326
 
327
+ **Note:** The stash commands must be run from within an editprompt editor session (where `EDITPROMPT=1` is set). Stash data is associated with the target pane and persisted using the Conf library.
328
+
383
329
  ### Environment Variables
384
330
 
385
331
  #### Editor Selection
@@ -392,18 +338,7 @@ editprompt respects the following editor priority:
392
338
 
393
339
  #### EDITPROMPT Environment Variable
394
340
 
395
- 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.
396
-
397
- **Example: Neovim Configuration**
398
-
399
- ```lua
400
- -- In your Neovim config (e.g., init.lua)
401
- if vim.env.EDITPROMPT then
402
- vim.opt.wrap = true
403
- -- Load a specific colorscheme
404
- vim.cmd('colorscheme blue')
405
- end
406
- ```
341
+ 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. For Neovim integration examples, see [docs/neovim.md](docs/neovim.md).
407
342
 
408
343
  #### Custom Environment Variables
409
344
 
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
10
10
 
11
11
  //#region package.json
12
- var version = "1.1.0";
12
+ var version = "1.2.0";
13
13
 
14
14
  //#endregion
15
15
  //#region src/modules/tmux.ts
@@ -96,10 +96,13 @@ async function inputToTmuxPane(paneId, content) {
96
96
  }
97
97
 
98
98
  //#endregion
99
- //#region src/modules/wezterm.ts
100
- const execAsync = promisify(exec);
99
+ //#region src/modules/conf.ts
101
100
  const projectName = process.env.NODE_ENV === "test" ? "editprompt-test" : "editprompt";
102
101
  const conf = new Conf({ projectName });
102
+
103
+ //#endregion
104
+ //#region src/modules/wezterm.ts
105
+ const execAsync = promisify(exec);
103
106
  async function getCurrentPaneId$1() {
104
107
  try {
105
108
  const { stdout } = await execAsync("wezterm cli list --format json");
@@ -677,6 +680,10 @@ async function runInputMode(rawContent, autoSend, sendKey) {
677
680
  } catch (error) {
678
681
  console.error(`Failed to send to pane ${targetPane}: ${error instanceof Error ? error.message : "Unknown error"}`);
679
682
  }
683
+ if (config.alwaysCopy) {
684
+ await copyToClipboard(content);
685
+ console.log("Also copied to clipboard.");
686
+ }
680
687
  if (successCount > 0) console.log("Content sent and submitted successfully!");
681
688
  else {
682
689
  console.error("Error: All target panes failed to receive content");
@@ -1023,6 +1030,179 @@ const resumeCommand = define({
1023
1030
  }
1024
1031
  });
1025
1032
 
1033
+ //#endregion
1034
+ //#region src/modes/stash.ts
1035
+ function getStashKey(mux, targetPaneId) {
1036
+ return `${mux}.targetPane.pane_${targetPaneId}.stash`;
1037
+ }
1038
+ async function pushStash(mux, targetPaneId, content) {
1039
+ const key = (/* @__PURE__ */ new Date()).toISOString();
1040
+ const stashKey = getStashKey(mux, targetPaneId);
1041
+ const existing = conf.get(stashKey) || {};
1042
+ existing[key] = content;
1043
+ conf.set(stashKey, existing);
1044
+ return key;
1045
+ }
1046
+ function getStashList(mux, targetPaneId) {
1047
+ const stashKey = getStashKey(mux, targetPaneId);
1048
+ const data = conf.get(stashKey) || {};
1049
+ return Object.entries(data).map(([key, content]) => ({
1050
+ key,
1051
+ content
1052
+ })).sort((a, b) => b.key.localeCompare(a.key));
1053
+ }
1054
+ function getStashContent(mux, targetPaneId, key) {
1055
+ const stashKey = getStashKey(mux, targetPaneId);
1056
+ const data = conf.get(stashKey) || {};
1057
+ if (key) return data[key] ?? "";
1058
+ const keys = Object.keys(data);
1059
+ if (keys.length === 0) return "";
1060
+ return data[keys.sort().pop()];
1061
+ }
1062
+ function dropStash(mux, targetPaneId, key) {
1063
+ const stashKey = getStashKey(mux, targetPaneId);
1064
+ const data = conf.get(stashKey) || {};
1065
+ let targetKey = key;
1066
+ if (!targetKey) {
1067
+ const keys = Object.keys(data);
1068
+ if (keys.length === 0) return false;
1069
+ targetKey = keys.sort().pop();
1070
+ }
1071
+ if (!(targetKey in data)) return false;
1072
+ delete data[targetKey];
1073
+ conf.set(stashKey, data);
1074
+ return true;
1075
+ }
1076
+ async function getTargetPaneForStash() {
1077
+ const config = readSendConfig();
1078
+ let currentPaneId;
1079
+ let isEditor;
1080
+ if (config.mux === "tmux") {
1081
+ currentPaneId = await getCurrentPaneId();
1082
+ isEditor = await isEditorPane(currentPaneId);
1083
+ } else {
1084
+ currentPaneId = await getCurrentPaneId$1();
1085
+ isEditor = isEditorPaneFromConf(currentPaneId);
1086
+ }
1087
+ if (!isEditor) {
1088
+ console.error("Error: Current pane is not an editor pane");
1089
+ process.exit(1);
1090
+ }
1091
+ let targetPanes;
1092
+ if (config.mux === "tmux") targetPanes = await getTargetPaneIds(currentPaneId);
1093
+ else targetPanes = await getTargetPaneIds$1(currentPaneId);
1094
+ if (targetPanes.length === 0) {
1095
+ console.error("Error: No target panes registered for this editor pane");
1096
+ process.exit(1);
1097
+ }
1098
+ return {
1099
+ mux: config.mux,
1100
+ targetPaneId: targetPanes[0]
1101
+ };
1102
+ }
1103
+ async function runPush(rest, positionals) {
1104
+ const rawContent = extractRawContent(rest, positionals);
1105
+ if (rawContent === void 0 || rawContent.trim() === "") {
1106
+ console.error("Error: Content is required for stash push");
1107
+ console.error("Usage: editprompt stash push -- \"your content\"");
1108
+ process.exit(1);
1109
+ }
1110
+ const { mux, targetPaneId } = await getTargetPaneForStash();
1111
+ const key = await pushStash(mux, targetPaneId, rawContent);
1112
+ console.log(`Stashed with key: ${key}`);
1113
+ }
1114
+ async function runList() {
1115
+ const { mux, targetPaneId } = await getTargetPaneForStash();
1116
+ const entries = getStashList(mux, targetPaneId);
1117
+ console.log(JSON.stringify(entries, null, 2));
1118
+ }
1119
+ async function runApply(key) {
1120
+ const { mux, targetPaneId } = await getTargetPaneForStash();
1121
+ const content = getStashContent(mux, targetPaneId, key);
1122
+ if (content === "") {
1123
+ if (key) console.error(`Error: No stash entry found with key: ${key}`);
1124
+ else console.error("Error: No stash entries found");
1125
+ process.exit(1);
1126
+ }
1127
+ process.stdout.write(content);
1128
+ }
1129
+ async function runDrop(key) {
1130
+ const { mux, targetPaneId } = await getTargetPaneForStash();
1131
+ if (!dropStash(mux, targetPaneId, key)) {
1132
+ if (key) console.error(`Error: No stash entry found with key: ${key}`);
1133
+ else console.error("Error: No stash entries found");
1134
+ process.exit(1);
1135
+ }
1136
+ console.log("Stash entry dropped");
1137
+ }
1138
+ async function runPop(key) {
1139
+ const { mux, targetPaneId } = await getTargetPaneForStash();
1140
+ const content = getStashContent(mux, targetPaneId, key);
1141
+ if (content === "") {
1142
+ if (key) console.error(`Error: No stash entry found with key: ${key}`);
1143
+ else console.error("Error: No stash entries found");
1144
+ process.exit(1);
1145
+ }
1146
+ process.stdout.write(content);
1147
+ dropStash(mux, targetPaneId, key);
1148
+ }
1149
+ function showHelp() {
1150
+ console.log("Usage: editprompt stash <subcommand> [options]");
1151
+ console.log("");
1152
+ console.log("Stash prompts for later use");
1153
+ console.log("");
1154
+ console.log("Subcommands:");
1155
+ console.log(" push -- \"<content>\" Push content to stash");
1156
+ console.log(" list List stashed entries (JSON)");
1157
+ console.log(" apply [--key <key>] Apply (output) stashed content");
1158
+ console.log(" drop [--key <key>] Drop stashed entry");
1159
+ console.log(" pop [--key <key>] Apply and drop stashed entry");
1160
+ console.log("");
1161
+ console.log("Options:");
1162
+ console.log(" -k, --key <key> Stash key (ISO datetime). Default: latest");
1163
+ console.log(" -h, --help Show this help message");
1164
+ }
1165
+ function parseKeyOption(args) {
1166
+ const keyIndex = args.findIndex((arg) => arg === "-k" || arg === "--key");
1167
+ if (keyIndex !== -1 && args[keyIndex + 1]) return args[keyIndex + 1];
1168
+ }
1169
+ const stashCommand = define({
1170
+ name: "stash",
1171
+ description: "Stash prompts for later use",
1172
+ args: {},
1173
+ async run(ctx) {
1174
+ const args = ctx.positionals.slice(1);
1175
+ if (args.length === 0 || args[0] === "-h" || args[0] === "--help") {
1176
+ showHelp();
1177
+ process.exit(args.length === 0 ? 1 : 0);
1178
+ }
1179
+ const subcommand = args[0];
1180
+ const subArgs = args.slice(1);
1181
+ switch (subcommand) {
1182
+ case "push":
1183
+ await runPush(ctx.rest, subArgs);
1184
+ break;
1185
+ case "list":
1186
+ await runList();
1187
+ break;
1188
+ case "apply":
1189
+ await runApply(parseKeyOption(subArgs));
1190
+ break;
1191
+ case "drop":
1192
+ await runDrop(parseKeyOption(subArgs));
1193
+ break;
1194
+ case "pop":
1195
+ await runPop(parseKeyOption(subArgs));
1196
+ break;
1197
+ default:
1198
+ console.error(`Error: Unknown subcommand '${subcommand}'`);
1199
+ console.error("");
1200
+ showHelp();
1201
+ process.exit(1);
1202
+ }
1203
+ }
1204
+ });
1205
+
1026
1206
  //#endregion
1027
1207
  //#region src/index.ts
1028
1208
  await cli(process.argv.slice(2), {
@@ -1051,7 +1231,8 @@ await cli(process.argv.slice(2), {
1051
1231
  resume: resumeCommand,
1052
1232
  input: inputCommand,
1053
1233
  collect: collectCommand,
1054
- dump: dumpCommand
1234
+ dump: dumpCommand,
1235
+ stash: stashCommand
1055
1236
  },
1056
1237
  renderHeader: null
1057
1238
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "editprompt",
3
- "version": "1.1.0",
3
+ "version": "1.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",