Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 31 additions & 31 deletions pkg/workflow/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,13 @@ func (c *Compiler) CompileWorkflow(markdownPath string) error {
lockFile := strings.TrimSuffix(markdownPath, ".md") + ".lock.yml"

if c.verbose {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Starting compilation of: %s", console.ToRelativePath(markdownPath))))
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Output file: %s", console.ToRelativePath(lockFile))))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Starting compilation of: %s", console.ToRelativePath(markdownPath))))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Output file: %s", console.ToRelativePath(lockFile))))
}

// Parse the markdown file
if c.verbose {
fmt.Println(console.FormatInfoMessage("Parsing workflow file..."))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Parsing workflow file..."))
}
workflowData, err := c.ParseWorkflowFile(markdownPath)
if err != nil {
Expand All @@ -222,7 +222,7 @@ func (c *Compiler) CompileWorkflow(markdownPath string) error {

// Validate expression safety - check that all GitHub Actions expressions are in the allowed list
if c.verbose {
fmt.Println(console.FormatInfoMessage("Validating expression safety..."))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Validating expression safety..."))
}
if err := validateExpressionSafety(workflowData.MarkdownContent); err != nil {
formattedErr := console.FormatError(console.CompilerError{
Expand All @@ -240,12 +240,12 @@ func (c *Compiler) CompileWorkflow(markdownPath string) error {
// Note: Markdown content size is now handled by splitting into multiple steps in generatePrompt

if c.verbose {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Workflow name: %s", workflowData.Name)))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Workflow name: %s", workflowData.Name)))
if len(workflowData.Tools) > 0 {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Tools configured: %d", len(workflowData.Tools))))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Tools configured: %d", len(workflowData.Tools))))
}
if workflowData.AIReaction != "" {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("AI reaction configured: %s", workflowData.AIReaction)))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("AI reaction configured: %s", workflowData.AIReaction)))
}
}

Expand All @@ -270,7 +270,7 @@ func (c *Compiler) CompileWorkflow(markdownPath string) error {
// Validate against GitHub Actions schema (unless skipped)
if !c.skipValidation {
if c.verbose {
fmt.Println(console.FormatInfoMessage("Validating workflow against GitHub Actions schema..."))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Validating workflow against GitHub Actions schema..."))
}
if err := c.validateGitHubActionsSchema(yamlContent); err != nil {
formattedErr := console.FormatError(console.CompilerError{
Expand All @@ -285,14 +285,14 @@ func (c *Compiler) CompileWorkflow(markdownPath string) error {
// Write the invalid YAML to a .invalid.yml file for inspection
invalidFile := strings.TrimSuffix(lockFile, ".lock.yml") + ".invalid.yml"
if writeErr := os.WriteFile(invalidFile, []byte(yamlContent), 0644); writeErr == nil {
fmt.Println(console.FormatWarningMessage(fmt.Sprintf("Invalid workflow YAML written to: %s", console.ToRelativePath(invalidFile))))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Invalid workflow YAML written to: %s", console.ToRelativePath(invalidFile))))
}
return errors.New(formattedErr)
}

// Validate expression sizes
if c.verbose {
fmt.Println(console.FormatInfoMessage("Validating expression sizes..."))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Validating expression sizes..."))
}
if err := c.validateExpressionSizes(yamlContent); err != nil {
formattedErr := console.FormatError(console.CompilerError{
Expand All @@ -307,22 +307,22 @@ func (c *Compiler) CompileWorkflow(markdownPath string) error {
// Write the invalid YAML to a .invalid.yml file for inspection
invalidFile := strings.TrimSuffix(lockFile, ".lock.yml") + ".invalid.yml"
if writeErr := os.WriteFile(invalidFile, []byte(yamlContent), 0644); writeErr == nil {
fmt.Println(console.FormatWarningMessage(fmt.Sprintf("Invalid workflow YAML written to: %s", console.ToRelativePath(invalidFile))))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Invalid workflow YAML written to: %s", console.ToRelativePath(invalidFile))))
}
return errors.New(formattedErr)
}
} else if c.verbose {
fmt.Println(console.FormatWarningMessage("Schema validation available but skipped (use SetSkipValidation(false) to enable)"))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage("Schema validation available but skipped (use SetSkipValidation(false) to enable)"))
}

// Write to lock file (unless noEmit is enabled)
if c.noEmit {
if c.verbose {
fmt.Println(console.FormatInfoMessage("Validation completed - no lock file generated (--no-emit enabled)"))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Validation completed - no lock file generated (--no-emit enabled)"))
}
} else {
if c.verbose {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Writing output to: %s", console.ToRelativePath(lockFile))))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Writing output to: %s", console.ToRelativePath(lockFile))))
}
if err := os.WriteFile(lockFile, []byte(yamlContent), 0644); err != nil {
formattedErr := console.FormatError(console.CompilerError{
Expand Down Expand Up @@ -358,15 +358,15 @@ func (c *Compiler) CompileWorkflow(markdownPath string) error {

// Display success message with file size if we generated a lock file
if c.noEmit {
fmt.Println(console.FormatSuccessMessage(console.ToRelativePath(markdownPath)))
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(console.ToRelativePath(markdownPath)))
} else {
// Get the size of the generated lock file for display
if lockFileInfo, err := os.Stat(lockFile); err == nil {
lockSize := pretty.FormatFileSize(lockFileInfo.Size())
fmt.Println(console.FormatSuccessMessage(fmt.Sprintf("%s (%s)", console.ToRelativePath(markdownPath), lockSize)))
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("%s (%s)", console.ToRelativePath(markdownPath), lockSize)))
} else {
// Fallback to original display if we can't get file info
fmt.Println(console.FormatSuccessMessage(console.ToRelativePath(markdownPath)))
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(console.ToRelativePath(markdownPath)))
}
}
return nil
Expand Down Expand Up @@ -421,7 +421,7 @@ func (c *Compiler) validateGitHubActionsSchema(yamlContent string) error {
// ParseWorkflowFile parses a markdown workflow file and extracts all necessary data
func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error) {
if c.verbose {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Reading file: %s", console.ToRelativePath(markdownPath))))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Reading file: %s", console.ToRelativePath(markdownPath))))
}

// Read the file
Expand All @@ -431,7 +431,7 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error)
}

