Skip to content

Piero Bosio Social Web Site Personale Logo Fediverso

Social Forum federato con il resto del mondo. Non contano le istanze, contano le persone

Optique 0.6.0: Shell completion support for type-safe CLI parsers

  • Optique 0.6.0 is here, bringing intelligent shell completion to your type-safe
    command-line applications. This release introduces built-in completion support
    for Bash, zsh, fish, PowerShell, and Nushell, making your CLIs more
    discoverable and user-friendly—all without sacrificing type safety or requiring
    duplicate definitions.

    For those new to [Optique]: it's a TypeScript CLI parser library that takes
    a fundamentally different approach from traditional configuration-based parsers.
    Instead of describing your CLI with configuration objects, you compose parsers
    from small, type-safe functions. TypeScript automatically infers the exact
    types of your parsed data, ensuring compile-time safety while the parser
    structure itself provides runtime validation. Think of it as bringing the
    composability of parser combinators (inspired by Haskell's
    optparse-applicative) together with the type safety of TypeScript's type
    system.

    Shell completion that just works

    The standout feature of this release is comprehensive shell completion support.
    Unlike many CLI frameworks that require separate completion definitions,
    Optique's completion system leverages the same parser structure used for
    argument parsing. This means your completion suggestions automatically stay
    synchronized with your CLI's actual behavior—no duplicate definitions, no
    manual maintenance.

    import { object } from "@optique/core/constructs";
    import { argument, option } from "@optique/core/primitives";
    import { string, choice } from "@optique/core/valueparser";
    import { run } from "@optique/run";
    
    const parser = object({
      format: option("-f", "--format", choice(["json", "yaml", "xml"])),
      output: option("-o", "--output", string({ metavar: "FILE" })),
      verbose: option("-v", "--verbose"),
      input: argument(string({ metavar: "INPUT" })),
    });
    
    // Enable completion with a single option
    const config = run(parser, { completion: "both" });
    

    Users can now press Tab to get intelligent suggestions:

    myapp <TAB>                    # Shows available commands and options
    myapp --format <TAB>           # Shows: json, yaml, xml
    myapp --format=<TAB>           # Same suggestions with equals syntax
    myapp -<TAB>                  # Shows: -f, -o, -v, and other short options
    

    Setting up completion is straightforward. Users generate a completion script
    for their shell and source it:

    # Bash
    myapp completion bash > ~/.bashrc.d/myapp.bash
    source ~/.bashrc.d/myapp.bash
    
    # zsh
    myapp completion zsh > ~/.zsh/completions/_myapp
    
    # fish
    myapp completion fish > ~/.config/fish/completions/myapp.fish
    
    # PowerShell
    myapp completion pwsh > myapp-completion.ps1
    . ./myapp-completion.ps1
    
    # Nushell
    myapp completion nu | save myapp-completion.nu
    source myapp-completion.nu
    

    The completion system works automatically with all Optique parser types. When
    you use choice() value parsers, the available options become completion
    suggestions. When you use path() parsers, file system completion kicks in
    with proper handling of extensions and file types. Subcommands, options, and
    arguments all provide context-aware suggestions.

    What makes Optique's completion special is that it leverages the same parser
    structure used for argument parsing. Every parser has an optional suggest()
    method that provides context-aware suggestions based on the current input.
    Parser combinators like object() and or() automatically aggregate
    suggestions from their constituent parsers, ensuring your completion logic
    stays in your TypeScript code where it benefits from type safety and testing.

    Optique handles the differences between shells transparently. Bash uses the
    complete command with proper handling of word splitting, zsh leverages its
    powerful compdef system with completion descriptions, fish provides
    tab-separated format with automatic file type detection, PowerShell uses
    Register-ArgumentCompleter with AST-based parsing, and Nushell integrates
    with its external completer system. For file and directory completions, Optique
    delegates to each shell's native file completion system, ensuring proper
    handling of spaces, symlinks, and platform-specific path conventions.

    Custom completion suggestions

    For domain-specific value parsers, you can implement custom completion logic
    that provides intelligent suggestions based on your application's needs:

    import type { ValueParser, ValueParserResult } from "@optique/core/valueparser";
    import type { Suggestion } from "@optique/core/parser";
    import { message } from "@optique/core/message";
    
    function httpMethod(): ValueParser<string> {
      const methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
    
      return {
        metavar: "METHOD",
        parse(input: string): ValueParserResult<string> {
          const method = input.toUpperCase();
          if (methods.includes(method)) {
            return { success: true, value: method };
          }
          return {
            success: false,
            error: message`Invalid HTTP method: ${input}. Valid methods: ${methods.join(", ")}.`,
          };
        },
        format(value: string): string {
          return value;
        },
        *suggest(prefix: string): Iterable<Suggestion> {
          for (const method of methods) {
            if (method.toLowerCase().startsWith(prefix.toLowerCase())) {
              yield {
                kind: "literal",
                text: method,
                description: message`HTTP ${method} request method`
              };
            }
          }
        },
      };
    }
    

    The built-in value parsers also provide intelligent suggestions. For instance,
    the locale() parser suggests common locale identifiers, the url() parser
    offers protocol completions when configured with allowedProtocols, and the
    timezone parsers from @optique/temporal use Intl.supportedValuesOf() for
    dynamic timezone suggestions.

    Enhanced command documentation

    This release also introduces new documentation capabilities for the command()
    parser. You can now provide separate brief and description texts, along
    with a footer for examples and additional information:

    import { command, object, constant } from "@optique/core/primitives";
    import { message } from "@optique/core/message";
    
    const deployCommand = command(
      "deploy",
      object({
        action: constant("deploy"),
        // ... options
      }),
      {
        brief: message`Deploy application to production`,  // Shown in command list
        description: message`Deploy the application to the production environment.
        
    This command handles database migrations, asset compilation, and cache warming
    automatically. It performs health checks before switching traffic to ensure
    zero-downtime deployment.`,  // Shown in detailed help
        footer: message`Examples:
      myapp deploy --environment staging --dry-run
      myapp deploy --environment production --force
    
    For deployment documentation, see: https://docs.example.com/deploy`
      }
    );
    

    The brief text appears when listing commands (like myapp help), while
    description provides detailed information when viewing command-specific help
    (myapp deploy --help or myapp help deploy). The footer appears at the
    bottom of the help text, perfect for examples and additional resources.

    Command-line example formatting

    To make help text and examples clearer, we've added a new commandLine()
    message term type. This displays command-line snippets with distinct cyan
    coloring in terminals, making it immediately clear what users should type:

    import { message, commandLine } from "@optique/core/message";
    import { run } from "@optique/run";
    
    const config = run(parser, {
      footer: message`Examples:
      ${commandLine("myapp --format json input.txt")}
      ${commandLine("myapp --format=yaml --output result.yml data.txt")}
      
    To enable shell completion:
      ${commandLine("myapp completion bash > ~/.bashrc.d/myapp.bash")}
      ${commandLine("source ~/.bashrc.d/myapp.bash")}`,
      
      completion: "both"
    });
    

    These command examples stand out visually in help text, making it easier for
    users to understand how to use your CLI.

    Migration guide

    If you're already using Optique, adding completion support is straightforward:

    1. Update to Optique 0.6.0
    2. Add the completion option to your run() configuration:
    // Before
    const config = run(parser, { help: "both" });
    
    // After
    const config = run(parser, { 
      help: "both",
      completion: "both"  // Adds both 'completion' command and '--completion' option
    });
    

    That's it! Your CLI now supports shell completion. The completion option
    accepts three modes:

    • "command": Only the completion subcommand (e.g., myapp completion bash)
    • "option": Only the --completion option (e.g., myapp --completion bash)
    • "both": Both patterns work

    For custom value parsers, you can optionally add a suggest() method to
    provide domain-specific completions. Existing parsers continue to work without
    modification—they just won't provide custom suggestions beyond what the parser
    structure implies.

    Looking forward

    Shell completion has been one of the most requested features for Optique, and
    we're thrilled to deliver it in a way that maintains our core principles: type
    safety, composability, and zero duplication. Your parser definitions remain
    the single source of truth for both parsing and completion behavior.

    This release represents a significant step toward making Optique-based CLIs as
    user-friendly as they are developer-friendly. The completion system proves that
    we can provide sophisticated runtime features without sacrificing the
    compile-time guarantees that make Optique unique.

    We hope you find the new shell completion feature useful and look forward to
    seeing what you build with it!

    Getting started

    To start using Optique 0.6.0:

    deno add --jsr @optique/core@^0.6.0 @optique/run@^0.6.0
    npm  add       @optique/core@^0.6.0 @optique/run@^0.6.0
    pnpm add       @optique/core@^0.6.0 @optique/run@^0.6.0
    yarn add       @optique/core@^0.6.0 @optique/run@^0.6.0
    bun  add       @optique/core@^0.6.0 @optique/run@^0.6.0
    

    For complete documentation, visit optique.dev. Check out the new
    shell completion guide for detailed setup instructions and advanced usage
    patterns.

    For bug reports and feature requests, please visit our GitHub repository.

  • hongminhee@hollo.socialundefined hongminhee@hollo.social shared this topic on

