Skip to content

Conversation

@indraneel12
Copy link
Contributor

@indraneel12 indraneel12 commented Oct 7, 2025

Resolves #70

Explained by the following Code Demo:

import 'package:cli_tools/cli_tools.dart' show BetterCommandRunner;

import 'package:config/config.dart'
    show FlagOption, OptionDefinition, OptionGroup;

FlagOption buildMockOption(
  final String argName,
  final String? groupName, {
  final bool hide = false,
}) => FlagOption(
  argName: argName,
  hide: hide,
  group: groupName != null ? OptionGroup(groupName) : null,
  helpText: 'Help section for $argName.',
);

void main() {
  print(
    BetterCommandRunner(
      'mock',
      'Mock Command to demonstrate the Group Name feature.',
      globalOptions: <OptionDefinition>[
        buildMockOption('option-1', null),
        buildMockOption('option-2', 'Group 1'),
        buildMockOption('option-3', 'Group 2'),
        buildMockOption('option-4', 'Group 1'),
        buildMockOption('option-5', null),
        buildMockOption('option-6', 'Group 2'),
        buildMockOption('option-7', 'Group 3', hide: true),
        buildMockOption('option-8', 'Group 4', hide: true),
        buildMockOption('option-9', 'Group 4', hide: true),
        buildMockOption('option-10', 'Group 5', hide: true),
        buildMockOption('option-11', 'Group 5'),
      ],
    ).usage,
  );
}

Buggy Output (old behavior; all Group Names vanish):

Mock Command to demonstrate the Group Name feature.

Usage: mock <command> [arguments]

Global options:
-h, --help              Print this usage information.
    --[no-]option-1     Help section for option-1.
    --[no-]option-2     Help section for option-2.
    --[no-]option-3     Help section for option-3.
    --[no-]option-4     Help section for option-4.
    --[no-]option-5     Help section for option-5.
    --[no-]option-6     Help section for option-6.
    --[no-]option-11    Help section for option-11.

Available commands:
  help   Display help information for mock.

Run "mock help <command>" for more information about a command.

Expected Output (new behavior; all non-hidden Group Names appear in original order):

Mock Command to demonstrate the Group Name feature.

Usage: mock <command> [arguments]

Global options:
-h, --help              Print this usage information.
    --[no-]option-1     Help section for option-1.
    --[no-]option-5     Help section for option-5.

Group 1
    --[no-]option-2     Help section for option-2.
    --[no-]option-4     Help section for option-4.

Group 2
    --[no-]option-3     Help section for option-3.
    --[no-]option-6     Help section for option-6.

Group 5
    --[no-]option-11    Help section for option-11.

Available commands:
  help   Display help information for mock.

Run "mock help <command>" for more information about a command.

Features

  • all Group Names (of Visible Groups) are now visible in usage
  • all Hidden Groups (i.e. not composed of any Visible Options) are not shown as Separators
  • all Groupless Options are shown first (without any explicit addition of Separator)
  • relative order of all Options within a Group is preserved
  • relative order of all Groups is preserved

Summary by CodeRabbit

  • New Features

    • CLI help/usage now groups options under visible headers with preserved ordering; groupless options appear first and hidden groups are omitted. Group headers include padding for readability and separators appear once.
  • Documentation

    • Clarified semantics for grouping behavior and header/separator presentation in option descriptions.
  • Tests

    • Added comprehensive tests covering grouped vs. groupless options, header padding, hidden groups, ordering, visibility, and separator counts.

Features:

* all Non-Blank (based on full whitespace) Group Names are shown as-is in Usage

* all Blank Group Names get a count-based default Group Name within Usage

* all Groupless Options are shown before Grouped Options

* relative order of all Options within a Group is preserved

* relative order of all Groups is preserved
Objective:

* All Group Names are properly padded with newlines in the presence of Groupless Options

* All Group Names are properly padded with newlines in the absence of Groupless Options

* Blank Group Names are not rendered as-is in the presence of Groupless Options

* Blank Group Names are not rendered as-is in the absence of Groupless Options

* Non-Blank Group Names are rendered as-is in the presence of Groupless Options

* Non-Blank Group Names are rendered as-is in the absence of Groupless Options

* Blank Group Names get a count-based default Group Name in the presence of Groupless Options

* Blank Group Names get a count-based default Group Name in the absence of Groupless Options

* All Groupless Options are shown before Grouped Options

* Relative order of all Options within a Group is preserved

* Relative order of all Groups is preserved
@coderabbitai
Copy link

coderabbitai bot commented Oct 7, 2025

📝 Walkthrough

Walkthrough

Adds support for grouping options when adding them to an ArgParser (groupless options emitted first, groups preserved with separators and fallback names for blank headers), enables grouping by default for prepareOptionsForParsing, preserves prior runtime behavior for single-option insertion, and adds tests validating usage text rendering.

Changes

