CLI Architecture
CLI Architecture
Section titled “CLI Architecture”This is a reference for contributors adding or changing commands in ksef2-cli.
For the in-progress typed rendering refactor (Pydantic results, singledispatch renderers), see Rendering Refactor Plan.
Design Goals
Section titled “Design Goals”- Keep command modules focused on user-facing workflows.
- Keep SDK authentication, output rendering, file I/O, and model conversion in shared helpers.
- Keep every implementation file comfortably below 400 lines.
- Preserve fast
--helpstartup by avoiding top-levelksef2SDK imports unless Typer needs the type for command registration.
Module Layout
Section titled “Module Layout”src/ksef2_cli/ app.py Root Typer app, global options, command registration config.py Profile config models, settings resolution, output mode, environment names context.py Typer adapter for command execution and rendering runtime.py Typer-free client creation, authentication, and model reads invoice_workflows.py Shared invoice metadata, send, export, and UPO workflows io.py JSON file/stdin reads and JSON writes parsing.py CLI string parsing helpers results.py CLI-owned typed result models renderers/ Plain text/JSON output rendering commands/ config.py Local config path/show/init commands profile.py Local profile create/list/current/show/use commands auth.py Authentication and refresh commands invoices/ Thin Typer adapters for invoice workflows online.py Online session workflows batch.py Batch submission and inspection tokens.py KSeF token lifecycle sessions.py Auth and invoice session listings certificates.py Certificate enrollment, query, retrieval, revocation permissions.py Permission grant/query/revoke workflows limits.py Limit query/update/reset workflows peppol.py Public PEPPOL provider query encryption.py Public encryption certificate query testdata.py TEST-environment data helpersCommand Flow
Section titled “Command Flow”app.pybuilds the rootTyperapp and stores globalSettingsonctx.obj.- A command module receives
ctx: typer.Context. config.resolve_settings()selects a profile, applies CLI/environment overrides, and stores runtime-ready settings.- Commands with local file or multi-step work define a zero-argument
operationand pass it torun_command(ctx, operation). - Single authenticated SDK calls use
run_authenticated_command(ctx, command); userun_authenticated(ctx, operation)only when the command has local work around the SDK call. - Public commands call
run_client_command(ctx, command)when they only need a root SDK client. - Shared invoice behavior lives behind
invoice_workflows.pyso command modules can stay thin while preserving one implementation for send/status/UPO flows. - Human output is plain text by default;
--jsonemits formatted JSON for scripts. The renderer preserves Pydantic/dataclass types until the final formatting step.
Adding a Command
Section titled “Adding a Command”- Put CLI-only parameter handling in the matching
src/ksef2_cli/commands/*.pymodule. - If the command needs a new reusable parser, add it to
parsing.py. - Put shared workflow behavior in an owner module such as
invoice_workflows.pyonly when multiple command modules use the same implementation. - Avoid importing
ksef2SDK models at module import time unless Typer needs the type for command choices. Prefer local imports inside helper functions or command operations. - Add or update a smoke test for command registration or helper behavior.
- Run:
uv run pytestuv run ksef2 --helpFile Size Rule
Section titled “File Size Rule”Use 400 lines as a hard warning threshold. If a command module approaches that size:
- Split repeated option groups into helper functions.
- Keep model construction near the owning command unless it has meaningful shared behavior.
- Split the command group into a subpackage only when a single domain naturally has multiple subdomains.