Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions docs/users/features/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,16 +212,17 @@ this setting.

Commands for obtaining information and performing system settings.

| Command | Description | Usage Examples |
| ----------- | ----------------------------------------------- | -------------------------------- |
| `/help` | Display help information for available commands | `/help` or `/?` |
| `/about` | Display version information | `/about` |
| `/stats` | Display detailed statistics for current session | `/stats` |
| `/settings` | Open settings editor | `/settings` |
| `/auth` | Change authentication method | `/auth` |
| `/bug` | Submit issue about Qwen Code | `/bug Button click unresponsive` |
| `/copy` | Copy last output content to clipboard | `/copy` |
| `/quit` | Exit Qwen Code immediately | `/quit` or `/exit` |
| Command | Description | Usage Examples |
| --------------- | ----------------------------------------------- | -------------------------------- |
| `/help` | Display help information for available commands | `/help` or `/?` |
| `/status` | Display version information | `/status` or `/about` |
| `/status paths` | Display current session file and log paths | `/status paths` |
| `/stats` | Display detailed statistics for current session | `/stats` |
| `/settings` | Open settings editor | `/settings` |
| `/auth` | Change authentication method | `/auth` |
| `/bug` | Submit issue about Qwen Code | `/bug Button click unresponsive` |
| `/copy` | Copy last output content to clipboard | `/copy` |
| `/quit` | Exit Qwen Code immediately | `/quit` or `/exit` |

### 1.9 Common Shortcuts

Expand Down
7 changes: 7 additions & 0 deletions packages/cli/src/gemini.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
type MockInstance,
} from 'vitest';
import {
createNonInteractivePromptId,
main,
setupUnhandledRejectionHandler,
validateDnsResolutionOrder,
Expand Down Expand Up @@ -309,6 +310,12 @@ describe('gemini.tsx main function', () => {
);
});

it('creates non-interactive prompt ids that preserve session correlation', () => {
expect(createNonInteractivePromptId('test-session-id')).toBe(
'test-session-id########0',
);
});

