Merge remote-tracking branch 'origin/main' into feat/review-skill-improvements

This commit is contained in:
wenshao 2026-04-08 23:09:01 +08:00
commit 3364cf880f
72 changed files with 2442 additions and 432 deletions

View file

@ -1069,6 +1069,7 @@ export async function loadCliConfig(
telemetry: telemetrySettings,
usageStatisticsEnabled: settings.privacy?.usageStatisticsEnabled ?? true,
fileFiltering: settings.context?.fileFiltering,
thinkingIdleThresholdMinutes: settings.context?.gapThresholdMinutes,
checkpointing:
argv.checkpointing || settings.general?.checkpointing?.enabled,
proxy:

View file

@ -518,7 +518,7 @@ const SETTINGS_SCHEMA = {
label: 'Enable Follow-up Suggestions',
category: 'UI',
requiresRestart: false,
default: true,
default: false,
description:
'Show context-aware follow-up suggestions after task completion. Press Tab or Right Arrow to accept, Enter to accept and submit.',
showInDialog: true,
@ -924,6 +924,16 @@ const SETTINGS_SCHEMA = {
},
},
},
gapThresholdMinutes: {
type: 'number',
label: 'Thinking Block Idle Threshold (minutes)',
category: 'Context',
requiresRestart: false,
default: 5,
description:
'Minutes of inactivity after which retained thinking blocks are cleared to free context tokens. Aligns with provider prompt-cache TTL.',
showInDialog: false,
},
},
},

View file