Cohort / File(s) Summary of changes
CLI usage text tests
packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart
New test suite validating BetterCommandRunner usage rendering for option groups: visible/invisible groups, newline padding, group separators count, groupless vs grouped ordering, blank-group fallback names, and preservation of option/group relative order.
Config: options helpers
packages/config/lib/src/config/options.dart
addOptionsToParser signature extended to accept {bool addGroupSeparators = false}; when true partitions options into groupless and ordered groups (preserves insertion order), emits groupless options first, inserts separators for groups with visible options (blank names mapped to fallback names), and emits each group's options in original order. prepareOptionsForParsing now calls with addGroupSeparators: true.
Config: dynamic insertion call site
packages/config/lib/src/config/config_parser.dart
Call site for dynamic single-option insertion updated to call addOptionsToParser(..., addGroupSeparators: false) to preserve previous runtime behavior (no group separators on incremental adds).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Runner as BetterCommandRunner
  participant Options as addOptionsToParser
  participant Arg as ArgParser
  participant Usage as UsageFormatter

  Runner->>Options: addOptionsToParser(opts, argParser, addGroupSeparators: true)
  Note right of Options: Phase 1 — Partition\n• groupless (preserve order)\n• ordered groups (preserve order)

  rect rgba(220,245,255,0.35)
    Note right of Options: Phase 2 — Emit groupless (original order)
    loop each groupless option
      Options->>Arg: addOption(option)
    end
  end

  rect rgba(235,245,220,0.35)
    Note right of Options: Phase 3 — Emit groups (original group order)
    loop each group (visible only)
      Options->>Arg: addSeparator(header) /* header uses fallback if blank */
      loop each option in group
        Options->>Arg: addOption(option)
      end
    end
  end

  Runner->>Arg: request usage()
  Arg-->>Usage: formatted usage text
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title “feat: Support for Option Group Name” succinctly highlights the primary enhancement of propagating option group names into CLI usage output and avoids extraneous details, making the main change clear to reviewers.
Linked Issues Check ✅ Passed This PR directly implements the key objectives of issue #70 by rendering non-empty OptionGroup.name headers in usage output, listing groupless options first, preserving group and option ordering, and omitting hidden groups; the fallback naming and extensibility suggestions from the issue were advisory and not mandatory.
Out of Scope Changes Check ✅ Passed All code modifications and added tests are focused exclusively on supporting and validating option group name behavior in the config and CLI tools packages, with no unrelated or extraneous changes introduced.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a4bbae and 0c947b2.

