Getting Started with quiCkLI: Your First Command-Line Application
If you have ever wanted to build a small Python command-line tool but found larger
frameworks overwhelming, quiCkLI was designed for you. It is a minimal framework
that keeps the essentials visible so you can learn - and build - without unnecessary
complexity.
In this first article of the Getting Started with quiCkLI series you will build a
greeting tool with exactly three concepts: Application, Argument, and Option.
What you will build
$ python hello.py Ada
$ Hello, Ada!
$ python hello.py Ada --uppercase
$ HELLO, ADA!
A single file. No configuration. Under thirty lines of Python.
Application.run() returns the handler result. A real executable wrapper still needs to
print that result and decide how to map errors to exit codes.
Prerequisites
Install quickli in a virtual environment:
$ python -m venv .venv
$ source .venv/bin/activate # Windows: .venv\Scripts\activate
$ pip install quickli
You need Python 3.12 or later.
Step 1: Create the application
from quickli import Application
app = Application(
name="hello",
description="Greet a person from the command line.",
)
Application is the root container. It owns command registration and dispatch. You give
it a name (used in help output) and a short description.
Step 2: Register a handler
from quickli import Application, Argument, Option
@app.entrypoint(
help_text="Print a greeting.",
arguments=[Argument("name", help_text="Name to greet.")],
options=[
Option(
"uppercase",
short_name="u",
is_flag=True,
help_text="Print the greeting in uppercase.",
),
],
)
def greet(name: str, uppercase: bool = False) -> str:
message = f"Hello, {name}!"
return message.upper() if uppercase else message
@app.entrypoint registers a function as the single handler for this application.
Argument("name")declares a required positional value. quickli passes it directly to thenameparameter of your function.Option("uppercase", is_flag=True)declares a boolean flag. Present →True, absent →False.
Step 3: Run the application
if __name__ == "__main__":
print(app.run())
Application.run() reads sys.argv[1:] by default and returns the handler result. You
decide what to do with it - in this case, you print it.
The full file
from __future__ import annotations
from quickli import Application, Argument, Option
app = Application(
name="hello",
description="Greet a person from the command line.",
)
@app.entrypoint(
help_text="Print a greeting.",
arguments=[Argument("name", help_text="Name to greet.")],
options=[
Option(
"uppercase",
short_name="u",
is_flag=True,
help_text="Print the greeting in uppercase.",
),
],
)
def greet(name: str, uppercase: bool = False) -> str:
message = f"Hello, {name}!"
return message.upper() if uppercase else message
if __name__ == "__main__":
print(app.run())
Try it
$ python hello.py Ada # Hello, Ada!
$ python hello.py Ada -u # HELLO, ADA!
$ python hello.py # (help output)
What you learned
Applicationowns registration and dispatch.@app.entrypointregisters a single handler for the whole application.Argumentdescribes positional, required input.Optionwithis_flag=Trueadds a boolean switch.Application.run(argv)returns the result - you control output.
Next in this series
The next article adds file input, validation, and global options by building a
small file viewer inspired by the Unix cat command.

