Fix/vscode ide companion completion menu content (#1243)

* refactor(vscode-ide-companion/types): move ApprovalModeValue type to dedicated file

feat(vscode-ide-companion/file-context): improve file context handling and search

Enhance file context hook to better handle search queries and reduce redundant requests.
Track last query to optimize when to refetch full file list.
Improve logging for debugging purposes.

* feat(vscode-ide-companion/completion): enhance completion menu performance and refresh logic

Implement item comparison to prevent unnecessary re-renders when completion items
haven't actually changed. Optimize refresh logic to only trigger when workspace
files content changes. Improve completion menu stability and responsiveness.

refactor(vscode-ide-companion/handlers): remove SettingsMessageHandler and consolidate functionality

Move setApprovalMode functionality from SettingsMessageHandler to SessionMessageHandler
to reduce code duplication and simplify message handling architecture. Remove unused
settings-related imports and clean up message router configuration.

chore(vscode-ide-companion/ui): minor UI improvements and code cleanup

Consolidate imports in SessionSelector component.
Remove debug console log statement from FileMessageHandler.
Move getTimeAgo utility function to sessionGrouping file and remove obsolete timeUtils file.
Clean up completion menu CSS classes.

* fix(vscode-ide-companion): resolve all ESLint errors

Fixed unused variable errors in SessionMessageHandler.ts:
- Commented out unused conversation and messages variables

Also includes previous commits:
1. feat(vscode-ide-companion): add upgrade button to CLI version warning
2. fix(vscode-ide-companion): resolve ESLint errors in InputForm component

When the Qwen Code CLI version is below the minimum required version,
the warning message now includes an "Upgrade Now" button that opens
a terminal and runs the npm install command to upgrade the CLI.

Added tests to verify the functionality works correctly.
This commit is contained in:
易良 2025-12-13 17:37:45 +08:00 committed by GitHub
parent 693a58d517
commit 65796e2799
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 400 additions and 530 deletions

View file

@ -7,8 +7,8 @@ import { AcpConnection } from './acpConnection.js';
import type {
AcpSessionUpdate,
AcpPermissionRequest,
ApprovalModeValue,
} from '../types/acpTypes.js';
import type { ApprovalModeValue } from '../types/approvalModeValueTypes.js';
import { QwenSessionReader, type QwenSession } from './qwenSessionReader.js';
import { QwenSessionManager } from './qwenSessionManager.js';
import type {
@ -336,8 +336,10 @@ export class QwenAgentManager {
name: this.sessionReader.getSessionTitle(session),
startTime: session.startTime,
lastUpdated: session.lastUpdated,
messageCount: session.messages.length,
messageCount: session.messageCount ?? session.messages.length,
projectHash: session.projectHash,
filePath: session.filePath,
cwd: session.cwd,
}),
);
@ -452,8 +454,10 @@ export class QwenAgentManager {
name: this.sessionReader.getSessionTitle(x.raw),
startTime: x.raw.startTime,
lastUpdated: x.raw.lastUpdated,
messageCount: x.raw.messages.length,
messageCount: x.raw.messageCount ?? x.raw.messages.length,
projectHash: x.raw.projectHash,
filePath: x.raw.filePath,
cwd: x.raw.cwd,
}));
const nextCursorVal =
page.length > 0 ? page[page.length - 1].mtime : undefined;
@ -891,80 +895,6 @@ export class QwenAgentManager {
return this.saveSessionViaCommand(sessionId, tag);
}
/**
* Save session as checkpoint (using CLI format)
* Saves to ~/.qwen/tmp/{projectHash}/checkpoint-{tag}.json
* Saves two copies with sessionId and conversationId to ensure recovery via either ID
*
* @param messages - Current session messages
* @param conversationId - Conversation ID (from VSCode extension)
* @returns Save result
*/
async saveCheckpoint(
messages: ChatMessage[],
conversationId: string,
): Promise<{ success: boolean; tag?: string; message?: string }> {
try {
console.log('[QwenAgentManager] ===== CHECKPOINT SAVE START =====');
console.log('[QwenAgentManager] Conversation ID:', conversationId);
console.log('[QwenAgentManager] Message count:', messages.length);
console.log(
'[QwenAgentManager] Current working dir:',
this.currentWorkingDir,
);
console.log(
'[QwenAgentManager] Current session ID (from CLI):',
this.currentSessionId,
);
// In ACP mode, the CLI does not accept arbitrary slash commands like
// "/chat save". To ensure we never block on unsupported features,
// persist checkpoints directly to ~/.qwen/tmp using our SessionManager.
const qwenMessages = messages.map((m) => ({
// Generate minimal QwenMessage shape expected by the writer
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
timestamp: new Date().toISOString(),
type: m.role === 'user' ? ('user' as const) : ('qwen' as const),
content: m.content,
}));
const tag = await this.sessionManager.saveCheckpoint(
qwenMessages,
conversationId,
this.currentWorkingDir,
this.currentSessionId || undefined,
);
return { success: true, tag };
} catch (error) {
console.error('[QwenAgentManager] ===== CHECKPOINT SAVE FAILED =====');
console.error('[QwenAgentManager] Error:', error);
console.error(
'[QwenAgentManager] Error stack:',
error instanceof Error ? error.stack : 'N/A',
);
return {
success: false,
message: error instanceof Error ? error.message : String(error),
};
}
}
/**
* Save session directly to file system (without relying on ACP)
*
* @param messages - Current session messages
* @param sessionName - Session name
* @returns Save result
*/
async saveSessionDirect(
messages: ChatMessage[],
sessionName: string,
): Promise<{ success: boolean; sessionId?: string; message?: string }> {
// Use checkpoint format instead of session format
// This matches CLI's /chat save behavior
return this.saveCheckpoint(messages, sessionName);
}
/**
* Try to load session via ACP session/load method
* This method will only be used if CLI version supports it
@ -1152,16 +1082,6 @@ export class QwenAgentManager {
}
}
/**
* Load session, preferring ACP method if CLI version supports it
*
* @param sessionId - Session ID
* @returns Loaded session messages or null
*/
async loadSessionDirect(sessionId: string): Promise<ChatMessage[] | null> {
return this.loadSession(sessionId);
}
/**
* Create new session
*