Repository: Argus-Labs/world-cli
Pull request #154: refactor: Replace fmt.Print with printer package for consistent output
Author: login: AnthonyWadham
Created: 2025-05-06T14:48:47Z | Merged: 2025-05-06T15:37:18Z

Closes: PLAT-362

feat: Implement consistent printer package for CLI output

## Overview

This PR introduces a new `printer` package to standardize CLI output formatting across the World CLI. The package provides consistent methods for displaying information, success messages, errors, headers, and notifications with appropriate styling.

## Brief Changelog

- Added new `common/printer` package with standardized output functions
- Replaced direct `fmt` calls with printer package methods throughout the codebase
- Enabled the `forbidigo` linter to catch future direct usage of `fmt` functions
- Improved visual consistency of CLI output with proper styling for different message types
- Added support for section dividers and multiple newlines
- Standardized formatting of headers, success messages, and error messages

## Testing and Verifying

This change is a rework of output formatting without changing functionality, and can be verified by running various CLI commands and observing consistent output styling.

<!-- greptile_comment -->

## Greptile Summary

Standardized CLI output formatting across the World CLI by introducing a new printer package and replacing direct fmt calls.

- Added new `common/printer` package with styled output functions (Success, Error, Info, Header, Notification) using lipgloss and logsymbols
- Replaced direct fmt.Print/Println calls with printer package methods across cardinal, forge, and evm packages
- Enabled forbidigo linter in .golangci.yaml to catch direct fmt usage and enforce consistent output formatting
- Added support for section dividers and multiple newlines through printer.SectionDivider and printer.NewLine
- Some fmt.Sprintf calls remain for string formatting rather than direct output, which is acceptable



<!-- /greptile_comment -->


Diff:
diff --git a/.golangci.yaml b/.golangci.yaml
index 5e6ec478..5cad0e63 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -20,7 +20,7 @@ linters:
     - errorlint
     - exhaustive
     - fatcontext
-    # - forbidigo
+    - forbidigo
     - funlen
     - gocheckcompilerdirectives
     # - gochecknoglobals
diff --git a/cmd/world/cardinal/build.go b/cmd/world/cardinal/build.go
index 5c4b9330..03d0ed9d 100644
--- a/cmd/world/cardinal/build.go
+++ b/cmd/world/cardinal/build.go
@@ -10,6 +10,7 @@ import (
 	"golang.org/x/sync/errgroup"
 	"pkg.world.dev/world-cli/common/config"
 	"pkg.world.dev/world-cli/common/docker"
+	"pkg.world.dev/world-cli/common/printer"
 	"pkg.world.dev/world-cli/tea/style"
 )
 
@@ -74,14 +75,14 @@ optimized for deployment. You can optionally push the image to a registry with t
 		}
 
 		// Print out header
-		fmt.Println(style.CLIHeader("Cardinal", ""))
+		printer.Infoln(style.CLIHeader("Cardinal", ""))
 		// Print out service addresses
 		printServiceAddress("Redis", cfg.DockerEnv["REDIS_ADDRESS"])
 		// this can be changed in code by calling WithPort() on world options, but we have no way to detect that
 		printServiceAddress("Cardinal", fmt.Sprintf("localhost:%s", CardinalPort))
-		fmt.Println()
-		fmt.Println("\nBuilding Cardinal game shard image...")
-		fmt.Println("This may take a few minutes.")
+		printer.NewLine(2)
+		printer.Infoln("Building Cardinal game shard image...")
+		printer.Infoln("This may take a few minutes.")
 
 		group, ctx := errgroup.WithContext(cmd.Context())
 
diff --git a/cmd/world/cardinal/dev.go b/cmd/world/cardinal/dev.go
index 95f07dbb..82784d89 100644
--- a/cmd/world/cardinal/dev.go
+++ b/cmd/world/cardinal/dev.go
@@ -20,6 +20,7 @@ import (
 	"pkg.world.dev/world-cli/common/docker"
 	"pkg.world.dev/world-cli/common/docker/service"
 	"pkg.world.dev/world-cli/common/logger"
+	"pkg.world.dev/world-cli/common/printer"
 	"pkg.world.dev/world-cli/tea/style"
 )
 
@@ -62,7 +63,7 @@ This mode runs Cardinal directly from your source code, providing:
 		}
 
 		// Print out header
-		fmt.Println(style.CLIHeader("Cardinal", ""))
+		printer.Infoln(style.CLIHeader("Cardinal", ""))
 
 		// Print out service addresses
 		printServiceAddress("Redis", fmt.Sprintf("localhost:%s", RedisPort))
@@ -77,7 +78,7 @@ This mode runs Cardinal directly from your source code, providing:
 		} else {
 			printServiceAddress("Cardinal Editor", "[disabled]")
 		}
-		fmt.Println()
+		printer.NewLine(1)
 
 		// Start redis, cardinal, and cardinal editor
 		// If any of the services terminates, the entire group will be terminated.
