@google/gemini-cli 0.15.0-nightly.20251110.c0b766ad → 0.15.0-preview.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/dist/package.json +3 -3
- package/dist/src/config/config.d.ts +3 -0
- package/dist/src/config/config.js +31 -0
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +20 -0
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/extension-manager.js +13 -1
- package/dist/src/config/extension-manager.js.map +1 -1
- package/dist/src/config/extension.test.js +65 -0
- package/dist/src/config/extension.test.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +10 -1
- package/dist/src/config/settingsSchema.js +10 -1
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/gemini.d.ts +2 -2
- package/dist/src/gemini.js +40 -4
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +14 -7
- package/dist/src/gemini.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/nonInteractiveCli.d.ts +3 -2
- package/dist/src/nonInteractiveCli.js +6 -1
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/nonInteractiveCli.test.js +1 -0
- package/dist/src/nonInteractiveCli.test.js.map +1 -1
- package/dist/src/test-utils/render.d.ts +2 -1
- package/dist/src/test-utils/render.js +13 -3
- package/dist/src/test-utils/render.js.map +1 -1
- package/dist/src/ui/App.js +9 -1
- package/dist/src/ui/App.js.map +1 -1
- package/dist/src/ui/App.test.js +25 -1
- package/dist/src/ui/App.test.js.map +1 -1
- package/dist/src/ui/AppContainer.d.ts +2 -3
- package/dist/src/ui/AppContainer.js +22 -7
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/AppContainer.test.js +354 -46
- package/dist/src/ui/AppContainer.test.js.map +1 -1
- package/dist/src/ui/commands/toolsCommand.js +4 -1
- package/dist/src/ui/commands/toolsCommand.js.map +1 -1
- package/dist/src/ui/components/AlternateBufferQuittingDisplay.d.ts +6 -0
- package/dist/src/ui/components/AlternateBufferQuittingDisplay.js +24 -0
- package/dist/src/ui/components/AlternateBufferQuittingDisplay.js.map +1 -0
- package/dist/src/ui/components/AlternateBufferQuittingDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/AlternateBufferQuittingDisplay.test.js +101 -0
- package/dist/src/ui/components/AlternateBufferQuittingDisplay.test.js.map +1 -0
- package/dist/src/ui/components/Composer.js +11 -4
- package/dist/src/ui/components/Composer.js.map +1 -1
- package/dist/src/ui/components/CopyModeWarning.d.ts +7 -0
- package/dist/src/ui/components/CopyModeWarning.js +12 -0
- package/dist/src/ui/components/CopyModeWarning.js.map +1 -0
- package/dist/src/ui/components/DetailedMessagesDisplay.js +1 -1
- package/dist/src/ui/components/Header.js +11 -1
- package/dist/src/ui/components/Header.js.map +1 -1
- package/dist/src/ui/components/Header.test.js +69 -14
- package/dist/src/ui/components/Header.test.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.test.js +43 -41
- package/dist/src/ui/components/HistoryItemDisplay.test.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.d.ts +2 -0
- package/dist/src/ui/components/InputPrompt.js +14 -8
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/MainContent.js +52 -15
- package/dist/src/ui/components/MainContent.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.test.js +3 -0
- package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
- package/dist/src/ui/components/StickyHeader.d.ts +11 -0
- package/dist/src/ui/components/StickyHeader.js +5 -0
- package/dist/src/ui/components/StickyHeader.js.map +1 -0
- package/dist/src/ui/components/StickyHeader.test.d.ts +6 -0
- package/dist/src/ui/components/StickyHeader.test.js +17 -0
- package/dist/src/ui/components/StickyHeader.test.js.map +1 -0
- package/dist/src/ui/components/ThemeDialog.js +11 -3
- package/dist/src/ui/components/ThemeDialog.js.map +1 -1
- package/dist/src/ui/components/messages/DiffRenderer.js +129 -89
- package/dist/src/ui/components/messages/DiffRenderer.js.map +1 -1
- package/dist/src/ui/components/messages/DiffRenderer.test.js +107 -115
- package/dist/src/ui/components/messages/DiffRenderer.test.js.map +1 -1
- package/dist/src/ui/components/messages/GeminiMessage.js +3 -1
- package/dist/src/ui/components/messages/GeminiMessage.js.map +1 -1
- package/dist/src/ui/components/messages/GeminiMessageContent.js +3 -1
- package/dist/src/ui/components/messages/GeminiMessageContent.js.map +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js +152 -121
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.js +5 -7
- package/dist/src/ui/components/messages/ToolGroupMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.test.js +1 -5
- package/dist/src/ui/components/messages/ToolGroupMessage.test.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessage.js +39 -16
- package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.js +33 -4
- package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.js.map +1 -1
- package/dist/src/ui/components/messages/UserMessage.d.ts +1 -0
- package/dist/src/ui/components/messages/UserMessage.js +2 -2
- package/dist/src/ui/components/messages/UserMessage.js.map +1 -1
- package/dist/src/ui/components/shared/ScrollableList.test.js +61 -24
- package/dist/src/ui/components/shared/ScrollableList.test.js.map +1 -1
- package/dist/src/ui/components/shared/VirtualizedList.js +1 -1
- package/dist/src/ui/components/shared/VirtualizedList.js.map +1 -1
- package/dist/src/ui/components/shared/text-buffer.d.ts +2 -8
- package/dist/src/ui/components/shared/text-buffer.js +1 -1
- package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
- package/dist/src/ui/components/shared/text-buffer.test.js +35 -0
- package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -1
- package/dist/src/ui/constants.d.ts +1 -0
- package/dist/src/ui/constants.js +5 -0
- package/dist/src/ui/constants.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.d.ts +1 -0
- package/dist/src/ui/contexts/KeypressContext.js +10 -0
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/contexts/UIStateContext.d.ts +1 -0
- package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.test.js +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/useAlternateBuffer.d.ts +6 -0
- package/dist/src/ui/hooks/useAlternateBuffer.js +11 -0
- package/dist/src/ui/hooks/useAlternateBuffer.js.map +1 -0
- package/dist/src/ui/hooks/useGeminiStream.test.js +4 -0
- package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
- package/dist/src/ui/hooks/useKeypress.test.js +3 -1
- package/dist/src/ui/hooks/useKeypress.test.js.map +1 -1
- package/dist/src/ui/hooks/usePromptCompletion.js +2 -9
- package/dist/src/ui/hooks/usePromptCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useSelectionList.test.js +3 -0
- package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -1
- package/dist/src/ui/hooks/useSessionBrowser.d.ts +18 -0
- package/dist/src/ui/hooks/useSessionBrowser.js +152 -0
- package/dist/src/ui/hooks/useSessionBrowser.js.map +1 -0
- package/dist/src/ui/hooks/useSessionBrowser.test.d.ts +6 -0
- package/dist/src/ui/hooks/useSessionBrowser.test.js +544 -0
- package/dist/src/ui/hooks/useSessionBrowser.test.js.map +1 -0
- package/dist/src/ui/hooks/useSessionResume.d.ts +30 -0
- package/dist/src/ui/hooks/useSessionResume.js +56 -0
- package/dist/src/ui/hooks/useSessionResume.js.map +1 -0
- package/dist/src/ui/hooks/useSessionResume.test.d.ts +6 -0
- package/dist/src/ui/hooks/useSessionResume.test.js +325 -0
- package/dist/src/ui/hooks/useSessionResume.test.js.map +1 -0
- package/dist/src/ui/hooks/useToolScheduler.test.js +1 -0
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/hooks/vim.js +1 -0
- package/dist/src/ui/hooks/vim.js.map +1 -1
- package/dist/src/ui/hooks/vim.test.js +1 -0
- package/dist/src/ui/hooks/vim.test.js.map +1 -1
- package/dist/src/ui/keyMatchers.test.js +1 -0
- package/dist/src/ui/keyMatchers.test.js.map +1 -1
- package/dist/src/ui/layouts/DefaultAppLayout.js +9 -3
- package/dist/src/ui/layouts/DefaultAppLayout.js.map +1 -1
- package/dist/src/ui/utils/CodeColorizer.d.ts +11 -3
- package/dist/src/ui/utils/CodeColorizer.js +18 -9
- package/dist/src/ui/utils/CodeColorizer.js.map +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.js +32 -6
- package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
- package/dist/src/ui/utils/textUtils.d.ts +1 -0
- package/dist/src/ui/utils/textUtils.js +3 -2
- package/dist/src/ui/utils/textUtils.js.map +1 -1
- package/dist/src/ui/utils/textUtils.test.js +7 -1
- package/dist/src/ui/utils/textUtils.test.js.map +1 -1
- package/dist/src/ui/utils/ui-sizing.js +9 -3
- package/dist/src/ui/utils/ui-sizing.js.map +1 -1
- package/dist/src/utils/cleanup.test.js +1 -1
- package/dist/src/utils/cleanup.test.js.map +1 -1
- package/dist/src/utils/sessionCleanup.test.js +76 -0
- package/dist/src/utils/sessionCleanup.test.js.map +1 -1
- package/dist/src/utils/sessionUtils.d.ts +54 -0
- package/dist/src/utils/sessionUtils.js +141 -1
- package/dist/src/utils/sessionUtils.js.map +1 -1
- package/dist/src/utils/sessionUtils.test.d.ts +6 -0
- package/dist/src/utils/sessionUtils.test.js +260 -0
- package/dist/src/utils/sessionUtils.test.js.map +1 -0
- package/dist/src/utils/sessions.d.ts +8 -0
- package/dist/src/utils/sessions.js +64 -0
- package/dist/src/utils/sessions.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/dist/google-gemini-cli-0.15.0-nightly.20251107.b8eeb553.tgz +0 -0
|
@@ -3,18 +3,27 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
import type { Config, ConversationRecord, MessageRecord } from '@google/gemini-cli-core';
|
|
6
7
|
/**
|
|
7
8
|
* Session information for display and selection purposes.
|
|
8
9
|
*/
|
|
9
10
|
export interface SessionInfo {
|
|
10
11
|
/** Unique session identifier (filename without .json) */
|
|
11
12
|
id: string;
|
|
13
|
+
/** Filename without extension */
|
|
14
|
+
file: string;
|
|
12
15
|
/** Full filename including .json extension */
|
|
13
16
|
fileName: string;
|
|
17
|
+
/** ISO timestamp when session started */
|
|
18
|
+
startTime: string;
|
|
14
19
|
/** ISO timestamp when session was last updated */
|
|
15
20
|
lastUpdated: string;
|
|
21
|
+
/** Cleaned first user message content */
|
|
22
|
+
firstUserMessage: string;
|
|
16
23
|
/** Whether this is the currently active session */
|
|
17
24
|
isCurrentSession: boolean;
|
|
25
|
+
/** Display index in the list */
|
|
26
|
+
index: number;
|
|
18
27
|
}
|
|
19
28
|
/**
|
|
20
29
|
* Represents a session file, which may be valid or corrupted.
|
|
@@ -25,6 +34,21 @@ export interface SessionFileEntry {
|
|
|
25
34
|
/** Parsed session info if valid, null if corrupted */
|
|
26
35
|
sessionInfo: SessionInfo | null;
|
|
27
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Result of resolving a session selection argument.
|
|
39
|
+
*/
|
|
40
|
+
export interface SessionSelectionResult {
|
|
41
|
+
sessionPath: string;
|
|
42
|
+
sessionData: ConversationRecord;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Extracts the first meaningful user message from conversation messages.
|
|
46
|
+
*/
|
|
47
|
+
export declare const extractFirstUserMessage: (messages: MessageRecord[]) => string;
|
|
48
|
+
/**
|
|
49
|
+
* Formats a timestamp as relative time (e.g., "2 hours ago", "3 days ago").
|
|
50
|
+
*/
|
|
51
|
+
export declare const formatRelativeTime: (timestamp: string) => string;
|
|
28
52
|
/**
|
|
29
53
|
* Loads all session files (including corrupted ones) from the chats directory.
|
|
30
54
|
* @returns Array of session file entries, with sessionInfo null for corrupted files
|
|
@@ -35,3 +59,33 @@ export declare const getAllSessionFiles: (chatsDir: string, currentSessionId?: s
|
|
|
35
59
|
* Corrupted files are automatically filtered out.
|
|
36
60
|
*/
|
|
37
61
|
export declare const getSessionFiles: (chatsDir: string, currentSessionId?: string) => Promise<SessionInfo[]>;
|
|
62
|
+
/**
|
|
63
|
+
* Utility class for session discovery and selection.
|
|
64
|
+
*/
|
|
65
|
+
export declare class SessionSelector {
|
|
66
|
+
private config;
|
|
67
|
+
constructor(config: Config);
|
|
68
|
+
/**
|
|
69
|
+
* Lists all available sessions for the current project.
|
|
70
|
+
*/
|
|
71
|
+
listSessions(): Promise<SessionInfo[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Finds a session by identifier (UUID or numeric index).
|
|
74
|
+
*
|
|
75
|
+
* @param identifier - Can be a full UUID or an index number (1-based)
|
|
76
|
+
* @returns Promise resolving to the found SessionInfo
|
|
77
|
+
* @throws Error if the session is not found or identifier is invalid
|
|
78
|
+
*/
|
|
79
|
+
findSession(identifier: string): Promise<SessionInfo>;
|
|
80
|
+
/**
|
|
81
|
+
* Resolves a resume argument to a specific session.
|
|
82
|
+
*
|
|
83
|
+
* @param resumeArg - Can be "latest", a full UUID, or an index number (1-based)
|
|
84
|
+
* @returns Promise resolving to session selection result
|
|
85
|
+
*/
|
|
86
|
+
resolveSession(resumeArg: string): Promise<SessionSelectionResult>;
|
|
87
|
+
/**
|
|
88
|
+
* Loads session data for a selected session.
|
|
89
|
+
*/
|
|
90
|
+
private selectSession;
|
|
91
|
+
}
|
|
@@ -3,9 +3,48 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { SESSION_FILE_PREFIX, } from '@google/gemini-cli-core';
|
|
6
|
+
import { SESSION_FILE_PREFIX, partListUnionToString, } from '@google/gemini-cli-core';
|
|
7
7
|
import * as fs from 'node:fs/promises';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
+
/**
|
|
10
|
+
* Extracts the first meaningful user message from conversation messages.
|
|
11
|
+
*/
|
|
12
|
+
export const extractFirstUserMessage = (messages) => {
|
|
13
|
+
const userMessage = messages.find((msg) => {
|
|
14
|
+
const content = partListUnionToString(msg.content);
|
|
15
|
+
return msg.type === 'user' && content?.trim() && content !== '/resume';
|
|
16
|
+
});
|
|
17
|
+
if (!userMessage) {
|
|
18
|
+
return 'Empty conversation';
|
|
19
|
+
}
|
|
20
|
+
// Truncate long messages for display
|
|
21
|
+
const content = partListUnionToString(userMessage.content).trim();
|
|
22
|
+
return content.length > 100 ? content.slice(0, 97) + '...' : content;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Formats a timestamp as relative time (e.g., "2 hours ago", "3 days ago").
|
|
26
|
+
*/
|
|
27
|
+
export const formatRelativeTime = (timestamp) => {
|
|
28
|
+
const now = new Date();
|
|
29
|
+
const time = new Date(timestamp);
|
|
30
|
+
const diffMs = now.getTime() - time.getTime();
|
|
31
|
+
const diffSeconds = Math.floor(diffMs / 1000);
|
|
32
|
+
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
33
|
+
const diffHours = Math.floor(diffMinutes / 60);
|
|
34
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
35
|
+
if (diffDays > 0) {
|
|
36
|
+
return `${diffDays} day${diffDays === 1 ? '' : 's'} ago`;
|
|
37
|
+
}
|
|
38
|
+
else if (diffHours > 0) {
|
|
39
|
+
return `${diffHours} hour${diffHours === 1 ? '' : 's'} ago`;
|
|
40
|
+
}
|
|
41
|
+
else if (diffMinutes > 0) {
|
|
42
|
+
return `${diffMinutes} minute${diffMinutes === 1 ? '' : 's'} ago`;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return 'Just now';
|
|
46
|
+
}
|
|
47
|
+
};
|
|
9
48
|
/**
|
|
10
49
|
* Loads all session files (including corrupted ones) from the chats directory.
|
|
11
50
|
* @returns Array of session file entries, with sessionInfo null for corrupted files
|
|
@@ -29,14 +68,19 @@ export const getAllSessionFiles = async (chatsDir, currentSessionId) => {
|
|
|
29
68
|
// Missing required fields - treat as corrupted
|
|
30
69
|
return { fileName: file, sessionInfo: null };
|
|
31
70
|
}
|
|
71
|
+
const firstUserMessage = extractFirstUserMessage(content.messages);
|
|
32
72
|
const isCurrentSession = currentSessionId
|
|
33
73
|
? file.includes(currentSessionId.slice(0, 8))
|
|
34
74
|
: false;
|
|
35
75
|
const sessionInfo = {
|
|
36
76
|
id: content.sessionId,
|
|
77
|
+
file: file.replace('.json', ''),
|
|
37
78
|
fileName: file,
|
|
79
|
+
startTime: content.startTime,
|
|
38
80
|
lastUpdated: content.lastUpdated,
|
|
81
|
+
firstUserMessage,
|
|
39
82
|
isCurrentSession,
|
|
83
|
+
index: 0, // Will be set after sorting valid sessions
|
|
40
84
|
};
|
|
41
85
|
return { fileName: file, sessionInfo };
|
|
42
86
|
}
|
|
@@ -66,6 +110,102 @@ export const getSessionFiles = async (chatsDir, currentSessionId) => {
|
|
|
66
110
|
const validSessions = allFiles
|
|
67
111
|
.filter((entry) => entry.sessionInfo !== null)
|
|
68
112
|
.map((entry) => entry.sessionInfo);
|
|
113
|
+
// Sort by startTime (oldest first) for stable session numbering
|
|
114
|
+
validSessions.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime());
|
|
115
|
+
// Set the correct 1-based indexes after sorting
|
|
116
|
+
validSessions.forEach((session, index) => {
|
|
117
|
+
session.index = index + 1;
|
|
118
|
+
});
|
|
69
119
|
return validSessions;
|
|
70
120
|
};
|
|
121
|
+
/**
|
|
122
|
+
* Utility class for session discovery and selection.
|
|
123
|
+
*/
|
|
124
|
+
export class SessionSelector {
|
|
125
|
+
config;
|
|
126
|
+
constructor(config) {
|
|
127
|
+
this.config = config;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Lists all available sessions for the current project.
|
|
131
|
+
*/
|
|
132
|
+
async listSessions() {
|
|
133
|
+
const chatsDir = path.join(this.config.storage.getProjectTempDir(), 'chats');
|
|
134
|
+
return getSessionFiles(chatsDir, this.config.getSessionId());
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Finds a session by identifier (UUID or numeric index).
|
|
138
|
+
*
|
|
139
|
+
* @param identifier - Can be a full UUID or an index number (1-based)
|
|
140
|
+
* @returns Promise resolving to the found SessionInfo
|
|
141
|
+
* @throws Error if the session is not found or identifier is invalid
|
|
142
|
+
*/
|
|
143
|
+
async findSession(identifier) {
|
|
144
|
+
const sessions = await this.listSessions();
|
|
145
|
+
if (sessions.length === 0) {
|
|
146
|
+
throw new Error('No previous sessions found for this project.');
|
|
147
|
+
}
|
|
148
|
+
// Sort by startTime (oldest first, so newest sessions get highest numbers)
|
|
149
|
+
const sortedSessions = sessions.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime());
|
|
150
|
+
// Try to find by UUID first
|
|
151
|
+
const sessionByUuid = sortedSessions.find((session) => session.id === identifier);
|
|
152
|
+
if (sessionByUuid) {
|
|
153
|
+
return sessionByUuid;
|
|
154
|
+
}
|
|
155
|
+
// Parse as index number (1-based) - only allow numeric indexes
|
|
156
|
+
const index = parseInt(identifier, 10);
|
|
157
|
+
if (!isNaN(index) &&
|
|
158
|
+
index.toString() === identifier &&
|
|
159
|
+
index > 0 &&
|
|
160
|
+
index <= sortedSessions.length) {
|
|
161
|
+
return sortedSessions[index - 1];
|
|
162
|
+
}
|
|
163
|
+
throw new Error(`Invalid session identifier "${identifier}". Use --list-sessions to see available sessions.`);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Resolves a resume argument to a specific session.
|
|
167
|
+
*
|
|
168
|
+
* @param resumeArg - Can be "latest", a full UUID, or an index number (1-based)
|
|
169
|
+
* @returns Promise resolving to session selection result
|
|
170
|
+
*/
|
|
171
|
+
async resolveSession(resumeArg) {
|
|
172
|
+
let selectedSession;
|
|
173
|
+
if (resumeArg === 'latest') {
|
|
174
|
+
const sessions = await this.listSessions();
|
|
175
|
+
if (sessions.length === 0) {
|
|
176
|
+
throw new Error('No previous sessions found for this project.');
|
|
177
|
+
}
|
|
178
|
+
// Sort by startTime (oldest first, so newest sessions get highest numbers)
|
|
179
|
+
sessions.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime());
|
|
180
|
+
selectedSession = sessions[sessions.length - 1];
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
try {
|
|
184
|
+
selectedSession = await this.findSession(resumeArg);
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
// Re-throw with more detailed message for resume command
|
|
188
|
+
throw new Error(`Invalid session identifier "${resumeArg}". Use --list-sessions to see available sessions, then use --resume {number}, --resume {uuid}, or --resume latest. Error: ${error}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return this.selectSession(selectedSession);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Loads session data for a selected session.
|
|
195
|
+
*/
|
|
196
|
+
async selectSession(sessionInfo) {
|
|
197
|
+
const chatsDir = path.join(this.config.storage.getProjectTempDir(), 'chats');
|
|
198
|
+
const sessionPath = path.join(chatsDir, sessionInfo.fileName);
|
|
199
|
+
try {
|
|
200
|
+
const sessionData = JSON.parse(await fs.readFile(sessionPath, 'utf8'));
|
|
201
|
+
return {
|
|
202
|
+
sessionPath,
|
|
203
|
+
sessionData,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
throw new Error(`Failed to load session ${sessionInfo.id}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
71
211
|
//# sourceMappingURL=sessionUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionUtils.js","sourceRoot":"","sources":["../../../src/utils/sessionUtils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"sessionUtils.js","sourceRoot":"","sources":["../../../src/utils/sessionUtils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,EACL,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AA0C7B;;GAEG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,QAAyB,EAAU,EAAE;IAC3E,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;QACxC,MAAM,OAAO,GAAG,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACnD,OAAO,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,EAAE,IAAI,EAAE,IAAI,OAAO,KAAK,SAAS,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IAED,qCAAqC;IACrC,MAAM,OAAO,GAAG,qBAAqB,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IAClE,OAAO,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;AACvE,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,SAAiB,EAAU,EAAE;IAC9D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC;IAE5C,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACjB,OAAO,GAAG,QAAQ,OAAO,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;IAC3D,CAAC;SAAM,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,GAAG,SAAS,QAAQ,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;IAC9D,CAAC;SAAM,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO,GAAG,WAAW,UAAU,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;IACpE,CAAC;SAAM,CAAC;QACN,OAAO,UAAU,CAAC;IACpB,CAAC;AACH,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,QAAgB,EAChB,gBAAyB,EACI,EAAE;IAC/B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,YAAY,GAAG,KAAK;aACvB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;aACvE,IAAI,EAAE,CAAC,CAAC,6CAA6C;QAExD,MAAM,eAAe,GAAG,YAAY,CAAC,GAAG,CACtC,KAAK,EAAE,IAAI,EAA6B,EAAE;YACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC3C,IAAI,CAAC;gBACH,MAAM,OAAO,GAAuB,IAAI,CAAC,KAAK,CAC5C,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CACpC,CAAC;gBAEF,2BAA2B;gBAC3B,IACE,CAAC,OAAO,CAAC,SAAS;oBAClB,CAAC,OAAO,CAAC,QAAQ;oBACjB,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC;oBAChC,CAAC,OAAO,CAAC,SAAS;oBAClB,CAAC,OAAO,CAAC,WAAW,EACpB,CAAC;oBACD,+CAA+C;oBAC/C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;gBAC/C,CAAC;gBAED,MAAM,gBAAgB,GAAG,uBAAuB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACnE,MAAM,gBAAgB,GAAG,gBAAgB;oBACvC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;oBAC7C,CAAC,CAAC,KAAK,CAAC;gBAEV,MAAM,WAAW,GAAgB;oBAC/B,EAAE,EAAE,OAAO,CAAC,SAAS;oBACrB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;oBAC/B,QAAQ,EAAE,IAAI;oBACd,SAAS,EAAE,OAAO,CAAC,SAAS;oBAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;oBAChC,gBAAgB;oBAChB,gBAAgB;oBAChB,KAAK,EAAE,CAAC,EAAE,2CAA2C;iBACtD,CAAC;gBAEF,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,+CAA+C;gBAC/C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YAC/C,CAAC;QACH,CAAC,CACF,CAAC;QAEF,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,2EAA2E;QAC3E,IAAI,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACzE,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,8EAA8E;QAC9E,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,QAAgB,EAChB,gBAAyB,EACD,EAAE;IAC1B,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;IAEtE,qDAAqD;IACrD,MAAM,aAAa,GAAG,QAAQ;SAC3B,MAAM,CACL,CAAC,KAAK,EAA2D,EAAE,CACjE,KAAK,CAAC,WAAW,KAAK,IAAI,CAC7B;SACA,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAErC,gEAAgE;IAChE,aAAa,CAAC,IAAI,CAChB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAC5E,CAAC;IAEF,gDAAgD;IAChD,aAAa,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACvC,OAAO,CAAC,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,OAAO,aAAa,CAAC;AACvB,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,OAAO,eAAe;IACN;IAApB,YAAoB,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAEtC;;OAEG;IACH,KAAK,CAAC,YAAY;QAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,EACvC,OAAO,CACR,CAAC;QACF,OAAO,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,WAAW,CAAC,UAAkB;QAClC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAE3C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAClE,CAAC;QAED,2EAA2E;QAC3E,MAAM,cAAc,GAAG,QAAQ,CAAC,IAAI,CAClC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CACpE,CAAC;QAEF,4BAA4B;QAC5B,MAAM,aAAa,GAAG,cAAc,CAAC,IAAI,CACvC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,UAAU,CACvC,CAAC;QACF,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,aAAa,CAAC;QACvB,CAAC;QAED,+DAA+D;QAC/D,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACvC,IACE,CAAC,KAAK,CAAC,KAAK,CAAC;YACb,KAAK,CAAC,QAAQ,EAAE,KAAK,UAAU;YAC/B,KAAK,GAAG,CAAC;YACT,KAAK,IAAI,cAAc,CAAC,MAAM,EAC9B,CAAC;YACD,OAAO,cAAc,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,IAAI,KAAK,CACb,+BAA+B,UAAU,mDAAmD,CAC7F,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,cAAc,CAAC,SAAiB;QACpC,IAAI,eAA4B,CAAC;QAEjC,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;YAE3C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAClE,CAAC;YAED,2EAA2E;YAC3E,QAAQ,CAAC,IAAI,CACX,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CACpE,CAAC;YAEF,eAAe,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAClD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC;gBACH,eAAe,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YACtD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,yDAAyD;gBACzD,MAAM,IAAI,KAAK,CACb,+BAA+B,SAAS,8HAA8H,KAAK,EAAE,CAC9K,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,aAAa,CACzB,WAAwB;QAExB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,EACvC,OAAO,CACR,CAAC;QACF,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;QAE9D,IAAI,CAAC;YACH,MAAM,WAAW,GAAuB,IAAI,CAAC,KAAK,CAChD,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CACvC,CAAC;YAEF,OAAO;gBACL,WAAW;gBACX,WAAW;aACZ,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,0BAA0B,WAAW,CAAC,EAAE,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CACxG,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
7
|
+
import { SessionSelector, extractFirstUserMessage, formatRelativeTime, } from './sessionUtils.js';
|
|
8
|
+
import { SESSION_FILE_PREFIX } from '@google/gemini-cli-core';
|
|
9
|
+
import * as fs from 'node:fs/promises';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { randomUUID } from 'node:crypto';
|
|
12
|
+
describe('SessionSelector', () => {
|
|
13
|
+
let tmpDir;
|
|
14
|
+
let config;
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
// Create a temporary directory for testing
|
|
17
|
+
tmpDir = path.join(process.cwd(), '.tmp-test-sessions');
|
|
18
|
+
await fs.mkdir(tmpDir, { recursive: true });
|
|
19
|
+
// Mock config
|
|
20
|
+
config = {
|
|
21
|
+
storage: {
|
|
22
|
+
getProjectTempDir: () => tmpDir,
|
|
23
|
+
},
|
|
24
|
+
getSessionId: () => 'current-session-id',
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
afterEach(async () => {
|
|
28
|
+
// Clean up test files
|
|
29
|
+
try {
|
|
30
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
31
|
+
}
|
|
32
|
+
catch (_error) {
|
|
33
|
+
// Ignore cleanup errors
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
it('should resolve session by UUID', async () => {
|
|
37
|
+
const sessionId1 = randomUUID();
|
|
38
|
+
const sessionId2 = randomUUID();
|
|
39
|
+
// Create test session files
|
|
40
|
+
const chatsDir = path.join(tmpDir, 'chats');
|
|
41
|
+
await fs.mkdir(chatsDir, { recursive: true });
|
|
42
|
+
const session1 = {
|
|
43
|
+
sessionId: sessionId1,
|
|
44
|
+
projectHash: 'test-hash',
|
|
45
|
+
startTime: '2024-01-01T10:00:00.000Z',
|
|
46
|
+
lastUpdated: '2024-01-01T10:30:00.000Z',
|
|
47
|
+
messages: [
|
|
48
|
+
{
|
|
49
|
+
type: 'user',
|
|
50
|
+
content: 'Test message 1',
|
|
51
|
+
id: 'msg1',
|
|
52
|
+
timestamp: '2024-01-01T10:00:00.000Z',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
const session2 = {
|
|
57
|
+
sessionId: sessionId2,
|
|
58
|
+
projectHash: 'test-hash',
|
|
59
|
+
startTime: '2024-01-01T11:00:00.000Z',
|
|
60
|
+
lastUpdated: '2024-01-01T11:30:00.000Z',
|
|
61
|
+
messages: [
|
|
62
|
+
{
|
|
63
|
+
type: 'user',
|
|
64
|
+
content: 'Test message 2',
|
|
65
|
+
id: 'msg2',
|
|
66
|
+
timestamp: '2024-01-01T11:00:00.000Z',
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
await fs.writeFile(path.join(chatsDir, `${SESSION_FILE_PREFIX}2024-01-01T10-00-${sessionId1.slice(0, 8)}.json`), JSON.stringify(session1, null, 2));
|
|
71
|
+
await fs.writeFile(path.join(chatsDir, `${SESSION_FILE_PREFIX}2024-01-01T11-00-${sessionId2.slice(0, 8)}.json`), JSON.stringify(session2, null, 2));
|
|
72
|
+
const sessionSelector = new SessionSelector(config);
|
|
73
|
+
// Test resolving by UUID
|
|
74
|
+
const result1 = await sessionSelector.resolveSession(sessionId1);
|
|
75
|
+
expect(result1.sessionData.sessionId).toBe(sessionId1);
|
|
76
|
+
expect(result1.sessionData.messages[0].content).toBe('Test message 1');
|
|
77
|
+
const result2 = await sessionSelector.resolveSession(sessionId2);
|
|
78
|
+
expect(result2.sessionData.sessionId).toBe(sessionId2);
|
|
79
|
+
expect(result2.sessionData.messages[0].content).toBe('Test message 2');
|
|
80
|
+
});
|
|
81
|
+
it('should resolve session by index', async () => {
|
|
82
|
+
const sessionId1 = randomUUID();
|
|
83
|
+
const sessionId2 = randomUUID();
|
|
84
|
+
// Create test session files
|
|
85
|
+
const chatsDir = path.join(tmpDir, 'chats');
|
|
86
|
+
await fs.mkdir(chatsDir, { recursive: true });
|
|
87
|
+
const session1 = {
|
|
88
|
+
sessionId: sessionId1,
|
|
89
|
+
projectHash: 'test-hash',
|
|
90
|
+
startTime: '2024-01-01T10:00:00.000Z',
|
|
91
|
+
lastUpdated: '2024-01-01T10:30:00.000Z',
|
|
92
|
+
messages: [
|
|
93
|
+
{
|
|
94
|
+
type: 'user',
|
|
95
|
+
content: 'First session',
|
|
96
|
+
id: 'msg1',
|
|
97
|
+
timestamp: '2024-01-01T10:00:00.000Z',
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
const session2 = {
|
|
102
|
+
sessionId: sessionId2,
|
|
103
|
+
projectHash: 'test-hash',
|
|
104
|
+
startTime: '2024-01-01T11:00:00.000Z',
|
|
105
|
+
lastUpdated: '2024-01-01T11:30:00.000Z',
|
|
106
|
+
messages: [
|
|
107
|
+
{
|
|
108
|
+
type: 'user',
|
|
109
|
+
content: 'Second session',
|
|
110
|
+
id: 'msg2',
|
|
111
|
+
timestamp: '2024-01-01T11:00:00.000Z',
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
await fs.writeFile(path.join(chatsDir, `${SESSION_FILE_PREFIX}2024-01-01T10-00-${sessionId1.slice(0, 8)}.json`), JSON.stringify(session1, null, 2));
|
|
116
|
+
await fs.writeFile(path.join(chatsDir, `${SESSION_FILE_PREFIX}2024-01-01T11-00-${sessionId2.slice(0, 8)}.json`), JSON.stringify(session2, null, 2));
|
|
117
|
+
const sessionSelector = new SessionSelector(config);
|
|
118
|
+
// Test resolving by index (1-based)
|
|
119
|
+
const result1 = await sessionSelector.resolveSession('1');
|
|
120
|
+
expect(result1.sessionData.messages[0].content).toBe('First session');
|
|
121
|
+
const result2 = await sessionSelector.resolveSession('2');
|
|
122
|
+
expect(result2.sessionData.messages[0].content).toBe('Second session');
|
|
123
|
+
});
|
|
124
|
+
it('should resolve latest session', async () => {
|
|
125
|
+
const sessionId1 = randomUUID();
|
|
126
|
+
const sessionId2 = randomUUID();
|
|
127
|
+
// Create test session files
|
|
128
|
+
const chatsDir = path.join(tmpDir, 'chats');
|
|
129
|
+
await fs.mkdir(chatsDir, { recursive: true });
|
|
130
|
+
const session1 = {
|
|
131
|
+
sessionId: sessionId1,
|
|
132
|
+
projectHash: 'test-hash',
|
|
133
|
+
startTime: '2024-01-01T10:00:00.000Z',
|
|
134
|
+
lastUpdated: '2024-01-01T10:30:00.000Z',
|
|
135
|
+
messages: [
|
|
136
|
+
{
|
|
137
|
+
type: 'user',
|
|
138
|
+
content: 'First session',
|
|
139
|
+
id: 'msg1',
|
|
140
|
+
timestamp: '2024-01-01T10:00:00.000Z',
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
const session2 = {
|
|
145
|
+
sessionId: sessionId2,
|
|
146
|
+
projectHash: 'test-hash',
|
|
147
|
+
startTime: '2024-01-01T11:00:00.000Z',
|
|
148
|
+
lastUpdated: '2024-01-01T11:30:00.000Z',
|
|
149
|
+
messages: [
|
|
150
|
+
{
|
|
151
|
+
type: 'user',
|
|
152
|
+
content: 'Latest session',
|
|
153
|
+
id: 'msg2',
|
|
154
|
+
timestamp: '2024-01-01T11:00:00.000Z',
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
};
|
|
158
|
+
await fs.writeFile(path.join(chatsDir, `${SESSION_FILE_PREFIX}2024-01-01T10-00-${sessionId1.slice(0, 8)}.json`), JSON.stringify(session1, null, 2));
|
|
159
|
+
await fs.writeFile(path.join(chatsDir, `${SESSION_FILE_PREFIX}2024-01-01T11-00-${sessionId2.slice(0, 8)}.json`), JSON.stringify(session2, null, 2));
|
|
160
|
+
const sessionSelector = new SessionSelector(config);
|
|
161
|
+
// Test resolving latest
|
|
162
|
+
const result = await sessionSelector.resolveSession('latest');
|
|
163
|
+
expect(result.sessionData.messages[0].content).toBe('Latest session');
|
|
164
|
+
});
|
|
165
|
+
it('should throw error for invalid session identifier', async () => {
|
|
166
|
+
const sessionId1 = randomUUID();
|
|
167
|
+
// Create test session files
|
|
168
|
+
const chatsDir = path.join(tmpDir, 'chats');
|
|
169
|
+
await fs.mkdir(chatsDir, { recursive: true });
|
|
170
|
+
const session1 = {
|
|
171
|
+
sessionId: sessionId1,
|
|
172
|
+
projectHash: 'test-hash',
|
|
173
|
+
startTime: '2024-01-01T10:00:00.000Z',
|
|
174
|
+
lastUpdated: '2024-01-01T10:30:00.000Z',
|
|
175
|
+
messages: [
|
|
176
|
+
{
|
|
177
|
+
type: 'user',
|
|
178
|
+
content: 'Test message 1',
|
|
179
|
+
id: 'msg1',
|
|
180
|
+
timestamp: '2024-01-01T10:00:00.000Z',
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
await fs.writeFile(path.join(chatsDir, `${SESSION_FILE_PREFIX}2024-01-01T10-00-${sessionId1.slice(0, 8)}.json`), JSON.stringify(session1, null, 2));
|
|
185
|
+
const sessionSelector = new SessionSelector(config);
|
|
186
|
+
await expect(sessionSelector.resolveSession('invalid-uuid')).rejects.toThrow('Invalid session identifier "invalid-uuid"');
|
|
187
|
+
await expect(sessionSelector.resolveSession('999')).rejects.toThrow('Invalid session identifier "999"');
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
describe('extractFirstUserMessage', () => {
|
|
191
|
+
it('should extract first non-resume user message', () => {
|
|
192
|
+
const messages = [
|
|
193
|
+
{
|
|
194
|
+
type: 'user',
|
|
195
|
+
content: '/resume',
|
|
196
|
+
id: 'msg1',
|
|
197
|
+
timestamp: '2024-01-01T10:00:00.000Z',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
type: 'user',
|
|
201
|
+
content: 'Hello world',
|
|
202
|
+
id: 'msg2',
|
|
203
|
+
timestamp: '2024-01-01T10:01:00.000Z',
|
|
204
|
+
},
|
|
205
|
+
];
|
|
206
|
+
expect(extractFirstUserMessage(messages)).toBe('Hello world');
|
|
207
|
+
});
|
|
208
|
+
it('should truncate long messages', () => {
|
|
209
|
+
const longMessage = 'a'.repeat(150);
|
|
210
|
+
const messages = [
|
|
211
|
+
{
|
|
212
|
+
type: 'user',
|
|
213
|
+
content: longMessage,
|
|
214
|
+
id: 'msg1',
|
|
215
|
+
timestamp: '2024-01-01T10:00:00.000Z',
|
|
216
|
+
},
|
|
217
|
+
];
|
|
218
|
+
const result = extractFirstUserMessage(messages);
|
|
219
|
+
expect(result).toBe('a'.repeat(97) + '...');
|
|
220
|
+
expect(result.length).toBe(100);
|
|
221
|
+
});
|
|
222
|
+
it('should return "Empty conversation" for no user messages', () => {
|
|
223
|
+
const messages = [
|
|
224
|
+
{
|
|
225
|
+
type: 'gemini',
|
|
226
|
+
content: 'Hello',
|
|
227
|
+
id: 'msg1',
|
|
228
|
+
timestamp: '2024-01-01T10:00:00.000Z',
|
|
229
|
+
},
|
|
230
|
+
];
|
|
231
|
+
expect(extractFirstUserMessage(messages)).toBe('Empty conversation');
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
describe('formatRelativeTime', () => {
|
|
235
|
+
it('should format time correctly', () => {
|
|
236
|
+
const now = new Date();
|
|
237
|
+
// 5 minutes ago
|
|
238
|
+
const fiveMinutesAgo = new Date(now.getTime() - 5 * 60 * 1000);
|
|
239
|
+
expect(formatRelativeTime(fiveMinutesAgo.toISOString())).toBe('5 minutes ago');
|
|
240
|
+
// 1 minute ago
|
|
241
|
+
const oneMinuteAgo = new Date(now.getTime() - 1 * 60 * 1000);
|
|
242
|
+
expect(formatRelativeTime(oneMinuteAgo.toISOString())).toBe('1 minute ago');
|
|
243
|
+
// 2 hours ago
|
|
244
|
+
const twoHoursAgo = new Date(now.getTime() - 2 * 60 * 60 * 1000);
|
|
245
|
+
expect(formatRelativeTime(twoHoursAgo.toISOString())).toBe('2 hours ago');
|
|
246
|
+
// 1 hour ago
|
|
247
|
+
const oneHourAgo = new Date(now.getTime() - 1 * 60 * 60 * 1000);
|
|
248
|
+
expect(formatRelativeTime(oneHourAgo.toISOString())).toBe('1 hour ago');
|
|
249
|
+
// 3 days ago
|
|
250
|
+
const threeDaysAgo = new Date(now.getTime() - 3 * 24 * 60 * 60 * 1000);
|
|
251
|
+
expect(formatRelativeTime(threeDaysAgo.toISOString())).toBe('3 days ago');
|
|
252
|
+
// 1 day ago
|
|
253
|
+
const oneDayAgo = new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000);
|
|
254
|
+
expect(formatRelativeTime(oneDayAgo.toISOString())).toBe('1 day ago');
|
|
255
|
+
// Just now (within 60 seconds)
|
|
256
|
+
const thirtySecondsAgo = new Date(now.getTime() - 30 * 1000);
|
|
257
|
+
expect(formatRelativeTime(thirtySecondsAgo.toISOString())).toBe('Just now');
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
//# sourceMappingURL=sessionUtils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionUtils.test.js","sourceRoot":"","sources":["../../../src/utils/sessionUtils.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { type Config } from '@google/gemini-cli-core';
|
|
7
|
+
export declare function listSessions(config: Config): Promise<void>;
|
|
8
|
+
export declare function deleteSession(config: Config, sessionIndex: string): Promise<void>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { ChatRecordingService } from '@google/gemini-cli-core';
|
|
7
|
+
import { formatRelativeTime, SessionSelector, } from './sessionUtils.js';
|
|
8
|
+
export async function listSessions(config) {
|
|
9
|
+
const sessionSelector = new SessionSelector(config);
|
|
10
|
+
const sessions = await sessionSelector.listSessions();
|
|
11
|
+
if (sessions.length === 0) {
|
|
12
|
+
console.log('No previous sessions found for this project.');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
console.log(`\nAvailable sessions for this project (${sessions.length}):\n`);
|
|
16
|
+
sessions
|
|
17
|
+
.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime())
|
|
18
|
+
.forEach((session, index) => {
|
|
19
|
+
const current = session.isCurrentSession ? ', current' : '';
|
|
20
|
+
const time = formatRelativeTime(session.lastUpdated);
|
|
21
|
+
console.log(` ${index + 1}. ${session.firstUserMessage} (${time}${current}) [${session.id}]`);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export async function deleteSession(config, sessionIndex) {
|
|
25
|
+
const sessionSelector = new SessionSelector(config);
|
|
26
|
+
const sessions = await sessionSelector.listSessions();
|
|
27
|
+
if (sessions.length === 0) {
|
|
28
|
+
console.error('No sessions found for this project.');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// Sort sessions by start time to match list-sessions ordering
|
|
32
|
+
const sortedSessions = sessions.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime());
|
|
33
|
+
let sessionToDelete;
|
|
34
|
+
// Try to find by UUID first
|
|
35
|
+
const sessionByUuid = sortedSessions.find((session) => session.id === sessionIndex);
|
|
36
|
+
if (sessionByUuid) {
|
|
37
|
+
sessionToDelete = sessionByUuid;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
// Parse session index
|
|
41
|
+
const index = parseInt(sessionIndex, 10);
|
|
42
|
+
if (isNaN(index) || index < 1 || index > sessions.length) {
|
|
43
|
+
console.error(`Invalid session identifier "${sessionIndex}". Use --list-sessions to see available sessions.`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
sessionToDelete = sortedSessions[index - 1];
|
|
47
|
+
}
|
|
48
|
+
// Prevent deleting the current session
|
|
49
|
+
if (sessionToDelete.isCurrentSession) {
|
|
50
|
+
console.error('Cannot delete the current active session.');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
// Use ChatRecordingService to delete the session
|
|
55
|
+
const chatRecordingService = new ChatRecordingService(config);
|
|
56
|
+
chatRecordingService.deleteSession(sessionToDelete.file);
|
|
57
|
+
const time = formatRelativeTime(sessionToDelete.lastUpdated);
|
|
58
|
+
console.log(`Deleted session ${sessionToDelete.index}: ${sessionToDelete.firstUserMessage} (${time})`);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.error(`Failed to delete session: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../../src/utils/sessions.ts"],"names":[],"mappings":"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"}
|