The current citydb-tool CLI works well for interactive human use but is challenging to automate or integrate into other tools. Only a few commands, such as info or connect, provide structured output (JSON or text). And such text output is mixed with log messages, which makes parsing difficult. Similarly, error handling relies on exit codes and log messages, requiring fragile log parsing to extract meaningful details. Structured JSON error messages would improve machine usability.
I propose a consistent CLI design for all commands, with the following principles:
1. Well-defined command output
Every command must produce a clearly defined output, or answer. Some commands already do so—for example, info returns a database report, and connect returns a yes/no result. For all other commands, we must define an answer as well, even if it is simply success/failure or a summary of actions performed (e.g., for the import or delete command).
2. Separation of output channels
The answer always goes to stdout, and nothing else is allowed on stdout. This is already a CLI best practice. Outputs should support multiple formats:
- Text: human-readable, and unlike log messages without timestamps, log levels or severity—just the command’s answer.
- JSON/YAML: machine-readable, following a common schema for success, failure, and errors (including exit codes).
3. Logs to stderr
All log messages, regardless of level, must go to stderr. This is partially implemented but some commands currently mix logs with the output or answer, which must be avoided.
4. Consistent exit codes
Exit codes should remain consistent across commands. This is implemented already.
5. Machine vs. human mode
The CLI defaults to human (interactive) mode. Machine mode can be enabled via a --response-format flag (text or json, with YAML and others possible in the future). A quiet flag can suppress logs.
6. Consistency within modes, not across modes
Each mode is internally consistent, but both modes may differ in stdout behavior. Human mode follows CLI conventions (data on stdout for success, empty stdout on error). Machine mode provides structured responses on stdout for both success and error cases. This allows human-friendly behavior in text mode while ensuring predictable API-like behavior for automation. For example, if the info command fails due to errors, then stdout is empty in human mode and all error information can be found in the logs. In machine mode, however, a JSON error message is printed to stdout in this case.
I think this design can be consistently applied to all commands, even those whose answer is just success/failure. A possible exception is the export command: the exported data could be seen as answer but it cannot go to stdout due to large size, multiple files in case of tiling, or formats like ZIP/GZIP. Similar to import and delete, the command’s answer should therefore be an export summary, while the data can only be written to files (as currently implemented).
Redesigning the CLI this way would make it significantly more usable for both humans and machines. But is also requires some implementation work, so feedback is welcome.
The current
citydb-toolCLI works well for interactive human use but is challenging to automate or integrate into other tools. Only a few commands, such asinfoorconnect, provide structured output (JSON or text). And such text output is mixed with log messages, which makes parsing difficult. Similarly, error handling relies on exit codes and log messages, requiring fragile log parsing to extract meaningful details. Structured JSON error messages would improve machine usability.I propose a consistent CLI design for all commands, with the following principles:
1. Well-defined command output
Every command must produce a clearly defined output, or answer. Some commands already do so—for example,
inforeturns a database report, andconnectreturns a yes/no result. For all other commands, we must define an answer as well, even if it is simply success/failure or a summary of actions performed (e.g., for theimportordeletecommand).2. Separation of output channels
The answer always goes to
stdout, and nothing else is allowed onstdout. This is already a CLI best practice. Outputs should support multiple formats:3. Logs to stderr
All log messages, regardless of level, must go to
stderr. This is partially implemented but some commands currently mix logs with the output or answer, which must be avoided.4. Consistent exit codes
Exit codes should remain consistent across commands. This is implemented already.
5. Machine vs. human mode
The CLI defaults to human (interactive) mode. Machine mode can be enabled via a
--response-formatflag (textorjson, with YAML and others possible in the future). Aquietflag can suppress logs.6. Consistency within modes, not across modes
Each mode is internally consistent, but both modes may differ in
stdoutbehavior. Human mode follows CLI conventions (data onstdoutfor success, emptystdouton error). Machine mode provides structured responses onstdoutfor both success and error cases. This allows human-friendly behavior in text mode while ensuring predictable API-like behavior for automation. For example, if theinfocommand fails due to errors, thenstdoutis empty in human mode and all error information can be found in the logs. In machine mode, however, a JSON error message is printed tostdoutin this case.I think this design can be consistently applied to all commands, even those whose answer is just success/failure. A possible exception is the
exportcommand: the exported data could be seen as answer but it cannot go tostdoutdue to large size, multiple files in case of tiling, or formats like ZIP/GZIP. Similar toimportanddelete, the command’s answer should therefore be an export summary, while the data can only be written to files (as currently implemented).Redesigning the CLI this way would make it significantly more usable for both humans and machines. But is also requires some implementation work, so feedback is welcome.