Files
resolutionflow/docs/superpowers/specs/2026-03-21-flowpilot-message-bar-and-script-builder-design.md
2026-03-21 16:46:26 -04:00

18 KiB

FlowPilot Message Bar & AI Script Builder

Date: 2026-03-21 Status: Approved Scope: Three interconnected features — FlowPilot always-visible message bar, standalone AI Script Builder, and Script Library reorganization


Problem

  1. FlowPilot sessions lack a visible text input. When FlowPilot asks the user to provide detailed information (e.g., AD user details for script building), the only way to type a response is a tiny "None of these — let me describe" link that expands into a textarea. Users don't notice it and feel stuck.

  2. No AI-powered script generation from natural language. The existing script system is template-based — users pick a pre-built template and fill in parameters. There's no way to say "build me a script that does X" and get a custom script generated.

  3. Script Library lacks personal/team separation. Scripts are either personal or team-shared via a toggle, but the UI doesn't clearly separate "My Scripts" from "Team Scripts." There's also no prominent entry point to create new scripts.


Feature 1: Always-Visible Message Bar (FlowPilot Sessions)

What Changes

Replace the hidden "None of these — let me describe" escape hatch with a persistent chat-style input bar pinned to the bottom of the FlowPilot session view.

Current Flow

  • Option buttons render inside the step card
  • A small "None of these — let me describe" text link appears below options (controlled by allow_free_text flag)
  • Clicking the link reveals a RichTextInput textarea with Submit/Cancel buttons
  • If the user doesn't notice the link, they have no way to respond with free text

New Flow

  • Option buttons still render inside the step card (unchanged)
  • A persistent message input bar is pinned to the bottom of the session view, outside step cards
  • Always visible, always ready — styled like Claude Desktop's input bar
  • Typing and submitting sends a free_text_input response to the current step (same backend flow, no backend changes needed)
  • The "None of these — let me describe" link is removed entirely from FlowPilotStepCard
  • The message bar is disabled when: FlowPilot is processing, the session is completed/resolved/escalated, or no current step exists

Component Changes

File Change
FlowPilotStepCard.tsx Remove the free text escape hatch block (the {!isResolutionSuggestion && step.allow_free_text && ...} conditional and its children)
FlowPilotSession.tsx Add FlowPilotMessageBar component, pinned to bottom of session area, above the Resolve/Escalate action bar
New: FlowPilotMessageBar.tsx Persistent input bar using existing RichTextInput. Rounded input with placeholder "Type a message...", send button. Calls onRespond({ free_text_input })

Visual Design

  • Rounded input field with bg-card border-border styling, consistent with the app's glass design system
  • Send button with bg-gradient-brand (cyan gradient)
  • Placeholder text: "Type a message..."
  • Pinned to bottom of session content area, above Resolve/Escalate bar
  • Disabled state (dimmed, no interaction) when processing or session is closed

Step Targeting

The message bar always submits to the current step at the moment of submission (same behavior as the existing free text mechanism). If the step changes between when the user starts typing and when they click Send, the submission targets the new current step. This is acceptable because FlowPilot won't advance steps without a user response.

Respecting allow_free_text: false

Certain step types explicitly set allow_free_text: false — specifically resolution suggestions and escalation briefings, where the AI expects a structured response (accept/reject). The message bar must respect this flag: when allow_free_text is false on the current step, the message bar is disabled (visually dimmed, input blocked) rather than hidden. This preserves visibility while preventing engineers from bypassing structured response constraints.

Backend Changes

None. The free_text_input field in StepResponseRequest already handles this. The allow_free_text flag on steps continues to control whether the message bar is enabled/disabled for a given step.


Feature 2: AI Script Builder (Standalone)

Overview

A new standalone page accessible from the sidebar that lets users describe what they need in natural language, and an AI generates a custom script. Users can iterate on the script through multi-turn conversation, then save it to the Script Library.

