Skip to main content

optique

Use this skill when writing any code that builds a command-line interface with Optique in TypeScript or JavaScript. Covers the combinatorial parser model, choosing @optique/core vs @optique/run, value parsers, structured messages, optional()/withDefault()/multiple(), subcommands with command() and or(), shell completion, async parsing, the integration packages, and common mistakes to avoid. Trigger whenever the user is parsing command-line arguments, building a CLI, or adding options or subcommands to a tool.

Source facts

Repository
dahlia/optique
Last source activity
September 28, 2026 at 12:58
Detected SKILL.md language
English
Stars
737
Forks
17

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
2 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
optique
description
Use this skill when writing any code that builds a command-line interface with Optique in TypeScript or JavaScript. Covers the combinatorial parser model, choosing @optique/core vs @optique/run, value parsers, structured messages, optional()/withDefault()/multiple(), subcommands with command() and or(), shell completion, async parsing, the integration packages, and common mistakes to avoid. Trigger whenever the user is parsing command-line arguments, building a CLI, or adding options or subcommands to a tool.
license
MIT
Start at <https://optique.dev/llms.txt> online; these rules also work offline. Core rules ---------- - Use `run()` from *@optique/run* for apps, `parse()`/`runParser()` to embed. - With `runParser()`, `onError(exitCode, error)` supplies a structured `Message`; `help.onShow(exitCode, page)` supplies the final `DocPage`. Both follow output. Supply `stdout: () => {}` for custom help rendering. See <https://optique.dev/concepts/runners.md#structured-help-callbacks>. - In tests, use `parseArgs()`/`parseArgsSync()` from *@optique/testing/parser* for parser results, `captureRun()` from *@optique/testing/run* for runner output/exits, `captureProgramRun()` from *@optique/testing/discover* for dispatch, and `createCliRunner()` from *@optique/testing/cli* for real CLIs. Pin `colors`/`maxWidth` in tests; defaults use terminal and environment. - Compose parsers with `object()`, `tuple()`, `seq()`, `or()`, `merge()`, and modifiers. Do not hand-write argument scanners around Optique parsers. - For command/option help headings, set the runner's `helpSections: { commands: "Commands", options: "Options" }`. It groups untitled entries only on pages with visible commands. See <https://optique.dev/cookbook.md#command-and-option-headings-in-help>. - Let TypeScript infer results unless another API needs a separate interface. - Parsers usually require input. `optional(p)` yields `undefined`; use `withDefault(p, value)` or `withDefault(flag("-v"), false)` for fallbacks. - Use semantic `message` helpers for descriptions and errors. Since 1.3.0, customize terminal output with `theme` or `messageFormatter`, preserving `initialWidth`, quoting, and width options. Keep canonical errors unthemed. `flag()` supports static/callback `errors.unexpectedValue` since 1.4.0. - Use value parsers such as `integer()`, `choice()`, `biject()`, `regExp()`, `url()`, `origin()`, and `uuid()` instead of validating raw strings after parsing. Use `regExp({ flags })` for user-supplied sources, `biject()` for one-to-one string-to-value choices, and `transform()` when an existing value parser describes the accepted CLI spelling but your app needs a different result type. Use `path()` from `@optique/run/valueparser` for file-system paths. Write a custom `{ mode, metavar, parse, format }` value parser only when the catalog does not cover the domain. - Since 1.4.0, `-p8080`/`-vp8080` accept attached values. Values consume the literal suffix (`-p=5` gives `"=5"`); full single-dash names win. - Async value parsers like *@optique/git* make containing parsers async. Await `run()`/`parse()`/`runParser()` or, for `bindKeyring()`, `runAsync()`. - Use `dependency()` when one value parser controls another's valid values. For a multi-level chain, wrap the middle derivation too: `dependency(source.deriveSync(...))`. Optique resolves such chains by dependency order, independently of object/tuple field order. - Use `derivePromptConfig(source, resolver)` (from *@optique/prompt*, re-exported by *@optique/inquirer* and *@optique/clack*) when a prompt's choices or message depend on another parsed value. The resolver may be async and runs only at the real prompt fallback, after the named sources resolve; pass `[sourceA, sourceB]` when it reads several sources. The resolver's prompt kind must return the wrapped parser's value type. Derive the wrapped parser separately when the CLI domain should change, and make it a dependency source only if another consumer needs its answer. Pass a lone resolver for fetched choices, forwarding its `signal` to the fetch. - Pass `{ validate, maxAttempts, signal }` as a generated prompt wrapper's third argument, including `prompt()` from *@optique/inquirer* and *@optique/clack*. The validator returns `undefined` to accept the prompted value or a structured `Message` to retry, synchronously or asynchronously. Attempt limits must be positive integers and default to unlimited retries. Selection prompts keep their config and use the shared `validate` option. - Implement a custom adapter's `execute(config, context)` so retries can show `context.previousValidationMessage`, and forward `context.signal` when the prompt library supports aborting active work. Adapter-native validation remains separate and completes inside one shared attempt. - Build subcommands with `command()` combined by `or()`. Put a literal field such as `command: constant("serve")` in each branch when you want a discriminated union. - Use `run(parser, { completion: "both" })` for completion, or the object form with `completion.errors` for custom shell errors. Do not hand-write scripts. - Use `usageLine: [{ type: "ellipsis" }]` in runner options when a large root synopsis should become a compact `Usage: myapp ...` line. This applies only to root full help; use `command()`'s `usageLine` for subcommand help. - Use `showUsage: false` in runner options when full help should show the brief and command or option sections without the `Usage:` synopsis. For deeply nested command trees, add `commandList: "top-level"` when root help should list only first-level command groups. - Use `termWidth: "auto"` in runner options when descriptions should align after the widest visible help term. Optique measures terminal display width after adding built-in help/version/completion entries. Canonical app shape ------------------- ~~~~ typescript import { object } from "@optique/core/constructs"; import { message } from "@optique/core/message"; import { withDefault } from "@optique/core/modifiers"; import { argument, flag, option } from "@optique/core/primitives"; import { integer, string } from "@optique/core/valueparser"; import { run } from "@optique/run"; const parser = object({ input: argument(string({ metavar: "FILE" }), { description: message`Input file to process.`, }), port: withDefault( option("--port", integer({ min: 1, max: 65535 }), { description: message`Port to listen on.`, }), 3000, ), verbose: withDefault( flag("-v", "--verbose", { description: message`Enable verbose logging.` }), false, ), }); const config = run(parser, { brief: message`Process a file.`, completion: "both", showDefault: true, termWidth: "auto", }); console.log(`Processing ${config.input} on port ${config.port}.`); ~~~~ Subcommands ----------- Use `command()` for each branch and `or()` to require exactly one matching subcommand. Use `optional(or(...))` only when no subcommand is valid. ~~~~ typescript import { object, or } from "@optique/core/constructs"; import { withDefault } from "@optique/core/modifiers"; import { parse } from "@optique/core/parser"; import { command, constant, flag, option } from "@optique/core/primitives"; import { integer } from "@optique/core/valueparser"; const parser = or( command("build", object({ command: constant("build"), watch: withDefault(flag("--watch"), false), })), command("serve", object({ command: constant("serve"), port: withDefault(option("--port", integer({ min: 1 })), 3000), })), ); const result = parse(parser, ["serve", "--port", "8080"]); if (result.success) { switch (result.value.command) { case "build": result.value.watch; break; case "serve": result.value.port; break; } } ~~~~ Custom value parsers -------------------- Prefer the built-in catalog first. If a one-to-one dictionary can describe the input tokens and domain values, use `biject()`. If an existing parser already accepts the right input syntax, wrap it with `transform()` before writing a custom parser: ~~~~ typescript import { biject, choice, transform } from "@optique/core/valueparser"; const exitCode = biject({ ok: 0, warning: 1, error: 2, }); const logLevel = transform(choice(["debug", "info", "warn", "error"] as const), { map(value) { return value.toUpperCase() as "DEBUG" | "INFO" | "WARN" | "ERROR"; }, unmap(value) { return value.toLowerCase() as "debug" | "info" | "warn" | "error"; }, }); ~~~~ When a custom domain is needed, keep the validation in a value parser so help, errors, defaults, prompts, and completion all see the same typed value. ~~~~ typescript import { message } from "@optique/core/message"; import type { ValueParser, ValueParserResult } from "@optique/core/valueparser"; const levels = ["debug", "info", "warn", "error"] as const; type Level = typeof levels[number]; function isLevel(input: string): input is Level { return (levels as readonly string[]).includes(input); } function logLevel(): ValueParser<"sync", Level> { return { mode: "sync", metavar: "LEVEL", placeholder: "info", parse(input: string): ValueParserResult<Level> { if (isLevel(input)) return { success: true, value: input }; return { success: false, error: message`Invalid log level: ${input}.` }; }, format(value: Level): string { return value; }, }; } const parser = logLevel(); ~~~~ Common mistakes checklist ------------------------- - Pass parsers to `run()` for apps; use explicit argument arrays with `parse()` in tests and embedded use. Do not pre-parse `process.argv`. - Do not treat `or(a, b)` as “zero or more alternatives.” It requires one matching branch unless the whole `or()` is wrapped in `optional()` or `withDefault()`. - Do not use `object()` for mutually exclusive subcommands. Use `or(command(...), command(...))`. - Do not forget that `flag("--x")` is required. Wrap it in `optional()` or `withDefault(..., false)` for ordinary optional flags. - Do not expect `multiple(p)` to fail when absent; it returns `[]`. Wrap with `nonEmpty()` when at least one value is required. - Do not confuse free-order parsing with `seq()`. Most constructs let child parsers compete by priority; use `seq()` only for truly ordered grammars. - Use structured `message` values for errors and descriptions. - Register contexts for `bindEnv()`, `bindConfig()`, `bindDerivedDefault()`, and `bindKeyring()` in the runner's `contexts` option. - Enable `showEnvironment` for env-only help; set `documentation.description`. - Do not flatten a multi-level dependency graph into duplicated one-level factories. Wrap each derived value that becomes a later source with `dependency()` and derive the next parser from it. - Do not probe runtime capabilities eagerly before constructing a prompt parser. Put synchronous or asynchronous checks in the prompt config's `when` field and provide a typed `otherwise` value. The check then runs only if parsing reaches the prompt fallback. For the detailed maintained guide, use <https://optique.dev/pitfalls.md>. Reference links --------------- - Documentation index for agents: <https://optique.dev/llms.txt> - Runners and entry points: <https://optique.dev/concepts/runners.md> - Primitive parsers: <https://optique.dev/concepts/primitives.md> - Construct combinators: <https://optique.dev/concepts/constructs.md> - Modifiers: <https://optique.dev/concepts/modifiers.md> - Value parser catalog: <https://optique.dev/concepts/valueparsers.md> - Inter-option dependencies: <https://optique.dev/concepts/dependencies.md> - Structured messages: <https://optique.dev/concepts/messages.md> - Shell completion: <https://optique.dev/concepts/completion.md> - Command discovery: <https://optique.dev/concepts/discover.md> - Man pages: <https://optique.dev/concepts/man.md> Integration packages -------------------- | Package | Use for | Docs | | --------------------------- | ------------------------------------------- | ----------------------------------------------------- |
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub