mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-21 18:44:30 -07:00
feat: rename /directory to /workspace and unify terminology
Renames the /directory command to /workspace, adds aliases for backward compatibility, and updates UI strings, settings, and prompts to use 'workspace' terminology consistently. Linked to #20737. Per Core/UX chat on 2/18.
This commit is contained in:
@@ -41,7 +41,7 @@ async function addMcpServer(
|
||||
const settings = loadSettings(process.cwd());
|
||||
const inHome = settings.workspace.path === settings.user.path;
|
||||
|
||||
if (scope === 'project' && inHome) {
|
||||
if ((scope === 'workspace' || scope === 'project') && inHome) {
|
||||
debugLogger.error(
|
||||
'Error: Please use --scope user to edit settings in the home directory.',
|
||||
);
|
||||
@@ -159,10 +159,10 @@ export const addCommand: CommandModule = {
|
||||
})
|
||||
.option('scope', {
|
||||
alias: 's',
|
||||
describe: 'Configuration scope (user or project)',
|
||||
describe: 'Configuration scope (user or workspace)',
|
||||
type: 'string',
|
||||
default: 'project',
|
||||
choices: ['user', 'project'],
|
||||
default: 'workspace',
|
||||
choices: ['user', 'workspace', 'project'],
|
||||
})
|
||||
.option('transport', {
|
||||
alias: ['t', 'type'],
|
||||
|
||||
@@ -99,7 +99,7 @@ describe('mcp remove command', () => {
|
||||
|
||||
expect(mockSetValue).not.toHaveBeenCalled();
|
||||
expect(debugLogSpy).toHaveBeenCalledWith(
|
||||
'Server "non-existent-server" not found in project settings.',
|
||||
'Server "non-existent-server" not found in workspace settings.',
|
||||
);
|
||||
debugLogSpy.mockRestore();
|
||||
});
|
||||
@@ -159,7 +159,7 @@ describe('mcp remove command', () => {
|
||||
expect(updatedContent).not.toContain('"server-to-remove"');
|
||||
|
||||
expect(debugLogSpy).toHaveBeenCalledWith(
|
||||
'Server "server-to-remove" removed from project settings.',
|
||||
'Server "server-to-remove" removed from workspace settings.',
|
||||
);
|
||||
|
||||
debugLogSpy.mockRestore();
|
||||
|
||||
@@ -49,10 +49,10 @@ export const removeCommand: CommandModule = {
|
||||
})
|
||||
.option('scope', {
|
||||
alias: 's',
|
||||
describe: 'Configuration scope (user or project)',
|
||||
describe: 'Configuration scope (user or workspace)',
|
||||
type: 'string',
|
||||
default: 'project',
|
||||
choices: ['user', 'project'],
|
||||
default: 'workspace',
|
||||
choices: ['user', 'workspace', 'project'],
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
|
||||
@@ -242,7 +242,7 @@ export async function parseArguments(
|
||||
.option('list-sessions', {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'List available sessions for the current project and exit.',
|
||||
'List available sessions for the current workspace and exit.',
|
||||
})
|
||||
.option('delete-session', {
|
||||
type: 'string',
|
||||
|
||||
@@ -573,12 +573,11 @@ const SETTINGS_SCHEMA = {
|
||||
properties: {
|
||||
hideCWD: {
|
||||
type: 'boolean',
|
||||
label: 'Hide CWD',
|
||||
label: 'Hide Workspace Path',
|
||||
category: 'UI',
|
||||
requiresRestart: false,
|
||||
default: false,
|
||||
description:
|
||||
'Hide the current working directory path in the footer.',
|
||||
description: 'Hide the workspace path in the footer.',
|
||||
showInDialog: true,
|
||||
},
|
||||
hideSandboxStatus: {
|
||||
|
||||
@@ -1646,7 +1646,7 @@ describe('runNonInteractive', () => {
|
||||
startTime: new Date().toISOString(),
|
||||
lastUpdated: new Date().toISOString(),
|
||||
firstUserMessage: 'Previous message',
|
||||
projectHash: 'test-hash',
|
||||
workspaceHash: 'test-hash',
|
||||
},
|
||||
filePath: '/path/to/session.json',
|
||||
};
|
||||
|
||||
@@ -28,7 +28,7 @@ import { compressCommand } from '../ui/commands/compressCommand.js';
|
||||
import { copyCommand } from '../ui/commands/copyCommand.js';
|
||||
import { corgiCommand } from '../ui/commands/corgiCommand.js';
|
||||
import { docsCommand } from '../ui/commands/docsCommand.js';
|
||||
import { directoryCommand } from '../ui/commands/directoryCommand.js';
|
||||
import { workspaceCommand } from '../ui/commands/workspaceCommand.js';
|
||||
import { editorCommand } from '../ui/commands/editorCommand.js';
|
||||
import { extensionsCommand } from '../ui/commands/extensionsCommand.js';
|
||||
import { helpCommand } from '../ui/commands/helpCommand.js';
|
||||
@@ -95,7 +95,7 @@ export class BuiltinCommandLoader implements ICommandLoader {
|
||||
copyCommand,
|
||||
corgiCommand,
|
||||
docsCommand,
|
||||
directoryCommand,
|
||||
workspaceCommand,
|
||||
editorCommand,
|
||||
...(this.config?.getExtensionsEnabled() === false
|
||||
? [
|
||||
|
||||
@@ -55,7 +55,7 @@ const TomlCommandDefSchema = z.object({
|
||||
|
||||
/**
|
||||
* Discovers and loads custom slash commands from .toml files in both the
|
||||
* user's global config directory and the current project's directory.
|
||||
* user's global config directory and the current workspace's directory.
|
||||
*
|
||||
* This loader is responsible for:
|
||||
* - Recursively scanning command directories.
|
||||
@@ -64,22 +64,22 @@ const TomlCommandDefSchema = z.object({
|
||||
* - Handling file system errors and malformed files gracefully.
|
||||
*/
|
||||
export class FileCommandLoader implements ICommandLoader {
|
||||
private readonly projectRoot: string;
|
||||
private readonly workspaceRoot: string;
|
||||
private readonly folderTrustEnabled: boolean;
|
||||
private readonly isTrustedFolder: boolean;
|
||||
|
||||
constructor(private readonly config: Config | null) {
|
||||
this.folderTrustEnabled = !!config?.getFolderTrust();
|
||||
this.isTrustedFolder = !!config?.isTrustedFolder();
|
||||
this.projectRoot = config?.getProjectRoot() || process.cwd();
|
||||
this.workspaceRoot = config?.getWorkspaceRoot() || process.cwd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all commands from user, project, and extension directories.
|
||||
* Returns commands in order: user → project → extensions (alphabetically).
|
||||
* Loads all commands from user, workspace, and extension directories.
|
||||
* Returns commands in order: user → workspace → extensions (alphabetically).
|
||||
*
|
||||
* Order is important for conflict resolution in CommandService:
|
||||
* - User/project commands (without extensionName) use "last wins" strategy
|
||||
* - User/workspace commands (without extensionName) use "last wins" strategy
|
||||
* - Extension commands (with extensionName) get renamed if conflicts exist
|
||||
*
|
||||
* @param signal An AbortSignal to cancel the loading process.
|
||||
@@ -148,13 +148,13 @@ export class FileCommandLoader implements ICommandLoader {
|
||||
private getCommandDirectories(): CommandDirectory[] {
|
||||
const dirs: CommandDirectory[] = [];
|
||||
|
||||
const storage = this.config?.storage ?? new Storage(this.projectRoot);
|
||||
const storage = this.config?.storage ?? new Storage(this.workspaceRoot);
|
||||
|
||||
// 1. User commands
|
||||
dirs.push({ path: Storage.getUserCommandsDir() });
|
||||
|
||||
// 2. Project commands (override user commands)
|
||||
dirs.push({ path: storage.getProjectCommandsDir() });
|
||||
// 2. Workspace commands (override user commands)
|
||||
dirs.push({ path: storage.getWorkspaceCommandsDir() });
|
||||
|
||||
// 3. Extension commands (processed last to detect all conflicts)
|
||||
if (this.config) {
|
||||
|
||||
@@ -1094,7 +1094,7 @@ describe('AppContainer State Management', () => {
|
||||
const mockResumedSessionData = {
|
||||
conversation: {
|
||||
sessionId: 'test-session-123',
|
||||
projectHash: 'test-project-hash',
|
||||
workspaceHash: 'test-workspace-hash',
|
||||
startTime: '2024-01-01T00:00:00Z',
|
||||
lastUpdated: '2024-01-01T00:00:01Z',
|
||||
messages: [
|
||||
@@ -1283,7 +1283,7 @@ describe('AppContainer State Management', () => {
|
||||
const resumedData = {
|
||||
conversation: {
|
||||
sessionId: 'resumed-session-456',
|
||||
projectHash: 'project-hash',
|
||||
workspaceHash: 'workspace-hash',
|
||||
startTime: '2024-01-01T00:00:00Z',
|
||||
lastUpdated: '2024-01-01T00:01:00Z',
|
||||
messages: [
|
||||
@@ -1339,7 +1339,7 @@ describe('AppContainer State Management', () => {
|
||||
const resumedData = {
|
||||
conversation: {
|
||||
sessionId: 'test-session',
|
||||
projectHash: 'project-hash',
|
||||
workspaceHash: 'workspace-hash',
|
||||
startTime: '2024-01-01T00:00:00Z',
|
||||
lastUpdated: '2024-01-01T00:01:00Z',
|
||||
messages: [],
|
||||
|
||||
@@ -222,7 +222,7 @@ export function AuthDialog({
|
||||
</Text>
|
||||
<Box marginTop={1}>
|
||||
<Text color={theme.text.primary}>
|
||||
How would you like to authenticate for this project?
|
||||
How would you like to authenticate for this workspace?
|
||||
</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
|
||||
@@ -5,7 +5,7 @@ exports[`AuthDialog > Snapshots > renders correctly with auth error 1`] = `
|
||||
│ │
|
||||
│ ? Get started │
|
||||
│ │
|
||||
│ How would you like to authenticate for this project? │
|
||||
│ How would you like to authenticate for this workspace? │
|
||||
│ │
|
||||
│ (selected) Login with Google(not selected) Use Gemini API Key(not selected) Vertex AI │
|
||||
│ │
|
||||
@@ -26,7 +26,7 @@ exports[`AuthDialog > Snapshots > renders correctly with default props 1`] = `
|
||||
│ │
|
||||
│ ? Get started │
|
||||
│ │
|
||||
│ How would you like to authenticate for this project? │
|
||||
│ How would you like to authenticate for this workspace? │
|
||||
│ │
|
||||
│ (selected) Login with Google(not selected) Use Gemini API Key(not selected) Vertex AI │
|
||||
│ │
|
||||
@@ -45,7 +45,7 @@ exports[`AuthDialog > Snapshots > renders correctly with enforced auth type 1`]
|
||||
│ │
|
||||
│ ? Get started │
|
||||
│ │
|
||||
│ How would you like to authenticate for this project? │
|
||||
│ How would you like to authenticate for this workspace? │
|
||||
│ │
|
||||
│ (selected) Use Gemini API Key │
|
||||
│ │
|
||||
|
||||
@@ -78,7 +78,7 @@ describe('initCommand', () => {
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: 'info',
|
||||
text: 'Empty GEMINI.md created. Now analyzing the project to populate it.',
|
||||
text: 'Empty GEMINI.md created. Now analyzing the workspace to populate it.',
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@ import { performInit } from '@google/gemini-cli-core';
|
||||
|
||||
export const initCommand: SlashCommand = {
|
||||
name: 'init',
|
||||
description: 'Analyzes the project and creates a tailored GEMINI.md file',
|
||||
description: 'Analyzes the workspace and creates a tailored GEMINI.md file',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
action: async (
|
||||
@@ -42,7 +42,7 @@ export const initCommand: SlashCommand = {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: 'info',
|
||||
text: 'Empty GEMINI.md created. Now analyzing the project to populate it.',
|
||||
text: 'Empty GEMINI.md created. Now analyzing the workspace to populate it.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
|
||||
+5
-5
@@ -6,7 +6,7 @@
|
||||
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import type { Mock } from 'vitest';
|
||||
import { directoryCommand } from './directoryCommand.js';
|
||||
import { workspaceCommand } from './workspaceCommand.js';
|
||||
import {
|
||||
expandHomeDir,
|
||||
getDirectorySuggestions,
|
||||
@@ -38,14 +38,14 @@ vi.mock('../utils/directoryUtils.js', async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
describe('directoryCommand', () => {
|
||||
describe('workspaceCommand', () => {
|
||||
let mockContext: CommandContext;
|
||||
let mockConfig: Config;
|
||||
let mockWorkspaceContext: WorkspaceContext;
|
||||
const addCommand = directoryCommand.subCommands?.find(
|
||||
const addCommand = workspaceCommand.subCommands?.find(
|
||||
(c) => c.name === 'add',
|
||||
);
|
||||
const showCommand = directoryCommand.subCommands?.find(
|
||||
const showCommand = workspaceCommand.subCommands?.find(
|
||||
(c) => c.name === 'show',
|
||||
);
|
||||
|
||||
@@ -126,7 +126,7 @@ describe('directoryCommand', () => {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content:
|
||||
'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.',
|
||||
'The /workspace add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.',
|
||||
});
|
||||
});
|
||||
|
||||
+4
-4
@@ -77,9 +77,9 @@ async function finishAddingDirectories(
|
||||
}
|
||||
}
|
||||
|
||||
export const directoryCommand: SlashCommand = {
|
||||
name: 'directory',
|
||||
altNames: ['dir'],
|
||||
export const workspaceCommand: SlashCommand = {
|
||||
name: 'workspace',
|
||||
altNames: ['directory', 'dir'],
|
||||
description: 'Manage workspace directories',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
subCommands: [
|
||||
@@ -156,7 +156,7 @@ export const directoryCommand: SlashCommand = {
|
||||
type: 'message' as const,
|
||||
messageType: 'error' as const,
|
||||
content:
|
||||
'The /directory add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.',
|
||||
'The /workspace add command is not supported in restrictive sandbox profiles. Please use --include-directories when starting the session instead.',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,7 +72,8 @@ export const NewAgentsNotification = ({
|
||||
New Agents Discovered
|
||||
</Text>
|
||||
<Text color={theme.text.primary}>
|
||||
The following agents were found in this project. Please review them:
|
||||
The following agents were found in this workspace. Please review
|
||||
them:
|
||||
</Text>
|
||||
<Box
|
||||
flexDirection="column"
|
||||
|
||||
@@ -55,7 +55,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
|
||||
const createConversation = (messages: MessageRecord[]): ConversationRecord => ({
|
||||
sessionId: 'test-session',
|
||||
projectHash: 'hash',
|
||||
workspaceHash: 'hash',
|
||||
startTime: new Date().toISOString(),
|
||||
lastUpdated: new Date().toISOString(),
|
||||
messages,
|
||||
|
||||
@@ -4,7 +4,7 @@ exports[`NewAgentsNotification > renders agent list 1`] = `
|
||||
" ╭────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ New Agents Discovered │
|
||||
│ The following agents were found in this project. Please review them: │
|
||||
│ The following agents were found in this workspace. Please review them: │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
@@ -24,7 +24,7 @@ exports[`NewAgentsNotification > truncates list if more than 5 agents 1`] = `
|
||||
" ╭────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ New Agents Discovered │
|
||||
│ The following agents were found in this project. Please review them: │
|
||||
│ The following agents were found in this workspace. Please review them: │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
|
||||
@@ -172,12 +172,12 @@ export const TriageIssues = ({
|
||||
async (issue: Issue): Promise<AnalysisResult> => {
|
||||
const client = config.getBaseLlmClient();
|
||||
const prompt = `
|
||||
I am triaging GitHub issues for the Gemini CLI project. I need to identify issues that should be closed because they are:
|
||||
I am triaging GitHub issues for the Gemini CLI workspace. I need to identify issues that should be closed because they are:
|
||||
- Bogus (not a real issue/request)
|
||||
- Not reproducible (insufficient info, "it doesn't work" without logs/details)
|
||||
- Abusive or offensive
|
||||
- Gibberish (nonsense text)
|
||||
- Clearly out of scope for this project
|
||||
- Clearly out of scope for this workspace
|
||||
- Non-deterministic model output (e.g., "it gave me a wrong answer once", complaints about model quality without a reproducible test case)
|
||||
|
||||
<issue>
|
||||
|
||||
@@ -19,7 +19,7 @@ export const INFORMATIVE_TIPS = [
|
||||
'Hide the startup banner for a cleaner launch (/settings)…',
|
||||
'Hide the context summary above the input (/settings)…',
|
||||
'Reclaim vertical space by hiding the footer (/settings)…',
|
||||
'Hide individual footer elements like CWD or sandbox status (/settings)…',
|
||||
'Hide individual footer elements like Workspace Path or sandbox status (/settings)…',
|
||||
'Hide the context window percentage in the footer (/settings)…',
|
||||
'Show memory usage for performance monitoring (/settings)…',
|
||||
'Show line numbers in the chat for easier reference (/settings)…',
|
||||
@@ -33,7 +33,7 @@ export const INFORMATIVE_TIPS = [
|
||||
'Control when chat history gets compressed based on token usage (settings.json)…',
|
||||
'Define custom context file names, like CONTEXT.md (settings.json)…',
|
||||
'Set max directories to scan for context files (/settings)…',
|
||||
'Expand your workspace with additional directories (/directory)…',
|
||||
'Expand your workspace with additional directories (/workspace)…',
|
||||
'Control how /memory refresh loads context files (/settings)…',
|
||||
'Toggle respect for .gitignore files in context (/settings)…',
|
||||
'Toggle respect for .geminiignore files in context (/settings)…',
|
||||
@@ -131,15 +131,15 @@ export const INFORMATIVE_TIPS = [
|
||||
'Save tokens by summarizing the context with /compress…',
|
||||
'Copy the last response to your clipboard with /copy…',
|
||||
'Open the full documentation in your browser with /docs…',
|
||||
'Add directories to your workspace with /directory add <path>…',
|
||||
'Show all directories in your workspace with /directory show…',
|
||||
'Use /dir as a shortcut for /directory…',
|
||||
'Add directories to your workspace with /workspace add <path>…',
|
||||
'Show all directories in your workspace with /workspace show…',
|
||||
'Use /dir or /directory as a shortcut for /workspace…',
|
||||
'Set your preferred external editor with /editor…',
|
||||
'List all active extensions with /extensions list…',
|
||||
'Update all or specific extensions with /extensions update…',
|
||||
'Get help on commands with /help…',
|
||||
'Manage IDE integration with /ide…',
|
||||
'Create a project-specific GEMINI.md file with /init…',
|
||||
'Create a workspace-specific GEMINI.md file with /init…',
|
||||
'List configured MCP servers and tools with /mcp list…',
|
||||
'Authenticate with an OAuth-enabled MCP server with /mcp auth…',
|
||||
'Restart MCP servers with /mcp refresh…',
|
||||
@@ -149,7 +149,7 @@ export const INFORMATIVE_TIPS = [
|
||||
'List the paths of the GEMINI.md files in use with /memory list…',
|
||||
'Choose your Gemini model with /model…',
|
||||
'Display the privacy notice with /privacy…',
|
||||
'Restore project files to a previous state with /restore…',
|
||||
'Restore workspace files to a previous state with /restore…',
|
||||
'Exit the CLI with /quit or /exit…',
|
||||
'Check model-specific usage stats with /stats model…',
|
||||
'Check tool-specific usage stats with /stats tools…',
|
||||
|
||||
@@ -154,7 +154,7 @@ describe('useFolderTrust', () => {
|
||||
renderHook(() => useFolderTrust(mockSettings, onTrustChange, addItem));
|
||||
expect(addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
text: 'This folder is untrusted, project settings, hooks, MCPs, and GEMINI.md files will not be applied for this folder.\nUse the `/permissions` command to change the trust level.',
|
||||
text: 'This folder is untrusted, workspace settings, hooks, MCPs, and GEMINI.md files will not be applied for this folder.\nUse the `/permissions` command to change the trust level.',
|
||||
type: 'info',
|
||||
},
|
||||
expect.any(Number),
|
||||
|
||||
@@ -59,7 +59,7 @@ export const useFolderTrust = (
|
||||
addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'This folder is untrusted, project settings, hooks, MCPs, and GEMINI.md files will not be applied for this folder.\nUse the `/permissions` command to change the trust level.',
|
||||
text: 'This folder is untrusted, workspace settings, hooks, MCPs, and GEMINI.md files will not be applied for this folder.\nUse the `/permissions` command to change the trust level.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
|
||||
@@ -38,7 +38,7 @@ describe('useRewindLogic', () => {
|
||||
|
||||
const mockConversation: ConversationRecord = {
|
||||
sessionId: 'conv-1',
|
||||
projectHash: 'hash-1',
|
||||
workspaceHash: 'hash-1',
|
||||
startTime: new Date(1000).toISOString(),
|
||||
lastUpdated: new Date(1001).toISOString(),
|
||||
messages: [mockUserMessage, mockModelMessage],
|
||||
|
||||
@@ -78,7 +78,7 @@ describe('useSessionResume', () => {
|
||||
const resumedData: ResumedSessionData = {
|
||||
conversation: {
|
||||
sessionId: 'test-123',
|
||||
projectHash: 'project-123',
|
||||
workspaceHash: 'project-123',
|
||||
startTime: '2025-01-01T00:00:00Z',
|
||||
lastUpdated: '2025-01-01T01:00:00Z',
|
||||
messages: [] as MessageRecord[],
|
||||
@@ -133,7 +133,7 @@ describe('useSessionResume', () => {
|
||||
const resumedData: ResumedSessionData = {
|
||||
conversation: {
|
||||
sessionId: 'test-123',
|
||||
projectHash: 'project-123',
|
||||
workspaceHash: 'project-123',
|
||||
startTime: '2025-01-01T00:00:00Z',
|
||||
lastUpdated: '2025-01-01T01:00:00Z',
|
||||
messages: [] as MessageRecord[],
|
||||
@@ -160,7 +160,7 @@ describe('useSessionResume', () => {
|
||||
const resumedData: ResumedSessionData = {
|
||||
conversation: {
|
||||
sessionId: 'test-123',
|
||||
projectHash: 'project-123',
|
||||
workspaceHash: 'project-123',
|
||||
startTime: '2025-01-01T00:00:00Z',
|
||||
lastUpdated: '2025-01-01T01:00:00Z',
|
||||
messages: [] as MessageRecord[],
|
||||
@@ -200,7 +200,7 @@ describe('useSessionResume', () => {
|
||||
const resumedData: ResumedSessionData = {
|
||||
conversation: {
|
||||
sessionId: 'test-123',
|
||||
projectHash: 'project-123',
|
||||
workspaceHash: 'project-123',
|
||||
startTime: '2025-01-01T00:00:00Z',
|
||||
lastUpdated: '2025-01-01T01:00:00Z',
|
||||
messages: [] as MessageRecord[],
|
||||
@@ -240,7 +240,7 @@ describe('useSessionResume', () => {
|
||||
const resumedData: ResumedSessionData = {
|
||||
conversation: {
|
||||
sessionId: 'test-123',
|
||||
projectHash: 'project-123',
|
||||
workspaceHash: 'project-123',
|
||||
startTime: '2025-01-01T00:00:00Z',
|
||||
lastUpdated: '2025-01-01T01:00:00Z',
|
||||
messages: [] as MessageRecord[],
|
||||
@@ -306,7 +306,7 @@ describe('useSessionResume', () => {
|
||||
it('should not resume when user is authenticating', () => {
|
||||
const conversation: ConversationRecord = {
|
||||
sessionId: 'auto-resume-123',
|
||||
projectHash: 'project-123',
|
||||
workspaceHash: 'project-123',
|
||||
startTime: '2025-01-01T00:00:00Z',
|
||||
lastUpdated: '2025-01-01T01:00:00Z',
|
||||
messages: [
|
||||
@@ -338,7 +338,7 @@ describe('useSessionResume', () => {
|
||||
it('should not resume when Gemini client is not initialized', () => {
|
||||
const conversation: ConversationRecord = {
|
||||
sessionId: 'auto-resume-123',
|
||||
projectHash: 'project-123',
|
||||
workspaceHash: 'project-123',
|
||||
startTime: '2025-01-01T00:00:00Z',
|
||||
lastUpdated: '2025-01-01T01:00:00Z',
|
||||
messages: [
|
||||
@@ -370,7 +370,7 @@ describe('useSessionResume', () => {
|
||||
it('should automatically resume session when resumedSessionData is provided', async () => {
|
||||
const conversation: ConversationRecord = {
|
||||
sessionId: 'auto-resume-123',
|
||||
projectHash: 'project-123',
|
||||
workspaceHash: 'project-123',
|
||||
startTime: '2025-01-01T00:00:00Z',
|
||||
lastUpdated: '2025-01-01T01:00:00Z',
|
||||
messages: [
|
||||
@@ -425,7 +425,7 @@ describe('useSessionResume', () => {
|
||||
it('should only resume once even if props change', async () => {
|
||||
const conversation: ConversationRecord = {
|
||||
sessionId: 'auto-resume-123',
|
||||
projectHash: 'project-123',
|
||||
workspaceHash: 'project-123',
|
||||
startTime: '2025-01-01T00:00:00Z',
|
||||
lastUpdated: '2025-01-01T01:00:00Z',
|
||||
messages: [
|
||||
@@ -480,7 +480,7 @@ describe('useSessionResume', () => {
|
||||
it('should convert session messages correctly during auto-resume', async () => {
|
||||
const conversation: ConversationRecord = {
|
||||
sessionId: 'auto-resume-with-tools',
|
||||
projectHash: 'project-123',
|
||||
workspaceHash: 'project-123',
|
||||
startTime: '2025-01-01T00:00:00Z',
|
||||
lastUpdated: '2025-01-01T01:00:00Z',
|
||||
messages: [
|
||||
|
||||
@@ -245,22 +245,22 @@ const saveFileWithXclip = async (tempFilePath: string) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the directory where clipboard images should be stored for a specific project.
|
||||
* Gets the directory where clipboard images should be stored for a specific workspace.
|
||||
*
|
||||
* This uses the global temporary directory but creates a project-specific subdirectory
|
||||
* based on the hash of the project path (via `Storage.getProjectTempDir()`).
|
||||
* This prevents path conflicts between different projects while keeping the images
|
||||
* outside of the user's project directory.
|
||||
* This uses the global temporary directory but creates a workspace-specific subdirectory
|
||||
* based on the hash of the workspace path (via `Storage.getProjectTempDir()`).
|
||||
* This prevents path conflicts between different workspaces while keeping the images
|
||||
* outside of the user's workspace directory.
|
||||
*
|
||||
* @param targetDir The root directory of the current project.
|
||||
* @param targetDir The root directory of the current workspace.
|
||||
* @returns The absolute path to the images directory.
|
||||
*/
|
||||
async function getProjectClipboardImagesDir(
|
||||
async function getWorkspaceClipboardImagesDir(
|
||||
targetDir: string,
|
||||
): Promise<string> {
|
||||
const storage = new Storage(targetDir);
|
||||
await storage.initialize();
|
||||
const baseDir = storage.getProjectTempDir();
|
||||
const baseDir = storage.getWorkspaceTempDir();
|
||||
return path.join(baseDir, 'images');
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ export async function saveClipboardImage(
|
||||
targetDir: string,
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
const tempDir = await getProjectClipboardImagesDir(targetDir);
|
||||
const tempDir = await getWorkspaceClipboardImagesDir(targetDir);
|
||||
await fs.mkdir(tempDir, { recursive: true });
|
||||
|
||||
// Generate a unique filename with timestamp
|
||||
@@ -398,7 +398,7 @@ export async function cleanupOldClipboardImages(
|
||||
targetDir: string,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const tempDir = await getProjectClipboardImagesDir(targetDir);
|
||||
const tempDir = await getWorkspaceClipboardImagesDir(targetDir);
|
||||
const files = await fs.readdir(tempDir);
|
||||
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ export class SessionError extends Error {
|
||||
static noSessionsFound(): SessionError {
|
||||
return new SessionError(
|
||||
'NO_SESSIONS_FOUND',
|
||||
'No previous sessions found for this project.',
|
||||
'No previous sessions found for this workspace.',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ export class SessionSelector {
|
||||
const sessions = await this.listSessions();
|
||||
|
||||
if (sessions.length === 0) {
|
||||
throw new Error('No previous sessions found for this project.');
|
||||
throw new Error('No previous sessions found for this workspace.');
|
||||
}
|
||||
|
||||
// Sort by startTime (oldest first, so newest sessions get highest numbers)
|
||||
|
||||
@@ -73,7 +73,7 @@ describe('listSessions', () => {
|
||||
// Assert
|
||||
expect(mockListSessions).toHaveBeenCalledOnce();
|
||||
expect(mocks.writeToStdout).toHaveBeenCalledWith(
|
||||
'No previous sessions found for this project.',
|
||||
'No previous sessions found for this workspace.',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -132,7 +132,7 @@ describe('listSessions', () => {
|
||||
|
||||
// Check that the header was displayed
|
||||
expect(mocks.writeToStdout).toHaveBeenCalledWith(
|
||||
'\nAvailable sessions for this project (3):\n',
|
||||
'\nAvailable sessions for this workspace (3):\n',
|
||||
);
|
||||
|
||||
// Check that each session was logged
|
||||
@@ -286,7 +286,7 @@ describe('listSessions', () => {
|
||||
|
||||
// Assert
|
||||
expect(mocks.writeToStdout).toHaveBeenCalledWith(
|
||||
'\nAvailable sessions for this project (1):\n',
|
||||
'\nAvailable sessions for this workspace (1):\n',
|
||||
);
|
||||
expect(mocks.writeToStdout).toHaveBeenCalledWith(
|
||||
expect.stringContaining('1. Only session'),
|
||||
@@ -380,7 +380,7 @@ describe('deleteSession', () => {
|
||||
// Assert
|
||||
expect(mockListSessions).toHaveBeenCalledOnce();
|
||||
expect(mocks.writeToStderr).toHaveBeenCalledWith(
|
||||
'No sessions found for this project.',
|
||||
'No sessions found for this workspace.',
|
||||
);
|
||||
expect(mockDeleteSession).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -25,12 +25,12 @@ export async function listSessions(config: Config): Promise<void> {
|
||||
const sessions = await sessionSelector.listSessions();
|
||||
|
||||
if (sessions.length === 0) {
|
||||
writeToStdout('No previous sessions found for this project.');
|
||||
writeToStdout('No previous sessions found for this workspace.');
|
||||
return;
|
||||
}
|
||||
|
||||
writeToStdout(
|
||||
`\nAvailable sessions for this project (${sessions.length}):\n`,
|
||||
`\nAvailable sessions for this workspace (${sessions.length}):\n`,
|
||||
);
|
||||
|
||||
sessions
|
||||
@@ -59,7 +59,7 @@ export async function deleteSession(
|
||||
const sessions = await sessionSelector.listSessions();
|
||||
|
||||
if (sessions.length === 0) {
|
||||
writeToStderr('No sessions found for this project.');
|
||||
writeToStderr('No sessions found for this workspace.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -971,7 +971,7 @@ export class Session {
|
||||
resolvedSuccessfully = true;
|
||||
} else {
|
||||
this.debug(
|
||||
`Path ${pathName} is outside the project directory. Skipping.`,
|
||||
`Path ${pathName} is outside the workspace directory. Skipping.`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user