Gli ultimi otto messaggi ricevuti dalla Federazione
Post suggeriti
  • #Optique 0.9.0 is here!

    Uncategorized cli async optique typescript
    1
    0 Votes
    1 Posts
    3 Views
    #Optique 0.9.0 is here! This release brings #async/await support to #CLI parsers. Now you can validate input against external resources—databases, APIs, Git repositories—directly at parse time, with full #TypeScript type safety. The new @optique/git package showcases this: validate branch names, tags, and commit SHAs against an actual Git repo, complete with shell completion suggestions. Other highlights: Hidden option support for deprecated/internal flags Numeric choices in choice() Security fix for shell completion scripts Fully backward compatible—your existing parsers work unchanged. https://github.com/dahlia/optique/discussions/75
  • twitch-dl 3.3.0 - Twitch CLI downloader

    Uncategorized twitch cli
    1
    0 Votes
    1 Posts
    4 Views
    twitch-dl 3.3.0 - Twitch CLI downloaderThis version lets you download HD streams (1440p, 2160p, where available) without being logged in and regardless of your geo location.https://github.com/ihabunek/twitch-dl/releases/tag/3.3.0#twitch #cli
  • 0 Votes
    1 Posts
    5 Views
    CLIパーサーの新しい記事を書きました。--reporterの値によって--output-fileが必須になったり禁止になったり…そういう関係、型で表現できたら楽じゃないですか? https://zenn.dev/hongminhee/articles/201ca6d2e57764 #TypeScript #CLI #Optique
  • 0 Votes
    1 Posts
    10 Views
    We're excited to announce Optique 0.8.0! This release introduces powerful new features for building sophisticated CLI applications: the conditional() combinator for discriminated union patterns, the passThrough() parser for wrapper tools, and the new @optique/logtape package for seamless logging configuration. Optique is a type-safe combinatorial CLI parser for TypeScript, providing a functional approach to building command-line interfaces with composable parsers and full type inference. New conditional parsing with conditional() Ever needed to enable different sets of options based on a discriminator value? The new conditional() combinator makes this pattern first-class. It creates discriminated unions where certain options only become valid when a specific discriminator value is selected. import { conditional, object } from "@optique/core/constructs"; import { option } from "@optique/core/primitives"; import { choice, string } from "@optique/core/valueparser"; const parser = conditional( option("--reporter", choice(["console", "junit", "html"])), { console: object({}), junit: object({ outputFile: option("--output-file", string()) }), html: object({ outputFile: option("--output-file", string()) }), } ); // Result type: ["console", {}] | ["junit", { outputFile: string }] | ... Key features: Explicit discriminator option determines which branch is selected Tuple result [discriminator, branchValue] for clear type narrowing Optional default branch for when discriminator is not provided Clear error messages indicating which options are required for each discriminator value The conditional() parser provides a more structured alternative to or() for discriminated union patterns. Use it when you have an explicit discriminator option that determines which set of options is valid. See the conditional() documentation for more details and examples. Pass-through options with passThrough() Building wrapper CLI tools that need to forward unrecognized options to an underlying tool? The new passThrough() parser enables legitimate wrapper/proxy patterns by capturing unknown options without validation errors. import { object } from "@optique/core/constructs"; import { option, passThrough } from "@optique/core/primitives"; const parser = object({ debug: option("--debug"), extra: passThrough(), }); // mycli --debug --foo=bar --baz=qux // → { debug: true, extra: ["--foo=bar", "--baz=qux"] } Key features: Three capture formats: "equalsOnly" (default, safest), "nextToken" (captures --opt val pairs), and "greedy" (captures all remaining tokens) Lowest priority (−10) ensures explicit parsers always match first Respects -- options terminator in "equalsOnly" and "nextToken" modes Works seamlessly with object(), subcommands, and other combinators This feature is designed for building Docker-like CLIs, build tool wrappers, or any tool that proxies commands to another process. See the passThrough() documentation for usage patterns and best practices. LogTape logging integration The new @optique/logtape package provides seamless integration with LogTape, enabling you to configure logging through command-line arguments with various parsing strategies. # Deno deno add --jsr @optique/logtape @logtape/logtape # npm npm add @optique/logtape @logtape/logtape Quick start with the loggingOptions() preset: import { loggingOptions, createLoggingConfig } from "@optique/logtape"; import { object } from "@optique/core/constructs"; import { parse } from "@optique/core/parser"; import { configure } from "@logtape/logtape"; const parser = object({ logging: loggingOptions({ level: "verbosity" }), }); const args = ["-vv", "--log-output=-"]; const result = parse(parser, args); if (result.success) { const config = await createLoggingConfig(result.value.logging); await configure(config); } The package offers multiple approaches to control log verbosity: verbosity() parser: The classic -v/-vv/-vvv pattern where each flag increases verbosity (no flags → "warning", -v → "info", -vv → "debug", -vvv → "trace") debug() parser: Simple --debug/-d flag that toggles between normal and debug levels logLevel() value parser: Explicit --log-level=debug option for direct level selection logOutput() parser: Log output destination with - for console or file path for file output See the LogTape integration documentation for complete examples and configuration options. Bug fix: negative integers now accepted Fixed an issue where the integer() value parser rejected negative integers when using type: "number". The regex pattern has been updated from /^\d+$/ to /^-?\d+$/ to correctly handle values like -42. Note that type: "bigint" already accepted negative integers, so this change brings consistency between the two types. Installation # Deno deno add jsr:@optique/core # npm npm add @optique/core # pnpm pnpm add @optique/core # Yarn yarn add @optique/core # Bun bun add @optique/core For the LogTape integration: # Deno deno add --jsr @optique/logtape @logtape/logtape # npm npm add @optique/logtape @logtape/logtape # pnpm pnpm add @optique/logtape @logtape/logtape # Yarn yarn add @optique/logtape @logtape/logtape # Bun bun add @optique/logtape @logtape/logtape Looking forward Optique 0.8.0 continues our focus on making CLI development more expressive and type-safe. The conditional() combinator brings discriminated union patterns to the forefront, passThrough() enables new wrapper tool use cases, and the LogTape integration makes logging configuration a breeze. As always, all new features maintain full backward compatibility—your existing parsers continue to work unchanged. We're grateful to the community for feedback and suggestions. If you have ideas for future improvements or encounter any issues, please let us know through GitHub Issues. For more information about Optique and its features, visit the documentation or check out the full changelog.