fs.go: Value receiver Result() returns new Result — must use return
value not discard it. Changed from r.Result(...); return *r to
return Result{}.Result(os.ReadDir(...)).
i18n: SetLanguage sets i.locale directly. Language() reads i.locale.
Translator reload is core/go-i18n's responsibility.
231 tests passing.
Co-Authored-By: Virgil <virgil@lethean.io>
Command(path, Command{Action: handler}) — typed struct input, Result output.
Command fields exported: Name, Description, Path, Action, Lifecycle, Flags, Hidden.
i18n.SetLanguage returns Result instead of error.
All public methods across core/go now return Result where applicable.
231 tests, 76.5% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
All 14 public Fs methods return Result instead of (value, error).
validatePath returns Result internally.
Tests updated to use r.OK / r.Value pattern.
231 tests, 77.1% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
Zero args returns Value. With args, sets Value from Go (value, error).
r.Result() // get
r.Result(file, err) // set — OK = err == nil
r.Result(value) // set — OK = true
One method. Get and set. Same pattern as Service(), Command().
Co-Authored-By: Virgil <virgil@lethean.io>
New() sets Value/OK on the receiver and returns *Result.
Result() returns the Value. Both pointer receivers.
r := &Result{}
r.New(file, err) // OK = err == nil
val := r.Result()
Co-Authored-By: Virgil <virgil@lethean.io>
Action, Query, QueryAll, Perform → Result
QueryHandler, TaskHandler → func returning Result
RegisterAction/RegisterActions → handler returns Result
ServiceStartup, ServiceShutdown → Result
LogError, LogWarn → Result
ACTION, QUERY, QUERYALL, PERFORM aliases → Result
Tests updated to match new signatures.
Co-Authored-By: Virgil <virgil@lethean.io>
Service is now a proper struct with OnStart/OnStop/OnReload lifecycle
functions — not a registry wrapping any. Packages create Service{} with
typed fields, same pattern as Command and Option.
Result drops generics — Value is any. The struct is the container,
Value is the generic. No more Result[T] ceremony.
Service(name, Service{}) to register, Service(name) to get — both
return Result. ServiceFactory returns Result not (any, error).
NewWithFactories/NewRuntime return Result.
232 tests, 77.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
Both registries now use Arg(0, args...) instead of ArgString directly.
Type checking flows through Arg's switch before assertion.
Co-Authored-By: Virgil <virgil@lethean.io>
Arg() detects the type at index and delegates to ArgString/ArgInt/ArgBool.
Index-first, args variadic. Typed extractors validate with ok check.
Co-Authored-By: Virgil <virgil@lethean.io>
core.Arg(args, 0) returns any with bounds check.
ArgString/ArgInt/ArgBool delegate through Arg() for type detection.
Co-Authored-By: Virgil <virgil@lethean.io>
core.ArgString(args, 0) replaces args[0].(string) pattern.
Bounds-checked, returns empty string on miss or wrong type.
Used by Command() and Service() registries.
Co-Authored-By: Virgil <virgil@lethean.io>
core.Concat("cmd.", key, ".description") — variadic string builder.
Gives a single point to add sanitisation, injection checks, or
encoding later. command.go I18nKey uses it.
Co-Authored-By: Virgil <virgil@lethean.io>
HasPrefix, HasSuffix, TrimPrefix, TrimSuffix, Contains, Split, SplitN,
StringJoin, Replace, Lower, Upper, Trim, RuneCount.
utils.go and command.go now use string.go helpers — zero direct
strings import in either file.
234 tests, 79.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
core.IsFlag(arg) checks if an argument starts with a dash.
Cli.go no longer imports strings — all string ops via utils.go helpers.
Co-Authored-By: Virgil <virgil@lethean.io>
core.Printl(w, format, args...) writes a formatted line to any writer,
defaulting to os.Stdout. Cli.Print() delegates to Printl.
Co-Authored-By: Virgil <virgil@lethean.io>
All CLI output goes through Cli.Print() instead of direct fmt calls.
SetOutput() allows redirecting (testing, logging, etc).
Co-Authored-By: Virgil <virgil@lethean.io>
- FilterArgs: removes empty strings and Go test runner flags
- ParseFlag: single dash (-v, -🔥) must be 1 char, double dash (--verbose) must be 2+ chars
- Cli.Run() now uses FilterArgs and ParseFlag — no test flag awareness in surface layer
- Invalid flags silently ignored (e.g. -verbose, --v)
221 tests, 79.7% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
Hit compress/compressFile via GeneratePack with actual asset files on disk.
Added SetOutput log test. Crash report test covers Reports() graceful nil.
Remaining 0%: getAllFiles (group dir scan), appendReport (unexported filePath).
Both are internal plumbing — public API is fully covered.
Co-Authored-By: Virgil <virgil@lethean.io>
Command is now a DTO with no root/child awareness:
- Path-based registration: c.Command("deploy/to/homelab", handler)
- Description is an i18n key derived from path: cmd.deploy.to.homelab.description
- Lifecycle: Run(), Start(), Stop(), Restart(), Reload(), Signal()
- All return core.Result — errors flow through Core internally
- Parent commands auto-created from path segments
Cli is now a surface layer that reads from Core's command registry:
- Resolves os.Args to command path
- Parses flags into Options (--port=8080 → Option{K:"port", V:"8080"})
- Calls command action with parsed Options
- Banner and help use i18n
Old Clir code preserved in tests/testdata/cli_clir.go.bak for reference.
211 tests, 77.5% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
164 tests, 41.3% coverage. Tests written against the public API only
(external test package, no _test.go in pkg/core/).
Covers: New(Options), Data, Drive, Config, Service, Error, IPC,
Fs, Cli, Lock, Array, Log, App, Runtime, Task.
Fixes: NewCommand now inits flagset, New() wires Cli root command.
Old tests removed — they referenced With*, RegisterService, and
other patterns that no longer exist.
Co-Authored-By: Virgil <virgil@lethean.io>
- All New* constructors removed (NewApp, NewIO, NewCoreCli, NewBus, NewService, NewCoreI18n, NewConfig)
- New() uses pure struct literals: &App{}, &Fs{}, &Config{ConfigOpts:}, &Cli{opts:}, &Service{}, &Ipc{}, &I18n{}
- Ipc methods moved to func (c *Core) — Ipc is now a DTO
- LockApply only called from WithServiceLock, not on every New()
- Service map lazy-inits on first write
- CliOpts DTO with Version/Name/Description
Co-Authored-By: Virgil <virgil@lethean.io>