sdd-full 4.8.0 → 4.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/bin.js +1 -1
  2. package/index.js +18 -12
  3. package/package.json +1 -1
  4. package/skills/VERSION.md +61 -175
  5. package/skills/design-planning/ai-coding-rules/SKILL.md +13 -5
  6. package/skills/design-planning/design-to-code/SKILL.md +14 -5
  7. package/skills/design-planning/enterprise-spec/SKILL.md +13 -5
  8. package/skills/design-planning/flutter-av/SKILL.md +16 -5
  9. package/skills/design-planning/flutter-map/SKILL.md +14 -5
  10. package/skills/design-planning/function-sdd/SKILL.md +13 -5
  11. package/skills/design-planning/global-overlay-stack-standard/SKILL.md +14 -4
  12. package/skills/design-planning/ui-motion-interaction-standard/SKILL.md +14 -4
  13. package/skills/design-planning/ui-sdd-specialized/SKILL.md +14 -5
  14. package/skills/development-execution/flutter-errors/SKILL.md +15 -5
  15. package/skills/quality-assurance/bdd-acceptance/SKILL.md +14 -5
  16. package/skills/quality-assurance/flutter-test/SKILL.md +16 -5
  17. package/skills/requirement-analysis/sdd/mock_sdd.md +156 -0
  18. package/skills/rules/project_rules.md +127 -538
  19. package/skills/rules/user_rules.md +263 -0
  20. package/skills/special-tools/env-check/SKILL.md +13 -5
  21. package/skills/special-tools/ios-full-auto-debug/SKILL.md +15 -5
  22. package/skills/flutter-skills/.github/dependabot.yaml +0 -15
  23. package/skills/flutter-skills/.github/workflows/dart_skills_lint_workflow.yaml +0 -68
  24. package/skills/flutter-skills/.github/workflows/skills_tool.yaml +0 -51
  25. package/skills/flutter-skills/CODE_OF_CONDUCT.md +0 -3
  26. package/skills/flutter-skills/CONTRIBUTING.md +0 -36
  27. package/skills/flutter-skills/LICENSE +0 -26
  28. package/skills/flutter-skills/README.md +0 -50
  29. package/skills/flutter-skills/pubspec.yaml +0 -9
  30. package/skills/flutter-skills/resources/flutter_skills.yaml +0 -434
  31. package/skills/flutter-skills/skills/flutter-add-integration-test/SKILL.md +0 -163
  32. package/skills/flutter-skills/skills/flutter-add-widget-preview/SKILL.md +0 -145
  33. package/skills/flutter-skills/skills/flutter-add-widget-test/SKILL.md +0 -154
  34. package/skills/flutter-skills/skills/flutter-apply-architecture-best-practices/SKILL.md +0 -162
  35. package/skills/flutter-skills/skills/flutter-build-responsive-layout/SKILL.md +0 -139
  36. package/skills/flutter-skills/skills/flutter-fix-layout-issues/SKILL.md +0 -130
  37. package/skills/flutter-skills/skills/flutter-implement-json-serialization/SKILL.md +0 -153
  38. package/skills/flutter-skills/skills/flutter-setup-declarative-routing/SKILL.md +0 -255
  39. package/skills/flutter-skills/skills/flutter-setup-localization/SKILL.md +0 -210
  40. package/skills/flutter-skills/skills/flutter-use-http-package/SKILL.md +0 -175
  41. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/add-dart-lint-validation-rule/SKILL.md +0 -196
  42. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-best-practices/SKILL.md +0 -65
  43. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-checks-migration/SKILL.md +0 -158
  44. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-cli-app-best-practices/SKILL.md +0 -168
  45. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-doc-validation/SKILL.md +0 -87
  46. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-long-lines/SKILL.md +0 -101
  47. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-matcher-best-practices/SKILL.md +0 -136
  48. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-modern-features/SKILL.md +0 -266
  49. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-package-maintenance/SKILL.md +0 -92
  50. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/SKILL.md +0 -92
  51. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/lib/src/calculator.dart +0 -7
  52. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/pubspec.yaml +0 -8
  53. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/test/calculator_test.dart +0 -11
  54. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/interpret_coverage.dart +0 -95
  55. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/pubspec.yaml +0 -6
  56. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/test/interpret_coverage_test.dart +0 -93
  57. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-fundamentals/SKILL.md +0 -173
  58. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/definition-of-done/SKILL.md +0 -27
  59. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/flutter_skills_ignore.json +0 -3
  60. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/grill-me/SKILL.md +0 -10
  61. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/ignore.json +0 -3
  62. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/test-driven-development/SKILL.md +0 -371
  63. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/test-driven-development/testing-anti-patterns.md +0 -299
  64. package/skills/flutter-skills/tool/dart_skills_lint/AUTHORS +0 -7
  65. package/skills/flutter-skills/tool/dart_skills_lint/CHANGELOG.md +0 -12
  66. package/skills/flutter-skills/tool/dart_skills_lint/CONTRIBUTING.md +0 -51
  67. package/skills/flutter-skills/tool/dart_skills_lint/LICENSE +0 -27
  68. package/skills/flutter-skills/tool/dart_skills_lint/README.md +0 -203
  69. package/skills/flutter-skills/tool/dart_skills_lint/analysis_options.yaml +0 -296
  70. package/skills/flutter-skills/tool/dart_skills_lint/bench/README.md +0 -23
  71. package/skills/flutter-skills/tool/dart_skills_lint/bench/baseline_throughput.dart +0 -230
  72. package/skills/flutter-skills/tool/dart_skills_lint/bin/cli.dart +0 -10
  73. package/skills/flutter-skills/tool/dart_skills_lint/dart_skills_lint.yaml +0 -14
  74. package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/PRODUCTION_READYNESS.md +0 -48
  75. package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/completion_migration_plan.md +0 -99
  76. package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/legacy_patterns_report.md +0 -110
  77. package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/pub_vs_skill_report.md +0 -56
  78. package/skills/flutter-skills/tool/dart_skills_lint/documentation/knowledge/SPECIFICATION.md +0 -79
  79. package/skills/flutter-skills/tool/dart_skills_lint/documentation/knowledge/architecture_overview.md +0 -64
  80. package/skills/flutter-skills/tool/dart_skills_lint/lib/dart_skills_lint.dart +0 -11
  81. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/config_parser.dart +0 -156
  82. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/entry_point.dart +0 -354
  83. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/fixable_rule.dart +0 -20
  84. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/analysis_severity.dart +0 -15
  85. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/check_type.dart +0 -17
  86. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/ignore_entry.dart +0 -34
  87. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/ignore_entry.g.dart +0 -19
  88. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skill_context.dart +0 -27
  89. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skill_rule.dart +0 -27
  90. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skills_ignores.dart +0 -26
  91. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skills_ignores.g.dart +0 -24
  92. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/validation_error.dart +0 -31
  93. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rule_registry.dart +0 -79
  94. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/absolute_paths_rule.dart +0 -74
  95. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/description_length_rule.dart +0 -49
  96. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/disallowed_field_rule.dart +0 -61
  97. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/name_format_rule.dart +0 -167
  98. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/relative_paths_rule.dart +0 -72
  99. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/trailing_whitespace_rule.dart +0 -93
  100. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/valid_yaml_metadata_rule.dart +0 -74
  101. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/skills_ignores_storage.dart +0 -36
  102. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/validation_session.dart +0 -559
  103. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/validator.dart +0 -238
  104. package/skills/flutter-skills/tool/dart_skills_lint/pubspec.yaml +0 -28
  105. package/skills/flutter-skills/tool/dart_skills_lint/skills/README.md +0 -10
  106. package/skills/flutter-skills/tool/dart_skills_lint/skills/dart-skills-lint-validation/SKILL.md +0 -195
  107. package/skills/flutter-skills/tool/dart_skills_lint/skills-lock.json +0 -75
  108. package/skills/flutter-skills/tool/dart_skills_lint/test/absolute_paths_test.dart +0 -167
  109. package/skills/flutter-skills/tool/dart_skills_lint/test/cli_integration_test.dart +0 -683
  110. package/skills/flutter-skills/tool/dart_skills_lint/test/config_file_test.dart +0 -292
  111. package/skills/flutter-skills/tool/dart_skills_lint/test/custom_rule_test.dart +0 -122
  112. package/skills/flutter-skills/tool/dart_skills_lint/test/directory_structure_test.dart +0 -163
  113. package/skills/flutter-skills/tool/dart_skills_lint/test/field_constraints_test.dart +0 -178
  114. package/skills/flutter-skills/tool/dart_skills_lint/test/fixer_test.dart +0 -172
  115. package/skills/flutter-skills/tool/dart_skills_lint/test/ignore_models_test.dart +0 -63
  116. package/skills/flutter-skills/tool/dart_skills_lint/test/metadata_validation_test.dart +0 -116
  117. package/skills/flutter-skills/tool/dart_skills_lint/test/relative_path_flag_test.dart +0 -70
  118. package/skills/flutter-skills/tool/dart_skills_lint/test/relative_paths_test.dart +0 -172
  119. package/skills/flutter-skills/tool/dart_skills_lint/test/resolve_rules_test.dart +0 -82
  120. package/skills/flutter-skills/tool/dart_skills_lint/test/rule_naming_test.dart +0 -29
  121. package/skills/flutter-skills/tool/dart_skills_lint/test/skills_ignores_storage_test.dart +0 -89
  122. package/skills/flutter-skills/tool/dart_skills_lint/test/test_utils.dart +0 -19
  123. package/skills/flutter-skills/tool/dart_skills_lint/test/trailing_whitespace_test.dart +0 -152
  124. package/skills/flutter-skills/tool/generator/README.md +0 -150
  125. package/skills/flutter-skills/tool/generator/analysis_options.yaml +0 -143
  126. package/skills/flutter-skills/tool/generator/bin/skills.dart +0 -73
  127. package/skills/flutter-skills/tool/generator/lib/src/commands/base_skill_command.dart +0 -87
  128. package/skills/flutter-skills/tool/generator/lib/src/commands/base_yaml_command.dart +0 -83
  129. package/skills/flutter-skills/tool/generator/lib/src/commands/generate_skill_command.dart +0 -92
  130. package/skills/flutter-skills/tool/generator/lib/src/commands/update_readme_command.dart +0 -150
  131. package/skills/flutter-skills/tool/generator/lib/src/commands/update_skill_command.dart +0 -97
  132. package/skills/flutter-skills/tool/generator/lib/src/commands/validate_skill_command.dart +0 -284
  133. package/skills/flutter-skills/tool/generator/lib/src/models/skill_params.dart +0 -41
  134. package/skills/flutter-skills/tool/generator/lib/src/services/gemini_service.dart +0 -310
  135. package/skills/flutter-skills/tool/generator/lib/src/services/markdown_converter.dart +0 -226
  136. package/skills/flutter-skills/tool/generator/lib/src/services/prompts.dart +0 -72
  137. package/skills/flutter-skills/tool/generator/lib/src/services/resource_fetcher_service.dart +0 -84
  138. package/skills/flutter-skills/tool/generator/lib/src/services/skill_instructions.dart +0 -30
  139. package/skills/flutter-skills/tool/generator/pubspec.yaml +0 -32
  140. package/skills/flutter-skills/tool/generator/test/commands/base_skill_command_test.dart +0 -131
  141. package/skills/flutter-skills/tool/generator/test/commands/validate_skills_input_test.dart +0 -263
  142. package/skills/flutter-skills/tool/generator/test/custom_skill_rules/last_modified_rule.dart +0 -32
  143. package/skills/flutter-skills/tool/generator/test/generate_skills_retry_test.dart +0 -105
  144. package/skills/flutter-skills/tool/generator/test/generate_skills_test.dart +0 -519
  145. package/skills/flutter-skills/tool/generator/test/lint_skills_test.dart +0 -34
  146. package/skills/flutter-skills/tool/generator/test/markdown_converter_test.dart +0 -103
  147. package/skills/flutter-skills/tool/generator/test/markdown_table_test.dart +0 -131
  148. package/skills/flutter-skills/tool/generator/test/models/skill_params_test.dart +0 -37
  149. package/skills/flutter-skills/tool/generator/test/services/gemini_service_test.dart +0 -291
  150. package/skills/flutter-skills/tool/generator/test/services/markdown_converter_test.dart +0 -156
  151. package/skills/flutter-skills/tool/generator/test/services/resource_fetcher_service_test.dart +0 -188
  152. package/skills/flutter-skills/tool/generator/test/update_skills_test.dart +0 -241
  153. package/skills/flutter-skills/tool/generator/test/validate_skills_test.dart +0 -728
  154. /package/skills/{.agents → flutter}/skills/flutter-add-integration-test/SKILL.md +0 -0
  155. /package/skills/{.agents → flutter}/skills/flutter-add-widget-preview/SKILL.md +0 -0
  156. /package/skills/{.agents → flutter}/skills/flutter-add-widget-test/SKILL.md +0 -0
  157. /package/skills/{.agents → flutter}/skills/flutter-apply-architecture-best-practices/SKILL.md +0 -0
  158. /package/skills/{.agents → flutter}/skills/flutter-build-responsive-layout/SKILL.md +0 -0
  159. /package/skills/{.agents → flutter}/skills/flutter-fix-layout-issues/SKILL.md +0 -0
  160. /package/skills/{.agents → flutter}/skills/flutter-implement-json-serialization/SKILL.md +0 -0
  161. /package/skills/{.agents → flutter}/skills/flutter-setup-declarative-routing/SKILL.md +0 -0
  162. /package/skills/{.agents → flutter}/skills/flutter-setup-localization/SKILL.md +0 -0
  163. /package/skills/{.agents → flutter}/skills/flutter-use-http-package/SKILL.md +0 -0
