Terminal toolkit and runtime for Go CLIs β styled output, prompts, flows, logs, and operational wrappers without a full TUI framework.
TFX β TermFX (short for Terminal Effects) is a modular Go toolkit for building expressive terminal software.
It started as color, logging, progress, and structured output. It now also
ships a lightweight runtime through runfx, flowfx, formfx, and cmd/tfx
for wrapping real CLI workflows.
It is still not a TUI framework. It is the layer between fmt.Println and a
full app framework: enough structure to build polished operational CLIs, but
without surrendering your whole architecture to a renderer.
| Tool | Use Case |
|---|---|
fmt.Println |
You donβt care about styling/logging |
log/slog |
You want structured logs, no styling |
TFX |
You want structure and style β¨ |
bubbletea |
Youβre building a full interactive TUI |
Use TFX when you want one or more of these at the same time:
- better terminal UX than ad-hoc prints
- a composable render/runtime loop
- prompts and validation without a heavy app framework
- a repeatable wrapper around real commands or tools
- π Multi-path API: same engine, multiple ways to use it.
- π« No reflection, no runtime surprises.
- π§° Structured logs with color and context.
- π§ͺ Built for testing: capture logs, inject writers.
- π¨ Themeable by design: ANSI + semantic palettes.
- π§± Minimal dependencies: No third-party bloat.
cmd/tfxis a wrapper runtime, not a product framework.runfxowns loops and rendering.formfxowns prompts and input capture.flowfxowns orchestration and sequencing.progrefxowns progress, spinners, steppers, and tables.- Domain logic belongs in the hosted tool or repo, not inside TFX.
-
Unified color system with support for:
- ANSI
- 256-color
- TrueColor (24-bit)
- Semantic palettes & themes (Dracula, Nord, GitHub, Tailwind)
-
Structured logging with:
- Badge-style tags ([INFO], [ERR], etc)
- Contextual fields
- Multi-writer support (console + file rotation)
- JSON, text, and badge formats
-
Terminal capability detection:
- Per-OS fallbacks
- Unicode/ANSI support
- CI-awareness,
NO_COLOR, etc
-
Interactive runtime and orchestration with:
runfxrender loops and multiplexed visualsformfxprompts, confirms, selects, and validationflowfxsequences, branches, retries, scripts, and runnerscmd/tfxas an integrated wrapper for config-driven pipelines and tool-hosting workflows
-
Progress bars, spinners, steppers, and tables with smart rendering
-
Internal
share/helpers:Option[T],Overload[T](standardized pattern)
Today TFX has two faces:
- Library surface for Go developers:
color,logfx,progrefx,runfx,formfx,flowfx,writer,terminal - Runtime surface for operational wrappers:
cmd/tfxplustfx.yaml
That second layer matters because it lets TFX host real tools. In practice,
that means a project like morfx can stay focused on AST refactoring while TFX
owns the terminal runtime around it: flow selection, prompts, progress, logs,
hooks, and artifacts.
| Package | Description |
|---|---|
color/ |
Core color system: hex, RGB, ANSI, themes, rendering |
runfx/ |
Lightweight terminal runtime for composable visual loops |
formfx/ |
Prompts, confirms, selects, secrets, and validation |
flowfx/ |
Declarative flow orchestration, scripts, branching, retry |
terminal/ |
Terminal detection and capability inference |
logfx/ |
Structured, badge-style logging with writers |
progrefx/ |
Progress bars, spinners, steppers, and tables |
writer/ |
Console & file writers with rotation and theming |
internal/share/ |
Internal DX helpers (option sets, overloads, conventions) |
import (
"github.com/oxhq/tfx/color"
"github.com/oxhq/tfx/logfx"
)
func main() {
logfx.Success("Server started on port %d", 8080)
logfx.Badge("DB", "Connected to postgres", color.MaterialGreen)
}Use a spinner:
import (
"fmt"
"time"
"github.com/oxhq/tfx/progrefx"
)
spinner := progrefx.StartSpinner(progrefx.SpinnerConfig{
Label: "Loading data...",
})
for i := 0; i < 10; i++ {
spinner.Tick()
fmt.Print(spinner.Render())
time.Sleep(100 * time.Millisecond)
}When input may be user-provided or otherwise fallible, prefer the safe Try*
constructors. Panic-on-error entrypoints now have explicit Must* siblings:
loop, err := runfx.TryStart(runfx.Config{TickInterval: 100 * time.Millisecond})
if err != nil {
return err
}
spinner, err := progrefx.TryStartSpinner(progrefx.SpinnerConfig{Label: "Syncing"})
if err != nil {
return err
}
_ = loop
_ = spinnerSee TFX in action with the demos:
# Build and run the simple demo
make demo
# Build the versioned wrapper binary
make build-tfx
# Print the wrapper version metadata
go run ./cmd/tfx --version
# Or run the built binary directly
./bin/demo
# Run the integrated wrapper dashboard
go run ./cmd/tfxcmd/demo stays as a minimal showcase. cmd/tfx is the integrated wrapper
that exercises runfx, formfx, flowfx, logfx, and progrefx
together.
See the full guide in docs/install.md.
Quick install on Unix-like systems:
curl -fsSL https://raw.githubusercontent.com/oxhq/tfx/main/tools/install.sh | bashQuick install on Windows PowerShell:
irm https://raw.githubusercontent.com/oxhq/tfx/main/tools/install.ps1 | iexRelease assets are built for linux, darwin, and windows on amd64 and
arm64.
cmd/tfx now loads a project pipeline from tfx.yaml, tfx.yml,
.tfx.yaml, or .tfx.yml, searching upward from the current working
directory. A config can define either:
- a single pipeline via top-level
steps - multiple named pipelines via
flowsorprofiles
If no config exists, it falls back to a sensible local pipeline:
- nearest
go.mod:go test ./...,go build ./..., andgo vet ./... - no Go module:
pwdandls
Interactive sessions now persist lightweight wrapper state to disk: the selected flow, selected lane, project name, last run timestamp, and recent run history are restored between sessions. Secrets stay process-local and are not written to the state file.
This repository ships its own real root config in
tfx.yaml, so running
go run ./cmd/tfx from the repo root gives you actual ci, quality, and
release flows for TFX itself.
This is also the pattern TFX uses to host other standalone tools. morfx is
the reference example: Morfx owns the refactoring engine, while TFX supplies
the runtime shell around it.
That is no longer just a template. TFX is already dogfooding this pattern in:
tfxitself for localci,quality, andreleasemorfxfor standalone tool verification and release orchestrationmitlfor CLI verification, doctor/preflight checks, and local release packaging
The pattern is generic, not Morfx-specific. See:
- docs/dogfooding-external-tools.md
- examples/tfx-external-tool.yaml
- docs/dogfooding-morfx-standalone.md
Named-flow example:
project: tfx
description: TFX local automation
lanes: [preview, canary, production]
working_dir: .
default_flow: ci
flows:
- name: ci
description: Fast local verification
steps:
- id: test
title: Run tests
command: [go, test, ./...]
- id: build
title: Build binaries
command: [go, build, ./...]
- name: release
description: Release candidate pipeline
default_lane: canary
require_approval: true
secret_prompt: Release token
secret_env: TFX_SECRET
steps:
- id: test
title: Run tests
command: [go, test, ./...]
- id: vet
title: Vet packages
lanes: [canary, production]
command: [go, vet, ./...]
- id: coverage
title: Check coverage threshold
lanes: [production]
command: [make, check-coverage]
continue_on_error: trueBehavior notes:
working_diris resolved relative to the config file directory.- Top-level and flow-level
before_all,after_all, andon_failurehooks are supported. - Each step
diris resolved relative toworking_dir. - Each step
commandis executed as argv, not through a shell. - Each step can declare
artifacts, and the wrapper also discovers files written under conventional directories such asdist/,coverage/,build/, andartifacts/. flowsis the canonical key for multiple named pipelines;profilesis a supported alias.default_flowselects the initial flow;default_profileis a supported alias.- You can preselect a named flow with
go run ./cmd/tfx --flow release. --profile releaseis supported as an alias, andTFX_FLOW/TFX_PROFILEwork too.- You can preselect a lane with
--lane productionorTFX_LANE=production. --runorTFX_RUN=truestarts the selected flow immediately without going through the prompts.--json/TFX_JSON=trueemits a machine-readable wrapper snapshot with the last run report.--quiet/TFX_QUIET=trueemits a compact single-line summary for automation.--versionprints the embedded wrapper version, commit, and build date when available.--flow,--lane, and--runcompose cleanly, sotfx --flow release --lane production --runis valid.TFX_PROJECTandTFX_LANEare exported for every step.- If
secret_envis set, the form-collected secret is exported under that name. - Step
lanesacts as an allow-list; steps withoutlanesrun everywhere. continue_on_error: truemarks the step as failed in the UI but keeps the run going.- A successful or failed run updates the persisted state and the recent-run list.
For the full schema and a copy-pasteable sample, see
docs/tfx-yaml.md and examples/tfx.yaml.
For standalone-tool dogfooding, including a real morfx-style CLI workflow,
see docs/dogfooding-morfx-standalone.md
and examples/tfx-morfx-standalone.yaml.
For the concrete release runbook, see docs/release-v0.2.0.md.
For the release summary itself, see docs/release-notes-v0.2.0.md.
"If there's only one way to use it, it's not the right way."
TFX promotes a multi-entry design for each API:
logfx.Success("Done!") // 1. Quick default
logger := logfx.LogWithConfig(logfx.DefaultOptions())
logger.Info("custom") // 2. Instantiated style
logfx.If(err).AsWarn().Msg("warn msg") // 3. DSL / fluentThis consistency is achieved via internal helpers like Overload() and Option[T].
If you are using both projects together, the boundary is simple:
- TFX is the terminal environment and workflow runtime
- Morfx is the refactoring engine
TFX should not absorb AST editing concerns. Morfx should not absorb prompt, progress, or wrapper-runtime concerns. They fit because their responsibilities stay separate.
- VISION.md β project intent and market gap
- DESIGN_GUIDELINES.md β API conventions and patterns
- ROADMAP.md β current status and future plans
- MULTIPATH.md β why TFX APIs support multiple entry paths
- docs/tfx-yaml.md β
cmd/tfxconfig schema and execution model - docs/dogfooding-morfx-standalone.md β standalone CLI dogfooding pattern for
cmd/tfx
TFX is under active development. Use at your own risk until v1.0.0 is tagged.
MIT
Built with β and π’ by @garaekz