洪 民憙 (Hong Minhee) 
@hongminhee@hollo.social
CLIパーサーの新しい記事を書きました。--reporterの値によって--output-fileが必須になったり禁止になったり…そういう関係、型で表現できたら楽じゃないですか?

@hongminhee@hollo.social
CLIパーサーの新しい記事を書きました。--reporterの値によって--output-fileが必須になったり禁止になったり…そういう関係、型で表現できたら楽じゃないですか?

@hongminhee@hollo.social
CLIパーサーの新しい記事を書きました。--reporterの値によって--output-fileが必須になったり禁止になったり…そういう関係、型で表現できたら楽じゃないですか?

@hongminhee@hollo.social
CLIパーサーの新しい記事を書きました。--reporterの値によって--output-fileが必須になったり禁止になったり…そういう関係、型で表現できたら楽じゃないですか?

@hongminhee@hollo.social
CLIパーサーの新しい記事を書きました。--reporterの値によって--output-fileが必須になったり禁止になったり…そういう関係、型で表現できたら楽じゃないですか?

@hongminhee@hollo.social
CLIパーサーの新しい記事を書きました。--reporterの値によって--output-fileが必須になったり禁止になったり…そういう関係、型で表現できたら楽じゃないですか?

@hongminhee@hollo.social
CLIパーサーの新しい記事を書きました。--reporterの値によって--output-fileが必須になったり禁止になったり…そういう関係、型で表現できたら楽じゃないですか?

@hongminhee@hollo.social
Interesting design question for #Optique (a type-safe #CLI parser for #TypeScript): how should it handle unrecognized options in wrapper/proxy tools? Proposed three modes but wondering if the complexity is worth it. Thoughts?

@hongminhee@hollo.social
Interesting design question for #Optique (a type-safe #CLI parser for #TypeScript): how should it handle unrecognized options in wrapper/proxy tools? Proposed three modes but wondering if the complexity is worth it. Thoughts?

@hongminhee@hollo.social
Interesting design question for #Optique (a type-safe #CLI parser for #TypeScript): how should it handle unrecognized options in wrapper/proxy tools? Proposed three modes but wondering if the complexity is worth it. Thoughts?

@hongminhee@hollo.social
Interesting design question for #Optique (a type-safe #CLI parser for #TypeScript): how should it handle unrecognized options in wrapper/proxy tools? Proposed three modes but wondering if the complexity is worth it. Thoughts?

