feat(pilot): Phase 5 — inline Script Generator integration
All checks were successful
Mirror to GitHub / mirror (push) Successful in 10s
All checks were successful
Mirror to GitHub / mirror (push) Successful in 10s
Wires the SuggestedFix card to an inline panel that handles both cases:
template-matched fixes open the Script Library generator with parameters
pre-filled from session context; un-matched fixes open the three-option
dialog (one_off / draft_template / build_template). The decision endpoint
records the path choice with side effects: draft_template persists a
draft_templates row via a Sonnet-driven TemplateExtractionService;
build_template returns a redirect to the Script Builder; one_off just
records the choice.
Backend:
- TemplateExtractionService: drafts a parameter schema from a concrete
rendered script. Conservative by default ("prefer fewer parameters").
Round-trip-validates that templated_body only references declared
parameters; missing-key mismatch falls back to the original script
with no params. LLM/parse failures fall back identically — the
engineer can still create a draft and refine in the post-resolve
prompt (Phase 6).
- /suggested-fixes/{fix_id}/decision side effects:
* one_off → returns rendered_script (engineer's edited version or the
fix's ai_drafted_script verbatim)
* draft_template → same + creates draft_templates row with extracted
params, returns draft_template_id
* build_template → returns redirect_path=/scripts/builder?from_session=
&fix= so the frontend can navigate to the builder pre-loaded
- 400 when a non-template fix has no ai_drafted_script (template-matched
fixes take the dedicated /scripts/generate path, not this endpoint).
- 12 tests: TemplateExtractionService parse + fallback paths, all four
decision branches, edited_script override, missing-script 400.
Frontend:
- src/components/pilot/script/{TemplateMatchPanel, NoTemplateDialog,
ParameterizationPreview}.tsx — inline panels rendered in the task
lane's bottom slot when the engineer clicks a SuggestedFix card.
- TemplateMatchPanel: loads template via /scripts/templates/{id},
pre-fills params from fix.ai_drafted_parameters with cyan "from
session" tags, generates via existing /scripts/generate (already
bumps state_version on ai_session_id from Phase 3). 404 falls back
with a clear message instead of erroring.
- NoTemplateDialog: shows the AI-drafted script with proposed parameter
values highlighted in amber via ParameterizationPreview; three option
cards with the middle (draft_template) flagged Recommended; inline
edit on the script body before deciding.
- SuggestedFix card now clickable: onActivate toggles the inline panel.
- AssistantChatPage: scriptPanelOpen state + handleScriptDecision that
navigates on build_template and toasts on the other paths. Active fix
changes auto-close the panel so engineers don't act on stale state.
- Cmd+K → "Open inline Script Generator" palette entry surfaces only on
/pilot/:id routes; fires a window event the chat page subscribes to.
No Resolve shortcut added per Section 14 decision (browser ⌘R conflict).
Verified 2026-04-22 against the dev stack:
- one_off / draft_template / build_template all return the right shape
with real Sonnet TemplateExtractionService for the draft path.
- Conservative extraction confirmed: cmdkey + Restart-Process script
yielded zero proposed parameters as intended.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect, useRef, useCallback, useMemo } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import {
|
||||
Search, Loader2, ArrowRight, FileText, Clock,
|
||||
Sparkles, LayoutDashboard, Tag, Plus, BookOpen, Terminal, Zap,
|
||||
@@ -60,6 +60,17 @@ const QUICK_ACTIONS: PaletteItem[] = [
|
||||
{ id: 'action-scripts', group: 'quick-actions', title: 'Open Script Generator', subtitle: 'Generate automation scripts', path: '/scripts', icon: 'action' },
|
||||
]
|
||||
|
||||
// Phase 5: only surfaced when on a /pilot/:id route. Fires the inline-script
|
||||
// open event instead of navigating away to /scripts.
|
||||
const SCRIPTS_INLINE_QUICK_ACTION: PaletteItem = {
|
||||
id: 'action-scripts-inline',
|
||||
group: 'quick-actions',
|
||||
title: 'Open inline Script Generator',
|
||||
subtitle: 'For the active suggested fix in this session',
|
||||
path: PILOT_INLINE_SCRIPT_PATH,
|
||||
icon: 'action',
|
||||
}
|
||||
|
||||
function ItemIcon({ icon, className }: { icon: PaletteItem['icon'], className?: string }) {
|
||||
const cls = cn('shrink-0', className)
|
||||
switch (icon) {
|
||||
@@ -75,9 +86,21 @@ function ItemIcon({ icon, className }: { icon: PaletteItem['icon'], className?:
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 5: sentinel path the palette uses to fire the inline-script-generator
|
||||
// open event instead of navigating. Listened for by AssistantChatPage when
|
||||
// the user is in an active session.
|
||||
export const PILOT_INLINE_SCRIPT_EVENT = 'flowpilot:open-inline-script'
|
||||
const PILOT_INLINE_SCRIPT_PATH = '__pilot_inline_script__'
|
||||
|
||||
export function CommandPalette({ open, onClose }: CommandPaletteProps) {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const user = useAuthStore(s => s.user)
|
||||
// True when the user is currently on a FlowPilot session deep-link.
|
||||
// Used to surface the "Open inline Script Generator" palette entry only
|
||||
// when it's actually actionable (the chat page listens for the event;
|
||||
// dispatching it from /trees would do nothing).
|
||||
const onPilotSession = location.pathname.startsWith('/pilot/')
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const [query, setQuery] = useState('')
|
||||
const [isSearching, setIsSearching] = useState(false)
|
||||
@@ -167,7 +190,10 @@ export function CommandPalette({ open, onClose }: CommandPaletteProps) {
|
||||
if (recentItems.length > 0) {
|
||||
result.push({ type: 'recent-flows', label: 'Recent Flows', items: recentItems })
|
||||
}
|
||||
result.push({ type: 'quick-actions', label: 'Quick Actions', items: QUICK_ACTIONS })
|
||||
const quickActions = onPilotSession
|
||||
? [SCRIPTS_INLINE_QUICK_ACTION, ...QUICK_ACTIONS]
|
||||
: QUICK_ACTIONS
|
||||
result.push({ type: 'quick-actions', label: 'Quick Actions', items: quickActions })
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -278,6 +304,12 @@ export function CommandPalette({ open, onClose }: CommandPaletteProps) {
|
||||
|
||||
const handleSelect = useCallback((item: PaletteItem) => {
|
||||
onClose()
|
||||
if (item.path === PILOT_INLINE_SCRIPT_PATH) {
|
||||
// Phase 5: window event lets the chat page open the inline panel
|
||||
// without coupling the global palette to chat-page state.
|
||||
window.dispatchEvent(new CustomEvent(PILOT_INLINE_SCRIPT_EVENT))
|
||||
return
|
||||
}
|
||||
if (item.group === 'flowpilot') {
|
||||
navigate(item.path, { state: { prefill: query.trim() } })
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user