sdd-full 4.6.1 → 4.8.0

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 (173) hide show
  1. package/bin.js +1 -1
  2. package/package.json +1 -1
  3. package/skills/.agents/skills/flutter-add-integration-test/SKILL.md +165 -0
  4. package/skills/.agents/skills/flutter-add-widget-preview/SKILL.md +147 -0
  5. package/skills/.agents/skills/flutter-add-widget-test/SKILL.md +156 -0
  6. package/skills/.agents/skills/flutter-apply-architecture-best-practices/SKILL.md +164 -0
  7. package/skills/.agents/skills/flutter-build-responsive-layout/SKILL.md +141 -0
  8. package/skills/.agents/skills/flutter-fix-layout-issues/SKILL.md +132 -0
  9. package/skills/.agents/skills/flutter-implement-json-serialization/SKILL.md +155 -0
  10. package/skills/.agents/skills/flutter-setup-declarative-routing/SKILL.md +257 -0
  11. package/skills/.agents/skills/flutter-setup-localization/SKILL.md +212 -0
  12. package/skills/.agents/skills/flutter-use-http-package/SKILL.md +177 -0
  13. package/skills/VERSION.md +186 -62
  14. package/skills/design-planning/ai-coding-rules/SKILL.md +5 -13
  15. package/skills/design-planning/design-to-code/SKILL.md +5 -14
  16. package/skills/design-planning/enterprise-spec/SKILL.md +5 -13
  17. package/skills/design-planning/flutter-av/SKILL.md +5 -16
  18. package/skills/design-planning/flutter-map/SKILL.md +5 -14
  19. package/skills/design-planning/function-sdd/SKILL.md +5 -13
  20. package/skills/design-planning/global-overlay-stack-standard/SKILL.md +73 -0
  21. package/skills/design-planning/ui-motion-interaction-standard/SKILL.md +69 -0
  22. package/skills/design-planning/ui-sdd-specialized/SKILL.md +5 -14
  23. package/skills/development-execution/flutter-errors/SKILL.md +5 -15
  24. package/skills/flutter-skills/.github/dependabot.yaml +15 -0
  25. package/skills/flutter-skills/.github/workflows/dart_skills_lint_workflow.yaml +68 -0
  26. package/skills/flutter-skills/.github/workflows/skills_tool.yaml +51 -0
  27. package/skills/flutter-skills/CODE_OF_CONDUCT.md +3 -0
  28. package/skills/flutter-skills/CONTRIBUTING.md +36 -0
  29. package/skills/flutter-skills/LICENSE +26 -0
  30. package/skills/flutter-skills/README.md +50 -0
  31. package/skills/flutter-skills/pubspec.yaml +9 -0
  32. package/skills/flutter-skills/resources/flutter_skills.yaml +434 -0
  33. package/skills/flutter-skills/skills/flutter-add-integration-test/SKILL.md +163 -0
  34. package/skills/flutter-skills/skills/flutter-add-widget-preview/SKILL.md +145 -0
  35. package/skills/flutter-skills/skills/flutter-add-widget-test/SKILL.md +154 -0
  36. package/skills/flutter-skills/skills/flutter-apply-architecture-best-practices/SKILL.md +162 -0
  37. package/skills/flutter-skills/skills/flutter-build-responsive-layout/SKILL.md +139 -0
  38. package/skills/flutter-skills/skills/flutter-fix-layout-issues/SKILL.md +130 -0
  39. package/skills/flutter-skills/skills/flutter-implement-json-serialization/SKILL.md +153 -0
  40. package/skills/flutter-skills/skills/flutter-setup-declarative-routing/SKILL.md +255 -0
  41. package/skills/flutter-skills/skills/flutter-setup-localization/SKILL.md +210 -0
  42. package/skills/flutter-skills/skills/flutter-use-http-package/SKILL.md +175 -0
  43. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/add-dart-lint-validation-rule/SKILL.md +196 -0
  44. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-best-practices/SKILL.md +65 -0
  45. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-checks-migration/SKILL.md +158 -0
  46. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-cli-app-best-practices/SKILL.md +168 -0
  47. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-doc-validation/SKILL.md +87 -0
  48. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-long-lines/SKILL.md +101 -0
  49. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-matcher-best-practices/SKILL.md +136 -0
  50. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-modern-features/SKILL.md +266 -0
  51. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-package-maintenance/SKILL.md +92 -0
  52. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/SKILL.md +92 -0
  53. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/lib/src/calculator.dart +7 -0
  54. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/pubspec.yaml +8 -0
  55. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/test/calculator_test.dart +11 -0
  56. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/interpret_coverage.dart +95 -0
  57. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/pubspec.yaml +6 -0
  58. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/test/interpret_coverage_test.dart +93 -0
  59. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-fundamentals/SKILL.md +173 -0
  60. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/definition-of-done/SKILL.md +27 -0
  61. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/flutter_skills_ignore.json +3 -0
  62. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/grill-me/SKILL.md +10 -0
  63. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/ignore.json +3 -0
  64. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/test-driven-development/SKILL.md +371 -0
  65. package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/test-driven-development/testing-anti-patterns.md +299 -0
  66. package/skills/flutter-skills/tool/dart_skills_lint/AUTHORS +7 -0
  67. package/skills/flutter-skills/tool/dart_skills_lint/CHANGELOG.md +12 -0
  68. package/skills/flutter-skills/tool/dart_skills_lint/CONTRIBUTING.md +51 -0
  69. package/skills/flutter-skills/tool/dart_skills_lint/LICENSE +27 -0
  70. package/skills/flutter-skills/tool/dart_skills_lint/README.md +203 -0
  71. package/skills/flutter-skills/tool/dart_skills_lint/analysis_options.yaml +296 -0
  72. package/skills/flutter-skills/tool/dart_skills_lint/bench/README.md +23 -0
  73. package/skills/flutter-skills/tool/dart_skills_lint/bench/baseline_throughput.dart +230 -0
  74. package/skills/flutter-skills/tool/dart_skills_lint/bin/cli.dart +10 -0
  75. package/skills/flutter-skills/tool/dart_skills_lint/dart_skills_lint.yaml +14 -0
  76. package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/PRODUCTION_READYNESS.md +48 -0
  77. package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/completion_migration_plan.md +99 -0
  78. package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/legacy_patterns_report.md +110 -0
  79. package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/pub_vs_skill_report.md +56 -0
  80. package/skills/flutter-skills/tool/dart_skills_lint/documentation/knowledge/SPECIFICATION.md +79 -0
  81. package/skills/flutter-skills/tool/dart_skills_lint/documentation/knowledge/architecture_overview.md +64 -0
  82. package/skills/flutter-skills/tool/dart_skills_lint/lib/dart_skills_lint.dart +11 -0
  83. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/config_parser.dart +156 -0
  84. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/entry_point.dart +354 -0
  85. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/fixable_rule.dart +20 -0
  86. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/analysis_severity.dart +15 -0
  87. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/check_type.dart +17 -0
  88. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/ignore_entry.dart +34 -0
  89. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/ignore_entry.g.dart +19 -0
  90. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skill_context.dart +27 -0
  91. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skill_rule.dart +27 -0
  92. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skills_ignores.dart +26 -0
  93. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skills_ignores.g.dart +24 -0
  94. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/validation_error.dart +31 -0
  95. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rule_registry.dart +79 -0
  96. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/absolute_paths_rule.dart +74 -0
  97. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/description_length_rule.dart +49 -0
  98. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/disallowed_field_rule.dart +61 -0
  99. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/name_format_rule.dart +167 -0
  100. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/relative_paths_rule.dart +72 -0
  101. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/trailing_whitespace_rule.dart +93 -0
  102. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/valid_yaml_metadata_rule.dart +74 -0
  103. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/skills_ignores_storage.dart +36 -0
  104. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/validation_session.dart +559 -0
  105. package/skills/flutter-skills/tool/dart_skills_lint/lib/src/validator.dart +238 -0
  106. package/skills/flutter-skills/tool/dart_skills_lint/pubspec.yaml +28 -0
  107. package/skills/flutter-skills/tool/dart_skills_lint/skills/README.md +10 -0
  108. package/skills/flutter-skills/tool/dart_skills_lint/skills/dart-skills-lint-validation/SKILL.md +195 -0
  109. package/skills/flutter-skills/tool/dart_skills_lint/skills-lock.json +75 -0
  110. package/skills/flutter-skills/tool/dart_skills_lint/test/absolute_paths_test.dart +167 -0
  111. package/skills/flutter-skills/tool/dart_skills_lint/test/cli_integration_test.dart +683 -0
  112. package/skills/flutter-skills/tool/dart_skills_lint/test/config_file_test.dart +292 -0
  113. package/skills/flutter-skills/tool/dart_skills_lint/test/custom_rule_test.dart +122 -0
  114. package/skills/flutter-skills/tool/dart_skills_lint/test/directory_structure_test.dart +163 -0
  115. package/skills/flutter-skills/tool/dart_skills_lint/test/field_constraints_test.dart +178 -0
  116. package/skills/flutter-skills/tool/dart_skills_lint/test/fixer_test.dart +172 -0
  117. package/skills/flutter-skills/tool/dart_skills_lint/test/ignore_models_test.dart +63 -0
  118. package/skills/flutter-skills/tool/dart_skills_lint/test/metadata_validation_test.dart +116 -0
  119. package/skills/flutter-skills/tool/dart_skills_lint/test/relative_path_flag_test.dart +70 -0
  120. package/skills/flutter-skills/tool/dart_skills_lint/test/relative_paths_test.dart +172 -0
  121. package/skills/flutter-skills/tool/dart_skills_lint/test/resolve_rules_test.dart +82 -0
  122. package/skills/flutter-skills/tool/dart_skills_lint/test/rule_naming_test.dart +29 -0
  123. package/skills/flutter-skills/tool/dart_skills_lint/test/skills_ignores_storage_test.dart +89 -0
  124. package/skills/flutter-skills/tool/dart_skills_lint/test/test_utils.dart +19 -0
  125. package/skills/flutter-skills/tool/dart_skills_lint/test/trailing_whitespace_test.dart +152 -0
  126. package/skills/flutter-skills/tool/generator/README.md +150 -0
  127. package/skills/flutter-skills/tool/generator/analysis_options.yaml +143 -0
  128. package/skills/flutter-skills/tool/generator/bin/skills.dart +73 -0
  129. package/skills/flutter-skills/tool/generator/lib/src/commands/base_skill_command.dart +87 -0
  130. package/skills/flutter-skills/tool/generator/lib/src/commands/base_yaml_command.dart +83 -0
  131. package/skills/flutter-skills/tool/generator/lib/src/commands/generate_skill_command.dart +92 -0
  132. package/skills/flutter-skills/tool/generator/lib/src/commands/update_readme_command.dart +150 -0
  133. package/skills/flutter-skills/tool/generator/lib/src/commands/update_skill_command.dart +97 -0
  134. package/skills/flutter-skills/tool/generator/lib/src/commands/validate_skill_command.dart +284 -0
  135. package/skills/flutter-skills/tool/generator/lib/src/models/skill_params.dart +41 -0
  136. package/skills/flutter-skills/tool/generator/lib/src/services/gemini_service.dart +310 -0
  137. package/skills/flutter-skills/tool/generator/lib/src/services/markdown_converter.dart +226 -0
  138. package/skills/flutter-skills/tool/generator/lib/src/services/prompts.dart +72 -0
  139. package/skills/flutter-skills/tool/generator/lib/src/services/resource_fetcher_service.dart +84 -0
  140. package/skills/flutter-skills/tool/generator/lib/src/services/skill_instructions.dart +30 -0
  141. package/skills/flutter-skills/tool/generator/pubspec.yaml +32 -0
  142. package/skills/flutter-skills/tool/generator/test/commands/base_skill_command_test.dart +131 -0
  143. package/skills/flutter-skills/tool/generator/test/commands/validate_skills_input_test.dart +263 -0
  144. package/skills/flutter-skills/tool/generator/test/custom_skill_rules/last_modified_rule.dart +32 -0
  145. package/skills/flutter-skills/tool/generator/test/generate_skills_retry_test.dart +105 -0
  146. package/skills/flutter-skills/tool/generator/test/generate_skills_test.dart +519 -0
  147. package/skills/flutter-skills/tool/generator/test/lint_skills_test.dart +34 -0
  148. package/skills/flutter-skills/tool/generator/test/markdown_converter_test.dart +103 -0
  149. package/skills/flutter-skills/tool/generator/test/markdown_table_test.dart +131 -0
  150. package/skills/flutter-skills/tool/generator/test/models/skill_params_test.dart +37 -0
  151. package/skills/flutter-skills/tool/generator/test/services/gemini_service_test.dart +291 -0
  152. package/skills/flutter-skills/tool/generator/test/services/markdown_converter_test.dart +156 -0
  153. package/skills/flutter-skills/tool/generator/test/services/resource_fetcher_service_test.dart +188 -0
  154. package/skills/flutter-skills/tool/generator/test/update_skills_test.dart +241 -0
  155. package/skills/flutter-skills/tool/generator/test/validate_skills_test.dart +728 -0
  156. package/skills/quality-assurance/bdd-acceptance/SKILL.md +5 -14
  157. package/skills/quality-assurance/flutter-test/SKILL.md +5 -16
  158. package/skills/rules/project_rules.md +538 -127
  159. package/skills/special-tools/env-check/SKILL.md +5 -13
  160. package/skills/special-tools/ios-full-auto-debug/SKILL.md +5 -15
  161. package/skills/writing-skills/SKILL.md +654 -0
  162. package/skills/writing-skills/anthropic-best-practices.md +1149 -0
  163. package/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  164. package/skills/writing-skills/graphviz-conventions.dot +172 -0
  165. package/skills/writing-skills/persuasion-principles.md +187 -0
  166. package/skills/writing-skills/render-graphs.js +168 -0
  167. package/skills/writing-skills/testing-skills-with-subagents.md +384 -0
  168. package/skills/checklist.md +0 -154
  169. package/skills/rules/user_rules.md +0 -263
  170. package/skills//345/256/214/346/225/264/345/274/200/345/217/221/346/265/201/347/250/213/346/211/213/345/206/214.md +0 -454
  171. package/skills//346/212/200/350/203/275/344/275/223/347/263/273/345/256/214/345/226/204/345/273/272/350/256/256.md +0 -308
  172. package/skills//346/212/200/350/203/275/344/275/277/347/224/250/346/214/207/345/215/227.md +0 -309
  173. package/skills//346/212/200/350/203/275/345/206/263/347/255/226/346/240/221.md +0 -338
@@ -0,0 +1,354 @@
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:io';
6
+
7
+ import 'package:args/args.dart';
8
+ import 'package:logging/logging.dart';
9
+ import 'package:meta/meta.dart';
10
+
11
+ import 'config_parser.dart';
12
+ import 'models/analysis_severity.dart';
13
+ import 'models/check_type.dart';
14
+ import 'models/skill_rule.dart';
15
+ import 'rule_registry.dart';
16
+ import 'validation_session.dart';
17
+
18
+ export 'validation_session.dart';
19
+
20
+ final _log = Logger('dart_skills_lint');
21
+
22
+ const _printWarningsFlag = 'print-warnings';
23
+ const _fastFailFlag = 'fast-fail';
24
+ const _quietFlag = 'quiet';
25
+ const _skillsDirectoryFlag = 'skills-directory';
26
+ const _skillOption = 'skill';
27
+ const _ignoreFileOption = 'ignore-file';
28
+ const _ignoreConfigFlag = 'ignore-config';
29
+ const _generateBaselineFlag = 'generate-baseline';
30
+ const _fixFlag = 'fix';
31
+ const _fixApplyFlag = 'fix-apply';
32
+ const _allowMisconfiguredKeysFlag = 'allow-misconfigured-keys';
33
+
34
+ /// Main entrypoint execution logic for the CLI tool.
35
+ ///
36
+ /// Parses arguments and runs validation on the specified directory.
37
+ Future<void> runApp(List<String> args) async {
38
+ // Setup logger to print to stdout/stderr
39
+ Logger.root.level = Level.ALL;
40
+ Logger.root.onRecord.listen((record) {
41
+ if (record.level >= Level.SEVERE) {
42
+ stderr.writeln(record.message);
43
+ } else {
44
+ stdout.writeln(record.message);
45
+ }
46
+ });
47
+
48
+ const helpFlag = 'help';
49
+
50
+ final ArgParser parser = _createArgParser(helpFlag);
51
+
52
+ final ArgResults results;
53
+ try {
54
+ results = parser.parse(args);
55
+ if (results[helpFlag] as bool) {
56
+ _printUsage(parser);
57
+ return;
58
+ }
59
+ } catch (e) {
60
+ _printUsage(parser, e.toString());
61
+ exitCode = 64; // Bad usage
62
+ return;
63
+ }
64
+
65
+ final Configuration? config = await _loadConfig(results);
66
+ if (config == null) {
67
+ exitCode = 1;
68
+ return;
69
+ }
70
+
71
+ var skillDirPaths = results[_skillsDirectoryFlag] as List<String>;
72
+ final individualSkillPaths = results[_skillOption] as List<String>;
73
+
74
+ if (skillDirPaths.isEmpty && individualSkillPaths.isEmpty) {
75
+ if (config.directoryConfigs.isNotEmpty) {
76
+ skillDirPaths = config.directoryConfigs.map((e) => e.path).toList();
77
+ } else {
78
+ final defaults = ['.claude/skills', '.agents/skills'];
79
+ final existingDefaults = <String>[];
80
+ for (final path in defaults) {
81
+ if (Directory(path).existsSync()) {
82
+ existingDefaults.add(path);
83
+ }
84
+ }
85
+ if (existingDefaults.isEmpty) {
86
+ _printUsage(parser, 'Missing skills directory. Checked defaults: ${defaults.join(', ')}');
87
+ exitCode = 64;
88
+ return;
89
+ }
90
+ skillDirPaths = existingDefaults;
91
+ }
92
+ }
93
+
94
+ final Map<String, AnalysisSeverity> resolvedRules = resolveRules(results, config);
95
+
96
+ final printWarnings = results[_printWarningsFlag] as bool;
97
+ final fastFail = results[_fastFailFlag] as bool;
98
+ final quiet = results[_quietFlag] as bool;
99
+ final generateBaseline = results[_generateBaselineFlag] as bool;
100
+ final fix = results[_fixFlag] as bool;
101
+ final fixApply = results[_fixApplyFlag] as bool;
102
+
103
+ String? ignoreFileOverride;
104
+ if (results.wasParsed(_ignoreFileOption)) {
105
+ ignoreFileOverride = results[_ignoreFileOption] as String?;
106
+ }
107
+
108
+ final bool success = await validateSkillsInternal(
109
+ skillDirPaths: skillDirPaths,
110
+ individualSkillPaths: individualSkillPaths,
111
+ resolvedRules: resolvedRules,
112
+ printWarnings: printWarnings,
113
+ fastFail: fastFail,
114
+ quiet: quiet,
115
+ generateBaseline: generateBaseline,
116
+ fix: fix,
117
+ fixApply: fixApply,
118
+ ignoreFileOverride: ignoreFileOverride,
119
+ config: config,
120
+ );
121
+
122
+ exitCode = success ? 0 : 1;
123
+ }
124
+
125
+ /// Creates the [ArgParser] for the CLI, adding all supported flags and options.
126
+ ///
127
+ /// Dynamically adds flags for all registered rules in [RuleRegistry].
128
+ ArgParser _createArgParser(String helpFlag) {
129
+ final parser = ArgParser()
130
+ ..addFlag(helpFlag, abbr: 'h', negatable: false, help: 'Show usage information.')
131
+ ..addFlag(_printWarningsFlag, abbr: 'w', defaultsTo: true, help: 'Print validation warnings.');
132
+
133
+ // Dynamically add flags for all registered rules.
134
+ for (final CheckType check in RuleRegistry.allChecks) {
135
+ parser.addFlag(
136
+ check.name,
137
+ defaultsTo: check.defaultSeverity != AnalysisSeverity.disabled,
138
+ help: check.help,
139
+ );
140
+ }
141
+
142
+ parser
143
+ ..addFlag(
144
+ _fastFailFlag,
145
+ negatable: false,
146
+ help: 'Fail immediately on the first skill validation error.',
147
+ )
148
+ ..addFlag(
149
+ _quietFlag,
150
+ abbr: 'q',
151
+ negatable: false,
152
+ help: 'Quiet mode (only print errors and warnings).',
153
+ )
154
+ ..addMultiOption(
155
+ _skillsDirectoryFlag,
156
+ abbr: 'd',
157
+ help: 'Path to a skills directory to validate. Can be specified multiple times.',
158
+ )
159
+ ..addMultiOption(
160
+ _skillOption,
161
+ abbr: 's',
162
+ help: 'Path to an individual skill directory to validate. Can be specified multiple times.',
163
+ )
164
+ ..addOption(_ignoreFileOption, help: 'Path to a JSON file listing lints to ignore for the run.')
165
+ ..addFlag(
166
+ _generateBaselineFlag,
167
+ negatable: false,
168
+ help: 'Write all current errors into $defaultIgnoreFileName to ignore on future runs.',
169
+ )
170
+ ..addFlag(
171
+ _ignoreConfigFlag,
172
+ negatable: false,
173
+ help: 'Ignore the YAML configuration file entirely.',
174
+ )
175
+ ..addFlag(_fixFlag, negatable: false, help: 'Preview fixes for failing lints (dry run).')
176
+ ..addFlag(_fixApplyFlag, negatable: false, help: 'Apply fixes for failing lints.')
177
+ ..addFlag(
178
+ _allowMisconfiguredKeysFlag,
179
+ negatable: false,
180
+ hide: true,
181
+ help: 'Allow misconfigured keys in dart_skills_lint.yaml.',
182
+ );
183
+
184
+ return parser;
185
+ }
186
+
187
+ Future<Configuration?> _loadConfig(ArgResults results) async {
188
+ final ignoreConfig = results[_ignoreConfigFlag] as bool;
189
+ final Configuration config = ignoreConfig ? Configuration() : await ConfigParser.loadConfig();
190
+ if (ignoreConfig && !(results[_quietFlag] as bool)) {
191
+ _log.info('Ignoring configuration file due to $_ignoreConfigFlag flag');
192
+ }
193
+
194
+ if (config.parsingErrors.isNotEmpty) {
195
+ final allowMisconfiguredKeys = results[_allowMisconfiguredKeysFlag] as bool;
196
+ if (allowMisconfiguredKeys) {
197
+ for (final String error in config.parsingErrors) {
198
+ _log.warning('Configuration warning: $error');
199
+ }
200
+ } else {
201
+ for (final String error in config.parsingErrors) {
202
+ _log.severe('Configuration error: $error');
203
+ }
204
+ _log.severe('Use --$_allowMisconfiguredKeysFlag to ignore these errors.');
205
+ return null;
206
+ }
207
+ }
208
+ return config;
209
+ }
210
+
211
+ /// Validates skills based on the provided configuration.
212
+ ///
213
+ /// This is the public API for validating skills. It does not support fixing
214
+ /// lints as that feature is currently considered internal to the CLI.
215
+ ///
216
+ /// [skillDirPaths] is a list of directories containing multiple skills.
217
+ /// [individualSkillPaths] is a list of paths to individual skill directories.
218
+ /// [resolvedRules] is a map of rule names to their severity overrides.
219
+ /// [printWarnings] controls whether to print validation warnings.
220
+ /// [fastFail] causes validation to stop on the first error.
221
+ /// [quiet] suppresses non-error/warning output.
222
+ /// [generateBaseline] writes current errors to a baseline file instead of reporting them.
223
+ /// [ignoreFileOverride] is an optional path to a baseline file to use.
224
+ /// [config] is the loaded configuration.
225
+ ///
226
+ /// Returns `true` if all validations passed (or if generating a baseline), `false` otherwise.
227
+ Future<bool> validateSkills({
228
+ List<String> skillDirPaths = const [],
229
+ List<String> individualSkillPaths = const [],
230
+ Map<String, AnalysisSeverity> resolvedRules = const {},
231
+ bool printWarnings = true,
232
+ bool fastFail = false,
233
+ bool quiet = false,
234
+ bool generateBaseline = false,
235
+ String? ignoreFileOverride,
236
+ Configuration? config,
237
+ List<SkillRule> customRules = const [],
238
+ }) {
239
+ return validateSkillsInternal(
240
+ skillDirPaths: skillDirPaths,
241
+ individualSkillPaths: individualSkillPaths,
242
+ resolvedRules: resolvedRules,
243
+ printWarnings: printWarnings,
244
+ fastFail: fastFail,
245
+ quiet: quiet,
246
+ generateBaseline: generateBaseline,
247
+ ignoreFileOverride: ignoreFileOverride,
248
+ config: config,
249
+ customRules: customRules,
250
+ );
251
+ }
252
+
253
+ /// Internal implementation of skill validation that supports fixing.
254
+ ///
255
+ /// Kept internal to avoid exposing experimental fix parameters in the public API.
256
+ @visibleForTesting
257
+ Future<bool> validateSkillsInternal({
258
+ List<String> skillDirPaths = const [],
259
+ List<String> individualSkillPaths = const [],
260
+ Map<String, AnalysisSeverity> resolvedRules = const {},
261
+ bool printWarnings = true,
262
+ bool fastFail = false,
263
+ bool quiet = false,
264
+ bool generateBaseline = false,
265
+ bool fix = false,
266
+ bool fixApply = false,
267
+ String? ignoreFileOverride,
268
+ Configuration? config,
269
+ List<SkillRule> customRules = const [],
270
+ }) async {
271
+ final session = ValidationSession(
272
+ config: config ?? Configuration(),
273
+ resolvedRules: resolvedRules,
274
+ ignoreFileOverride: ignoreFileOverride,
275
+ customRules: customRules,
276
+ printWarnings: printWarnings,
277
+ fastFail: fastFail,
278
+ quiet: quiet,
279
+ generateBaseline: generateBaseline,
280
+ fix: fix,
281
+ fixApply: fixApply,
282
+ );
283
+
284
+ for (final skillPath in individualSkillPaths) {
285
+ final bool keepGoing = await session.processIndividualSkill(skillPath);
286
+ if (!keepGoing) {
287
+ break;
288
+ }
289
+ }
290
+ if (session.anyFailed && fastFail) {
291
+ return false;
292
+ }
293
+
294
+ for (final rootPath in skillDirPaths) {
295
+ final bool keepGoing = await session.processSkillRoot(rootPath);
296
+ if (!keepGoing) {
297
+ break;
298
+ }
299
+ }
300
+
301
+ session.reportNoSkillsValidated(skillDirPaths);
302
+
303
+ if (generateBaseline) {
304
+ return true;
305
+ }
306
+ return !session.anyFailed;
307
+ }
308
+
309
+ @visibleForTesting
310
+ Map<String, AnalysisSeverity> resolveRules(ArgResults results, Configuration config) {
311
+ final resolved = <String, AnalysisSeverity>{};
312
+
313
+ // 1. Initialize with default severities from the registry.
314
+ for (final CheckType check in RuleRegistry.allChecks) {
315
+ resolved[check.name] = check.defaultSeverity;
316
+ }
317
+
318
+ // 2. Override with configurations from the YAML file.
319
+ resolved.addAll(config.configuredRules);
320
+
321
+ // 3. Override with CLI flags. CLI flags take highest precedence.
322
+ for (final CheckType check in RuleRegistry.allChecks) {
323
+ final String name = check.name;
324
+
325
+ // Skip if the flag was not passed on the command line.
326
+ if (!results.wasParsed(name)) {
327
+ continue;
328
+ }
329
+
330
+ // TODO(reidbaker): Handle options in addition to flags.
331
+ final Object? value = results[name];
332
+ if (value is! bool) {
333
+ continue;
334
+ }
335
+
336
+ if (value) {
337
+ // If the user explicitly enabled the rule via flag (e.g., --rule), set to error.
338
+ resolved[name] = AnalysisSeverity.error;
339
+ } else {
340
+ // If the user explicitly disabled the rule via flag (e.g., --no-rule).
341
+ resolved[name] = AnalysisSeverity.disabled;
342
+ }
343
+ }
344
+
345
+ return resolved;
346
+ }
347
+
348
+ void _printUsage(ArgParser parser, [String? error]) {
349
+ if (error != null) {
350
+ _log.severe('Error: $error');
351
+ }
352
+ _log.info('Usage: dart_skills_lint [options] --$_skillsDirectoryFlag <$_skillsDirectoryFlag>');
353
+ _log.info(parser.usage);
354
+ }
@@ -0,0 +1,20 @@
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:io';
6
+ import 'models/skill_rule.dart';
7
+
8
+ /// Interface for rules that support fixes.
9
+ /// Kept internal to the package for now.
10
+ abstract class FixableRule extends SkillRule {
11
+ /// Returns the updated content of the file at [filePath].
12
+ /// [currentContent] is the content after previous fixes have been applied.
13
+ /// If the rule does not support fixing the file at [filePath], it should return [currentContent].
14
+ ///
15
+ /// Rules should rely on [currentContent] for the current state of the file.
16
+ /// If a rule needs structured access (like parsed YAML), it should parse
17
+ /// [currentContent] itself, as structured data in [SkillContext] may be stale
18
+ /// if previous rules applied fixes.
19
+ Future<String> fix(String filePath, String currentContent, Directory directory);
20
+ }
@@ -0,0 +1,15 @@
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
+ /// Severity level for a specific analysis rule.
6
+ enum AnalysisSeverity {
7
+ /// Check is completely disabled.
8
+ disabled,
9
+
10
+ /// Failures are reported as warnings and do not fail the overall validation.
11
+ warning,
12
+
13
+ /// Failures are reported as errors and fail the overall validation.
14
+ error,
15
+ }
@@ -0,0 +1,17 @@
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 'analysis_severity.dart';
6
+
7
+ /// Encapsulates metadata and severity state for a specific validation rule.
8
+ class CheckType {
9
+ const CheckType({required this.name, required this.defaultSeverity, required this.help});
10
+ final String name;
11
+
12
+ /// The default severity if not overridden by config or flags.
13
+ final AnalysisSeverity defaultSeverity;
14
+
15
+ /// The help message displayed by the CLI.
16
+ final String help;
17
+ }
@@ -0,0 +1,34 @@
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 'package:json_annotation/json_annotation.dart';
6
+
7
+ part 'ignore_entry.g.dart';
8
+
9
+ /// Represents a single ignored rule entry for a specific file.
10
+ @JsonSerializable()
11
+ class IgnoreEntry {
12
+ IgnoreEntry({required this.ruleId, required this.fileName, this.used = false});
13
+
14
+ /// Creates an IgnoreEntry from a JSON map.
15
+ factory IgnoreEntry.fromJson(Map<String, dynamic> json) => _$IgnoreEntryFromJson(json);
16
+
17
+ static const String ruleIdKey = 'rule_id';
18
+ static const String fileNameKey = 'file_name';
19
+
20
+ /// The rule ID that should be suppressed (e.g., 'description_too_long').
21
+ @JsonKey(name: ruleIdKey)
22
+ final String ruleId;
23
+
24
+ /// The file name to apply this suppression to.
25
+ @JsonKey(name: fileNameKey)
26
+ final String fileName;
27
+
28
+ /// Whether this entry has been used during the run.
29
+ @JsonKey(includeFromJson: false, includeToJson: false)
30
+ bool used;
31
+
32
+ /// Converts an IgnoreEntry to a JSON map.
33
+ Map<String, dynamic> toJson() => _$IgnoreEntryToJson(this);
34
+ }
@@ -0,0 +1,19 @@
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
+ // GENERATED CODE - DO NOT MODIFY BY HAND
6
+
7
+ part of 'ignore_entry.dart';
8
+
9
+ // **************************************************************************
10
+ // JsonSerializableGenerator
11
+ // **************************************************************************
12
+
13
+ IgnoreEntry _$IgnoreEntryFromJson(Map<String, dynamic> json) =>
14
+ IgnoreEntry(ruleId: json['rule_id'] as String, fileName: json['file_name'] as String);
15
+
16
+ Map<String, dynamic> _$IgnoreEntryToJson(IgnoreEntry instance) => <String, dynamic>{
17
+ 'rule_id': instance.ruleId,
18
+ 'file_name': instance.fileName,
19
+ };
@@ -0,0 +1,27 @@
1
+ import 'dart:io';
2
+ import 'package:yaml/yaml.dart';
3
+
4
+ /// Context provided to [SkillRule]s during validation.
5
+ class SkillContext {
6
+ SkillContext({
7
+ required this.directory,
8
+ required this.rawContent,
9
+ this.parsedYaml,
10
+ this.yamlParsingError,
11
+ });
12
+
13
+ /// The required filename for skill documentation.
14
+ static const String skillFileName = 'SKILL.md';
15
+
16
+ /// Regex to match the YAML frontmatter in SKILL.md.
17
+ static final RegExp skillStartRegex = RegExp(r'^---\s*\n(.*?)\n---\s*\n', dotAll: true);
18
+
19
+ final Directory directory;
20
+
21
+ /// Guaranteed to be non-null because we only run rules if SKILL.md exists.
22
+ final String rawContent;
23
+
24
+ final YamlMap? parsedYaml;
25
+
26
+ final String? yamlParsingError;
27
+ }
@@ -0,0 +1,27 @@
1
+ import 'analysis_severity.dart';
2
+ import 'skill_context.dart';
3
+ import 'validation_error.dart';
4
+
5
+ /// Abstract base class for all skill validation rules.
6
+ ///
7
+ /// Custom rules should follow these guidelines to play nice with others:
8
+ /// 1. **Unique Name**: The [name] must be unique to allow for overrides in
9
+ /// configuration.
10
+ /// 2. **Statelessness**: Rules should not maintain state between [validate] calls.
11
+ /// 3. **Use Context**: Prefer using data in [SkillContext] (like [context.parsedYaml])
12
+ /// rather than reading files manually to avoid duplicate I/O.
13
+ /// 4. **Handle Parsing Errors**: If [context.parsedYaml] is null, check
14
+ /// [context.yamlParsingError]. Rules that require valid YAML should return
15
+ /// quickly if parsing failed.
16
+ /// 5. **Respect Severity**: The rule should use its [severity] when creating
17
+ /// [ValidationError]s unless there is a good reason not to.
18
+ abstract class SkillRule {
19
+ /// The unique name of the rule (e.g., 'check-relative-paths').
20
+ /// Used in configuration and flags.
21
+ String get name;
22
+
23
+ AnalysisSeverity get severity;
24
+
25
+ /// Validates the skill provided in [context].
26
+ Future<List<ValidationError>> validate(SkillContext context);
27
+ }
@@ -0,0 +1,26 @@
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 'package:json_annotation/json_annotation.dart';
6
+ import 'ignore_entry.dart';
7
+
8
+ part 'skills_ignores.g.dart';
9
+
10
+ /// Represents the top-level structure of the skills ignore JSON file.
11
+ @JsonSerializable(explicitToJson: true)
12
+ class SkillsIgnores {
13
+ SkillsIgnores({required this.skills});
14
+
15
+ /// Creates a SkillsIgnores from a JSON map.
16
+ factory SkillsIgnores.fromJson(Map<String, dynamic> json) => _$SkillsIgnoresFromJson(json);
17
+
18
+ static const String skillsKey = 'skills';
19
+
20
+ /// Map of skill names to their list of ignore entries.
21
+ @JsonKey(name: skillsKey)
22
+ final Map<String, List<IgnoreEntry>> skills;
23
+
24
+ /// Converts a SkillsIgnores to a JSON map.
25
+ Map<String, dynamic> toJson() => _$SkillsIgnoresToJson(this);
26
+ }
@@ -0,0 +1,24 @@
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
+ // GENERATED CODE - DO NOT MODIFY BY HAND
6
+
7
+ part of 'skills_ignores.dart';
8
+
9
+ // **************************************************************************
10
+ // JsonSerializableGenerator
11
+ // **************************************************************************
12
+
13
+ SkillsIgnores _$SkillsIgnoresFromJson(Map<String, dynamic> json) => SkillsIgnores(
14
+ skills: (json['skills'] as Map<String, dynamic>).map(
15
+ (k, e) => MapEntry(
16
+ k,
17
+ (e as List<dynamic>).map((e) => IgnoreEntry.fromJson(e as Map<String, dynamic>)).toList(),
18
+ ),
19
+ ),
20
+ );
21
+
22
+ Map<String, dynamic> _$SkillsIgnoresToJson(SkillsIgnores instance) => <String, dynamic>{
23
+ 'skills': instance.skills.map((k, e) => MapEntry(k, e.map((e) => e.toJson()).toList())),
24
+ };
@@ -0,0 +1,31 @@
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 'analysis_severity.dart';
6
+
7
+ /// Represents a single validation error found during analysis.
8
+ class ValidationError {
9
+ ValidationError({
10
+ required this.ruleId,
11
+ required this.file,
12
+ required this.message,
13
+ required this.severity,
14
+ this.isIgnored = false,
15
+ });
16
+
17
+ /// The unique rule ID (e.g., 'description_too_long').
18
+ final String ruleId;
19
+
20
+ /// The file name context (e.g., 'SKILL.md' or relative path).
21
+ final String file;
22
+
23
+ /// The human-readable error message.
24
+ final String message;
25
+
26
+ /// The severity of the error.
27
+ final AnalysisSeverity severity;
28
+
29
+ /// Whether this error has been ignored via configuration.
30
+ bool isIgnored;
31
+ }
@@ -0,0 +1,79 @@
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 'models/analysis_severity.dart';
6
+ import 'models/check_type.dart';
7
+ import 'models/skill_rule.dart';
8
+ import 'rules/absolute_paths_rule.dart';
9
+ import 'rules/description_length_rule.dart';
10
+ import 'rules/disallowed_field_rule.dart';
11
+ import 'rules/name_format_rule.dart';
12
+ import 'rules/relative_paths_rule.dart';
13
+ import 'rules/trailing_whitespace_rule.dart';
14
+ import 'rules/valid_yaml_metadata_rule.dart';
15
+
16
+ /// Registry of all built-in rules.
17
+ class RuleRegistry {
18
+ /// All registered rules and their default configurations.
19
+ // TODO(reidbaker): Break out flags vs options here so entry_point can generate appropriate CLI arguments.
20
+ static final List<CheckType> allChecks = [
21
+ const CheckType(
22
+ name: AbsolutePathsRule.ruleName,
23
+ defaultSeverity: AbsolutePathsRule.defaultSeverity,
24
+ help: 'Check if absolute paths exist.',
25
+ ),
26
+ const CheckType(
27
+ name: DescriptionLengthRule.ruleName,
28
+ defaultSeverity: DescriptionLengthRule.defaultSeverity,
29
+ help: 'Check if description is too long.',
30
+ ),
31
+ const CheckType(
32
+ name: DisallowedFieldRule.ruleName,
33
+ defaultSeverity: DisallowedFieldRule.defaultSeverity,
34
+ help: 'Check for disallowed fields in YAML metadata.',
35
+ ),
36
+ const CheckType(
37
+ name: NameFormatRule.ruleName,
38
+ defaultSeverity: NameFormatRule.defaultSeverity,
39
+ help: 'Check if skill name is invalid.',
40
+ ),
41
+ const CheckType(
42
+ name: RelativePathsRule.ruleName,
43
+ defaultSeverity: RelativePathsRule.defaultSeverity,
44
+ help: 'Check if relative paths exist.',
45
+ ),
46
+ const CheckType(
47
+ name: TrailingWhitespaceRule.ruleName,
48
+ defaultSeverity: TrailingWhitespaceRule.defaultSeverity,
49
+ help: 'Check for trailing whitespace (allows exactly 2 spaces for line breaks).',
50
+ ),
51
+ const CheckType(
52
+ name: ValidYamlMetadataRule.ruleName,
53
+ defaultSeverity: ValidYamlMetadataRule.defaultSeverity,
54
+ help: 'Check if YAML metadata is valid.',
55
+ ),
56
+ ];
57
+
58
+ /// Creates a rule instance by name, or returns null if not a class-based rule.
59
+ static SkillRule? createRule(String name, AnalysisSeverity severity) {
60
+ switch (name) {
61
+ case AbsolutePathsRule.ruleName:
62
+ return AbsolutePathsRule(severity: severity);
63
+ case DescriptionLengthRule.ruleName:
64
+ return DescriptionLengthRule(severity: severity);
65
+ case DisallowedFieldRule.ruleName:
66
+ return DisallowedFieldRule(severity: severity);
67
+ case NameFormatRule.ruleName:
68
+ return NameFormatRule(severity: severity);
69
+ case RelativePathsRule.ruleName:
70
+ return RelativePathsRule(severity: severity);
71
+ case TrailingWhitespaceRule.ruleName:
72
+ return TrailingWhitespaceRule(severity: severity);
73
+ case ValidYamlMetadataRule.ruleName:
74
+ return ValidYamlMetadataRule(severity: severity);
75
+ default:
76
+ return null;
77
+ }
78
+ }
79
+ }