@@ -1,683 +0,0 @@
1
- // Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
2
- // for details. All rights reserved. Use of this source code is governed by a
3
- // BSD-style license that can be found in the LICENSE file.
4
-
5
- import 'dart:convert';
6
- import 'dart:io';
7
-
8
- import 'package:dart_skills_lint/src/entry_point.dart';
9
- import 'package:dart_skills_lint/src/models/check_type.dart';
10
- import 'package:dart_skills_lint/src/models/ignore_entry.dart';
11
- import 'package:dart_skills_lint/src/models/skills_ignores.dart';
12
- import 'package:dart_skills_lint/src/rule_registry.dart';
13
- import 'package:dart_skills_lint/src/validator.dart';
14
- import 'package:path/path.dart' as p;
15
- import 'package:test/test.dart';
16
- import 'package:test_process/test_process.dart';
17
-
18
- import 'test_utils.dart';
19
-
20
- void main() {
21
- group('CLI Integration', () {
22
- late Directory tempDir;
23
-
24
- setUp(() async {
25
- tempDir = await Directory.systemTemp.createTemp('cli_test.');
26
- });
27
-
28
- tearDown(() async {
29
- if (tempDir.existsSync()) {
30
- await tempDir.delete(recursive: true);
31
- }
32
- });
33
-
34
- test('de-duplicates baseline entries for multiple identical rule failures', () async {
35
- final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
36
- await File('${skillDir.path}/SKILL.md').writeAsString(
37
- '${buildFrontmatter(name: 'test-skill')}[Link 1](missing1.md)\n[Link 2](missing2.md)\n',
38
- );
39
-
40
- // Run with --generate-baseline
41
- final TestProcess process = await TestProcess.start('dart', [
42
- 'bin/cli.dart',
43
- '-s',
44
- skillDir.path,
45
- '--generate-baseline',
46
- ]);
47
- await process.shouldExit(0);
48
-
49
- final ignoreFile = File('${skillDir.parent.path}/$defaultIgnoreFileName');
50
- expect(ignoreFile.existsSync(), isTrue);
51
-
52
- final String content = await ignoreFile.readAsString();
53
- final json = jsonDecode(content) as Map<String, dynamic>;
54
- final skills = json[SkillsIgnores.skillsKey] as Map<String, dynamic>;
55
- final ignores = skills['test-skill'] as List;
56
-
57
- // Should be 1 entry only! Both relative link failures utilize the same ruleId/fileName de-duplication.
58
- expect(ignores.length, equals(1));
59
- });
60
-
61
- test('individual skill baseline is loaded on subsequent runs', () async {
62
- final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
63
- await File(
64
- '${skillDir.path}/SKILL.md',
65
- ).writeAsString('${buildFrontmatter(name: 'test-skill')}[Link](missing.md)\n');
66
-
67
- final TestProcess genProcess = await TestProcess.start('dart', [
68
- 'bin/cli.dart',
69
- '-s',
70
- skillDir.path,
71
- '--generate-baseline',
72
- ]);
73
- await genProcess.shouldExit(0);
74
-
75
- final ignoreFile = File('${tempDir.path}/$defaultIgnoreFileName');
76
- expect(ignoreFile.existsSync(), isTrue);
77
-
78
- final TestProcess runProcess = await TestProcess.start('dart', [
79
- 'bin/cli.dart',
80
- '-s',
81
- skillDir.path,
82
- ]);
83
- await runProcess.shouldExit(0);
84
- });
85
-
86
- test(
87
- 'cross-skill baseline de-duplicates and suppresses all errors across different skills',
88
- () async {
89
- final Directory skillsDir = await Directory('${tempDir.path}/skills').create();
90
-
91
- // Create skill-one with a broken link
92
- final Directory skill1Dir = await Directory('${skillsDir.path}/skill-one').create();
93
- await File('${skill1Dir.path}/SKILL.md').writeAsString(
94
- '${buildFrontmatter(name: 'skill-one', description: 'Skill one with a broken link')}[Link to nowhere](../nowhere/SKILL.md)\n',
95
- );
96
-
97
- // Create skill-two with a broken link
98
- final Directory skill2Dir = await Directory('${skillsDir.path}/skill-two').create();
99
- await File('${skill2Dir.path}/SKILL.md').writeAsString(
100
- '${buildFrontmatter(name: 'skill-two', description: 'Skill two with a broken link')}[Link to nowhere](../nowhere/SKILL.md)\n',
101
- );
102
-
103
- final configFile = File('${tempDir.path}/dart_skills_lint.yaml');
104
- await configFile.writeAsString('''
105
- dart_skills_lint:
106
- directories:
107
- - path: "skills"
108
- rules:
109
- check-relative-paths: error
110
- ignore_file: "$defaultIgnoreFileName"
111
- ''');
112
-
113
- // 1. Run with --generate-baseline. It should evaluate all skills and write both to the baseline!
114
- final TestProcess genProcess = await TestProcess.start('dart', [
115
- p.normalize(p.absolute('bin/cli.dart')),
116
- '-d',
117
- 'skills',
118
- '--generate-baseline',
119
- ], workingDirectory: tempDir.path);
120
- await genProcess.shouldExit(0); // Exits 0 if --generate-baseline is passed
121
-
122
- final ignoreFile = File('${tempDir.path}/$defaultIgnoreFileName');
123
- expect(ignoreFile.existsSync(), isTrue);
124
-
125
- final String content = await ignoreFile.readAsString();
126
- final json = jsonDecode(content) as Map<String, dynamic>;
127
- final skills = json[SkillsIgnores.skillsKey] as Map<String, dynamic>;
128
-
129
- expect(skills.containsKey('skill-one'), isTrue);
130
- expect(skills.containsKey('skill-two'), isTrue);
131
-
132
- // 2. Run again silently. It should succeed with exit 0 because all errors are ignored!
133
- final TestProcess runProcess = await TestProcess.start('dart', [
134
- p.normalize(p.absolute('bin/cli.dart')),
135
- '-d',
136
- 'skills',
137
- '-q',
138
- ], workingDirectory: tempDir.path);
139
- await runProcess.shouldExit(0);
140
- },
141
- );
142
-
143
- test('exits with 0 and success message for valid skill', () async {
144
- final Directory skillDir = await Directory('${tempDir.path}/valid-skill').create();
145
- await File(
146
- '${skillDir.path}/SKILL.md',
147
- ).writeAsString('${buildFrontmatter(name: 'valid-skill', description: 'A valid skill')}Body');
148
-
149
- final TestProcess process = await TestProcess.start('dart', [
150
- 'bin/cli.dart',
151
- '-s',
152
- skillDir.path,
153
- ]);
154
-
155
- final List<String> stdout = await process.stdout.rest.toList();
156
- expect(stdout.join('\n'), contains(skillIsValidMsg));
157
- await process.shouldExit(0);
158
- });
159
-
160
- test('exits with 1 and error message for invalid skill', () async {
161
- final Directory skillDir = await Directory('${tempDir.path}/invalid-skill').create();
162
- // SKILL.md is missing
163
-
164
- final TestProcess process = await TestProcess.start('dart', [
165
- 'bin/cli.dart',
166
- '-s',
167
- skillDir.path,
168
- ]);
169
-
170
- final List<String> stderr = await process.stderr.rest.toList();
171
- final String stderrStr = stderr.join('\n');
172
- expect(stderrStr, contains(skillIsInvalidMsg));
173
- expect(stderrStr, contains('SKILL.md is missing'));
174
- await process.shouldExit(1);
175
- });
176
-
177
- test('exits with 0 and validates subdirectories if named "skills"', () async {
178
- final Directory skillsDir = await Directory('${tempDir.path}/skills').create();
179
- final Directory skill1 = await Directory('${skillsDir.path}/skill-a').create();
180
- await File(
181
- '${skill1.path}/SKILL.md',
182
- ).writeAsString('${buildFrontmatter(name: 'skill-a', description: 'Skill A')}Body');
183
-
184
- final Directory skill2 = await Directory('${skillsDir.path}/skill-b').create();
185
- await File(
186
- '${skill2.path}/SKILL.md',
187
- ).writeAsString('${buildFrontmatter(name: 'skill-b', description: 'Skill B')}Body');
188
-
189
- final TestProcess process = await TestProcess.start('dart', [
190
- 'bin/cli.dart',
191
- '-d',
192
- skillsDir.path,
193
- ]);
194
-
195
- // Verify outputs for both skills (sorted order)
196
- final List<String> stdout = await process.stdout.rest.toList();
197
- final String stdoutStr = stdout.join('\n');
198
- expect(stdoutStr, contains(evaluatingDirMsg));
199
- expect(stdoutStr, contains('--- Validating skill: skill-a ---'));
200
- expect(stdoutStr, contains(skillIsValidMsg));
201
-
202
- expect(stdoutStr, contains('--- Validating skill: skill-b ---'));
203
- expect(stdoutStr, contains(skillIsValidMsg));
204
-
205
- await process.shouldExit(0);
206
- });
207
-
208
- test('ignores subdirectories starting with a dot "." in "skills" folder', () async {
209
- final Directory skillsDir = await Directory('${tempDir.path}/skills').create();
210
- final Directory skill1 = await Directory('${skillsDir.path}/skill-a').create();
211
- await File(
212
- '${skill1.path}/SKILL.md',
213
- ).writeAsString('${buildFrontmatter(name: 'skill-a', description: 'Skill A')}Body');
214
-
215
- await Directory('${skillsDir.path}/.dart_tool').create();
216
-
217
- final TestProcess process = await TestProcess.start('dart', [
218
- 'bin/cli.dart',
219
- '-d',
220
- skillsDir.path,
221
- ]);
222
-
223
- final List<String> stdout = await process.stdout.rest.toList();
224
- final String stdoutStr = stdout.join('\n');
225
- expect(stdoutStr, contains('--- Validating skill: skill-a ---'));
226
- expect(stdoutStr, contains(skillIsValidMsg));
227
- expect(stdoutStr, isNot(contains('.dart_tool')));
228
-
229
- await process.shouldExit(0);
230
- });
231
-
232
- test('exits with 1 if any subdirectory skill fails in "skills" folder', () async {
233
- final Directory skillsDir = await Directory('${tempDir.path}/skills').create();
234
- final Directory skill1 = await Directory('${skillsDir.path}/skill-a').create();
235
- await File(
236
- '${skill1.path}/SKILL.md',
237
- ).writeAsString('${buildFrontmatter(name: 'skill-a', description: 'Skill A')}Body');
238
-
239
- await Directory('${skillsDir.path}/skill-b').create(); // No SKILL.md
240
-
241
- final TestProcess process = await TestProcess.start('dart', [
242
- 'bin/cli.dart',
243
- '-d',
244
- skillsDir.path,
245
- ]);
246
-
247
- // Verify outputs
248
- final List<String> stdout = await process.stdout.rest.toList();
249
- expect(stdout.join('\n'), contains('--- Validating skill: skill-a ---'));
250
- expect(stdout.join('\n'), contains(skillIsValidMsg));
251
-
252
- expect(stdout.join('\n'), contains('--- Validating skill: skill-b ---'));
253
- final List<String> stderr = await process.stderr.rest.toList();
254
- expect(stderr.join('\n'), contains(skillIsInvalidMsg));
255
- await process.shouldExit(1);
256
- });
257
-
258
- test(
259
- 'exits with 1 early and does not process subsequent skills if --fast-fail is passed',
260
- () async {
261
- final Directory skillsDir = await Directory('${tempDir.path}/skills').create();
262
-
263
- await Directory('${skillsDir.path}/skill-a').create();
264
- // skill-a does not create SKILL.md, so it is invalid and will fail first (sorted order)
265
-
266
- await Directory('${skillsDir.path}/skill-b').create();
267
- await File(
268
- '${p.join(tempDir.path, 'skills', 'skill-b')}/SKILL.md',
269
- ).writeAsString('${buildFrontmatter(name: 'skill-b', description: 'Skill B')}Body');
270
-
271
- final TestProcess process = await TestProcess.start('dart', [
272
- 'bin/cli.dart',
273
- '-d',
274
- skillsDir.path,
275
- '--fast-fail',
276
- ]);
277
-
278
- // Verify outputs for skill-a
279
- final List<String> stdout = await process.stdout.rest.toList();
280
- final String stdoutStr = stdout.join('\n');
281
- expect(stdoutStr, contains(evaluatingDirMsg));
282
- expect(stdoutStr, contains('--- Validating skill: skill-a ---'));
283
-
284
- final List<String> stderr = await process.stderr.rest.toList();
285
- expect(stderr.join('\n'), contains(skillIsInvalidMsg));
286
-
287
- // Since process exits after skill-a, stdout should be closed and no further lines (like skill-b) should appear.
288
- await process.shouldExit(1);
289
- },
290
- );
291
-
292
- test('exits with 0 and suppresses success messages if --quiet is passed', () async {
293
- final Directory skillDir = await Directory('${tempDir.path}/valid-skill').create();
294
- await File(
295
- '${skillDir.path}/SKILL.md',
296
- ).writeAsString('${buildFrontmatter(name: 'valid-skill', description: 'A valid skill')}Body');
297
-
298
- final TestProcess process = await TestProcess.start('dart', [
299
- 'bin/cli.dart',
300
- '-s',
301
- skillDir.path,
302
- '--quiet',
303
- ]);
304
-
305
- await process.shouldExit(0);
306
-
307
- // Stdout should be empty for a valid skill in quiet mode
308
- final List<String> rest = await process.stdout.rest.toList();
309
- expect(rest, isEmpty);
310
- });
311
- test('fails with 64 when no flags passed and both defaults are missing', () async {
312
- final TestProcess process = await TestProcess.start('dart', [
313
- p.normalize(p.absolute('bin/cli.dart')),
314
- ], workingDirectory: tempDir.path);
315
-
316
- final List<String> stderr = await process.stderr.rest.toList();
317
- expect(stderr.join('\n'), contains('Missing skills directory. Checked defaults:'));
318
- await process.shouldExit(64);
319
- });
320
-
321
- test('picks up .claude/skills when no flags passed and it exists', () async {
322
- final Directory claudeDir = await Directory(
323
- '${tempDir.path}/.claude/skills',
324
- ).create(recursive: true);
325
- final Directory skillDir = await Directory('${claudeDir.path}/valid-skill').create();
326
- await File(
327
- '${skillDir.path}/SKILL.md',
328
- ).writeAsString('${buildFrontmatter(name: 'valid-skill', description: 'A valid skill')}Body');
329
-
330
- final TestProcess process = await TestProcess.start('dart', [
331
- p.normalize(p.absolute('bin/cli.dart')),
332
- ], workingDirectory: tempDir.path);
333
-
334
- final List<String> stdout = await process.stdout.rest.toList();
335
- expect(stdout.join('\n'), contains('Skill is valid.'));
336
- await process.shouldExit(0);
337
- });
338
- test('expands ~/ to HOME environment variable', () async {
339
- final Directory skillDir = await Directory('${tempDir.path}/some-skill').create();
340
- await File(
341
- '${skillDir.path}/SKILL.md',
342
- ).writeAsString('${buildFrontmatter(name: 'some-skill')}Body');
343
-
344
- final TestProcess process = await TestProcess.start(
345
- 'dart',
346
- [p.normalize(p.absolute('bin/cli.dart')), '-s', '~/some-skill'],
347
- environment: {'HOME': tempDir.path},
348
- );
349
-
350
- final List<String> stdout = await process.stdout.rest.toList();
351
- expect(stdout.join('\n'), contains('Skill is valid.'));
352
- await process.shouldExit(0);
353
- });
354
-
355
- test('overrides valid-yaml-metadata flag to disabled', () async {
356
- final Directory skillDir = await Directory('${tempDir.path}/invalid-yaml').create();
357
- await File('${skillDir.path}/SKILL.md').writeAsString('Invalid YAML No Frontmatter');
358
-
359
- // 1. Run normally. Should fail because valid-yaml-metadata defaults to true (error).
360
- final TestProcess process = await TestProcess.start('dart', [
361
- 'bin/cli.dart',
362
- '-s',
363
- skillDir.path,
364
- ]);
365
- await process.shouldExit(1);
366
-
367
- // 2. Run with --no-valid-yaml-metadata. Should pass because the check is disabled!
368
- final TestProcess noYamlProcess = await TestProcess.start('dart', [
369
- 'bin/cli.dart',
370
- '-s',
371
- skillDir.path,
372
- '--no-valid-yaml-metadata',
373
- ]);
374
- await noYamlProcess.shouldExit(0);
375
- });
376
-
377
- test('fails if -d specifies a directory with zero skills', () async {
378
- final Directory emptyDir = await Directory('${tempDir.path}/empty-root').create();
379
-
380
- final TestProcess process = await TestProcess.start('dart', [
381
- 'bin/cli.dart',
382
- '-d',
383
- emptyDir.path,
384
- ]);
385
-
386
- await process.shouldExit(1);
387
- final List<String> stderr = await process.stderr.rest.toList();
388
- expect(
389
- stderr.join('\n'),
390
- contains('No skills found to validate in the specified directories.'),
391
- );
392
- });
393
-
394
- test('fails if -d specifies a single skill directory (no sub-folders found)', () async {
395
- final Directory skillAsRoot = await Directory('${tempDir.path}/single-skill-root').create();
396
- await File('${skillAsRoot.path}/SKILL.md').writeAsString(
397
- '${buildFrontmatter(name: 'single-skill-root', description: 'Not a root, but a skill folder.')}Body',
398
- );
399
-
400
- final TestProcess process = await TestProcess.start('dart', [
401
- 'bin/cli.dart',
402
- '-d',
403
- skillAsRoot.path,
404
- ]);
405
-
406
- await process.shouldExit(1);
407
- final List<String> stderr = await process.stderr.rest.toList();
408
- expect(
409
- stderr.join('\n'),
410
- contains(
411
- 'appears to be an individual skill. Use --skill / -s instead of -d / --skills-directory.',
412
- ),
413
- );
414
- });
415
-
416
- test('validates multiple skills with multiple -s flags', () async {
417
- final Directory skill1 = await Directory('${tempDir.path}/skill-1').create();
418
- await File(
419
- '${skill1.path}/SKILL.md',
420
- ).writeAsString('${buildFrontmatter(name: 'skill-1', description: 'Skill 1')}Body');
421
-
422
- final Directory skill2 = await Directory('${tempDir.path}/skill-2').create();
423
- await File(
424
- '${skill2.path}/SKILL.md',
425
- ).writeAsString('${buildFrontmatter(name: 'skill-2', description: 'Skill 2')}Body');
426
-
427
- final TestProcess process = await TestProcess.start('dart', [
428
- 'bin/cli.dart',
429
- '-s',
430
- skill1.path,
431
- '-s',
432
- skill2.path,
433
- ]);
434
-
435
- await process.shouldExit(0);
436
- final List<String> stdout = await process.stdout.rest.toList();
437
- final String stdoutStr = stdout.join('\n');
438
- expect(stdoutStr, contains('--- Validating skill: skill-1 ---'));
439
- expect(stdoutStr, contains('--- Validating skill: skill-2 ---'));
440
- });
441
-
442
- test('handles malformed JSON ignore-file gracefully by falling back', () async {
443
- final malformedFile = File('${tempDir.path}/malformed.json');
444
- await malformedFile.writeAsString('{ malformed json }');
445
-
446
- final Directory skillFolder = await Directory('${tempDir.path}/skill-x').create();
447
- await File(
448
- '${skillFolder.path}/SKILL.md',
449
- ).writeAsString('${buildFrontmatter(name: 'skill-x', description: 'Valid skill')}Body');
450
-
451
- final TestProcess process = await TestProcess.start('dart', [
452
- 'bin/cli.dart',
453
- '-s',
454
- skillFolder.path,
455
- '--ignore-file',
456
- malformedFile.path,
457
- ]);
458
-
459
- await process.shouldExit(0); // Valid skill should still pass
460
- final List<String> stdout = await process.stdout.rest.toList();
461
- expect(stdout.join('\n'), contains('Evaluating directory:'));
462
- });
463
-
464
- test('CLI help displays all registered rules', () async {
465
- final TestProcess process = await TestProcess.start('dart', [
466
- p.normalize(p.absolute('bin/cli.dart')),
467
- '--help',
468
- ]);
469
- await process.shouldExit(0);
470
- final List<String> stdout = await process.stdout.rest.toList();
471
- final String stdoutStr = stdout.join('\n');
472
-
473
- for (final CheckType check in RuleRegistry.allChecks) {
474
- expect(stdoutStr, contains(check.name));
475
- }
476
- });
477
-
478
- test('CLI help does not display path-does-not-exist', () async {
479
- final TestProcess process = await TestProcess.start('dart', [
480
- p.normalize(p.absolute('bin/cli.dart')),
481
- '--help',
482
- ]);
483
- await process.shouldExit(0);
484
- final List<String> stdout = await process.stdout.rest.toList();
485
- final String stdoutStr = stdout.join('\n');
486
-
487
- expect(stdoutStr, isNot(contains(Validator.pathDoesNotExist)));
488
- });
489
-
490
- test('ignores directory missing SKILL.md if listed in ignore file', () async {
491
- final Directory skillsDir = await Directory('${tempDir.path}/skills').create();
492
-
493
- // Create a valid skill
494
- final Directory skillDir = await Directory('${skillsDir.path}/valid-skill').create();
495
- await File(
496
- '${skillDir.path}/SKILL.md',
497
- ).writeAsString('---\nname: valid-skill\ndescription: A valid skill\n---\nBody');
498
-
499
- // Create a non-skill directory
500
- await Directory('${skillsDir.path}/contributing').create();
501
-
502
- // Create ignore file
503
- final ignoreFile = File('${tempDir.path}/$defaultIgnoreFileName');
504
- await ignoreFile.writeAsString(
505
- jsonEncode({
506
- SkillsIgnores.skillsKey: {
507
- 'contributing': [
508
- {
509
- IgnoreEntry.ruleIdKey: Validator.pathDoesNotExist,
510
- IgnoreEntry.fileNameKey: 'skills/contributing',
511
- },
512
- ],
513
- },
514
- }),
515
- );
516
-
517
- final configFile = File('${tempDir.path}/dart_skills_lint.yaml');
518
- await configFile.writeAsString('''
519
- dart_skills_lint:
520
- directories:
521
- - path: "skills"
522
- ignore_file: "$defaultIgnoreFileName"
523
- ''');
524
-
525
- final TestProcess process = await TestProcess.start('dart', [
526
- p.normalize(p.absolute('bin/cli.dart')),
527
- '-d',
528
- 'skills',
529
- ], workingDirectory: tempDir.path);
530
-
531
- await process.shouldExit(0);
532
-
533
- final List<String> stdout = await process.stdout.rest.toList();
534
- final String stdoutStr = stdout.join('\n');
535
- expect(stdoutStr, contains('--- Validating skill: valid-skill ---'));
536
- expect(stdoutStr, contains('--- Validating skill: contributing ---'));
537
- });
538
-
539
- test('CLI reports trailing whitespace as error when enabled via config', () async {
540
- final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
541
- await File(
542
- '${skillDir.path}/SKILL.md',
543
- ).writeAsString('${buildFrontmatter(name: 'test-skill')}Line with 1 space \n');
544
-
545
- final configFile = File('${tempDir.path}/dart_skills_lint.yaml');
546
- await configFile.writeAsString('''
547
- dart_skills_lint:
548
- directories:
549
- - path: "test-skill"
550
- rules:
551
- check-trailing-whitespace: error
552
- ''');
553
-
554
- final TestProcess process = await TestProcess.start('dart', [
555
- p.normalize(p.absolute('bin/cli.dart')),
556
- '-s',
557
- 'test-skill',
558
- ], workingDirectory: tempDir.path);
559
-
560
- final List<String> stderr = await process.stderr.rest.toList();
561
- final String stderrStr = stderr.join('\n');
562
- expect(stderrStr, contains('has 1 trailing space(s)'));
563
- await process.shouldExit(1);
564
- });
565
-
566
- test('--fix dry-runs and shows diff but does not modify file', () async {
567
- final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
568
- await File(
569
- '${skillDir.path}/SKILL.md',
570
- ).writeAsString('${buildFrontmatter(name: 'test-skill')}Line with 1 space \n');
571
-
572
- final TestProcess process = await TestProcess.start('dart', [
573
- 'bin/cli.dart',
574
- '-s',
575
- skillDir.path,
576
- '--fix',
577
- '--check-trailing-whitespace',
578
- ]);
579
-
580
- final List<String> stdout = await process.stdout.rest.toList();
581
- final String stdoutStr = stdout.join('\n');
582
- expect(stdoutStr, contains('[Dry Run] Proposed changes for test-skill (SKILL.md):'));
583
-
584
- await process.shouldExit(1);
585
-
586
- // Verify file was not modified
587
- final String content = await File('${skillDir.path}/SKILL.md').readAsString();
588
- expect(content, contains('Line with 1 space \n'));
589
- });
590
-
591
- test('--fix-apply modifies file', () async {
592
- final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
593
- await File(
594
- '${skillDir.path}/SKILL.md',
595
- ).writeAsString('${buildFrontmatter(name: 'test-skill')}Line with 1 space \n');
596
-
597
- final TestProcess process = await TestProcess.start('dart', [
598
- 'bin/cli.dart',
599
- '-s',
600
- skillDir.path,
601
- '--fix-apply',
602
- '--check-trailing-whitespace',
603
- ]);
604
-
605
- final List<String> stdout = await process.stdout.rest.toList();
606
- expect(stdout.join('\n'), contains('Applied fixes for test-skill'));
607
-
608
- await process.shouldExit(0);
609
-
610
- // Verify file was modified
611
- final String content = await File('${skillDir.path}/SKILL.md').readAsString();
612
- expect(content, isNot(contains('Line with 1 space \n')));
613
- expect(content, contains('Line with 1 space\n'));
614
- });
615
-
616
- test('--fix-apply does not modify file if lint is ignored', () async {
617
- final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
618
- await File(
619
- '${skillDir.path}/SKILL.md',
620
- ).writeAsString('${buildFrontmatter(name: 'test-skill')}Line with 1 space \n');
621
-
622
- final ignoreFile = File('${tempDir.path}/$defaultIgnoreFileName');
623
- await ignoreFile.writeAsString(
624
- jsonEncode({
625
- SkillsIgnores.skillsKey: {
626
- 'test-skill': [
627
- {
628
- IgnoreEntry.ruleIdKey: 'check-trailing-whitespace',
629
- IgnoreEntry.fileNameKey: 'SKILL.md',
630
- },
631
- ],
632
- },
633
- }),
634
- );
635
-
636
- final TestProcess process = await TestProcess.start('dart', [
637
- 'bin/cli.dart',
638
- '-s',
639
- skillDir.path,
640
- '--fix-apply',
641
- '--check-trailing-whitespace',
642
- ]);
643
-
644
- await process.shouldExit(0);
645
-
646
- final String content = await File('${skillDir.path}/SKILL.md').readAsString();
647
- expect(content, contains('Line with 1 space \n'));
648
- });
649
-
650
- test('--fix-apply does not modify file if invalid-skill-name is ignored', () async {
651
- final Directory skillDir = await Directory('${tempDir.path}/my_skill').create();
652
- await File('${skillDir.path}/SKILL.md').writeAsString('''
653
- ---
654
- name: wrong-name
655
- description: A test skill
656
- ---
657
- Body''');
658
-
659
- final ignoreFile = File('${tempDir.path}/$defaultIgnoreFileName');
660
- await ignoreFile.writeAsString(
661
- jsonEncode({
662
- SkillsIgnores.skillsKey: {
663
- 'my_skill': [
664
- {IgnoreEntry.ruleIdKey: 'invalid-skill-name', IgnoreEntry.fileNameKey: 'SKILL.md'},
665
- ],
666
- },
667
- }),
668
- );
669
-
670
- final TestProcess process = await TestProcess.start('dart', [
671
- 'bin/cli.dart',
672
- '-s',
673
- skillDir.path,
674
- '--fix-apply',
675
- ]);
676
-
677
- await process.shouldExit(0);
678
-
679
- final String content = await File('${skillDir.path}/SKILL.md').readAsString();
680
- expect(content, contains('name: wrong-name'));
681
- });
682
- });
683
- }