mirror of
https://github.com/mark3labs/kit.git
synced 2026-06-14 03:30:26 +00:00
fix: script mode streaming display and example script formatting
- Accumulate stream chunks in a buffer and flush through DisplayAssistantMessageWithModel at boundaries (tool calls, step complete), mirroring the TUI's StreamComponent accumulate-and-flush strategy. Text accompanying tool calls now renders identically to solo assistant responses. - Fix example-script.sh: add missing --- frontmatter delimiters and convert legacy command/args format to new type+command list format so Viper YAML parsing works correctly. - Fix env-substitution-script.sh: add missing execute permission.
This commit is contained in:
+54
-18
@@ -730,15 +730,21 @@ func runScriptMode(ctx context.Context, mcpConfig *config.Config, prompt string,
|
||||
}
|
||||
|
||||
// scriptEventHandler routes app events to CLI display methods for script mode.
|
||||
// It maintains the spinner state needed for proper display. Script mode does
|
||||
// not support in-place streaming updates (no Bubble Tea); the final response
|
||||
// is always rendered as a complete block at the end via StepCompleteEvent.
|
||||
// It mirrors the TUI's StreamComponent accumulate-and-flush strategy
|
||||
// (see internal/ui/model.go flushStreamContent and stream.go):
|
||||
// - StreamChunkEvents are accumulated in a buffer (like StreamComponent).
|
||||
// - Before tool calls the buffer is flushed through DisplayAssistantMessageWithModel
|
||||
// so that text accompanying tool calls renders identically to solo responses.
|
||||
// - ToolCallContentEvent is ignored during streaming (text already in the buffer).
|
||||
// - ResponseCompleteEvent is used only as a non-streaming fallback.
|
||||
// - StepCompleteEvent flushes any remaining buffered text.
|
||||
type scriptEventHandler struct {
|
||||
cli *ui.CLI
|
||||
modelName string
|
||||
|
||||
spinner *ui.Spinner
|
||||
lastDisplayed string // tracks content shown via ToolCallContentEvent
|
||||
lastDisplayed string // tracks content shown (non-streaming)
|
||||
streamBuf strings.Builder // accumulated stream chunks (mirrors StreamComponent)
|
||||
}
|
||||
|
||||
func newScriptEventHandler(cli *ui.CLI, modelName string) *scriptEventHandler {
|
||||
@@ -766,6 +772,17 @@ func (h *scriptEventHandler) startSpinner() {
|
||||
h.spinner.Start()
|
||||
}
|
||||
|
||||
// flushStreamBuffer renders any accumulated stream chunks through the CLI
|
||||
// formatter (mirrors TUI's flushStreamContent at model.go:781-791).
|
||||
func (h *scriptEventHandler) flushStreamBuffer() {
|
||||
text := strings.TrimSpace(h.streamBuf.String())
|
||||
h.streamBuf.Reset()
|
||||
if text != "" {
|
||||
_ = h.cli.DisplayAssistantMessageWithModel(text, h.modelName)
|
||||
h.lastDisplayed = text
|
||||
}
|
||||
}
|
||||
|
||||
// Handle processes a single app event and renders it via the CLI.
|
||||
func (h *scriptEventHandler) Handle(msg tea.Msg) {
|
||||
switch e := msg.(type) {
|
||||
@@ -776,8 +793,27 @@ func (h *scriptEventHandler) Handle(msg tea.Msg) {
|
||||
h.stopSpinner()
|
||||
}
|
||||
|
||||
case app.StreamChunkEvent:
|
||||
// Accumulate chunks in the buffer (like TUI StreamComponent).
|
||||
// Text is rendered as a formatted message when flushed at boundaries.
|
||||
h.stopSpinner()
|
||||
h.streamBuf.WriteString(e.Content)
|
||||
|
||||
case app.ToolCallContentEvent:
|
||||
// In streaming mode this text was already buffered via StreamChunkEvents
|
||||
// (mirrors TUI behavior at model.go:405-408). Only display when the
|
||||
// buffer is empty (non-streaming path).
|
||||
if h.streamBuf.Len() == 0 {
|
||||
h.stopSpinner()
|
||||
_ = h.cli.DisplayAssistantMessageWithModel(e.Content, h.modelName)
|
||||
h.lastDisplayed = e.Content
|
||||
h.startSpinner()
|
||||
}
|
||||
|
||||
case app.ToolCallStartedEvent:
|
||||
h.stopSpinner()
|
||||
// Flush buffered text before tool call output (mirrors TUI flushStreamContent).
|
||||
h.flushStreamBuffer()
|
||||
h.cli.DisplayToolCallMessage(e.ToolName, e.ToolArgs)
|
||||
|
||||
case app.ToolExecutionEvent:
|
||||
@@ -793,29 +829,26 @@ func (h *scriptEventHandler) Handle(msg tea.Msg) {
|
||||
h.cli.DisplayToolMessage(e.ToolName, e.ToolArgs, resultContent, e.IsError)
|
||||
h.startSpinner()
|
||||
|
||||
case app.StreamChunkEvent:
|
||||
// Script mode has no in-place streaming display; chunks are ignored.
|
||||
// The final response is rendered as a complete block in StepCompleteEvent.
|
||||
h.stopSpinner()
|
||||
|
||||
case app.ToolCallContentEvent:
|
||||
// Text content that accompanies tool calls (e.g. "Let me check that...").
|
||||
h.stopSpinner()
|
||||
_ = h.cli.DisplayAssistantMessageWithModel(e.Content, h.modelName)
|
||||
h.lastDisplayed = e.Content
|
||||
h.startSpinner()
|
||||
|
||||
case app.ResponseCompleteEvent:
|
||||
h.stopSpinner()
|
||||
// Non-streaming fallback: display the complete response.
|
||||
// In streaming mode the buffer will be flushed at StepCompleteEvent.
|
||||
if h.streamBuf.Len() == 0 && e.Content != "" {
|
||||
_ = h.cli.DisplayAssistantMessageWithModel(e.Content, h.modelName)
|
||||
h.lastDisplayed = e.Content
|
||||
}
|
||||
|
||||
case app.StepCompleteEvent:
|
||||
h.stopSpinner()
|
||||
|
||||
// Flush any remaining buffered stream content.
|
||||
h.flushStreamBuffer()
|
||||
|
||||
// Non-streaming fallback: render the full response if not already shown.
|
||||
responseText := ""
|
||||
if e.Response != nil {
|
||||
responseText = e.Response.Content.Text()
|
||||
}
|
||||
|
||||
// Display the final response unless already shown via ToolCallContentEvent.
|
||||
if responseText != "" && responseText != h.lastDisplayed {
|
||||
_ = h.cli.DisplayAssistantMessageWithModel(responseText, h.modelName)
|
||||
}
|
||||
@@ -825,6 +858,9 @@ func (h *scriptEventHandler) Handle(msg tea.Msg) {
|
||||
h.cli.UpdateUsageFromResponse(e.Response, "")
|
||||
}
|
||||
h.cli.DisplayUsageAfterResponse()
|
||||
|
||||
// Reset for next step in the agentic loop.
|
||||
h.lastDisplayed = ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Regular → Executable
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env -S mcphost script
|
||||
---
|
||||
# This script uses the container-use MCP server from https://github.com/dagger/container-use
|
||||
mcpServers:
|
||||
container-use:
|
||||
command: cu
|
||||
args:
|
||||
- "stdio"
|
||||
prompt: |
|
||||
Create 2 variations of a simple hello world app using Flask and FastAPI. each in their own environment. Give me the URL of each app
|
||||
type: "local"
|
||||
command: ["container-use", "stdio"]
|
||||
---
|
||||
Create 2 variations of a simple hello world app using Flask and FastAPI. each in their own environment. Give me the URL of each app
|
||||
|
||||
Reference in New Issue
Block a user