@code-fixer-23/pi-session-manager 1.0.4 → 1.0.6
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 +21 -0
- package/README.md +1 -1
- package/extensions/index.test.ts +248 -1
- package/extensions/index.ts +1066 -981
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## 1.0.6 (2026-07-14)
|
|
2
|
+
|
|
3
|
+
### 🩹 Fixes
|
|
4
|
+
|
|
5
|
+
- **pi-session-manager:** display README asset ([4628004](https://github.com/louiss0/pi-packages/commit/4628004))
|
|
6
|
+
- **pi-session-manager:** respect empty title cancellation ([37729af](https://github.com/louiss0/pi-packages/commit/37729af))
|
|
7
|
+
|
|
8
|
+
### ❤️ Thank You
|
|
9
|
+
|
|
10
|
+
- louiss0 @louiss0
|
|
11
|
+
|
|
12
|
+
## 1.0.5 (2026-07-08)
|
|
13
|
+
|
|
14
|
+
### 🩹 Fixes
|
|
15
|
+
|
|
16
|
+
- prompt parsing and session-series continuation ([#6](https://github.com/louiss0/pi-packages/pull/6))
|
|
17
|
+
|
|
18
|
+
### ❤️ Thank You
|
|
19
|
+
|
|
20
|
+
- louiss0 @louiss0
|
|
21
|
+
|
|
1
22
|
## 1.0.4 (2026-07-04)
|
|
2
23
|
|
|
3
24
|
### 🩹 Fixes
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pi-session-manager
|
|
2
2
|
|
|
3
|
-
[
|
|
3
|
+

|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
[](https://www.npmjs.com/package/@code-fixer-23/pi-session-manager)
|
package/extensions/index.test.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
handleSessionCleanOlderThan,
|
|
12
12
|
handleSessionDeleteLast,
|
|
13
13
|
handleSessionSeries,
|
|
14
|
+
getSessionEntryWithSeries,
|
|
14
15
|
getSessionSeriesDataTempPath,
|
|
15
16
|
persistSessionSeriesData,
|
|
16
17
|
consumePersistedSessionSeriesData,
|
|
@@ -254,6 +255,7 @@ describe("persisted session series data", () => {
|
|
|
254
255
|
entry: {
|
|
255
256
|
customType: sessionSeriesEntrySchema.entries.customType.literal,
|
|
256
257
|
series: "Implement Auth",
|
|
258
|
+
sessionTitle: "Create JWT Token",
|
|
257
259
|
createdAt: new Date().toISOString(),
|
|
258
260
|
},
|
|
259
261
|
};
|
|
@@ -279,6 +281,7 @@ describe("persisted session series data", () => {
|
|
|
279
281
|
expect(pi.setSessionName).toHaveBeenCalledWith(sessionData.sessionName);
|
|
280
282
|
expect(pi.appendEntry).toHaveBeenCalledWith(sessionData.entry.customType, {
|
|
281
283
|
series: sessionData.entry.series,
|
|
284
|
+
sessionTitle: sessionData.entry.sessionTitle,
|
|
282
285
|
createdAt: sessionData.entry.createdAt,
|
|
283
286
|
});
|
|
284
287
|
expect(ctx.ui.notify).toHaveBeenCalledWith(
|
|
@@ -288,6 +291,82 @@ describe("persisted session series data", () => {
|
|
|
288
291
|
});
|
|
289
292
|
});
|
|
290
293
|
|
|
294
|
+
describe("getSessionEntryWithSeries", () => {
|
|
295
|
+
it("only returns the series entry that matches the current session name", () => {
|
|
296
|
+
const createdAt = new Date().toISOString();
|
|
297
|
+
const lspCoverageEntry = {
|
|
298
|
+
type: "custom",
|
|
299
|
+
customType: sessionSeriesEntrySchema.entries.customType.literal,
|
|
300
|
+
data: {
|
|
301
|
+
series: "LSP Coverage",
|
|
302
|
+
sessionTitle: "Add diagnostics",
|
|
303
|
+
createdAt,
|
|
304
|
+
},
|
|
305
|
+
} as SessionSeriesEntry;
|
|
306
|
+
const authEntry = {
|
|
307
|
+
type: "custom",
|
|
308
|
+
customType: sessionSeriesEntrySchema.entries.customType.literal,
|
|
309
|
+
data: {
|
|
310
|
+
series: "Auth Cleanup",
|
|
311
|
+
sessionTitle: "Fix token refresh",
|
|
312
|
+
createdAt,
|
|
313
|
+
},
|
|
314
|
+
} as SessionSeriesEntry;
|
|
315
|
+
|
|
316
|
+
expect(
|
|
317
|
+
getSessionEntryWithSeries(
|
|
318
|
+
[lspCoverageEntry, authEntry],
|
|
319
|
+
`Auth Cleanup${SESION_TITLE_SEPARATOR}Fix token refresh`,
|
|
320
|
+
),
|
|
321
|
+
).toBe(authEntry);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it("does not return another session's series entry", () => {
|
|
325
|
+
const createdAt = new Date().toISOString();
|
|
326
|
+
const lspCoverageEntry = {
|
|
327
|
+
type: "custom",
|
|
328
|
+
customType: sessionSeriesEntrySchema.entries.customType.literal,
|
|
329
|
+
data: {
|
|
330
|
+
series: "LSP Coverage",
|
|
331
|
+
sessionTitle: "Add diagnostics",
|
|
332
|
+
createdAt,
|
|
333
|
+
},
|
|
334
|
+
} as SessionSeriesEntry;
|
|
335
|
+
|
|
336
|
+
expect(
|
|
337
|
+
getSessionEntryWithSeries(
|
|
338
|
+
[lspCoverageEntry],
|
|
339
|
+
`Auth Cleanup${SESION_TITLE_SEPARATOR}Fix token refresh`,
|
|
340
|
+
),
|
|
341
|
+
).toBeUndefined();
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it("falls back to the session name when legacy series entries are missing sessionTitle", () => {
|
|
345
|
+
const createdAt = new Date().toISOString();
|
|
346
|
+
const legacyEntry = {
|
|
347
|
+
type: "custom",
|
|
348
|
+
customType: sessionSeriesEntrySchema.entries.customType.literal,
|
|
349
|
+
data: {
|
|
350
|
+
series: "Auth Cleanup",
|
|
351
|
+
createdAt,
|
|
352
|
+
},
|
|
353
|
+
} as unknown as SessionSeriesEntry;
|
|
354
|
+
|
|
355
|
+
expect(
|
|
356
|
+
getSessionEntryWithSeries(
|
|
357
|
+
[legacyEntry],
|
|
358
|
+
`Auth Cleanup${SESION_TITLE_SEPARATOR}Fix token refresh`,
|
|
359
|
+
),
|
|
360
|
+
).toEqual({
|
|
361
|
+
...legacyEntry,
|
|
362
|
+
data: {
|
|
363
|
+
...legacyEntry.data,
|
|
364
|
+
sessionTitle: "Fix token refresh",
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
291
370
|
describe("handleSessionCleanInactive", () => {
|
|
292
371
|
test("gets rid of all sessions that haven't been modified in the last three days", ({
|
|
293
372
|
sessions,
|
|
@@ -507,6 +586,75 @@ describe("handleSessionSeries", () => {
|
|
|
507
586
|
);
|
|
508
587
|
});
|
|
509
588
|
|
|
589
|
+
it("stops creating a session series when the series prompt is cancelled", async () => {
|
|
590
|
+
const context = {
|
|
591
|
+
cwd: "/pi-packages",
|
|
592
|
+
newSession: vi.fn<ExtensionCommandContext["newSession"]>(),
|
|
593
|
+
ui: {
|
|
594
|
+
notify: vi.fn<ExtensionUIContext["notify"]>(),
|
|
595
|
+
input: vi
|
|
596
|
+
.fn<ExtensionUIContext["input"]>()
|
|
597
|
+
.mockResolvedValue(undefined),
|
|
598
|
+
},
|
|
599
|
+
} satisfies MockExtenstionCommandContext;
|
|
600
|
+
|
|
601
|
+
await handleSessionSeries(
|
|
602
|
+
"create",
|
|
603
|
+
{
|
|
604
|
+
sessionManagerConfigurator: new SessionManagerConfiguratorMock(),
|
|
605
|
+
sessionFilter: new MockSessionFilter(
|
|
606
|
+
[],
|
|
607
|
+
new MockPastTimestampCalculator(),
|
|
608
|
+
),
|
|
609
|
+
getSessionEntryWithSeries() {
|
|
610
|
+
return undefined;
|
|
611
|
+
},
|
|
612
|
+
removeSessionFiles() {
|
|
613
|
+
return;
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
castToExtensionContext(context),
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
expect(context.ui.input).toHaveBeenCalledTimes(1);
|
|
620
|
+
expect(context.newSession).not.toHaveBeenCalled();
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it("stops creating a session series when the series input is empty", async () => {
|
|
624
|
+
const context = {
|
|
625
|
+
cwd: "/pi-packages",
|
|
626
|
+
newSession: vi.fn<ExtensionCommandContext["newSession"]>(),
|
|
627
|
+
ui: {
|
|
628
|
+
notify: vi.fn<ExtensionUIContext["notify"]>(),
|
|
629
|
+
input: vi
|
|
630
|
+
.fn<ExtensionUIContext["input"]>()
|
|
631
|
+
.mockResolvedValueOnce("")
|
|
632
|
+
.mockResolvedValueOnce("Should not be prompted"),
|
|
633
|
+
},
|
|
634
|
+
} satisfies MockExtenstionCommandContext;
|
|
635
|
+
|
|
636
|
+
await handleSessionSeries(
|
|
637
|
+
"create",
|
|
638
|
+
{
|
|
639
|
+
sessionManagerConfigurator: new SessionManagerConfiguratorMock(),
|
|
640
|
+
sessionFilter: new MockSessionFilter(
|
|
641
|
+
[],
|
|
642
|
+
new MockPastTimestampCalculator(),
|
|
643
|
+
),
|
|
644
|
+
getSessionEntryWithSeries() {
|
|
645
|
+
return undefined;
|
|
646
|
+
},
|
|
647
|
+
removeSessionFiles() {
|
|
648
|
+
return;
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
castToExtensionContext(context),
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
expect(context.ui.input).toHaveBeenCalledTimes(1);
|
|
655
|
+
expect(context.newSession).not.toHaveBeenCalled();
|
|
656
|
+
});
|
|
657
|
+
|
|
510
658
|
const seriesInput = "Implement Auth";
|
|
511
659
|
it("keeps asking for a unique trimmed session series when creating", async () => {
|
|
512
660
|
const sessionCtx = {
|
|
@@ -528,6 +676,7 @@ describe("handleSessionSeries", () => {
|
|
|
528
676
|
notify: vi.fn<ExtensionUIContext["notify"]>(),
|
|
529
677
|
input: vi
|
|
530
678
|
.fn<ExtensionUIContext["input"]>()
|
|
679
|
+
.mockResolvedValueOnce(" ")
|
|
531
680
|
.mockResolvedValueOnce(" Implement Auth ")
|
|
532
681
|
.mockResolvedValueOnce(" Implement Billing ")
|
|
533
682
|
.mockResolvedValueOnce(" Create JWT Token "),
|
|
@@ -581,7 +730,7 @@ describe("handleSessionSeries", () => {
|
|
|
581
730
|
"warning",
|
|
582
731
|
);
|
|
583
732
|
|
|
584
|
-
expect(context.ui.input).toHaveBeenCalledTimes(
|
|
733
|
+
expect(context.ui.input).toHaveBeenCalledTimes(4);
|
|
585
734
|
|
|
586
735
|
expect(appendSessionSeriesBasedOnCwdSpy).toHaveBeenCalledWith(
|
|
587
736
|
sessionCtx.cwd,
|
|
@@ -834,6 +983,50 @@ describe("handleSessionSeries", () => {
|
|
|
834
983
|
);
|
|
835
984
|
});
|
|
836
985
|
|
|
986
|
+
it("stops creating a new session in a series when the title prompt is cancelled", async () => {
|
|
987
|
+
const selectedSeries = "refactor-auth-middleware";
|
|
988
|
+
const context = {
|
|
989
|
+
cwd: "/user/work/cancel-new-title",
|
|
990
|
+
newSession: vi.fn<ExtensionCommandContext["newSession"]>(),
|
|
991
|
+
ui: {
|
|
992
|
+
notify: vi.fn<ExtensionUIContext["notify"]>(),
|
|
993
|
+
input: vi
|
|
994
|
+
.fn<ExtensionUIContext["input"]>()
|
|
995
|
+
.mockResolvedValue(undefined),
|
|
996
|
+
select: vi
|
|
997
|
+
.fn<ExtensionUIContext["select"]>()
|
|
998
|
+
.mockResolvedValue(selectedSeries),
|
|
999
|
+
},
|
|
1000
|
+
} satisfies MockExtenstionCommandContext;
|
|
1001
|
+
|
|
1002
|
+
await handleSessionSeries(
|
|
1003
|
+
"new",
|
|
1004
|
+
{
|
|
1005
|
+
sessionManagerConfigurator: new SessionManagerConfiguratorMock({
|
|
1006
|
+
seriesRecord: {
|
|
1007
|
+
[context.cwd]: {
|
|
1008
|
+
[selectedSeries]: [],
|
|
1009
|
+
},
|
|
1010
|
+
},
|
|
1011
|
+
}),
|
|
1012
|
+
sessionFilter: new MockSessionFilter(
|
|
1013
|
+
[],
|
|
1014
|
+
new MockPastTimestampCalculator(),
|
|
1015
|
+
),
|
|
1016
|
+
getSessionEntryWithSeries() {
|
|
1017
|
+
return undefined;
|
|
1018
|
+
},
|
|
1019
|
+
removeSessionFiles() {
|
|
1020
|
+
return;
|
|
1021
|
+
},
|
|
1022
|
+
},
|
|
1023
|
+
castToExtensionContext(context),
|
|
1024
|
+
);
|
|
1025
|
+
|
|
1026
|
+
expect(context.ui.input).toHaveBeenCalledTimes(1);
|
|
1027
|
+
expect(context.newSession).not.toHaveBeenCalled();
|
|
1028
|
+
});
|
|
1029
|
+
|
|
837
1030
|
it("keeps asking for a unique trimmed title when creating a new session in a series", async () => {
|
|
838
1031
|
const selectedSeries = "refactor-auth-middleware";
|
|
839
1032
|
|
|
@@ -922,6 +1115,55 @@ describe("handleSessionSeries", () => {
|
|
|
922
1115
|
);
|
|
923
1116
|
});
|
|
924
1117
|
|
|
1118
|
+
it("stops continuing a session in a series when the title prompt is cancelled", async () => {
|
|
1119
|
+
const context = {
|
|
1120
|
+
cwd: "/user/work/cancel-continue-title",
|
|
1121
|
+
sessionManager: {
|
|
1122
|
+
getEntries:
|
|
1123
|
+
vi.fn<ExtensionCommandContext["sessionManager"]["getEntries"]>(),
|
|
1124
|
+
getSessionName: vi
|
|
1125
|
+
.fn<ExtensionCommandContext["sessionManager"]["getSessionName"]>()
|
|
1126
|
+
.mockReturnValue("refactor-auth-middleware--current-task"),
|
|
1127
|
+
},
|
|
1128
|
+
newSession: vi.fn<ExtensionCommandContext["newSession"]>(),
|
|
1129
|
+
ui: {
|
|
1130
|
+
notify: vi.fn<ExtensionUIContext["notify"]>(),
|
|
1131
|
+
input: vi
|
|
1132
|
+
.fn<ExtensionUIContext["input"]>()
|
|
1133
|
+
.mockResolvedValue(undefined),
|
|
1134
|
+
},
|
|
1135
|
+
} satisfies MockExtenstionCommandContext;
|
|
1136
|
+
|
|
1137
|
+
await handleSessionSeries(
|
|
1138
|
+
"continue",
|
|
1139
|
+
{
|
|
1140
|
+
sessionFilter: new MockSessionFilter(
|
|
1141
|
+
[],
|
|
1142
|
+
new MockPastTimestampCalculator(),
|
|
1143
|
+
),
|
|
1144
|
+
getSessionEntryWithSeries() {
|
|
1145
|
+
return {
|
|
1146
|
+
type: "custom",
|
|
1147
|
+
customType: sessionSeriesEntrySchema.entries.customType.literal,
|
|
1148
|
+
data: {
|
|
1149
|
+
series: "refactor-auth-middleware",
|
|
1150
|
+
sessionTitle: "current-task",
|
|
1151
|
+
createdAt: new Date().toISOString(),
|
|
1152
|
+
},
|
|
1153
|
+
} as SessionSeriesEntry;
|
|
1154
|
+
},
|
|
1155
|
+
sessionManagerConfigurator: new SessionManagerConfiguratorMock(),
|
|
1156
|
+
removeSessionFiles() {
|
|
1157
|
+
return;
|
|
1158
|
+
},
|
|
1159
|
+
},
|
|
1160
|
+
castToExtensionContext(context),
|
|
1161
|
+
);
|
|
1162
|
+
|
|
1163
|
+
expect(context.ui.input).toHaveBeenCalledTimes(1);
|
|
1164
|
+
expect(context.newSession).not.toHaveBeenCalled();
|
|
1165
|
+
});
|
|
1166
|
+
|
|
925
1167
|
it("continues a session in a series when continue is passed", async () => {
|
|
926
1168
|
const sessionCtx = {
|
|
927
1169
|
cwd: "/session/continue",
|
|
@@ -935,6 +1177,9 @@ describe("handleSessionSeries", () => {
|
|
|
935
1177
|
sessionManager: {
|
|
936
1178
|
getEntries:
|
|
937
1179
|
vi.fn<ExtensionCommandContext["sessionManager"]["getEntries"]>(),
|
|
1180
|
+
getSessionName: vi
|
|
1181
|
+
.fn<ExtensionCommandContext["sessionManager"]["getSessionName"]>()
|
|
1182
|
+
.mockReturnValue("refactor-auth-middleware--current-task"),
|
|
938
1183
|
},
|
|
939
1184
|
newSession: vi.fn<ExtensionCommandContext["newSession"]>(
|
|
940
1185
|
async (options) => {
|
|
@@ -957,6 +1202,7 @@ describe("handleSessionSeries", () => {
|
|
|
957
1202
|
customType: sessionSeriesEntrySchema.entries.customType.literal,
|
|
958
1203
|
data: {
|
|
959
1204
|
series: "refactor-auth-middleware",
|
|
1205
|
+
sessionTitle: "current-task",
|
|
960
1206
|
createdAt: new Date().toISOString(),
|
|
961
1207
|
},
|
|
962
1208
|
} as SessionSeriesEntry;
|
|
@@ -1010,6 +1256,7 @@ describe("handleSessionSeries", () => {
|
|
|
1010
1256
|
|
|
1011
1257
|
expect(getSessionEntryWithSeries).toHaveBeenCalledWith(
|
|
1012
1258
|
context.sessionManager.getEntries.mock.results[0]?.value,
|
|
1259
|
+
context.sessionManager.getSessionName(),
|
|
1013
1260
|
);
|
|
1014
1261
|
|
|
1015
1262
|
const entry = getSessionEntryWithSeries.mock.results[0]
|