User Experience

  1. User clicks "Script Builder" in the sidebar nav
  2. Selects a language (PowerShell, Bash, Python) via pill-style selector at the top
  3. Types a description of what they need in a chat-style input: "I need a script that pulls all GPOs linked to a domain, showing name, link location, and enforcement status"
  4. AI responds in the conversation with an explanation and a collapsed code preview (first few lines + line count)
  5. Inline action buttons on each code block: "View Full Script", "Copy", "Save to Library"
  6. "View Full Script" opens a fullscreen modal overlay with the complete syntax-highlighted script, Copy/Save buttons in the header, line count and metadata in the footer, and a "Close & Return to Chat" button
  7. User can type follow-up messages to refine: "Add CSV export", "Handle multiple domains" — AI responds with an updated script
  8. "Save to Library" opens a dialog for: name, description (optional), category, and whether to share with team

Sidebar Navigation

  • Label: "Script Builder"
  • Section: "Knowledge" group (alongside Flows, Step Library, Scripts)
  • Icon: TBD (a Lucide icon that conveys "build/generate" — e.g., Wand2, Sparkles, or Terminal)
  • Mobile nav: Also add to mobileNavItems array in AppLayout.tsx

Frontend Architecture

Component Purpose
ScriptBuilderPage.tsx New page at /script-builder. Chat-style layout with language selector
ScriptBuilderChat.tsx Chat message list — user messages (right-aligned, cyan tint) and AI responses (left-aligned, glass card)
ScriptBuilderInput.tsx Chat input bar at bottom with send button
ScriptCodeBlock.tsx Collapsed code preview in AI messages with View/Copy/Save buttons
ScriptPreviewModal.tsx Fullscreen modal overlay for viewing complete script with syntax highlighting
SaveToLibraryDialog.tsx Dialog for naming and categorizing a script before saving
Route in router.tsx /script-builder inside ProtectedRoute/AppLayout children

Backend Architecture

New Service: script_builder_service.py

Location: backend/app/services/script_builder_service.py

Responsibilities:

  • Manages AI conversation for script generation
  • Language-specific system prompts with syntax knowledge, best practices, error handling patterns, security considerations
  • Multi-turn conversation support — maintains message history per session
  • Extracts generated script from AI response (parses code fences)
  • On save: auto-detects parameters from the generated script to populate parameters_schema (reuses existing ParameterCandidate analysis pattern)

Uses the existing AnthropicProvider with standard model tier (Sonnet) via settings.get_model_for_action().

Syntax Highlighting

Use react-syntax-highlighter with the oneDark theme (already a common choice in React ecosystems, lightweight). Supports PowerShell, Bash, and Python out of the box. Used for both the inline collapsed code preview (first 5 lines shown) and the fullscreen modal.

Inline Code Preview

The collapsed code block in AI chat messages shows:

  • Filename (auto-generated by AI, e.g., Get-LinkedGPOs.ps1)
  • First 5 lines of the script with syntax highlighting
  • Total line count (e.g., "42 lines")
  • Clicking anywhere on the code block opens the fullscreen modal (same as "View Full Script" button)

New Model: ScriptBuilderSession

Location: backend/app/models/script_builder_session.py

Column Type Description
id UUID Primary key
user_id UUID FK Creator
team_id UUID FK (nullable) Team context
language String(30) Selected language (powershell, bash, python)
title String(200, nullable) Auto-generated from first message
messages JSONB Array of {role, content, timestamp}
latest_script Text (nullable) Most recent generated script (for quick access)
ai_session_id UUID FK → ai_sessions.id (nullable) Link to FlowPilot session if launched from there
created_at DateTime(tz)
updated_at DateTime(tz)

New Endpoints: /scripts/builder/

