Skip to content
Kona

Running commands

Grind Console accepts the same shape of input whether you type it in a terminal, forward it through kona grind, or call Grind.call from application code or tests.

Terminal window
./grind list
./grind mail:digest 42 --queue=high
./grind mail:digest --help
./grind -v schedule:run
./grind -q list
./grind --base=/path/to/app list
Flag Effect
-v / --verbose Verbose diagnostics (GrindIO.verbose)
-q / --quiet Suppress normal stdout (errors still print)
--help / -h Empty argv / global help, or on a custom command: print signature help
--base= App root for discovery / loaders (and many built-ins)
--bootstrap= Bootstrap class for in-process boots (default bootstrap.App)
--isolated With isolatable custom commands: single-process lock

Option parsing rules (parseOptions):

  • -v, -q, -h set verbose, quiet, and help.
  • --name sets name=true.
  • --name=value sets name to value.
  • Anything else is positional.

Exit code 0 means success. 1 (and other non-zero values) means failure — missing args, unknown command, generator conflicts, Cadence errors surfaced as grind errors, and so on. Unknown built-in-style names print an error, show the help catalog, and exit 1.

Terminal window
./grind
./grind help
./grind --help
./grind -h

These discover custom commands, print the boxed “grind” usage catalog (built-ins + discovered customs), and exit 0.

Terminal window
./grind list
./grind mail:digest --help

list prints a two-column catalog: built-in names from ListCommand.BUILTIN, then registered custom commands (description, or signature when description is empty). Duplicate names are shown once.

For one custom command, --help / -h prints a box with the name/description, the rendered signature, and argument/option help lines — then exits 0 without running handle(). Built-in commands generally use their own usage/GrindException messages rather than the custom-command help renderer.

When you are not already invoking ./grind directly, the global CLI can forward to the nearest project’s grind script:

Terminal window
kona grind serve
kona grind migrate
kona grind mail:digest 42
kona grind --help

Behavior (org.konaframework.cli.GrindCommand):

  1. Walk upward from the current directory until a Gradle settings/build script is found (same markers as serve).
  2. Require ./grind or grind.bat in that root (apps from kona new).
  3. Run that script with your remaining arguments and propagate the exit code.

If no Gradle project is found, or the project has no grind script (for example a monorepo sample), the proxy fails with a clear error pointing at kona new. Prefer ./grind when you are already inside the app.

See Installation for installing the global kona CLI.

Use org.konaframework.grind.Grind to run a command name with argument tokens (the same strings you would type after the command name on the CLI):

import org.konaframework.grind.CommandRegistry
import org.konaframework.grind.Grind
val registry = CommandRegistry().register(SendDigestCommand::class.java)
val code = Grind.call("mail:digest", listOf("42", "--queue=default"), registry)
val quiet = Grind.callSilently("schedule:run", listOf("--base=$root"))
val listed = Grind.call("list")

Overloads accept combinations of name, args, optional CommandRegistry, basePath, and ClassLoader (all @JvmStatic).

  1. Merge the optional registry with CommandDiscovery.registerAll on the given loader.
  2. If the merged registry has the name, run it through CommandExecutor (custom-command path: signature bind, --help, --isolated).
  3. Otherwise fall through to GrindApplication.run(name + args) so built-ins (serve, migrate, list, …) still work.

callSilently temporarily replaces System.out with a buffer so normal stdout is suppressed; stderr still surfaces errors from GrindIO.error / ConsoleIO.error.

Default basePath is the current working directory (absolute, normalized). Default loader is Grind’s class loader.

Register work on the application Schedule, then run due tasks with Grind Console:

Terminal window
./grind schedule:run
./grind schedule:list
./grind schedule:work --sleep=60 --max=1

Those commands boot the app in-process (via the same loader path as migrate) and require ScheduleServiceProvider so a Schedule is bound.

Custom Grind Console commands are ordinary processes: point a scheduled callback at a shell invocation of ./grind your:command, or call Grind.call from code the scheduler already runs. For isolatable cron-style commands, combine schedule with --isolated.

Full guide: Task scheduling.

Capture stdout and exit codes with ConsoleTester:

ConsoleTester()
.run("list")
.assertOk()
.assertSee("schedule:run")

See HTTP & console tests. For custom commands, compile the app first so discovery can see META-INF/kona/commands and build/classes.

Step What runs
Parse argv Positionals + options map; build GrindIO.fromOptions
Discover CommandDiscovery on grind’s loader + optional app build/ outputs
Dispatch Built-in when branches, else CommandRegistry.create + CommandExecutor
Custom bind Signature → CommandInput; optional isolation lock
Finish Exit code from built-in run / handle() / help / errors
  • Wrong directory — many generators use --base as-is; serve / key:generate / kona grind walk upward to a Gradle root.
  • Silent stdout ≠ silent errors — -q and callSilently do not hide error(...).
  • Registry-only names in Grind.call — if you pass a registry without putting the command on the class loader’s META-INF, that is fine; if you rely only on META-INF, ensure the loader can see resources.
  • Built-in vs custom help — --help on a custom command is rich; on many built-ins you get usage via exceptions or dedicated output instead.