pi-fast-resume 1.0.1 → 1.1.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 +37 -7
- package/fast-resume.ts +96 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,9 +107,31 @@ Reload with `/reload` after any install method.
|
|
|
107
107
|
|
|
108
108
|
### Keyboard shortcut
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
There are two ways to bind a key to the fast resume picker:
|
|
111
|
+
|
|
112
|
+
**Option 1: Rebind `app.session.resume`** (hijack mode only)
|
|
113
|
+
|
|
114
|
+
In hijack mode (default), the fast picker replaces `/resume` — bind `app.session.resume` in `~/.pi/agent/keybindings.json`:
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"app.session.resume": "alt+u"
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Option 2: Standalone shortcut** (works regardless of hijack mode)
|
|
123
|
+
|
|
124
|
+
Set the `shortcut` key in `~/.pi/agent/extensions/pi-fast-resume.json`:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"shortcut": "ctrl+shift+f"
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
This registers an independent shortcut that opens the fast picker without overriding the built-in `/resume`. Works in both hijack and non-hijack modes.
|
|
133
|
+
|
|
134
|
+
See [keybindings.md](https://github.com/earendil-works/pi-coding-agent/blob/main/docs/keybindings.md) for the key format and all available actions.
|
|
113
135
|
|
|
114
136
|
### Picker controls
|
|
115
137
|
|
|
@@ -184,17 +206,25 @@ However, pi-fast-resume can **prototype-patch** `InteractiveMode.showSessionSele
|
|
|
184
206
|
- `/fast-resume` is not registered (no duplicate command)
|
|
185
207
|
- `pi -r` / `pi --resume` are **not** affected (they run before the interactive mode starts)
|
|
186
208
|
|
|
187
|
-
###
|
|
209
|
+
### Config options
|
|
210
|
+
|
|
211
|
+
All options go in `~/.pi/agent/extensions/pi-fast-resume.json`:
|
|
212
|
+
|
|
213
|
+
| Key | Type | Default | Description |
|
|
214
|
+
| --- | ---- | ------- | ----------- |
|
|
215
|
+
| `hijackResume` | `boolean` | `true` | When `true`, `/resume` and `app.session.resume` open the fast picker. Set `false` to use the built-in picker instead and keep `/fast-resume` as a separate command. |
|
|
216
|
+
| `shortcut` | `string` | (none) | Register a standalone keybinding to open the fast picker. Works regardless of `hijackResume`. Example: `"ctrl+shift+f"` or `"alt+u"`.
|
|
188
217
|
|
|
189
|
-
|
|
218
|
+
Example:
|
|
190
219
|
|
|
191
220
|
```json
|
|
192
221
|
{
|
|
193
|
-
"hijackResume": false
|
|
222
|
+
"hijackResume": false,
|
|
223
|
+
"shortcut": "alt+u"
|
|
194
224
|
}
|
|
195
225
|
```
|
|
196
226
|
|
|
197
|
-
|
|
227
|
+
Reload with `/reload` after changing config.
|
|
198
228
|
|
|
199
229
|
### How it works
|
|
200
230
|
|
package/fast-resume.ts
CHANGED
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Usage:
|
|
11
11
|
* /fast-resume [query] Open fast session picker (current project scope)
|
|
12
|
-
* Ctrl+Shift+F Open fast session picker via shortcut
|
|
13
12
|
*
|
|
14
|
-
*
|
|
13
|
+
* Config (~/.pi/agent/extensions/pi-fast-resume.json):
|
|
14
|
+
* { "hijackResume": false }
|
|
15
|
+
*
|
|
16
|
+
* Hijack mode (on by default, opt-out via config):
|
|
15
17
|
* { "hijackResume": false }
|
|
16
18
|
*
|
|
17
19
|
* When enabled, /resume and Ctrl+Shift+R open the fast picker instead.
|
|
@@ -41,6 +43,7 @@
|
|
|
41
43
|
*/
|
|
42
44
|
|
|
43
45
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
46
|
+
import type { KeyId } from "@earendil-works/pi-tui";
|
|
44
47
|
import {
|
|
45
48
|
DynamicBorder,
|
|
46
49
|
InteractiveMode,
|
|
@@ -54,7 +57,6 @@ import {
|
|
|
54
57
|
type Component,
|
|
55
58
|
getKeybindings,
|
|
56
59
|
Input,
|
|
57
|
-
Key,
|
|
58
60
|
Spacer,
|
|
59
61
|
Text,
|
|
60
62
|
truncateToWidth,
|
|
@@ -92,10 +94,12 @@ import type { PickerScope } from "./src/picker-state.js";
|
|
|
92
94
|
const HOME = homedir();
|
|
93
95
|
|
|
94
96
|
// Config — read from ~/.pi/agent/extensions/pi-fast-resume.json
|
|
95
|
-
// Example: { "hijackResume": false
|
|
97
|
+
// Example: { "hijackResume": false, "shortcut": "alt+u" }
|
|
96
98
|
// By default hijackResume is true — /resume opens the fast picker
|
|
99
|
+
// Set shortcut to register a standalone shortcut (e.g. "ctrl+shift+f")
|
|
97
100
|
interface FastResumeConfig {
|
|
98
101
|
hijackResume?: boolean;
|
|
102
|
+
shortcut?: string;
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
const CONFIG_PATH = join(HOME, ".pi", "agent", "extensions", "pi-fast-resume.json");
|
|
@@ -990,9 +994,52 @@ async function showFastResumePicker(
|
|
|
990
994
|
}
|
|
991
995
|
}
|
|
992
996
|
|
|
997
|
+
// Stored reference to the extension runner, captured via prototype patch on
|
|
998
|
+
// InteractiveMode.prototype.setupExtensionShortcuts. Used by the shortcut
|
|
999
|
+
// handler to create an ExtensionCommandContext with switchSession(), since
|
|
1000
|
+
// pi.registerShortcut handlers only receive ExtensionContext.
|
|
1001
|
+
let storedExtensionRunner: any = null;
|
|
1002
|
+
|
|
993
1003
|
// Reference to the original showSessionSelector, saved before patching
|
|
994
1004
|
let origShowSessionSelector: ((this: InteractiveMode) => void) | null = null;
|
|
995
1005
|
|
|
1006
|
+
// Reference to the original setupExtensionShortcuts, saved before patching
|
|
1007
|
+
let origSetupExtensionShortcuts: Function | null = null;
|
|
1008
|
+
|
|
1009
|
+
function patchSetupExtensionShortcuts(): void {
|
|
1010
|
+
if (origSetupExtensionShortcuts !== null) return; // Already patched
|
|
1011
|
+
const proto = InteractiveMode.prototype as any;
|
|
1012
|
+
if (
|
|
1013
|
+
!InteractiveMode ||
|
|
1014
|
+
typeof InteractiveMode !== "function" ||
|
|
1015
|
+
typeof proto.setupExtensionShortcuts !== "function"
|
|
1016
|
+
) {
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
origSetupExtensionShortcuts = proto.setupExtensionShortcuts;
|
|
1020
|
+
proto.setupExtensionShortcuts = function (
|
|
1021
|
+
this: InteractiveMode,
|
|
1022
|
+
extensionRunner: any,
|
|
1023
|
+
) {
|
|
1024
|
+
storedExtensionRunner = extensionRunner;
|
|
1025
|
+
origSetupExtensionShortcuts!.call(this, extensionRunner);
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
function unpatchSetupExtensionShortcuts(): void {
|
|
1030
|
+
if (origSetupExtensionShortcuts === null) return;
|
|
1031
|
+
const proto = InteractiveMode.prototype as any;
|
|
1032
|
+
if (
|
|
1033
|
+
InteractiveMode &&
|
|
1034
|
+
typeof InteractiveMode === "function" &&
|
|
1035
|
+
typeof proto.setupExtensionShortcuts === "function"
|
|
1036
|
+
) {
|
|
1037
|
+
proto.setupExtensionShortcuts = origSetupExtensionShortcuts;
|
|
1038
|
+
}
|
|
1039
|
+
origSetupExtensionShortcuts = null;
|
|
1040
|
+
storedExtensionRunner = null;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
996
1043
|
function installResumeHijack(): void {
|
|
997
1044
|
if (origShowSessionSelector !== null) return; // Already patched
|
|
998
1045
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- prototype patching requires any cast for private access
|
|
@@ -1056,17 +1103,55 @@ export default function (pi: ExtensionAPI) {
|
|
|
1056
1103
|
});
|
|
1057
1104
|
}
|
|
1058
1105
|
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1106
|
+
// Register a standalone keyboard shortcut for the fast resume picker.
|
|
1107
|
+
// pi.registerShortcut handlers receive ExtensionContext (no switchSession),
|
|
1108
|
+
// so we capture the extension runner via a prototype patch on
|
|
1109
|
+
// InteractiveMode.prototype.setupExtensionShortcuts and use it to create
|
|
1110
|
+
// an ExtensionCommandContext inside the handler.
|
|
1111
|
+
//
|
|
1112
|
+
// Users can rebind the key via pi-fast-resume.json:
|
|
1113
|
+
// { "shortcut": "alt+u" }
|
|
1114
|
+
//
|
|
1115
|
+
// In hijack mode, app.session.resume also opens the fast picker (rebindable
|
|
1116
|
+
// in ~/.pi/agent/keybindings.json). The shortcut config is an additional
|
|
1117
|
+
// independent binding that does not override the built-in /resume.
|
|
1118
|
+
const shortcut = config.shortcut;
|
|
1119
|
+
if (shortcut) {
|
|
1120
|
+
// Patch setupExtensionShortcuts so we can capture the extension runner.
|
|
1121
|
+
// This runs before setupExtensionShortcuts is called (during extension
|
|
1122
|
+
// load, which precedes the shortcut setup phase).
|
|
1123
|
+
patchSetupExtensionShortcuts();
|
|
1124
|
+
|
|
1125
|
+
pi.registerShortcut(shortcut as KeyId, {
|
|
1126
|
+
description: "Fast session resume",
|
|
1127
|
+
handler: async (ctx) => {
|
|
1128
|
+
// Use the stored extension runner to get a full command context
|
|
1129
|
+
// with switchSession(), since the shortcut handler ctx (ExtensionContext)
|
|
1130
|
+
// does not include session-switching methods.
|
|
1131
|
+
if (
|
|
1132
|
+
!storedExtensionRunner ||
|
|
1133
|
+
typeof storedExtensionRunner.createCommandContext !== "function"
|
|
1134
|
+
) {
|
|
1135
|
+
ctx.ui.notify(
|
|
1136
|
+
"Fast resume shortcut: extension runner not available. Try reloading with /reload.",
|
|
1137
|
+
"error",
|
|
1138
|
+
);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
const cmdCtx =
|
|
1142
|
+
storedExtensionRunner.createCommandContext() as ExtensionCommandContext;
|
|
1143
|
+
await showFastResumePicker(cmdCtx);
|
|
1144
|
+
},
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1065
1147
|
|
|
1066
|
-
// Clean up
|
|
1148
|
+
// Clean up prototype patches on session shutdown (reload, quit, session switch)
|
|
1067
1149
|
pi.on("session_shutdown", () => {
|
|
1068
1150
|
if (hijackResume) {
|
|
1069
1151
|
uninstallResumeHijack();
|
|
1070
1152
|
}
|
|
1153
|
+
if (shortcut) {
|
|
1154
|
+
unpatchSetupExtensionShortcuts();
|
|
1155
|
+
}
|
|
1071
1156
|
});
|
|
1072
1157
|
}
|
package/package.json
CHANGED