Dash_skills dart-cli-app-best-practices

|-

install
source · Clone the upstream repo
git clone https://github.com/kevmoo/dash_skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/kevmoo/dash_skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.agent/skills/dart-cli-app-best-practices" ~/.claude/skills/kevmoo-dash-skills-dart-cli-app-best-practices && rm -rf "$T"
manifest: .agent/skills/dart-cli-app-best-practices/SKILL.md
source content

Dart CLI Application Best Practices

1. When to use this skill

Use this skill when:

  • Creating a new Dart CLI application.
  • Refactoring an existing CLI entrypoint (
    bin/
    ).
  • Reviewing CLI code for quality and standards.
  • Setting up executable scripts for Linux/Mac.

2. Best Practices

Entrypoint Structure (
bin/
)

Keep the contents of your entrypoint file (e.g.,

bin/my_app.dart
) minimal. This improves testability by decoupling logic from the process runner.

DO:

// bin/my_app.dart
import 'package:my_app/src/entry_point.dart';

Future<void> main(List<String> arguments) async {
  await runApp(arguments);
}

DON'T:

  • Put complex logic directly in
    bin/my_app.dart
    .
  • Define classes or heavy functions in the entrypoint.

Put an
executable
entry in
pubspec.yaml

List your executables in

pubspec.yaml
to make them available for global activation and clean invocation via
dart run
.

DO: Add an

executables
section mapping the command name to the Dart file in
bin/
(excluding the
.dart
extension).

executables:
  my_app: # Maps to bin/my_app.dart
  custom_name: main # Maps to bin/main.dart

Then run via

dart run my_app
or
dart run custom_name
.

CONSIDER
#!
for other scripts on *nix systems

This is NOT a hard and fast rule, but it is something to consider.

For CLI tools intended to be run directly on Linux and Mac, add a shebang and ensure the file is executable.

DO:

  1. Add
    #!/usr/bin/env dart
    to the first line.
  2. Run
    chmod +x bin/my_script.dart
    to make it executable.
#!/usr/bin/env dart

void main() => print('Ready to run!');

Process Termination (
exitCode
)

Properly handle process termination to allow for debugging and correct status reporting.

DO:

  • Use the
    exitCode
    setter to report failure.
  • Allow
    main
    to complete naturally.
  • Use standard exit codes (sysexits) for clarity (e.g.,
    64
    for bad usage,
    78
    for configuration errors).
    • See
      package:io
      ExitCode
      class or FreeBSD sysexits man page.
import 'dart:io';

void main() {
  if (someFailure) {
    exitCode = 64; // DO!
    return;
  }
}

AVOID:

  • Calling
    exit(code)
    directly, as it terminates the VM immediately, preventing "pause on exit" debugging and
    finally
    blocks from running.

Exception Handling

Uncaught exceptions automatically set a non-zero exit code, but you should handle expected errors gracefully.

Example:

Future<void> main(List<String> arguments) async {
  try {
    await runApp(arguments);
  } catch (e, stack) {
    print('App crashed!');
    print(e);
    print(stack);
    exitCode = 1; // Explicitly fail
  }
}

Cross-Platform Compatibility (Windows Support)

When writing CLI applications and tests, ensure compatibility with Windows:

  • Paths: Never hardcode path separators like
    /
    . Use
    package:path
    's
    p.join
    or
    p.normalize
    to construct paths portably.
  • File Permissions: When testing file permission errors, remember that
    chmod
    is not available on Windows. Use
    icacls
    on Windows or appropriate mock libraries. Never skip tests on Windows simply because of permission commands if a Windows equivalent exists.

3. Recommended Packages

Use these community-standard packages owned by the Dart team to solve common CLI problems:

CategoryRecommended PackageUsage
Stack Traces
package:stack_trace
detailed, cleaner stack traces
Arg Parsing
package:args
standard flag/option parsing
Testing
package:test_process
integration testing for CLI apps
Testing
package:test_descriptor
file system fixtures for tests
Networking
package:http
standard HTTP client (remember user-agent!)
ANSI Output
package:io
handling ANSI colors and styles

4. Interesting community packages

CategoryRecommended PackageUsage
Configuration
package:json_serializable
strongly typed config objects
CLI Generation
package:build_cli
generate arg parsers from classes
Version Info
package:build_version
automatic version injection
Configuration
package:checked_yaml
precise YAML parsing with line numbers

5. Conventions

  • File Caching: Write cached files to
    .dart_tool/[pkg_name]/
    .
  • User-Agent: Always set a User-Agent header in HTTP requests, including version info.