AI primer
Paste this into an AI chat when you want accurate help writing or reviewing Clak code. Prefer this file first; use the reference only for advanced topics.
How to use
Click the copy button on the block below, then paste it into your prompt (optionally add: “Follow this Clak context.”).
clak-ai-primer.txt
# Clak — AI context (primer)
Package: mrjk.clak (PyPI https://pypi.org/project/mrjk.clak/)
Import package name: clak
Python: 3.10–3.14 (requires-python >=3.10,<4.0)
Docs: https://mrjk.github.io/python-clak/
## What Clak is
Clak builds CLIs with Python classes on top of stdlib argparse.
If you know argparse, you know most of Clak. There is no Click/Typer decorator DSL.
Canonical API (prefer these names):
- Parser — app or subcommand class (argparse.ArgumentParser)
- Argument — class attribute = one option/positional (add_argument kwargs)
- Command — binds a child Parser as a subcommand (add_subparsers)
Optional helpers (not required; Argument still accepts both):
- Arg — positional names only (no leading -)
- Opt — option flags only (- / --)
Mixing names (Arg("--x"), Opt("NAME")) raises ValueError.
Aliases still work but prefer canonical names:
- ArgumentParser = Parser
- SubParser / SubCommand / Cmd = Command
## Install
pip install mrjk.clak
# optional: mrjk.clak[colors] (coloredlogs)
# optional: mrjk.clak[config] (PyYAML for YAML config / --format yaml)
# optional: mrjk.clak[markdown] (rich for MarkdownView)
# optional: mrjk.clak[rst] (docutils for RstView)
## Minimal app
from clak import Argument, Command, Parser
class Greet(Parser):
"""Greet someone."""
name = Argument("NAME", help="Who to greet")
def cli_run(self, name=None, **_):
print(f"Hello, {name}")
class App(Parser):
"""My CLI."""
verbose = Argument("-v", "--verbose", action="store_true")
greet = Command(Greet)
if __name__ == "__main__":
App() # instantiating root parses argv and runs the command
## Rules of thumb
1. Every command is a Parser subclass. Put logic in cli_run(**kwargs).
2. Argument destinations become cli_run keyword args. Use **_ for unused.
3. Parent options (on root) are visible to child cli_run.
4. Class docstring = help description. Attribute name of Command = CLI name.
5. Argument(...) uses the same kwargs as argparse add_argument().
6. Optional flags start with -/-- ; positionals are bare names (e.g. "NAME").
7. Prefer raise ClakUserError("msg") over print+sys.exit for user mistakes.
8. Mixins go LEFT of Parser: class App(LoggingOptMixin, Parser):
9. Arg/Opt are optional sugar. Argument still accepts both. Do not mix:
Arg is positionals only, Opt is flags only (ValueError if mixed).
10. Do not assume env vars auto-map to CLI options — not shipped.
11. Do not use Click/Typer patterns (@app.command, typer.Option, etc.).
12. Default root App() auto-dispatches. Use App(parse=False) to build without running.
## Nested commands
class Child(Parser):
def cli_run(self, **_):
print("child")
class Root(Parser):
child = Command(Child, help="Run child")
# CLI: prog child
# Optional: Meta.command_groups = (("base", "subcommands (base):"),)
# and Command(Child, command_group="base") for help sections (formatter only).
# Optional: Meta.help_subcommands = "top" # default "all" lists nested children
# Optional: Meta.help_hide_parent = False # default True hides parent path in nested listing
## Errors (basic)
from clak.exception import ClakUserError
def cli_run(self, name=None, **_):
if not name:
raise ClakUserError("NAME is required", advice="Pass a name")
Optional Meta on root:
class Meta:
known_exceptions = [MyAppError] # app exceptions with .rc / .advice
## Optional components (use only when needed)
| Need | Use |
| --- | --- |
| Tables / json/csv/yaml from return value | ListViewMixin / ShowViewMixin / PprintViewMixin / DataViewMixin |
| Plain / markdown / rst text | RawViewMixin / MarkdownViewMixin / RstViewMixin |
| Primary table + extras | CompositeViewMixin + return CompositeView(...) |
| Colored --help | default when Rich + TTY (mrjk.clak[markdown]); opt out Meta.help_formatter = RecursiveHelpFormatter |
| Nested --help listing | Meta.help_subcommands = "all" (default) or "top"; Meta.help_hide_parent = True (default) hides parent path |
| -v logging + self.logger | LoggingOptMixin |
| XDG paths + load config file | XDGConfigMixin (--conf-file → ctx.config) |
| Emit shell completion script | CompCmdRender as a Command |
| TTY / size / color on ctx | ctx.runtime (core); optional ctx.facts (host/user/distro) |
Views: return data from cli_run; mixin prints it.
Logging: configure Meta.log_levels as cumulative tiers of "LEVEL|logger".
Config: JSON always; YAML needs mrjk.clak[config]. Not merged into args.
MarkdownView needs mrjk.clak[markdown]; RstView needs mrjk.clak[rst].
Colored --help uses the same markdown extra (TTY; opt out RecursiveHelpFormatter).
Completion: generates argcomplete shellcode; runtime autocomplete during parse is not fully wired yet.
## Anti-patterns (do not generate)
- from clak import ListView # wrong; use from clak.views import ListView
Prefer mixins: from clak import ListViewMixin
- Calling .dispatch() after App() unless you used parse=False
- Hand-rolled argparse.ArgumentParser alongside Clak for the same CLI
- Using Argument(..., group="Title") (removed; use option_group= /
argument_group=). Exclusive XOR: exclusive_group="key" (shipped).
Intermixed args remain planned, not shipped.
## When this file is not enough
Ask for / paste the long document: “Clak AI reference” covering Meta,
hooks, views/logging/config details, testing with parse=False, and edge cases.