feat: add /chat command for saving, listing, resuming, and deleting named sessions#3105
feat: add /chat command for saving, listing, resuming, and deleting named sessions#3105lnxsun wants to merge 4 commits intoQwenLM:mainfrom
Conversation
|
我已经在本地重新构建了cli.js并逐一测试了/chat的4个子命令(save、list、resume、delete)的功能,对于一开始存在的resume不能跳转到原save的会话的问题也进行了修正和复测通过,确认都能实现预期功能,请测试并拉取 |
| * 读取索引文件 | ||
| * @returns 索引对象,如果文件不存在则返回空对象 | ||
| */ | ||
| export async function readChatIndex(): Promise<ChatIndex> { |
There was a problem hiding this comment.
[Critical] readChatIndex() currently turns every read/parse failure into {}. That means permission errors and other I/O failures are silently reported to callers as "no saved sessions" / "session not found", which makes the new /chat commands lose data visibility without any actionable error.
| export async function readChatIndex(): Promise<ChatIndex> { | |
| export async function readChatIndex(): Promise<ChatIndex> { | |
| try { | |
| const content = await fs.readFile(getIndexPath(), 'utf-8'); | |
| return JSON.parse(content) as ChatIndex; | |
| } catch (error) { | |
| if (error instanceof SyntaxError) { | |
| return {}; | |
| } | |
| if ( | |
| typeof error === 'object' && | |
| error !== null && | |
| 'code' in error && | |
| error.code === 'ENOENT' | |
| ) { | |
| return {}; | |
| } | |
| throw error; | |
| } | |
| } |
— gpt-5.4 via Qwen Code /review
| /** | ||
| * 获取索引文件路径 | ||
| */ | ||
| function getIndexPath(): string { |
There was a problem hiding this comment.
[Critical] This index is written to a single global ~/.qwen/chat-index.json, but session lookup/removal is project-scoped through SessionService(cwd). In practice that means /chat list can show names saved in other repos that /chat resume cannot load here, and the index also ignores custom runtime roots such as QWEN_RUNTIME_DIR.
| function getIndexPath(): string { | |
| function getIndexPath(): string { | |
| return path.join(new Storage(process.cwd()).getProjectDir(), 'chat-index.json'); | |
| } |
The exact storage helper may differ, but the index needs to follow the same project/runtime scoping rules as session storage.
— gpt-5.4 via Qwen Code /review
| if (!deleted) { | ||
| return { | ||
| type: 'message', | ||
| messageType: 'error', |
There was a problem hiding this comment.
[Critical] /chat delete <name> only removes the alias from chat-index.json; it never deletes the underlying session file. That makes the command behave like "unlink alias" rather than "delete session", which is surprising for users and leaves conversation data on disk.
| messageType: 'error', | |
| const sessionId = await getSessionIdByName(name); | |
| if (!sessionId) { | |
| return { | |
| type: 'message', | |
| messageType: 'error', | |
| content: t('Session "{{name}}" not found.', { name }), | |
| }; | |
| } | |
| const config = context.services.config; | |
| if (!config) { | |
| return { | |
| type: 'message', | |
| messageType: 'error', | |
| content: t('Config not loaded.'), | |
| }; | |
| } | |
| const sessionService = new SessionService(config.getTargetDir()); | |
| const removed = await sessionService.removeSession(sessionId); | |
| if (!removed) { | |
| return { | |
| type: 'message', | |
| messageType: 'error', | |
| content: t('Session "{{name}}" not found.', { name }), | |
| }; | |
| } | |
| await deleteSessionFromIndex(name); |
— gpt-5.4 via Qwen Code /review
| 'test-session-1', | ||
| ); | ||
|
|
||
| expect(result).toEqual({ |
There was a problem hiding this comment.
[Critical] This test is still asserting the old resume behavior, but the implementation now returns a dialog: 'resume' action and requires a valid CommandContext. The focused test run is currently red because this case calls resumeCommand.action(undefined as any, ...) and then expects an info message containing Found session.
| expect(result).toEqual({ | |
| const mockContext: CommandContext = { | |
| services: { | |
| config: { | |
| getTargetDir: () => '/tmp/project', | |
| }, | |
| }, | |
| ui: {} as never, | |
| executionMode: 'non_interactive', | |
| }; | |
| const result = await resumeCommand?.action!(mockContext, 'test-session-1'); | |
| expect(result).toEqual({ | |
| type: 'dialog', | |
| dialog: 'resume', | |
| params: { sessionId: 'test-session-id-12345' }, | |
| }); |
— gpt-5.4 via Qwen Code /review
目标:
我想给qwen code开发一个/chat命令,用其来保存、查看、恢复和删除会话。
实现的
/chat子命令:**/chat save [name]- 给当前会话命名保存/chat list- 列出已命名保存的会话/chat resume [name]- 按名字恢复会话/chat delete [name]- 删除已命名的会话Related to iflow cli的好功能接过来 #3025