pi-resume 1.4.0 → 1.5.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/CHANGELOG.md +40 -0
- package/README.md +11 -2
- package/package.json +5 -3
- package/src/ext/context.ts +6 -0
- package/src/ext/resume.ts +1 -1
- package/src/ext/startup.ts +7 -3
- package/src/format.ts +6 -5
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.5.0
|
|
4
|
+
- `/rn` / `/rp` position is shown in the footer status (`session 3/80`); the
|
|
5
|
+
transient notice was being replaced by pi's own "Resumed session".
|
|
6
|
+
- `pi --rN` waits for the provider refresh at most 15 s before resuming.
|
|
7
|
+
- Picker rows leave room for the duplicate suffix ` (N)` so it is never clipped.
|
|
8
|
+
- New interactive e2e suite (`npm run test:tui`): a real pi TUI in tmux with an
|
|
9
|
+
isolated agent dir — startup flag, `/rn` `/rp` walk stability, `/r1`, `/rs`
|
|
10
|
+
picker (single-line rows at 80 cols, arrow navigation, Enter, duplicates),
|
|
11
|
+
`/rs set`, `/rds`. Runs in CI.
|
|
12
|
+
|
|
13
|
+
## 1.4.0
|
|
14
|
+
- Refactor only: `extensions/index.ts` split into `src/ext/*` (one module per
|
|
15
|
+
concern), shared `loadSessions()`, all user-facing strings in `messages.ts`,
|
|
16
|
+
pure `applySetting()` / `resolveStartupRank()` with unit tests.
|
|
17
|
+
|
|
18
|
+
## 1.3.2
|
|
19
|
+
- Fix: `pi --rN` / `--rn` never actually switched (startup context has no
|
|
20
|
+
`switchSession`). Now dispatches `/rN` as a command after the provider
|
|
21
|
+
refresh finishes (switching mid-refresh crashed pi with an AbortError).
|
|
22
|
+
|
|
23
|
+
## 1.3.1
|
|
24
|
+
- Fix: `/rn` `/rp` walk by creation time instead of mtime — resuming bumps
|
|
25
|
+
mtime and made the walk ping-pong between two sessions.
|
|
26
|
+
|
|
27
|
+
## 1.3.0
|
|
28
|
+
- Legacy subagent fork sessions migrated into `<parent>/forks/`.
|
|
29
|
+
- Sessions open in another live pi are skipped by navigation and marked in
|
|
30
|
+
`/rs`; pid-reuse guard for the registry.
|
|
31
|
+
|
|
32
|
+
## 1.2.x
|
|
33
|
+
- `/r3`–`/r5` removed (`/rn` `/rp` cover it); docs fixes.
|
|
34
|
+
|
|
35
|
+
## 1.1.0
|
|
36
|
+
- `/rn` `/rp` step navigation; `pi --r N` / `--r1` / `--rn` startup flags.
|
|
37
|
+
- Fix: picker rows wrapped on narrow terminals and broke cursor navigation.
|
|
38
|
+
- Fix: duplicate picker rows resolved to the wrong session.
|
|
39
|
+
- Session renames appended past the head window are picked up (tail read).
|
|
40
|
+
- Metadata cache keyed by (file, mtime, size); CI.
|
package/README.md
CHANGED
|
@@ -72,8 +72,9 @@ in time instead of recalculating ranks.
|
|
|
72
72
|
`/rn` / `/rp` walk by **creation time** (the timestamp in the session filename),
|
|
73
73
|
not by mtime: resuming a session makes extensions append to it, which bumps its
|
|
74
74
|
mtime and would otherwise reshuffle the list into a ping-pong between two
|
|
75
|
-
sessions. `/r1` and `/rs` still rank by last activity (mtime).
|
|
76
|
-
each
|
|
75
|
+
sessions. `/r1` and `/rs` still rank by last activity (mtime). The footer status
|
|
76
|
+
shows your position (`session 3/80`) after each step. At the ends of the list a
|
|
77
|
+
notice is shown and nothing is switched.
|
|
77
78
|
|
|
78
79
|
### `/rs` — Smart Resume
|
|
79
80
|
|
|
@@ -153,3 +154,11 @@ npm test
|
|
|
153
154
|
## License
|
|
154
155
|
|
|
155
156
|
MIT
|
|
157
|
+
|
|
158
|
+
## Development
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npm test # unit tests (pure modules)
|
|
162
|
+
npm run test:tui # interactive e2e: real pi TUI in tmux, isolated agent dir (needs tmux)
|
|
163
|
+
docker build -f tests/e2e/Dockerfile -t e2e . && docker run --rm e2e # install/CLI e2e
|
|
164
|
+
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-resume",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Fast session resume for pi coding agent
|
|
3
|
+
"version": "1.5.0",
|
|
4
|
+
"description": "Fast session resume for pi coding agent \u2014 /r1,/r2 ranked resume, /rn and /rp step navigation, pi --r1/--rn startup flags, /rs paginated picker, /rds subagent session cleanup; skips sessions open in other pi instances, tidies legacy subagent forks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"extensions",
|
|
20
20
|
"src",
|
|
21
21
|
"README.md",
|
|
22
|
+
"CHANGELOG.md",
|
|
22
23
|
"LICENSE"
|
|
23
24
|
],
|
|
24
25
|
"pi": {
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"typecheck": "tsc --noEmit",
|
|
43
44
|
"test": "node --experimental-vm-modules --test tests/*.test.ts",
|
|
44
45
|
"check": "npm run typecheck && npm pack --dry-run",
|
|
45
|
-
"prepublishOnly": "npm run check"
|
|
46
|
+
"prepublishOnly": "npm run check",
|
|
47
|
+
"test:tui": "node --experimental-vm-modules --test tests/tui/tui.test.ts"
|
|
46
48
|
}
|
|
47
49
|
}
|
package/src/ext/context.ts
CHANGED
|
@@ -54,12 +54,18 @@ export async function switchTo(
|
|
|
54
54
|
ctx: ExtensionCommandContext,
|
|
55
55
|
target: StatResult,
|
|
56
56
|
announce: (label: string) => string = MSG.resumed,
|
|
57
|
+
status?: string,
|
|
57
58
|
): Promise<{ cancelled: boolean }> {
|
|
58
59
|
const meta = await readSessionMeta(target.file, target);
|
|
59
60
|
const label = truncate(sessionLabel(meta), LABEL_MAX);
|
|
60
61
|
return ctx.switchSession(target.file, {
|
|
61
62
|
withSession: async (newCtx) => {
|
|
63
|
+
// pi's own "Resumed session" notice lands right after ours and replaces
|
|
64
|
+
// it, so the durable part (position) goes to the footer status instead.
|
|
62
65
|
newCtx.ui.notify(announce(label), "info");
|
|
66
|
+
newCtx.ui.setStatus(STATUS_KEY, status);
|
|
63
67
|
},
|
|
64
68
|
});
|
|
65
69
|
}
|
|
70
|
+
|
|
71
|
+
export const STATUS_KEY = "pi-resume";
|
package/src/ext/resume.ts
CHANGED
|
@@ -42,7 +42,7 @@ export async function resumeStep(dir: 1 | -1, ctx: ExtensionCommandContext): Pro
|
|
|
42
42
|
ctx.ui.notify(dir === 1 ? MSG.atOldest : MSG.atNewest, "info");
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
|
-
await switchTo(ctx, target, (label) => MSG.resumedAt(pos, total, label));
|
|
45
|
+
await switchTo(ctx, target, (label) => MSG.resumedAt(pos, total, label), `session ${pos}/${total}`);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export function registerResumeCommands(pi: ExtensionAPI): void {
|
package/src/ext/startup.ts
CHANGED
|
@@ -12,6 +12,9 @@ import { currentSessionFile, sessionDirFor } from "./context.ts";
|
|
|
12
12
|
import { MAX_RANK, ordinal } from "./resume.ts";
|
|
13
13
|
import { MSG } from "./messages.ts";
|
|
14
14
|
|
|
15
|
+
/** Upper bound on waiting for the provider refresh before resuming anyway. */
|
|
16
|
+
const REFRESH_WAIT_MAX_MS = 15_000;
|
|
17
|
+
|
|
15
18
|
export type FlagReader = (name: string) => unknown;
|
|
16
19
|
|
|
17
20
|
/**
|
|
@@ -62,9 +65,10 @@ async function resumeAtStartup(pi: ExtensionAPI, ctx: ExtensionContext): Promise
|
|
|
62
65
|
// dispatch our own slash command to run with a command context. Switching
|
|
63
66
|
// while a provider refresh is in flight aborts it and crashes pi (uncaught
|
|
64
67
|
// AbortError from the provider), so let the refresh finish first.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
await Promise.race([
|
|
69
|
+
ctx.modelRegistry.refresh().catch(() => {}),
|
|
70
|
+
new Promise((r) => setTimeout(r, REFRESH_WAIT_MAX_MS)),
|
|
71
|
+
]);
|
|
68
72
|
pi.sendUserMessage(`/r${wanted.rank}`, { expandPromptTemplates: true });
|
|
69
73
|
}
|
|
70
74
|
|
package/src/format.ts
CHANGED
|
@@ -34,11 +34,12 @@ export function sessionLabel(e: SessionEntry): string {
|
|
|
34
34
|
* Reserves space for the age/size columns and the picker's cursor/indent
|
|
35
35
|
* so rows never wrap (wrapped rows break selection navigation in the TUI).
|
|
36
36
|
*/
|
|
37
|
-
// Columns
|
|
38
|
-
// uniquify suffix
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
|
|
37
|
+
// Columns reserved at the end of each picker row: the select component's
|
|
38
|
+
// cursor/indent (~4) plus the uniquify suffix " (NN)" (5) that
|
|
39
|
+
// buildPickerItems() may append to duplicate rows. Rows longer than the
|
|
40
|
+
// terminal width either wrap (breaking cursor navigation) or get clipped by
|
|
41
|
+
// the component (hiding the suffix), so the label is truncated to fit.
|
|
42
|
+
const PICKER_ROW_MARGIN = 10;
|
|
42
43
|
|
|
43
44
|
/** Label marker for a session currently open in another pi process. */
|
|
44
45
|
export const OPEN_ELSEWHERE_MARK = "⦿ ";
|