@hongminhee@hollo.social
#Optique 0.3.0 is out with dependent options and flexible parser composition, shaped by feedback from @z9mb1's work migrating @fedify CLI from Cliffy to Optique.
@hongminhee@hackers.pub
We're releasing Optique 0.3.0 with several improvements that make building complex CLI applications more straightforward. This release focuses on expanding parser flexibility and improving the help system based on feedback from the community, particularly from the Fedify project's migration from Cliffy to Optique. Special thanks to @z9mb1 for her invaluable insights during this process.
flag() parser for dependent options patternswithDefault() supporting union types for conditional CLI structuresor() capacity now supporting up to 10 parsers (previously 5)merge() combinator that works with any object-producing parser, not just object()longestMatch() combinator@optique/core and @optique/runflag() The new flag() parser creates Boolean flags that must be explicitly provided. While option() defaults to false when absent, flag() fails parsing entirely if not present. This subtle difference enables cleaner patterns for dependent options.
Consider a scenario where certain options only make sense when a mode is explicitly enabled:
import { flag, object, option, withDefault } from "@optique/core/parser";
import { integer } from "@optique/core/valueparser";
// Without the --advanced flag, these options aren't available
const parser = withDefault(
object({
advanced: flag("--advanced"),
maxThreads: option("--threads", integer()),
cacheSize: option("--cache-size", integer())
}),
{ advanced: false as const }
);
// Usage:
// myapp → { advanced: false }
// myapp --advanced → Error: --threads and --cache-size required
// myapp --advanced --threads 4 --cache-size 100 → Success
This pattern is particularly useful for confirmation flags (--yes-i-am-sure) or mode switches that fundamentally change how your CLI behaves.
withDefault() Previously, withDefault() required the default value to match the parser's type exactly. Now it supports different types, creating union types that enable conditional CLI structures:
const conditionalParser = withDefault(
object({
server: flag("-s", "--server"),
port: option("-p", "--port", integer()),
host: option("-h", "--host", string())
}),
{ server: false as const }
);
// Result type is now a union:
// | { server: false }
// | { server: true, port: number, host: string }
This change makes it much easier to build CLIs where different flags enable different sets of options, without resorting to complex or() chains.
merge() combinator The merge() combinator now accepts any parser that produces object-like values. Previously limited to object() parsers, it now works with withDefault(), map(), and other transformative parsers:
const transformedConfig = map(
object({
host: option("--host", string()),
port: option("--port", integer())
}),
({ host, port }) => ({ endpoint: `${host}:${port}` })
);
const conditionalFeatures = withDefault(
object({
experimental: flag("--experimental"),
debugLevel: option("--debug-level", integer())
}),
{ experimental: false as const }
);
// Can now merge different parser types
const appConfig = merge(
transformedConfig, // map() result
conditionalFeatures, // withDefault() parser
object({ // traditional object()
verbose: option("-v", "--verbose")
})
);
This improvement came from recognizing that many parsers ultimately produce objects, and artificially restricting merge() to only object() parsers was limiting composition patterns.
longestMatch() The new longestMatch() combinator selects the parser that consumes the most input tokens. This enables sophisticated help systems where command --help shows help for that specific command rather than global help:
const normalParser = object({
help: constant(false),
command: or(
command("list", listOptions),
command("add", addOptions)
)
});
const contextualHelp = object({
help: constant(true),
commands: multiple(argument(string())),
helpFlag: flag("--help")
});
const cli = longestMatch(normalParser, contextualHelp);
// myapp --help → Shows global help
// myapp list --help → Shows help for 'list' command
// myapp add --help → Shows help for 'add' command
The run() functions in both @optique/core/facade and @optique/run now use this pattern automatically, so your CLI gets context-aware help without any additional configuration.
Both @optique/core/facade and @optique/run now support version display through --version flags and version commands. See the runners documentation for details:
// @optique/run - simple API
run(parser, {
version: "1.0.0", // Adds --version flag
help: "both"
});
// @optique/core/facade - detailed control
run(parser, "myapp", args, {
version: {
mode: "both", // --version flag AND version command
value: "1.0.0",
onShow: process.exit
}
});
The API follows the same pattern as help configuration, keeping things consistent and predictable.
The new output functions in @optique/run provide consistent terminal formatting with automatic capability detection. Learn more in the messages documentation:
import { print, printError, createPrinter } from "@optique/run";
import { message } from "@optique/core/message";
// Standard output with automatic formatting
print(message`Processing ${filename}...`);
// Error output to stderr with optional exit
printError(message`File ${filename} not found`, { exitCode: 1 });
// Custom printer for specific needs
const debugPrint = createPrinter({
stream: "stderr",
colors: true,
maxWidth: 80
});
debugPrint(message`Debug: ${details}`);
These functions automatically detect terminal capabilities and apply appropriate formatting, making your CLI output consistent across different environments.
While we've tried to maintain backward compatibility, there are a few changes to be aware of:
help option in @optique/run no longer accepts "none". Simply omit the option to disable help.getDocFragments() need to update their signature to use DocState<TState> instead of direct state values.object() parser now uses greedy parsing, attempting to consume all matching fields in one pass. This shouldn't affect most use cases but may change parsing order in complex scenarios.To upgrade to Optique 0.3.0, update both packages:
# Deno (JSR)
deno add @optique/core@^0.3.0 @optique/run@^0.3.0
# npm
npm update @optique/core @optique/run
# pnpm
pnpm update @optique/core @optique/run
# Yarn
yarn upgrade @optique/core @optique/run
# Bun
bun update @optique/core @optique/run
If you're only using the core package:
# Deno (JSR)
deno add @optique/core@^0.3.0
# npm
npm update @optique/core
These improvements came from real-world usage and community feedback. We're particularly interested in hearing how the new dependent options patterns work for your use cases, and whether the context-aware help system meets your needs.
As always, you can find complete documentation at optique.dev and file issues or suggestions on GitHub.

@hongminhee@hollo.social
#Optique 0.3.0 is out with dependent options and flexible parser composition, shaped by feedback from @z9mb1's work migrating @fedify CLI from Cliffy to Optique.
@hongminhee@hackers.pub
We're releasing Optique 0.3.0 with several improvements that make building complex CLI applications more straightforward. This release focuses on expanding parser flexibility and improving the help system based on feedback from the community, particularly from the Fedify project's migration from Cliffy to Optique. Special thanks to @z9mb1 for her invaluable insights during this process.
flag() parser for dependent options patternswithDefault() supporting union types for conditional CLI structuresor() capacity now supporting up to 10 parsers (previously 5)merge() combinator that works with any object-producing parser, not just object()longestMatch() combinator@optique/core and @optique/runflag() The new flag() parser creates Boolean flags that must be explicitly provided. While option() defaults to false when absent, flag() fails parsing entirely if not present. This subtle difference enables cleaner patterns for dependent options.
Consider a scenario where certain options only make sense when a mode is explicitly enabled:
import { flag, object, option, withDefault } from "@optique/core/parser";
import { integer } from "@optique/core/valueparser";
// Without the --advanced flag, these options aren't available
const parser = withDefault(
object({
advanced: flag("--advanced"),
maxThreads: option("--threads", integer()),
cacheSize: option("--cache-size", integer())
}),
{ advanced: false as const }
);
// Usage:
// myapp → { advanced: false }
// myapp --advanced → Error: --threads and --cache-size required
// myapp --advanced --threads 4 --cache-size 100 → Success
This pattern is particularly useful for confirmation flags (--yes-i-am-sure) or mode switches that fundamentally change how your CLI behaves.
withDefault() Previously, withDefault() required the default value to match the parser's type exactly. Now it supports different types, creating union types that enable conditional CLI structures:
const conditionalParser = withDefault(
object({
server: flag("-s", "--server"),
port: option("-p", "--port", integer()),
host: option("-h", "--host", string())
}),
{ server: false as const }
);
// Result type is now a union:
// | { server: false }
// | { server: true, port: number, host: string }
This change makes it much easier to build CLIs where different flags enable different sets of options, without resorting to complex or() chains.
merge() combinator The merge() combinator now accepts any parser that produces object-like values. Previously limited to object() parsers, it now works with withDefault(), map(), and other transformative parsers:
const transformedConfig = map(
object({
host: option("--host", string()),
port: option("--port", integer())
}),
({ host, port }) => ({ endpoint: `${host}:${port}` })
);
const conditionalFeatures = withDefault(
object({
experimental: flag("--experimental"),
debugLevel: option("--debug-level", integer())
}),
{ experimental: false as const }
);
// Can now merge different parser types
const appConfig = merge(
transformedConfig, // map() result
conditionalFeatures, // withDefault() parser
object({ // traditional object()
verbose: option("-v", "--verbose")
})
);
This improvement came from recognizing that many parsers ultimately produce objects, and artificially restricting merge() to only object() parsers was limiting composition patterns.
longestMatch() The new longestMatch() combinator selects the parser that consumes the most input tokens. This enables sophisticated help systems where command --help shows help for that specific command rather than global help:
const normalParser = object({
help: constant(false),
command: or(
command("list", listOptions),
command("add", addOptions)
)
});
const contextualHelp = object({
help: constant(true),
commands: multiple(argument(string())),
helpFlag: flag("--help")
});
const cli = longestMatch(normalParser, contextualHelp);
// myapp --help → Shows global help
// myapp list --help → Shows help for 'list' command
// myapp add --help → Shows help for 'add' command
The run() functions in both @optique/core/facade and @optique/run now use this pattern automatically, so your CLI gets context-aware help without any additional configuration.
Both @optique/core/facade and @optique/run now support version display through --version flags and version commands. See the runners documentation for details:
// @optique/run - simple API
run(parser, {
version: "1.0.0", // Adds --version flag
help: "both"
});
// @optique/core/facade - detailed control
run(parser, "myapp", args, {
version: {
mode: "both", // --version flag AND version command
value: "1.0.0",
onShow: process.exit
}
});
The API follows the same pattern as help configuration, keeping things consistent and predictable.
The new output functions in @optique/run provide consistent terminal formatting with automatic capability detection. Learn more in the messages documentation:
import { print, printError, createPrinter } from "@optique/run";
import { message } from "@optique/core/message";
// Standard output with automatic formatting
print(message`Processing ${filename}...`);
// Error output to stderr with optional exit
printError(message`File ${filename} not found`, { exitCode: 1 });
// Custom printer for specific needs
const debugPrint = createPrinter({
stream: "stderr",
colors: true,
maxWidth: 80
});
debugPrint(message`Debug: ${details}`);
These functions automatically detect terminal capabilities and apply appropriate formatting, making your CLI output consistent across different environments.
While we've tried to maintain backward compatibility, there are a few changes to be aware of:
help option in @optique/run no longer accepts "none". Simply omit the option to disable help.getDocFragments() need to update their signature to use DocState<TState> instead of direct state values.object() parser now uses greedy parsing, attempting to consume all matching fields in one pass. This shouldn't affect most use cases but may change parsing order in complex scenarios.To upgrade to Optique 0.3.0, update both packages:
# Deno (JSR)
deno add @optique/core@^0.3.0 @optique/run@^0.3.0
# npm
npm update @optique/core @optique/run
# pnpm
pnpm update @optique/core @optique/run
# Yarn
yarn upgrade @optique/core @optique/run
# Bun
bun update @optique/core @optique/run
If you're only using the core package:
# Deno (JSR)
deno add @optique/core@^0.3.0
# npm
npm update @optique/core
These improvements came from real-world usage and community feedback. We're particularly interested in hearing how the new dependent options patterns work for your use cases, and whether the context-aware help system meets your needs.
As always, you can find complete documentation at optique.dev and file issues or suggestions on GitHub.

