mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-01-31 06:42:06 +00:00
feat: Add robust fallback mechanisms for spec creation chat
Add multiple escape hatches to prevent users from getting stuck during
spec creation when the WebSocket completion signal fails.
Changes:
- Add "Exit to Project" button always visible in chat header
- Add /exit command detection to immediately exit to project
- Add backend GET /api/spec/status/{project} endpoint to poll status file
- Add getSpecStatus() API function in frontend
- Add status file polling (every 3s) in useSpecChat hook
- Update create-spec.md with status file write instructions
How it works:
1. Happy path: Claude writes .spec_status.json as final step, UI polls
and detects completion, shows "Continue to Project" button
2. Escape hatch: User can always click "Exit to Project" or type /exit
to instantly select the project and close modal, then manually start
the agent from the main UI
This ensures users always have a way forward even if the WebSocket
completion detection fails due to tool call tracking issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { Send, X, CheckCircle2, AlertCircle, Wifi, WifiOff, RotateCcw, Loader2, ArrowRight, Zap, Paperclip } from 'lucide-react'
|
||||
import { Send, X, CheckCircle2, AlertCircle, Wifi, WifiOff, RotateCcw, Loader2, ArrowRight, Zap, Paperclip, ExternalLink } from 'lucide-react'
|
||||
import { useSpecChat } from '../hooks/useSpecChat'
|
||||
import { ChatMessage } from './ChatMessage'
|
||||
import { QuestionOptions } from './QuestionOptions'
|
||||
@@ -23,6 +23,7 @@ interface SpecCreationChatProps {
|
||||
projectName: string
|
||||
onComplete: (specPath: string, yoloMode?: boolean) => void
|
||||
onCancel: () => void
|
||||
onExitToProject: () => void // Exit to project without starting agent
|
||||
initializerStatus?: InitializerStatus
|
||||
initializerError?: string | null
|
||||
onRetryInitializer?: () => void
|
||||
@@ -32,6 +33,7 @@ export function SpecCreationChat({
|
||||
projectName,
|
||||
onComplete,
|
||||
onCancel,
|
||||
onExitToProject,
|
||||
initializerStatus = 'idle',
|
||||
initializerError = null,
|
||||
onRetryInitializer,
|
||||
@@ -86,6 +88,13 @@ export function SpecCreationChat({
|
||||
// Allow sending if there's text OR attachments
|
||||
if ((!trimmed && pendingAttachments.length === 0) || isLoading) return
|
||||
|
||||
// Detect /exit command - exit to project without sending to Claude
|
||||
if (/^\s*\/exit\s*$/i.test(trimmed)) {
|
||||
setInput('')
|
||||
onExitToProject()
|
||||
return
|
||||
}
|
||||
|
||||
sendMessage(trimmed, pendingAttachments.length > 0 ? pendingAttachments : undefined)
|
||||
setInput('')
|
||||
setPendingAttachments([]) // Clear attachments after sending
|
||||
@@ -210,6 +219,16 @@ export function SpecCreationChat({
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Exit to Project - always visible escape hatch */}
|
||||
<button
|
||||
onClick={onExitToProject}
|
||||
className="neo-btn neo-btn-ghost text-sm py-2"
|
||||
title="Exit chat and go to project (you can start the agent manually)"
|
||||
>
|
||||
<ExternalLink size={16} />
|
||||
Exit to Project
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={onCancel}
|
||||
className="neo-btn neo-btn-ghost p-2"
|
||||
@@ -347,7 +366,7 @@ export function SpecCreationChat({
|
||||
? 'Or type a custom response...'
|
||||
: pendingAttachments.length > 0
|
||||
? 'Add a message with your image(s)...'
|
||||
: 'Type your response...'
|
||||
: 'Type your response... (or /exit to go to project)'
|
||||
}
|
||||
className="neo-input flex-1"
|
||||
disabled={(isLoading && !currentQuestions) || connectionStatus !== 'connected'}
|
||||
|
||||
Reference in New Issue
Block a user