Files
summer-bonfire/README.md
2026-02-24 00:43:22 +01:00

123 lines
3.6 KiB
Markdown

# summer-bonfire
CLI command framework module for [SummerCMS](https://git.golem15.com/golem15/summercms). Provides command registration, dispatch, and rich terminal output — the Scala 3 equivalent of Laravel's `Illuminate\Console`.
Bonfire is **not** a CLI argument parser. [case-app](https://github.com/alexarchambault/case-app) handles argument parsing and help generation. Bonfire is the CMS command framework on top: how commands are registered, discovered via plugins, dispatched, and how they produce rich terminal output (spinners, progress bars, tables, interactive prompts).
## Quick start
```scala
import summer.bonfire.*
// Define a command
object GreetCommand extends Command:
val name = "greet"
val description = "Greet a user"
def run(input: Input, output: Output): ExitCode =
val name = input.argument("name").getOrElse("World")
output.success(s"Hello, $name!")
ExitCode.Success
// Register and run
val runner = CommandRunner()
runner.register(GreetCommand)
runner.dispatch(args)
```
## Rich terminal output
All widgets are custom-built in Scala 3 — no external TUI framework. Inspired by [SSU](https://git.golem15.com/golem15/ssu)'s terminal UX.
```scala
// Spinner with braille animation (virtual thread powered)
output.withSpinner("Compiling plugin...") {
compilePlugin()
}
// Progress bar with 256-color gradient
output.withProgress(files.size) { bar =>
files.foreach { file =>
processFile(file)
bar.advance()
}
}
// Box-drawing table
output.table(
headers = Seq("Plugin", "Version", "Status"),
rows = Seq(
Seq("golem15.blog", "1.2.0", "active"),
Seq("golem15.pages", "1.0.3", "active"),
)
)
// Interactive prompts (via JLine)
val name = output.ask("Plugin name?")
val confirm = output.confirm("Create plugin?")
val choice = output.choice("Select database:", Seq("postgres", "mysql", "sqlite"))
```
## Plugin command discovery
Plugins register commands via `CommandProvider` (ServiceLoader SPI):
```scala
class BlogCommandProvider extends CommandProvider:
def commands: Seq[Command] = Seq(
BlogSeedCommand,
BlogImportCommand,
)
```
Commands are auto-discovered at boot from all plugins on the classpath.
## Non-TTY graceful degradation
All widgets detect terminal capabilities and degrade gracefully:
- Spinners become `[...] message` static lines
- Progress bars become `[N/M] percentage%` text updates
- Colors stripped when `NO_COLOR` is set or `TERM=dumb`
- Interactive prompts fall back to line-by-line stdin
## Dependencies
| Dependency | Version | Purpose |
|-----------|---------|---------|
| `com.github.alexarchambault:case-app` | 2.1.0-M29 | CLI argument parsing, auto-generated help |
| `com.lihaoyi:fansi` | 0.5.1 | ANSI colors/styles, visible-width-aware strings |
| `org.jline:jline` | 3.28.0 | Terminal raw mode, readline, key events, terminal detection |
| `org.scalameta:munit` | 1.0.3 | Testing (test scope only) |
No effect systems. No Cats, no ZIO. Pure direct-style Scala 3 on JDK 21.
## Building
```bash
# sbt
sbt compile
sbt test
# scala-cli
scala-cli compile .
scala-cli test .
```
Requires JDK 21+ and sbt 1.10+.
## Roadmap
See [PLAN.md](PLAN.md) for the full module architecture and implementation phases.
- **Phase 1** — Core types and output abstractions
- **Phase 2** — Command framework with case-app and ServiceLoader SPI
- **Phase 3** — Rich output widgets (table, spinner, progress bar)
- **Phase 4** — Interactive prompts via JLine
- **Phase 5** — GeneratorCommand base for `summer create:*` scaffolding
- **Phase 6** — Polish: shell completion, multiselect, non-TTY tests
## License
Part of SummerCMS.