@ -52,6 +52,18 @@ export function generateCodingPlanTemplate(
// China region uses legacy fields to maintain backward compatibility
// This ensures existing users don't get prompted for unnecessary updates
return [
{
id: 'qwen3.6-plus',
name: '[ModelStudio Coding Plan] qwen3.6-plus',
baseUrl: 'https://coding.dashscope.aliyuncs.com/v1',
envKey: CODING_PLAN_ENV_KEY,
generationConfig: {
extra_body: {
enable_thinking: true,
},
contextWindowSize: 1000000,
},
},
{
id: 'qwen3.5-plus',
name: '[ModelStudio Coding Plan] qwen3.5-plus',
@ -147,6 +159,18 @@ export function generateCodingPlanTemplate(
// Global region uses ModelStudio Coding Plan branding for Global/Intl
return [
{
id: 'qwen3.6-plus',
name: '[ModelStudio Coding Plan for Global/Intl] qwen3.6-plus',
baseUrl: 'https://coding-intl.dashscope.aliyuncs.com/v1',
envKey: CODING_PLAN_ENV_KEY,
generationConfig: {
extra_body: {
enable_thinking: true,
},
contextWindowSize: 1000000,
},
},
{
id: 'qwen3.5-plus',
name: '[ModelStudio Coding Plan for Global/Intl] qwen3.5-plus',

View file

@ -1973,4 +1973,15 @@ export default {
'Vollständige Tool-Ausgabe und Denkprozess im ausführlichen Modus anzeigen (mit Strg+O umschalten).',
'Press Ctrl+O to show full tool output':
'Strg+O für vollständige Tool-Ausgabe drücken',
'Switch to plan mode or exit plan mode':
'Switch to plan mode or exit plan mode',
'Exited plan mode. Previous approval mode restored.':
'Exited plan mode. Previous approval mode restored.',
'Enabled plan mode. The agent will analyze and plan without executing tools.':
'Enabled plan mode. The agent will analyze and plan without executing tools.',
'Already in plan mode. Use "/plan exit" to exit plan mode.':
'Already in plan mode. Use "/plan exit" to exit plan mode.',
'Not in plan mode. Use "/plan" to enter plan mode first.':
'Not in plan mode. Use "/plan" to enter plan mode first.',
};

View file

@ -2013,4 +2013,15 @@ export default {
'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).',
'Press Ctrl+O to show full tool output':
'Press Ctrl+O to show full tool output',
'Switch to plan mode or exit plan mode':
'Switch to plan mode or exit plan mode',
'Exited plan mode. Previous approval mode restored.':
'Exited plan mode. Previous approval mode restored.',
'Enabled plan mode. The agent will analyze and plan without executing tools.':
'Enabled plan mode. The agent will analyze and plan without executing tools.',
'Already in plan mode. Use "/plan exit" to exit plan mode.':
'Already in plan mode. Use "/plan exit" to exit plan mode.',
'Not in plan mode. Use "/plan" to enter plan mode first.':
'Not in plan mode. Use "/plan" to enter plan mode first.',
};

View file

@ -1464,4 +1464,15 @@ export default {
'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).':
'詳細モードで完全なツール出力と思考を表示しますCtrl+O で切り替え)。',
'Press Ctrl+O to show full tool output': 'Ctrl+O で完全なツール出力を表示',
'Switch to plan mode or exit plan mode':
'Switch to plan mode or exit plan mode',
'Exited plan mode. Previous approval mode restored.':
'Exited plan mode. Previous approval mode restored.',
'Enabled plan mode. The agent will analyze and plan without executing tools.':
'Enabled plan mode. The agent will analyze and plan without executing tools.',
'Already in plan mode. Use "/plan exit" to exit plan mode.':
'Already in plan mode. Use "/plan exit" to exit plan mode.',
'Not in plan mode. Use "/plan" to enter plan mode first.':
'Not in plan mode. Use "/plan" to enter plan mode first.',
};

View file

@ -1963,4 +1963,15 @@ export default {
'Mostrar saída completa da ferramenta e raciocínio no modo detalhado (alternar com Ctrl+O).',
'Press Ctrl+O to show full tool output':
'Pressione Ctrl+O para exibir a saída completa da ferramenta',
'Switch to plan mode or exit plan mode':
'Switch to plan mode or exit plan mode',
'Exited plan mode. Previous approval mode restored.':
'Exited plan mode. Previous approval mode restored.',
'Enabled plan mode. The agent will analyze and plan without executing tools.':
'Enabled plan mode. The agent will analyze and plan without executing tools.',
'Already in plan mode. Use "/plan exit" to exit plan mode.':
'Already in plan mode. Use "/plan exit" to exit plan mode.',
'Not in plan mode. Use "/plan" to enter plan mode first.':
'Not in plan mode. Use "/plan" to enter plan mode first.',
};

View file

@ -1970,4 +1970,15 @@ export default {
'Показывать полный вывод инструментов и процесс рассуждений в подробном режиме (переключить с помощью Ctrl+O).',
'Press Ctrl+O to show full tool output':
'Нажмите Ctrl+O для показа полного вывода инструментов',
'Switch to plan mode or exit plan mode':
'Switch to plan mode or exit plan mode',
'Exited plan mode. Previous approval mode restored.':
'Exited plan mode. Previous approval mode restored.',
'Enabled plan mode. The agent will analyze and plan without executing tools.':
'Enabled plan mode. The agent will analyze and plan without executing tools.',
'Already in plan mode. Use "/plan exit" to exit plan mode.':
'Already in plan mode. Use "/plan exit" to exit plan mode.',
'Not in plan mode. Use "/plan" to enter plan mode first.':
'Not in plan mode. Use "/plan" to enter plan mode first.',
};

View file

@ -1817,4 +1817,14 @@ export default {
'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).':
'详细模式下显示完整工具输出和思考过程Ctrl+O 切换)。',
'Press Ctrl+O to show full tool output': '按 Ctrl+O 查看详细工具调用结果',
'Switch to plan mode or exit plan mode': '切换到计划模式或退出计划模式',
'Exited plan mode. Previous approval mode restored.':
'已退出计划模式,已恢复之前的审批模式。',
'Enabled plan mode. The agent will analyze and plan without executing tools.':
'启用计划模式。智能体将只分析和规划,而不执行工具。',
'Already in plan mode. Use "/plan exit" to exit plan mode.':
'已处于计划模式。使用 "/plan exit" 退出计划模式。',
'Not in plan mode. Use "/plan" to enter plan mode first.':
'未处于计划模式。请先使用 "/plan" 进入计划模式。',
};

View file

@ -32,6 +32,7 @@ import { languageCommand } from '../ui/commands/languageCommand.js';
import { mcpCommand } from '../ui/commands/mcpCommand.js';
import { memoryCommand } from '../ui/commands/memoryCommand.js';
import { modelCommand } from '../ui/commands/modelCommand.js';
import { planCommand } from '../ui/commands/planCommand.js';
import { permissionsCommand } from '../ui/commands/permissionsCommand.js';
import { trustCommand } from '../ui/commands/trustCommand.js';
import { quitCommand } from '../ui/commands/quitCommand.js';
@ -103,6 +104,7 @@ export class BuiltinCommandLoader implements ICommandLoader {
mcpCommand,
memoryCommand,
modelCommand,
planCommand,
permissionsCommand,
...(this.config?.getFolderTrust() ? [trustCommand] : []),
quitCommand,

View file

@ -1113,7 +1113,7 @@ export const AppContainer = (props: AppContainerProps) => {
// Generate prompt suggestions when streaming completes
const followupSuggestionsEnabled =
settings.merged.ui?.enableFollowupSuggestions !== false;
settings.merged.ui?.enableFollowupSuggestions === true;
useEffect(() => {
// Clear suggestion when feature is disabled at runtime

View file

@ -0,0 +1,159 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import { planCommand } from './planCommand.js';
import { type CommandContext } from './types.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
import { ApprovalMode } from '@qwen-code/qwen-code-core';
describe('planCommand', () => {
let mockContext: CommandContext;
beforeEach(() => {
mockContext = createMockCommandContext({
services: {
config: {
getApprovalMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
getPrePlanMode: vi.fn().mockReturnValue(ApprovalMode.DEFAULT),
setApprovalMode: vi.fn(),
} as unknown as import('@qwen-code/qwen-code-core').Config,
},
});
});
it('should switch to plan mode if not in plan mode', async () => {
if (!planCommand.action) {
throw new Error('The plan command must have an action.');
}
const result = await planCommand.action(mockContext, '');
expect(mockContext.services.config?.setApprovalMode).toHaveBeenCalledWith(
ApprovalMode.PLAN,
);
expect(result).toEqual({
type: 'message',
messageType: 'info',
content:
'Enabled plan mode. The agent will analyze and plan without executing tools.',
});
});
it('should return submit prompt if arguments are provided when switching to plan mode', async () => {
if (!planCommand.action) {
throw new Error('The plan command must have an action.');
}
const result = await planCommand.action(mockContext, 'refactor the code');
expect(mockContext.services.config?.setApprovalMode).toHaveBeenCalledWith(
ApprovalMode.PLAN,
);
expect(result).toEqual({
type: 'submit_prompt',
content: [{ text: 'refactor the code' }],
});
});
it('should return already in plan mode if mode is already plan', async () => {
if (!planCommand.action) {
throw new Error('The plan command must have an action.');
}
(mockContext.services.config?.getApprovalMode as Mock).mockReturnValue(
ApprovalMode.PLAN,
);
const result = await planCommand.action(mockContext, '');
expect(mockContext.services.config?.setApprovalMode).not.toHaveBeenCalled();
expect(result).toEqual({
type: 'message',
messageType: 'info',
content: 'Already in plan mode. Use "/plan exit" to exit plan mode.',
});
});
it('should return submit prompt if arguments are provided and already in plan mode', async () => {
if (!planCommand.action) {
throw new Error('The plan command must have an action.');
}
(mockContext.services.config?.getApprovalMode as Mock).mockReturnValue(
ApprovalMode.PLAN,
);
const result = await planCommand.action(mockContext, 'keep planning');
expect(mockContext.services.config?.setApprovalMode).not.toHaveBeenCalled();
expect(result).toEqual({
type: 'submit_prompt',
content: [{ text: 'keep planning' }],
});
});
it('should exit plan mode when exit argument is passed', async () => {
if (!planCommand.action) {
throw new Error('The plan command must have an action.');
}
(mockContext.services.config?.getApprovalMode as Mock).mockReturnValue(
ApprovalMode.PLAN,
);
const result = await planCommand.action(mockContext, 'exit');
expect(mockContext.services.config?.setApprovalMode).toHaveBeenCalledWith(
ApprovalMode.DEFAULT,
);
expect(result).toEqual({
type: 'message',
messageType: 'info',
content: 'Exited plan mode. Previous approval mode restored.',
});
});
it('should restore pre-plan mode when executing from plan mode', async () => {
if (!planCommand.action) {
throw new Error('The plan command must have an action.');
}
(mockContext.services.config?.getApprovalMode as Mock).mockReturnValue(
ApprovalMode.PLAN,
);
(mockContext.services.config?.getPrePlanMode as Mock).mockReturnValue(
ApprovalMode.AUTO_EDIT,
);
const result = await planCommand.action(mockContext, 'exit');
expect(mockContext.services.config?.setApprovalMode).toHaveBeenCalledWith(
ApprovalMode.AUTO_EDIT,
);
expect(result).toEqual({
type: 'message',
messageType: 'info',
content: 'Exited plan mode. Previous approval mode restored.',
});
});
it('should return error when execute is used but not in plan mode', async () => {
if (!planCommand.action) {
throw new Error('The plan command must have an action.');
}
// Default mock returns ApprovalMode.DEFAULT (not PLAN)
const result = await planCommand.action(mockContext, 'exit');
expect(mockContext.services.config?.setApprovalMode).not.toHaveBeenCalled();
expect(result).toEqual({
type: 'message',
messageType: 'error',
content: 'Not in plan mode. Use "/plan" to enter plan mode first.',
});
});
});

View file

@ -0,0 +1,104 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import {
type CommandContext,
CommandKind,
type SlashCommand,
type MessageActionReturn,
type SubmitPromptActionReturn,
} from './types.js';
import { t } from '../../i18n/index.js';
import { ApprovalMode } from '@qwen-code/qwen-code-core';
export const planCommand: SlashCommand = {
name: 'plan',
get description() {
return t('Switch to plan mode or exit plan mode');
},
kind: CommandKind.BUILT_IN,
action: async (
context: CommandContext,
args: string,
): Promise<MessageActionReturn | SubmitPromptActionReturn> => {
const { config } = context.services;
if (!config) {
return {
type: 'message',
messageType: 'error',
content: t('Configuration is not available.'),
};
}
const trimmedArgs = args.trim();
const currentMode = config.getApprovalMode();
if (trimmedArgs === 'exit') {
if (currentMode !== ApprovalMode.PLAN) {
return {
type: 'message',
messageType: 'error',
content: t('Not in plan mode. Use "/plan" to enter plan mode first.'),
};
}
try {
config.setApprovalMode(config.getPrePlanMode());
} catch (e) {
return {
type: 'message',
messageType: 'error',
content: (e as Error).message,
};
}
return {
type: 'message',
messageType: 'info',
content: t('Exited plan mode. Previous approval mode restored.'),
};
}
if (currentMode !== ApprovalMode.PLAN) {
try {
config.setApprovalMode(ApprovalMode.PLAN);
} catch (e) {
return {
type: 'message',
messageType: 'error',
content: (e as Error).message,
};
}
if (trimmedArgs) {
return {
type: 'submit_prompt',
content: [{ text: trimmedArgs }],
};
}
return {
type: 'message',
messageType: 'info',
content: t(
'Enabled plan mode. The agent will analyze and plan without executing tools.',
),
};
}
// Already in plan mode
if (trimmedArgs) {
return {
type: 'submit_prompt',
content: [{ text: trimmedArgs }],
};
}
return {
type: 'message',
messageType: 'info',
content: t('Already in plan mode. Use "/plan exit" to exit plan mode.'),
};
},
};

View file

@ -849,7 +849,6 @@ describe('InputPrompt', () => {
// Verify useCompletion was called with correct signature
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -878,7 +877,6 @@ describe('InputPrompt', () => {
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -907,7 +905,6 @@ describe('InputPrompt', () => {
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -936,7 +933,6 @@ describe('InputPrompt', () => {
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -965,7 +961,6 @@ describe('InputPrompt', () => {
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -995,7 +990,6 @@ describe('InputPrompt', () => {
// Verify useCompletion was called with the buffer
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -1024,7 +1018,6 @@ describe('InputPrompt', () => {
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -1054,7 +1047,6 @@ describe('InputPrompt', () => {
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -1084,7 +1076,6 @@ describe('InputPrompt', () => {
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -1114,7 +1105,6 @@ describe('InputPrompt', () => {
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -1144,7 +1134,6 @@ describe('InputPrompt', () => {
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -1176,7 +1165,6 @@ describe('InputPrompt', () => {
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -1206,7 +1194,6 @@ describe('InputPrompt', () => {
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,
@ -1238,7 +1225,6 @@ describe('InputPrompt', () => {
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
mockBuffer,
['/test/project/src'],
path.join('test', 'project', 'src'),
mockSlashCommands,
mockCommandContext,

View file

@ -168,15 +168,6 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
}
}, []);
const [dirs, setDirs] = useState<readonly string[]>(
config.getWorkspaceContext().getDirectories(),
);
const dirsChanged = config.getWorkspaceContext().getDirectories();
useEffect(() => {
if (dirs.length !== dirsChanged.length) {
setDirs(dirsChanged);
}
}, [dirs.length, dirsChanged]);
const [reverseSearchActive, setReverseSearchActive] = useState(false);
const [commandSearchActive, setCommandSearchActive] = useState(false);
const [textBeforeReverseSearch, setTextBeforeReverseSearch] = useState('');
@ -190,7 +181,6 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
const completion = useCommandCompletion(
buffer,
dirs,
config.getTargetDir(),
slashCommands,
commandContext,

View file

@ -189,13 +189,35 @@ export function KeypressProvider({
clearKittyTimeout();
kittySequenceTimeout = setTimeout(() => {
if (kittySequenceBufferRef.current) {
if (debugKeystrokeLogging) {
debugLogger.debug(
'[DEBUG] Kitty buffer timeout, clearing:',
kittySequenceBufferRef.current,
);
// Before discarding, try to salvage any parseable sequences
// that may have been missed (e.g., due to chunked input).
while (kittySequenceBufferRef.current) {
const parsed = parseKittyPrefix(kittySequenceBufferRef.current);
if (parsed) {
kittySequenceBufferRef.current =
kittySequenceBufferRef.current.slice(parsed.length);
broadcast(parsed.key);
continue;
}
const plain = parsePlainTextPrefix(kittySequenceBufferRef.current);
if (plain) {
kittySequenceBufferRef.current =
kittySequenceBufferRef.current.slice(plain.length);
broadcast(plain.key);
continue;
}
break;
}
// Clear any remaining unparseable content
if (kittySequenceBufferRef.current) {
if (debugKeystrokeLogging) {
debugLogger.debug(
'[DEBUG] Kitty buffer timeout, clearing:',
kittySequenceBufferRef.current,
);
}
kittySequenceBufferRef.current = '';
}
kittySequenceBufferRef.current = '';
}
}, KITTY_SEQUENCE_TIMEOUT_MS);
};
@ -331,14 +353,19 @@ export function KeypressProvider({
};
}
// 3) CSI-u form: ESC [ <code> ; <mods> (u|~)
// 3) CSI-u and tilde-coded functional keys: ESC [ <code> ; <mods> (u|~)
// 3) CSI-u form: ESC [ <code>[:<shifted>][:<base>] ; <mods>[:<event>] [; <text>] (u|~)
// 3) CSI-u and tilde-coded functional keys with optional kitty extensions:
// Full kitty format: ESC [ code:shifted:base ; mods:event ; text u
// 'u' terminator: Kitty CSI-u; '~' terminator: tilde-coded function keys.
const csiUPrefix = new RegExp(`^${ESC}\\[(\\d+)(;(\\d+))?([u~])`);
// The colon-separated fields (shifted key, base key, event type, text)
// are optional extensions that some terminals send.
const csiUPrefix = new RegExp(
`^${ESC}\\[(\\d+)(?::\\d+)*(?:;(\\d+)(?::\\d+)*)?(?:;\\d+)?([u~])`,
);
m = buffer.match(csiUPrefix);
if (m) {
const keyCode = parseInt(m[1], 10);
let modifiers = m[3] ? parseInt(m[3], 10) : KITTY_MODIFIER_BASE;
let modifiers = m[2] ? parseInt(m[2], 10) : KITTY_MODIFIER_BASE;
if (modifiers >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
modifiers -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
}
@ -347,7 +374,7 @@ export function KeypressProvider({
(modifierBits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
const alt = (modifierBits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
const ctrl = (modifierBits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
const terminator = m[4];
const terminator = m[3];
// Tilde-coded functional keys (Delete, Insert, PageUp/Down, Home/End)
if (terminator === '~') {

View file

@ -391,9 +391,9 @@ describe('useCodingPlanUpdates', () => {
>;
// Should have new China configs + custom config only (global config removed since regions are mutually exclusive)
// The China template has 8 models, so we expect 8 (from template) + 1 (custom) = 9
// The China template has 9 models, so we expect 9 (from template) + 1 (custom) = 10
// Note: description field has been removed, only name field contains the branding
expect(updatedConfigs.length).toBe(9);
expect(updatedConfigs.length).toBe(10);
// Should NOT contain the Global config (mutually exclusive)
expect(

View file

@ -84,7 +84,6 @@ const setupMocks = ({
describe('useCommandCompletion', () => {
const mockCommandContext = {} as CommandContext;
const mockConfig = {} as Config;
const testDirs: string[] = [];
const testRootDir = '/';
// Helper to create real TextBuffer objects within renderHook
@ -114,7 +113,6 @@ describe('useCommandCompletion', () => {
const { result } = renderHook(() =>
useCommandCompletion(
useTextBufferForTest(''),
testDirs,
testRootDir,
[],
mockCommandContext,
@ -139,7 +137,6 @@ describe('useCommandCompletion', () => {
const textBuffer = useTextBufferForTest('@file');
const completion = useCommandCompletion(
textBuffer,
testDirs,
testRootDir,
[],
mockCommandContext,
@ -172,7 +169,6 @@ describe('useCommandCompletion', () => {
const { result } = renderHook(() =>
useCommandCompletion(
useTextBufferForTest('@files'),
testDirs,
testRootDir,
[],
mockCommandContext,
@ -200,7 +196,6 @@ describe('useCommandCompletion', () => {
renderHook(() =>
useCommandCompletion(
useTextBufferForTest(text),
testDirs,
testRootDir,
[],
mockCommandContext,
@ -226,7 +221,6 @@ describe('useCommandCompletion', () => {
renderHook(() =>
useCommandCompletion(
useTextBufferForTest(text, cursorOffset),
testDirs,
testRootDir,
[],
mockCommandContext,
@ -265,7 +259,6 @@ describe('useCommandCompletion', () => {
const { result } = renderHook(() =>
useCommandCompletion(
useTextBufferForTest('/'),
testDirs,
testRootDir,
[],
mockCommandContext,
@ -286,7 +279,6 @@ describe('useCommandCompletion', () => {
const { result } = renderHook(() =>
useCommandCompletion(
useTextBufferForTest('/'),
testDirs,
testRootDir,
[],
mockCommandContext,
@ -306,7 +298,6 @@ describe('useCommandCompletion', () => {
const { result } = renderHook(() =>
useCommandCompletion(
useTextBufferForTest('/'),
testDirs,
testRootDir,
[],
mockCommandContext,
@ -332,7 +323,6 @@ describe('useCommandCompletion', () => {
const { result } = renderHook(() =>
useCommandCompletion(
useTextBufferForTest('/'),
testDirs,
testRootDir,
[],
mockCommandContext,
@ -361,7 +351,6 @@ describe('useCommandCompletion', () => {
const { result } = renderHook(() =>
useCommandCompletion(
useTextBufferForTest('/'),
testDirs,
testRootDir,
[],
mockCommandContext,
@ -398,7 +387,6 @@ describe('useCommandCompletion', () => {
const { result } = renderHook(() =>
useCommandCompletion(
useTextBufferForTest('/'),
testDirs,
testRootDir,
[],
mockCommandContext,
@ -427,7 +415,6 @@ describe('useCommandCompletion', () => {
renderHook(() =>
useCommandCompletion(
useTextBufferForTest(text),
testDirs,
testRootDir,
[],
mockCommandContext,
@ -455,7 +442,6 @@ describe('useCommandCompletion', () => {
renderHook(() =>
useCommandCompletion(
useTextBufferForTest(text),
testDirs,
testRootDir,
[],
mockCommandContext,
@ -484,7 +470,6 @@ describe('useCommandCompletion', () => {
const textBuffer = useTextBufferForTest(text);
const completion = useCommandCompletion(
textBuffer,
testDirs,
testRootDir,
[],
mockCommandContext,
@ -517,7 +502,6 @@ describe('useCommandCompletion', () => {
const textBuffer = useTextBufferForTest('/mem');
const completion = useCommandCompletion(
textBuffer,
testDirs,
testRootDir,
[],
mockCommandContext,
@ -547,7 +531,6 @@ describe('useCommandCompletion', () => {
const textBuffer = useTextBufferForTest('@src/fi');
const completion = useCommandCompletion(
textBuffer,
testDirs,
testRootDir,
[],
mockCommandContext,
@ -580,7 +563,6 @@ describe('useCommandCompletion', () => {
const textBuffer = useTextBufferForTest(text, cursorOffset);
const completion = useCommandCompletion(
textBuffer,
testDirs,
testRootDir,
[],
mockCommandContext,

View file

@ -39,7 +39,6 @@ export interface UseCommandCompletionReturn {
export function useCommandCompletion(
buffer: TextBuffer,
dirs: readonly string[],
cwd: string,
slashCommands: readonly SlashCommand[],
commandContext: CommandContext,