mirror of
https://github.com/wu736139669/hapi.git
synced 2026-11-08 01:43:07 +00:00
feat(cli): wire Cursor /summarize and /clear slash builtins (#747)
* feat(cursor): wire /summarize and /clear slash builtins for remote sessions Seed cursor builtins for web autocomplete, parse summarize/clear in cursorRemoteLauncher (pass-through to agent -p; reject /clear with args). Fixes tiann/hapi#738 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cursor): isolate slash commands before message queue batching Parse summarize/clear at enqueue time (runCursor) with pushIsolateAndClear so waitForMessagesAndGetAsString never merges a slash with the next prompt. Adds queue policy tests for invalid /clear + following message. Addresses PR #747 review. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cursor): preserve pending messages when isolating slash commands pushIsolateAndClear() wipes the entire queue, so a normal prompt queued before /summarize or /clear would be silently dropped. Add pushIsolated() - isolation without clearing - and route Cursor slash commands through it instead. Adds queue tests covering the preserve-then-isolate path. Addresses PR #747 review. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { MessageQueue2 } from '@/utils/MessageQueue2';
|
||||
import { parseCursorSpecialCommand } from './cursorSpecialCommands';
|
||||
import { enqueueCursorUserMessage } from './cursorUserMessageQueue';
|
||||
import type { EnhancedMode } from './loop';
|
||||
|
||||
const mode: EnhancedMode = { permissionMode: 'default' };
|
||||
|
||||
describe('enqueueCursorUserMessage', () => {
|
||||
it('does not batch invalid /clear with a following prompt', async () => {
|
||||
const queue = new MessageQueue2<EnhancedMode>((m) => m.permissionMode);
|
||||
enqueueCursorUserMessage(queue, '/clear now', mode, 'a');
|
||||
enqueueCursorUserMessage(queue, 'continue work', mode, 'b');
|
||||
|
||||
const first = await queue.waitForMessagesAndGetAsString();
|
||||
expect(first?.message).toBe('/clear now');
|
||||
expect(parseCursorSpecialCommand(first!.message).type).toBe('invalid');
|
||||
|
||||
const second = await queue.waitForMessagesAndGetAsString();
|
||||
expect(second?.message).toBe('continue work');
|
||||
});
|
||||
|
||||
it('isolates /summarize from a following same-mode prompt', async () => {
|
||||
const queue = new MessageQueue2<EnhancedMode>((m) => m.permissionMode);
|
||||
enqueueCursorUserMessage(queue, '/summarize keep recap', mode, 'a');
|
||||
enqueueCursorUserMessage(queue, 'next task', mode, 'b');
|
||||
|
||||
const first = await queue.waitForMessagesAndGetAsString();
|
||||
expect(first?.message).toBe('/summarize keep recap');
|
||||
expect(parseCursorSpecialCommand(first!.message).type).toBe('summarize');
|
||||
|
||||
const second = await queue.waitForMessagesAndGetAsString();
|
||||
expect(second?.message).toBe('next task');
|
||||
});
|
||||
|
||||
it('preserves a normal prompt queued before a slash command', async () => {
|
||||
const queue = new MessageQueue2<EnhancedMode>((m) => m.permissionMode);
|
||||
enqueueCursorUserMessage(queue, 'first work', mode, 'a');
|
||||
enqueueCursorUserMessage(queue, '/summarize', mode, 'b');
|
||||
enqueueCursorUserMessage(queue, 'after summarize', mode, 'c');
|
||||
|
||||
const first = await queue.waitForMessagesAndGetAsString();
|
||||
expect(first?.message).toBe('first work');
|
||||
expect(first?.isolate).toBe(false);
|
||||
|
||||
const second = await queue.waitForMessagesAndGetAsString();
|
||||
expect(second?.message).toBe('/summarize');
|
||||
expect(second?.isolate).toBe(true);
|
||||
|
||||
const third = await queue.waitForMessagesAndGetAsString();
|
||||
expect(third?.message).toBe('after summarize');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user