@hongminhee@hollo.social
#Optique 0.3.0 is out with dependent options and flexible parser composition, shaped by feedback from @z9mb1's work migrating @fedify CLI from Cliffy to Optique.
@hongminhee@hackers.pub
We're releasing Optique 0.3.0 with several improvements that make building complex CLI applications more straightforward. This release focuses on expanding parser flexibility and improving the help system based on feedback from the community, particularly from the Fedify project's migration from Cliffy to Optique. Special thanks to @z9mb1 for her invaluable insights during this process.
flag() parser for dependent options patternswithDefault() supporting union types for conditional CLI structuresor() capacity now supporting up to 10 parsers (previously 5)merge() combinator that works with any object-producing parser, not just object()longestMatch() combinator@optique/core and @optique/runflag() The new flag() parser creates Boolean flags that must be explicitly provided. While option() defaults to false when absent, flag() fails parsing entirely if not present. This subtle difference enables cleaner patterns for dependent options.
Consider a scenario where certain options only make sense when a mode is explicitly enabled:
import { flag, object, option, withDefault } from "@optique/core/parser";
import { integer } from "@optique/core/valueparser";
// Without the --advanced flag, these options aren't available
const parser = withDefault(
object({
advanced: flag("--advanced"),
maxThreads: option("--threads", integer()),
cacheSize: option("--cache-size", integer())
}),
{ advanced: false as const }
);
// Usage:
// myapp → { advanced: false }
// myapp --advanced → Error: --threads and --cache-size required
// myapp --advanced --threads 4 --cache-size 100 → Success
This pattern is particularly useful for confirmation flags (--yes-i-am-sure) or mode switches that fundamentally change how your CLI behaves.
withDefault() Previously, withDefault() required the default value to match the parser's type exactly. Now it supports different types, creating union types that enable conditional CLI structures:
const conditionalParser = withDefault(
object({
server: flag("-s", "--server"),
port: option("-p", "--port", integer()),
host: option("-h", "--host", string())
}),
{ server: false as const }
);
// Result type is now a union:
// | { server: false }
// | { server: true, port: number, host: string }
This change makes it much easier to build CLIs where different flags enable different sets of options, without resorting to complex or() chains.
merge() combinator The merge() combinator now accepts any parser that produces object-like values. Previously limited to object() parsers, it now works with withDefault(), map(), and other transformative parsers:
const transformedConfig = map(
object({
host: option("--host", string()),
port: option("--port", integer())
}),
({ host, port }) => ({ endpoint: `${host}:${port}` })
);
const conditionalFeatures = withDefault(
object({
experimental: flag("--experimental"),
debugLevel: option("--debug-level", integer())
}),
{ experimental: false as const }
);
// Can now merge different parser types
const appConfig = merge(
transformedConfig, // map() result
conditionalFeatures, // withDefault() parser
object({ // traditional object()
verbose: option("-v", "--verbose")
})
);
This improvement came from recognizing that many parsers ultimately produce objects, and artificially restricting merge() to only object() parsers was limiting composition patterns.
longestMatch() The new longestMatch() combinator selects the parser that consumes the most input tokens. This enables sophisticated help systems where command --help shows help for that specific command rather than global help:
const normalParser = object({
help: constant(false),
command: or(
command("list", listOptions),
command("add", addOptions)
)
});
const contextualHelp = object({
help: constant(true),
commands: multiple(argument(string())),
helpFlag: flag("--help")
});
const cli = longestMatch(normalParser, contextualHelp);
// myapp --help → Shows global help
// myapp list --help → Shows help for 'list' command
// myapp add --help → Shows help for 'add' command
The run() functions in both @optique/core/facade and @optique/run now use this pattern automatically, so your CLI gets context-aware help without any additional configuration.
Both @optique/core/facade and @optique/run now support version display through --version flags and version commands. See the runners documentation for details:
// @optique/run - simple API
run(parser, {
version: "1.0.0", // Adds --version flag
help: "both"
});
// @optique/core/facade - detailed control
run(parser, "myapp", args, {
version: {
mode: "both", // --version flag AND version command
value: "1.0.0",
onShow: process.exit
}
});
The API follows the same pattern as help configuration, keeping things consistent and predictable.
The new output functions in @optique/run provide consistent terminal formatting with automatic capability detection. Learn more in the messages documentation:
import { print, printError, createPrinter } from "@optique/run";
import { message } from "@optique/core/message";
// Standard output with automatic formatting
print(message`Processing ${filename}...`);
// Error output to stderr with optional exit
printError(message`File ${filename} not found`, { exitCode: 1 });
// Custom printer for specific needs
const debugPrint = createPrinter({
stream: "stderr",
colors: true,
maxWidth: 80
});
debugPrint(message`Debug: ${details}`);
These functions automatically detect terminal capabilities and apply appropriate formatting, making your CLI output consistent across different environments.
While we've tried to maintain backward compatibility, there are a few changes to be aware of:
help option in @optique/run no longer accepts "none". Simply omit the option to disable help.getDocFragments() need to update their signature to use DocState<TState> instead of direct state values.object() parser now uses greedy parsing, attempting to consume all matching fields in one pass. This shouldn't affect most use cases but may change parsing order in complex scenarios.To upgrade to Optique 0.3.0, update both packages:
# Deno (JSR)
deno add @optique/core@^0.3.0 @optique/run@^0.3.0
# npm
npm update @optique/core @optique/run
# pnpm
pnpm update @optique/core @optique/run
# Yarn
yarn upgrade @optique/core @optique/run
# Bun
bun update @optique/core @optique/run
If you're only using the core package:
# Deno (JSR)
deno add @optique/core@^0.3.0
# npm
npm update @optique/core
These improvements came from real-world usage and community feedback. We're particularly interested in hearing how the new dependent options patterns work for your use cases, and whether the context-aware help system meets your needs.
As always, you can find complete documentation at optique.dev and file issues or suggestions on GitHub.

