Skip to content
Closed
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
16 changes: 14 additions & 2 deletions packages/cli/src/ui/hooks/useStatusLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,13 @@ export function useStatusLine(): {
// Initialized with current values so the state-change effect
// does not fire redundantly on mount.
const { lastPromptTokenCount } = uiState.sessionStats;
const { currentModel, branchName, activeWorktree, streamingState } = uiState;
const {
currentModel,
branchName,
activeWorktree,
streamingState,
showAutoAcceptIndicator,
} = uiState;
// Track only the slug — equality on the whole object would re-fire on
// every render because `activeWorktree` is rebuilt by AppContainer's
// useMemo each time the sidecar reloads.
Expand All @@ -288,6 +294,7 @@ export function useStatusLine(): {
totalLinesRemoved: number;
streamingState: string;
reasoningEffortKey: string;
approvalMode: string | undefined;
}>({
promptTokenCount: lastPromptTokenCount,
currentModel,
Expand All @@ -299,6 +306,7 @@ export function useStatusLine(): {
totalLinesRemoved,
streamingState,
reasoningEffortKey,
approvalMode: showAutoAcceptIndicator,
});

// Guard: when true, the mount effect has already called doUpdate so the
Expand Down Expand Up @@ -432,6 +440,7 @@ export function useStatusLine(): {
totalLinesAdded: m.files.totalLinesAdded,
totalLinesRemoved: m.files.totalLinesRemoved,
streamingState: ui.streamingState,
approvalMode: ui.showAutoAcceptIndicator,
});
setOutput(buildStatusLinePresetLines(preset, data));
return;
Expand Down Expand Up @@ -596,7 +605,8 @@ export function useStatusLine(): {
totalLinesAdded !== prev.totalLinesAdded ||
totalLinesRemoved !== prev.totalLinesRemoved ||
streamingState !== prev.streamingState ||
reasoningEffortKey !== prev.reasoningEffortKey
reasoningEffortKey !== prev.reasoningEffortKey ||
showAutoAcceptIndicator !== prev.approvalMode
) {
prev.promptTokenCount = lastPromptTokenCount;
prev.currentModel = currentModel;
Expand All @@ -608,6 +618,7 @@ export function useStatusLine(): {
prev.totalLinesRemoved = totalLinesRemoved;
prev.streamingState = streamingState;
prev.reasoningEffortKey = reasoningEffortKey;
prev.approvalMode = showAutoAcceptIndicator;
scheduleUpdate();
}
}, [
Expand All @@ -626,6 +637,7 @@ export function useStatusLine(): {
totalLinesRemoved,
streamingState,
reasoningEffortKey,
showAutoAcceptIndicator,
scheduleUpdate,
updatePullRequestNumber,
]);
Expand Down
50 changes: 50 additions & 0 deletions packages/cli/src/ui/statusLinePresets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { describe, expect, it } from 'vitest';
import { ApprovalMode } from '@qwen-code/qwen-code-core';
import { StreamingState } from './types.js';
import {
aggregateModelTokens,
Expand Down Expand Up @@ -337,4 +338,53 @@ describe('statusLinePresets', () => {
expect(inferPullRequestNumber('main')).toBeUndefined();
expect(inferPullRequestNumber(undefined)).toBeUndefined();
});

it('renders approval-mode preset item', () => {
const data = buildStatusLinePresetData({
sessionId: 'session-123',
version: '1.2.3',
modelDisplayName: 'qwen3-code-plus',
currentDir: '/repo/project',
branch: 'main',
contextWindowSize: 1000,
currentUsage: 250,
totalInputTokens: 0,
totalOutputTokens: 0,
totalLinesAdded: 0,
totalLinesRemoved: 0,
streamingState: StreamingState.Idle,
approvalMode: ApprovalMode.YOLO,
});

expect(
buildStatusLinePresetLines(
{ type: 'preset', items: ['approval-mode'] },
data,
),
).toEqual(['YOLO mode']);
});

it('omits approval-mode when mode is undefined', () => {
const data = buildStatusLinePresetData({
sessionId: 'session-123',
version: '1.2.3',
modelDisplayName: 'qwen3-code-plus',
currentDir: '/repo/project',
branch: 'main',
contextWindowSize: 1000,
currentUsage: 250,
totalInputTokens: 0,
totalOutputTokens: 0,
totalLinesAdded: 0,
totalLinesRemoved: 0,
streamingState: StreamingState.Idle,
});

expect(
buildStatusLinePresetLines(
{ type: 'preset', items: ['approval-mode', 'model'] },
data,
),
).toEqual(['qwen3-code-plus']);
});
});
33 changes: 33 additions & 0 deletions packages/cli/src/ui/statusLinePresets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

import nodePath from 'node:path';
import type { ReasoningEffort } from '@qwen-code/qwen-code-core';
import { ApprovalMode } from '@qwen-code/qwen-code-core';
import { StreamingState } from './types.js';
import { t } from '../i18n/index.js';

export const STATUS_LINE_PRESET_ITEM_IDS = [
'project-name',
Expand All @@ -21,6 +23,7 @@ export const STATUS_LINE_PRESET_ITEM_IDS = [
'branch-changes',
'context-used',
'run-state',
'approval-mode',
'qwen-version',
'context-window-size',
'used-tokens',
Expand Down Expand Up @@ -69,6 +72,7 @@ export interface StatusLinePresetData {
totalLinesAdded: number;
totalLinesRemoved: number;
streamingState: StreamingState;
approvalMode: ApprovalMode | undefined;
}

export function aggregateModelTokens(metrics: {
Expand Down Expand Up @@ -148,6 +152,11 @@ export const STATUS_LINE_PRESET_ITEMS: readonly StatusLinePresetItem[] = [
label: 'run-state',
description: 'Compact session run-state text',
},
{
id: 'approval-mode',
label: 'approval-mode',
description: 'Current approval / interaction mode',
},
{
id: 'qwen-version',
label: 'qwen-version',
Expand Down Expand Up @@ -307,6 +316,7 @@ export function buildStatusLinePresetData(params: {
totalLinesAdded: number;
totalLinesRemoved: number;
streamingState: StreamingState;
approvalMode?: ApprovalMode | undefined;
}): StatusLinePresetData {
const usedPercentage =
params.contextWindowSize > 0
Expand Down Expand Up @@ -339,9 +349,30 @@ export function buildStatusLinePresetData(params: {
totalLinesAdded: params.totalLinesAdded,
totalLinesRemoved: params.totalLinesRemoved,
streamingState: params.streamingState,
approvalMode: params.approvalMode,
};
}

function getApprovalModeLabel(
mode: ApprovalMode | undefined,
): string | undefined {
if (!mode) return undefined;
switch (mode) {
case ApprovalMode.PLAN:
return t('plan mode');
case ApprovalMode.AUTO_EDIT:
return t('auto-accept edits');
case ApprovalMode.AUTO:
return t('Auto mode');
case ApprovalMode.YOLO:
return t('YOLO mode');
case ApprovalMode.DEFAULT:
return t('Ask permissions');
default:
return undefined;
}
}

function formatPresetItem(
item: StatusLinePresetItemId,
data: StatusLinePresetData,
Expand Down Expand Up @@ -388,6 +419,8 @@ function formatPresetItem(
return undefined;
case 'run-state':
return getRunStateLabel(data.streamingState);
case 'approval-mode':
return getApprovalModeLabel(data.approvalMode);
case 'qwen-version':
return `v${data.version}`;
case 'context-window-size':
Expand Down