const runSandboxRelaunch = async (
argv: string[],
sessionId = '123e4567-e89b-12d3-a456-426614174000',
Expand Down
6 changes: 5 additions & 1 deletion packages/cli/src/gemini.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -891,7 +891,7 @@ export async function main() {
settings,
);

const prompt_id = Math.random().toString(16).slice(2);
const prompt_id = createNonInteractivePromptId(config.getSessionId());

if (inputFormat === InputFormat.STREAM_JSON) {
const trimmedInput = (input ?? '').trim();
Expand Down Expand Up @@ -948,6 +948,10 @@ export async function main() {
}
}

export function createNonInteractivePromptId(sessionId: string): string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] createNonInteractivePromptId is exported but always returns the same deterministic string ({sessionId}########0) for every call within a session. The function name implies uniqueness, and downstream log-analysis tools that group by prompt_id will merge distinct non-interactive requests into one.

Consider either renaming to createSessionCorrelationId to set correct expectations, or appending a monotonic counter:

Suggested change
export function createNonInteractivePromptId(sessionId: string): string {
let nonInteractivePromptCounter = 0;
export function createNonInteractivePromptId(sessionId: string): string {
return `${sessionId}########${nonInteractivePromptCounter++}`;
}

— mimo-v2.5-pro via Qwen Code /review

return `${sessionId}########0`;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The ######## delimiter is hardcoded here but defined as MAIN_SESSION_PROMPT_ID_DELIMITER (unexported) in openaiLogger.ts. If the delimiter changes in core, this CLI code will silently produce IDs that sessionIdFromPromptId cannot parse, breaking OpenAI log session correlation.

Consider exporting the constant from core and importing it here:

import { MAIN_SESSION_PROMPT_ID_DELIMITER } from '@qwen-code/qwen-code-core';

— mimo-v2.5-pro via Qwen Code /review

}

function setWindowTitle(title: string, settings: LoadedSettings) {
if (!settings.merged.ui?.hideWindowTitle) {
const windowTitle = computeWindowTitle(title);
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/i18n/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ export default {
'docs/keyboard-shortcuts.md': 'docs/keyboard-shortcuts.md',
'for help on Qwen Code': 'for help on Qwen Code',
'show version info': 'show version info',
'show paths for current session files and logs':
'show paths for current session files and logs',
'submit a bug report': 'submit a bug report',
Status: 'Status',

Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/i18n/locales/zh-TW.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export default {
'docs/keyboard-shortcuts.md': 'docs/keyboard-shortcuts.md',
'for help on Qwen Code': '獲取 Qwen Code 幫助',
'show version info': '顯示版本信息',
'show paths for current session files and logs': '顯示目前會話檔案和日誌路徑',
'submit a bug report': '提交錯誤報告',
Status: '狀態',
'Qwen Code': 'Qwen Code',
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/i18n/locales/zh.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export default {
'docs/keyboard-shortcuts.md': 'docs/keyboard-shortcuts.md',
'for help on Qwen Code': '获取 Qwen Code 帮助',
'show version info': '显示版本信息',
'show paths for current session files and logs': '显示当前会话文件和日志路径',
'submit a bug report': '提交错误报告',
Status: '状态',

Expand Down
56 changes: 56 additions & 0 deletions packages/cli/src/ui/commands/aboutCommand.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import { type CommandContext } from './types.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
import { MessageType } from '../types.js';
import * as systemInfoUtils from '../../utils/systemInfo.js';
import * as sessionPathsUtils from '../../utils/sessionPaths.js';

vi.mock('../../utils/systemInfo.js');
vi.mock('../../utils/sessionPaths.js');

describe('aboutCommand', () => {
let mockContext: CommandContext;
Expand Down Expand Up @@ -55,6 +57,12 @@ describe('aboutCommand', () => {
memoryUsage: '100 MB',
baseUrl: undefined,
});
vi.mocked(sessionPathsUtils.collectSessionPathInfo).mockResolvedValue({
sections: [],
});
vi.mocked(sessionPathsUtils.formatSessionPathInfo).mockReturnValue(
'Session files:\n Session ID: test-session-id',
);
});

afterEach(() => {
Expand Down Expand Up @@ -281,6 +289,31 @@ describe('aboutCommand', () => {
);
});

it('paths subcommand should return current session file paths', async () => {
const pathsSubCommand = aboutCommand.subCommands?.find(
(sc) => sc.name === 'paths',
);
if (!pathsSubCommand?.action) {
throw new Error('The paths subcommand must have an action.');
}

const result = (await pathsSubCommand.action(mockContext, '')) as {
type: string;
messageType: string;
content: string;
};

expect(sessionPathsUtils.collectSessionPathInfo).toHaveBeenCalledWith(
mockContext,
);
expect(result).toEqual({
type: 'message',
messageType: 'info',
content: 'Session files:\n Session ID: test-session-id',
});
expect(mockContext.ui.addItem).not.toHaveBeenCalled();
});

describe('non-interactive mode', () => {
it('should return text summary without calling addItem', async () => {
if (!aboutCommand.action) {
Expand Down Expand Up @@ -341,6 +374,29 @@ describe('aboutCommand', () => {
expect(result.content).toContain('vscode');
});

it('paths subcommand should return text without calling addItem', async () => {
const pathsSubCommand = aboutCommand.subCommands?.find(
(sc) => sc.name === 'paths',
);
if (!pathsSubCommand?.action) {
throw new Error('The paths subcommand must have an action.');
}

const nonInteractiveContext = createMockCommandContext({
executionMode: 'non_interactive',
} as unknown as Partial<CommandContext>);
nonInteractiveContext.ui.addItem = vi.fn();

const result = await pathsSubCommand.action(nonInteractiveContext, '');

expect(result).toEqual({
type: 'message',
messageType: 'info',
content: 'Session files:\n Session ID: test-session-id',
});
expect(nonInteractiveContext.ui.addItem).not.toHaveBeenCalled();
});

it('should include LSP status when available', async () => {
if (!aboutCommand.action) throw new Error('No action');

Expand Down
23 changes: 23 additions & 0 deletions packages/cli/src/ui/commands/aboutCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import { CommandKind } from './types.js';
import { MessageType, type HistoryItemAbout } from '../types.js';
import { getExtendedSystemInfo } from '../../utils/systemInfo.js';
import { t } from '../../i18n/index.js';
import {
collectSessionPathInfo,
formatSessionPathInfo,
} from '../../utils/sessionPaths.js';

export const aboutCommand: SlashCommand = {
name: 'status',
Expand Down Expand Up @@ -51,4 +55,23 @@ export const aboutCommand: SlashCommand = {
context.ui.addItem(aboutItem, Date.now());
return;
},
subCommands: [
{
name: 'paths',
get description() {
return t('show paths for current session files and logs');
},
kind: CommandKind.BUILT_IN,
supportedModes: ['interactive', 'non_interactive', 'acp'] as const,
action: async (context) => {
const info = await collectSessionPathInfo(context);
const content = formatSessionPathInfo(info);
return {
type: 'message' as const,
messageType: 'info' as const,
content,
};
},
},
],
};
Loading
Loading