@hongminhee@hollo.social
#Optique 0.3.0 is out with dependent options and flexible parser composition, shaped by feedback from @z9mb1's work migrating @fedify CLI from Cliffy to Optique.
@hongminhee@hackers.pub
We're releasing Optique 0.3.0 with several improvements that make building complex CLI applications more straightforward. This release focuses on expanding parser flexibility and improving the help system based on feedback from the community, particularly from the Fedify project's migration from Cliffy to Optique. Special thanks to @z9mb1 for her invaluable insights during this process.
flag() parser for dependent options patternswithDefault() supporting union types for conditional CLI structuresor() capacity now supporting up to 10 parsers (previously 5)merge() combinator that works with any object-producing parser, not just object()longestMatch() combinator@optique/core and @optique/runflag() The new flag() parser creates Boolean flags that must be explicitly provided. While option() defaults to false when absent, flag() fails parsing entirely if not present. This subtle difference enables cleaner patterns for dependent options.
Consider a scenario where certain options only make sense when a mode is explicitly enabled:
import { flag, object, option, withDefault } from "@optique/core/parser";
import { integer } from "@optique/core/valueparser";
// Without the --advanced flag, these options aren't available
const parser = withDefault(
object({
advanced: flag("--advanced"),
maxThreads: option("--threads", integer()),
cacheSize: option("--cache-size", integer())
}),
{ advanced: false as const }
);
// Usage:
// myapp → { advanced: false }
// myapp --advanced → Error: --threads and --cache-size required
// myapp --advanced --threads 4 --cache-size 100 → Success
This pattern is particularly useful for confirmation flags (--yes-i-am-sure) or mode switches that fundamentally change how your CLI behaves.
withDefault() Previously, withDefault() required the default value to match the parser's type exactly. Now it supports different types, creating union types that enable conditional CLI structures:
const conditionalParser = withDefault(
object({
server: flag("-s", "--server"),
port: option("-p", "--port", integer()),
host: option("-h", "--host", string())
}),
{ server: false as const }
);
// Result type is now a union:
// | { server: false }
// | { server: true, port: number, host: string }
This change makes it much easier to build CLIs where different flags enable different sets of options, without resorting to complex or() chains.
merge() combinator The merge() combinator now accepts any parser that produces object-like values. Previously limited to object() parsers, it now works with withDefault(), map(), and other transformative parsers:
const transformedConfig = map(
object({
host: option("--host", string()),
port: option("--port", integer())
}),
({ host, port }) => ({ endpoint: `${host}:${port}` })
);
const conditionalFeatures = withDefault(
object({
experimental: flag("--experimental"),
debugLevel: option("--debug-level", integer())
}),
{ experimental: false as const }
);
// Can now merge different parser types
const appConfig = merge(
transformedConfig, // map() result
conditionalFeatures, // withDefault() parser
object({ // traditional object()
verbose: option("-v", "--verbose")
})
);
This improvement came from recognizing that many parsers ultimately produce objects, and artificially restricting merge() to only object() parsers was limiting composition patterns.
longestMatch() The new longestMatch() combinator selects the parser that consumes the most input tokens. This enables sophisticated help systems where command --help shows help for that specific command rather than global help:
const normalParser = object({
help: constant(false),
command: or(
command("list", listOptions),
command("add", addOptions)
)
});
const contextualHelp = object({
help: constant(true),
commands: multiple(argument(string())),
helpFlag: flag("--help")
});
const cli = longestMatch(normalParser, contextualHelp);
// myapp --help → Shows global help
// myapp list --help → Shows help for 'list' command
// myapp add --help → Shows help for 'add' command
The run() functions in both @optique/core/facade and @optique/run now use this pattern automatically, so your CLI gets context-aware help without any additional configuration.
Both @optique/core/facade and @optique/run now support version display through --version flags and version commands. See the runners documentation for details:
// @optique/run - simple API
run(parser, {
version: "1.0.0", // Adds --version flag
help: "both"
});
// @optique/core/facade - detailed control
run(parser, "myapp", args, {
version: {
mode: "both", // --version flag AND version command
value: "1.0.0",
onShow: process.exit
}
});
The API follows the same pattern as help configuration, keeping things consistent and predictable.
The new output functions in @optique/run provide consistent terminal formatting with automatic capability detection. Learn more in the messages documentation:
import { print, printError, createPrinter } from "@optique/run";
import { message } from "@optique/core/message";
// Standard output with automatic formatting
print(message`Processing ${filename}...`);
// Error output to stderr with optional exit
printError(message`File ${filename} not found`, { exitCode: 1 });
// Custom printer for specific needs
const debugPrint = createPrinter({
stream: "stderr",
colors: true,
maxWidth: 80
});
debugPrint(message`Debug: ${details}`);
These functions automatically detect terminal capabilities and apply appropriate formatting, making your CLI output consistent across different environments.
While we've tried to maintain backward compatibility, there are a few changes to be aware of:
help option in @optique/run no longer accepts "none". Simply omit the option to disable help.getDocFragments() need to update their signature to use DocState<TState> instead of direct state values.object() parser now uses greedy parsing, attempting to consume all matching fields in one pass. This shouldn't affect most use cases but may change parsing order in complex scenarios.To upgrade to Optique 0.3.0, update both packages:
# Deno (JSR)
deno add @optique/core@^0.3.0 @optique/run@^0.3.0
# npm
npm update @optique/core @optique/run
# pnpm
pnpm update @optique/core @optique/run
# Yarn
yarn upgrade @optique/core @optique/run
# Bun
bun update @optique/core @optique/run
If you're only using the core package:
# Deno (JSR)
deno add @optique/core@^0.3.0
# npm
npm update @optique/core
These improvements came from real-world usage and community feedback. We're particularly interested in hearing how the new dependent options patterns work for your use cases, and whether the context-aware help system meets your needs.
As always, you can find complete documentation at optique.dev and file issues or suggestions on GitHub.