@@ -127,8 +128,9 @@ func init() {
 
 // Otherwise, it runs cardinal using `go run .`.
 func startCardinalDevMode(ctx context.Context, cfg *config.Config, prettyLog bool) error { //nolint:gocognit
-	fmt.Println("Starting Cardinal...")
-	fmt.Println(style.BoldText.Render("Press Ctrl+C to stop\n"))
+	printer.Infoln("Starting Cardinal...")
+	printer.Infoln(style.BoldText.Render("Press Ctrl+C to stop"))
+	printer.NewLine(1)
 
 	// Check and wait until Redis is running and is available in the expected port
 	isRedisHealthy := false
@@ -284,5 +286,5 @@ func printServiceAddress(service string, address string) {
 	serviceStr := lipgloss.NewStyle().Foreground(lipgloss.Color("2")).Render(service)
 	arrowStr := lipgloss.NewStyle().Foreground(lipgloss.Color("7")).Render(" → ")
 	addressStr := lipgloss.NewStyle().Render(address)
-	fmt.Println(serviceStr + arrowStr + addressStr)
+	printer.Infoln(serviceStr + arrowStr + addressStr)
 }
diff --git a/cmd/world/cardinal/purge.go b/cmd/world/cardinal/purge.go
index bcdff5ac..c5859c10 100644
--- a/cmd/world/cardinal/purge.go
+++ b/cmd/world/cardinal/purge.go
@@ -1,12 +1,11 @@
 package cardinal
 
 import (
-	"fmt"
-
 	"github.com/spf13/cobra"
 	"pkg.world.dev/world-cli/common/config"
 	"pkg.world.dev/world-cli/common/docker"
 	"pkg.world.dev/world-cli/common/docker/service"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 /////////////////
@@ -41,7 +40,7 @@ from scratch or resolve persistent state issues.`,
 		if err != nil {
 			return err
 		}
-		fmt.Println("Cardinal successfully purged")
+		printer.Infoln("Cardinal successfully purged")
 
 		return nil
 	},
diff --git a/cmd/world/cardinal/start.go b/cmd/world/cardinal/start.go
index d8c9ef50..9e9a3a07 100644
--- a/cmd/world/cardinal/start.go
+++ b/cmd/world/cardinal/start.go
@@ -13,6 +13,7 @@ import (
 	"pkg.world.dev/world-cli/common"
 	"pkg.world.dev/world-cli/common/config"
 	"pkg.world.dev/world-cli/common/docker"
+	"pkg.world.dev/world-cli/common/printer"
 	"pkg.world.dev/world-cli/tea/style"
 )
 
@@ -111,7 +112,7 @@ This will start the following Docker services and their dependencies:
 		}
 
 		// Print out header
-		fmt.Println(style.CLIHeader("Cardinal", ""))
+		printer.Infoln(style.CLIHeader("Cardinal", ""))
 
 		// Print out service addresses
 		printServiceAddress("Redis", cfg.DockerEnv["REDIS_ADDRESS"])
@@ -127,14 +128,15 @@ This will start the following Docker services and their dependencies:
 		} else {
 			printServiceAddress("Cardinal Editor", "[disabled]")
 		}
-		fmt.Println()
+		printer.NewLine(1)
 
-		fmt.Print("Press <ENTER> to continue...")
+		printer.Info("Press <ENTER> to continue...")
 		_, _ = bufio.NewReader(os.Stdin).ReadBytes('\n')
 
-		fmt.Println("\nStarting Cardinal game shard...")
-		fmt.Println("This may take a few minutes to rebuild the Docker images.")
-		fmt.Println("Use `world cardinal dev` to run Cardinal faster/easier in development mode.")
+		printer.NewLine(1)
+		printer.Infoln("Starting Cardinal game shard...")
+		printer.Infoln("This may take a few minutes to rebuild the Docker images.")
+		printer.Infoln("Use `world cardinal dev` to run Cardinal faster/easier in development mode.")
 
 		group, ctx := errgroup.WithContext(cmd.Context())
 
diff --git a/cmd/world/cardinal/stop.go b/cmd/world/cardinal/stop.go
index 23d5c6a8..2a3b0788 100644
--- a/cmd/world/cardinal/stop.go
+++ b/cmd/world/cardinal/stop.go
@@ -1,12 +1,11 @@
 package cardinal
 
 import (
-	"fmt"
-
 	"github.com/spf13/cobra"
 	"pkg.world.dev/world-cli/common/config"
 	"pkg.world.dev/world-cli/common/docker"
 	"pkg.world.dev/world-cli/common/docker/service"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 /////////////////
@@ -46,7 +45,7 @@ need to free up system resources.`,
 			return err
 		}
 
-		fmt.Println("Cardinal successfully stopped")
+		printer.Successln("Cardinal successfully stopped")
 
 		return nil
 	},
diff --git a/cmd/world/evm/start.go b/cmd/world/evm/start.go
index 8da642cd..d82777c4 100644
--- a/cmd/world/evm/start.go
+++ b/cmd/world/evm/start.go
@@ -3,7 +3,6 @@ package evm
 import (
 	"context"
 	"errors"
-	"fmt"
 	"net"
 
 	"github.com/rotisserie/eris"
@@ -12,6 +11,7 @@ import (
 	"pkg.world.dev/world-cli/common/docker"
 	"pkg.world.dev/world-cli/common/docker/service"
 	"pkg.world.dev/world-cli/common/logger"
+	"pkg.world.dev/world-cli/common/printer"
 	"pkg.world.dev/world-cli/common/teacmd"
 )
 
@@ -146,7 +146,7 @@ func validateDALayer(cmd *cobra.Command, cfg *config.Config, dockerClient *docke
 }
 
 func getDAToken(ctx context.Context, cfg *config.Config, dockerClient *docker.Client) (string, error) {
-	fmt.Println("Getting DA token")
+	printer.Infoln("Getting DA token")
 
 	containerName := service.CelestiaDevNet(cfg)
 
diff --git a/cmd/world/evm/stop.go b/cmd/world/evm/stop.go
index b1c105eb..d366a2a1 100644
--- a/cmd/world/evm/stop.go
+++ b/cmd/world/evm/stop.go
@@ -1,12 +1,11 @@
 package evm
 
 import (
-	"fmt"
-
 	"github.com/spf13/cobra"
 	"pkg.world.dev/world-cli/common/config"
 	"pkg.world.dev/world-cli/common/docker"
 	"pkg.world.dev/world-cli/common/docker/service"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 var stopCmd = &cobra.Command{
@@ -35,7 +34,7 @@ done working with your EVM environment.`,
 			return err
 		}
 
-		fmt.Println("EVM successfully stopped")
+		printer.Infoln("EVM successfully stopped")
 		return nil
 	},
 }
diff --git a/cmd/world/forge/common.go b/cmd/world/forge/common.go
index 4d5027cf..49847960 100644
--- a/cmd/world/forge/common.go
+++ b/cmd/world/forge/common.go
@@ -26,6 +26,7 @@ import (
 	"github.com/tidwall/gjson"
 	"golang.org/x/term"
 	"pkg.world.dev/world-cli/common/globalconfig"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 const (
@@ -60,22 +61,22 @@ var openBrowser = func(url string) error {
 	case "darwin":
 		err = exec.Command("open", url).Start()
 	default:
-		fmt.Printf("Could not automatically open browser. Please visit this URL:\n%s\n", url)
+		printer.Infof("Could not automatically open browser. Please visit this URL:\n%s\n", url)
 	}
 	if err != nil {
-		fmt.Printf("Failed to open browser automatically. Please visit this URL:\n%s\n", url)
+		printer.Infof("Failed to open browser automatically. Please visit this URL:\n%s\n", url)
 	}
 	return nil
 }
 
 var getInput = func(prompt, defaultStr string) string {
 	if prompt != "" {
-		fmt.Print(prompt)
+		printer.Info(prompt)
 	}
 	if defaultStr != "" {
-		fmt.Printf(" [%s]: ", defaultStr)
+		printer.Infof(" [%s]: ", defaultStr)
 	} else {
-		fmt.Print(": ")
+		printer.Info(": ")
 	}
 	reader := bufio.NewReader(os.Stdin)
 	input, _ := reader.ReadString('\n') // only returns error if input doesn't end in delimiter
@@ -83,8 +84,8 @@ var getInput = func(prompt, defaultStr string) string {
 	if input == "" && defaultStr != "" {
 		// display the default value as if they typed it in
 		promptEnd := len(defaultStr) + 4 + len(prompt)
-		fmt.Printf("\033[1A\033[%dC", promptEnd) // move cursor up one line, and right the length of the prompt
-		fmt.Printf("%s\n", defaultStr)
+		printer.Infof("\033[1A\033[%dC", promptEnd) // move cursor up one line, and right the length of the prompt
+		printer.Infoln(defaultStr)
 		return defaultStr
 	}
 	return input
@@ -164,8 +165,8 @@ func makeRequestWithRetries(ctx context.Context, req *http.Request) ([]byte, err
 			}
 
 			if i < maxRetries-1 { // Don't print retry message on last attempt
-				fmt.Printf("Failed to make request [%s]: %s\n", req.URL, err.Error())
-				fmt.Println("Retrying...")
+				printer.Errorf("Failed to make request [%s]: %s\n", req.URL, err.Error())
+				printer.Infoln("Retrying...")
 
 				// Apply exponential backoff with jitter
 				delay := exponentialBackoffWithJitter(baseDelay, i)
@@ -252,38 +253,43 @@ func parseResponse[T any](body []byte) (*T, error) {
 }
 
 func printNoOrganizations() {
-	fmt.Println("\n   No Organizations Found")
-	fmt.Println("============================")
-	fmt.Println("\nYou don't have any organizations yet.")
-	fmt.Println("\nUse 'world forge organization create' to create one.")
+	printer.NewLine(1)
+	printer.Headerln("   No Organizations Found   ")
+	printer.Infoln("You don't have any organizations yet.")
+	printer.NewLine(1)
+	printer.Infoln("Use 'world forge organization create' to create one.")
 }
 
 func printNoSelectedOrganization() {
-	fmt.Println("\n   No Organization Selected")
-	fmt.Println("==============================")
-	fmt.Println("\nYou don't have any organization selected.")
-	fmt.Println("\nUse 'world forge organization switch' to select one")
+	printer.NewLine(1)
+	printer.Headerln("   No Organization Selected   ")
+	printer.Infoln("You don't have any organization selected.")
+	printer.NewLine(1)
+	printer.Infoln("Use 'world forge organization switch' to select one")
 }
 
 func printNoSelectedProject() {
-	fmt.Println("\n   No Project Selected")
-	fmt.Println("=========================")
-	fmt.Println("\nYou don't have any project selected.")
-	fmt.Println("\nUse 'world forge project switch' to select one")
+	printer.NewLine(1)
+	printer.Headerln("   No Project Selected   ")
+	printer.Infoln("You don't have any project selected.")
+	printer.NewLine(1)
+	printer.Infoln("Use 'world forge project switch' to select one")
 }
 
 func printNoProjectsInOrganization() {
-	fmt.Println("\n   No Projects Found")
-	fmt.Println("=======================")
-	fmt.Println("\nYou don't have any projects in this organization yet.")
-	fmt.Println("\nUse 'world forge project create' to create your first project!")
+	printer.NewLine(1)
+	printer.Headerln("   No Projects Found   ")
+	printer.Infoln("You don't have any projects in this organization yet.")
+	printer.NewLine(1)
+	printer.Infoln("Use 'world forge project create' to create your first project!")
 }
 
 func printAuthenticationRequired() {
-	fmt.Println("\n   Authentication Required")
-	fmt.Println("=============================")
-	fmt.Println("\nYou are not currently logged in")
-	fmt.Println("\nUse 'world login' to authenticate")
+	printer.NewLine(1)
+	printer.Headerln("   Authentication Required   ")
+	printer.Infoln("You are not currently logged in")
+	printer.NewLine(1)
+	printer.Infoln("Use 'world login' to authenticate")
 }
 
 func isAlphanumeric(s string) bool {
@@ -442,8 +448,8 @@ func GetCurrentConfigWithContext(ctx context.Context) (*globalconfig.GlobalConfi
 			baseURL, url.QueryEscape(currConfig.CurrRepoURL), url.QueryEscape(currConfig.CurrRepoPath))
 		body, err := sendRequest(ctx, http.MethodGet, deployURL, nil)
 		if err != nil {
-			fmt.Println("⚠️ Warning: Failed to lookup World Forge project for Git Repo",
-				currConfig.CurrRepoURL, "and path", currConfig.CurrRepoPath, ":", err)
+			printer.Infof("⚠️ Warning: Failed to lookup World Forge project for Git Repo %s and path %s: %v\n",
+				currConfig.CurrRepoURL, currConfig.CurrRepoPath, err)
 			return &currConfig, err
 		}
 
@@ -451,7 +457,7 @@ func GetCurrentConfigWithContext(ctx context.Context) (*globalconfig.GlobalConfi
 		proj, err := parseResponse[project](body)
 		if err != nil && err.Error() != "Missing data field in response" {
 			// missing data field in response just means nothing was found
-			fmt.Println("⚠️ Warning: Failed to parse project lookup response: ", err)
+			printer.Infof("⚠️ Warning: Failed to parse project lookup response: %v\n", err)
 			return &currConfig, err
 		}
 		if proj != nil {
@@ -466,7 +472,7 @@ func GetCurrentConfigWithContext(ctx context.Context) (*globalconfig.GlobalConfi
 			// save the config, but don't change the default ProjectID & OrgID
 			err := globalconfig.SaveGlobalConfig(currConfig)
 			if err != nil {
-				fmt.Println("⚠️ Warning: Failed to save config: ", err)
+				printer.Infof("⚠️ Warning: Failed to save config: %v\n", err)
 				// continue on, this is not fatal
 			}
 			// now return a copy of it with the looked up ProjectID and OrganizationID set
diff --git a/cmd/world/forge/deployment.go b/cmd/world/forge/deployment.go
index 0385b4d4..b36f34a1 100644
--- a/cmd/world/forge/deployment.go
+++ b/cmd/world/forge/deployment.go
@@ -10,6 +10,7 @@ import (
 	"time"
 
 	"github.com/rotisserie/eris"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 const (
@@ -62,7 +63,7 @@ func deployment(ctx context.Context, deployType string) error {
 			return eris.Wrap(err, "Failed on deployment to get selected organization")
 		}
 
-		fmt.Printf("Deploy requires a project created in World Forge: %s\n", org.Name)
+		printer.Infof("Deploy requires a project created in World Forge: %s\n", org.Name)
 
 		pID, err := createProject(ctx)
 		if err != nil {
@@ -78,20 +79,22 @@ func deployment(ctx context.Context, deployType string) error {
 	}
 
 	// prompt user to confirm deployment
-	fmt.Println("\n   Confirm Deployment")
-	fmt.Println("========================")
-	fmt.Println("\nReview the deployment details above.")
+	printer.NewLine(1)
+	printer.Headerln("   Confirm Deployment")
+	printer.Infoln("Review the deployment details above.")
 	prompt := fmt.Sprintf("\nDo you want to proceed with the %s? (Y/n): ", processTitle[deployType])
 
 	confirmation := getInput(prompt, "n")
 
 	if confirmation != "Y" {
 		if confirmation == "y" {
-			fmt.Println("You need to put Y (uppercase) to confirm deployment")
-			fmt.Println("\n❌ Deployment cancelled")
+			printer.Infoln("You need to put Y (uppercase) to confirm deployment")
+			printer.NewLine(1)
+			printer.Errorln("Deployment cancelled")
 			return nil
 		}
-		fmt.Println("\n❌ Deployment cancelled")
+		printer.NewLine(1)
+		printer.Errorln("Deployment cancelled")
 		return nil
 	}
 
@@ -104,9 +107,11 @@ func deployment(ctx context.Context, deployType string) error {
 		return eris.Wrap(err, fmt.Sprintf("Failed to %s project", deployType))
 	}
 
-	fmt.Printf("\nYour %s is being processed!\n", deployType)
-	fmt.Printf("\nTo check the status of your %s, run:\n", deployType)
-	fmt.Println("  $ 'world status'")
+	printer.NewLine(1)
+	printer.Successf("Your %s is being processed!\n", deployType)
+	printer.NewLine(1)
+	printer.Infof("To check the status of your %s, run:\n", deployType)
+	printer.Infoln("  $ 'world status'")
 
 	return nil
 }
@@ -149,13 +154,14 @@ func status(ctx context.Context) error {
 			return eris.New("Failed to unmarshal deployment data")
 		}
 	}
-	fmt.Println(" Deployment Status")
-	fmt.Println("-------------------")
-	fmt.Printf("Project:      %s\n", prj.Name)
-	fmt.Printf("Project Slug: %s\n", prj.Slug)
-	fmt.Printf("Repository:   %s\n", prj.RepoURL)
+	printer.Infoln(" Deployment Status ")
+	printer.SectionDivider("-", 19)
+	printer.Infof("Project:      %s\n", prj.Name)
+	printer.Infof("Project Slug: %s\n", prj.Slug)
+	printer.Infof("Repository:   %s\n", prj.RepoURL)
 	if len(envMap) == 0 {
-		fmt.Printf("\n** Project has not been deployed **\n")
+		printer.NewLine(1)
+		printer.Infoln("** Project has not been deployed **")
 		return nil
 	}
 	checkHealth := false
@@ -233,32 +239,32 @@ func status(ctx context.Context) error {
 
 			switch buildState {
 			case DeploymentStatusPassed:
-				fmt.Printf("✅ Build:     [%s] #%d (duration %s) completed %s (%s ago) by %s\n",
+				printer.Successf("Build:     [%s] #%d (duration %s) completed %s (%s ago) by %s\n",
 					strings.ToUpper(env), buildNumber,
 					formattedDuration(buildDuration),
 					bet.Format(time.RFC822), formattedDuration(time.Since(bet)), executorID)
 				shouldShowHealth[env] = true
 			case DeploymentStatusFailed:
-				fmt.Printf("❌ Build:     [%s] #%d (duration %s) failed at %s (%s ago)\n",
+				printer.Errorf("Build:     [%s] #%d (duration %s) failed at %s (%s ago)\n",
 					strings.ToUpper(env), buildNumber, formattedDuration(buildDuration),
 					bet.Format(time.RFC822), formattedDuration(time.Since(bet)))
 			default:
-				fmt.Printf("🔄 Build:     [%s] #%d started %s (%s ago) by %s - %s\n",
+				printer.Infof("🔄 Build:     [%s] #%d started %s (%s ago) by %s - %s\n",
 					strings.ToUpper(env), buildNumber,
 					bst.Format(time.RFC822), formattedDuration(time.Since(bst)), executorID, buildState)
 			}
 		case DeploymentTypeDestroy:
 			switch buildState {
 			case DeploymentStatusPassed:
-				fmt.Printf("✅ Destroyed: [%s] on %s by %s\n",
+				printer.Successf("Destroyed: [%s] on %s by %s\n",
 					strings.ToUpper(env), dt.Format(time.RFC822), executorID)
 			case DeploymentStatusFailed:
-				fmt.Printf("❌ Destroy:   [%s] failed on %s by %s\n",
+				printer.Errorf("Destroy:   [%s] failed on %s by %s\n",
 					strings.ToUpper(env), dt.Format(time.RFC822), executorID)
 				// if destroy failed, continue on to show health
 				shouldShowHealth[env] = true
 			default:
-				fmt.Printf("🔄 Destroy:   [%s] started %s (%s ago) by %s - %s\n",
+				printer.Infof("🔄 Destroy:   [%s] started %s (%s ago) by %s - %s\n",
 					strings.ToUpper(env), dt.Format(time.RFC822),
 					formattedDuration(time.Since(dt)), executorID, buildState)
 			}
@@ -266,16 +272,16 @@ func status(ctx context.Context) error {
 			// results can be "passed" or "failed", but either way continue to show the health
 			switch buildState {
 			case DeploymentStatusPassed:
-				fmt.Printf("✅ Reset:     [%s] on %s by %s\n",
+				printer.Successf("Reset:     [%s] on %s by %s\n",
 					strings.ToUpper(env), dt.Format(time.RFC822), executorID)
 				shouldShowHealth[env] = true
 			case DeploymentStatusFailed:
-				fmt.Printf("❌ Reset:     [%s] failed on %s by %s\n",
+				printer.Errorf("Reset:     [%s] failed on %s by %s\n",
 					strings.ToUpper(env), dt.Format(time.RFC822), executorID)
 				// if destroy failed, continue on to show health
 				shouldShowHealth[env] = true
 			default:
-				fmt.Printf("🔄 Reset:     [%s] started %s (%s ago) by %s - %s\n",
+				printer.Infof("🔄 Reset:     [%s] started %s (%s ago) by %s - %s\n",
 					strings.ToUpper(env), dt.Format(time.RFC822),
 					formattedDuration(time.Since(dt)), executorID, buildState)
 			}
@@ -324,17 +330,17 @@ func status(ctx context.Context) error {
 		// neither will be set if status is mixed
 		switch {
 		case data["ok"] == true:
-			fmt.Printf("✅ Health:    [%s] ", strings.ToUpper(env))
+			printer.Successf("Health:    [%s] ", strings.ToUpper(env))
 		case data["offline"] == true:
-			fmt.Printf("❌ Health:    [%s] ", strings.ToUpper(env))
+			printer.Errorf("Health:    [%s] ", strings.ToUpper(env))
 		default:
-			fmt.Printf("⚠️ Health:    [%s] ", strings.ToUpper(env))
+			printer.Infof("⚠️ Health:    [%s] ", strings.ToUpper(env))
 		}
 		if len(instances) == 0 {
-			fmt.Println("** No deployed instances found **")
+			printer.Infoln("** No deployed instances found **")
 			return nil
 		}
-		fmt.Printf("(%d deployed instances)\n", len(instances))
+		printer.Infof("(%d deployed instances)\n", len(instances))
 		currRegion := ""
 		for _, instance := range instances {
 			info, ok := instance.(map[string]any)
@@ -396,27 +402,27 @@ func status(ctx context.Context) error {
 			}
 			if region != currRegion {
 				currRegion = region
-				fmt.Printf("• %s\n", currRegion)
+				printer.Infof("• %s\n", currRegion)
 			}
-			fmt.Printf("  %d)", instanceNum)
+			printer.Infof("  %d)", instanceNum)
 			switch {
 			case cardinalOK:
-				fmt.Printf("\t✅ Cardinal: %s - OK\n", cardinalHost)
+				printer.Successf("Cardinal: %s - OK\n", cardinalHost)
 			case cardinalResultCode == 0:
-				fmt.Printf("\t❌ Cardinal: %s - FAIL %s\n", cardinalHost,
+				printer.Errorf("Cardinal: %s - FAIL %s\n", cardinalHost,
 					statusFailRegEx.ReplaceAllString(cardinalResultStr, ""))
 			default:
-				fmt.Printf("\t❌ Cardinal: %s - FAIL %d %s\n", cardinalHost, cardinalResultCode,
+				printer.Errorf("Cardinal: %s - FAIL %d %s\n", cardinalHost, cardinalResultCode,
 					statusFailRegEx.ReplaceAllString(cardinalResultStr, ""))
 			}
 			switch {
 			case nakamaOK:
-				fmt.Printf("\t✅ Nakama:   %s - OK\n", nakamaHost)
+				printer.Successf("Nakama:   %s - OK\n", nakamaHost)
 			case nakamaResultCode == 0:
-				fmt.Printf("\t❌ Nakama:   %s - FAIL %s\n", nakamaHost,
+				printer.Errorf("Nakama:   %s - FAIL %s\n", nakamaHost,
 					statusFailRegEx.ReplaceAllString(nakamaResultStr, ""))
 			default:
-				fmt.Printf("\t❌ Nakama:   %s - FAIL %d %s\n", nakamaHost, nakamaResultCode,
+				printer.Errorf("Nakama:   %s - FAIL %d %s\n", nakamaHost, nakamaResultCode,
 					statusFailRegEx.ReplaceAllString(nakamaResultStr, ""))
 			}
 		}
@@ -440,24 +446,28 @@ func previewDeployment(ctx context.Context, deployType string, organizationID st
 	if err != nil {
 		return eris.Wrap(err, "Failed to unmarshal deployment preview")
 	}
-	fmt.Println("\n   Deployment Preview")
-	fmt.Println("========================")
-	fmt.Println("\n   Basic Information")
-	fmt.Println("------------------------")
-	fmt.Printf("Organization:    %s\n", response.Data.OrgName)
-	fmt.Printf("Org Slug:        %s\n", response.Data.OrgSlug)
-	fmt.Printf("Project:         %s\n", response.Data.ProjectName)
-	fmt.Printf("Project Slug:    %s\n", response.Data.ProjectSlug)
+	printer.NewLine(1)
+	printer.Headerln("   Deployment Preview")
 
-	fmt.Println("\n     Configuration")
-	fmt.Println("------------------------")
-	fmt.Printf("Executor:        %s\n", response.Data.ExecutorName)
-	fmt.Printf("Deployment Type: %s\n", response.Data.DeploymentType)
-	fmt.Printf("Tick Rate:       %d\n", response.Data.TickRate)
+	printer.NewLine(1)
+	printer.Headerln("   Basic Information   ")
+	printer.SectionDivider("-", 23)
+	printer.Infof("Organization:    %s\n", response.Data.OrgName)
+	printer.Infof("Org Slug:        %s\n", response.Data.OrgSlug)
+	printer.Infof("Project:         %s\n", response.Data.ProjectName)
+	printer.Infof("Project Slug:    %s\n", response.Data.ProjectSlug)
 
-	fmt.Println("\n  Deployment Regions")
-	fmt.Println("------------------------")
-	fmt.Printf("%s\n", strings.Join(response.Data.Regions, ", "))
+	printer.NewLine(1)
+	printer.Headerln("     Configuration     ")
+	printer.SectionDivider("-", 23)
+	printer.Infof("Executor:        %s\n", response.Data.ExecutorName)
+	printer.Infof("Deployment Type: %s\n", response.Data.DeploymentType)
+	printer.Infof("Tick Rate:       %d\n", response.Data.TickRate)
+
+	printer.NewLine(1)
+	printer.Headerln("  Deployment Regions  ")
+	printer.SectionDivider("-", 23)
+	printer.Infof("%s\n", strings.Join(response.Data.Regions, ", "))
 
 	return nil
 }
diff --git a/cmd/world/forge/forge.go b/cmd/world/forge/forge.go
index 2702b63f..6d5cae8a 100644
--- a/cmd/world/forge/forge.go
+++ b/cmd/world/forge/forge.go
@@ -7,6 +7,7 @@ import (
 	"github.com/rotisserie/eris"
 	"github.com/spf13/cobra"
 	"pkg.world.dev/world-cli/common/globalconfig"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 const (
@@ -16,10 +17,10 @@ const (
 	// For production.
 	worldForgeBaseURLProd = "https://forge.world.dev"
 
-	// RPC Dev URL
+	// RPC Dev URL.
 	worldForgeRPCBaseURLLocal = "http://localhost:8002/rpc"
 
-	// RPC Prod URL
+	// RPC Prod URL.
 	worldForgeRPCBaseURLProd = "https://rpc.world.dev" // TODO: change this to the actual RPC URL
 )
 
@@ -63,12 +64,13 @@ allowing you to organize teams, manage deployments, and monitor your game servic
 			return eris.Wrap(err, "Failed to get user")
 		}
 
-		fmt.Println("   World Forge Status")
-		fmt.Println("========================")
-		fmt.Println("\n    User Information")
-		fmt.Println("------------------------")
-		fmt.Printf("ID:   %s\n", globalConfig.Credential.ID)
-		fmt.Printf("Name: %s\n", globalConfig.Credential.Name)
+		printer.NewLine(1)
+		printer.Headerln("   World Forge Status  ")
+		printer.NewLine(1)
+		printer.Headerln("    User Information   ")
+		printer.SectionDivider("-", 23)
+		printer.Infof("ID:   %s\n", globalConfig.Credential.ID)
+		printer.Infof("Name: %s\n", globalConfig.Credential.Name)
 
 		// Try to show org list and project list
 		// Show organization list
@@ -80,7 +82,8 @@ allowing you to organize teams, manage deployments, and monitor your game servic
 		}
 
 		// add separator
-		fmt.Println("\n================================================")
+		printer.NewLine(1)
+		printer.SectionDivider("=", 50)
 
 		return cmd.Help()
 	},
@@ -112,7 +115,8 @@ that serve as containers for your World Forge projects and team members.`,
 			err := showOrganizationList(cmd.Context())
 			if err == nil {
 				// add separator
-				fmt.Println("\n================================================")
+				printer.NewLine(1)
+				printer.SectionDivider("=", 50)
 			}
 			return cmd.Help()
 		},
@@ -153,7 +157,7 @@ are organized within organizations.`,
 			if err != nil {
 				return eris.Wrap(err, "Failed to select organization")
 			}
-			fmt.Println("Switched to organization: ", org.Name)
+			printer.Successf("Switched to organization: %s\n", org.Name)
 			return nil
 		},
 	}
@@ -171,7 +175,8 @@ var (
 			err := showOrganizationList(cmd.Context())
 			if err == nil {
 				// add separator
-				fmt.Println("\n================================================")
+				printer.NewLine(1)
+				printer.SectionDivider("=", 50)
 			}
 			return cmd.Help()
 		},
@@ -221,7 +226,8 @@ providing a centralized way to handle your game's development lifecycle.`,
 			err := showProjectList(cmd.Context())
 			if err == nil {
 				// add separator
-				fmt.Println("\n================================================")
+				printer.NewLine(1)
+				printer.SectionDivider("=", 50)
 			}
 			return cmd.Help()
 		},
@@ -244,10 +250,10 @@ management operations will target this selected project.`,
 				return eris.Wrap(err, "Failed to select project")
 			}
 			if prj == nil {
-				fmt.Println("No project selected.")
+				printer.Infoln("No project selected.")
 				return nil
 			}
-			fmt.Println("Switched to project: ", prj.Name)
+			printer.Successf("Switched to project: %s\n", prj.Name)
 			return nil
 		},
 	}
diff --git a/cmd/world/forge/forge_test.go b/cmd/world/forge/forge_test.go
index 4754c219..0790c759 100644
--- a/cmd/world/forge/forge_test.go
+++ b/cmd/world/forge/forge_test.go
@@ -18,6 +18,7 @@ import (
 	"github.com/rotisserie/eris"
 	"github.com/stretchr/testify/suite"
 	"pkg.world.dev/world-cli/common/globalconfig"
+	"pkg.world.dev/world-cli/common/printer"
 	"pkg.world.dev/world-cli/tea/component/multiselect"
 )
 
@@ -826,7 +827,7 @@ func (s *ForgeTestSuite) TestDeploy() {
 				}
 				input := tc.inputs[inputIndex]
 				inputIndex++
-				fmt.Printf("%s [%s]: %s", prompt, defaultVal, input)
+				printer.Infof("%s [%s]: %s", prompt, defaultVal, input)
 				return input
 			}
 			defer func() { getInput = originalGetInput }()
@@ -840,7 +841,7 @@ func (s *ForgeTestSuite) TestDeploy() {
 				tea.WithInput(nil),
 			)
 			if regionSelector == nil {
-				print("failed to create region selector")
+				print("failed to create region selector") //nolint:forbidigo // test
 			}
 			defer func() { regionSelector = nil }()
 
@@ -855,7 +856,7 @@ func (s *ForgeTestSuite) TestDeploy() {
 						// wait for 100ms to make sure the action is processed
 						time.Sleep(100 * time.Millisecond)
 					} else {
-						print("region selector is nil")
+						print("region selector is nil") //nolint:forbidigo // test
 					}
 				}
 			}()
@@ -1050,7 +1051,7 @@ func (s *ForgeTestSuite) TestDestroy() {
 			s.Require().NoError(err)
 
 			getInput = func(prompt string, defaultVal string) string {
-				fmt.Printf("%s [%s]: %s", prompt, defaultVal, tc.input)
+				printer.Infof("%s [%s]: %s", prompt, defaultVal, tc.input)
 				return tc.input
 			}
 			defer func() { getInput = originalGetInput }()
@@ -1140,7 +1141,7 @@ func (s *ForgeTestSuite) TestReset() {
 			s.Require().NoError(err)
 
 			getInput = func(prompt string, defaultVal string) string {
-				fmt.Printf("%s [%s]: %s", prompt, defaultVal, tc.input)
+				printer.Infof("%s [%s]: %s", prompt, defaultVal, tc.input)
 				return tc.input
 			}
 			defer func() { getInput = originalGetInput }()
@@ -1533,7 +1534,7 @@ func (s *ForgeTestSuite) TestOrganizationOperations() {
 
 			case "select":
 				getInput = func(prompt string, defaultVal string) string {
-					fmt.Printf("%s [%s]: %s", prompt, defaultVal, tc.input)
+					printer.Infof("%s [%s]: %s", prompt, defaultVal, tc.input)
 					return tc.input
 				}
 				defer func() { getInput = originalGetInput }()
@@ -1578,10 +1579,10 @@ func (s *ForgeTestSuite) TestCreateOrganization() {
 				Slug: "testo",
 			},
 			expectedPrompt: []string{
-				"\nEnter organization name",
-				"\nEnter organization slug",
-				"\nEnter organization avatar URL [none]",
-				"\nCreate organization with these details? (Y/n)",
+				"Enter organization name",
+				"Enter organization slug",
+				"Enter organization avatar URL [none]",
+				"Create organization with these details? (Y/n)",
 			},
 		},
 		{
@@ -1600,10 +1601,10 @@ func (s *ForgeTestSuite) TestCreateOrganization() {
 				Slug: "testo",
 			},
 			expectedPrompt: []string{
-				"\nEnter organization name",
-				"\nEnter organization slug",
-				"\nEnter organization avatar URL [none]",
-				"\nCreate organization with these details? (Y/n)",
+				"Enter organization name",
+				"Enter organization slug",
+				"Enter organization avatar URL [none]",
+				"Create organization with these details? (Y/n)",
 			},
 		},
 		{
@@ -1616,11 +1617,11 @@ func (s *ForgeTestSuite) TestCreateOrganization() {
 				"Y",               // confirm
 			},
 			expectedPrompt: []string{
-				"\nEnter organization name",
-				"\nEnter organization slug",
-				"\nEnter organization slug",
-				"\nEnter organization avatar URL [none]",
-				"\nCreate organization with these details? (Y/n)",
+				"Enter organization name",
+				"Enter organization slug",
+				"Enter organization slug",
+				"Enter organization avatar URL [none]",
+				"Create organization with these details? (Y/n)",
 			},
 			expectInputFail: 0,
 			expectedError:   false,
@@ -1640,11 +1641,11 @@ func (s *ForgeTestSuite) TestCreateOrganization() {
 				"Y",               // confirm
 			},
 			expectedPrompt: []string{
-				"\nEnter organization name",
-				"\nEnter organization name",
-				"\nEnter organization slug",
-				"\nEnter organization avatar URL [none]",
-				"\nCreate organization with these details? (Y/n)",
+				"Enter organization name",
+				"Enter organization name",
+				"Enter organization slug",
+				"Enter organization avatar URL [none]",
+				"Create organization with these details? (Y/n)",
 			},
 			expectInputFail: 0,
 			expectedError:   false,
@@ -1667,14 +1668,14 @@ func (s *ForgeTestSuite) TestCreateOrganization() {
 				"Y",                // Second attempt - confirm
 			},
 			expectedPrompt: []string{
-				"\nEnter organization name",
-				"\nEnter organization slug",
-				"\nEnter organization avatar URL [none]",
-				"\nCreate organization with these details? (Y/n)",
-				"\nEnter organization name",
-				"\nEnter organization slug",
-				"\nEnter organization avatar URL [none]",
-				"\nCreate organization with these details? (Y/n)",
+				"Enter organization name",
+				"Enter organization slug",
+				"Enter organization avatar URL [none]",
+				"Create organization with these details? (Y/n)",
+				"Enter organization name",
+				"Enter organization slug",
+				"Enter organization avatar URL [none]",
+				"Create organization with these details? (Y/n)",
 			},
 			expectInputFail: 0,
 			expectedError:   false,
@@ -1690,7 +1691,7 @@ func (s *ForgeTestSuite) TestCreateOrganization() {
 		s.Run(tc.name, func() {
 			inputIndex := 0
 			getInput = func(prompt string, defaultVal string) string {
-				fmt.Printf("%s [%s]: ", prompt, defaultVal)
+				fmt.Printf("%s [%s]: ", prompt, defaultVal) //nolint:forbidigo // test
 
 				// Validate against expected prompts if defined
 				if len(tc.expectedPrompt) > 0 {
@@ -1707,7 +1708,7 @@ func (s *ForgeTestSuite) TestCreateOrganization() {
 				if input == "" {
 					input = defaultVal
 				}
-				fmt.Printf("%s\n", input)
+				printer.Infoln(input)
 				inputIndex++
 				return input
 			}
@@ -2107,7 +2108,7 @@ PROJECT_NAME = "test-project-from-toml"
 			if len(tc.inputs) > 0 {
 				inputIndex := 0
 				getInput = func(prompt string, defaultVal string) string {
-					fmt.Printf("%s [%s]: ", prompt, defaultVal)
+					printer.Infof("%s [%s]: ", prompt, defaultVal)
 
 					if inputIndex >= len(tc.inputs) {
 						panic(fmt.Errorf("Input %d Failed", inputIndex))
@@ -2120,7 +2121,7 @@ PROJECT_NAME = "test-project-from-toml"
 						input = defaultVal
 					}
 
-					fmt.Printf("%s\n", input)
+					printer.Infoln(input)
 					return input
 				}
 				defer func() { getInput = originalGetInput }()
@@ -2135,7 +2136,7 @@ PROJECT_NAME = "test-project-from-toml"
 				tea.WithInput(nil),
 			)
 			if regionSelector == nil {
-				print("failed to create region selector")
+				printer.Errorln("failed to create region selector")
 			}
 			defer func() { regionSelector = nil }()
 
@@ -2150,7 +2151,7 @@ PROJECT_NAME = "test-project-from-toml"
 						// wait for 100ms to make sure the action is processed
 						time.Sleep(100 * time.Millisecond)
 					} else {
-						print("region selector is nil")
+						printer.Errorln("region selector is nil")
 					}
 				}
 			}()
@@ -2259,7 +2260,8 @@ func (s *ForgeTestSuite) TestSelectProject() {
 			s.Require().NoError(err)
 
 			getInput = func(prompt string, defaultVal string) string {
-				fmt.Printf("%s [%s]: %s", prompt, defaultVal, tc.input)
+				printer.Infof("%s [%s]: ", prompt, defaultVal)
+				printer.Infoln(tc.input)
 				return tc.input
 			}
 			defer func() { getInput = originalGetInput }()
@@ -2532,7 +2534,7 @@ func (s *ForgeTestSuite) TestInviteUserToOrganization() { //nolint: gocognit //
 				inputIndex := 0
 				lastPrompt := ""
 				getInput = func(prompt string, defaultVal string) string {
-					fmt.Printf("%s [%s]: ", prompt, defaultVal)
+					printer.Infof("%s [%s]: ", prompt, defaultVal)
 					if prompt == lastPrompt {
 						panic(eris.Errorf("Input %d Failed", inputIndex))
 					}
@@ -2541,7 +2543,7 @@ func (s *ForgeTestSuite) TestInviteUserToOrganization() { //nolint: gocognit //
 					if input == "" {
 						input = defaultVal
 					}
-					fmt.Printf("%s\n", input)
+					printer.Infoln(input)
 					inputIndex++
 					return input
 				}
@@ -2682,7 +2684,7 @@ func (s *ForgeTestSuite) TestUpdateRoleInOrganization() { //nolint: gocognit //
 				lastPrompt := ""
 				nextToLastPrompt := ""
 				getInput = func(prompt string, defaultVal string) string {
-					fmt.Printf("%s [%s]: ", prompt, defaultVal)
+					printer.Infof("%s [%s]: ", prompt, defaultVal)
 					if (prompt == lastPrompt || prompt == nextToLastPrompt) && tc.expectInputFail <= inputIndex {
 						panic(eris.Errorf("Input %d Failed", inputIndex))
 					}
@@ -2692,7 +2694,7 @@ func (s *ForgeTestSuite) TestUpdateRoleInOrganization() { //nolint: gocognit //
 					if input == "" {
 						input = defaultVal
 					}
-					fmt.Printf("%s\n", input)
+					printer.Infoln(input)
 					inputIndex++
 					return input
 				}
diff --git a/cmd/world/forge/log.go b/cmd/world/forge/log.go
index 8f4184af..d6b89af4 100644
--- a/cmd/world/forge/log.go
+++ b/cmd/world/forge/log.go
@@ -10,6 +10,7 @@ import (
 	"connectrpc.com/connect"
 	"github.com/rotisserie/eris"
 	"pkg.world.dev/world-cli/common/globalconfig"
+	"pkg.world.dev/world-cli/common/printer"
 	logsv1 "pkg.world.dev/world-cli/gen/logs/v1"
 	"pkg.world.dev/world-cli/gen/logs/v1/logsv1connect"
 )
@@ -69,9 +70,9 @@ func selectRegion(project project) (string, error) {
 	}
 
 	// If there are multiple regions, print them and let the user choose
-	fmt.Println("Available regions:")
+	printer.Infoln("Available regions:")
 	for i, region := range project.Config.Region {
-		fmt.Printf("%d. %s\n", i+1, region)
+		printer.Infof("%d. %s\n", i+1, region)
 	}
 
 	inputStr := getInput("Choose a region", "1")
@@ -93,9 +94,10 @@ func selectEnvironment(availableEnvs []string) (string, error) {
 	}
 
 	// If there are multiple environments, print them and let the user choose
-	fmt.Println("\nAvailable environments:")
-	fmt.Printf("%d. %s\n", 1, "dev")
-	fmt.Printf("%d. %s\n", 2, "prod")
+	printer.NewLine(1)
+	printer.Infoln("Available environments:")
+	printer.Infof("%d. %s\n", 1, "dev")
+	printer.Infof("%d. %s\n", 2, "prod")
 
 	inputStr := getInput("Choose an environment", "1")
 	inputInt, err := strconv.Atoi(inputStr)
@@ -142,8 +144,10 @@ func getListOfEnvironments(ctx context.Context, project project) ([]string, erro
 }
 
 func confirmLogParams(params *logParams) error {
-	fmt.Printf("\nShowing logs for '%s-%s-cardinal' in '%s-%s'\n(Press Enter to continue | Ctrl+C to cancel/exit)",
+	printer.NewLine(1)
+	printer.Infof("Showing logs for '%s-%s-cardinal' in '%s-%s'\n",
 		params.organization.Slug, params.project.Slug, params.env, params.region)
+	printer.Infoln("(Press Enter to continue | Ctrl+C to cancel/exit)")
 	inputStr := getInput("", "")
 	if inputStr != "" {
 		return eris.New("Operation cancelled by user")
@@ -208,7 +212,7 @@ func streamLogs(ctx context.Context,
 				// Stream ended normally
 				return nil
 			}
-			log.Println(stream.Msg().Log)
+			log.Println(stream.Msg().GetLog())
 		}
 	}
 }
diff --git a/cmd/world/forge/login.go b/cmd/world/forge/login.go
index 3dac3e81..6694cac2 100644
--- a/cmd/world/forge/login.go
+++ b/cmd/world/forge/login.go
@@ -18,6 +18,7 @@ import (
 	"github.com/rotisserie/eris"
 	"github.com/rs/zerolog/log"
 	"pkg.world.dev/world-cli/common/globalconfig"
+	"pkg.world.dev/world-cli/common/printer"
 	teaspinner "pkg.world.dev/world-cli/tea/component/spinner"
 )
 
@@ -107,14 +108,14 @@ func handlePostLoginConfig(ctx context.Context, config *globalconfig.GlobalConfi
 func handleKnownRepoConfig(ctx context.Context, config *globalconfig.GlobalConfig) error {
 	proj, err := getSelectedProject(ctx)
 	if err != nil {
-		fmt.Println("⚠️ Warning: Failed to get project", config.ProjectID, ":", err)
+		printer.Infof("⚠️ Warning: Failed to get project %s: %s", config.ProjectID, err.Error())
 	}
 	org, err := getSelectedOrganization(ctx)
 	if err != nil {
-		fmt.Println("⚠️ Warning: Failed to get organization", config.OrganizationID, ":", err)
+		printer.Infof("⚠️ Warning: Failed to get organization %s: %s", config.OrganizationID, err.Error())
 	}
 	if proj.Name != "" && org.Name != "" {
-		fmt.Printf("Auto-selected project %s (%s) in organization %s (%s)\n",
+		printer.Infof("Auto-selected project %s (%s) in organization %s (%s)\n",
 			proj.Name, proj.Slug,
 			org.Name, org.Slug)
 	}
@@ -154,11 +155,13 @@ func handleNewRepoConfig(ctx context.Context, config *globalconfig.GlobalConfig)
 }
 
 func displayLoginSuccess(config globalconfig.GlobalConfig) {
-	fmt.Println("\n   Login successful!")
-	fmt.Println("=======================")
-	fmt.Printf("\nWelcome, %s!\n", config.Credential.Name)
-	fmt.Printf("Your ID is: %s\n", config.Credential.ID)
-	fmt.Println("\nYou're all set to start using World Forge!")
+	printer.NewLine(1)
+	printer.Headerln("   Login successful!  ")
+	printer.NewLine(1)
+	printer.Infof("Welcome, %s!\n", config.Credential.Name)
+	printer.Infof("Your ID is: %s\n", config.Credential.ID)
+	printer.NewLine(1)
+	printer.Infoln("You're all set to start using World Forge!")
 }
 
 // GetToken will get the token from the config file.
@@ -185,7 +188,7 @@ func getToken(ctx context.Context, url string, argusid bool, result interface{})
 		defer wg.Done()
 		if _, err := p.Run(); err != nil {
 			log.Error().Err(err).Msg("failed to run spinner")
-			fmt.Printf("Logging in...\n") // If the spinner doesn't start correctly, fallback to a simple print.
+			printer.Infoln("Logging in...") // If the spinner doesn't start correctly, fallback to a simple print.
 		}
 		spinnerExited.Store(true)
 	}()
@@ -198,9 +201,9 @@ func getToken(ctx context.Context, url string, argusid bool, result interface{})
 			wg.Wait()
 		}
 		if didLogin {
-			fmt.Printf("✅ Logged in!\n")
+			printer.Successln("Logged in!")
 		} else {
-			fmt.Printf("❌ Login failed!\n")
+			printer.Errorln("Login failed!")
 		}
 	}
 
@@ -288,7 +291,8 @@ func handleArgusIDToken(response []byte, result interface{}) error {
 	case "pending":
 		return errPending
 	case "success":
-		fmt.Println("\nLogin token received successfully!")
+		printer.NewLine(1)
+		printer.Successln("Login token received successfully!")
 		return nil
 	default:
 		return eris.New(fmt.Sprintf("Status: %s", tokenStruct.Status))
diff --git a/cmd/world/forge/organization.go b/cmd/world/forge/organization.go
index 76b28d37..f9288291 100644
--- a/cmd/world/forge/organization.go
+++ b/cmd/world/forge/organization.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/rotisserie/eris"
 	"pkg.world.dev/world-cli/common/globalconfig"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 type organization struct {
@@ -40,19 +41,22 @@ func showOrganizationList(ctx context.Context) error {
 		return eris.Wrap(err, "Failed to get organization list")
 	}
 
-	fmt.Println("\n  Organization Information")
-	fmt.Println("============================")
+	printer.NewLine(1)
+	printer.Headerln("  Organization Information  ")
 	if organization.Name == "" {
-		fmt.Println("\nNo organization selected")
-		fmt.Println("\nUse 'world forge organization switch' to choose an organization")
+		printer.NewLine(1)
+		printer.Errorln("No organization selected")
+		printer.NewLine(1)
+		printer.Infoln("Use 'world forge organization switch' to choose an organization")
 	} else {
-		fmt.Println("\n Available Organizations:")
-		fmt.Println("--------------------------")
+		printer.NewLine(1)
+		printer.Infoln(" Available Organizations: ")
+		printer.SectionDivider("-", 26)
 		for _, org := range organizations {
 			if org.ID == organization.ID {
-				fmt.Printf("• %s (%s) [SELECTED]\n", org.Name, org.Slug)
+				printer.Infof("• %s (%s) [SELECTED]\n", org.Name, org.Slug)
 			} else {
-				fmt.Printf("  %s (%s)\n", org.Name, org.Slug)
+				printer.Infof("  %s (%s)\n", org.Name, org.Slug)
 			}
 		}
 	}
@@ -128,10 +132,10 @@ func selectOrganization(ctx context.Context) (organization, error) {
 
 func promptForOrganization(ctx context.Context, orgs []organization) (organization, error) {
 	// Display organizations as a numbered list
-	fmt.Println("\n   Available Organizations")
-	fmt.Println("=============================")
+	printer.NewLine(1)
+	printer.Headerln("   Available Organizations  ")
 	for i, org := range orgs {
-		fmt.Printf("  %d. %s\n    └─ Slug: %s\n", i+1, org.Name, org.Slug)
+		printer.Infof("  %d. %s\n    └─ Slug: %s\n", i+1, org.Name, org.Slug)
 	}
 
 	// Get user input
@@ -140,17 +144,20 @@ func promptForOrganization(ctx context.Context, orgs []organization) (organizati
 		case <-ctx.Done():
 			return organization{}, ctx.Err()
 		default:
-			input := getInput("\nEnter organization number (or 'q' to quit)", "")
+			printer.NewLine(1)
+			input := getInput("Enter organization number (or 'q' to quit)", "")
 
 			if input == "q" {
-				fmt.Println("\n❌ Organization selection canceled")
+				printer.NewLine(1)
+				printer.Errorln("Organization selection canceled")
 				return organization{}, eris.New("Organization selection canceled")
 			}
 
 			// Parse selection
 			num, err := strconv.Atoi(input)
 			if err != nil || num < 1 || num > len(orgs) {
-				fmt.Printf("\n❌ Invalid selection. Please enter a number between 1 and %d\n", len(orgs))
+				printer.NewLine(1)
+				printer.Errorf("Invalid selection. Please enter a number between 1 and %d\n", len(orgs))
 				continue
 			}
 
@@ -167,7 +174,8 @@ func promptForOrganization(ctx context.Context, orgs []organization) (organizati
 				return organization{}, eris.Wrap(err, "Failed to save organization")
 			}
 
-			fmt.Printf("\n✅ Selected organization: %s\n", selectedOrg.Name)
+			printer.NewLine(1)
+			printer.Successf("Selected organization: %s\n", selectedOrg.Name)
 			return selectedOrg, nil
 		}
 	}
@@ -198,18 +206,19 @@ func handleProjectConfig(ctx context.Context) error {
 	return showProjectList(ctx)
 }
 
-//nolint:gocognit // Makes sense to keep in one function.
+//nolint:gocognit,funlen // Makes sense to keep in one function.
 func createOrganization(ctx context.Context) (*organization, error) {
 	var orgName, orgSlug, orgAvatarURL string
 
 	for {
 		// Get organization name
-		fmt.Println("\n  Create New Organization ")
-		fmt.Println("==============================")
+		printer.NewLine(1)
+		printer.Headerln("  Create New Organization  ")
 		for {
-			orgName = getInput("\nEnter organization name", "")
+			orgName = getInput("Enter organization name", "")
 			if orgName == "" {
-				fmt.Printf("\nOrganization name is required\n")
+				printer.NewLine(1)
+				printer.Errorln("Organization name is required")
 				continue
 			}
 			break
@@ -220,13 +229,14 @@ func createOrganization(ctx context.Context) (*organization, error) {
 			minLength := 3
 			maxLength := 15
 			orgSlug = CreateSlugFromName(orgName, minLength, maxLength)
-			orgSlug = getInput("\nEnter organization slug", orgSlug)
+			orgSlug = getInput("Enter organization slug", orgSlug)
 
 			// Validate slug
 			var err error
 			orgSlug, err = slugToSaneCheck(orgSlug, minLength, maxLength)
 			if err != nil {
-				fmt.Printf("\n❌ Error: %s\n", err)
+				printer.NewLine(1)
+				printer.Errorf("Error: %s\n", err)
 				continue
 			}
 			break
@@ -234,15 +244,17 @@ func createOrganization(ctx context.Context) (*organization, error) {
 
 		// Get and validate organization avatar URL
 		for {
-			orgAvatarURL = getInput("\nEnter organization avatar URL [none]", "")
+			orgAvatarURL = getInput("Enter organization avatar URL [none]", "")
 
 			if orgAvatarURL == "" {
-				fmt.Print("\nSkipped. No avatar URL will be used.\n")
+				printer.NewLine(1)
+				printer.Infoln("Skipped. No avatar URL will be used.")
 				break
 			}
 
 			if !isValidURL(orgAvatarURL) {
-				fmt.Printf("\n❌ Error: Invalid URL, leave empty to skip\n")
+				printer.NewLine(1)
+				printer.Errorln("Invalid URL, leave empty to skip")
 				continue
 			}
 
@@ -250,26 +262,28 @@ func createOrganization(ctx context.Context) (*organization, error) {
 		}
 
 		// Show confirmation
-		fmt.Println("\n  Organization Details")
-		fmt.Println("==============================")
-		fmt.Printf("Name: %s\n", orgName)
-		fmt.Printf("Slug: %s\n", orgSlug)
+		printer.NewLine(1)
+		printer.Headerln("  Organization Details  ")
+		printer.Infof("Name: %s\n", orgName)
+		printer.Infof("Slug: %s\n", orgSlug)
 		if orgAvatarURL != "" {
-			fmt.Printf("Avatar URL: %s\n", orgAvatarURL)
+			printer.Infof("Avatar URL: %s\n", orgAvatarURL)
 		} else {
-			fmt.Println("Avatar URL: None")
+			printer.Infoln("Avatar URL: None")
 		}
 
 		// Get confirmation
 		for redo := true; redo; {
-			confirm := getInput("\nCreate organization with these details? (Y/n)", "n")
+			printer.NewLine(1)
+			confirm := getInput("Create organization with these details? (Y/n)", "n")
 			switch confirm {
 			case "Y":
 				return createOrgRequestAndSave(ctx, orgName, orgSlug, orgAvatarURL)
 			case "n":
 				redo = false
 			default:
-				fmt.Println("\nPlease enter capital 'Y' to confirm, 'n' to cancel, or 'redo' to start over")
+				printer.NewLine(1)
+				printer.Errorln("Please enter capital 'Y' to confirm, 'n' to cancel, or 'redo' to start over")
 			}
 		}
 	}
@@ -303,14 +317,15 @@ func createOrgRequestAndSave(ctx context.Context, name, slug, avatarURL string)
 		return nil, eris.Wrap(err, "Failed to save organization in config")
 	}
 
-	fmt.Printf("\nOrganization '%s' with slug '%s' created successfully!\n", name, slug)
+	printer.NewLine(1)
+	printer.Successf("Organization '%s' with slug '%s' created successfully!\n", name, slug)
 	return org, nil
 }
 
 func inviteUserToOrganization(ctx context.Context) error { //nolint:dupl // TODO: refactor
-	fmt.Println("\n   Invite User to Organization")
-	fmt.Println("=================================")
-	userID := getInput("\nEnter user ID to invite", "")
+	printer.NewLine(1)
+	printer.Headerln("   Invite User to Organization   ")
+	userID := getInput("Enter user ID to invite", "")
 	if userID == "" {
 		return eris.New("User ID cannot be empty")
 	}
@@ -338,15 +353,16 @@ func inviteUserToOrganization(ctx context.Context) error { //nolint:dupl // TODO
 		return eris.Wrap(err, "Failed to invite user to organization")
 	}
 
-	fmt.Printf("\nSuccessfully invited user %s to organization!\n", userID)
-	fmt.Printf("Assigned role: %s\n", role)
+	printer.NewLine(1)
+	printer.Successf("Successfully invited user %s to organization!\n", userID)
+	printer.Infof("Assigned role: %s\n", role)
 	return nil
 }
 
 func updateUserRoleInOrganization(ctx context.Context) error { //nolint:dupl // TODO: refactor
-	fmt.Println("\n  Update User Role in Organization ")
-	fmt.Println("=====================================")
-	userID := getInput("\nEnter user ID to update", "")
+	printer.NewLine(1)
+	printer.Headerln("  Update User Role in Organization  ")
+	userID := getInput("Enter user ID to update", "")
 
 	if userID == "" {
 		return eris.New("User ID cannot be empty")
@@ -375,8 +391,9 @@ func updateUserRoleInOrganization(ctx context.Context) error { //nolint:dupl //
 		return eris.Wrap(err, "Failed to set user role in organization")
 	}
 
-	fmt.Printf("\nSuccessfully updated role for user %s!\n", userID)
-	fmt.Printf("New role: %s\n", role)
+	printer.NewLine(1)
+	printer.Successf("Successfully updated role for user %s!\n", userID)
+	printer.Infof("New role: %s\n", role)
 	return nil
 }
 
@@ -389,15 +406,17 @@ func getRoleInput(allowNone bool) string {
 		opts = "owner, admin, or member"
 	}
 	for {
-		fmt.Println("\n Role Assignment")
-		fmt.Println("----------------")
-		fmt.Printf("Available Roles: %s\n", opts)
-		role := getInput("\nEnter organization role", "member")
+		printer.NewLine(1)
+		printer.Headerln(" Role Assignment  ")
+		printer.Infof("Available Roles: %s\n", opts)
+		role := getInput("Enter organization role", "member")
 		if allowNone && role == "none" {
-			fmt.Print("\nWarning: Role \"none\" removes user from this organization")
-			answer := getInput("\nConfirm removal? (Yes/no)", "no")
+			printer.NewLine(1)
+			printer.Infoln("Warning: Role \"none\" removes user from this organization")
+			answer := getInput("Confirm removal? (Yes/no)", "no")
 			if answer != "Yes" {
-				fmt.Println("\n❌ User not removed")
+				printer.NewLine(1)
+				printer.Errorln("User not removed")
 				continue // let them try again
 			}
 			return role
@@ -405,7 +424,8 @@ func getRoleInput(allowNone bool) string {
 		if role == "admin" || role == "owner" || role == "member" {
 			return role
 		}
-		fmt.Printf("\n❌ Error: Role must be one of %s\n", opts)
+		printer.NewLine(1)
+		printer.Errorf("Error: Role must be one of %s\n", opts)
 	}
 }
 
@@ -445,18 +465,22 @@ func handleMultipleOrgs(ctx context.Context, orgID string, orgs []organization)
 func handleNoOrgs(ctx context.Context) (string, error) {
 	for redo := true; redo; {
 		// Confirmation prompt
-		confirmation := getInput("\nYou don't have any organizations. Create a new one now? (Y/n)", "n")
+		printer.NewLine(1)
+		confirmation := getInput("You don't have any organizations. Create a new one now? (Y/n)", "n")
 
 		switch confirmation {
 		case "Y":
 			redo = false
 		case "y":
-			fmt.Println("You need to enter Y (uppercase) to confirm creation")
+			printer.NewLine(1)
+			printer.Errorln("You need to enter Y (uppercase) to confirm creation")
 		case "n":
-			fmt.Println("❌ Organization creation canceled")
+			printer.NewLine(1)
+			printer.Errorln("Organization creation canceled")
 			return "", nil
 		default:
-			fmt.Println("❌ Invalid input")
+			printer.NewLine(1)
+			printer.Errorln("Invalid input")
 		}
 	}
 
diff --git a/cmd/world/forge/project.go b/cmd/world/forge/project.go
index 1e81f197..d10a3abd 100644
--- a/cmd/world/forge/project.go
+++ b/cmd/world/forge/project.go
@@ -14,6 +14,7 @@ import (
 	tea "github.com/charmbracelet/bubbletea"
 	"github.com/rotisserie/eris"
 	"pkg.world.dev/world-cli/common/globalconfig"
+	"pkg.world.dev/world-cli/common/printer"
 	"pkg.world.dev/world-cli/common/tomlutil"
 	"pkg.world.dev/world-cli/tea/component/multiselect"
 )
@@ -84,19 +85,22 @@ func showProjectList(ctx context.Context) error {
 		return eris.Wrap(err, "Failed to get selected project")
 	}
 
-	fmt.Println("\n   Project Information")
-	fmt.Println("========================")
+	printer.NewLine(1)
+	printer.Headerln("   Project Information   ")
 	if project.Name == "" {
-		fmt.Println("\n❌ No project selected")
-		fmt.Println("\nUse 'world forge project switch' to choose a project")
+		printer.NewLine(1)
+		printer.Errorln("No project selected")
+		printer.NewLine(1)
+		printer.Infoln("Use 'world forge project switch' to choose a project")
 	} else {
-		fmt.Println("\n  Available Projects:")
-		fmt.Println("-----------------------")
+		printer.NewLine(1)
+		printer.Infoln("  Available Projects:")
+		printer.SectionDivider("-", 23)
 		for _, prj := range projects {
 			if prj.ID == project.ID {
-				fmt.Printf("• %s (%s) [SELECTED]\n", prj.Name, prj.Slug)
+				printer.Infof("• %s (%s) [SELECTED]\n", prj.Name, prj.Slug)
 			} else {
-				fmt.Printf("  %s (%s)\n", prj.Name, prj.Slug)
+				printer.Infof("  %s (%s)\n", prj.Name, prj.Slug)
 			}
 		}
 	}
@@ -221,7 +225,6 @@ func createProject(ctx context.Context) (*project, error) {
 	if err != nil {
 		return nil, eris.Wrap(err, "Failed to get available regions")
 	}
-	// fmt.Println(regions)
 
 	p := project{
 		update: false,
@@ -264,45 +267,46 @@ func createProject(ctx context.Context) (*project, error) {
 		return nil, eris.Wrap(err, "Failed to select project")
 	}
 
-	fmt.Printf("\nProject '%s' created successfully!\n", prj.Name)
-	fmt.Printf("Project Details:\n")
-	fmt.Printf("• Name: %s\n", prj.Name)
-	fmt.Printf("• Slug: %s\n", prj.Slug)
-	fmt.Printf("• ID: %s\n", prj.ID)
-	fmt.Printf("• Repository URL: %s\n", prj.RepoURL)
-	fmt.Printf("• Repository Path: %s\n", prj.RepoPath)
-	fmt.Printf("• Tick Rate: %d\n", prj.Config.TickRate)
-	fmt.Printf("• Regions:\n")
+	printer.NewLine(1)
+	printer.Successf("Project '%s' created successfully!\n", prj.Name)
+	printer.Infoln("Project Details:")
+	printer.Infof("• Name: %s\n", prj.Name)
+	printer.Infof("• Slug: %s\n", prj.Slug)
+	printer.Infof("• ID: %s\n", prj.ID)
+	printer.Infof("• Repository URL: %s\n", prj.RepoURL)
+	printer.Infof("• Repository Path: %s\n", prj.RepoPath)
+	printer.Infof("• Tick Rate: %d\n", prj.Config.TickRate)
+	printer.Infoln("• Regions:")
 	for _, region := range prj.Config.Region {
-		fmt.Printf("    - %s\n", region)
+		printer.Infof("    - %s\n", region)
 	}
-	fmt.Printf("• Discord Configuration:\n")
+	printer.Infoln("• Discord Configuration:")
 	if prj.Config.Discord.Enabled {
-		fmt.Printf("  - Enabled: Yes\n")
-		fmt.Printf("  - Channel ID: %s\n", prj.Config.Discord.Channel)
-		fmt.Printf("  - Bot Token: %s\n", prj.Config.Discord.Token)
+		printer.Infoln("  - Enabled: Yes")
+		printer.Infof("  - Channel ID: %s\n", prj.Config.Discord.Channel)
+		printer.Infof("  - Bot Token: %s\n", prj.Config.Discord.Token)
 	} else {
-		fmt.Printf("  - Enabled: No\n")
+		printer.Infoln("  - Enabled: No")
 	}
-	fmt.Printf("• Slack Configuration:\n")
+	printer.Infoln("• Slack Configuration:")
 	if prj.Config.Slack.Enabled {
-		fmt.Printf("  - Enabled: Yes\n")
-		fmt.Printf("  - Channel ID: %s\n", prj.Config.Slack.Channel)
-		fmt.Printf("  - Token: %s\n", prj.Config.Slack.Token)
+		printer.Infoln("  - Enabled: Yes")
+		printer.Infof("  - Channel ID: %s\n", prj.Config.Slack.Channel)
+		printer.Infof("  - Token: %s\n", prj.Config.Slack.Token)
 	} else {
-		fmt.Printf("  - Enabled: No\n")
+		printer.Infoln("  - Enabled: No")
 	}
-	fmt.Printf("• Avatar URL: %s\n", prj.AvatarURL)
-	fmt.Printf("• Deploy Secret (for deploy via CI/CD pipeline tools):\n")
-	fmt.Printf("    %s\n", prj.DeploySecret)
-	fmt.Printf("Note: Deploy Secret will not be shown again. Save it now in a secure location.\n")
+	printer.Infof("• Avatar URL: %s\n", prj.AvatarURL)
+	printer.Infoln("• Deploy Secret (for deploy via CI/CD pipeline tools):")
+	printer.Infof("    %s\n", prj.DeploySecret)
+	printer.Infoln("Note: Deploy Secret will not be shown again. Save it now in a secure location.")
 
 	return prj, nil
 }
 
 func (p *project) inputProjectName(ctx context.Context) error {
-	fmt.Println("\n   Project Name Configuration")
-	fmt.Println("================================")
+	printer.NewLine(1)
+	printer.Headerln("   Project Name Configuration   ")
 
 	for {
 		if err := ctx.Err(); err != nil {
@@ -319,7 +323,8 @@ func (p *project) inputProjectName(ctx context.Context) error {
 
 		err = p.validateAndSetName(name)
 		if err == nil {
-			fmt.Printf("\n✅ Project name \"%s\" accepted!\n", name)
+			printer.NewLine(1)
+			printer.Successf("Project name \"%s\" accepted!\n", name)
 			return nil
 		}
 	}
@@ -360,19 +365,21 @@ func (p *project) getForgeProjectNameFromWorldToml() error {
 
 func (p *project) validateAndSetName(name string) error {
 	if name == "" {
-		fmt.Printf("\n❌ Error: Project name cannot be empty\n")
+		printer.NewLine(1)
+		printer.Errorln("Error: Project name cannot be empty")
 		return eris.New("empty name")
 	}
 
 	if len(name) > MaxProjectNameLen {
-		fmt.Printf("\n❌ Error: Project name cannot be longer than %d characters\n",
-			MaxProjectNameLen)
+		printer.NewLine(1)
+		printer.Errorf("Error: Project name cannot be longer than %d characters\n", MaxProjectNameLen)
 		return eris.New("name too long")
 	}
 
 	if strings.ContainsAny(name, "<>:\"/\\|?*") {
-		fmt.Printf("\n❌ Error: Project name contains invalid characters\n" +
-			"   Invalid characters: < > : \" / \\ | ? *\n")
+		printer.NewLine(1)
+		printer.Errorln("Error: Project name contains invalid characters" +
+			"   Invalid characters: < > : \" / \\ | ? *")
 		return eris.New("invalid characters")
 	}
 
@@ -386,8 +393,8 @@ func (p *project) inputProjectSlug(ctx context.Context) error {
 		case <-ctx.Done():
 			return ctx.Err()
 		default:
-			fmt.Println("\n   Project Slug Configuration")
-			fmt.Println("================================")
+			printer.NewLine(1)
+			printer.Headerln("   Project Slug Configuration   ")
 
 			// if no slug exists, create a default one from the name
 			minLength := 3
@@ -402,7 +409,8 @@ func (p *project) inputProjectSlug(ctx context.Context) error {
 			var err error
 			slug, err = slugToSaneCheck(slug, minLength, maxLength)
 			if err != nil {
-				fmt.Printf("\n❌ Error: %s\n", err)
+				printer.NewLine(1)
+				printer.Errorf("Error: %s\n", err)
 				continue
 			}
 
@@ -437,7 +445,8 @@ func (p *project) inputRepoURLAndToken(ctx context.Context) error {
 				repoToken = p.processRepoToken(repoToken)
 
 				if err := validateRepoToken(ctx, repoURL, repoToken); err != nil {
-					fmt.Printf("\n❌ Error: %v\n", err)
+					printer.NewLine(1)
+					printer.Errorf("Error: %v\n", err)
 					continue
 				}
 			}
@@ -450,20 +459,23 @@ func (p *project) inputRepoURLAndToken(ctx context.Context) error {
 }
 
 func (p *project) promptForRepoURL() string {
-	fmt.Printf("\n  Repository URL Configuration")
-	fmt.Printf("\n================================")
-	repoURL := getInput("\nEnter Repository URL", p.RepoURL)
+	printer.NewLine(1)
+	printer.Headerln("  Repository URL Configuration   ")
+
+	printer.NewLine(1)
+	repoURL := getInput("Enter Repository URL", p.RepoURL)
 
 	return repoURL
 }
 
 func (p *project) validateRepoURL(repoURL string) error {
 	if !strings.HasPrefix(repoURL, "http://") && !strings.HasPrefix(repoURL, "https://") {
-		fmt.Printf("\n❌ Error: Invalid Repository URL Format\n")
-		fmt.Printf("========================================\n")
-		fmt.Printf("The URL must start with:\n")
-		fmt.Printf("• http://\n")
-		fmt.Printf("• https://\n\n")
+		printer.NewLine(1)
+		printer.Errorln("Error: Invalid Repository URL Format")
+		printer.Infoln("The URL must start with:")
+		printer.Infoln("• http://")
+		printer.Infoln("• https://")
+		printer.NewLine(1)
 		return eris.New("invalid URL format")
 	}
 	return nil
@@ -471,12 +483,12 @@ func (p *project) validateRepoURL(repoURL string) error {
 
 func (p *project) promptForRepoToken() string {
 	if p.update {
-		fmt.Printf("\n  Update Repository Access Token\n")
-		fmt.Printf("==================================\n")
-		fmt.Printf("\nEnter new token (options):\n")
-		fmt.Printf("• Press Enter to keep existing token\n")
-		fmt.Printf("• Type 'public' for public repositories\n")
-		fmt.Printf("• Enter new token for private repositories\n")
+		printer.NewLine(1)
+		printer.Headerln("  Update Repository Access Token   ")
+		printer.Infoln("Enter new token (options):")
+		printer.Infoln("• Press Enter to keep existing token")
+		printer.Infoln("• Type 'public' for public repositories")
+		printer.Infoln("• Enter new token for private repositories")
 	}
 	repoToken := getInput("\nEnter Token", p.RepoToken)
 
@@ -500,12 +512,15 @@ func (p *project) inputRepoPath(ctx context.Context) {
 	for {
 		// Get repository URL
 		if p.update {
-			fmt.Printf("\n  Change Repository Cardinal Path\n")
+			printer.NewLine(1)
+			printer.Headerln("  Change Repository Cardinal Path   ")
 		} else {
-			fmt.Printf("\n  Set Repository Cardinal Path\n")
+			printer.NewLine(1)
+			printer.Headerln("  Set Repository Cardinal Path   ")
 		}
-		fmt.Printf("================================\n")
-		repoPath = getInput("\nEnter Repository Cardinal Path", p.RepoPath)
+
+		printer.NewLine(1)
+		repoPath = getInput("Enter Repository Cardinal Path", p.RepoPath)
 
 		// strip off any leading slash
 		repoPath = strings.TrimPrefix(repoPath, "/")
@@ -513,7 +528,8 @@ func (p *project) inputRepoPath(ctx context.Context) {
 		// Validate the path exists using the new validateRepoPath function
 		if len(repoPath) > 0 {
 			if err := validateRepoPath(ctx, p.RepoURL, p.RepoToken, repoPath); err != nil {
-				fmt.Printf("\n❌ Error: %v\n", err)
+				printer.NewLine(1)
+				printer.Errorf("Error: %v\n", err)
 				continue
 			}
 		}
@@ -529,7 +545,7 @@ func selectProject(ctx context.Context) (*project, error) {
 		return nil, eris.Wrap(err, "Could not get config")
 	}
 	if config.CurrRepoKnown {
-		fmt.Printf("❌ Current git working directory belongs to project %s. Cannot switch.",
+		printer.Errorf("Current git working directory belongs to project %s. Cannot switch.\n",
 			config.CurrProjectName)
 		return nil, nil //nolint: nilnil // See: https://www.dolthub.com/blog/2024-05-31-benchmarking-go-error-handling/
 	}
@@ -546,12 +562,13 @@ func selectProject(ctx context.Context) (*project, error) {
 	}
 
 	// Display projects as a numbered list
-	fmt.Println("\n   Available Projects")
-	fmt.Println("========================")
-	fmt.Println("\n Project List:")
-	fmt.Println("---------------")
+	printer.NewLine(1)
+	printer.Headerln("   Available Projects   ")
+	printer.NewLine(1)
+	printer.Infoln(" Project List:")
+	printer.SectionDivider("-", 15)
 	for i, proj := range projects {
-		fmt.Printf("  %d. %s\n     └─ Slug: %s\n", i+1, proj.Name, proj.Slug)
+		printer.Infof("  %d. %s\n     └─ Slug: %s\n", i+1, proj.Name, proj.Slug)
 	}
 
 	// Get user input
@@ -565,8 +582,8 @@ func selectProject(ctx context.Context) (*project, error) {
 		// Parse selection
 		num, err := strconv.Atoi(input)
 		if err != nil || num < 1 || num > len(projects) {
-			fmt.Printf("\n❌ Please enter a number between 1 and %d\n",
-				len(projects))
+			printer.NewLine(1)
+			printer.Errorf("Please enter a number between 1 and %d\n", len(projects))
 			continue
 		}
 
@@ -589,20 +606,21 @@ func deleteProject(ctx context.Context) error {
 	}
 
 	// Print project details with fancy formatting
-	fmt.Println("\n   Project Deletion")
-	fmt.Println("======================")
-	fmt.Printf("\nProject Details:")
-	fmt.Printf("\n• Name: %s", project.Name)
-	fmt.Printf("\n• Slug: %s\n", project.Slug)
+	printer.NewLine(1)
+	printer.Headerln("   Project Deletion   ")
+	printer.NewLine(1)
+	printer.Infoln("Project Details:")
+	printer.Infof("• Name: %s\n", project.Name)
+	printer.Infof("• Slug: %s\n", project.Slug)
 
 	// Warning message with fancy formatting
-	fmt.Println("\n  ⚠️WARNING!⚠️")
-	fmt.Println("================")
-	fmt.Println("\nThis action will permanently delete:")
-	fmt.Println("• All deployments")
-	fmt.Println("• All logs")
-	fmt.Println("• All associated resources")
-	fmt.Println("")
+	printer.NewLine(1)
+	printer.Headerln("  ⚠️WARNING!⚠️  ")
+	printer.Infoln("This action will permanently delete:")
+	printer.Infoln("• All deployments")
+	printer.Infoln("• All logs")
+	printer.Infoln("• All associated resources")
+	printer.NewLine(1)
 
 	// Confirmation prompt with fancy formatting
 	deletePrompt := fmt.Sprintf("Type 'Yes' to confirm deletion of '%s': ", project.Name)
@@ -610,9 +628,11 @@ func deleteProject(ctx context.Context) error {
 
 	if confirmation != "Yes" {
 		if confirmation == "yes" {
-			fmt.Println("\nError: You must type 'Yes' with uppercase Y to confirm deletion")
+			printer.NewLine(1)
+			printer.Errorln("Error: You must type 'Yes' with uppercase Y to confirm deletion")
 		}
-		fmt.Println("\nProject deletion canceled")
+		printer.NewLine(1)
+		printer.Infoln("Project deletion canceled")
 		return nil
 	}
 
@@ -629,9 +649,10 @@ func deleteProject(ctx context.Context) error {
 		return eris.Wrap(err, "Failed to parse response")
 	}
 
-	fmt.Println("\n  Success!")
-	fmt.Println("============")
-	fmt.Printf("\n✅ Project deleted: %s (%s)\n", project.Name, project.Slug)
+	printer.NewLine(1)
+	printer.Infoln("  Success!  ")
+	printer.SectionDivider("-", 12)
+	printer.Successf("Project deleted: %s (%s)\n", project.Name, project.Slug)
 
 	// Remove project from config
 	config, err := GetCurrentConfig()
@@ -662,8 +683,9 @@ func updateProject(ctx context.Context) error {
 	// set update to true
 	p.update = true
 
-	fmt.Println("\n  Project Update")
-	fmt.Println("==================")
+	printer.NewLine(1)
+	printer.Infoln("  Project Update  ")
+	printer.SectionDivider("-", 18)
 
 	// get project input
 	err = p.projectInput(ctx, regions)
@@ -671,7 +693,8 @@ func updateProject(ctx context.Context) error {
 		return eris.Wrap(err, "Failed to get project input")
 	}
 
-	fmt.Println("\nUpdating project...")
+	printer.NewLine(1)
+	printer.Infoln("Updating project...")
 
 	// Send request
 	url := fmt.Sprintf(projectURLPattern, baseURL, p.OrgID) + "/" + p.ID
@@ -693,35 +716,36 @@ func updateProject(ctx context.Context) error {
 		return eris.Wrap(err, "Failed to parse response")
 	}
 
-	fmt.Printf("\nProject '%s' updated successfully!\n", p.Name)
-	fmt.Printf("Project Details:\n")
-	fmt.Printf("• Name: %s\n", p.Name)
-	fmt.Printf("• Slug: %s\n", p.Slug)
-	fmt.Printf("• ID: %s\n", p.ID)
-	fmt.Printf("• Repository URL: %s\n", p.RepoURL)
-	fmt.Printf("• Repository Path: %s\n", p.RepoPath)
-	fmt.Printf("• Tick Rate: %d\n", p.Config.TickRate)
-	fmt.Printf("• Regions:\n")
+	printer.NewLine(1)
+	printer.Successf("Project '%s' updated successfully!\n", p.Name)
+	printer.Infoln("Project Details:")
+	printer.Infof("• Name: %s\n", p.Name)
+	printer.Infof("• Slug: %s\n", p.Slug)
+	printer.Infof("• ID: %s\n", p.ID)
+	printer.Infof("• Repository URL: %s\n", p.RepoURL)
+	printer.Infof("• Repository Path: %s\n", p.RepoPath)
+	printer.Infof("• Tick Rate: %d\n", p.Config.TickRate)
+	printer.Infoln("• Regions:")
 	for _, region := range p.Config.Region {
-		fmt.Printf("    - %s\n", region)
+		printer.Infof("    - %s\n", region)
 	}
-	fmt.Printf("• Discord Configuration:\n")
+	printer.Infoln("• Discord Configuration:")
 	if p.Config.Discord.Enabled {
-		fmt.Printf("  - Enabled: Yes\n")
-		fmt.Printf("  - Channel ID: %s\n", p.Config.Discord.Channel)
-		fmt.Printf("  - Bot Token: %s\n", p.Config.Discord.Token)
+		printer.Infoln("  - Enabled: Yes")
+		printer.Infof("  - Channel ID: %s\n", p.Config.Discord.Channel)
+		printer.Infof("  - Bot Token: %s\n", p.Config.Discord.Token)
 	} else {
-		fmt.Printf("  - Enabled: No\n")
+		printer.Infoln("  - Enabled: No")
 	}
-	fmt.Printf("• Slack Configuration:\n")
+	printer.Infoln("• Slack Configuration:")
 	if p.Config.Slack.Enabled {
-		fmt.Printf("  - Enabled: Yes\n")
-		fmt.Printf("  - Channel ID: %s\n", p.Config.Slack.Channel)
-		fmt.Printf("  - Token: %s\n", p.Config.Slack.Token)
+		printer.Infoln("  - Enabled: Yes")
+		printer.Infof("  - Channel ID: %s\n", p.Config.Slack.Channel)
+		printer.Infof("  - Token: %s\n", p.Config.Slack.Token)
 	} else {
-		fmt.Printf("  - Enabled: No\n")
+		printer.Infoln("  - Enabled: No")
 	}
-	fmt.Printf("• Avatar URL: %s\n", p.AvatarURL)
+	printer.Infof("• Avatar URL: %s\n", p.AvatarURL)
 	return nil
 }
 
@@ -795,26 +819,27 @@ func (p *project) inputTickRate(ctx context.Context) error {
 		case <-ctx.Done():
 			return ctx.Err()
 		default:
-			fmt.Println("\n  Tick Rate Configuration")
-			fmt.Println("===========================")
+			printer.NewLine(1)
+			printer.Headerln("  Tick Rate Configuration  ")
 			var defaultValStr string
 			if p.Config.TickRate != 0 {
-				fmt.Printf("\nCurrent tick rate: %d\n", p.Config.TickRate)
+				printer.Infof("Current tick rate: %d\n", p.Config.TickRate)
 				defaultValStr = strconv.Itoa(p.Config.TickRate)
 			} else {
-				fmt.Print("\nEnter tick rate for your project:\n")
+				printer.Infoln("Enter tick rate for your project:")
 				defaultValStr = "1"
 			}
-			fmt.Print()
 
 			tickRateStr := getInput("  └─ Examples: 10, 20, 30", defaultValStr)
 
 			p.Config.TickRate, _ = strconv.Atoi(tickRateStr)
 			if p.Config.TickRate <= 0 {
-				fmt.Printf("\n❌ Invalid input. Please enter a non-zero positive number\n")
+				printer.NewLine(1)
+				printer.Errorln("Invalid input. Please enter a non-zero positive number")
 				continue
 			}
-			fmt.Printf("\n✅ Tick rate set to: %d\n", p.Config.TickRate)
+			printer.NewLine(1)
+			printer.Successf("Tick rate set to: %d\n", p.Config.TickRate)
 			return nil
 		}
 	}
@@ -852,14 +877,15 @@ func (p *project) promptEnableNotifications(ctx context.Context, serviceName str
 	case <-ctx.Done():
 		return false, ctx.Err()
 	default:
-		fmt.Printf("\n  %s Notification Configuration\n", serviceName)
-		fmt.Printf("================================")
-		prompt := fmt.Sprintf("\nDo you want to set up %s notifications? (y/n)", serviceName)
+		printer.NewLine(1)
+		printer.Headerf("  %s Notification Configuration\n", serviceName)
+		prompt := fmt.Sprintf("Do you want to set up %s notifications? (y/n)", serviceName)
 
 		confirmation := getInput(prompt, "y")
 
 		if strings.ToLower(confirmation) != "y" {
-			fmt.Printf("\n✅ Skipping %s configuration\n", serviceName)
+			printer.NewLine(1)
+			printer.Successf("Skipping %s configuration\n", serviceName)
 			return false, nil
 		}
 
@@ -872,7 +898,8 @@ func (p *project) promptForToken(ctx context.Context, config notificationConfig)
 	case <-ctx.Done():
 		return "", ctx.Err()
 	default:
-		prompt := fmt.Sprintf("\nEnter %s %s", config.name, config.tokenName)
+		printer.NewLine(1)
+		prompt := fmt.Sprintf("Enter %s %s", config.name, config.tokenName)
 		token := getInput(prompt, "")
 		return token, nil
 	}
@@ -894,7 +921,8 @@ func (p *project) showSuccessMessage(ctx context.Context, serviceName string) er
 	case <-ctx.Done():
 		return ctx.Err()
 	default:
-		fmt.Printf("\n✅ %s notifications configured successfully\n", serviceName)
+		printer.NewLine(1)
+		printer.Successf("%s notifications configured successfully\n", serviceName)
 		return nil
 	}
 }
@@ -944,18 +972,21 @@ func (p *project) chooseRegion(ctx context.Context, regions []string) error {
 		default:
 			aborted, err := p.runRegionSelector(ctx, regions)
 			if aborted {
-				fmt.Printf("\n❌ %v\n", err)
+				printer.NewLine(1)
+				printer.Errorln(err.Error())
 				return err
 			}
 			if err != nil {
-				fmt.Printf("\n❌ Error: %v\n", err)
+				printer.NewLine(1)
+				printer.Errorf("Error: %v\n", err)
 				continue
 			}
 			if len(p.Config.Region) > 0 {
 				return nil
 			}
-			fmt.Printf("\n⚠️  Error: At least one region must be selected")
-			fmt.Printf("\n🔄 Please try again\n")
+			printer.NewLine(1)
+			printer.Errorln("At least one region must be selected")
+			printer.Infoln("🔄 Please try again")
 		}
 	}
 }
@@ -1035,13 +1066,15 @@ func handleMultipleProjects(ctx context.Context, projectID string, projects []pr
 // handleNoProjects handles the case when there are no projects.
 func handleNoProjects(ctx context.Context) (string, error) {
 	// Confirmation prompt
+	printer.NewLine(1)
 	confirmation := getInput(
-		"\nYou don't have any projects in this organization. Do you want to create a new project now? (y/n)",
+		"You don't have any projects in this organization. Do you want to create a new project now? (y/n)",
 		"y",
 	)
 
 	if strings.ToLower(confirmation) != "y" {
-		fmt.Println("\n❌ Project creation canceled")
+		printer.NewLine(1)
+		printer.Errorln("Project creation canceled")
 		return "", nil
 	}
 
@@ -1053,15 +1086,16 @@ func handleNoProjects(ctx context.Context) (string, error) {
 }
 
 func (p *project) inputAvatarURL(ctx context.Context) error {
-	fmt.Println("\n  Avatar URL Configuration")
-	fmt.Println("================================")
+	printer.NewLine(1)
+	printer.Headerln("  Avatar URL Configuration  ")
 
 	for {
 		select {
 		case <-ctx.Done():
 			return ctx.Err()
 		default:
-			avatarURL := getInput("\nEnter avatar URL", p.AvatarURL)
+			printer.NewLine(1)
+			avatarURL := getInput("Enter avatar URL", p.AvatarURL)
 
 			if avatarURL == "" {
 				// No avatar URL provided
@@ -1070,7 +1104,8 @@ func (p *project) inputAvatarURL(ctx context.Context) error {
 			}
 
 			if !isValidURL(avatarURL) {
-				fmt.Printf("\n❌ Error: Invalid URL\n")
+				printer.NewLine(1)
+				printer.Errorln("Invalid URL")
 				continue
 			}
 
diff --git a/cmd/world/forge/repo.go b/cmd/world/forge/repo.go
index 1762c0e0..aa3ec95c 100644
--- a/cmd/world/forge/repo.go
+++ b/cmd/world/forge/repo.go
@@ -8,6 +8,7 @@ import (
 	"strings"
 
 	"github.com/rotisserie/eris"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 // identifyProvider determines the Git provider based on the URL's host.
@@ -93,7 +94,7 @@ func validateGitHub(ctx context.Context, repoURL, token, apiBaseURL string) erro
 	defer resp.Body.Close()
 
 	if resp.StatusCode == http.StatusOK {
-		fmt.Println("✅ GitHub repository and token validation successful!")
+		printer.Successln("GitHub repository and token validation successful!")
 		return nil
 	}
 	return fmt.Errorf("GitHub validation failed: %s", resp.Status)
@@ -130,7 +131,7 @@ func validateGitLab(ctx context.Context, repoURL, token, apiBaseURL string) erro
 	defer resp.Body.Close()
 
 	if resp.StatusCode == http.StatusOK {
-		fmt.Println("GitLab repository and token are valid!")
+		printer.Successln("GitLab repository and token are valid!")
 		return nil
 	}
 	return fmt.Errorf("GitLab validation failed: %s", resp.Status)
@@ -168,7 +169,7 @@ func validateBitbucket(ctx context.Context, repoURL, token, apiBaseURL string) e
 	defer resp.Body.Close()
 
 	if resp.StatusCode == http.StatusOK {
-		fmt.Println("Bitbucket repository and token are valid!")
+		printer.Successln("Bitbucket repository and token are valid!")
 		return nil
 	}
 	return fmt.Errorf("bitbucket validation failed: %s", resp.Status)
diff --git a/cmd/world/forge/user.go b/cmd/world/forge/user.go
index f047c073..0c606e58 100644
--- a/cmd/world/forge/user.go
+++ b/cmd/world/forge/user.go
@@ -2,10 +2,10 @@ package forge
 
 import (
 	"context"
-	"fmt"
 	"net/http"
 
 	"github.com/rotisserie/eris"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 type User struct {
@@ -71,7 +71,8 @@ func updateUser(ctx context.Context) error {
 		return eris.Wrap(err, "Failed to update user")
 	}
 
-	fmt.Println("\n✅ User updated successfully")
+	printer.NewLine(1)
+	printer.Success("User updated successfully")
 
 	return nil
 }
@@ -82,17 +83,20 @@ func inputUserName(ctx context.Context, currentUserName string) (string, error)
 		case <-ctx.Done():
 			return "", ctx.Err()
 		default:
-			fmt.Println("\n   Update User Name")
-			fmt.Println("======================")
+			printer.NewLine(1)
+			printer.Header("   Update User Name   ")
 
-			name := getInput("\nEnter name", currentUserName)
+			printer.NewLine(1)
+			name := getInput("Enter name", currentUserName)
 
 			if name == "" {
-				fmt.Printf("\n❌ Error: Name cannot be empty\n")
+				printer.NewLine(1)
+				printer.Errorf("Error: Name cannot be empty\n")
 				continue
 			}
 
-			fmt.Printf("\n✅ Name updated to: %s\n", name)
+			printer.NewLine(1)
+			printer.Successf("Name updated to: %s\n", name)
 			return name, nil
 		}
 	}
@@ -104,16 +108,19 @@ func inputUserEmail(ctx context.Context, currentUserEmail string) (string, error
 		case <-ctx.Done():
 			return "", ctx.Err()
 		default:
-			fmt.Println("\n   Update User Email")
-			fmt.Println("=======================")
+			printer.NewLine(1)
+			printer.Header("   Update User Email   ")
 
-			email := getInput("\nEnter email", currentUserEmail)
+			printer.NewLine(1)
+			email := getInput("Enter email", currentUserEmail)
 			if !isValidEmail(email) {
-				fmt.Printf("\n❌ Error: Invalid email format\n")
+				printer.NewLine(1)
+				printer.Errorf("Error: Invalid email format\n")
 				continue
 			}
 
-			fmt.Printf("\n✅ Email updated to: %s\n", email)
+			printer.NewLine(1)
+			printer.Successf("Email updated to: %s\n", email)
 			return email, nil
 		}
 	}
@@ -126,15 +133,19 @@ func inputUserAvatarURL(ctx context.Context, // TODO: refactor
 		case <-ctx.Done():
 			return "", ctx.Err()
 		default:
-			fmt.Println("\n  Update User Avatar URL")
-			fmt.Println("=============================")
-			avatarURL := getInput("\nEnter avatar URL", currentUserAvatarURL)
+			printer.NewLine(1)
+			printer.Header("   Update User Avatar URL   ")
+
+			printer.NewLine(1)
+			avatarURL := getInput("Enter avatar URL", currentUserAvatarURL)
 			if !isValidURL(avatarURL) {
-				fmt.Printf("\n❌ Error: Invalid URL format\n")
+				printer.NewLine(1)
+				printer.Errorf("Error: Invalid URL format\n")
 				continue
 			}
 
-			fmt.Printf("\n✅ Avatar URL updated to: %s\n", avatarURL)
+			printer.NewLine(1)
+			printer.Successf("Avatar URL updated to: %s\n", avatarURL)
 			return avatarURL, nil
 		}
 	}
diff --git a/cmd/world/root/root.go b/cmd/world/root/root.go
index 48349011..11b2a596 100644
--- a/cmd/world/root/root.go
+++ b/cmd/world/root/root.go
@@ -2,7 +2,6 @@ package root
 
 import (
 	"context"
-	"fmt"
 	"net/http"
 	"os"
 	"os/signal"
@@ -19,6 +18,7 @@ import (
 	"pkg.world.dev/world-cli/cmd/world/evm"
 	"pkg.world.dev/world-cli/cmd/world/forge"
 	"pkg.world.dev/world-cli/common/logger"
+	"pkg.world.dev/world-cli/common/printer"
 	"pkg.world.dev/world-cli/tea/style"
 )
 
@@ -123,17 +123,10 @@ func checkLatestVersion() error {
 		}
 
 		if currentVersion.LessThan(latestVersion) {
-			notificationStyle := lipgloss.NewStyle().
-				Foreground(lipgloss.Color("178"))
-				// Bright yellow, good for notifications
-			fmt.Printf(
-				"\n%s\n",
-				notificationStyle.Render(fmt.Sprintf("New version %s is available!", latestVersion.String())),
-			)
-			fmt.Printf(
-				"%s\n\n",
-				notificationStyle.Render("To update, run: go install pkg.world.dev/world-cli/cmd/world@latest"),
-			)
+			printer.NewLine(1)
+			printer.Notificationf("New version %s is available!", latestVersion.String())
+
+			printer.Notificationln("To update, run: go install pkg.world.dev/world-cli/cmd/world@latest")
 		}
 	}
 	return nil
@@ -164,9 +157,9 @@ func contextWithSigterm(ctx context.Context) context.Context {
 
 		select {
 		case <-signalCh:
-			fmt.Println(textStyle.Render("Interrupt signal received. Terminating..."))
+			printer.Infoln(textStyle.Render("Interrupt signal received. Terminating..."))
 		case <-ctx.Done():
-			fmt.Println(textStyle.Render("Cancellation signal received. Terminating..."))
+			printer.Infoln(textStyle.Render("Cancellation signal received. Terminating..."))
 		}
 	}()
 
diff --git a/cmd/world/root/version.go b/cmd/world/root/version.go
index adbcef70..ebdb9b6b 100644
--- a/cmd/world/root/version.go
+++ b/cmd/world/root/version.go
@@ -1,9 +1,8 @@
 package root
 
 import (
-	"fmt"
-
 	"github.com/spf13/cobra"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 var AppVersion string
@@ -18,6 +17,6 @@ var versionCmd = &cobra.Command{
 This information is useful when reporting issues, checking for updates,
 or verifying compatibility with World Engine features.`,
 	Run: func(_ *cobra.Command, _ []string) {
-		fmt.Printf("World CLI %s\n", AppVersion)
+		printer.Infof("World CLI %s\n", AppVersion)
 	},
 }
diff --git a/common/docker/client_container.go b/common/docker/client_container.go
index 1f75bebc..fa11c491 100644
--- a/common/docker/client_container.go
+++ b/common/docker/client_container.go
@@ -16,6 +16,7 @@ import (
 	"github.com/rotisserie/eris"
 	"pkg.world.dev/world-cli/cmd/world/forge"
 	"pkg.world.dev/world-cli/common/docker/service"
+	"pkg.world.dev/world-cli/common/printer"
 	"pkg.world.dev/world-cli/tea/component/multispinner"
 	"pkg.world.dev/world-cli/tea/style"
 )
@@ -200,7 +201,7 @@ func (c *Client) logMultipleContainers(ctx context.Context, services ...service.
 				default:
 					err := c.logContainerOutput(ctx, id, i)
 					if err != nil && !errors.Is(err, context.Canceled) {
-						fmt.Printf("Error logging container %s: %v. Reattaching...\n", id, err)
+						printer.Infof("Error logging container %s: %v. Reattaching...\n", id, err)
 						time.Sleep(2 * time.Second) //nolint:gomnd // Sleep for 2 seconds before reattaching
 					}
 				}
@@ -271,10 +272,10 @@ func (c *Client) logContainerOutput(ctx context.Context, containerID string, sty
 		switch streamType {
 		case 1: // Stdout
 			// TODO: what content should be printed for stdout?
-			fmt.Printf("[%s] %s", style.ForegroundPrint(containerID, colors[styleNumber]), cleanLog)
+			printer.Infof("[%s] %s", style.ForegroundPrint(containerID, colors[styleNumber]), cleanLog)
 		case 2: //nolint:gomnd // Stderr
 			// TODO: what content should be printed for stderr?
-			fmt.Printf("[%s] %s", style.ForegroundPrint(containerID, colors[styleNumber]), cleanLog)
+			printer.Infof("[%s] %s", style.ForegroundPrint(containerID, colors[styleNumber]), cleanLog)
 		}
 	}
 
diff --git a/common/docker/client_image.go b/common/docker/client_image.go
index 26afacbe..7e96b0f7 100644
--- a/common/docker/client_image.go
+++ b/common/docker/client_image.go
@@ -23,6 +23,7 @@ import (
 	"github.com/vbauerster/mpb/v8/decor"
 	"pkg.world.dev/world-cli/cmd/world/forge"
 	"pkg.world.dev/world-cli/common/docker/service"
+	"pkg.world.dev/world-cli/common/printer"
 	"pkg.world.dev/world-cli/tea/component/multispinner"
 	"pkg.world.dev/world-cli/tea/style"
 )
@@ -403,7 +404,7 @@ func (c *Client) pullImages(ctx context.Context, services ...service.Service) er
 
 			if err != nil {
 				// Handle the error: log it and send it to the error channel
-				fmt.Printf("Error pulling image %s: %v\n", imageName, err)
+				printer.Infof("Error pulling image %s: %v\n", imageName, err)
 				errChan <- eris.Wrapf(err, "error pulling image %s", imageName)
 
 				// Stop the progress bar without clearing
@@ -420,7 +421,7 @@ func (c *Client) pullImages(ctx context.Context, services ...service.Service) er
 				select {
 				case <-ctx.Done():
 					// Handle context cancellation
-					fmt.Printf("Pulling of image %s was canceled\n", imageName)
+					printer.Infof("Pulling of image %s was canceled\n", imageName)
 					bar.Abort(false) // Stop the progress bar without clearing
 					return
 				default:
@@ -520,7 +521,7 @@ func (c *Client) pushImages(ctx context.Context, pushTo string, authString strin
 
 			if err != nil {
 				// Handle the error: log it and send it to the error channel
-				fmt.Printf("Error pushing image %s: %v\n", imageName, err)
+				printer.Infof("Error pushing image %s: %v\n", imageName, err)
 				errChan <- eris.Wrapf(err, "error pushing image %s", imageName)
 
 				// Stop the progress bar without clearing
@@ -537,7 +538,7 @@ func (c *Client) pushImages(ctx context.Context, pushTo string, authString strin
 				select {
 				case <-ctx.Done():
 					// Handle context cancellation
-					fmt.Printf("Pushing image %s was canceled\n", imageName)
+					printer.Infof("Pushing image %s was canceled\n", imageName)
 					bar.Abort(false) // Stop the progress bar without clearing
 					return
 				default:
diff --git a/common/logger/init.go b/common/logger/init.go
index aaf61475..dadacdfa 100644
--- a/common/logger/init.go
+++ b/common/logger/init.go
@@ -2,11 +2,11 @@ package logger
 
 import (
 	"bytes"
-	"fmt"
 
 	"github.com/rs/zerolog"
 	"github.com/rs/zerolog/log"
 	"github.com/spf13/cobra"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 const (
@@ -55,8 +55,9 @@ func PrintLogs() {
 		// Extract the logs from the buffer and print them
 		logs := logBuffer.String()
 		if len(logs) > 0 {
-			fmt.Println("\n----- Log -----")
-			fmt.Println(logs)
+			printer.NewLine(1)
+			printer.Infoln("----- Log -----")
+			printer.Infoln(logs)
 		}
 	}
 }
diff --git a/common/logger/logger.go b/common/logger/logger.go
index 53e04323..aced24b8 100644
--- a/common/logger/logger.go
+++ b/common/logger/logger.go
@@ -119,20 +119,20 @@ func FatalWithFields(msg string, kv map[string]interface{}) {
 // Printf standard printf with debug mode validation.
 func Printf(format string, v ...interface{}) {
 	if verboseMode {
-		fmt.Printf(format, v...)
+		fmt.Printf(format, v...) //nolint:forbidigo // Need customer friendly output
 	}
 }
 
 // Println standard println with debug mode validation.
 func Println(v ...interface{}) {
 	if verboseMode {
-		fmt.Println(v...)
+		fmt.Println(v...) //nolint:forbidigo // Need customer friendly output
 	}
 }
 
 // Print standard print with debug mode validation.
 func Print(v ...interface{}) {
 	if verboseMode {
-		fmt.Print(v...)
+		fmt.Print(v...) //nolint:forbidigo // Need customer friendly output
 	}
 }
diff --git a/common/printer/printer.go b/common/printer/printer.go
new file mode 100644
index 00000000..8f63ed24
--- /dev/null
+++ b/common/printer/printer.go
@@ -0,0 +1,102 @@
+package printer
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/charmbracelet/lipgloss"
+	"github.com/guumaster/logsymbols"
+)
+
+var (
+	headerStyle       = lipgloss.NewStyle().Bold(true).Underline(true)
+	successStyle      = lipgloss.NewStyle().Bold(true)
+	errorStyle        = lipgloss.NewStyle().Bold(true)
+	notificationStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("178")) // Bright yellow, good for notifications
+)
+
+func Success(msg string) {
+	//nolint:forbidigo // Need customer friendly output
+	fmt.Print(successStyle.Render(string(logsymbols.Success) + " " + msg))
+}
+
+func Successln(msg string) {
+	//nolint:forbidigo // Need customer friendly output
+	fmt.Println(successStyle.Render(string(logsymbols.Success) + " " + msg))
+}
+
+func Successf(format string, args ...any) {
+	msg := successStyle.Render(string(logsymbols.Success) + " " + fmt.Sprintf(format, args...))
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Error(msg string) {
+	//nolint:forbidigo // Need customer friendly output
+	fmt.Print(errorStyle.Render(string(logsymbols.Error) + " " + msg))
+}
+
+func Errorln(msg string) {
+	//nolint:forbidigo // Need customer friendly output
+	fmt.Println(errorStyle.Render(string(logsymbols.Error) + " " + msg))
+}
+
+func Errorf(format string, args ...any) {
+	msg := errorStyle.Render(string(logsymbols.Error) + " " + fmt.Sprintf(format, args...))
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+// Info prints a message with newline.
+func Info(msg string) {
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Infoln(msg string) {
+	fmt.Println(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Infof(format string, args ...any) {
+	msg := fmt.Sprintf(format, args...)
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Header(msg string) {
+	fmt.Print(headerStyle.Render(msg)) //nolint:forbidigo // Need customer friendly output
+}
+
+func Headerln(msg string) {
+	fmt.Println(headerStyle.Render(msg)) //nolint:forbidigo // Need customer friendly output
+}
+
+func Headerf(format string, args ...any) {
+	msg := headerStyle.Render(fmt.Sprintf(format, args...))
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Notification(msg string) {
+	fmt.Print(notificationStyle.Render(msg)) //nolint:forbidigo // Need customer friendly output
+}
+
+func Notificationf(format string, args ...any) {
+	msg := notificationStyle.Render(fmt.Sprintf(format, args...))
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Notificationln(msg string) {
+	fmt.Println(notificationStyle.Render(msg)) //nolint:forbidigo // Need customer friendly output
+}
+
+func NewLine(numberOfLines int) {
+	if numberOfLines <= 0 {
+		numberOfLines = 1
+	}
+	fmt.Print(strings.Repeat("\n", numberOfLines)) //nolint:forbidigo // Need customer friendly output
+}
+
+// SectionDivider prints a divider line of a given symbol and length.
+// Default length is 1.
+func SectionDivider(symbol string, length int) {
+	if length <= 0 {
+		length = 1
+	}
+	fmt.Println(strings.Repeat(symbol, length)) //nolint:forbidigo // Need customer friendly output
+}
diff --git a/common/teacmd/git_test.go b/common/teacmd/git_test.go
index fb4e1cd0..10fecd23 100644
--- a/common/teacmd/git_test.go
+++ b/common/teacmd/git_test.go
@@ -69,7 +69,7 @@ func cleanUpDir(targetDir string) {
 	if _, err := os.Stat(targetDir); !os.IsNotExist(err) {
 		err := os.RemoveAll(targetDir)
 		if err != nil {
-			fmt.Println(err)
+			fmt.Println(err) //nolint:forbidigo // test
 		}
 	}
 }
diff --git a/common/tomlutil/toml_test.go b/common/tomlutil/toml_test.go
index 94205c76..47d8fe86 100644
--- a/common/tomlutil/toml_test.go
+++ b/common/tomlutil/toml_test.go
@@ -18,7 +18,7 @@ func (s *TOMLTestSuite) SetupTest() {
 	s.tempDir = s.T().TempDir()
 }
 
-// createTestFile is a helper function that creates a temporary TOML file with given content
+// createTestFile is a helper function that creates a temporary TOML file with given content.
 func (s *TOMLTestSuite) createTestFile(content string) string {
 	tmpFile := filepath.Join(s.tempDir, "test.toml")
 	err := os.WriteFile(tmpFile, []byte(content), 0644)
@@ -180,7 +180,7 @@ func (s *TOMLTestSuite) TestCreateTOMLFile() {
 	s.Equal("value", config["section1"].(map[string]any)["key"])
 }
 
-// TestTOMLSuite runs the test suite
+// TestTOMLSuite runs the test suite.
 func TestTOMLSuite(t *testing.T) {
 	suite.Run(t, new(TOMLTestSuite))
 }
diff --git a/tea/component/program/program.go b/tea/component/program/program.go
index 830f8317..a2e21b84 100644
--- a/tea/component/program/program.go
+++ b/tea/component/program/program.go
@@ -3,7 +3,6 @@ package program
 import (
 	"bytes"
 	"context"
-	"fmt"
 	"os"
 	"strings"
 
@@ -13,6 +12,7 @@ import (
 	"github.com/charmbracelet/lipgloss"
 	"github.com/muesli/reflow/wrap"
 	"golang.org/x/term"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 // An interface describing the parts of BubbleTea's Program that we actually use.
@@ -78,10 +78,10 @@ func (p *fakeProgram) Start() error {
 func (p *fakeProgram) Send(msg tea.Msg) {
 	switch msg := msg.(type) {
 	case StatusMsg:
-		fmt.Println(msg)
+		printer.Infoln(string(msg))
 	case PsqlMsg:
 		if msg != nil {
-			fmt.Println(*msg)
+			printer.Infoln(*msg)
 		}
 	}
 
diff --git a/tea/style/util.go b/tea/style/util.go
index a09e9d9d..00589312 100644
--- a/tea/style/util.go
+++ b/tea/style/util.go
@@ -1,9 +1,8 @@
 package style
 
 import (
-	"fmt"
-
 	"github.com/charmbracelet/lipgloss"
+	"pkg.world.dev/world-cli/common/printer"
 )
 
 func ContextPrint(title, titleColor, subject, object string) {
@@ -11,7 +10,7 @@ func ContextPrint(title, titleColor, subject, object string) {
 	arrowStr := ForegroundPrint("→", "241")
 	subjectStr := ForegroundPrint(subject, "4")
 
-	fmt.Printf("%s %s %s %s ", titleStr, arrowStr, subjectStr, object)
+	printer.Infof("%s %s %s %s ", titleStr, arrowStr, subjectStr, object)
 }
 
 func ForegroundPrint(text string, color string) string {
diff --git a/test b/test
deleted file mode 160000
index b6682e47..00000000
--- a/test
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit b6682e47445122906361f8db05028a3b96e1e48e


Reviews:
user: login: greptile-apps[bot]
body: <sub>30 file(s) reviewed, 24 comment(s)</sub>
<sub>[Edit PR Review Bot Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews&utm_content=world-cli_154)</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T14:55:15Z
user: login: graphite-app[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T15:08:31Z
user: login: ezavada
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-06T15:34:14Z

Review comments:
diff_hunk: @@ -20,7 +20,7 @@ linters:
     - errorlint
     - exhaustive
     - fatcontext
-    # - forbidigo
+    - forbidigo
path: .golangci.yaml
user: login: greptile-apps[bot]
body: style: The forbidigo linter is enabled but lacks configuration settings to specify which patterns to forbid (e.g., fmt.Print). Consider adding settings to explicitly define forbidden patterns.
created_at: 2025-05-06T14:50:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -46,7 +45,7 @@ need to free up system resources.`,
 			return err
 		}
 
-		fmt.Println("Cardinal successfully stopped")
+		printer.Infoln("Cardinal successfully stopped")
path: cmd/world/cardinal/stop.go
user: login: greptile-apps[bot]
body: style: Consider using printer.Successln instead of printer.Infoln for successful completion messages
created_at: 2025-05-06T14:50:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -840,7 +841,7 @@ func (s *ForgeTestSuite) TestDeploy() {
 				tea.WithInput(nil),
 			)
 			if regionSelector == nil {
-				print("failed to create region selector")
+				print("failed to create region selector") //nolint:forbidigo // test
path: cmd/world/forge/forge_test.go
user: login: greptile-apps[bot]
body: style: print statement should use printer.Errorln for consistency with other error messages in the file
created_at: 2025-05-06T14:51:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -855,7 +856,7 @@ func (s *ForgeTestSuite) TestDeploy() {
 						// wait for 100ms to make sure the action is processed
 						time.Sleep(100 * time.Millisecond)
 					} else {
-						print("region selector is nil")
+						print("region selector is nil") //nolint:forbidigo // test
path: cmd/world/forge/forge_test.go
user: login: greptile-apps[bot]
body: style: print statement should use printer.Errorln for consistency with other error messages in the file
created_at: 2025-05-06T14:51:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -389,23 +406,26 @@ func getRoleInput(allowNone bool) string {
 		opts = "owner, admin, or member"
 	}
 	for {
-		fmt.Println("\n Role Assignment")
-		fmt.Println("----------------")
-		fmt.Printf("Available Roles: %s\n", opts)
-		role := getInput("\nEnter organization role", "member")
+		printer.NewLine(1)
+		printer.Headerln(" Role Assignment  ")
+		printer.Infof("Available Roles: %s\n", opts)
+		role := getInput("Enter organization role", "member")
 		if allowNone && role == "none" {
-			fmt.Print("\nWarning: Role \"none\" removes user from this organization")
-			answer := getInput("\nConfirm removal? (Yes/no)", "no")
+			printer.NewLine(1)
+			printer.Infoln("Warning: Role \"none\" removes user from this organization")
+			answer := getInput("Confirm removal? (Yes/no)", "no")
 			if answer != "Yes" {
path: cmd/world/forge/organization.go
user: login: greptile-apps[bot]
body: style: consider using printer.Warnln instead of printer.Infoln for role removal warning
created_at: 2025-05-06T14:52:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -208,7 +212,7 @@ func streamLogs(ctx context.Context,
 				// Stream ended normally
 				return nil
 			}
-			log.Println(stream.Msg().Log)
+			log.Println(stream.Msg().GetLog())
path: cmd/world/forge/log.go
user: login: greptile-apps[bot]
body: style: log.Println should be replaced with printer.Infoln for consistency with other logging
created_at: 2025-05-06T14:52:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -288,7 +291,8 @@ func handleArgusIDToken(response []byte, result interface{}) error {
 	case "pending":
 		return errPending
 	case "success":
-		fmt.Println("\nLogin token received successfully!")
+		printer.NewLine(1)
+		printer.Successln("Login token received successfully!")
 		return nil
 	default:
 		return eris.New(fmt.Sprintf("Status: %s", tokenStruct.Status))
path: cmd/world/forge/login.go
user: login: greptile-apps[bot]
body: style: Replace `fmt.Sprintf` with `printer.Errorf` for consistent error message formatting
created_at: 2025-05-06T14:52:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -107,14 +108,14 @@ func handlePostLoginConfig(ctx context.Context, config *globalconfig.GlobalConfi
 func handleKnownRepoConfig(ctx context.Context, config *globalconfig.GlobalConfig) error {
 	proj, err := getSelectedProject(ctx)
 	if err != nil {
-		fmt.Println("⚠️ Warning: Failed to get project", config.ProjectID, ":", err)
+		printer.Infoln("⚠️ Warning: Failed to get project " + config.ProjectID + ": " + err.Error())
path: cmd/world/forge/login.go
user: login: greptile-apps[bot]
body: style: Use `printer.Infof` instead of string concatenation for better performance and consistency
created_at: 2025-05-06T14:52:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -360,19 +365,21 @@ func (p *project) getForgeProjectNameFromWorldToml() error {
 
 func (p *project) validateAndSetName(name string) error {
 	if name == "" {
-		fmt.Printf("\n❌ Error: Project name cannot be empty\n")
+		printer.NewLine(1)
+		printer.Errorln("Error: Project name cannot be empty")
 		return eris.New("empty name")
 	}
 
 	if len(name) > MaxProjectNameLen {
-		fmt.Printf("\n❌ Error: Project name cannot be longer than %d characters\n",
-			MaxProjectNameLen)
+		printer.NewLine(1)
+		printer.Errorf("Error: Project name cannot be longer than %d characters\n", MaxProjectNameLen)
 		return eris.New("name too long")
 	}
 
 	if strings.ContainsAny(name, "<>:\"/\\|?*") {
-		fmt.Printf("\n❌ Error: Project name contains invalid characters\n" +
-			"   Invalid characters: < > : \" / \\ | ? *\n")
+		printer.NewLine(1)
+		printer.Errorln("Error: Project name contains invalid characters" +
+			"   Invalid characters: < > : \" / \\ | ? *")
 		return eris.New("invalid characters")
path: cmd/world/forge/project.go
user: login: greptile-apps[bot]
body: style: Error message concatenation could be simplified to a single line for better readability
created_at: 2025-05-06T14:52:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -82,17 +83,20 @@ func inputUserName(ctx context.Context, currentUserName string) (string, error)
 		case <-ctx.Done():
 			return "", ctx.Err()
 		default:
-			fmt.Println("\n   Update User Name")
-			fmt.Println("======================")
+			printer.NewLine(1)
+			printer.Header("   Update User Name   ")
 
-			name := getInput("\nEnter name", currentUserName)
+			printer.NewLine(1)
+			name := getInput("Enter name", currentUserName)
 
 			if name == "" {
-				fmt.Printf("\n❌ Error: Name cannot be empty\n")
+				printer.NewLine(1)
+				printer.Errorf("Error: Name cannot be empty\n")
path: cmd/world/forge/user.go
user: login: greptile-apps[bot]
body: style: Remove unnecessary \n since printer.Errorf already handles line endings
created_at: 2025-05-06T14:52:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -82,17 +83,20 @@ func inputUserName(ctx context.Context, currentUserName string) (string, error)
 		case <-ctx.Done():
 			return "", ctx.Err()
 		default:
-			fmt.Println("\n   Update User Name")
-			fmt.Println("======================")
+			printer.NewLine(1)
+			printer.Header("   Update User Name   ")
 
-			name := getInput("\nEnter name", currentUserName)
+			printer.NewLine(1)
+			name := getInput("Enter name", currentUserName)
 
 			if name == "" {
-				fmt.Printf("\n❌ Error: Name cannot be empty\n")
+				printer.NewLine(1)
+				printer.Errorf("Error: Name cannot be empty\n")
 				continue
 			}
 
-			fmt.Printf("\n✅ Name updated to: %s\n", name)
+			printer.NewLine(1)
+			printer.Successf("Name updated to: %s\n", name)
path: cmd/world/forge/user.go
user: login: greptile-apps[bot]
body: style: Remove unnecessary \n since printer.Successf already handles line endings
created_at: 2025-05-06T14:52:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -104,16 +108,19 @@ func inputUserEmail(ctx context.Context, currentUserEmail string) (string, error
 		case <-ctx.Done():
 			return "", ctx.Err()
 		default:
-			fmt.Println("\n   Update User Email")
-			fmt.Println("=======================")
+			printer.NewLine(1)
+			printer.Header("   Update User Email   ")
 
-			email := getInput("\nEnter email", currentUserEmail)
+			printer.NewLine(1)
+			email := getInput("Enter email", currentUserEmail)
 			if !isValidEmail(email) {
-				fmt.Printf("\n❌ Error: Invalid email format\n")
+				printer.NewLine(1)
+				printer.Errorf("Error: Invalid email format\n")
path: cmd/world/forge/user.go
user: login: greptile-apps[bot]
body: style: Remove unnecessary \n since printer.Errorf already handles line endings
created_at: 2025-05-06T14:52:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -104,16 +108,19 @@ func inputUserEmail(ctx context.Context, currentUserEmail string) (string, error
 		case <-ctx.Done():
 			return "", ctx.Err()
 		default:
-			fmt.Println("\n   Update User Email")
-			fmt.Println("=======================")
+			printer.NewLine(1)
+			printer.Header("   Update User Email   ")
 
-			email := getInput("\nEnter email", currentUserEmail)
+			printer.NewLine(1)
+			email := getInput("Enter email", currentUserEmail)
 			if !isValidEmail(email) {
-				fmt.Printf("\n❌ Error: Invalid email format\n")
+				printer.NewLine(1)
+				printer.Errorf("Error: Invalid email format\n")
 				continue
 			}
 
-			fmt.Printf("\n✅ Email updated to: %s\n", email)
+			printer.NewLine(1)
+			printer.Successf("Email updated to: %s\n", email)
path: cmd/world/forge/user.go
user: login: greptile-apps[bot]
body: style: Remove unnecessary \n since printer.Successf already handles line endings
created_at: 2025-05-06T14:52:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -126,15 +133,19 @@ func inputUserAvatarURL(ctx context.Context, // TODO: refactor
 		case <-ctx.Done():
 			return "", ctx.Err()
 		default:
-			fmt.Println("\n  Update User Avatar URL")
-			fmt.Println("=============================")
-			avatarURL := getInput("\nEnter avatar URL", currentUserAvatarURL)
+			printer.NewLine(1)
+			printer.Header("   Update User Avatar URL   ")
+
+			printer.NewLine(1)
+			avatarURL := getInput("Enter avatar URL", currentUserAvatarURL)
 			if !isValidURL(avatarURL) {
-				fmt.Printf("\n❌ Error: Invalid URL format\n")
+				printer.NewLine(1)
+				printer.Errorf("Error: Invalid URL format\n")
path: cmd/world/forge/user.go
user: login: greptile-apps[bot]
body: style: Remove unnecessary \n since printer.Errorf already handles line endings
created_at: 2025-05-06T14:52:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -126,15 +133,19 @@ func inputUserAvatarURL(ctx context.Context, // TODO: refactor
 		case <-ctx.Done():
 			return "", ctx.Err()
 		default:
-			fmt.Println("\n  Update User Avatar URL")
-			fmt.Println("=============================")
-			avatarURL := getInput("\nEnter avatar URL", currentUserAvatarURL)
+			printer.NewLine(1)
+			printer.Header("   Update User Avatar URL   ")
+
+			printer.NewLine(1)
+			avatarURL := getInput("Enter avatar URL", currentUserAvatarURL)
 			if !isValidURL(avatarURL) {
-				fmt.Printf("\n❌ Error: Invalid URL format\n")
+				printer.NewLine(1)
+				printer.Errorf("Error: Invalid URL format\n")
 				continue
 			}
 
-			fmt.Printf("\n✅ Avatar URL updated to: %s\n", avatarURL)
+			printer.NewLine(1)
+			printer.Successf("Avatar URL updated to: %s\n", avatarURL)
path: cmd/world/forge/user.go
user: login: greptile-apps[bot]
body: style: Remove unnecessary \n since printer.Successf already handles line endings
created_at: 2025-05-06T14:52:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -55,8 +55,8 @@ func PrintLogs() {
 		// Extract the logs from the buffer and print them
 		logs := logBuffer.String()
 		if len(logs) > 0 {
-			fmt.Println("\n----- Log -----")
-			fmt.Println(logs)
+			printer.Infoln("\n----- Log -----")
path: common/logger/init.go
user: login: greptile-apps[bot]
body: style: Consider removing the hardcoded newline character and using printer.NewLine(1) before the log header for consistent spacing
created_at: 2025-05-06T14:53:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -119,20 +120,20 @@ func FatalWithFields(msg string, kv map[string]interface{}) {
 // Printf standard printf with debug mode validation.
 func Printf(format string, v ...interface{}) {
 	if verboseMode {
-		fmt.Printf(format, v...)
+		printer.Infof(format, v...)
 	}
path: common/logger/logger.go
user: login: greptile-apps[bot]
body: style: Printf was converted to printer.Infof but Println and Print were not. All three functions should use the same output method for consistency
created_at: 2025-05-06T14:53:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -119,20 +120,20 @@ func FatalWithFields(msg string, kv map[string]interface{}) {
 // Printf standard printf with debug mode validation.
 func Printf(format string, v ...interface{}) {
 	if verboseMode {
-		fmt.Printf(format, v...)
+		printer.Infof(format, v...)
 	}
 }
 
 // Println standard println with debug mode validation.
 func Println(v ...interface{}) {
 	if verboseMode {
-		fmt.Println(v...)
+		fmt.Println(v...) //nolint:forbidigo // Need customer friendly output
path: common/logger/logger.go
user: login: greptile-apps[bot]
body: style: Inconsistent with Printf implementation - should use printer.Infoln instead of fmt.Println with linter exception

```suggestion
		printer.Infoln(v...)
```
created_at: 2025-05-06T14:53:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -119,20 +120,20 @@ func FatalWithFields(msg string, kv map[string]interface{}) {
 // Printf standard printf with debug mode validation.
 func Printf(format string, v ...interface{}) {
 	if verboseMode {
-		fmt.Printf(format, v...)
+		printer.Infof(format, v...)
 	}
 }
 
 // Println standard println with debug mode validation.
 func Println(v ...interface{}) {
 	if verboseMode {
-		fmt.Println(v...)
+		fmt.Println(v...) //nolint:forbidigo // Need customer friendly output
 	}
 }
 
 // Print standard print with debug mode validation.
 func Print(v ...interface{}) {
 	if verboseMode {
-		fmt.Print(v...)
+		fmt.Print(v...) //nolint:forbidigo // Need customer friendly output
path: common/logger/logger.go
user: login: greptile-apps[bot]
body: style: Inconsistent with Printf implementation - should use printer.Info instead of fmt.Print with linter exception

```suggestion
		printer.Info(v...)
```
created_at: 2025-05-06T14:53:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -69,7 +69,7 @@ func cleanUpDir(targetDir string) {
 	if _, err := os.Stat(targetDir); !os.IsNotExist(err) {
 		err := os.RemoveAll(targetDir)
 		if err != nil {
-			fmt.Println(err)
+			fmt.Println(err) //nolint:forbidigo // test
path: common/teacmd/git_test.go
user: login: greptile-apps[bot]
body: style: Consider using t.Log() instead of fmt.Println() for test output, as it's more idiomatic for Go tests and provides better integration with the testing framework
created_at: 2025-05-06T14:53:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+package printer
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/charmbracelet/lipgloss"
+	"github.com/guumaster/logsymbols"
+)
+
+var (
+	headerStyle       = lipgloss.NewStyle().Bold(true).Underline(true)
+	successStyle      = lipgloss.NewStyle().Bold(true)
+	errorStyle        = lipgloss.NewStyle().Bold(true)
+	notificationStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("178")) // Bright yellow, good for notifications
path: common/printer/printer.go
user: login: greptile-apps[bot]
body: style: successStyle and errorStyle are identical (both just bold). Consider differentiating error messages with a red color for better visibility
created_at: 2025-05-06T14:54:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+package printer
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/charmbracelet/lipgloss"
+	"github.com/guumaster/logsymbols"
+)
+
+var (
+	headerStyle       = lipgloss.NewStyle().Bold(true).Underline(true)
+	successStyle      = lipgloss.NewStyle().Bold(true)
+	errorStyle        = lipgloss.NewStyle().Bold(true)
+	notificationStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("178")) // Bright yellow, good for notifications
+)
+
+func Success(msg string) {
+	//nolint:forbidigo // Need customer friendly output
+	fmt.Print(successStyle.Render(string(logsymbols.Success) + " " + msg))
+}
+
+func Successln(msg string) {
+	//nolint:forbidigo // Need customer friendly output
+	fmt.Println(successStyle.Render(string(logsymbols.Success) + " " + msg))
+}
+
+func Successf(format string, args ...any) {
+	msg := successStyle.Render(string(logsymbols.Success) + " " + fmt.Sprintf(format, args...))
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Error(msg string) {
+	//nolint:forbidigo // Need customer friendly output
+	fmt.Print(errorStyle.Render(string(logsymbols.Error) + " " + msg))
+}
+
+func Errorln(msg string) {
+	//nolint:forbidigo // Need customer friendly output
+	fmt.Println(errorStyle.Render(string(logsymbols.Error) + " " + msg))
+}
+
+func Errorf(format string, args ...any) {
+	msg := errorStyle.Render(string(logsymbols.Error) + " " + fmt.Sprintf(format, args...))
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+// Info prints a message with newline.
+func Info(msg string) {
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Infoln(msg string) {
+	fmt.Println(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Infof(format string, args ...any) {
+	msg := fmt.Sprintf(format, args...)
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Header(msg string) {
+	fmt.Print(headerStyle.Render(msg)) //nolint:forbidigo // Need customer friendly output
+}
+
+func Headerln(msg string) {
+	fmt.Println(headerStyle.Render(msg)) //nolint:forbidigo // Need customer friendly output
+}
+
+func Headerf(format string, args ...any) {
+	msg := headerStyle.Render(fmt.Sprintf(format, args...))
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Notification(msg string) {
+	fmt.Print(notificationStyle.Render(msg)) //nolint:forbidigo // Need customer friendly output
+}
+
+func Notificationf(format string, args ...any) {
+	msg := notificationStyle.Render(fmt.Sprintf(format, args...))
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Notificationln(msg string) {
+	fmt.Println(notificationStyle.Render(msg)) //nolint:forbidigo // Need customer friendly output
+}
+
+func NewLine(numberOfLines int) {
+	if numberOfLines <= 0 {
+		numberOfLines = 1
+	}
+	fmt.Print(strings.Repeat("\n", numberOfLines)) //nolint:forbidigo // Need customer friendly output
+}
+
+func SectionDivider(symbol string, length int) {
+	fmt.Println(strings.Repeat(symbol, length)) //nolint:forbidigo // Need customer friendly output
+}
path: common/printer/printer.go
user: login: greptile-apps[bot]
body: logic: Add validation to prevent negative length values which could cause panic
created_at: 2025-05-06T14:54:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+package printer
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/charmbracelet/lipgloss"
+	"github.com/guumaster/logsymbols"
+)
+
+var (
+	headerStyle       = lipgloss.NewStyle().Bold(true).Underline(true)
+	successStyle      = lipgloss.NewStyle().Bold(true)
+	errorStyle        = lipgloss.NewStyle().Bold(true)
+	notificationStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("178")) // Bright yellow, good for notifications
+)
+
+func Success(msg string) {
+	//nolint:forbidigo // Need customer friendly output
+	fmt.Print(successStyle.Render(string(logsymbols.Success) + " " + msg))
+}
+
+func Successln(msg string) {
+	//nolint:forbidigo // Need customer friendly output
+	fmt.Println(successStyle.Render(string(logsymbols.Success) + " " + msg))
+}
+
+func Successf(format string, args ...any) {
+	msg := successStyle.Render(string(logsymbols.Success) + " " + fmt.Sprintf(format, args...))
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Error(msg string) {
+	//nolint:forbidigo // Need customer friendly output
+	fmt.Print(errorStyle.Render(string(logsymbols.Error) + " " + msg))
+}
+
+func Errorln(msg string) {
+	//nolint:forbidigo // Need customer friendly output
+	fmt.Println(errorStyle.Render(string(logsymbols.Error) + " " + msg))
+}
+
+func Errorf(format string, args ...any) {
+	msg := errorStyle.Render(string(logsymbols.Error) + " " + fmt.Sprintf(format, args...))
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+// Info prints a message with newline.
+func Info(msg string) {
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Infoln(msg string) {
+	fmt.Println(msg) //nolint:forbidigo // Need customer friendly output
+}
+
+func Infof(format string, args ...any) {
+	msg := fmt.Sprintf(format, args...)
+	fmt.Print(msg) //nolint:forbidigo // Need customer friendly output
+}
path: common/printer/printer.go
user: login: greptile-apps[bot]
body: style: Infof splits fmt.Sprintf and fmt.Print into two steps while other *f functions combine them. Keep pattern consistent
created_at: 2025-05-06T14:54:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -78,10 +79,10 @@ func (p *fakeProgram) Start() error {
 func (p *fakeProgram) Send(msg tea.Msg) {
 	switch msg := msg.(type) {
 	case StatusMsg:
-		fmt.Println(msg)
+		fmt.Println(msg) //nolint:forbidigo // cannot convert to printer.Infoln
path: tea/component/program/program.go
user: login: greptile-apps[bot]
body: logic: This fmt.Println call has a nolint directive claiming it cannot be converted to printer.Infoln, but this seems incorrect. The StatusMsg type is just a string and should be convertible. Consider replacing with printer.Infoln(string(msg))
created_at: 2025-05-06T14:54:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -360,19 +365,21 @@ func (p *project) getForgeProjectNameFromWorldToml() error {
 
 func (p *project) validateAndSetName(name string) error {
 	if name == "" {
-		fmt.Printf("\n❌ Error: Project name cannot be empty\n")
+		printer.NewLine(1)
+		printer.Errorln("Error: Project name cannot be empty")
 		return eris.New("empty name")
 	}
 
 	if len(name) > MaxProjectNameLen {
-		fmt.Printf("\n❌ Error: Project name cannot be longer than %d characters\n",
-			MaxProjectNameLen)
+		printer.NewLine(1)
+		printer.Errorf("Error: Project name cannot be longer than %d characters\n", MaxProjectNameLen)
 		return eris.New("name too long")
 	}
 
 	if strings.ContainsAny(name, "<>:\"/\\|?*") {
-		fmt.Printf("\n❌ Error: Project name contains invalid characters\n" +
-			"   Invalid characters: < > : \" / \\ | ? *\n")
+		printer.NewLine(1)
+		printer.Errorln("Error: Project name contains invalid characters" +
+			"   Invalid characters: < > : \" / \\ | ? *")
path: cmd/world/forge/project.go
user: login: graphite-app[bot]
body: There's a formatting issue in the error message - it's missing a space or newline between "invalid characters" and "Invalid characters". This causes the two parts to run together in the output.

Consider adding a space or newline:

```go
printer.Errorln("Error: Project name contains invalid characters" +
    "\n   Invalid characters: < > : \" / \\ | ? *")
```

This will ensure proper visual separation between the error statement and the list of invalid characters.
```suggestion

printer.NewLine(1)
printer.Errorln("Error: Project name contains invalid characters" +
	"\n   Invalid characters: < > : \" / \\ | ? *")

```
  

*Spotted by [Diamond](https://app.graphite.dev/diamond/?org=Argus-Labs&ref=ai-review-comment)*<i class='graphite__hidden'><br /><br />Is this helpful? React 👍 or 👎 to let us know.</i>
created_at: 2025-05-06T15:08:30Z
author_association: CONTRIBUTOR

PR comments:
user: login: AnthonyWadham
created_at: 2025-05-06T14:49:05Z
body: * **#154** <a href="https://app.graphite.dev/github/pr/Argus-Labs/world-cli/154?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/Argus-Labs/world-cli/154?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* `main`
<hr />

## How to use the Graphite Merge Queue

Add the label <kbd>graphite/merge</kbd> to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using [this link](https://app.graphite.dev/invite/github/Argus-Labs?ref=merge-queue-instructions-in-stack-comment&prId=154).

<h2></h2>
<sub>

An organization admin has enabled the [Graphite Merge Queue](https://graphite.dev/docs/graphite-merge-queue) in this repository.

</sub>

<sub>

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

</sub>

<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: CONTRIBUTOR
user: login: codecov[bot]
created_at: 2025-05-06T14:58:49Z
body: ## [Codecov](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) Report
Attention: Patch coverage is `46.30350%` with `276 lines` in your changes missing coverage. Please review.
> Project coverage is 33.24%. Comparing base [(`9cc1902`)](https://app.codecov.io/gh/Argus-Labs/world-cli/commit/9cc1902ae711e123213ce9c35c4a0e6b434e38d5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) to head [(`51dbe0c`)](https://app.codecov.io/gh/Argus-Labs/world-cli/commit/51dbe0ccee1729711822a6930b567ea8bccd6f9b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs).
> Report is 1 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) | Patch % | Lines |
|---|---|---|
| [cmd/world/forge/project.go](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&filepath=cmd%2Fworld%2Fforge%2Fproject.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs#diff-Y21kL3dvcmxkL2ZvcmdlL3Byb2plY3QuZ28=) | 46.74% | [90 Missing :warning: ](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) |
| [common/printer/printer.go](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&filepath=common%2Fprinter%2Fprinter.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs#diff-Y29tbW9uL3ByaW50ZXIvcHJpbnRlci5nbw==) | 0.00% | [49 Missing :warning: ](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) |
| [cmd/world/forge/user.go](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&filepath=cmd%2Fworld%2Fforge%2Fuser.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs#diff-Y21kL3dvcmxkL2ZvcmdlL3VzZXIuZ28=) | 0.00% | [27 Missing :warning: ](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) |
| [cmd/world/forge/common.go](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&filepath=cmd%2Fworld%2Fforge%2Fcommon.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs#diff-Y21kL3dvcmxkL2ZvcmdlL2NvbW1vbi5nbw==) | 52.63% | [18 Missing :warning: ](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) |
| [cmd/world/forge/forge.go](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&filepath=cmd%2Fworld%2Fforge%2Fforge.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs#diff-Y21kL3dvcmxkL2ZvcmdlL2ZvcmdlLmdv) | 0.00% | [18 Missing :warning: ](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) |
| [cmd/world/forge/deployment.go](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&filepath=cmd%2Fworld%2Fforge%2Fdeployment.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs#diff-Y21kL3dvcmxkL2ZvcmdlL2RlcGxveW1lbnQuZ28=) | 82.53% | [11 Missing :warning: ](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) |
| [cmd/world/forge/log.go](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&filepath=cmd%2Fworld%2Fforge%2Flog.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs#diff-Y21kL3dvcmxkL2ZvcmdlL2xvZy5nbw==) | 0.00% | [10 Missing :warning: ](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) |
| [cmd/world/forge/organization.go](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&filepath=cmd%2Fworld%2Fforge%2Forganization.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs#diff-Y21kL3dvcmxkL2ZvcmdlL29yZ2FuaXphdGlvbi5nbw==) | 87.17% | [10 Missing :warning: ](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) |
| [cmd/world/cardinal/start.go](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&filepath=cmd%2Fworld%2Fcardinal%2Fstart.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs#diff-Y21kL3dvcmxkL2NhcmRpbmFsL3N0YXJ0Lmdv) | 0.00% | [7 Missing :warning: ](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) |
| [cmd/world/cardinal/dev.go](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&filepath=cmd%2Fworld%2Fcardinal%2Fdev.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs#diff-Y21kL3dvcmxkL2NhcmRpbmFsL2Rldi5nbw==) | 0.00% | [6 Missing :warning: ](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) |
| ... and [14 more](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs) | |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #154      +/-   ##
==========================================
- Coverage   33.28%   33.24%   -0.04%     
==========================================
  Files          70       71       +1     
  Lines        6911     7048     +137     
==========================================
+ Hits         2300     2343      +43     
- Misses       4282     4376      +94     
  Partials      329      329              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Argus-Labs/world-cli/pull/154?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Argus-Labs).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: graphite-app[bot]
created_at: 2025-05-06T15:35:20Z
body: ### Merge activity

* **May 6, 11:35 AM EDT**: `Doglightning` added this pull request to the [Graphite merge queue](https://app.graphite.dev/merges?org=Argus-Labs&repo=world-cli).
* **May 6, 11:37 AM EDT**: Merged by the [Graphite merge queue](https://app.graphite.dev/merges?org=Argus-Labs&repo=world-cli).

author_association: CONTRIBUTOR