3.6 KiB
summer-bonfire
CLI command framework module for 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 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
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's terminal UX.
// 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):
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
[...] messagestatic lines - Progress bars become
[N/M] percentage%text updates - Colors stripped when
NO_COLORis set orTERM=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
# sbt
sbt compile
sbt test
# scala-cli
scala-cli compile .
scala-cli test .
Requires JDK 21+ and sbt 1.10+.
Roadmap
See 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.