@yusukeshib/pi-stash 0.2.0 → 0.3.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 CHANGED
@@ -52,6 +52,16 @@ pi install npm:@yusukeshib/pi-stash
52
52
  | `/stash` | **Pop** — pick a saved entry, insert it into the editor, and remove it from the stash. Run repeatedly to stack several fragments together. |
53
53
  | `/stash-clear` | Delete every stashed entry in this session (with confirm). |
54
54
 
55
+ ### Shortcut
56
+
57
+ | Key | Action |
58
+ |-----|--------|
59
+ | `Ctrl+S` | If the editor has text, **push** it onto the stash and clear the editor. If the editor is empty, **pop** a saved entry into it. |
60
+
61
+ `Ctrl+S` is the one-key way to do what `/stash` does: park the prompt you're
62
+ halfway through typing, or pull one back — without typing the command. Rebind it
63
+ in `~/.pi/agent/keybindings.json` if it clashes with another shortcut.
64
+
55
65
  While the stash is non-empty, a red `stash:N` badge is shown in the footer so
56
66
  you never forget you have prompts parked.
57
67
 
@@ -12,6 +12,12 @@
12
12
  * remove it from the stash. Run repeatedly to stack fragments.
13
13
  * /stash-clear Delete every entry (with confirm).
14
14
  *
15
+ * Shortcut:
16
+ * Ctrl+S If the editor holds text, push it onto the stash (and clear
17
+ * the editor); otherwise pop a saved entry into the editor.
18
+ * A one-key way to park the prompt you're typing, or pull one
19
+ * back, without typing `/stash`.
20
+ *
15
21
  * Storage: stash entries are persisted INSIDE the session itself via custom
16
22
  * session entries (`pi.appendEntry`). Each session has its own stash, it
17
23
  * survives restarts/resume, and it follows branching (fork/clone) correctly.
@@ -73,44 +79,67 @@ export default function (pi: ExtensionAPI) {
73
79
  updateStatus(ctx);
74
80
  });
75
81
 
76
- pi.registerCommand("stash", {
77
- description: "Push text (with arg) or pop an entry into the editor (no arg)",
78
- handler: async (args, ctx) => {
79
- const text = (args ?? "").trim();
80
-
81
- // PUSH: /stash <text>
82
- if (text) {
83
- if (entries.some((e) => e.text === text)) {
84
- ctx.ui.notify("Already in stash", "info");
85
- return;
86
- }
87
- entries.push({ text, addedAt: Date.now() });
88
- persist();
89
- updateStatus(ctx);
90
- ctx.ui.notify(`Stashed (${entries.length} total)`, "info");
91
- return;
92
- }
82
+ /**
83
+ * Core stash behaviour shared by the `/stash` command and the Ctrl+S
84
+ * shortcut. With text push; without text → pop into the editor.
85
+ */
86
+ async function runStash(rawText: string, ctx: ExtensionContext): Promise<void> {
87
+ const text = (rawText ?? "").trim();
93
88
 
94
- // POP: /stash → pick, insert into editor, remove from stash
95
- if (entries.length === 0) {
96
- ctx.ui.notify("Stash is empty. Add one with /stash <text>", "info");
89
+ // PUSH: /stash <text>
90
+ if (text) {
91
+ if (entries.some((e) => e.text === text)) {
92
+ ctx.ui.notify("Already in stash", "info");
97
93
  return;
98
94
  }
99
- const labels = entries.map((e, i) => preview(e.text, i));
100
- const choice = await ctx.ui.select("Pop prompt:", labels);
101
- if (choice === undefined) return; // cancelled / timed out
102
- const idx = labels.indexOf(choice);
103
- if (idx < 0) return;
104
-
105
- const chosen = entries[idx].text;
106
- const current = ctx.ui.getEditorText() ?? "";
107
- const next = current.trim().length > 0 ? `${current}\n${chosen}` : chosen;
108
- ctx.ui.setEditorText(next);
109
-
110
- // pop = remove the chosen entry
111
- entries.splice(idx, 1);
95
+ entries.push({ text, addedAt: Date.now() });
112
96
  persist();
113
97
  updateStatus(ctx);
98
+ ctx.ui.notify(`Stashed (${entries.length} total)`, "info");
99
+ return;
100
+ }
101
+
102
+ // POP: /stash → pick, insert into editor, remove from stash
103
+ if (entries.length === 0) {
104
+ ctx.ui.notify("Stash is empty. Add one with /stash <text>", "info");
105
+ return;
106
+ }
107
+ const labels = entries.map((e, i) => preview(e.text, i));
108
+ const choice = await ctx.ui.select("Pop prompt:", labels);
109
+ if (choice === undefined) return; // cancelled / timed out
110
+ const idx = labels.indexOf(choice);
111
+ if (idx < 0) return;
112
+
113
+ const chosen = entries[idx].text;
114
+ const current = ctx.ui.getEditorText() ?? "";
115
+ const next = current.trim().length > 0 ? `${current}\n${chosen}` : chosen;
116
+ ctx.ui.setEditorText(next);
117
+
118
+ // pop = remove the chosen entry
119
+ entries.splice(idx, 1);
120
+ persist();
121
+ updateStatus(ctx);
122
+ }
123
+
124
+ pi.registerCommand("stash", {
125
+ description: "Push text (with arg) or pop an entry into the editor (no arg)",
126
+ handler: async (args, ctx) => {
127
+ await runStash(args ?? "", ctx);
128
+ },
129
+ });
130
+
131
+ // Ctrl+S → same as bare `/stash`: pop an entry into the editor. If the
132
+ // editor already holds text, push it onto the stash instead.
133
+ pi.registerShortcut("ctrl+s", {
134
+ description: "Stash: push editor text, or pop a saved prompt",
135
+ handler: async (ctx) => {
136
+ const editorText = (ctx.ui.getEditorText() ?? "").trim();
137
+ if (editorText) {
138
+ ctx.ui.setEditorText("");
139
+ await runStash(editorText, ctx);
140
+ } else {
141
+ await runStash("", ctx);
142
+ }
114
143
  },
115
144
  });
116
145
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yusukeshib/pi-stash",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A personal stash of reusable prompt fragments for the Pi coding agent. Push prompts you think of mid-task, then pop them into the editor when you need them.",
5
5
  "type": "module",
6
6
  "license": "MIT",