feat: add craft-goal session starter and command

Adds /craft-goal autocomplete and chat handling for starting a Goal crafting session.
Introduces new Magic Prompts content and localized labels/descriptions for Goal crafting.
Migrates desktop draft starters to include Craft a Goal once and persists the migration marker.
This commit is contained in:
Bohdan Triapitsyn
2026-07-12 10:58:58 +03:00
parent e8be7ef55b
commit a0bdcae54c
30 changed files with 210 additions and 3 deletions
+30 -2
View File
@@ -1255,7 +1255,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
const availableSkills = useSkillsStore((s) => s.skills);
const knownSlashNames = React.useMemo(() => {
const names = new Set<string>([
'init', 'review', 'undo', 'redo', 'timeline', 'compact', 'summary', 'workspace-review', 'plan-feature', 'catch-up', 'debug', 'weigh', 'explore',
'init', 'review', 'undo', 'redo', 'timeline', 'compact', 'summary', 'workspace-review', 'plan-feature', 'craft-goal', 'catch-up', 'debug', 'weigh', 'explore',
]);
if (!isMobile && !isVSCodeRuntime()) names.add('handoff-review');
for (const command of availableCommands) names.add(command.name.toLowerCase());
@@ -2165,6 +2165,32 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
}
return;
}
else if (commandName === 'craft-goal' && (currentSessionId || newSessionDraftOpen)) {
try {
await sessionActions.waitForConnectionOrThrow();
const idea = normalizedCommand.replace(/^\/craft-goal\b/i, '').trim();
const visibleText = await renderMagicPrompt('session.craftGoal.visible', {
idea_block: idea ? `\n\nHere is my initial idea:\n${idea}` : '',
});
const instructionsText = await renderMagicPrompt('session.craftGoal.instructions');
await sendMessage(
visibleText,
providerIdToSend,
modelIdToSend,
agentNameToSend,
[],
agentMentionName,
[{ text: instructionsText, synthetic: true }],
variantToSend,
inputMode,
sendMessageOptions,
);
scrollToBottom?.();
} catch (error) {
toast.error(error instanceof Error ? error.message : t('chat.chatInput.toast.craftGoalFailed'));
}
return;
}
else if (commandName === 'catch-up' && (currentSessionId || newSessionDraftOpen)) {
try {
await sessionActions.waitForConnectionOrThrow();
@@ -2388,7 +2414,9 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
// The text goes straight into the submit (see SubmitOptions.presetText)
// instead of through the composer input — the collapsed mobile pill has
// no mounted textarea to stage it in.
void handleSubmitRef.current({ presetText: text });
const draft = (textareaRef.current?.value ?? messageRef.current).trim();
const presetText = draft ? `${text}\n${draft}` : text;
void handleSubmitRef.current({ presetText });
}, []);
// Dictation: insert the transcript inline; optionally submit immediately.
@@ -166,6 +166,10 @@ export const CommandAutocomplete = React.forwardRef<CommandAutocompleteHandle, C
? [{ id: 'openchamber:plan-feature', name: 'plan-feature', source: 'openchamber' as const, description: t('chat.commandAutocomplete.command.featurePlanDescription'), isOpenChamber: true }]
: []
),
...(canStartSessionCommand
? [{ id: 'openchamber:craft-goal', name: 'craft-goal', source: 'openchamber' as const, description: t('chat.commandAutocomplete.command.craftGoalDescription'), isOpenChamber: true }]
: []
),
...(canStartSessionCommand
? [{ id: 'openchamber:catch-up', name: 'catch-up', source: 'openchamber' as const, description: t('chat.commandAutocomplete.command.catchUpDescription'), isOpenChamber: true }]
: []
@@ -235,6 +239,10 @@ export const CommandAutocomplete = React.forwardRef<CommandAutocompleteHandle, C
? [{ id: 'openchamber:plan-feature', name: 'plan-feature', source: 'openchamber' as const, description: t('chat.commandAutocomplete.command.featurePlanDescription'), isOpenChamber: true }]
: []
),
...(canStartSessionCommand
? [{ id: 'openchamber:craft-goal', name: 'craft-goal', source: 'openchamber' as const, description: t('chat.commandAutocomplete.command.craftGoalDescription'), isOpenChamber: true }]
: []
),
...(canStartSessionCommand
? [{ id: 'openchamber:catch-up', name: 'catch-up', source: 'openchamber' as const, description: t('chat.commandAutocomplete.command.catchUpDescription'), isOpenChamber: true }]
: []