In Chapter 6: MCP Server Bridge, we gave our AI agent "hands" to run commands and fetch data. The agent can now check statuses, read logs, and perform audits.
However, the agent and the system communicate in raw data (JSON, structs, and error codes). While machines love raw data, humans do not.
If you run a command and get a wall of unformatted white text, you might miss a critical error. If you get a JSON blob, it's hard to read at a glance.
This brings us to the Console UI & Formatting layer.
Think of this layer as the Waiter in a restaurant.
Why is this important? GitHub Actions logs usually don't support advanced cursor movements or interactive prompts. But your local terminal does. The Console UI detects where it is running and adapts automatically.
Let's look at a concrete example. You made a mistake in your workflow file: you typed engine: c3po instead of copilot.
The system might just crash with:
Error: invalid engine 'c3po' at line 4.
The system treats you like a human. It highlights the mistake visually.
/path/to/workflow.md:4:10: error: invalid engine value
3 | name: Fixer
4 | engine: c3po
| ^^^^
This is called "Rust-like" error formatting, and our Console UI handles this rendering logic so the rest of the app doesn't have to worry about it.
How does the system decide how to dress up the text?
The application logic just says "I succeeded." The Console package figures out if that should look like โ Success! (green) or Success! (plain).
Let's look at how this is implemented in pkg/console/console.go.
The foundation of the entire system is knowing where we are running.
// From pkg/console/console.go
// isTTY checks if stdout is a terminal
func isTTY() bool {
// Uses a helper to check file descriptors
return tty.IsStdoutTerminal()
}
true. If it's being piped to a file or running in GitHub Actions, it returns false.We use a library called Lipgloss to handle colors. But we wrap it to be safe.
// applyStyle conditionally applies styling
func applyStyle(style lipgloss.Style, text string) string {
// 1. If we are a human, render with colors
if isTTY() {
return style.Render(text)
}
// 2. If we are a robot, return plain text
return text
}
isTTY() is true, style.Render wraps the text in ANSI color codes. If not, we return the raw text. This ensures your CI logs aren't filled with garbage characters like \033[31m.To keep the UI consistent, we create helper functions for common messages.
// FormatSuccessMessage adds a green checkmark
func FormatSuccessMessage(message string) string {
// styles.Success is defined as Green color
return applyStyle(styles.Success, "โ ") + message
}
// FormatErrorMessage adds a red X
func FormatErrorMessage(message string) string {
// styles.Error is defined as Red color
return applyStyle(styles.Error, "โ ") + message
}
console.FormatSuccessMessage("Done").Printing a list of workflows isn't helpful if the columns don't align. The Console UI includes a table renderer.
The system uses a configuration struct to define the table data.
type TableConfig struct {
Headers []string
Rows [][]string
Title string
// ...
}
In pkg/console/console.go, the RenderTable function handles the complexity of calculating column widths.
func RenderTable(config TableConfig) string {
// 1. Create a new Lipgloss table
t := table.New().
Headers(config.Headers...).
Rows(config.Rows...).
Border(styles.RoundedBorder) // Use pretty rounded corners
// 2. Return the string representation
return t.String()
}
โญโโโโโโโโโโโโโฌโโโโโโโโโโโฎ
โ Workflow โ Status โ
โโโโโโโโโโโโโโผโโโโโโโโโโโค
โ Triage โ Active โ
โ CI Doctor โ Disabled โ
โฐโโโโโโโโโโโโโดโโโโโโโโโโโฏ
If isTTY() was false, it would render a simpler version without the fancy rounded border characters, ensuring compatibility with older log viewers.
The most complex part of the Console UI is FormatError. This function takes an error and the source code, and draws arrows pointing to the problem.
type CompilerError struct {
Position ErrorPosition // File, Line, Column
Message string
Context []string // The actual code lines
}
The rendering logic acts like a mini-painter:
func renderContext(err CompilerError) string {
var output strings.Builder
// Loop through the code lines
for _, line := range err.Context {
// ... (logic to print line numbers) ...
// Highlight the specific bad word
if lineNum == err.Position.Line {
output.WriteString(applyStyle(styles.Highlight, line))
}
}
return output.String()
}
1 | vs 10 |), highlights the specific line causing the error, and draws a pointer ^^^^ under the specific column if provided.The Console UI & Formatting layer is the face of the GitHub Agentic Workflows project. It abstracts away the complexity of terminal codes, ensuring that:
It unifies the "Look and Feel" of the application. Whether you are seeing a success message, a table of data, or a compilation error, the style remains consistent because it all passes through this single package.
You have now completed the entire architecture tutorial for GitHub Agentic Workflows!
You are now ready to contribute to the project or build your own Agentic Workflows!
Generated by Code IQ