diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..041525a --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,29 @@ +include: package:dart_flutter_team_lints/analysis_options.yaml + +analyzer: + language: + strict-raw-types: true + +linter: + rules: + - avoid_bool_literals_in_conditional_expressions + - avoid_classes_with_only_static_members + - avoid_redundant_argument_values + - avoid_returning_this + - avoid_unused_constructor_parameters + - avoid_void_async + - cancel_subscriptions + - cascade_invocations + - join_return_with_assignment + - literal_only_boolean_expressions + - missing_whitespace_between_adjacent_strings + - no_adjacent_strings_in_list + - no_runtimeType_toString + - prefer_const_declarations + - prefer_expression_function_bodies + - prefer_final_locals + - require_trailing_commas + - simple_directive_paths + - unnecessary_ignore + - use_raw_strings + - use_string_buffers diff --git a/packages/dash_discover/lib/src/cli.dart b/packages/dash_discover/lib/src/cli.dart index a77ba15..53c1943 100644 --- a/packages/dash_discover/lib/src/cli.dart +++ b/packages/dash_discover/lib/src/cli.dart @@ -23,7 +23,8 @@ ArgParser buildArgParser() => ArgParser() 'rule', abbr: 'r', help: - 'Run only a specific rule by ID (e.g. checks-migration, pattern-matching).', + 'Run only a specific rule by ID ' + '(e.g. checks-migration, pattern-matching).', ) ..addOption( 'category', @@ -81,19 +82,21 @@ Future runCli( try { results = parser.parse(args); } on FormatException catch (e) { - err.writeln('Error: ${e.message}\n'); - err.writeln( - 'Usage: dart run dash_discover [path-to-target-package] [options]\n', - ); - err.writeln(parser.usage); + err + ..writeln('Error: ${e.message}\n') + ..writeln( + 'Usage: dart run dash_discover [path-to-target-package] [options]\n', + ) + ..writeln(parser.usage); return ExitCode.usage.code; } if (results.flag('help')) { - out.writeln( - 'Usage: dart run dash_discover [path-to-target-package] [options]\n', - ); - out.writeln(parser.usage); + out + ..writeln( + 'Usage: dart run dash_discover [path-to-target-package] [options]\n', + ) + ..writeln(parser.usage); return ExitCode.success.code; } @@ -110,10 +113,11 @@ Future runCli( out.writeln('skills/dash-discover/SKILL.md is up-to-date!'); return ExitCode.success.code; } else { - err.writeln('Error: skills/dash-discover/SKILL.md is out of date.'); - err.writeln( - 'Run `dart run dash_discover --update-skill` to update it.', - ); + err + ..writeln('Error: skills/dash-discover/SKILL.md is out of date.') + ..writeln( + 'Run `dart run dash_discover --update-skill` to update it.', + ); return ExitCode.data.code; } } @@ -144,16 +148,15 @@ Future runCli( workingDirectory: io.Directory(absPath), ); for (final rule in defaultDiscoveryRules) { - out.writeln( - '• ${rule.id} (${rule.category.label}) [${rule.lifecycle.label}]', - ); - out.writeln(' Skill: ${rule.target.skillName}'); - out.writeln(' Lifecycle: ${rule.lifecycle.label}'); - out.writeln(' Category: ${rule.category.label}'); - out.writeln( - ' Confidence: ${rule.defaultConfidence.name.toUpperCase()}', - ); - out.writeln(' Description: ${rule.description}'); + out + ..writeln( + '• ${rule.id} (${rule.category.label}) [${rule.lifecycle.label}]', + ) + ..writeln(' Skill: ${rule.target.skillName}') + ..writeln(' Lifecycle: ${rule.lifecycle.label}') + ..writeln(' Category: ${rule.category.label}') + ..writeln(' Confidence: ${rule.defaultConfidence.name.toUpperCase()}') + ..writeln(' Description: ${rule.description}'); final local = catalog.findByName(rule.target.skillName); if (local != null) { out.writeln(' Resolution: Local (${local.skillPath})'); @@ -163,7 +166,7 @@ Future runCli( if (rule.target.commitSha != null) { out.writeln(' Pinned SHA: ${rule.target.commitSha}'); } - out.writeln(''); + out.writeln(); } return ExitCode.success.code; } @@ -184,10 +187,11 @@ Future runCli( }).toList(); if (activeRules.isEmpty) { - err.writeln('Error: No rules match the specified filters.'); - err.writeln( - 'Available rules: ${defaultDiscoveryRules.map((r) => r.id).join(', ')}', - ); + err + ..writeln('Error: No rules match the specified filters.') + ..writeln( + 'Available rules: ${defaultDiscoveryRules.map((r) => r.id).join(', ')}', + ); return ExitCode.usage.code; } diff --git a/packages/dash_discover/lib/src/context.dart b/packages/dash_discover/lib/src/context.dart index 00b6ad6..b420dcc 100644 --- a/packages/dash_discover/lib/src/context.dart +++ b/packages/dash_discover/lib/src/context.dart @@ -88,9 +88,8 @@ class PackageContext { rawPubspec.contains('$name:'); } - String readContent(File file) { - return _contentCache.putIfAbsent(file.path, () => file.readAsStringSync()); - } + String readContent(File file) => + _contentCache.putIfAbsent(file.path, () => file.readAsStringSync()); List? _allTestFiles; diff --git a/packages/dash_discover/lib/src/discovery_engine.dart b/packages/dash_discover/lib/src/discovery_engine.dart index 00e3443..6f3275b 100644 --- a/packages/dash_discover/lib/src/discovery_engine.dart +++ b/packages/dash_discover/lib/src/discovery_engine.dart @@ -28,9 +28,9 @@ class DiscoveryReport { }; String toMarkdown() { - final buffer = StringBuffer(); - buffer.writeln('# Meta-Skill Discovery Report: $packageName\n'); - buffer.writeln('Path: `$packagePath`\n'); + final buffer = StringBuffer() + ..writeln('# Meta-Skill Discovery Report: $packageName\n') + ..writeln('Path: `$packagePath`\n'); if (staticOpportunities.isEmpty) { buffer.writeln( @@ -41,19 +41,21 @@ class DiscoveryReport { '## 🎯 Detected Opportunities (${staticOpportunities.length})\n', ); for (final opp in staticOpportunities) { - buffer.writeln( - '### [${opp.skill}](${opp.target.resolvedUri}) (${opp.category.label})\n', - ); final targetDesc = opp.target.isLocal ? 'Local file (`${opp.target.localPath}`)' : 'Remote GitHub (${opp.target.githubUrl})'; - buffer.writeln('- **Target Skill**: $targetDesc'); - buffer.writeln('- **Lifecycle**: ${opp.lifecycle.label}'); - buffer.writeln('- **Confidence**: ${opp.confidence.label}'); - buffer.writeln('- **Impact**: ${opp.affectedCount} file(s)'); - buffer.writeln('- **Diagnosis**: ${opp.diagnosis}'); - buffer.writeln('- **Prescription**: ${opp.prescription}'); - buffer.writeln('- **Evidence**:'); + buffer + ..writeln( + '### [${opp.skill}](${opp.target.resolvedUri}) ' + '(${opp.category.label})\n', + ) + ..writeln('- **Target Skill**: $targetDesc') + ..writeln('- **Lifecycle**: ${opp.lifecycle.label}') + ..writeln('- **Confidence**: ${opp.confidence.label}') + ..writeln('- **Impact**: ${opp.affectedCount} file(s)') + ..writeln('- **Diagnosis**: ${opp.diagnosis}') + ..writeln('- **Prescription**: ${opp.prescription}') + ..writeln('- **Evidence**:'); for (final ev in opp.evidence) { buffer.writeln(' - $ev'); } @@ -85,7 +87,8 @@ class DiscoveryEngine { ); final rawOpportunities = staticEngine.scan(); - // Resolve skill targets against catalog (prefer local SKILL.md over remote GitHub) + // Resolve skill targets against catalog (prefer local SKILL.md over + // remote GitHub) final staticOpportunities = rawOpportunities.map((opp) { final localMeta = catalog.findByName(opp.target.skillName); if (localMeta != null) { diff --git a/packages/dash_discover/lib/src/models.dart b/packages/dash_discover/lib/src/models.dart index f7eec91..5e5c347 100644 --- a/packages/dash_discover/lib/src/models.dart +++ b/packages/dash_discover/lib/src/models.dart @@ -62,7 +62,8 @@ class SkillTarget { this.localPath, }); - /// The basename identifier of the skill (e.g. `dart-migrate-to-checks-package`). + /// The basename identifier of the skill + /// (e.g. `dart-migrate-to-checks-package`). String get skillName => path.split('/').last; /// Full permalink or branch URL to the skill definition on GitHub. @@ -122,7 +123,8 @@ class Opportunity implements Comparable { String get skill => target.skillName; - /// Resolved URI pointing to the local SKILL.md if installed, or upstream GitHub. + /// Resolved URI pointing to the local SKILL.md if installed, or upstream + /// GitHub. Uri get resolvedUri => target.resolvedUri; /// Whether this opportunity's target skill is installed locally. diff --git a/packages/dash_discover/lib/src/outline_generator.dart b/packages/dash_discover/lib/src/outline_generator.dart index 4ecf6af..4081772 100644 --- a/packages/dash_discover/lib/src/outline_generator.dart +++ b/packages/dash_discover/lib/src/outline_generator.dart @@ -11,9 +11,9 @@ class OutlineGenerator { /// Builds the markdown outline string. String generate({int maxSignatureLines = 250}) { - final buffer = StringBuffer(); - buffer.writeln('# REPOSITORY OUTLINE: $packageName'); - buffer.writeln('Path: $packagePath\n'); + final buffer = StringBuffer() + ..writeln('# REPOSITORY OUTLINE: $packageName') + ..writeln('Path: $packagePath\n'); _appendPubspec(buffer); _appendDirectoryStructure(buffer); @@ -113,7 +113,8 @@ class OutlineGenerator { buffer.writeln(trimmed); if (raw.length > maxSignatureLines) { buffer.writeln( - '\n... [${raw.length - maxSignatureLines} signature lines truncated for token efficiency] ...', + '\n... [${raw.length - maxSignatureLines} signature lines ' + 'truncated for token efficiency] ...', ); } return; diff --git a/packages/dash_discover/lib/src/package_facts.dart b/packages/dash_discover/lib/src/package_facts.dart index e81ed27..567d307 100644 --- a/packages/dash_discover/lib/src/package_facts.dart +++ b/packages/dash_discover/lib/src/package_facts.dart @@ -35,7 +35,7 @@ class ParsedSource { /// not part of the package's public API. bool get isImplementation => relativePath.startsWith('lib/src/') || - relativePath.startsWith('lib\\src\\'); + relativePath.startsWith(r'lib\src\'); /// 1-based line number for [offset], for human-readable evidence. int lineOf(int offset) { @@ -186,18 +186,14 @@ class PackageFacts { final sources = []; for (final file in files) { final content = context.readContent(file); - final CompilationUnit unit; - try { - unit = parseString( - content: content, - path: file.path, - throwIfDiagnostics: false, - ).unit; - } on ArgumentError { - // Unparseable source (e.g. a future language feature this analyzer - // does not know). Skipping is correct: no facts beats wrong facts. - continue; - } + // `throwIfDiagnostics: false` keeps syntactically broken sources (e.g. + // a future language feature this analyzer does not know) from throwing; + // the recovered partial AST is still useful for fact extraction. + final unit = parseString( + content: content, + path: file.path, + throwIfDiagnostics: false, + ).unit; sources.add( ParsedSource( file: file, diff --git a/packages/dash_discover/lib/src/rule.dart b/packages/dash_discover/lib/src/rule.dart index e90e885..521d97a 100644 --- a/packages/dash_discover/lib/src/rule.dart +++ b/packages/dash_discover/lib/src/rule.dart @@ -2,7 +2,8 @@ import 'dart:io'; import 'context.dart'; import 'models.dart'; -/// Base contract for a discovery rule aligning project signals with an upstream skill. +/// Base contract for a discovery rule aligning project signals with an +/// upstream skill. abstract class DiscoveryRule { const DiscoveryRule(); @@ -15,7 +16,8 @@ abstract class DiscoveryRule { /// Architectural category organizing this rule. RuleCategory get category; - /// Skill lifecycle type: one-time migration, periodic hygiene, or architecture. + /// Skill lifecycle type: one-time migration, periodic hygiene, or + /// architecture. SkillLifecycle get lifecycle; /// Concise summary of what this rule detects. @@ -27,7 +29,8 @@ abstract class DiscoveryRule { /// Fast precondition check before running full evaluation. bool appliesTo(PackageContext context) => true; - /// Evaluates the package and yields any discovered modernization opportunities. + /// Evaluates the package and yields any discovered modernization + /// opportunities. Iterable evaluate(PackageContext context); } diff --git a/packages/dash_discover/lib/src/rules/checks_migration_rule.dart b/packages/dash_discover/lib/src/rules/checks_migration_rule.dart index c3b6789..171cda4 100644 --- a/packages/dash_discover/lib/src/rules/checks_migration_rule.dart +++ b/packages/dash_discover/lib/src/rules/checks_migration_rule.dart @@ -53,9 +53,11 @@ final class ChecksMigrationRule extends DiscoveryRule { confidence: defaultConfidence, affectedCount: context.testFiles.length, diagnosis: - 'Repository depends on `package:test` but does not use `package:checks`.', + 'Repository depends on `package:test` but does not use ' + '`package:checks`.', prescription: - 'Migrate test assertions from legacy `expect(actual, matcher)` to fluent, strongly-typed `check(actual)...` chains.', + 'Migrate test assertions from legacy `expect(actual, matcher)` to ' + 'fluent, strongly-typed `check(actual)...` chains.', evidence: [ 'pubspec.yaml lacks checks dependency', ...context.testFiles.take(3).map(context.relativePath), diff --git a/packages/dash_discover/lib/src/rules/cli_app_rule.dart b/packages/dash_discover/lib/src/rules/cli_app_rule.dart index 40f810d..52e49f7 100644 --- a/packages/dash_discover/lib/src/rules/cli_app_rule.dart +++ b/packages/dash_discover/lib/src/rules/cli_app_rule.dart @@ -63,7 +63,8 @@ final class CliAppRule extends DiscoveryRule { return null; } - // 3. Thin Trampoline Guardrail (<30 non-empty lines delegating to package library) + // 3. Thin Trampoline Guardrail (<30 non-empty lines delegating to + // package library) final lines = content.split('\n').where((l) => l.trim().isNotEmpty).length; if (lines <= 30 && (content.contains('package:${context.packageName}/') || diff --git a/packages/dash_discover/lib/src/rules/matcher_best_practices_rule.dart b/packages/dash_discover/lib/src/rules/matcher_best_practices_rule.dart index 4fbe38e..f355bd1 100644 --- a/packages/dash_discover/lib/src/rules/matcher_best_practices_rule.dart +++ b/packages/dash_discover/lib/src/rules/matcher_best_practices_rule.dart @@ -24,7 +24,8 @@ final class MatcherBestPracticesRule extends FileDiscoveryRule { r'expect\(\s*[^,]+?\.length\b\s*,\s*([^,)]+)', ); - // Checks if the expected argument is a floating-point number (e.g. 25.0 or equals(25.0)): + // Checks if the expected argument is a floating-point number + // (e.g. 25.0 or equals(25.0)): static final _floatPattern = RegExp(r'^(?:equals\(\s*)?\d+\.\d+'); // Matches map string key lookups: @@ -57,7 +58,8 @@ final class MatcherBestPracticesRule extends FileDiscoveryRule { @override String get description => - 'Detects unidiomatic expect() assertions (e.g. expect(x.length, ...) or expect(x.isEmpty, true)).'; + 'Detects unidiomatic expect() assertions (e.g. expect(x.length, ...) ' + 'or expect(x.isEmpty, true)).'; @override Confidence get defaultConfidence => Confidence.high; @@ -73,11 +75,13 @@ final class MatcherBestPracticesRule extends FileDiscoveryRule { @override String get diagnosisTemplate => - '{count} test file(s) use suboptimal expect() assertions instead of dedicated matchers.'; + '{count} test file(s) use suboptimal expect() assertions instead of ' + 'dedicated matchers.'; @override String get prescription => - 'Migrate to first-class matchers (hasLength, isEmpty, isNotEmpty, contains) for clearer assertion failure messages.'; + 'Migrate to first-class matchers (hasLength, isEmpty, isNotEmpty, ' + 'contains) for clearer assertion failure messages.'; @override String? checkFile(File file, String content, PackageContext context) { diff --git a/packages/dash_discover/lib/src/rules/mock_generation_rule.dart b/packages/dash_discover/lib/src/rules/mock_generation_rule.dart index c9d1eff..5809a2f 100644 --- a/packages/dash_discover/lib/src/rules/mock_generation_rule.dart +++ b/packages/dash_discover/lib/src/rules/mock_generation_rule.dart @@ -34,7 +34,8 @@ final class MockGenerationRule extends FileDiscoveryRule { @override String get description => - 'Detects handwritten fake or mock class definitions without mockito or mocktail.'; + 'Detects handwritten fake or mock class definitions without mockito ' + 'or mocktail.'; @override Confidence get defaultConfidence => Confidence.medium; @@ -48,7 +49,8 @@ final class MockGenerationRule extends FileDiscoveryRule { @override String get prescription => - 'Automate test stubbing using `@GenerateMocks` or `mocktail` to avoid manual maintenance of interface stubs.'; + 'Automate test stubbing using `@GenerateMocks` or `mocktail` to avoid ' + 'manual maintenance of interface stubs.'; @override String? checkFile(File file, String content, PackageContext context) { diff --git a/packages/dash_discover/lib/src/rules/path_package_rule.dart b/packages/dash_discover/lib/src/rules/path_package_rule.dart index edc4d62..b1d4613 100644 --- a/packages/dash_discover/lib/src/rules/path_package_rule.dart +++ b/packages/dash_discover/lib/src/rules/path_package_rule.dart @@ -148,11 +148,7 @@ class _PathJoinVisitor extends RecursiveAstVisitor { /// True when the literal is an argument to a `Uri` constructor or factory, /// where `/` is a URI separator by definition. bool _isInsideUriConstruction(StringInterpolation node) { - for ( - AstNode? current = node.parent; - current != null; - current = current.parent - ) { + for (var current = node.parent; current != null; current = current.parent) { switch (current) { case MethodInvocation(:final target): if (target?.toSource() == 'Uri') return true; diff --git a/packages/dash_discover/lib/src/rules/pattern_matching_rule.dart b/packages/dash_discover/lib/src/rules/pattern_matching_rule.dart index c20b801..29cf357 100644 --- a/packages/dash_discover/lib/src/rules/pattern_matching_rule.dart +++ b/packages/dash_discover/lib/src/rules/pattern_matching_rule.dart @@ -4,7 +4,8 @@ import '../models.dart'; import '../rule.dart'; /// Discovery rule for identifying legacy type cascades and statements that can -/// be modernized with Dart 3 pattern matching, switch expressions, and destructuring. +/// be modernized with Dart 3 pattern matching, switch expressions, and +/// destructuring. /// /// **Target Skill**: /// - GitHub: https://github.com/dart-lang/skills/tree/26b2dcc5654cbbc3b2ec56ea94719469bc8bae9e/skills/dart-use-pattern-matching @@ -18,7 +19,8 @@ final class PatternMatchingRule extends FileDiscoveryRule { r'(?:\bif\s*\([^)]+\s+is\s+[^)]+\)[^{}]*\{[^{}]*\}\s*else\s+if\s*\([^)]+\s+is\s+[^)]+\)|else\s+if\s*\([^)]+\s+is\s+[^)]+\)[^{}]*\{[^{}]*\}\s*else\s+if\s*\([^)]+\s+is\s+[^)]+\))', ); - // Switch statement where a case immediately returns or throws (linear matching without backtracking): + // Switch statement where a case immediately returns or throws (linear + // matching without backtracking): static final _returningSwitchPattern = RegExp( r'\bswitch\s*\([^)]+\)\s*\{[^{}]*?\b(?:case\b[^:]+|default)\s*:\s*(?:return\b|throw\b)', ); @@ -42,18 +44,21 @@ final class PatternMatchingRule extends FileDiscoveryRule { @override String get description => - 'Detects legacy else if (... is ...) type cascades and returning switch statements.'; + 'Detects legacy else if (... is ...) type cascades and returning ' + 'switch statements.'; @override Confidence get defaultConfidence => Confidence.high; @override String get diagnosisTemplate => - '{count} file(s) use legacy `else if (... is ...)` type cascades or returning switch statements.'; + '{count} file(s) use legacy `else if (... is ...)` type cascades or ' + 'returning switch statements.'; @override String get prescription => - 'Refactor into concise Dart 3 switch expressions, sealed class exhaustiveness, and pattern destructuring.'; + 'Refactor into concise Dart 3 switch expressions, sealed class ' + 'exhaustiveness, and pattern destructuring.'; @override String? checkFile(File file, String content, PackageContext context) { diff --git a/packages/dash_discover/lib/src/skill_doc_generator.dart b/packages/dash_discover/lib/src/skill_doc_generator.dart index d4ecfee..c76333c 100644 --- a/packages/dash_discover/lib/src/skill_doc_generator.dart +++ b/packages/dash_discover/lib/src/skill_doc_generator.dart @@ -9,15 +9,15 @@ const discoveryRulesEndTag = ''; /// Generates the Markdown list of discovery rules for inclusion in SKILL.md. String generateDiscoveryRulesBlock([List? rules]) { final activeRules = rules ?? defaultDiscoveryRules; - final buffer = StringBuffer(); - buffer.writeln(discoveryRulesStartTag); - buffer.writeln(); - buffer.writeln( - 'The static scanner performs rapid, zero-network checks across ' - '${activeRules.length} built-in rules:', - ); - buffer.writeln(); - buffer.writeln(''); + final buffer = StringBuffer() + ..writeln(discoveryRulesStartTag) + ..writeln() + ..writeln( + 'The static scanner performs rapid, zero-network checks across ' + '${activeRules.length} built-in rules:', + ) + ..writeln() + ..writeln(''); for (var i = 0; i < activeRules.length; i++) { final rule = activeRules[i]; final num = i + 1; @@ -26,8 +26,9 @@ String generateDiscoveryRulesBlock([List? rules]) { '${rule.description}', ); } - buffer.writeln(); - buffer.write(discoveryRulesEndTag); + buffer + ..writeln() + ..write(discoveryRulesEndTag); return buffer.toString(); } @@ -37,7 +38,8 @@ String updateSkillContent(String content, [List? rules]) { final endIndex = content.indexOf(discoveryRulesEndTag); if (startIndex == -1 || endIndex == -1) { throw StateError( - 'Could not find $discoveryRulesStartTag and $discoveryRulesEndTag in content', + 'Could not find $discoveryRulesStartTag and $discoveryRulesEndTag ' + 'in content', ); } final generated = generateDiscoveryRulesBlock(rules); diff --git a/packages/dash_discover/lib/src/skills_catalog.dart b/packages/dash_discover/lib/src/skills_catalog.dart index 1689e08..d2a7b5c 100644 --- a/packages/dash_discover/lib/src/skills_catalog.dart +++ b/packages/dash_discover/lib/src/skills_catalog.dart @@ -135,8 +135,8 @@ class SkillsCatalog { /// Formats the skills catalog for inclusion in an LLM probe prompt. String formatForPrompt() { - final buffer = StringBuffer(); - buffer.writeln('### AVAILABLE SKILLS CATALOG (${skills.length} skills):'); + final buffer = StringBuffer() + ..writeln('### AVAILABLE SKILLS CATALOG (${skills.length} skills):'); for (final skill in skills) { buffer.writeln('- **${skill.name}**: ${skill.description}'); } diff --git a/packages/dash_discover/lib/src/static_discovery.dart b/packages/dash_discover/lib/src/static_discovery.dart index 3b5f152..69bfc1e 100644 --- a/packages/dash_discover/lib/src/static_discovery.dart +++ b/packages/dash_discover/lib/src/static_discovery.dart @@ -19,11 +19,10 @@ class StaticDiscoveryEngine { factory StaticDiscoveryEngine.forPath( String path, { List? rules, - }) { - return StaticDiscoveryEngine(PackageContext.load(path), rules: rules); - } + }) => StaticDiscoveryEngine(PackageContext.load(path), rules: rules); - /// Runs all applicable rules against the package context, sorted deterministically. + /// Runs all applicable rules against the package context, sorted + /// deterministically. List scan() { final opportunities = []; for (final rule in rules) { diff --git a/packages/dash_discover/test/cli_app_eval_test.dart b/packages/dash_discover/test/cli_app_eval_test.dart index 37ee670..aba4b97 100644 --- a/packages/dash_discover/test/cli_app_eval_test.dart +++ b/packages/dash_discover/test/cli_app_eval_test.dart @@ -57,7 +57,8 @@ void main() { result, isNull, reason: - 'Must not flag thin entrypoint delegating to package implementation', + 'Must not flag thin entrypoint delegating to package ' + 'implementation', ); }); diff --git a/packages/dash_discover/test/cli_test.dart b/packages/dash_discover/test/cli_test.dart index 0cf584c..aad015c 100644 --- a/packages/dash_discover/test/cli_test.dart +++ b/packages/dash_discover/test/cli_test.dart @@ -42,55 +42,49 @@ void main() { }, ); - test( - 'invalid option writes error and usage to stderr and returns ExitCode.usage', - () async { - final code = await runCli(['--unknown-flag'], stdout: out, stderr: err); - - expect(code, equals(ExitCode.usage.code)); - expect( - err.toString(), - contains('Could not find an option named "--unknown-flag".'), - ); - expect(err.toString(), contains('Usage: dart run dash_discover')); - expect(out.toString(), isEmpty); - }, - ); + test('invalid option writes error and usage to stderr and returns ' + 'ExitCode.usage', () async { + final code = await runCli(['--unknown-flag'], stdout: out, stderr: err); + + expect(code, equals(ExitCode.usage.code)); + expect( + err.toString(), + contains('Could not find an option named "--unknown-flag".'), + ); + expect(err.toString(), contains('Usage: dart run dash_discover')); + expect(out.toString(), isEmpty); + }); - test( - 'non-existent target directory writes error to stderr and returns ExitCode.noInput', - () async { - final nonExistent = p.join( - Directory.systemTemp.path, - 'non_existent_dir_12345', - ); - final code = await runCli([nonExistent], stdout: out, stderr: err); + test('non-existent target directory writes error to stderr and returns ' + 'ExitCode.noInput', () async { + final nonExistent = p.join( + Directory.systemTemp.path, + 'non_existent_dir_12345', + ); + final code = await runCli([nonExistent], stdout: out, stderr: err); - expect(code, equals(ExitCode.noInput.code)); - expect( - err.toString(), - contains('Error: Target directory does not exist:'), - ); - expect(out.toString(), isEmpty); - }, - ); + expect(code, equals(ExitCode.noInput.code)); + expect( + err.toString(), + contains('Error: Target directory does not exist:'), + ); + expect(out.toString(), isEmpty); + }); - test( - '--list-rules outputs rule listing to stdout and returns ExitCode.success', - () async { - final code = await runCli( - [repoRoot.path, '--list-rules'], - stdout: out, - stderr: err, - ); + test('--list-rules outputs rule listing to stdout and returns ' + 'ExitCode.success', () async { + final code = await runCli( + [repoRoot.path, '--list-rules'], + stdout: out, + stderr: err, + ); - expect(code, equals(ExitCode.success.code)); - expect(out.toString(), contains('Available Discovery Rules')); - expect(out.toString(), contains('checks-migration')); - expect(out.toString(), contains('dart-seal-type-hierarchies')); - expect(err.toString(), isEmpty); - }, - ); + expect(code, equals(ExitCode.success.code)); + expect(out.toString(), contains('Available Discovery Rules')); + expect(out.toString(), contains('checks-migration')); + expect(out.toString(), contains('dart-seal-type-hierarchies')); + expect(err.toString(), isEmpty); + }); test( 'unmatched rule filter writes error to stderr and returns ExitCode.usage', @@ -137,20 +131,18 @@ void main() { expect(err.toString(), isEmpty); }); - test( - 'valid discovery run with --outline-only emits repository outline to stdout', - () async { - final code = await runCli( - [repoRoot.path, '--outline-only'], - stdout: out, - stderr: err, - ); + test('valid discovery run with --outline-only emits repository outline ' + 'to stdout', () async { + final code = await runCli( + [repoRoot.path, '--outline-only'], + stdout: out, + stderr: err, + ); - expect(code, equals(ExitCode.success.code)); - expect(out.toString(), contains('REPOSITORY OUTLINE')); - expect(err.toString(), isEmpty); - }, - ); + expect(code, equals(ExitCode.success.code)); + expect(out.toString(), contains('REPOSITORY OUTLINE')); + expect(err.toString(), isEmpty); + }); test( 'valid discovery run with --prompt-only emits probe prompt to stdout', diff --git a/packages/dash_discover/test/discover_test.dart b/packages/dash_discover/test/discover_test.dart index 958616c..7a64c38 100644 --- a/packages/dash_discover/test/discover_test.dart +++ b/packages/dash_discover/test/discover_test.dart @@ -128,7 +128,7 @@ void main() { }); test('ChecksMigrationRule declares unambiguous upstream GitHub target', () { - final rule = ChecksMigrationRule(); + const rule = ChecksMigrationRule(); expect(rule.target.org, 'dart-lang'); expect(rule.target.repo, 'skills'); expect(rule.target.path, 'skills/dart-migrate-to-checks-package'); @@ -153,7 +153,7 @@ dev_dependencies: ).writeAsStringSync('void main() {}'); final context = PackageContext.load(tempDir.path); - final rule = ChecksMigrationRule(); + const rule = ChecksMigrationRule(); expect(rule.appliesTo(context), isTrue); final opps = rule.evaluate(context).toList(); @@ -180,7 +180,7 @@ void main(List args) { '''); final context = PackageContext.load(tempDir.path); - final rule = CliAppRule(); + const rule = CliAppRule(); expect(rule.appliesTo(context), isTrue); final opps = rule.evaluate(context).toList(); @@ -203,7 +203,7 @@ void parse(Object x) { '''); final context = PackageContext.load(tempDir.path); - final rule = PatternMatchingRule(); + const rule = PatternMatchingRule(); final opps = rule.evaluate(context).toList(); expect(opps, hasLength(1)); @@ -211,18 +211,17 @@ void parse(Object x) { }, ); - test( - 'MatcherBestPracticesRule detects suboptimal expect() calls in test files', - () { - File(p.join(tempDir.path, 'pubspec.yaml')).writeAsStringSync(''' + test('MatcherBestPracticesRule detects suboptimal expect() calls in test ' + 'files', () { + File(p.join(tempDir.path, 'pubspec.yaml')).writeAsStringSync(''' name: sample_test_pkg environment: sdk: ^3.0.0 dev_dependencies: test: ^1.24.0 '''); - final testDir = Directory(p.join(tempDir.path, 'test'))..createSync(); - File(p.join(testDir.path, 'sample_test.dart')).writeAsStringSync(''' + final testDir = Directory(p.join(tempDir.path, 'test'))..createSync(); + File(p.join(testDir.path, 'sample_test.dart')).writeAsStringSync(''' import 'package:test/test.dart'; void main() { @@ -234,20 +233,19 @@ void main() { } '''); - final context = PackageContext.load(tempDir.path); - final rule = MatcherBestPracticesRule(); + final context = PackageContext.load(tempDir.path); + const rule = MatcherBestPracticesRule(); - expect(rule.appliesTo(context), isTrue); - final opps = rule.evaluate(context).toList(); + expect(rule.appliesTo(context), isTrue); + final opps = rule.evaluate(context).toList(); - expect(opps, hasLength(1)); - expect(opps.single.skill, 'dart-matcher-best-practices'); - expect(opps.single.target.org, 'kevmoo'); - expect(opps.single.category, RuleCategory.testing); - expect(opps.single.lifecycle, SkillLifecycle.hygiene); - expect(opps.single.confidence, Confidence.high); - }, - ); + expect(opps, hasLength(1)); + expect(opps.single.skill, 'dart-matcher-best-practices'); + expect(opps.single.target.org, 'kevmoo'); + expect(opps.single.category, RuleCategory.testing); + expect(opps.single.lifecycle, SkillLifecycle.hygiene); + expect(opps.single.confidence, Confidence.high); + }); }); group('DiscoveryEngine & Registry', () { @@ -302,7 +300,6 @@ void main() { target: target, category: RuleCategory.testing, lifecycle: SkillLifecycle.migration, - confidence: Confidence.high, affectedCount: 5, diagnosis: 'd', prescription: 'p', @@ -322,8 +319,6 @@ void main() { target: target, category: RuleCategory.cli, lifecycle: SkillLifecycle.architecture, - confidence: Confidence.high, - affectedCount: 1, diagnosis: 'd', prescription: 'p', evidence: [], @@ -332,7 +327,6 @@ void main() { target: target, category: RuleCategory.codeQuality, lifecycle: SkillLifecycle.hygiene, - confidence: Confidence.high, affectedCount: 50, diagnosis: 'd', prescription: 'p', @@ -342,7 +336,6 @@ void main() { target: target, category: RuleCategory.codeQuality, lifecycle: SkillLifecycle.hygiene, - confidence: Confidence.high, affectedCount: 2, diagnosis: 'd', prescription: 'p', diff --git a/packages/dash_discover/test/fixtures/analysis_options.yaml b/packages/dash_discover/test/fixtures/analysis_options.yaml new file mode 100644 index 0000000..c41dcf2 --- /dev/null +++ b/packages/dash_discover/test/fixtures/analysis_options.yaml @@ -0,0 +1,19 @@ +# Test fixtures are deliberate positive/negative examples for the discovery +# engine (not shipped code). Keep the canonical lint set, but relax rules that +# the fixtures intentionally violate so their semantics stay untouched: +# - lines_longer_than_80_chars: inline "Suboptimal:/Skill abstention" notes +# are part of the fixture narrative. +# - depend_on_referenced_packages: `path_package/negative` imports +# `package:path` without a fixture pubspec dep (resolved via the host +# package). +# - avoid_relative_lib_imports / unreachable_from_main: +# `sealed_hierarchy/negatives/test` models a subtype declared outside +# `lib/` via a relative import so the rule must abstain from sealing. +include: ../../../../analysis_options.yaml + +linter: + rules: + avoid_relative_lib_imports: false + depend_on_referenced_packages: false + lines_longer_than_80_chars: false + unreachable_from_main: false diff --git a/packages/dash_discover/test/fixtures/path_package/positive/lib/raw_path.dart b/packages/dash_discover/test/fixtures/path_package/positive/lib/raw_path.dart index 2f54606..2d4a1d6 100644 --- a/packages/dash_discover/test/fixtures/path_package/positive/lib/raw_path.dart +++ b/packages/dash_discover/test/fixtures/path_package/positive/lib/raw_path.dart @@ -4,7 +4,7 @@ class PathDemo { void process(String dir, String workspace, String root) { // Suboptimal: raw string interpolation with hardcoded slash final p1 = '$dir/lib/src/foo.dart'; - final p2 = '${dir}/test'; + final p2 = '$dir/test'; final p3 = '${workspace.length}/lib'; // Suboptimal: File / Directory constructor with raw interpolation diff --git a/packages/dash_discover/test/fixtures/pattern_matching/negative_idiomatic.dart b/packages/dash_discover/test/fixtures/pattern_matching/negative_idiomatic.dart index d083fc0..1e4b593 100644 --- a/packages/dash_discover/test/fixtures/pattern_matching/negative_idiomatic.dart +++ b/packages/dash_discover/test/fixtures/pattern_matching/negative_idiomatic.dart @@ -15,7 +15,7 @@ class IdiomaticDemo { }; void handlePattern(Map json) { - if (json case {'id': int id, 'name': String name}) { + if (json case {'id': final int id, 'name': final String name}) { print('User $id: $name'); } } diff --git a/packages/dash_discover/test/pattern_matching_eval_test.dart b/packages/dash_discover/test/pattern_matching_eval_test.dart index f475da8..a667a89 100644 --- a/packages/dash_discover/test/pattern_matching_eval_test.dart +++ b/packages/dash_discover/test/pattern_matching_eval_test.dart @@ -48,7 +48,8 @@ void main() { result, isNull, reason: - 'Must abstain on simple 2-branch guards with single type promotion', + 'Must abstain on simple 2-branch guards with single type ' + 'promotion', ); }, ); diff --git a/packages/dash_discover/tool/check_aux_effect.dart b/packages/dash_discover/tool/check_aux_effect.dart index 2ef2a2b..7c4a186 100644 --- a/packages/dash_discover/tool/check_aux_effect.dart +++ b/packages/dash_discover/tool/check_aux_effect.dart @@ -42,9 +42,10 @@ void main(List args) { } rejectedByAux++; - details.add( - ' ${type.name} <- ${outsideLib.map((s) => '${s.name} (${s.source.relativePath})').join(', ')}', - ); + final outside = outsideLib + .map((s) => '${s.name} (${s.source.relativePath})') + .join(', '); + details.add(' ${type.name} <- $outside'); } print( diff --git a/skills/dart-test-coverage/scripts/interpret_coverage.dart b/skills/dart-test-coverage/scripts/interpret_coverage.dart index 7e7b833..608c1e8 100644 --- a/skills/dart-test-coverage/scripts/interpret_coverage.dart +++ b/skills/dart-test-coverage/scripts/interpret_coverage.dart @@ -54,11 +54,13 @@ void main(List args) { }); } -/// Processes a list of coverage files and returns a map of file paths to line hit counts. +/// Processes a list of coverage files and returns a map of file paths to +/// line hit counts. /// /// The returned map structure is: `{ file_uri: { line_number: hit_count } }`. /// -/// If [packageName] is provided, only files starting with `package:$packageName/` are included. +/// If [packageName] is provided, only files starting with +/// `package:$packageName/` are included. /// Files containing `/test/` are skipped. Map> processCoverage( Iterable files, @@ -70,7 +72,7 @@ Map> processCoverage( final json = jsonDecode(content) as Map; final coverage = json['coverage'] as List; - for (final item in coverage) { + for (final item in coverage.cast>()) { final source = item['source'] as String; // Filter by package name if provided diff --git a/skills/dart-test-coverage/scripts/test/interpret_coverage_test.dart b/skills/dart-test-coverage/scripts/test/interpret_coverage_test.dart index 3ac0ed2..b7887d2 100644 --- a/skills/dart-test-coverage/scripts/test/interpret_coverage_test.dart +++ b/skills/dart-test-coverage/scripts/test/interpret_coverage_test.dart @@ -20,8 +20,9 @@ void main() { final hitsMap = item['hits'] as Map; final flatHits = []; hitsMap.forEach((line, count) { - flatHits.add(line); - flatHits.add(count); + flatHits + ..add(line) + ..add(count); }); return {'source': source, 'hits': flatHits}; }).toList(); diff --git a/skills/profile-dart-code/scripts/bin/profile.dart b/skills/profile-dart-code/scripts/bin/profile.dart index eeaa4b8..8122402 100644 --- a/skills/profile-dart-code/scripts/bin/profile.dart +++ b/skills/profile-dart-code/scripts/bin/profile.dart @@ -32,11 +32,10 @@ Future main(List arguments) async { try { results = parser.parse(arguments); } on ArgParserException catch (e) { - stderr.writeln('Error: ${e.message}'); - stderr.writeln( - 'Usage: dart profile.dart [options] -- [args...]', - ); - stderr.writeln(parser.usage); + stderr + ..writeln('Error: ${e.message}') + ..writeln('Usage: dart profile.dart [options] -- [args...]') + ..writeln(parser.usage); exitCode = 64; return; } @@ -47,10 +46,9 @@ Future main(List arguments) async { return; } if (results.rest.isEmpty) { - stderr.writeln( - 'Usage: dart profile.dart [options] -- [args...]', - ); - stderr.writeln(parser.usage); + stderr + ..writeln('Usage: dart profile.dart [options] -- [args...]') + ..writeln(parser.usage); exitCode = 64; return; } @@ -59,7 +57,8 @@ Future main(List arguments) async { final period = int.tryParse(results['period'] as String); if (period == null || period < 50) { stderr.writeln( - 'Error: Invalid --period value "${results['period']}". Must be an integer >= 50.', + 'Error: Invalid --period value "${results['period']}". ' + 'Must be an integer >= 50.', ); exitCode = 64; return; @@ -166,9 +165,11 @@ Future main(List arguments) async { final isolateId = isolateRef.id!; var connectionLost = false; - service.onDone.then((_) { - connectionLost = true; - }); + unawaited( + service.onDone.then((_) { + connectionLost = true; + }), + ); var isPausedAtExit = false; while (!isPausedAtExit && !connectionLost) { @@ -196,12 +197,13 @@ Future main(List arguments) async { print('Target process exited with code $procExitCode'); break; } - await Future.delayed(const Duration(milliseconds: 100)); + await Future.delayed(const Duration(milliseconds: 100)); } if (!isPausedAtExit) { stderr.writeln( - 'Error: Target process did not pause at exit. Cannot retrieve CPU profile.', + 'Error: Target process did not pause at exit. ' + 'Cannot retrieve CPU profile.', ); exitCode = 1; return; @@ -225,7 +227,8 @@ Future main(List arguments) async { print('\n=== Top 15 Functions by Self CPU Samples ==='); print( - '${'Self %'.padRight(8)} | ${'Self'.padRight(8)} | ${'Total %'.padRight(8)} | Function', + '${'Self %'.padRight(8)} | ${'Self'.padRight(8)} | ' + '${'Total %'.padRight(8)} | Function', ); print( '-----------------------------------------------------------------------', @@ -241,9 +244,20 @@ Future main(List arguments) async { final totalPct = sampleCount > 0 ? (totalCount * 100.0 / sampleCount) : 0.0; - final name = func.function?.name ?? func.resolvedUrl ?? 'Unknown'; + // `ProfileFunction.function` is `dynamic`: a FuncRef or NativeFunction. + final Object? function = func.function; + final name = + switch (function) { + FuncRef(:final name) => name, + NativeFunction(:final name) => name, + _ => null, + } ?? + func.resolvedUrl ?? + 'Unknown'; print( - '${pct.toStringAsFixed(1).padLeft(6)}% | ${count.toString().padLeft(8)} | ${totalPct.toStringAsFixed(1).padLeft(6)}% | $name', + '${pct.toStringAsFixed(1).padLeft(6)}% | ' + '${count.toString().padLeft(8)} | ' + '${totalPct.toStringAsFixed(1).padLeft(6)}% | $name', ); displayed++; } diff --git a/skills/profile-dart-code/scripts/test/profile_test.dart b/skills/profile-dart-code/scripts/test/profile_test.dart index f5a2b91..f06aaad 100644 --- a/skills/profile-dart-code/scripts/test/profile_test.dart +++ b/skills/profile-dart-code/scripts/test/profile_test.dart @@ -29,7 +29,8 @@ String _locateProfileScript() { } } throw StateError( - 'Could not locate profile.dart. Current directory: ${Directory.current.path}', + 'Could not locate profile.dart. ' + 'Current directory: ${Directory.current.path}', ); } diff --git a/tool/bin/readme.dart b/tool/bin/readme.dart index 228efcc..9787998 100644 --- a/tool/bin/readme.dart +++ b/tool/bin/readme.dart @@ -51,15 +51,15 @@ void main(List arguments) async { .toList() ..sort((a, b) => p.basename(a.path).compareTo(p.basename(b.path))); - final listBuffer = StringBuffer(); - listBuffer.writeln('\n'); - listBuffer.writeln('To install any skill individually:\n'); - listBuffer.writeln('```bash'); - listBuffer.writeln('npx skills add $repoSlug --skill '); - listBuffer.writeln('```\n'); - listBuffer.writeln(''); - listBuffer.writeln('| Skill | Description | Key Features |'); - listBuffer.writeln('|-------|-------------|--------------|'); + final listBuffer = StringBuffer() + ..writeln('\n') + ..writeln('To install any skill individually:\n') + ..writeln('```bash') + ..writeln('npx skills add $repoSlug --skill ') + ..writeln('```\n') + ..writeln('') + ..writeln('| Skill | Description | Key Features |') + ..writeln('|-------|-------------|--------------|'); for (final dir in skillDirs) { final skillName = p.basename(dir.path); final skillFile = File(p.join(dir.path, 'SKILL.md')); @@ -70,7 +70,7 @@ void main(List arguments) async { frontMatter['name']?.toString() ?? _getSkillTitle(content, skillName); final description = frontMatter['description']?.toString() ?? ''; final keyFeaturesRaw = frontMatter['key_features']; - final List keyFeatures = []; + final keyFeatures = []; if (keyFeaturesRaw is List) { keyFeatures.addAll(keyFeaturesRaw.map((e) => e.toString())); } else if (keyFeaturesRaw is String) { @@ -84,12 +84,12 @@ void main(List arguments) async { final cleanDescription = LineSplitter.split( description.trim(), - ).map((line) => line.trim()).join(' ').replaceAll('|', '\\|'); + ).map((line) => line.trim()).join(' ').replaceAll('|', r'\|'); final cleanFeatures = keyFeatures .map((f) => LineSplitter.split(f.trim()).map((l) => l.trim()).join(' ')) .join(', ') - .replaceAll('|', '\\|'); + .replaceAll('|', r'\|'); listBuffer.writeln( '| **[$title](skills/$skillName/SKILL.md)** | $cleanDescription | $cleanFeatures |', @@ -100,8 +100,8 @@ void main(List arguments) async { final generatedTable = listBuffer.toString(); final readmeContent = readmeFile.readAsStringSync(); - final startTag = ''; - final endTag = ''; + const startTag = ''; + const endTag = ''; final startIndex = readmeContent.indexOf(startTag); final endIndex = startIndex == -1 @@ -110,7 +110,8 @@ void main(List arguments) async { if (startIndex == -1 || endIndex == -1) { print( - 'Error: Could not find comments and in correct order in README.md', + 'Error: Could not find comments and ' + ' in correct order in README.md', ); exit(1); } diff --git a/tool/pubspec.yaml b/tool/pubspec.yaml index eca488b..816ef3d 100644 --- a/tool/pubspec.yaml +++ b/tool/pubspec.yaml @@ -1,16 +1,17 @@ name: _dash_skill_tool description: Helper tools for managing and validating skills. -version: 1.0.0 +version: 1.0.1-wip environment: sdk: ^3.9.0 resolution: workspace dependencies: - yaml: ^3.1.0 args: ^2.4.0 path: ^1.9.0 + yaml: ^3.1.0 dev_dependencies: + dart_flutter_team_lints: ^3.5.0 logging: ^1.3.0 skills_lint: ^0.5.2 test: ^1.24.0 diff --git a/tool/test/skills_evals_test.dart b/tool/test/skills_evals_test.dart index ba7cd5e..52048d2 100644 --- a/tool/test/skills_evals_test.dart +++ b/tool/test/skills_evals_test.dart @@ -4,18 +4,16 @@ import 'dart:io'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; -Directory _getRepoRoot() { - return Directory.current.path.endsWith('tool') - ? Directory.current.parent - : Directory.current; -} +Directory _getRepoRoot() => Directory.current.path.endsWith('tool') + ? Directory.current.parent + : Directory.current; List _findEvalsFiles(Directory baseDir) { if (!baseDir.existsSync()) { return []; } return baseDir.listSync(recursive: true).whereType().where((File f) { - final String name = p.basename(f.path); + final name = p.basename(f.path); return name == 'evals.json' || name.endsWith('_evals.json'); }).toList(); } @@ -28,11 +26,11 @@ void _verifyStructuralConsistency(List files, String itemsKey) { for (final file in files) { final Object? decoded = jsonDecode(file.readAsStringSync()); - final Map decodedMap = switch (decoded) { + final decodedMap = switch (decoded) { final Map map => map, _ => fail('${file.path} must be a JSON map.'), }; - final Set rootKeys = decodedMap.keys.toSet(); + final rootKeys = decodedMap.keys.toSet(); if (expectedRootKeys == null) { expectedRootKeys = rootKeys; expectedRootKeysFilePath = file.path; @@ -42,21 +40,22 @@ void _verifyStructuralConsistency(List files, String itemsKey) { equals(expectedRootKeys), reason: '${file.path} root keys do not match consistency pattern. ' - 'Expected keys to match the first processed file ($expectedRootKeysFilePath).', + 'Expected keys to match the first processed file ' + '($expectedRootKeysFilePath).', ); } final Object? itemsRaw = decodedMap[itemsKey]; - final List itemsList = switch (itemsRaw) { + final itemsList = switch (itemsRaw) { final List list => list, _ => fail('$itemsKey key in ${file.path} must be a List.'), }; for (final Object? item in itemsList) { - final Map itemMap = switch (item) { + final itemMap = switch (item) { final Map map => map, _ => fail('Item in $itemsKey list in ${file.path} must be a JSON map.'), }; - final Set itemKeys = itemMap.keys.toSet(); + final itemKeys = itemMap.keys.toSet(); if (expectedItemKeys == null) { expectedItemKeys = itemKeys; expectedItemFilePath = file.path; @@ -66,7 +65,8 @@ void _verifyStructuralConsistency(List files, String itemsKey) { equals(expectedItemKeys), reason: 'Item in ${file.path} keys do not match consistency pattern. ' - 'Expected item keys to match the first processed file ($expectedItemFilePath).', + 'Expected item keys to match the first processed file ' + '($expectedItemFilePath).', ); } } @@ -79,7 +79,7 @@ void main() { 'all evals.json files across skills share consistent structure and keys', () { final repoRoot = _getRepoRoot(); - final List evalsFiles = [ + final evalsFiles = [ ..._findEvalsFiles(Directory(p.join(repoRoot.path, 'skills'))), ..._findEvalsFiles(Directory(p.join(repoRoot.path, 'evals'))), ]..sort((a, b) => a.path.compareTo(b.path)); @@ -102,7 +102,7 @@ void main() { return; } - final List skillDirsWithEvals = skillsDir + final skillDirsWithEvals = skillsDir .listSync() .whereType() .where((dir) => Directory(p.join(dir.path, 'evals')).existsSync()) @@ -120,7 +120,9 @@ void main() { evalsFile.existsSync(), isTrue, reason: - 'Published skill "${p.basename(skillDir.path)}" has an evals directory but is missing an evals.json file at ${evalsFile.path}', + 'Published skill "${p.basename(skillDir.path)}" has an evals ' + 'directory but is missing an evals.json file at ' + '${evalsFile.path}', ); } }); @@ -129,7 +131,7 @@ void main() { 'all referenced rubrics in evals.json exist and have valid structure', () { final repoRoot = _getRepoRoot(); - final List evalsFiles = [ + final evalsFiles = [ ..._findEvalsFiles(Directory(p.join(repoRoot.path, 'skills'))), ..._findEvalsFiles(Directory(p.join(repoRoot.path, 'evals'))), ]; @@ -138,7 +140,7 @@ void main() { for (final file in evalsFiles) { final Object? decoded = jsonDecode(file.readAsStringSync()); - final Map decodedMap = switch (decoded) { + final decodedMap = switch (decoded) { final Map map => map, _ => fail('${file.path} must be a JSON map.'), }; @@ -148,7 +150,7 @@ void main() { continue; } - final List repoCriteriaList = switch (repoCriteriaRaw) { + final repoCriteriaList = switch (repoCriteriaRaw) { final List list => list, _ => fail('repo_criteria in ${file.path} must be a List.'), }; @@ -162,13 +164,14 @@ void main() { rubricFile.existsSync(), isTrue, reason: - 'Referenced rubric "$rubricPath" in ${file.path} does not exist at ${rubricFile.path}', + 'Referenced rubric "$rubricPath" in ${file.path} does not ' + 'exist at ${rubricFile.path}', ); final Object? rubricDecoded = jsonDecode( rubricFile.readAsStringSync(), ); - final Map rubricMap = switch (rubricDecoded) { + final rubricMap = switch (rubricDecoded) { final Map map => map, _ => fail('${rubricFile.path} must be a JSON map.'), }; @@ -191,7 +194,7 @@ void main() { return; } - final List rubricFiles = + final rubricFiles = rubricsDir .listSync() .whereType() diff --git a/tool/test/validate_skills_test.dart b/tool/test/validate_skills_test.dart index 1b8034c..b293bf1 100644 --- a/tool/test/validate_skills_test.dart +++ b/tool/test/validate_skills_test.dart @@ -17,9 +17,7 @@ void main() { }); try { - final Configuration config = await ConfigParser.loadConfig( - path: _configFilePath, - ); + final config = await ConfigParser.loadConfig(path: _configFilePath); final isValid = await validateSkills(config: config); expect( isValid, @@ -66,7 +64,7 @@ void main() { await process.shouldExit(0); } } - }, timeout: Timeout(Duration(minutes: 3))); + }, timeout: const Timeout(Duration(minutes: 3))); test('Verify formatting and analysis of all skills Dart code', () async { final skillsDir = Directory( @@ -86,7 +84,8 @@ void main() { ]); await formatProcess.shouldExit(0); - // Ensure pub get has been run for all nested packages to prevent analysis failures + // Ensure pub get has been run for all nested packages to prevent + // analysis failures final pubspecs = []; for (final dir in skillsDir.listSync().whereType().where( (dir) => File('${dir.path}/SKILL.md').existsSync(), @@ -118,5 +117,5 @@ void main() { ['analyze', '--fatal-infos', skillsDir.path], ); await analyzeProcess.shouldExit(0); - }, timeout: Timeout(Duration(minutes: 3))); + }, timeout: const Timeout(Duration(minutes: 3))); }