if c.verbose {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("File size: %d bytes", len(content))))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("File size: %d bytes", len(content))))
}

// Parse frontmatter and markdown
Expand Down Expand Up @@ -464,7 +464,7 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error)
}

if c.verbose {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Frontmatter: %d characters, Markdown content length: %d characters",
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Frontmatter: %d characters, Markdown content length: %d characters",
len(result.Frontmatter), len(result.Markdown))))
}

Expand Down Expand Up @@ -520,7 +520,7 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error)
if c.engineOverride != "" {
originalEngineSetting := engineSetting
if originalEngineSetting != "" && originalEngineSetting != c.engineOverride {
fmt.Println(console.FormatWarningMessage(fmt.Sprintf("Command line --engine %s overrides markdown file engine: %s", c.engineOverride, originalEngineSetting)))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Command line --engine %s overrides markdown file engine: %s", c.engineOverride, originalEngineSetting)))
}
engineSetting = c.engineOverride
}
Expand Down Expand Up @@ -563,7 +563,7 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error)
defaultEngine := c.engineRegistry.GetDefaultEngine()
engineSetting = defaultEngine.GetID()
if c.verbose {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("NOTE: No 'engine:' setting found, defaulting to: %s", engineSetting)))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("NOTE: No 'engine:' setting found, defaulting to: %s", engineSetting)))
}
// Create a default EngineConfig with the default engine ID if not already set
if engineConfig == nil {
Expand All @@ -585,11 +585,11 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error)
}

if c.verbose {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("AI engine: %s (%s)", agenticEngine.GetDisplayName(), engineSetting)))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("AI engine: %s (%s)", agenticEngine.GetDisplayName(), engineSetting)))
if agenticEngine.IsExperimental() {
fmt.Println(console.FormatWarningMessage(fmt.Sprintf("Using experimental engine: %s", agenticEngine.GetDisplayName())))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Using experimental engine: %s", agenticEngine.GetDisplayName())))
}
fmt.Println(console.FormatInfoMessage("Processing tools and includes..."))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Processing tools and includes..."))
}

// Extract SafeOutputs configuration early so we can use it when applying default tools
Expand Down Expand Up @@ -663,9 +663,9 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error)

if !agenticEngine.SupportsToolsAllowlist() {
// For engines that don't support tool allowlists (like codex), ignore tools section and provide warnings
fmt.Println(console.FormatWarningMessage(fmt.Sprintf("Using experimental %s support (engine: %s)", agenticEngine.GetDisplayName(), engineSetting)))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Using experimental %s support (engine: %s)", agenticEngine.GetDisplayName(), engineSetting)))
if _, hasTools := result.Frontmatter["tools"]; hasTools {
fmt.Println(console.FormatWarningMessage(fmt.Sprintf("'tools' section ignored when using engine: %s (%s doesn't support MCP tool allow-listing)", engineSetting, agenticEngine.GetDisplayName())))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("'tools' section ignored when using engine: %s (%s doesn't support MCP tool allow-listing)", engineSetting, agenticEngine.GetDisplayName())))
}
tools = map[string]any{}
// For now, we'll add a basic github tool (always uses docker MCP)
Expand Down Expand Up @@ -694,7 +694,7 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error)
}

if c.verbose {
fmt.Println(console.FormatInfoMessage("Expanded includes in markdown content"))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Expanded includes in markdown content"))
}

// Combine all included files (from tools and markdown)
Expand Down Expand Up @@ -726,7 +726,7 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error)
}

if c.verbose {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Extracted workflow name: '%s'", workflowName)))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Extracted workflow name: '%s'", workflowName)))
}