📒 Files selected for processing (2)
  • packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart (1 hunks)
  • packages/config/lib/src/config/options.dart (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: indraneel12
PR: serverpod/cli_tools#0
File: :0-0
Timestamp: 2025-10-10T08:50:09.885Z
Learning: PR #71 in serverpod/cli_tools adds support for displaying OptionGroup names in CLI usage output. The implementation does NOT include fallback group names or numbered defaults for anonymous groups - it only displays the group.name when present and shows groupless options first without any header.
🔇 Additional comments (4)
packages/config/lib/src/config/options.dart (4)

44-45: LGTM! Clear documentation for group name usage.

The updated documentation clearly states that the group name will be used as a header in usage information and provides helpful formatting guidance.


725-725: LGTM! Grouping enabled for standard usage.

Enabling addGroupSeparators: true by default in prepareOptionsForParsing ensures that group names appear in usage output, which is the core objective of this PR.


803-820: LGTM! Well-documented API with backward compatibility.

The documentation clearly explains the grouping behavior and the default false value ensures existing code paths remain unaffected. The explicit note about order preservation is particularly helpful.


821-866: LGTM! Solid implementation with correct grouping logic.

The implementation correctly handles all key requirements:

  1. Backward compatibility: Early return when addGroupSeparators is false preserves existing behavior
  2. Order preservation: Both Map and List maintain insertion order in Dart, ensuring groupless options, groups, and options within groups appear in their original order
  3. Hidden group handling: Groups with only hidden options are omitted (Line 858 correctly checks for at least one visible option before adding the separator)
  4. No fallback names: Only group.name is used when present, as confirmed in the learnings

The logic is clean, efficient (O(n) complexity), and well-commented.

Based on learnings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/config/lib/src/config/options.dart (1)

822-831: Consider extracting helper functions for better testability.

The inline helper functions work well, but extracting them to module-level or a separate utility would improve testability and reusability. Additionally, consider using more idiomatic Dart naming (numberOfOptionGroups instead of nOptionGroups).

If refactoring, you could extract to:

bool _isNotBlank(String name) => name.trim().isNotEmpty;

String _buildFallbackGroupName(int groupCounter, int totalGroups) =>
    'Option Group${totalGroups > 1 ? " $groupCounter" : ""}';
packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart (1)

8-21: Consider more idiomatic Dart naming.

The variable nBlankNameExamples uses Hungarian notation (n prefix for count), which is not idiomatic Dart. Consider renaming to blankNameExampleCount or simply computing it inline where needed.

-  final nBlankNameExamples = blankNameExamples.length;
+  final blankNameExampleCount = blankNameExamples.length;

Then update usages:

-              for (var i = 0; i < nBlankNameExamples; ++i)
+              for (var i = 0; i < blankNameExampleCount; ++i)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 974bcdc and 31a938e.

📒 Files selected for processing (2)
  • packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart (1 hunks)
  • packages/config/lib/src/config/options.dart (1 hunks)
🔇 Additional comments (7)
packages/config/lib/src/config/options.dart (2)

807-821: LGTM! Clean separation of groupless and grouped options.

The logic correctly categorizes options into groupless and grouped, maintaining insertion order. This sets up the two-phase processing approach (groupless first, then grouped with headers) as intended.


836-862: Group ordering is already tested
The existing test "Relative order of all Groups is preserved" in option_group_usage_text_test.dart verifies that groups appear in declaration order.

packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart (5)

1-36: LGTM! Well-structured test setup.

The helper functions (buildSeparatorView, buildMockArgName, etc.) make the tests highly readable and maintainable. The constants for blank name examples are comprehensive.


37-86: Tests validate the newline hack behavior.

These tests verify that group names are properly padded with newlines. However, they depend on the workaround implemented in addOptionsToParser (lines 842-853 of options.dart). When the upstream args package bug is fixed and the hack is removed, these tests will need to be updated or may fail.

This is by design per the implementation comments, but ensure there's a clear link between the hack removal and test updates.

Consider adding a comment in these test groups referencing the hack:

group('All Group Names are properly padded with newlines', () {
  // NOTE: These tests validate behavior that includes a workaround
  // for an args package padding bug. See options.dart lines 842-853.
  test(/* ... */);
});

190-255: Excellent coverage of fallback naming behavior.

These tests thoroughly validate the count-based default group names for blank groups, including the interaction with both groupless and grouped options. The use of stringContainsInOrder effectively verifies the ordering.


257-293: Critical test validating groupless-before-grouped ordering.

This test confirms the core requirement that groupless options appear before grouped ones. The assertion correctly uses stringContainsInOrder to verify the sequence.


295-357: Strong validation of relative ordering preservation.

Both tests (within-group and between-group) effectively validate that the implementation maintains the declaration order of options and groups. The use of counters to track expected ordering is clever and ensures correctness.

@indraneel12 indraneel12 changed the title added support for Option Group Name feat: added support for Option Group Name Oct 7, 2025
@indraneel12 indraneel12 changed the title feat: added support for Option Group Name feat: Support for Option Group Name Oct 7, 2025
To match the existing Code Style.
AND-condition Optimization.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/config/lib/src/config/options.dart (1)

813-817: Avoid repeated list copying when appending to group entries.

Each call to [...value, opt] clones the entire list, introducing avoidable allocations. Mutating the existing list in place keeps the order while being allocation-free.

   if (group != null) {
     optionGroups.update(
       group,
-      (final value) => [...value, opt],
+      (final value) => value..add(opt),
       ifAbsent: () => [opt],
     );
   } else {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aaa430e and c9ed4a4.

📒 Files selected for processing (1)
  • packages/config/lib/src/config/options.dart (1 hunks)

As suggested (nitpick) by the Review Bot on my Pull Request.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/config/lib/src/config/options.dart (1)

807-822: LGTM! Clean categorization of options into groups.

The two-phase collection approach (groupless options first, then grouped options) is well-structured and maintains proper ordering. The use of an ordered map for optionGroups and an ordered list for grouplessOptions correctly preserves insertion order.

Optional minor refactor: Line 822 could use optionGroups.length instead of optionGroups.keys.length for brevity, as they're equivalent:

-  final nOptionGroups = optionGroups.keys.length;
+  final nOptionGroups = optionGroups.length;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9ed4a4 and 6146a2e.

📒 Files selected for processing (1)
  • packages/config/lib/src/config/options.dart (1 hunks)
🔇 Additional comments (3)
packages/config/lib/src/config/options.dart (3)

824-830: LGTM! Well-designed helper functions.

The local helper functions enhance code readability and maintainability. The buildFallbackGroupName function is particularly well-thought-out—it adds a numeric suffix only when there are multiple groups, providing clear fallback names like "Option Group" or "Option Group 1", "Option Group 2", etc.


832-833: LGTM! Groupless options processed first.

This correctly ensures that options without a group appear before any grouped options in the usage text, which is the intended behavior.


835-862: LGTM! Proper group handling with fallback names.

The implementation correctly:

  1. Processes groups in their declared order
  2. Provides sensible fallback names for blank group names using buildFallbackGroupName
  3. Uses addSeparator to ensure proper visual separation between groups
  4. Maintains the relative order of options within each group

The use of isNotBlank(givenGroupName) at line 857 elegantly handles the case where a group name is blank or whitespace-only, falling back to the generated name. This addresses the PR objective of supporting option group names robustly.

Anyways, I reckon it's a good improvement lol
A flag to choose between the previous behavior of `addOptionsToParser` (default to not affect the existing Codebase) and the new implementation that handles Group Option Name logic.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/config/lib/src/config/options.dart (1)

832-838: Optional: Consider extracting defaultFallbackGroupName to file scope.

The defaultFallbackGroupName constant is currently defined inside the function (line 833), which means it gets re-declared on every invocation. While Dart's compiler may optimize this, extracting it as a private top-level constant would make the optimization explicit:

const _defaultFallbackGroupName = 'Option Group';

Then reference it in the function:

String buildFallbackGroupName(final int counter) =>
    '$_defaultFallbackGroupName${optionGroups.length > 1 ? " $counter" : ""}';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5561b7 and abba30e.

📒 Files selected for processing (2)
  • packages/config/lib/src/config/config_parser.dart (1 hunks)
  • packages/config/lib/src/config/options.dart (2 hunks)
🔇 Additional comments (5)
packages/config/lib/src/config/config_parser.dart (1)

213-213: LGTM: Correct use of addGroupSeparators: false for incremental option addition.

When options are added one at a time through _addOption, passing addGroupSeparators: false correctly prevents separator insertion between individual options. This preserves the intended single-pass behavior for dynamic option registration while allowing grouped separators only during the final two-phase parsing flow.

packages/config/lib/src/config/options.dart (4)

725-725: LGTM: Two-phase parsing enabled for option preparation.

The call to addOptionsToParser with addGroupSeparators: true correctly activates the two-phase grouping flow during option preparation. This ensures group separators are inserted at the appropriate points in the usage text while maintaining backward compatibility for other call sites.


803-814: LGTM: Clean design for backward-compatible grouping.

The optional addGroupSeparators parameter with a false default preserves existing behavior for callers that don't specify it, while the early-return path (lines 808-814) efficiently handles the non-grouped case without executing the grouping logic. This design cleanly separates the single-pass and two-phase flows.


816-841: LGTM: Well-structured grouping preparation.

The grouping logic correctly:

  • Uses ordered collections to preserve declaration order (lines 817-818)
  • Categorizes options by group membership (lines 819-830)
  • Defines clear helper functions for consistent processing (lines 832-838)
  • Adds groupless options first to ensure they appear before grouped options in usage text (lines 840-841)

The buildFallbackGroupName helper (lines 837-838) thoughtfully omits the counter suffix when there's only one group, improving UX.


843-870: LGTM: Proper group separator insertion and blank name handling.

The grouped option processing correctly:

  • Iterates through groups in declaration order (line 845)
  • Handles blank group names by falling back to generated names (lines 847-848, 864-866)
  • Inserts separators before each group's options (lines 863-866)
  • Adds all options within each group in order (lines 868-869)

The fallback logic at line 865 using isNotBlank ensures users always see meaningful group headers even when group names are empty or whitespace-only.

Note: The newline workaround (lines 850-861) was already discussed in previous review threads and is understood to be intentional technical debt tracked for removal when the upstream args package bug is resolved.

Addresses the Review Bot's nitpick comment on `defaultFallbackGroupName` in a better way.
I have raised [an Issue](dart-lang/core#917) for `package:args` and updated this Codebase with the expected behavior and adjusted (+refactored) the Test Cases accordingly.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/config/lib/src/config/options.dart (1)

803-858: Consider adding function documentation.

While the implementation is solid, consider adding documentation to addOptionsToParser to clarify:

  • The purpose and behavior of addGroupSeparators
  • When and why to use defaultFallbackGroupName
  • The ordering guarantees (groupless first, then groups in insertion order)

This would help future maintainers understand the function's contract without reading the implementation.

Example:

/// Adds options to the argument parser.
///
/// If [addGroupSeparators] is true, options are organized into groups:
/// - Groupless options are added first in their original order
/// - Grouped options follow, with each group preceded by a separator
/// - Groups appear in order of first encounter
/// - Blank group names use [defaultFallbackGroupName] with optional numbering
///
/// If [addGroupSeparators] is false, all options are added in their original order
/// without any grouping or separators.
void addOptionsToParser(
  final Iterable<OptionDefinition> argNameOpts,
  final ArgParser argParser, {
  final bool addGroupSeparators = false,
  final String defaultFallbackGroupName = 'Option Group',
}) {
  // ... implementation
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48e440b and 2eaea17.

📒 Files selected for processing (2)
  • packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart (1 hunks)
  • packages/config/lib/src/config/options.dart (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart
🔇 Additional comments (4)
packages/config/lib/src/config/options.dart (4)

725-725: LGTM! Group separators enabled in standard path.

Enabling addGroupSeparators: true in the standard preparation path is the correct choice for user-facing commands. The implementation maintains backward compatibility through the default parameter value in addOptionsToParser.


803-808: LGTM! Function signature is backward compatible.

The addition of optional named parameters with sensible defaults maintains backward compatibility while enabling the new grouping feature. The parameter names are clear and the defaults are appropriate.


809-815: LGTM! Clean backward compatibility path.

The early return when addGroupSeparators is false provides a clean separation between the original behavior and the new grouping logic, avoiding any performance overhead when grouping is not needed.


817-857: LGTM! Well-structured grouping implementation.

The grouping logic is clear and handles edge cases appropriately:

  • Partitioning phase (lines 817-831): Efficiently separates groupless and grouped options in a single pass
  • Helper functions (lines 833-838): Improve readability and maintain consistency
  • Execution flow (lines 840-857): Correctly processes groupless options first, then grouped options with separators

Edge cases handled correctly:

  • Blank group names fall back to numbered names
  • Single group gets "Option Group" without number
  • Multiple groups get "Option Group 1", "Option Group 2", etc.
  • Groups with identical names are merged (per OptionGroup.operator==)
  • Map insertion order ensures groups appear in order of first encounter

- more rigorous (e.g. newline precision and order)
- more readable (e.g. highly modular)
- easily enhanceable (e.g. via almost direct copy-paste for similar and additional Test Cases)
Copy link
Collaborator

@christerswahn christerswahn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work! And good that you minded supporting backwards compatibility for the drop-in replacement wrapper, and logged an issue to the upstream package!

A couple of comments, and one major point to change re not filling in group names.

@christerswahn
Copy link
Collaborator

In addition, please enter a couple of output examples in the top PR description, so the reader can see what the outcome of this change brings.

- entirely removed Fallback Group Name feature
- updated corresponding Test Cases with the actual expected behavior
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/config/lib/src/config/options.dart (3)

805-814: Clean backward-compatible implementation.

The optional parameter with a false default maintains existing behavior while enabling the new grouping functionality. The early return is a clean pattern that avoids nested conditionals.

Consider removing the comment on line 808 as the conditional and return statement clearly convey the intent. As per previous feedback, self-evident comments can be omitted.

-  // Plain Option-addition without any Group Logic
   if (!addGroupSeparators) {

817-818: Address previous feedback: verify map ordering and remove self-evident comment.

Two unresolved items from previous review:

  1. Map ordering (line 817): While Dart's default map literal {} produces a LinkedHashMap that maintains insertion order (since Dart 2.0+), this behavior should be explicitly verified or documented. Consider adding a brief note explaining that insertion order is guaranteed by Dart's map implementation.

  2. Self-evident comment (line 818): The comment // ordered list is self-evident since Dart lists are always ordered. Per previous feedback, this can be removed.

-  final optionGroups = <OptionGroup, List<OptionDefinition>>{}; // ordered map
+  // Dart map literals maintain insertion order (LinkedHashMap by default)
+  final optionGroups = <OptionGroup, List<OptionDefinition>>{};
-  final grouplessOptions = <OptionDefinition>[]; // ordered list
+  final grouplessOptions = <OptionDefinition>[];

832-843: Logic is correct; consider consolidating comments.

The implementation correctly adds groupless options first, then processes each group with its separator and options in order. Line 841 appropriately uses group.name directly without fallback, aligning with previous feedback that group names are mandatory.

The comments on lines 832, 836, and 839 somewhat restate what the code clearly expresses. Per previous feedback about omitting self-evident comments, consider consolidating into a single higher-level comment or removing them:

-  // Helpers for consistent processing and validation
   void addOne(final OptionDefinition x) => x.option._addToArgParser(argParser);
   void addAll(final List<OptionDefinition> options) => options.forEach(addOne);

-  // Add all Groupless Options first (in order)
   addAll(grouplessOptions);

-  // Add all Groups (in order) and their Options (in order)
+  // Process groups: add separator followed by grouped options
   optionGroups.forEach((final group, final options) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff9368e and 6e522d8.

📒 Files selected for processing (2)
  • packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart (1 hunks)
  • packages/config/lib/src/config/options.dart (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart
🔇 Additional comments (1)
packages/config/lib/src/config/options.dart (1)

725-725: LGTM! Grouped option rendering enabled.

The change to pass addGroupSeparators: true correctly enables the new grouped option functionality in the main flow, aligning with the PR objectives.

Explicit mention of Ordered Containers.
Ensures that the relative order of all Groups and Options is preserved.
Reflects the correct Option Group Name behavior.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/config/lib/src/config/options.dart (1)

803-846: LGTM! Well-structured implementation with good backwards compatibility.

The refactoring clearly separates the old behavior (simple iteration) from the new grouping logic. The use of ordered containers preserves option and group order as required, and the approach of adding groupless options first followed by grouped options with separators aligns with the PR objectives.

Minor nitpick: The comment on Line 834 mentions "validation" but the helper functions only perform processing (adding options to the parser). Actual validation occurs in validateOptions.

Consider this diff for accuracy:

-  // Helpers for consistent processing and validation
+  // Helpers for consistent processing
   void addOne(final OptionDefinition x) => x.option._addToArgParser(argParser);
   void addAll(final List<OptionDefinition> options) => options.forEach(addOne);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 173d6de and 293ac9e.

📒 Files selected for processing (1)
  • packages/config/lib/src/config/options.dart (3 hunks)
🔇 Additional comments (2)
packages/config/lib/src/config/options.dart (2)

44-45: LGTM!

The documentation update clearly explains the purpose of the group name in usage output and provides helpful formatting guidance.


725-725: Grouping change is backward-compatible for config usage
Grouping separators are only added when options have a non-null group (config options default to null), so the usage output and existing tests/documentation remain unchanged.

Also addresses a nitpick comment by the Review Bot.
Copy link
Collaborator

@christerswahn christerswahn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good stuff! Just one more minor comment.

- inlined small helper functions
- comments emphasize the intent behind modified implemention
- added external reference for explanation of Ordered Map in Dart
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/config/lib/src/config/options.dart (1)

816-821: Trim self-evident comment and external links.

This reiterates Dart’s ordered Map/List semantics; prior feedback requested omitting such comments.

-  // The following containers are ordered by default i.e.
-  // preserves insertion-order:
-  // - Map  : https://stackoverflow.com/q/79786585/10251345
-  // - List : https://api.dart.dev/dart-core/List-class.html
   final optionGroups = <OptionGroup, List<OptionDefinition>>{};
   final grouplessOptions = <OptionDefinition>[];
🧹 Nitpick comments (2)
packages/config/lib/src/config/options.dart (2)

803-808: Add a brief doc comment for the new flag.

Document that addGroupSeparators only affects usage output (adds ArgParser separators) and does not alter parsing. Minor API polish.

-void addOptionsToParser(
+/// Adds named options to [argParser]. If [addGroupSeparators] is true,
+/// inserts separators using each option's [OptionGroup.name] before the
+/// group's options in the generated usage text. Parsing behavior is unchanged.
+void addOptionsToParser(
   final Iterable<OptionDefinition> argNameOpts,
   final ArgParser argParser, {
   final bool addGroupSeparators = false,
 })

842-848: Avoid empty headers for groups with only hidden options.

If a group’s options are all hidden, the header appears with no visible entries. Keep adding the options (so they parse), but skip the separator for such groups.

-  optionGroups.forEach((final group, final groupedOptions) {
-    argParser.addSeparator(group.name);
-    for (final o in groupedOptions) {
-      o.option._addToArgParser(argParser);
-    }
-  });
+  optionGroups.forEach((final group, final groupedOptions) {
+    final hasVisible = groupedOptions.any((o) => !o.option.hide);
+    if (hasVisible) {
+      argParser.addSeparator(group.name);
+    }
+    for (final o in groupedOptions) {
+      o.option._addToArgParser(argParser);
+    }
+  });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67ec08d and f08dc50.

📒 Files selected for processing (1)
  • packages/config/lib/src/config/options.dart (3 hunks)
🔇 Additional comments (2)
packages/config/lib/src/config/options.dart (2)

44-47: Doc clarification looks good.

Clear that name is used as the usage header; no issues.


719-726: Confirm default grouping in prepareOptionsForParsing callers.
prepareOptionsForParsing now passes addGroupSeparators=true by default. It’s invoked in:

  • config/lib/src/config/options.dart (prepareForParsing)
  • cli_tools/lib/src/better_command_runner/better_command_runner.dart:177
  • cli_tools/lib/src/better_command_runner/better_command.dart:67
    Ensure grouped usage headers are desired in all these contexts.

- for Groups with only Hidden Options
- minor Comment improvements
- Test Cases for the recent fix (bug caught by the Review Bot)
- minor Comment improvement
Combination of:
- Groupless Options
- Grouped Options
- Hidden Groups
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (7)
packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart (7)

83-129: Also assert each visible group header appears exactly once

You verify presence/absence, but not duplication. Add an occurrence check per visible group to catch regressions where headers could repeat.

Apply within each test by asserting a single match for a couple of groups:

-        expect(
-          buildRunner(grouplessOptions + groupedOptions + hiddenGroups).usage,
-          expectation,
-        );
+        final usage =
+            buildRunner(grouplessOptions + groupedOptions + hiddenGroups).usage;
+        expect(usage, expectation);
+        // Ensure visible group headers are not duplicated.
+        expect(RegExp(RegExp.escape(buildMockGroupName(1)))
+            .allMatches(usage)
+            .length, equals(1));
+        expect(RegExp(RegExp.escape(buildMockGroupName(5)))
+            .allMatches(usage)
+            .length, equals(1));

Repeat similarly in the “absence of Groupless Options” test if desired.


131-161: Relax newline padding assertion to avoid brittle failures (CRLF/format tweaks)

Matching exactly “two newlines before + one after” is brittle. Prefer “one or more before + one after.”

-  final expectation = allOf([
-    for (var i = 5; i < 10; ++i)
-      contains(buildSeparatorView(buildMockGroupName(i))),
-  ]);
+  final expectation = allOf([
+    for (var i = 5; i < 10; ++i)
+      // Allow one or more newlines before the header; require one after.
+      contains(RegExp(r'\n+' + RegExp.escape(buildMockGroupName(i)) + r'\n')),
+  ]);

163-191: Make “groupless before grouped” assertion line-ending agnostic and less formatting-sensitive

Relying on explicit newline tokens can be fragile across platforms or minor formatting changes. Compare indices instead.

-  final expectation = stringContainsInOrder([
-    '\n',
-    for (var i = 5; i < 10; ++i) ...[
-      buildMockArgName(i),
-      '\n',
-    ],
-    '\n',
-    for (var i = 0; i < 5; ++i) ...[
-      buildMockArgName(i),
-      '\n',
-    ],
-    '\n',
-  ]);
-  expect(
-    buildRunner(groupedOptions + grouplessOptions).usage,
-    expectation,
-  );
+  final usage = buildRunner(groupedOptions + grouplessOptions)
+      .usage
+      .replaceAll('\r\n', '\n');
+  final lastGrouplessIdx =
+      usage.lastIndexOf(buildMockArgName(9)); // last of 5..9
+  final firstGroupedIdx =
+      usage.indexOf(buildMockArgName(0)); // first of 0..4
+  expect(firstGroupedIdx, greaterThan(lastGrouplessIdx));

193-229: Reuse the same OptionGroup instances to avoid relying on name-based grouping semantics

Currently each option calls buildMockGroup(i), creating new objects; if grouping ever used identity, this test wouldn’t validate intra-group ordering. Reuse group instances per i.

   final groupedOptions = <OptionDefinition>[
-    for (var i = 0; i < 3; ++i)
+    // Reuse the same OptionGroup instance per group.
+    for (var i = 0; i < 3; ++i)
+      for (var j = 0; j < 0; ++j) {} // no-op to preserve diff context
+  ];
+  final groups = [buildMockGroup(0), buildMockGroup(1), buildMockGroup(2)];
+  groupedOptions.addAll([
     for (var j = 0; j < 5; ++j)
       buildMockOption(
         buildMockArgName(++testOptionCount),
-        buildMockGroup(i),
+        groups[0],
       ),
-  ];
+    for (var j = 0; j < 5; ++j)
+      buildMockOption(
+        buildMockArgName(++testOptionCount),
+        groups[1],
+      ),
+    for (var j = 0; j < 5; ++j)
+      buildMockOption(
+        buildMockArgName(++testOptionCount),
+        groups[2],
+      ),
+  ]);

Note: Any equivalent approach that reuses group instances per i works.


231-258: LGTM; order of group headers is validated

Confirms visible group headers appear in declared order. Consider also asserting no duplicate headers, similar to earlier comment, but optional.


260-349: Strengthen combined test by asserting single header occurrence for visible groups

You already assert absence for fully hidden groups. Also ensure headers like “Group 1/2/5” appear exactly once.

-      expect(
-        buildRunner(const <OptionDefinition>[ ... ]).usage,
-        allOf([
+      final usage = buildRunner(const <OptionDefinition>[ ... ]).usage;
+      expect(
+        usage,
+        allOf([
           stringContainsInOrder([
             'option-1',
             'option-5',
             'Group 1',
             'option-2',
             'option-4',
             'Group 2',
             'option-3',
             'option-6',
             'Group 5',
             'option-11',
           ]),
           isNot(contains('Group 3')),
           isNot(contains('option-7')),
           isNot(contains('Group 4')),
           isNot(contains('option-8')),
           isNot(contains('option-9')),
           isNot(contains('option-10')),
         ]),
       );
+      // Ensure visible group headers appear only once.
+      expect(RegExp(r'\bGroup 1\b').allMatches(usage).length, equals(1));
+      expect(RegExp(r'\bGroup 2\b').allMatches(usage).length, equals(1));
+      expect(RegExp(r'\bGroup 5\b').allMatches(usage).length, equals(1));

1-349: Add tests for anonymous/blank group names and whitespace-only names

PR objective mentions default naming for anonymous groups; this suite doesn’t cover it. Add scenarios for:

  • Multiple OptionGroup('') groups: headers get distinct fallback names in order.
  • OptionGroup(' '): trimmed treated as anonymous; same fallback semantics.

Would you like me to open a follow-up PR with tests? Minimal example to append at end of file:

+  test(
+    'Anonymous/blank group names get fallback headers in order',
+    () {
+      final options = <OptionDefinition>[
+        buildMockOption('groupless-a'),
+        // Two anonymous groups with visible options.
+        FlagOption(argName: 'anon-1a', group: const OptionGroup(''), helpText: 'h'),
+        FlagOption(argName: 'anon-1b', group: const OptionGroup(''), helpText: 'h'),
+        FlagOption(argName: 'anon-2a', group: const OptionGroup(''), helpText: 'h'),
+        // Whitespace-only should be treated as anonymous too.
+        FlagOption(argName: 'anon-ws-a', group: const OptionGroup('   '), helpText: 'h'),
+      ];
+      final usage = buildRunner(options).usage.replaceAll('\r\n', '\n');
+      // Expect at least two non-empty group headers after groupless options.
+      final headerMatches = RegExp(r'\n+\s*(.+\S)\s*\n').allMatches(usage).toList();
+      expect(headerMatches.length, greaterThanOrEqualTo(2));
+      // The two first fallback headers should be distinct and ordered.
+      final first = headerMatches[0].group(1)!;
+      final second = headerMatches[1].group(1)!;
+      expect(first, isNotEmpty);
+      expect(second, isNotEmpty);
+      expect(first, isNot(equals(second)));
+      // Ensure anonymous options appear under/after their respective headers (order check).
+      expect(usage.indexOf('anon-1a'), lessThan(usage.indexOf('anon-2a')));
+    },
+  );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78dc901 and 98cd918.

📒 Files selected for processing (1)
  • packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart (1 hunks)
🔇 Additional comments (1)
packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart (1)

52-81: Good coverage for visible group headers

Validates that named, visible groups render in usage both with and without groupless options. Looks solid.

- more compact Helper Functions
- better Call Site readability
@indraneel12
Copy link
Contributor Author

indraneel12 commented Oct 10, 2025

@coderabbitai I've been sadly noticing that you have a bug (visible through your comments and edits to PR Summary since yesterday). Please update your Context Window to recognize the fact that Fallback Group Name feature is no longer part of this Pull Request, as required above by a Package Admin.

@coderabbitai
Copy link

coderabbitai bot commented Oct 10, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the actual implementation of addOptionsToParser to see what features are present
cat packages/config/lib/src/config/options.dart

Length of output: 25231


@indraneel12, you're absolutely right, and I apologize for the confusion. I've reviewed the actual implementation in packages/config/lib/src/config/options.dart and can confirm that the Fallback Group Name feature (numbered default names for anonymous groups) is NOT part of this PR.

The actual implementation is straightforward:

  • Groupless options are added first without any group header
  • Options with an OptionGroup display that group's name as a header using addSeparator(group.name)
  • There are no fallback names or numbered defaults

I was incorrectly referencing the workaround described in issue #70, which mentioned assigning numbered default names, but that approach was not implemented in this PR. Thank you for catching this error and giving me the opportunity to correct my understanding.


✏️ Learnings added
Learnt from: indraneel12
URL: https://github.com/serverpod/cli_tools/pull/71

Timestamp: 2025-10-10T08:50:09.885Z
Learning: PR #71 in serverpod/cli_tools adds support for displaying OptionGroup names in CLI usage output. The implementation does NOT include fallback group names or numbered defaults for anonymous groups - it only displays the group.name when present and shows groupless options first without any header.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart (4)

119-149: Avoid brittle assertions on exact newline padding

Tests require exactly two preceding newlines for headers. Minor formatting tweaks could break them without changing semantics. Prefer a regex that allows “2 or more” newlines.

Example rewrite for expectation:

final expectation = allOf([
  for (var i = 5; i < 10; ++i)
    matches(RegExp(r'\n{2,}${RegExp.escape(buildMockGroupName(i))}\n')),
]);

151-179: Make ordering assertion less fragile (remove newline tokens)

The order test currently depends on explicit newline placement. Assert semantic order: all groupless args come before the first group header.

Apply this diff:

-      final expectation = stringContainsInOrder([
-        '\n',
-        for (var i = 5; i < 10; ++i) ...[
-          buildMockArgName(i),
-          '\n',
-        ],
-        '\n',
-        for (var i = 0; i < 5; ++i) ...[
-          buildMockArgName(i),
-          '\n',
-        ],
-        '\n',
-      ]);
-      expect(
-        buildRunner(groupedOptions + grouplessOptions).usage,
-        expectation,
-      );
+      final usage = buildRunner(groupedOptions + grouplessOptions).usage;
+      expect(
+        usage,
+        stringContainsInOrder([
+          for (var i = 5; i < 10; ++i) buildMockArgName(i),
+          // First visible group header should appear after all groupless options.
+          buildMockGroupName(0),
+        ]),
+      );

219-246: Add a test for non-contiguous options of the same group (header appears once, options consolidate)

You verify group-order preservation, but here each option gets a new group. Add a case where the same group appears in disjoint positions to ensure a single header consolidates its options in order.

Example:

test('Group appears once and consolidates non-contiguous options', () {
  final options = <OptionDefinition>[
    buildMockOption('a', 'G'),
    buildMockOption('b', null),
    buildMockOption('c', 'G'),
  ];
  final usage = buildRunner(options).usage;
  expect(usage, contains('G')); // header present
  // Header appears exactly once.
  expect(RegExp(r'^\s*G\s*$', multiLine: true).allMatches(usage).length, equals(1));
  expect(usage, stringContainsInOrder(['a', 'c']));
});

1-3: Missing test for anonymous/blank group names (fallback naming)

PR notes numbered defaults for anonymous groups. Add a test where OptionGroup('') or whitespace name is used multiple times to assert headers render with fallback names and preserve group/option order.

I can draft this test if you confirm the expected fallback label format (e.g., “Group 1”, “Group 2”, etc.).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98cd918 and 2f49719.

📒 Files selected for processing (1)
  • packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart (1 hunks)
🔇 Additional comments (4)
packages/cli_tools/test/better_command_runner/option_group_usage_text_test.dart (4)

20-37: Good test scaffolding and reuse

Helper factories and runner builder keep tests clear and DRY. LGTM.


70-117: Hidden-group behavior is well covered

Solid checks: only groups with at least one visible option render; fully hidden groups and their options don’t. LGTM.


181-217: Option order within groups is well-validated

Nice check that preserves intra-group option order while mixing with groupless options. LGTM.


248-287: Comprehensive scenario test

Covers groupless, grouped, and hidden interactions with correct visibility and ordering. LGTM.

Copy link
Collaborator

@christerswahn christerswahn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final spelling comment.

Adjusted according to the guidelines of a Package Admin.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/config/lib/src/config/options.dart (2)

803-809: Document the addGroupSeparators switch

Add a brief doc comment describing semantics and default to help future callers.

-void addOptionsToParser(
+/// Adds options to [argParser]. When [addGroupSeparators] is true, options are
+/// grouped by [OptionGroup] and a separator with the group name is inserted
+/// before each group that has at least one visible option. Groupless options
+/// are added first in their original order.
+void addOptionsToParser(
   final Iterable<OptionDefinition> argNameOpts,
   final ArgParser argParser, {
   final bool addGroupSeparators = false,
 })

816-821: Make ordering intent explicit; drop explanatory comment

Use LinkedHashMap to encode insertion-order intent directly and remove the inline explanatory comment.

-  // the following containers are ordered by default i.e.
-  // preserves insertion-order:
-  // - Map  : https://stackoverflow.com/q/79786585/10251345
-  // - List : https://api.dart.dev/dart-core/List-class.html
-  final optionGroups = <OptionGroup, List<OptionDefinition>>{};
-  final grouplessOptions = <OptionDefinition>[];
+  final optionGroups = LinkedHashMap<OptionGroup, List<OptionDefinition>>();
+  final grouplessOptions = <OptionDefinition>[];

Add import:

import 'dart:collection';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2f49719 and 2a4bbae.

📒 Files selected for processing (1)
  • packages/config/lib/src/config/options.dart (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: indraneel12
PR: serverpod/cli_tools#0
File: :0-0
Timestamp: 2025-10-10T08:50:09.885Z
Learning: PR #71 in serverpod/cli_tools adds support for displaying OptionGroup names in CLI usage output. The implementation does NOT include fallback group names or numbered defaults for anonymous groups - it only displays the group.name when present and shows groupless options first without any header.
🔇 Additional comments (3)
packages/config/lib/src/config/options.dart (3)

44-47: Doc tweak on group header usage — LGTM

Clear and accurate. Matches the implemented behavior.


842-852: Header insertion logic is correct

Only emits a group header when at least one option in the group is visible; options remain in declared order. Matches the stated requirements.


725-726: Grouping default is safe ConfigParser calls addOptionsToParser with addGroupSeparators:false and remains flat, and CLI tools invoke prepareOptionsForParsing (addGroupSeparators:true) with tests updated for grouped output.

@indraneel12
Copy link
Contributor Author

@christerswahn Thank you. Btw I haven't focused very much on the Test Cases yet, so there might be some room for improvement in that file. After lunch, I'll see what I can do there (some nitpick comments by the Review Bot) and inform you regardless of any changes by requesting a final review.

@coderabbitai
Copy link

coderabbitai bot commented Oct 10, 2025

Tip

For best results, initiate chat on the files or code changes.

@indraneel12, you're absolutely right, and I apologize for the confusion in my earlier comments. I've reviewed the actual implementation and can confirm that the Fallback Group Name feature is NOT part of this PR.

The implementation in packages/config/lib/src/config/options.dart simply:

  • Displays groupless options first without any header
  • Shows each OptionGroup.name as a header when the group is present and visible
  • Does not include any fallback names or numbered defaults for anonymous groups

I've updated my context and added this to my long-term memory to ensure consistent understanding in future interactions. Thank you for catching this and helping me correct my understanding! 🙏


🧠 Learnings used
Learnt from: indraneel12
PR: serverpod/cli_tools#0
File: :0-0
Timestamp: 2025-10-10T08:50:09.885Z
Learning: PR #71 in serverpod/cli_tools adds support for displaying OptionGroup names in CLI usage output. The implementation does NOT include fallback group names or numbered defaults for anonymous groups - it only displays the group.name when present and shows groupless options first without any header.

@indraneel12 indraneel12 marked this pull request as draft October 10, 2025 09:37
Ensures that only one Separator is added per unique Group Name.
- explained the new feature
- explained the reason behind a default value
@indraneel12 indraneel12 marked this pull request as ready for review October 10, 2025 10:50
Copy link
Collaborator

@christerswahn christerswahn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, good job!

@christerswahn christerswahn merged commit a811a6f into serverpod:main Oct 13, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[config] Named Option Group limitation+suggestion

2 participants