diff --git a/packages/ui/src/components/chat/ChatInput.tsx b/packages/ui/src/components/chat/ChatInput.tsx index 277acd14..a7fa620a 100644 --- a/packages/ui/src/components/chat/ChatInput.tsx +++ b/packages/ui/src/components/chat/ChatInput.tsx @@ -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 = ({ 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 diff --git a/packages/ui/src/components/chat/CommandAutocomplete.tsx b/packages/ui/src/components/chat/CommandAutocomplete.tsx index 35a290b5..ec13827b 100644 --- a/packages/ui/src/components/chat/CommandAutocomplete.tsx +++ b/packages/ui/src/components/chat/CommandAutocomplete.tsx @@ -13,6 +13,7 @@ interface CommandInfo { agent?: string; model?: string; isBuiltIn?: boolean; + isOpenChamber?: boolean; isSkill?: boolean; scope?: string; } @@ -111,6 +112,10 @@ export const CommandAutocomplete = React.forwardRef(); @@ -154,6 +159,10 @@ export const CommandAutocomplete = React.forwardRef {commands.map((command, index) => { const isSystem = command.isBuiltIn; + const isOpenChamberBadge = command.isOpenChamber; const isProject = command.scope === 'project'; return ( @@ -359,7 +369,18 @@ export const CommandAutocomplete = React.forwardRef ) : null} - {isSystem ? ( + {isOpenChamberBadge ? ( + + openchamber + + ) : isSystem ? ( system diff --git a/packages/ui/src/components/sections/magic-prompts/MagicPromptsPage.tsx b/packages/ui/src/components/sections/magic-prompts/MagicPromptsPage.tsx index 4830c26d..d5858b85 100644 --- a/packages/ui/src/components/sections/magic-prompts/MagicPromptsPage.tsx +++ b/packages/ui/src/components/sections/magic-prompts/MagicPromptsPage.tsx @@ -123,6 +123,14 @@ const PROMPT_PAGE_MAP: Record = { { id: 'plan.implement.instructions', title: 'Instructions' }, ], }, + 'session.summary': { + title: 'Session Summary', + description: 'Prompts used by the /summary slash command: visible user message + hidden instructions. Non-destructive — does not compact session history.', + blocks: [ + { id: 'session.summary.visible', title: 'Visible Prompt' }, + { id: 'session.summary.instructions', title: 'Instructions' }, + ], + }, }; const hasOwn = (input: Record, key: string) => Object.prototype.hasOwnProperty.call(input, key); diff --git a/packages/ui/src/components/sections/magic-prompts/MagicPromptsSidebar.tsx b/packages/ui/src/components/sections/magic-prompts/MagicPromptsSidebar.tsx index dae0f868..751a2a17 100644 --- a/packages/ui/src/components/sections/magic-prompts/MagicPromptsSidebar.tsx +++ b/packages/ui/src/components/sections/magic-prompts/MagicPromptsSidebar.tsx @@ -40,6 +40,12 @@ export const MagicPromptsSidebar: React.FC = ({ onItem { id: 'plan.implement', title: 'Implement Plan' }, ], }, + { + group: 'Session', + items: [ + { id: 'session.summary', title: 'Session Summary' }, + ], + }, ] as const; }, []); diff --git a/packages/ui/src/lib/magicPrompts.ts b/packages/ui/src/lib/magicPrompts.ts index 7a45ee6c..20765a8c 100644 --- a/packages/ui/src/lib/magicPrompts.ts +++ b/packages/ui/src/lib/magicPrompts.ts @@ -22,13 +22,15 @@ export type MagicPromptId = | 'plan.improve.visible' | 'plan.improve.instructions' | 'plan.implement.visible' - | 'plan.implement.instructions'; + | 'plan.implement.instructions' + | 'session.summary.visible' + | 'session.summary.instructions'; export interface MagicPromptDefinition { id: MagicPromptId; title: string; description: string; - group: 'Git' | 'GitHub' | 'Planning'; + group: 'Git' | 'GitHub' | 'Planning' | 'Session'; template: string; placeholders?: Array<{ key: string; description: string }>; } @@ -466,6 +468,44 @@ Do the implementation work continuously. When a plan step is ambiguous, do not s Do not expand scope beyond the plan. If during implementation you find the plan itself is wrong or genuinely blocks completion (not merely ambiguous), stop, state exactly what is broken and why, and propose a plan adjustment to save back into this same file ({{plan_path}}) before continuing.`, }, + { + id: 'session.summary.visible', + title: 'Session Summary Visible Prompt', + group: 'Session', + description: 'Visible user message sent by the /summary command.', + placeholders: [ + { key: 'topic_line', description: 'Pre-formatted topic clause (e.g. " focused on: ") or empty string.' }, + ], + template: 'Summarize this session{{topic_line}}.', + }, + { + id: 'session.summary.instructions', + title: 'Session Summary Instructions', + group: 'Session', + description: 'Hidden instructions attached to the /summary command. Produces a non-destructive summary usable for handing off to a new session.', + placeholders: [ + { key: 'topic_block', description: 'Pre-formatted topic focus paragraph, or empty string when no topic hint was given.' }, + ], + template: `Produce a non-destructive summary of this conversation. Do NOT compact or mutate session history — your output is an additional assistant message the user will read and may use to hand off to a new session. + +Cover the information useful for continuing this work: +- What was done (completed work, in order) +- What is currently in progress +- Files modified — brief what and why per file +- Open questions and next steps +- User requests, constraints, or preferences to carry forward +- Important technical decisions and why they were made + +{{topic_block}} + +Formatting: +- Concise markdown with short sections and bullet lists +- No preamble like "Here is a summary" — jump straight to content +- Do not answer questions found in the conversation — only summarize +- Keep length proportional to session length; do not pad + +Respond in the same language the user used most in the conversation.`, + }, ] as const; const MAGIC_PROMPT_DEFINITION_BY_ID = new Map(