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,74 +0,0 @@
1
- import 'dart:io';
2
- import 'package:path/path.dart';
3
- import '../fixable_rule.dart';
4
- import '../models/analysis_severity.dart';
5
- import '../models/skill_context.dart';
6
- import '../models/skill_rule.dart';
7
- import '../models/validation_error.dart';
8
-
9
- /// Enforces that links in SKILL.md do not use absolute paths.
10
- class AbsolutePathsRule extends SkillRule implements FixableRule {
11
- AbsolutePathsRule({this.severity = defaultSeverity});
12
-
13
- static const String ruleName = 'check-absolute-paths';
14
- static const AnalysisSeverity defaultSeverity = AnalysisSeverity.warning;
15
-
16
- @override
17
- String get name => ruleName;
18
-
19
- @override
20
- final AnalysisSeverity severity;
21
-
22
- static final _markdownLinkRegex = RegExp(r'\[.*?\]\((.*?)\)');
23
- static const String _skillFileName = SkillContext.skillFileName;
24
-
25
- @override
26
- Future<List<ValidationError>> validate(SkillContext context) async {
27
- final errors = <ValidationError>[];
28
-
29
- // Extract content after YAML frontmatter
30
- final skillStartRegex = RegExp(r'^---\s*\n(.*?)\n---\s*\n', dotAll: true);
31
- final RegExpMatch? match = skillStartRegex.firstMatch(context.rawContent);
32
- final String markdownContent = match != null
33
- ? context.rawContent.substring(match.end)
34
- : context.rawContent;
35
-
36
- for (final RegExpMatch linkMatch in _markdownLinkRegex.allMatches(markdownContent)) {
37
- final String path = linkMatch.group(1)!;
38
- if (isAbsolute(path) || windows.isAbsolute(path)) {
39
- errors.add(
40
- ValidationError(
41
- ruleId: name,
42
- severity: severity,
43
- file: _skillFileName,
44
- message: 'Absolute filepath found in link: $path',
45
- ),
46
- );
47
- }
48
- }
49
-
50
- return errors;
51
- }
52
-
53
- @override
54
- Future<String> fix(String filePath, String currentContent, Directory directory) async {
55
- if (filePath != SkillContext.skillFileName) {
56
- return currentContent;
57
- }
58
-
59
- return currentContent.replaceAllMapped(_markdownLinkRegex, (match) {
60
- final String path = match.group(1)!;
61
- if (isAbsolute(path) || windows.isAbsolute(path)) {
62
- final file = File(path);
63
- if (file.existsSync()) {
64
- final String relativePath = relative(path, from: directory.path);
65
- final String posixRelativePath = relativePath.replaceAll(r'\', '/');
66
- final String fullMatch = match.group(0)!;
67
- final int lastParen = fullMatch.lastIndexOf('(');
68
- return '${fullMatch.substring(0, lastParen + 1)}$posixRelativePath)';
69
- }
70
- }
71
- return match.group(0)!;
72
- });
73
- }
74
- }
@@ -1,49 +0,0 @@
1
- import 'package:yaml/yaml.dart';
2
- import '../models/analysis_severity.dart';
3
- import '../models/skill_context.dart';
4
- import '../models/skill_rule.dart';
5
- import '../models/validation_error.dart';
6
-
7
- /// Enforces that the description field is not too long.
8
- class DescriptionLengthRule extends SkillRule {
9
- DescriptionLengthRule({this.severity = defaultSeverity});
10
-
11
- static const String ruleName = 'description-too-long';
12
- static const AnalysisSeverity defaultSeverity = AnalysisSeverity.error;
13
-
14
- @override
15
- String get name => ruleName;
16
-
17
- @override
18
- final AnalysisSeverity severity;
19
-
20
- static const maxDescriptionLength = 1024;
21
- static const _skillFileName = 'SKILL.md';
22
- static const _descriptionFieldUrl = 'https://agentskills.io/specification#description-field';
23
-
24
- @override
25
- Future<List<ValidationError>> validate(SkillContext context) async {
26
- final errors = <ValidationError>[];
27
-
28
- if (context.parsedYaml == null) {
29
- return errors;
30
- }
31
-
32
- final YamlMap yaml = context.parsedYaml!;
33
- final String description = yaml['description']?.toString() ?? '';
34
-
35
- if (description.length > maxDescriptionLength) {
36
- errors.add(
37
- ValidationError(
38
- ruleId: name,
39
- severity: severity,
40
- file: _skillFileName,
41
- message:
42
- 'Description field is too long. Maximum $maxDescriptionLength characters (see $_descriptionFieldUrl)',
43
- ),
44
- );
45
- }
46
-
47
- return errors;
48
- }
49
- }
@@ -1,61 +0,0 @@
1
- import 'package:yaml/yaml.dart';
2
- import '../models/analysis_severity.dart';
3
- import '../models/skill_context.dart';
4
- import '../models/skill_rule.dart';
5
- import '../models/validation_error.dart';
6
-
7
- /// Enforces that only allowed fields are present in YAML metadata.
8
- class DisallowedFieldRule extends SkillRule {
9
- DisallowedFieldRule({this.severity = defaultSeverity});
10
-
11
- static const String ruleName = 'disallowed-field';
12
- static const AnalysisSeverity defaultSeverity = AnalysisSeverity.disabled;
13
-
14
- @override
15
- String get name => ruleName;
16
-
17
- @override
18
- final AnalysisSeverity severity;
19
-
20
- static const _allowedFields = {
21
- 'name',
22
- 'description',
23
- 'license',
24
- 'allowed-tools',
25
- 'metadata',
26
- 'compatibility',
27
- 'category',
28
- 'tags',
29
- 'version',
30
- 'eval_task',
31
- };
32
-
33
- static const _skillFileName = 'SKILL.md';
34
- static const _metadataUrl = 'https://agentskills.io/specification#frontmatter';
35
-
36
- @override
37
- Future<List<ValidationError>> validate(SkillContext context) async {
38
- final errors = <ValidationError>[];
39
-
40
- if (context.parsedYaml == null) {
41
- return errors;
42
- }
43
-
44
- final YamlMap yaml = context.parsedYaml!;
45
- for (final Object? key in yaml.keys) {
46
- final bool isDisallowed = key is! String || !_allowedFields.contains(key);
47
- if (isDisallowed) {
48
- errors.add(
49
- ValidationError(
50
- ruleId: name,
51
- severity: severity,
52
- file: _skillFileName,
53
- message: 'Disallowed field: $key (see $_metadataUrl)',
54
- ),
55
- );
56
- }
57
- }
58
-
59
- return errors;
60
- }
61
- }
@@ -1,167 +0,0 @@
1
- import 'dart:io';
2
- import 'package:meta/meta.dart';
3
- import 'package:path/path.dart';
4
- import 'package:yaml/yaml.dart';
5
- import '../fixable_rule.dart';
6
- import '../models/analysis_severity.dart';
7
- import '../models/skill_context.dart';
8
- import '../models/skill_rule.dart';
9
- import '../models/validation_error.dart';
10
-
11
- /// Enforces constraints on the skill name field.
12
- class NameFormatRule extends SkillRule implements FixableRule {
13
- NameFormatRule({this.severity = defaultSeverity});
14
-
15
- static const String ruleName = 'invalid-skill-name';
16
- static const AnalysisSeverity defaultSeverity = AnalysisSeverity.error;
17
-
18
- @override
19
- String get name => ruleName;
20
-
21
- @override
22
- final AnalysisSeverity severity;
23
-
24
- static const maxNameLength = 64;
25
- static final _validNameRegex = RegExp(r'^[a-z0-9\-]+$');
26
- static const String _skillFileName = SkillContext.skillFileName;
27
- static const _nameFieldUrl = 'https://agentskills.io/specification#name-field';
28
-
29
- @override
30
- Future<List<ValidationError>> validate(SkillContext context) async {
31
- final errors = <ValidationError>[];
32
-
33
- if (context.parsedYaml == null) {
34
- return errors;
35
- }
36
-
37
- final YamlMap yaml = context.parsedYaml!;
38
- final String skillName = getNameNode(yaml)?.value.toString() ?? '';
39
-
40
- if (skillName.isEmpty) {
41
- return errors; // Handled by required fields check
42
- }
43
-
44
- if (skillName != skillName.toLowerCase()) {
45
- errors.add(
46
- ValidationError(
47
- ruleId: name,
48
- severity: severity,
49
- file: _skillFileName,
50
- message: 'Skill name must be lowercase: $skillName (see $_nameFieldUrl)',
51
- ),
52
- );
53
- }
54
-
55
- if (skillName.length > maxNameLength) {
56
- errors.add(
57
- ValidationError(
58
- ruleId: name,
59
- severity: severity,
60
- file: _skillFileName,
61
- message: 'Skill name too long. Maximum $maxNameLength characters (see $_nameFieldUrl)',
62
- ),
63
- );
64
- }
65
-
66
- if (!_validNameRegex.hasMatch(skillName)) {
67
- errors.add(
68
- ValidationError(
69
- ruleId: name,
70
- severity: severity,
71
- file: _skillFileName,
72
- message:
73
- 'Skill name contains invalid characters. Only lowercase letters, digits, and hyphens allowed (see $_nameFieldUrl)',
74
- ),
75
- );
76
- }
77
-
78
- if (skillName.startsWith('-') || skillName.endsWith('-')) {
79
- errors.add(
80
- ValidationError(
81
- ruleId: name,
82
- severity: severity,
83
- file: _skillFileName,
84
- message: 'Skill name cannot have leading or trailing hyphens (see $_nameFieldUrl)',
85
- ),
86
- );
87
- }
88
-
89
- if (skillName.contains('--')) {
90
- errors.add(
91
- ValidationError(
92
- ruleId: name,
93
- severity: severity,
94
- file: _skillFileName,
95
- message: 'Skill name cannot have consecutive hyphens (see $_nameFieldUrl)',
96
- ),
97
- );
98
- }
99
-
100
- final String dirName = basename(context.directory.path);
101
- if (skillName != dirName) {
102
- errors.add(
103
- ValidationError(
104
- ruleId: name,
105
- severity: severity,
106
- file: _skillFileName,
107
- message:
108
- 'Skill name ($skillName) must exactly match the parent directory name ($dirName) (see $_nameFieldUrl)',
109
- ),
110
- );
111
- }
112
-
113
- return errors;
114
- }
115
-
116
- @override
117
- Future<String> fix(String filePath, String currentContent, Directory directory) async {
118
- if (filePath != SkillContext.skillFileName) {
119
- return currentContent;
120
- }
121
-
122
- final RegExpMatch? match = SkillContext.skillStartRegex.firstMatch(currentContent);
123
- if (match == null) {
124
- return currentContent;
125
- }
126
- final String yamlStr = match.group(1)!;
127
-
128
- final Object? yamlObj;
129
- try {
130
- yamlObj = loadYaml(yamlStr);
131
- } catch (e) {
132
- return currentContent;
133
- }
134
-
135
- if (yamlObj is! YamlMap) {
136
- return currentContent;
137
- }
138
-
139
- final YamlMap yaml = yamlObj;
140
- final YamlNode? nameNode = getNameNode(yaml);
141
- if (nameNode == null) {
142
- return currentContent;
143
- }
144
-
145
- final String dirName = basename(directory.path);
146
-
147
- final currentName = nameNode.value.toString();
148
- if (currentName == dirName) {
149
- return currentContent;
150
- }
151
-
152
- final int yamlOffset = currentContent.indexOf(yamlStr, match.start);
153
-
154
- // ignore: specify_nonobvious_local_variable_types
155
- final span = nameNode.span;
156
- final String before = currentContent.substring(0, yamlOffset + span.start.offset);
157
- final String after = currentContent.substring(yamlOffset + span.end.offset);
158
-
159
- return '$before$dirName$after';
160
- }
161
-
162
- /// Returns the YAML node for the skill name.
163
- @visibleForTesting
164
- static YamlNode? getNameNode(YamlMap yaml) {
165
- return yaml.nodes['name'];
166
- }
167
- }
@@ -1,72 +0,0 @@
1
- import 'dart:io';
2
- import 'package:path/path.dart';
3
- import '../models/analysis_severity.dart';
4
- import '../models/skill_context.dart';
5
- import '../models/skill_rule.dart';
6
- import '../models/validation_error.dart';
7
-
8
- /// Enforces that relative links in SKILL.md point to existing files.
9
- class RelativePathsRule extends SkillRule {
10
- RelativePathsRule({this.severity = defaultSeverity});
11
-
12
- static const String ruleName = 'check-relative-paths';
13
- static const AnalysisSeverity defaultSeverity = AnalysisSeverity.disabled;
14
-
15
- @override
16
- String get name => ruleName;
17
-
18
- @override
19
- final AnalysisSeverity severity;
20
-
21
- static final _markdownLinkRegex = RegExp(r'\[.*?\]\((.*?)\)');
22
- static const _skillFileName = 'SKILL.md';
23
-
24
- @override
25
- Future<List<ValidationError>> validate(SkillContext context) async {
26
- final errors = <ValidationError>[];
27
-
28
- // Extract content after YAML frontmatter
29
- final skillStartRegex = RegExp(r'^---\s*\n(.*?)\n---\s*\n', dotAll: true);
30
- final RegExpMatch? match = skillStartRegex.firstMatch(context.rawContent);
31
- final String markdownContent = match != null
32
- ? context.rawContent.substring(match.end)
33
- : context.rawContent;
34
-
35
- for (final RegExpMatch linkMatch in _markdownLinkRegex.allMatches(markdownContent)) {
36
- final String fullPath = linkMatch.group(1)!;
37
- // Markdown links can have a title after the URL, separated by spaces.
38
- // e.g. [text](url "title")
39
- final String path = fullPath.trim().split(RegExp(r'\s+')).first;
40
-
41
- // Skip absolute paths (handled by AbsolutePathsRule)
42
- if (isAbsolute(path) || windows.isAbsolute(path)) {
43
- continue;
44
- }
45
-
46
- var effectivePath = path;
47
- try {
48
- final Uri uri = Uri.parse(path);
49
- if (uri.hasScheme || path.startsWith('#')) {
50
- continue; // Ignore web URLs, email links, anchors, etc.
51
- }
52
- effectivePath = uri.path;
53
- } catch (_) {
54
- // If Uri parsing fails, treat it as a potential filepath.
55
- }
56
-
57
- final linkedFile = File(join(context.directory.path, effectivePath));
58
- if (!linkedFile.existsSync()) {
59
- errors.add(
60
- ValidationError(
61
- ruleId: name,
62
- severity: severity,
63
- file: _skillFileName,
64
- message: 'Linked file does not exist: $path',
65
- ),
66
- );
67
- }
68
- }
69
-
70
- return errors;
71
- }
72
- }
@@ -1,93 +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:io';
6
- import 'package:meta/meta.dart';
7
-
8
- import '../fixable_rule.dart';
9
- import '../models/analysis_severity.dart';
10
- import '../models/skill_context.dart';
11
- import '../models/skill_rule.dart';
12
- import '../models/validation_error.dart';
13
-
14
- /// Enforces that lines in SKILL.md do not have trailing whitespace,
15
- /// except for exactly two spaces which indicate a hard line break.
16
- class TrailingWhitespaceRule extends SkillRule implements FixableRule {
17
- TrailingWhitespaceRule({this.severity = defaultSeverity});
18
-
19
- static const String ruleName = 'check-trailing-whitespace';
20
- static const AnalysisSeverity defaultSeverity = AnalysisSeverity.disabled;
21
- static final RegExp _whitespaceRegExp = RegExp(r'([ \t]+)$');
22
-
23
- @override
24
- String get name => ruleName;
25
-
26
- @override
27
- final AnalysisSeverity severity;
28
-
29
- @override
30
- Future<List<ValidationError>> validate(SkillContext context) async {
31
- final errors = <ValidationError>[];
32
- final List<String> lines = context.rawContent.split('\n');
33
-
34
- for (var i = 0; i < lines.length; i++) {
35
- final String line = lines[i];
36
-
37
- // Remove carriage return if present (Windows line endings)
38
- final String trimmedLine = line.endsWith('\r') ? line.substring(0, line.length - 1) : line;
39
-
40
- final RegExpMatch? match = _whitespaceRegExp.firstMatch(trimmedLine);
41
- if (match != null) {
42
- final String whitespace = match.group(1)!;
43
- String? message;
44
-
45
- if (whitespace.contains('\t')) {
46
- message = 'Line ${i + 1} has trailing whitespace containing tabs.';
47
- } else {
48
- final int spacesCount = whitespace.length;
49
- if (spacesCount == 1 || spacesCount >= 3) {
50
- message =
51
- 'Line ${i + 1} has $spacesCount trailing space(s). Only exactly 2 spaces are allowed for line breaks.';
52
- }
53
- }
54
-
55
- if (message != null) {
56
- errors.add(
57
- ValidationError(ruleId: name, severity: severity, file: 'SKILL.md', message: message),
58
- );
59
- }
60
- }
61
- }
62
-
63
- return errors;
64
- }
65
-
66
- @override
67
- Future<String> fix(String filePath, String currentContent, Directory directory) async {
68
- if (filePath != 'SKILL.md') {
69
- return currentContent;
70
- }
71
-
72
- return currentContent.split('\n').map(fixLine).join('\n');
73
- }
74
-
75
- @visibleForTesting
76
- String fixLine(String line) {
77
- final bool hasCR = line.endsWith('\r');
78
- final String lineWithoutCR = hasCR ? line.substring(0, line.length - 1) : line;
79
-
80
- final RegExpMatch? match = _whitespaceRegExp.firstMatch(lineWithoutCR);
81
- if (match == null) {
82
- return line;
83
- }
84
-
85
- final String whitespace = match.group(1)!;
86
- if (whitespace == ' ') {
87
- return line; // Keep the 2 space hard line break.
88
- }
89
-
90
- final String fixedLine = lineWithoutCR.replaceAll(_whitespaceRegExp, '');
91
- return hasCR ? '$fixedLine\r' : fixedLine;
92
- }
93
- }
@@ -1,74 +0,0 @@
1
- import 'package:yaml/yaml.dart';
2
- import '../models/analysis_severity.dart';
3
- import '../models/skill_context.dart';
4
- import '../models/skill_rule.dart';
5
- import '../models/validation_error.dart';
6
-
7
- /// Enforces that SKILL.md has valid YAML frontmatter and required fields.
8
- class ValidYamlMetadataRule extends SkillRule {
9
- ValidYamlMetadataRule({this.severity = defaultSeverity});
10
-
11
- static const String ruleName = 'valid-yaml-metadata';
12
- static const AnalysisSeverity defaultSeverity = AnalysisSeverity.error;
13
-
14
- @override
15
- String get name => ruleName;
16
-
17
- @override
18
- final AnalysisSeverity severity;
19
-
20
- static const _requiredFields = {'name', 'description'};
21
- static const _skillFileName = 'SKILL.md';
22
- static const _metadataUrl = 'https://agentskills.io/specification#frontmatter';
23
- static const maxCompatibilityLength = 500;
24
- static const _compatibilityFieldUrl = 'https://agentskills.io/specification#compatibility-field';
25
-
26
- @override
27
- Future<List<ValidationError>> validate(SkillContext context) async {
28
- final errors = <ValidationError>[];
29
-
30
- if (context.parsedYaml == null) {
31
- errors.add(
32
- ValidationError(
33
- ruleId: name,
34
- severity: severity,
35
- file: _skillFileName,
36
- message:
37
- 'Invalid YAML metadata: ${context.yamlParsingError ?? 'Missing or invalid'} (see $_metadataUrl)',
38
- ),
39
- );
40
- return errors;
41
- }
42
-
43
- final YamlMap yaml = context.parsedYaml!;
44
- for (final String field in _requiredFields) {
45
- if (!yaml.containsKey(field)) {
46
- errors.add(
47
- ValidationError(
48
- ruleId: name,
49
- severity: severity,
50
- file: _skillFileName,
51
- message: 'Missing required field: $field (see $_metadataUrl)',
52
- ),
53
- );
54
- }
55
- }
56
-
57
- if (yaml.containsKey('compatibility')) {
58
- final String compatibility = yaml['compatibility']?.toString() ?? '';
59
- if (compatibility.length > maxCompatibilityLength) {
60
- errors.add(
61
- ValidationError(
62
- ruleId: name,
63
- severity: severity,
64
- file: _skillFileName,
65
- message:
66
- 'Compatibility field is too long. Maximum $maxCompatibilityLength characters (see $_compatibilityFieldUrl)',
67
- ),
68
- );
69
- }
70
- }
71
-
72
- return errors;
73
- }
74
- }
@@ -1,36 +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 'models/skills_ignores.dart';
9
-
10
- /// Service class for reading and writing the `SkillsIgnores` model to/from disk.
11
- class SkillsIgnoresStorage {
12
- /// Loads `SkillsIgnores` from the specified path.
13
- ///
14
- /// Returns an empty `SkillsIgnores` if the file does not exist or fails to parse.
15
- Future<SkillsIgnores> load(String path) async {
16
- final file = File(path);
17
- if (!file.existsSync()) {
18
- return SkillsIgnores(skills: {});
19
- }
20
-
21
- try {
22
- final String content = await file.readAsString();
23
- final json = jsonDecode(content) as Map<String, dynamic>;
24
- return SkillsIgnores.fromJson(json);
25
- } catch (_) {
26
- return SkillsIgnores(skills: {});
27
- }
28
- }
29
-
30
- /// Saves `SkillsIgnores` to the specified path.
31
- Future<void> save(String path, SkillsIgnores ignores) async {
32
- final file = File(path);
33
- final String jsonString = const JsonEncoder.withIndent(' ').convert(ignores.toJson());
34
- await file.writeAsString(jsonString);
35
- }
36
- }