go/docs/commands.md

178 lines
3.7 KiB
Markdown
Raw Permalink Normal View History

---
title: Commands
description: Path-based command registration and CLI execution.
---
# Commands
Commands are one of the most AX-native parts of CoreGO. The path is the identity.
## Register a Command
```go
c.Command("deploy/to/homelab", core.Command{
Action: func(opts core.Options) core.Result {
target := opts.String("target")
return core.Result{Value: "deploying to " + target, OK: true}
},
})
```
## Command Paths
Paths must be clean:
- no empty path
- no leading slash
- no trailing slash
- no double slash
These paths are valid:
```text
deploy
deploy/to/homelab
workspace/create
```
These are rejected:
```text
/deploy
deploy/
deploy//to
```
## Parent Commands Are Auto-Created
When you register `deploy/to/homelab`, CoreGO also creates placeholder parents if they do not already exist:
- `deploy`
- `deploy/to`
This makes the path tree navigable without extra setup.
## Read a Command Back
```go
r := c.Command("deploy/to/homelab")
if r.OK {
cmd := r.Value.(*core.Command)
_ = cmd
}
```
## Run a Command Directly
```go
cmd := c.Command("deploy/to/homelab").Value.(*core.Command)
r := cmd.Run(core.Options{
{Key: "target", Value: "uk-prod"},
})
```
If `Action` is nil, `Run` returns `Result{OK:false}` with a structured error.
## Run Through the CLI Surface
```go
r := c.Cli().Run("deploy", "to", "homelab", "--target=uk-prod", "--debug")
```
`Cli.Run` resolves the longest matching command path from the arguments, then converts the remaining args into `core.Options`.
## Flag Parsing Rules
### Double Dash
```text
--target=uk-prod -> key "target", value "uk-prod"
--debug -> key "debug", value true
```
### Single Dash
```text
-v -> key "v", value true
-n=4 -> key "n", value "4"
```
### Positional Arguments
Non-flag arguments after the command path are stored as repeated `_arg` options.
```go
r := c.Cli().Run("workspace", "open", "alpha")
```
That produces an option like:
```go
core.Option{Key: "_arg", Value: "alpha"}
```
### Important Details
- flag values stay as strings
- `opts.Int("port")` only works if some code stored an actual `int`
- invalid flags such as `-verbose` and `--v` are ignored
## Help Output
`Cli.PrintHelp()` prints executable commands:
```go
c.Cli().PrintHelp()
```
It skips:
- hidden commands
- placeholder parents with no `Action` and no `Lifecycle`
Descriptions are resolved through `cmd.I18nKey()`.
## I18n Description Keys
If `Description` is empty, CoreGO derives a key from the path.
```text
deploy -> cmd.deploy.description
deploy/to/homelab -> cmd.deploy.to.homelab.description
workspace/create -> cmd.workspace.create.description
```
If `Description` is already set, CoreGO uses it as-is.
## Lifecycle Commands
Commands can also delegate to a lifecycle implementation.
```go
type daemonCommand struct{}
func (d *daemonCommand) Start(opts core.Options) core.Result { return core.Result{OK: true} }
func (d *daemonCommand) Stop() core.Result { return core.Result{OK: true} }
func (d *daemonCommand) Restart() core.Result { return core.Result{OK: true} }
func (d *daemonCommand) Reload() core.Result { return core.Result{OK: true} }
func (d *daemonCommand) Signal(sig string) core.Result { return core.Result{Value: sig, OK: true} }
c.Command("agent/serve", core.Command{
Lifecycle: &daemonCommand{},
})
```
Important behavior:
- `Start` falls back to `Run` when `Lifecycle` is nil
- `Stop`, `Restart`, `Reload`, and `Signal` return an empty `Result` when `Lifecycle` is nil
## List Command Paths
```go
paths := c.Commands()
```
Like the service registry, the command registry is map-backed, so iteration order is not guaranteed.