# bonfire

> Declarative console commands for the `summer` tool and application binaries, adapted to Cobra with typed input, prompts and styled output.

`import "git.golem15.com/golem15/summercms/modules/bonfire"`

## Overview

bonfire is the console layer of SummerCMS. Plugins and framework modules describe commands as plain `bonfire.Command` values (name, flags, arguments and a run function), and `bonfire.NewRoot` turns a slice of them into a Cobra root command. Commands never touch Cobra directly: they read arguments through `bonfire.Input` and write through `bonfire.Output`, which also provides tables, spinners, progress bars and interactive prompts. It is the counterpart of WinterCMS's artisan console commands (`registerConsoleCommand` and Laravel's `Illuminate\Console\Command` output helpers).

## Features

- Command values with a description, positional arguments (`bonfire.Arg`) and string flags (`bonfire.Flag`), collected from plugins or the tool itself.
- Command name validation: plugin commands must use the `namespace:verb` form (for example `blog:import`); `build`, `dev`, `serve` and `migrate` are the only bare names accepted. Invalid names make `bonfire.NewRoot` fail with `bonfire.ErrCommandName`.
- Usage strings and argument-count checks derived from the declared arguments (`<name>` for required, `[name]` for optional).
- Scalar flags, bare flags (`bonfire.Flag.Bare`, so `--force` alone stores `true`) and ordered repeatable flags (`bonfire.Flag.Repeatable`, read back through `bonfire.Input.Flags`).
- Styled status lines: `bonfire.Output.Info`, `bonfire.Output.Success`, `bonfire.Output.Warning` and `bonfire.Output.Error` (the last one writes to the error stream).
- Widgets: box-drawn tables (`bonfire.Output.Table`), a spinner around a function (`bonfire.Output.Spinner`) and a progress bar (`bonfire.Output.Progress`); both fall back to plain lines when output is not a terminal.
- Prompts: `bonfire.Output.Ask`, `bonfire.Output.Confirm`, `bonfire.Output.Choice` and `bonfire.Output.Secret`, which reads a hidden value on a terminal. Prompts return their defaults when input ends, and `bonfire.Output.Confirm` returns its default without asking when the session is not interactive.
- Injectable streams (`bonfire.NewRootIO`, `bonfire.NewOutput`) so commands can be tested against buffers.
- In-process calls: `bonfire.Call` runs a named command with arguments against any writer (Laravel `Artisan::call`), and `bonfire.Catalog` holds an application binary's final command list so code outside the Cobra root, such as the conga scheduler, can call any registered command.

## Usage

```go
package main

import (
	"context"
	"os"

	"git.golem15.com/golem15/summercms/modules/bonfire"
)

func main() {
	importPosts := bonfire.Command{
		Name:        "blog:import",
		Description: "Import posts from a feed",
		Args:        []bonfire.Arg{{Name: "url", Description: "Feed URL", Required: true}},
		Flags: []bonfire.Flag{
			{Name: "dry-run", Description: "Report without writing", Bare: true},
			{Name: "tag", Description: "Tag to apply (repeatable)", Repeatable: true},
		},
		Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
			url, _ := in.Argument("url")
			_, dryRun := in.Flag("dry-run")
			return out.Spinner("Importing "+url, func() error {
				out.Table([]string{"Tag"}, [][]string{{"news"}})
				if dryRun {
					out.Warning("dry run: nothing written")
				}
				return nil
			})
		},
	}

	root, err := bonfire.NewRoot("acme", []bonfire.Command{importPosts}, os.Stdout)
	if err != nil {
		os.Exit(1)
	}
	if err := root.Execute(); err != nil {
		os.Exit(1)
	}
}
```

Running a command in-process, with empty stdin so prompts take their defaults:

```go
catalog := bonfire.NewCatalog(commands)
if catalog.Has("blog:import") {
	err := catalog.Call(ctx, "blog:import", []string{"--dry-run", "https://example.com/feed"}, os.Stdout)
	if errors.Is(err, bonfire.ErrUnknownCommand) {
		// not registered in this binary
	}
}
```

## API reference

| Identifier | Description |
|------------|-------------|
| `bonfire.Command` | A console command: name, description, flags, arguments and the `bonfire.Command.Run` function. |
| `bonfire.Flag` | A string flag; `bonfire.Flag.Bare` allows the flag without a value, `bonfire.Flag.Repeatable` makes it an ordered multi-value flag. |
| `bonfire.Arg` | A positional argument with a name, description and required marker. |
| `bonfire.Input` | Parsed view handed to `bonfire.Command.Run`: `bonfire.Input.Args`, `bonfire.Input.Argument`, `bonfire.Input.Flag` and `bonfire.Input.Flags`. |
| `bonfire.Output` | Injected console: printing, status lines, tables, spinner, progress bar and prompts. |
| `bonfire.Progress` | A progress bar advanced from inside `bonfire.Output.Progress`. |
| `bonfire.NewRoot` | Builds the Cobra root command for a binary from a slice of commands, using the process stdin. |
| `bonfire.NewRootIO` | `bonfire.NewRoot` with injected stdin, stdout and stderr. |
| `bonfire.NewOutput` | Builds a `bonfire.Output` over the given streams, applying the terminal and color policy. |
| `bonfire.ErrCommandName` | Returned when a plugin command name is not in `namespace:verb` form. |
| `bonfire.Call` | Runs one command of a slice by exact name with arguments, writing output to a writer; stdin is empty. |
| `bonfire.ErrUnknownCommand` | Returned by `bonfire.Call` when no command has the name. |
| `bonfire.Catalog` | An immutable copy of a binary's command list; the generated app main publishes one on the app. |
| `bonfire.NewCatalog` | Builds a `bonfire.Catalog` from a command slice. |

## Configuration

bonfire reads no config keys. Output color follows these environment variables:

| Variable | Effect |
|----------|--------|
| `NO_COLOR` | Any non-empty value disables color. |
| `TERM` | The value `dumb` disables color. |
| `FORCE_COLOR` | Any non-empty value enables color even when output is not a terminal (ignored when color is disabled by `NO_COLOR` or `TERM`). |

Without these variables, color is enabled only when stdout is a terminal.

## Dependencies

- SummerCMS modules: none.
- Third-party: `github.com/spf13/cobra`, `golang.org/x/term`.
- Standard library: `bufio`, `context`, `errors`, `fmt`, `io`, `os`, `strconv`, `strings`, `sync`, `time`, `unicode/utf8`.

## Testing

```sh
go test ./modules/bonfire/...
```

The tests use in-memory streams and need no external services.
