pi-session-cleanup 1.1.0 → 1.1.2
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 +27 -17
- package/package.json +3 -3
- package/src/agent-target.ts +361 -361
- package/src/index.ts +1 -1
- package/src/session-agent.ts +103 -103
- package/src/session-cleanup-command.ts +268 -268
- package/src/session-delete.ts +165 -11
- package/src/session-entry.ts +23 -23
- package/src/session-format.ts +98 -98
- package/src/session-nix-command.ts +353 -329
- package/src/session-quit-shutdown.ts +40 -40
- package/src/session-selection.ts +167 -167
- package/src/session-sort.ts +137 -137
- package/src/session-source.ts +32 -32
- package/src/tui/agent-target-picker.ts +306 -306
- package/src/tui/session-cleanup-picker.ts +592 -592
- package/src/types-shims.d.ts +23 -9
- package/src/types.ts +1 -1
|
@@ -1,329 +1,353 @@
|
|
|
1
|
-
import { basename } from "node:path";
|
|
2
|
-
|
|
3
|
-
import type { ExtensionCommandContext } from "@
|
|
4
|
-
|
|
5
|
-
import { SESSION_NIX_COMMAND } from "./constants.js";
|
|
6
|
-
import {
|
|
7
|
-
resolveTargetAgentForSessionNix,
|
|
8
|
-
type SelectableAgent,
|
|
9
|
-
} from "./agent-target.js";
|
|
10
|
-
import { extractPersistedActiveAgentNameFromEntries } from "./session-agent.js";
|
|
11
|
-
import { deleteSessionFile } from "./session-delete.js";
|
|
12
|
-
import { appendActiveAgentSessionEntry } from "./session-entry.js";
|
|
13
|
-
import {
|
|
14
|
-
clearScheduledSessionDeletionForQuit,
|
|
15
|
-
scheduleSessionDeletionForQuit,
|
|
16
|
-
} from "./session-quit-shutdown.js";
|
|
17
|
-
|
|
18
|
-
const ARG_COMPLETIONS = [
|
|
19
|
-
{
|
|
20
|
-
value: "quit",
|
|
21
|
-
label: "quit",
|
|
22
|
-
description: "Delete the current session and quit Pi immediately",
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
value: "agent",
|
|
26
|
-
label: "agent",
|
|
27
|
-
description: "Start a fresh session with a selected target agent",
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
value: "help",
|
|
31
|
-
label: "help",
|
|
32
|
-
description: "Show usage",
|
|
33
|
-
},
|
|
34
|
-
] as const;
|
|
35
|
-
|
|
36
|
-
type NixMode = "fresh" | "quit" | "agent";
|
|
37
|
-
|
|
38
|
-
interface ParsedArgs {
|
|
39
|
-
help: boolean;
|
|
40
|
-
mode: NixMode;
|
|
41
|
-
targetAgentName?: string;
|
|
42
|
-
error?: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
if (!
|
|
68
|
-
return { help: false, mode: "fresh" };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
"
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
|
|
3
|
+
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
|
|
5
|
+
import { SESSION_NIX_COMMAND } from "./constants.js";
|
|
6
|
+
import {
|
|
7
|
+
resolveTargetAgentForSessionNix,
|
|
8
|
+
type SelectableAgent,
|
|
9
|
+
} from "./agent-target.js";
|
|
10
|
+
import { extractPersistedActiveAgentNameFromEntries } from "./session-agent.js";
|
|
11
|
+
import { deleteSessionFile } from "./session-delete.js";
|
|
12
|
+
import { appendActiveAgentSessionEntry } from "./session-entry.js";
|
|
13
|
+
import {
|
|
14
|
+
clearScheduledSessionDeletionForQuit,
|
|
15
|
+
scheduleSessionDeletionForQuit,
|
|
16
|
+
} from "./session-quit-shutdown.js";
|
|
17
|
+
|
|
18
|
+
const ARG_COMPLETIONS = [
|
|
19
|
+
{
|
|
20
|
+
value: "quit",
|
|
21
|
+
label: "quit",
|
|
22
|
+
description: "Delete the current session and quit Pi immediately",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
value: "agent",
|
|
26
|
+
label: "agent",
|
|
27
|
+
description: "Start a fresh session with a selected target agent",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
value: "help",
|
|
31
|
+
label: "help",
|
|
32
|
+
description: "Show usage",
|
|
33
|
+
},
|
|
34
|
+
] as const;
|
|
35
|
+
|
|
36
|
+
type NixMode = "fresh" | "quit" | "agent";
|
|
37
|
+
|
|
38
|
+
interface ParsedArgs {
|
|
39
|
+
help: boolean;
|
|
40
|
+
mode: NixMode;
|
|
41
|
+
targetAgentName?: string;
|
|
42
|
+
error?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type DeleteSessionFileFn = typeof deleteSessionFile;
|
|
46
|
+
|
|
47
|
+
interface SessionNixCommandOptions {
|
|
48
|
+
deleteSessionFile?: DeleteSessionFileFn;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function usage(): string {
|
|
52
|
+
return [
|
|
53
|
+
`Usage: /${SESSION_NIX_COMMAND}`,
|
|
54
|
+
` /${SESSION_NIX_COMMAND} quit`,
|
|
55
|
+
` /${SESSION_NIX_COMMAND} agent [name]`,
|
|
56
|
+
` /${SESSION_NIX_COMMAND} help`,
|
|
57
|
+
].join("\n");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getSessionLabel(sessionPath: string): string {
|
|
61
|
+
const fileName = basename(sessionPath).trim();
|
|
62
|
+
return fileName.length > 0 ? fileName : sessionPath;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseArgs(args: string): ParsedArgs {
|
|
66
|
+
const trimmed = args.trim();
|
|
67
|
+
if (!trimmed) {
|
|
68
|
+
return { help: false, mode: "fresh" };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const parts = trimmed.split(/\s+/);
|
|
72
|
+
const command = parts[0]?.toLowerCase();
|
|
73
|
+
if (!command) {
|
|
74
|
+
return { help: false, mode: "fresh" };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (command === "help") {
|
|
78
|
+
return { help: true, mode: "fresh" };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (command === "quit" || command === "exit") {
|
|
82
|
+
return parts.length === 1
|
|
83
|
+
? { help: false, mode: "quit" }
|
|
84
|
+
: {
|
|
85
|
+
help: false,
|
|
86
|
+
mode: "quit",
|
|
87
|
+
error: `/${SESSION_NIX_COMMAND} quit does not accept additional arguments.`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (command === "agent") {
|
|
92
|
+
return {
|
|
93
|
+
help: false,
|
|
94
|
+
mode: "agent",
|
|
95
|
+
targetAgentName: parts.length > 1 ? parts.slice(1).join(" ") : undefined,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
help: false,
|
|
101
|
+
mode: "fresh",
|
|
102
|
+
error: `Unknown argument: ${trimmed}`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function buildFreshConfirmationMessage(previousSessionFile: string | undefined): string {
|
|
107
|
+
if (!previousSessionFile) {
|
|
108
|
+
return "This will start a new session. The current session is not persisted yet, so there is no session file to delete.";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return [
|
|
112
|
+
"This will start a new session and permanently remove the current session from your session history.",
|
|
113
|
+
"",
|
|
114
|
+
`Current session: ${getSessionLabel(previousSessionFile)}`,
|
|
115
|
+
"",
|
|
116
|
+
"Pi will try moving it to trash first, then permanently delete it if trash is unavailable.",
|
|
117
|
+
].join("\n");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function buildQuitConfirmationMessage(previousSessionFile: string | undefined): string {
|
|
121
|
+
if (!previousSessionFile) {
|
|
122
|
+
return "This will quit Pi immediately. The current session is not persisted yet, so there is no session file to delete first.";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return [
|
|
126
|
+
"This will permanently remove the current session and then quit Pi immediately.",
|
|
127
|
+
"",
|
|
128
|
+
`Current session: ${getSessionLabel(previousSessionFile)}`,
|
|
129
|
+
"",
|
|
130
|
+
"Pi will try moving it to trash first, then permanently delete it if trash is unavailable.",
|
|
131
|
+
].join("\n");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function buildAgentConfirmationMessage(
|
|
135
|
+
previousSessionFile: string | undefined,
|
|
136
|
+
targetAgent: SelectableAgent,
|
|
137
|
+
): string {
|
|
138
|
+
if (!previousSessionFile) {
|
|
139
|
+
return [
|
|
140
|
+
`This will start a new session with agent '${targetAgent.name}'.`,
|
|
141
|
+
"",
|
|
142
|
+
"The current session is not persisted yet, so there is no session file to delete.",
|
|
143
|
+
].join("\n");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return [
|
|
147
|
+
`This will start a new session with agent '${targetAgent.name}' and remove the current session from history.`,
|
|
148
|
+
"",
|
|
149
|
+
`Current session: ${getSessionLabel(previousSessionFile)}`,
|
|
150
|
+
`Target agent: ${targetAgent.name}`,
|
|
151
|
+
"",
|
|
152
|
+
"Pi will try moving the old session to trash first, then permanently delete it if trash is unavailable.",
|
|
153
|
+
].join("\n");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function getSessionNixArgumentCompletions(
|
|
157
|
+
argumentPrefix: string,
|
|
158
|
+
): Array<{ value: string; label: string; description?: string }> | null {
|
|
159
|
+
const normalizedPrefix = argumentPrefix.trim().toLowerCase();
|
|
160
|
+
if (!normalizedPrefix) {
|
|
161
|
+
return [...ARG_COMPLETIONS];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const matched = ARG_COMPLETIONS.filter((item) => item.value.startsWith(normalizedPrefix));
|
|
165
|
+
if (matched.length === 0) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return matched.map((item) => ({ ...item }));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function deletePreviousSessionAfterSwitch(
|
|
173
|
+
ctx: ExtensionCommandContext,
|
|
174
|
+
previousSessionFile: string | undefined,
|
|
175
|
+
deleteSessionFileFn: DeleteSessionFileFn,
|
|
176
|
+
): Promise<void> {
|
|
177
|
+
if (!previousSessionFile) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const deleteResult = await deleteSessionFileFn(previousSessionFile);
|
|
183
|
+
if (!deleteResult.ok) {
|
|
184
|
+
ctx.ui.notify(
|
|
185
|
+
`Failed to delete the previous session after starting the new session: ${deleteResult.error}`,
|
|
186
|
+
"warning",
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
191
|
+
ctx.ui.notify(
|
|
192
|
+
`Failed to delete the previous session after starting the new session: ${message}`,
|
|
193
|
+
"warning",
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function startFreshSession(
|
|
199
|
+
ctx: ExtensionCommandContext,
|
|
200
|
+
previousSessionFile: string | undefined,
|
|
201
|
+
deleteSessionFileFn: DeleteSessionFileFn,
|
|
202
|
+
): Promise<void> {
|
|
203
|
+
try {
|
|
204
|
+
const newSessionResult = await ctx.newSession();
|
|
205
|
+
if (newSessionResult.cancelled) {
|
|
206
|
+
ctx.ui.notify("New session cancelled.", "info");
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
211
|
+
ctx.ui.notify(`Failed to start a new session: ${message}`, "error");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
await deletePreviousSessionAfterSwitch(ctx, previousSessionFile, deleteSessionFileFn);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function startAgentTargetSession(
|
|
219
|
+
ctx: ExtensionCommandContext,
|
|
220
|
+
previousSessionFile: string | undefined,
|
|
221
|
+
targetAgent: SelectableAgent,
|
|
222
|
+
deleteSessionFileFn: DeleteSessionFileFn,
|
|
223
|
+
): Promise<void> {
|
|
224
|
+
try {
|
|
225
|
+
const newSessionResult = await ctx.newSession({
|
|
226
|
+
setup: async (sessionManager) => {
|
|
227
|
+
appendActiveAgentSessionEntry(sessionManager, targetAgent.name);
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if (newSessionResult.cancelled) {
|
|
232
|
+
ctx.ui.notify("New session cancelled.", "info");
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
} catch (error) {
|
|
236
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
237
|
+
ctx.ui.notify(`Failed to start a new session for agent '${targetAgent.name}': ${message}`, "error");
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
await deletePreviousSessionAfterSwitch(ctx, previousSessionFile, deleteSessionFileFn);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function requestGracefulQuit(
|
|
245
|
+
ctx: ExtensionCommandContext,
|
|
246
|
+
previousSessionFile: string | undefined,
|
|
247
|
+
): Promise<void> {
|
|
248
|
+
const shutdown = (ctx as ExtensionCommandContext & {
|
|
249
|
+
shutdown?: () => Promise<void> | void;
|
|
250
|
+
}).shutdown;
|
|
251
|
+
|
|
252
|
+
if (typeof shutdown !== "function") {
|
|
253
|
+
ctx.ui.notify(
|
|
254
|
+
"Graceful shutdown is unavailable in this Pi build. Update Pi to use /nix quit safely.",
|
|
255
|
+
"warning",
|
|
256
|
+
);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
scheduleSessionDeletionForQuit(previousSessionFile);
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
await Promise.resolve(shutdown.call(ctx));
|
|
264
|
+
} catch (error) {
|
|
265
|
+
clearScheduledSessionDeletionForQuit();
|
|
266
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
267
|
+
ctx.ui.notify(`Failed to quit Pi gracefully: ${message}`, "error");
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export async function handleSessionNixCommand(
|
|
272
|
+
args: string,
|
|
273
|
+
ctx: ExtensionCommandContext,
|
|
274
|
+
options: SessionNixCommandOptions = {},
|
|
275
|
+
): Promise<void> {
|
|
276
|
+
const parsed = parseArgs(args);
|
|
277
|
+
if (parsed.help) {
|
|
278
|
+
ctx.ui.notify(usage(), "info");
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (parsed.error) {
|
|
283
|
+
ctx.ui.notify(`${parsed.error}\n${usage()}`, "warning");
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (!ctx.hasUI) {
|
|
288
|
+
ctx.ui.notify(`/${SESSION_NIX_COMMAND} requires interactive TUI mode.`, "warning");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const previousSessionFile = ctx.sessionManager.getSessionFile();
|
|
293
|
+
const deleteSessionFileFn = options.deleteSessionFile ?? deleteSessionFile;
|
|
294
|
+
|
|
295
|
+
if (parsed.mode === "fresh") {
|
|
296
|
+
const confirmed = await ctx.ui.confirm(
|
|
297
|
+
"Start fresh and delete current session",
|
|
298
|
+
buildFreshConfirmationMessage(previousSessionFile),
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
if (!confirmed) {
|
|
302
|
+
ctx.ui.notify(`/${SESSION_NIX_COMMAND} cancelled.`, "info");
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
await startFreshSession(ctx, previousSessionFile, deleteSessionFileFn);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (parsed.mode === "quit") {
|
|
311
|
+
const confirmed = await ctx.ui.confirm(
|
|
312
|
+
"Delete current session and quit Pi",
|
|
313
|
+
buildQuitConfirmationMessage(previousSessionFile),
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
if (!confirmed) {
|
|
317
|
+
ctx.ui.notify(`/${SESSION_NIX_COMMAND} quit cancelled.`, "info");
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
await requestGracefulQuit(ctx, previousSessionFile);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const currentAgentName =
|
|
326
|
+
extractPersistedActiveAgentNameFromEntries(ctx.sessionManager.getEntries()) ?? null;
|
|
327
|
+
const targetAgent = await resolveTargetAgentForSessionNix(
|
|
328
|
+
ctx,
|
|
329
|
+
parsed.targetAgentName,
|
|
330
|
+
currentAgentName,
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
if (targetAgent === null) {
|
|
334
|
+
ctx.ui.notify(`/${SESSION_NIX_COMMAND} agent cancelled.`, "info");
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (!targetAgent) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const confirmed = await ctx.ui.confirm(
|
|
343
|
+
`Start a new '${targetAgent.name}' session`,
|
|
344
|
+
buildAgentConfirmationMessage(previousSessionFile, targetAgent),
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
if (!confirmed) {
|
|
348
|
+
ctx.ui.notify(`/${SESSION_NIX_COMMAND} agent cancelled.`, "info");
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
await startAgentTargetSession(ctx, previousSessionFile, targetAgent, deleteSessionFileFn);
|
|
353
|
+
}
|