MCPcopy Create free account
hub / github.com/github/gh-aw / ParseWorkflowString

Method ParseWorkflowString

pkg/workflow/compiler_string_api.go:57–225  ·  view source on GitHub ↗

ParseWorkflowString parses workflow markdown content from a string rather than a file. This is the primary entry point for Wasm/browser usage where filesystem access is unavailable. The virtualPath is used for error messages and lock file naming (e.g., "workflow.md").

(content string, virtualPath string)

Source from the content-addressed store, hash-verified

55// This is the primary entry point for Wasm/browser usage where filesystem access is unavailable.
56// The virtualPath is used for error messages and lock file naming (e.g., "workflow.md").
57func (c *Compiler) ParseWorkflowString(content string, virtualPath string) (*WorkflowData, error) {
58 workflowLog.Printf("ParseWorkflowString: parsing %d bytes with virtual path %s", len(content), virtualPath)
59
60 cleanPath := filepath.Clean(virtualPath)
61 contentBytes := []byte(content)
62
63 // Store content so downstream code can use it instead of reading from disk.
64 // Cleared in CompileToYAML after compilation completes.
65 c.contentOverride = content
66
67 // Enable inline prompt mode for string-based compilation (Wasm/browser)
68 // since runtime-import macros cannot resolve without filesystem access
69 c.inlinePrompt = true
70
71 // Parse frontmatter directly from content string
72 result, err := parser.ExtractFrontmatterFromContent(content)
73 if err != nil {
74 frontmatterStart := 2
75 if result != nil && result.FrontmatterStart > 0 {
76 frontmatterStart = result.FrontmatterStart
77 }
78 return nil, c.createFrontmatterError(cleanPath, content, err, frontmatterStart)
79 }
80
81 if len(result.Frontmatter) == 0 {
82 return nil, errors.New("no frontmatter found")
83 }
84
85 compilerStringAPILog.Printf("ParseWorkflowString: extracted frontmatter with %d fields", len(result.Frontmatter))
86
87 // Preprocess schedule fields
88 if err := c.preprocessScheduleFields(result.Frontmatter, cleanPath, content); err != nil {
89 return nil, err
90 }
91
92 frontmatterForValidation := c.copyFrontmatterWithoutInternalMarkers(result.Frontmatter)
93
94 // Check if "on" field is missing - distinguish redirect-only placeholders from shared workflows
95 _, hasOnField := frontmatterForValidation["on"]
96 if !hasOnField {
97 // Check if this is a redirect-only placeholder (has redirect field but no 'on' trigger).
98 // Redirect-only files are distinct from regular shared workflows: they are placeholders
99 // pointing to a workflow's new canonical location and should not be treated as importable components.
100 if redirectVal, hasRedirect := frontmatterForValidation["redirect"]; hasRedirect {
101 if redirectStr, ok := redirectVal.(string); ok {
102 if redirectTarget := strings.TrimSpace(redirectStr); redirectTarget != "" {
103 compilerStringAPILog.Printf("ParseWorkflowString: redirect-only workflow detected: redirect=%s", redirectTarget)
104 return nil, &RedirectOnlyWorkflowError{Path: cleanPath, Target: redirectTarget}
105 }
106 }
107 }
108 compilerStringAPILog.Printf("ParseWorkflowString: no 'on' field, treating as shared workflow: %s", cleanPath)
109 return nil, &SharedWorkflowError{Path: cleanPath}
110 }
111
112 if err := c.validateEngineBeforeSchema(cleanPath, contentBytes, result, frontmatterForValidation); err != nil {
113 compilerStringAPILog.Printf("ParseWorkflowString: string engine pre-validation failed for %s", cleanPath)
114 return nil, err