Method Endpoint Purpose
POST /scripts/builder/sessions Start a new builder session. Body: { language }. Returns session with ID
POST /scripts/builder/sessions/{id}/messages Send user message, get AI response with generated script. Body: { content }. Returns { message, script, script_filename, line_count }
GET /scripts/builder/sessions List user's recent builder sessions (paginated). Returns lightweight schema without messages array — only id, title, language, created_at, updated_at
GET /scripts/builder/sessions/{id} Get full session with message history (for resuming). Returns complete messages JSONB
POST /scripts/builder/sessions/{id}/save Save latest script to library. Body: SaveToLibraryRequest { name, description?, category_id?, share_with_team? }. Service layer pulls script_body from session's latest_script and combines with user-provided metadata to create ScriptTemplate. Uses a dedicated SaveToLibraryRequest schema (not ScriptTemplateCreate)
DELETE /scripts/builder/sessions/{id} Delete a builder session. Owner only.

Language-Specific System Prompts

Each language gets a tailored system prompt injected into the AI conversation:

  • PowerShell: Advanced function patterns, param() blocks, CmdletBinding, module imports, error handling with try/catch/finally, Write-Verbose/Write-Error, pipeline support, .SYNOPSIS/.DESCRIPTION comment-based help
  • Bash: Shebang lines, set -euo pipefail, argument parsing with getopts or positional params, error handling, shellcheck-clean output, POSIX compatibility notes
  • Python: Type hints, argparse for CLI scripts, if __name__ == "__main__" guard, logging module, docstrings, virtual environment considerations

Frontend API Client

New module: frontend/src/api/scriptBuilder.ts

export const scriptBuilderApi = {
  createSession(language: string): Promise<ScriptBuilderSession>,
  sendMessage(sessionId: string, content: string): Promise<ScriptBuilderMessage>,
  listSessions(params?: PaginationParams): Promise<PaginatedResponse<ScriptBuilderSession>>,
  getSession(sessionId: string): Promise<ScriptBuilderSessionDetail>,
  saveToLibrary(sessionId: string, data: SaveToLibraryRequest): Promise<ScriptTemplateDetail>,
}

FlowPilot Integration

When FlowPilot detects the user needs a custom script (no matching template):

  1. FlowPilot responds with a step: "It sounds like you need a custom script for this. I can help you build one."
  2. The step includes an action button: "Open Script Builder"
  3. Clicking stores context in sessionStorage ({ from_session, prompt, language }) and opens /script-builder?from=flowpilot in a new browser tab. The Script Builder reads and clears sessionStorage on mount. This avoids URL length limits for long prompts.
  4. The ScriptBuilderSession stores the ai_session_id for traceability
  5. The FlowPilot session continues independently

Existing template-based flow (InSessionScriptGenerator) remains unchanged for when FlowPilot matches an existing template.


Feature 3: Script Library Reorganization

Tab Structure

Two tabs at the top of the Script Library page (/scripts):

  • My Scripts — scripts where created_by == current_user. Includes both AI-generated saves and manually created templates.
  • Team Scripts — scripts where the template is shared to the team (team_id == current_team, shared by any team member).

"Build a New Script" Button

A prominent action button at the top of the library page (next to the tab bar or in the header area). Styled with bg-gradient-brand (primary CTA). Clicking routes to /script-builder.

Data Model Changes

The existing ScriptTemplate model needs two additions:

  • language column (String(30), nullable, default 'powershell'): Stores the script language. Without this, saved scripts lose their language metadata and can't be filtered by language in the library. Added via migration.
  • Default "AI Generated" category: The existing category_id column is NOT NULL with a RESTRICT FK. Rather than making it nullable (which would break existing queries), create a default "AI Generated" category via migration seed data. The save dialog auto-selects this category when no other is chosen.

Existing fields used for library filtering:

  • created_by (user ID) — used for "My Scripts" filter
  • team_id (team ID, nullable) — used for "Team Scripts" filter
  • Team sharing toggle already exists

The frontend filtering changes:

  • My Scripts tab: GET /scripts/templates?mine=true (new query param, filters by created_by)
  • Team Scripts tab: GET /scripts/templates?shared=true (existing behavior, filters by team_id)

Library Backend Changes

Add mine query parameter to GET /scripts/templates endpoint. When mine=true, filter by created_by == current_user.id regardless of team sharing status.

Save to Library Dialog

When triggered from the Script Builder's "Save to Library" button:

Field Type Required Notes
Name Text input Yes Auto-suggested from AI-generated filename
Description Textarea No Auto-suggested from AI's explanation
Category Select dropdown No Defaults to "AI Generated" category if not selected
Share with team Toggle No Default off. Shares to user's team

On save, the backend:

  1. Creates a ScriptTemplate with the generated script as script_body (pulled from session's latest_script)
  2. Sets language from the builder session's language
  3. Auto-detects parameters from the script to populate parameters_schema
  4. Sets created_by to current user
  5. Sets category_id to selected category or default "AI Generated" category
  6. If "Share with team" is on, sets team_id to user's team

Implementation Phases

Phase 1: Always-Visible Message Bar

  • Remove free text escape hatch from FlowPilotStepCard
  • Create FlowPilotMessageBar component
  • Add to FlowPilotSession layout
  • No backend changes

Phase 2: Script Builder Core

  • New ScriptBuilderSession model + migration (includes language column on ScriptTemplate + "AI Generated" seed category)
  • script_builder_service.py with language-specific prompts
  • API endpoints for sessions and messages
  • Frontend: ScriptBuilderPage, chat components, code block with syntax highlighting via react-syntax-highlighter
  • Sidebar nav entry (desktop + mobile)
  • Route setup

Phase 3: Script Preview Modal & Save Flow

  • ScriptPreviewModal fullscreen overlay
  • SaveToLibraryDialog with auto-parameter detection
  • Backend save endpoint (creates ScriptTemplate via SaveToLibraryRequest schema)

Phase 4: Library Reorganization

  • My Scripts / Team Scripts tabs
  • "Build a New Script" button routing to Script Builder
  • Backend mine filter on templates endpoint

Phase 5: FlowPilot Integration

  • FlowPilot prompt updates to detect custom script needs
  • "Open Script Builder" action button in step cards
  • Context pre-fill via sessionStorage
  • ai_session_id linking

Operational Constraints

Rate Limiting & Token Budget

  • Max messages per session: 30 (same safety limit as FlowPilot sessions)
  • Max concurrent builder sessions per user: 5 active sessions
  • Rate limit: 10 messages per minute per user (uses existing @limiter.limit() decorator, disabled when DEBUG=True)
  • Context window management: When conversation history exceeds ~80% of the model's context window, the service truncates older messages (keeping the system prompt and last 10 exchanges). The latest_script field ensures the most recent script is always accessible even if early messages are truncated.

Session Lifecycle

  • Builder sessions have no explicit expiration — they persist until the user deletes them
  • The list endpoint returns sessions ordered by updated_at descending
  • A "New Conversation" button on the Script Builder page starts a fresh session
  • Recent sessions are accessible via a collapsible sidebar/drawer on the Script Builder page (not a separate page)
  • Session title is auto-generated after the first AI response (AI summarizes the intent)

Error Handling

AI Failures

Scenario Handling
AI response has no code block Display the AI's text response normally. Show a subtle hint: "No script was generated. Try being more specific about what you need."
AI request times out (120s) Show error toast: "Script generation timed out. Please try again." Keep conversation intact so user can retry.
AI returns malformed/unparseable response Log the raw response for debugging. Show generic error: "Something went wrong generating your script. Please try again."
Context window exceeded Auto-truncate older messages and retry once. If still fails, prompt user to start a new session.
AnthropicProvider rate limited (429) Show error with retry-after hint: "AI service is busy. Please wait a moment and try again."

Save Failures

Scenario Handling
No latest_script on session Disable "Save to Library" button. Show tooltip: "Generate a script first."
category_id constraint violation Should not occur (default "AI Generated" category). If it does, return 400 with clear message.
Duplicate template name Return 409 conflict. Dialog shows inline error: "A script with this name already exists."

Out of Scope

  • Script execution/testing within ResolutionFlow (scripts are generated and copied, not run)
  • Script version history (save creates a new template, no versioning on iterations)
  • Marketplace/community script sharing beyond team level
  • Languages beyond PowerShell, Bash, Python (extensible later)
  • Script Builder conversation branching (linear conversation only)