This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Core CLI (forge.lthn.ai/core/cli) is a Go CLI tool for managing development workflows across Go, PHP, and Wails projects. It wraps common tooling (testing, linting, building, releasing, multi-repo management) behind a unified core command. Built on Cobra with the Core framework's dependency injection for service lifecycle management.
# Run all tests
go test ./...
# Run a single test
go test -run TestName ./...
# Build CLI binary
go build -o dist/core .
# Install from source
go install forge.lthn.ai/core/cli@latest
# Verify environment
go run . doctor
# Format and lint
gofmt -w .
go vet ./...
golangci-lint run ./...Go version: 1.26+
main.go wires everything together. Commands register in two ways:
-
Explicit registration via
cli.WithCommands()— local command packages incmd/pass anAddXCommands(root *cli.Command)function that receives the root cobra command during service startup. -
Self-registration via
cli.RegisterCommands()— ecosystem packages (imported as blank_imports inmain.go) callcli.RegisterCommands()in theirinit()functions. This is how external modules likego-build,go-devops,go-scm,agent, etc. contribute commands without coupling tomain.go.
This is the core package. Everything commands need is re-exported here to avoid direct Cobra/lipgloss/bubbletea imports:
runtime.go: SingletonInit()/Shutdown()/Execute()lifecycle. Creates a Core framework instance, attaches services, handles signals (SIGINT/SIGTERM/SIGHUP).command.go:NewCommand(),NewGroup(),NewRun()builders, flag helpers (StringFlag,BoolFlag, etc.), arg validators — all wrapping Cobra types so command packages don't import cobra directly.Commandis a type alias forcobra.Command.output.go: Styled output functions —Success(),Error(),Warn(),Info(),Dim(),Progress(),Label(),Section(),Hint(),Severity(). Use these instead of rawfmt.Print.errors.go:Err(),Wrap(),WrapVerb(),Exit()for error creation. Re-exportserrors.Is/As/Join. Commands should return errors (viaRunE), not callFatal()(deprecated).frame.go: Bubbletea-based TUI framework.NewFrame("HCF")with HLCRF region layout (Header, Left, Content, Right, Footer), focus management, content navigation stack.tracker.go:TaskTrackerfor concurrent task display with spinners. TTY-aware (live updates vs static output).daemon.go: Execution mode detection (ModeInteractive/ModePipe/ModeDaemon).styles.go: Shared lipgloss styles and colour constants.glyph.go: Shortcode system for emoji/symbols (:check:,:cross:,:warn:,:info:).
Every command package in cmd/ follows this structure:
package mycommand
import "forge.lthn.ai/core/cli/pkg/cli"
func AddMyCommands(root *cli.Command) {
myCmd := cli.NewGroup("my", "My commands", "")
root.AddCommand(myCmd)
// Add subcommands...
}Commands use cli.NewCommand() (returns error) or cli.NewRun() (no error) and the cli.StringFlag()/cli.BoolFlag() helpers for flags.
The CLI imports ecosystem modules as blank imports that self-register via init():
forge.lthn.ai/core/go-build— build, CI, SDK commandsforge.lthn.ai/core/go-devops— dev, deploy, docs, git, setup commandsforge.lthn.ai/core/go-scm— forge, gitea, collect commandsforge.lthn.ai/core/agent— agent, dispatch, task commandsforge.lthn.ai/core/lint— QA commands- Others: go-ansible, go-api, go-container, go-crypt, go-infra
cmd/service/ implements start/stop/list/restart for manifest-driven daemons. Reads .core/manifest.yaml from the project directory (walks up). Daemons run detached with CORE_DAEMON=1 env var and are tracked in ~/.core/daemons/.
Tests use _Good, _Bad, _Ugly suffix pattern:
_Good: Happy path tests_Bad: Expected error conditions_Ugly: Panic/edge cases
Conventional Commits: feat:, fix:, docs:, refactor:, chore:
- Global config:
~/.core/config.yaml(YAML, dot-notation keys) - Project config:
.core/build.yaml,.core/release.yaml,.core/ci.yaml - Environment override:
CORE_CONFIG_<KEY>(underscores become dots, lowercased) - Multi-repo registry:
repos.yaml(searched cwd upward, then~/.config/core/repos.yaml)
Commands use i18n.T("key") for translatable strings and the grammar system (i18n.ActionFailed(), i18n.Progress()) for consistent error/progress messages. The CLI wraps these in cli.Echo(), cli.WrapVerb(), cli.ErrorWrapVerb().