tristan fe6112795a Made several modifications:
- Added more complex date/time parsing for Time objects. This includes
  the ability to provide times in epoch format and several others with
  the ability to provide inline timezone offsets without external
  codebase works to convert them. Alongside this I added the ability to
  do relative time (ex: [+-]2 hours|minutes|days) (along with the ability
  to specify timezone if needed)
- Littered the entire codebase with Log object calls to show state of
  everything as the framework does it's stuff. Just needs Log to be
  configured in the codebase consuming my shard
- removed flag collection for use in displaying all flags in the app
  help output. So it will only show global flags & subcommands
- Changed CommandNode#get(short:) & get(long:) & flag?(arg : String)
  methods to check @flags -> @commands.@flags -> CliGen::GLOBAL_FLAGS
  when finding flags. Making it possible to not PRINT out the flag
  output but still be able to set values to a child flag recursively.
2026-08-29 18:51:08 -05:00
2026-08-09 15:20:06 -05:00
2026-08-11 11:24:22 -05:00
2026-08-29 18:51:08 -05:00
2026-08-11 11:22:02 -05:00
2026-08-09 15:20:06 -05:00
2026-02-23 10:30:34 -06:00
2026-08-09 15:20:06 -05:00
2026-02-23 13:09:33 -06:00
2026-08-09 15:20:06 -05:00

cligen

A Crystal shard that generates CLI parsers from class definitions using annotations and macros. Define your commands as classes; cligen builds the runtime parse tree.

How It Works

Subclass CliGen::Command, annotate your instance variables with @[CliGen::Argument] or @[CliGen::Selection], and register the command with an CliGen::App. At compile time, macros inspect the annotations and generate typed Flag(T) objects; at runtime, CliGen::App.process walks the CommandNode tree to route arguments, populate your command instance, and dispatch to the right method.

Installation

  1. Add the dependency to your shard.yml:

    dependencies:
      cligen:
        git: https://git.arcanium.tech/tristan/cligen
    
  2. Run shards install

Usage

require "cligen"

@[CliGen::CommandInfo(description: "Greet someone")]
class Greet < CliGen::Command
  @[CliGen::Argument(short: "-n", long: "--name VALUE", description: "Name to greet")]
  @name : String = "world"

  def main
    puts "Hello, #{@name}!"
  end
end

CliGen::App.process
$ myapp --name Alice
Hello, Alice!

Full API documentation and design notes are in design.adoc.

Development

# Type-check without running
crystal build src/cligen.cr --no-codegen

# Run specs
crystal spec

AI Assistance Disclosure

This project uses Claude Code as a development aid — specifically for catching bugs, spotting typos, reviewing implementations, and talking through design decisions. All architecture decisions, code, and design are written by the author. Claude is used the way one might use a second pair of eyes on a diff, not as a code generator.

CLAUDE.md at the repo root documents the project structure for Claude's context. .claude/ holds project-level Claude Code settings.

Contributors

S
Description
No description provided
Readme MIT 666 KiB
Languages
Crystal 100%