editprompt 1.1.1 → 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.
- package/README.md +30 -95
- package/dist/index.js +181 -4
- 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
|
|
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
|
-
|
|
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
|
-
|
|
303
|
+
### Prompt Stash
|
|
337
304
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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.
|
|
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/
|
|
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");
|
|
@@ -1027,6 +1030,179 @@ const resumeCommand = define({
|
|
|
1027
1030
|
}
|
|
1028
1031
|
});
|
|
1029
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
|
+
|
|
1030
1206
|
//#endregion
|
|
1031
1207
|
//#region src/index.ts
|
|
1032
1208
|
await cli(process.argv.slice(2), {
|
|
@@ -1055,7 +1231,8 @@ await cli(process.argv.slice(2), {
|
|
|
1055
1231
|
resume: resumeCommand,
|
|
1056
1232
|
input: inputCommand,
|
|
1057
1233
|
collect: collectCommand,
|
|
1058
|
-
dump: dumpCommand
|
|
1234
|
+
dump: dumpCommand,
|
|
1235
|
+
stash: stashCommand
|
|
1059
1236
|
},
|
|
1060
1237
|
renderHeader: null
|
|
1061
1238
|
});
|