Files
cligen/CLAUDE.md
T
2026-08-09 15:20:06 -05:00

77 lines
4.5 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
```bash
# Run all specs
crystal spec
# Run a single spec file
crystal spec spec/command_spec.cr
# Type-check without running
crystal build src/cligen.cr --no-codegen
# Install dependencies
shards install
```
## What This Is
`cligen` is a Crystal **shard (library)** — not a standalone application. It wraps Crystal's built-in `OptionParser` with an annotation- and macro-driven system that auto-generates CLI parsers from class definitions. Users of the shard subclass `CliGen::Command` and annotate methods; the library generates the `OptionParser` wiring at compile time via Crystal macros.
## Architecture
### Entry point: `src/cligen.cr`
Defines the `CliGen` module. The `macro finished` hook calls `define_root_parser`, which scans all `CliGen::Command` subclasses at compile time and registers each as a subcommand on the root `OptionParser`. `CliGen.parse` runs the root parser.
`ADDITIONAL_DEFAULT_FLAGS` / `add_default_flag` let users inject extra flags into every parser (root and subcommand).
### Command definition: `src/cligen/command.cr`
`CliGen::Command` is the base class. When subclassed, `macro inherited` installs a `macro finished` block that triggers four code-generation macros in order:
1. `define_actions` — collects all `@[SubCommand]`-annotated class methods into `ACTIONS : Array(String)` and `@@action : String`.
2. `define_header` — builds the `HEADER` string (banner + examples) from `@[SubCommand]` and `@[CommandSelection]` annotation metadata.
3. `define_runner` (skipped if `@[CommandInfo(def_runner: false)]`) — generates a `self.run` method that dispatches on `@@action` or a selection variable via a `case` statement.
4. `define_action_setter` — generates `self.action=` with bounds-checking against `ACTIONS`.
#### Key macros on `Command`
- **`define_argument`** — declares a class-level variable (`@@`) and an annotated setter method (`@[CommandArgument]`). Handles `String`, `Bool`, `Int32`, `Array(String|Int32)`, and `Time`. Optional `check:` proc, `logger:` method, and `def_getter:` flag.
- **`define_selection`** — like `define_argument` but validates against a fixed list of values; annotated with `@[CommandArgument]` and participates in selection-based dispatch.
### Parser generation: `src/cligen/command/parser.cr`
`CliGen::Parser` is `extend`ed by `Command`. It provides `define_parser`, which generates `self.make_parser(parent_parser)`. That method:
1. Creates a subparser `OptionParser` with the command's `HEADER` as banner.
2. Wires `@[CommandSelection]`-annotated methods as `parser.on(name, description)` that set the selection variable.
3. Wires `@[SubCommand]`-annotated methods as subcommand strings that set `@@action`.
4. Wires `@[CommandArgument]`-annotated methods as `parser.on(short, long, description)` flag handlers.
5. Calls `CliGen.define_default_flags` (adds `-h`/`--help`, error handlers).
6. Registers the whole subparser on `parent_parser` under the command's lowercase class name.
### Annotations
| Annotation | Applied to | Purpose |
|---|---|---|
| `@[CommandInfo(description:, def_runner:)]` | Command subclass | Required; provides the description shown in root help; `def_runner: false` skips auto-generating `run` |
| `@[SubCommand(description:, examples:)]` | class method on Command | Marks a method as a dispatachable subcommand |
| `@[CommandArgument(short:, long:, description:, type:)]` | class method on Command | Generated automatically by `define_argument`/`define_selection`; drives parser wiring |
| `@[CommandSelection(selector:, description:, examples:)]` | class method on Command | Alternative to `SubCommand`; dispatches via a named selector variable instead of `@@action` |
| `@[CommandPreRun]` | class method on Command | Methods run unconditionally before dispatch inside `self.run` |
| `@[DefaultFlag]` | (reserved) | Defined but not currently used in generation |
### Supporting files
- `src/cligen/format.cr` — date/datetime format strings used by `Time` argument parsing.
- `src/cligen/regex.cr` — regexes for validating date/datetime input strings.
## Spec structure
`spec/command_spec.cr` uses Crystal's macro system heavily: most `it` blocks are generated at compile time by inspecting `CommandSubclass` via `@type` introspection. The `macro finished` wrapper around the entire `describe` block is required because `make_parser` and `run` don't exist until all `macro finished` hooks have fired.