// Check if the markdown content uses the text output
Expand Down Expand Up @@ -1546,9 +1546,9 @@ func (c *Compiler) detectTextOutputUsage(markdownContent string) bool {
hasUsage := strings.Contains(markdownContent, "${{ needs.activation.outputs.text }}")
if c.verbose {
if hasUsage {
fmt.Println(console.FormatInfoMessage("Detected usage of activation.outputs.text - compute-text step will be included"))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Detected usage of activation.outputs.text - compute-text step will be included"))
} else {
fmt.Println(console.FormatInfoMessage("No usage of activation.outputs.text found - compute-text step will be skipped"))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("No usage of activation.outputs.text found - compute-text step will be skipped"))
}
}
return hasUsage
Expand Down
7 changes: 4 additions & 3 deletions pkg/workflow/custom_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package workflow

import (
"fmt"
"os"
"strings"
)

Expand Down Expand Up @@ -265,7 +266,7 @@ func (e *CustomEngine) ParseLogMetrics(logContent string, verbose bool) LogMetri
if claudeMetrics.Turns > 0 || claudeMetrics.TokenUsage > 0 || claudeMetrics.EstimatedCost > 0 {
// Found structured data, use Claude parsing
if verbose {
fmt.Println("Custom engine: Using Claude-style parsing for logs")
fmt.Fprintln(os.Stderr, "Custom engine: Using Claude-style parsing for logs")
}
return claudeMetrics
}
Expand All @@ -278,15 +279,15 @@ func (e *CustomEngine) ParseLogMetrics(logContent string, verbose bool) LogMetri
if codexMetrics.Turns > 0 || codexMetrics.TokenUsage > 0 {
// Found some data, use Codex parsing
if verbose {
fmt.Println("Custom engine: Using Codex-style parsing for logs")
fmt.Fprintln(os.Stderr, "Custom engine: Using Codex-style parsing for logs")
}
return codexMetrics
}
}

// Fall back to basic parsing if neither Claude nor Codex approaches work
if verbose {
fmt.Println("Custom engine: Using basic fallback parsing for logs")
fmt.Fprintln(os.Stderr, "Custom engine: Using basic fallback parsing for logs")
}

lines := strings.Split(logContent, "\n")
Expand Down
7 changes: 4 additions & 3 deletions pkg/workflow/mcp-config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package workflow

import (
"fmt"
"os"
"sort"
"strings"

Expand Down Expand Up @@ -121,7 +122,7 @@ func renderSharedMCPConfig(yaml *strings.Builder, toolName string, toolConfig ma
case "http":
if renderer.Format == "toml" {
// TOML format doesn't support HTTP type in some engines
fmt.Println(console.FormatWarningMessage(fmt.Sprintf("Custom MCP server '%s' has type '%s', but %s only supports 'stdio'. Ignoring this server.", toolName, mcpType, renderer.Format)))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Custom MCP server '%s' has type '%s', but %s only supports 'stdio'. Ignoring this server.", toolName, mcpType, renderer.Format)))
return nil
} else {
// JSON format - include copilot fields if required
Expand All @@ -133,9 +134,9 @@ func renderSharedMCPConfig(yaml *strings.Builder, toolName string, toolConfig ma
}
default:
if renderer.Format == "toml" {
fmt.Println(console.FormatWarningMessage(fmt.Sprintf("Custom MCP server '%s' has unsupported type '%s'. Supported types: stdio", toolName, mcpType)))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Custom MCP server '%s' has unsupported type '%s'. Supported types: stdio", toolName, mcpType)))
} else {
fmt.Println(console.FormatWarningMessage(fmt.Sprintf("Custom MCP server '%s' has unsupported type '%s'. Supported types: stdio, http", toolName, mcpType)))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Custom MCP server '%s' has unsupported type '%s'. Supported types: stdio, http", toolName, mcpType)))
}
return nil
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/workflow/stop_after.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (c *Compiler) processStopAfterConfiguration(frontmatter map[string]any, wor
// Preserve existing stop time during recompilation
workflowData.StopTime = existingStopTime
if c.verbose {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Preserving existing stop time from lock file: %s", existingStopTime)))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Preserving existing stop time from lock file: %s", existingStopTime)))
}
} else {
// First compilation or no existing stop time, generate new one
Expand All @@ -66,9 +66,9 @@ func (c *Compiler) processStopAfterConfiguration(frontmatter map[string]any, wor
workflowData.StopTime = resolvedStopTime

if c.verbose && isRelativeStopTime(originalStopTime) {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Resolved relative stop-after to: %s", resolvedStopTime)))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Resolved relative stop-after to: %s", resolvedStopTime)))
} else if c.verbose && originalStopTime != resolvedStopTime {
fmt.Println(console.FormatInfoMessage(fmt.Sprintf("Parsed absolute stop-after from '%s' to: %s", originalStopTime, resolvedStopTime)))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Parsed absolute stop-after from '%s' to: %s", originalStopTime, resolvedStopTime)))
}
}
}
Expand Down
Loading