feat(chat): /summary slash command for non-destructive session summaries

Typing /summary (optionally with a topic hint) produces a structured
summary as a normal assistant message — without compacting or mutating
session history. Reuses the existing "Start new session from this answer"
button for hand-off.

Prompts are customizable in Settings → Magic Prompts under a new Session
group. Autocomplete shows an 'openchamber' badge to distinguish our
commands from OpenCode-built-in ones.
This commit is contained in:
Bohdan Triapitsyn
2026-04-22 21:56:35 +03:00
parent f24e6de21b
commit 863be8073b
5 changed files with 108 additions and 3 deletions
@@ -28,6 +28,7 @@ import * as sessionActions from '@/sync/session-actions';
import { useUserMessageHistory } from '@/sync/sync-context';
import { useInlineCommentDraftStore, type InlineCommentDraft } from '@/stores/useInlineCommentDraftStore';
import { appendInlineComments } from '@/lib/messages/inlineComments';
import { renderMagicPrompt } from '@/lib/magicPrompts';
import { AttachedFilesList } from './FileAttachment';
import { QueuedMessageChips } from './QueuedMessageChips';
import { FileMentionAutocomplete, type FileMentionHandle } from './FileMentionAutocomplete';
@@ -1533,6 +1534,35 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
}
return;
}
else if (commandName === 'summary' && currentSessionId) {
try {
await sessionActions.waitForConnectionOrThrow();
// Everything after `/summary ` is an optional topic hint
// the user wants the summary focused on.
const topic = normalizedCommand.replace(/^\/summary\b/i, '').trim();
const topicLine = topic ? ` focused on: ${topic}` : '';
const topicBlock = topic
? `The user asked you to focus this summary on: ${topic}. Prioritize that topic; mention unrelated threads only in passing.`
: '';
const visibleText = await renderMagicPrompt('session.summary.visible', { topic_line: topicLine });
const instructionsText = await renderMagicPrompt('session.summary.instructions', { topic_block: topicBlock });
await sendMessage(
visibleText,
currentProviderId,
currentModelId,
currentAgentName,
[],
agentMentionName,
[{ text: instructionsText, synthetic: true }],
currentVariant,
inputMode,
);
scrollToBottom?.({ instant: true, force: true });
} catch (error) {
toast.error(error instanceof Error ? error.message : 'Failed to generate summary');
}
return;
}
}
// Collect all attachments for error recovery