diff --git a/src/App.tsx b/src/App.tsx index e9bff77..fdf5b53 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -143,6 +143,7 @@ function App() { const [triageError, setTriageError] = useState('') const [triageBatchStatus, setTriageBatchStatus] = useState<'idle' | 'running'>('idle') const [triageEditMode, setTriageEditMode] = useState(false) + const [triageClarificationAnswer, setTriageClarificationAnswer] = useState('') const [triageSavedMessage, setTriageSavedMessage] = useState('') const [showManualFeatureEditor, setShowManualFeatureEditor] = useState(false) const [showManualParkingEditor, setShowManualParkingEditor] = useState(false) @@ -656,6 +657,7 @@ function App() { setTriageOpen(true) setTriageError('') setTriageEditMode(false) + setTriageClarificationAnswer('') setTriageSavedMessage('') setTriageStatus(triageRecommendation && !seed ? 'ready' : 'idle') if (seed) { @@ -671,6 +673,7 @@ function App() { setTriageStatus('idle') setTriageError('') setTriageEditMode(false) + setTriageClarificationAnswer('') } const openDecisionPulse = (pulseId?: string | null) => { @@ -741,7 +744,7 @@ function App() { .map((pulse) => ({ message: pulse.message, evidence_refs: pulse.evidence_refs })), }) - const runAiTriage = async () => { + const runAiTriage = async (optionalContextOverride?: string) => { const rawIdea = triageDraft.rawIdea.trim() if (!rawIdea) { setTriageError('Write the idea first. The scope goblin needs bait.') @@ -749,6 +752,8 @@ function App() { return } + const optionalContext = optionalContextOverride ?? triageDraft.optionalContext.trim() + setTriageStatus('loading') setTriageError('') setTriageSavedMessage('') @@ -757,7 +762,7 @@ function App() { try { const recommendation = await triageIdeaWithAi({ raw_idea: rawIdea, - optional_context: triageDraft.optionalContext.trim(), + optional_context: optionalContext, app_context: buildAiTriageContext(), }) const timestamp = nowIso() @@ -765,7 +770,7 @@ function App() { id: `rec_${slugify(rawIdea)}_${timestamp.replace(/[^0-9]/g, '')}`, created_at: timestamp, raw_idea: rawIdea, - optional_context: triageDraft.optionalContext.trim(), + optional_context: optionalContext, context_summary: `${appState.project.name}: ${appState.project.current_goal}`, ...recommendation, user_decision: 'pending', @@ -786,6 +791,7 @@ function App() { }) setTriageStatus('ready') setTriageEditMode(false) + setTriageClarificationAnswer('') setStatusMessage(`AI suggested ${placementLabels[fullRecommendation.suggested_placement]} with ${fullRecommendation.scope_risk} scope risk.`) } catch (error) { setTriageStatus('error') @@ -1203,6 +1209,28 @@ function App() { setStatusMessage('Pulse deleted.') } + const answerClarifyingQuestion = async () => { + const answer = triageClarificationAnswer.trim() + if (!answer) { + setTriageError('Give the AI a real answer first.') + setTriageStatus('error') + return + } + + const mergedContext = [triageDraft.optionalContext.trim(), `Clarification answer: ${answer}`] + .filter(Boolean) + .join('\n\n') + + setTriageDraft((current) => ({ + ...current, + optionalContext: mergedContext, + })) + setTriageRecommendation(null) + setTriageEditMode(false) + setStatusMessage('Re-running triage with your clarification…') + await runAiTriage(mergedContext) + } + const copyMarkdown = async (filename: string) => { try { await navigator.clipboard.writeText(markdownPackage[filename as keyof typeof markdownPackage]) @@ -1274,8 +1302,10 @@ function App() { ? `${target} handoff for “${feature.title}” copied${shouldLogIntent ? ' and INTENT logged' : ''}.` : `${target} handoff copied${shouldLogIntent ? ' and INTENT logged' : ''}.`, ) + return true } catch { setStatusMessage('Clipboard copy failed. Browser said no.') + return false } } @@ -1292,9 +1322,15 @@ function App() { } const syncHandoffPreviewTarget = (target: (typeof PROMPT_TARGETS)[number]) => { - const { prompt } = buildHandoffPrompt(handoffPreviewFeatureId || undefined, target) + const { prompt } = buildHandoffPrompt(handoffPreviewFeatureId || undefined, handoffPreviewTarget) + if (handoffPreviewDraft.trim() !== prompt.trim()) { + const shouldReplace = window.confirm('Switching target will replace your current edits with the new preset. Continue?') + if (!shouldReplace) return + } + + const { prompt: nextPrompt } = buildHandoffPrompt(handoffPreviewFeatureId || undefined, target) setHandoffPreviewTarget(target) - setHandoffPreviewDraft(prompt) + setHandoffPreviewDraft(nextPrompt) } const resetHandoffPreview = () => { @@ -1621,16 +1657,20 @@ function App() {

Step 3 of 3

-

Choose action

-

AI advises. You decide.

+

{triageNeedsClarification ? 'Clarify before deciding' : 'Choose action'}

+

{triageNeedsClarification ? 'Answer the question, then re-run triage with the missing detail.' : 'AI advises. You decide.'}

{triageNeedsClarification ? ( <> - + @@ -1656,7 +1696,32 @@ function App() { {triageEditMode && (
-

Edit before saving

+

{triageNeedsClarification ? 'Answer and refine' : 'Edit before saving'}

+ {triageNeedsClarification && ( + <> +
+ Answer the question +

{triageRecommendation?.clarifying_question || 'Clarify the idea, then re-run triage.'}

+
+