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,131 @@
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:skills/src/services/markdown_converter.dart';
6
+ import 'package:test/test.dart';
7
+
8
+ void main() {
9
+ group('MarkdownConverter Tables', () {
10
+ late MarkdownConverter converter;
11
+
12
+ setUp(() {
13
+ converter = MarkdownConverter();
14
+ });
15
+
16
+ test('converts simple table', () {
17
+ const html = '''
18
+ <table>
19
+ <thead>
20
+ <tr>
21
+ <th>Header 1</th>
22
+ <th>Header 2</th>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ <tr>
27
+ <td>Cell 1</td>
28
+ <td>Cell 2</td>
29
+ </tr>
30
+ </tbody>
31
+ </table>
32
+ ''';
33
+ final markdown = converter.convert(html);
34
+ expect(markdown, contains('| Header 1 | Header 2 |'));
35
+ expect(markdown, contains('|---|---|'));
36
+ expect(markdown, contains('| Cell 1 | Cell 2 |'));
37
+ });
38
+
39
+ test('converts table without thead', () {
40
+ const html = '''
41
+ <table>
42
+ <tr>
43
+ <td>Cell 1</td>
44
+ <td>Cell 2</td>
45
+ </tr>
46
+ </table>
47
+ ''';
48
+ final markdown = converter.convert(html);
49
+ // Fallback: treated as table with first row as header
50
+ expect(markdown, contains('| Cell 1 | Cell 2 |'));
51
+ expect(markdown, contains('|---|---|'));
52
+ });
53
+
54
+ test('converts definition lists', () {
55
+ const html = '''
56
+ <dl>
57
+ <dt>Term 1</dt>
58
+ <dd>Definition 1</dd>
59
+ <dt>Term 2</dt>
60
+ <dd>Definition 2</dd>
61
+ </dl>
62
+ ''';
63
+ final markdown = converter.convert(html);
64
+ expect(markdown, contains('**Term 1**'));
65
+ expect(markdown, contains(': Definition 1'));
66
+ expect(markdown, contains('**Term 2**'));
67
+ expect(markdown, contains(': Definition 2'));
68
+ });
69
+
70
+ test('converts details/summary', () {
71
+ const html = '''
72
+ <details>
73
+ <summary>Summary</summary>
74
+ Details content
75
+ </details>
76
+ ''';
77
+ // We'll preserve HTML for details as it's often supported in markdown rendering
78
+ // OR we can just output the content.
79
+ // Preserving HTML is usually safer for details.
80
+ final markdown = converter.convert(html);
81
+ expect(markdown, contains('<details>'));
82
+ expect(markdown, contains('<summary>Summary</summary>'));
83
+ expect(markdown, contains('Details content'));
84
+ expect(markdown, contains('</details>'));
85
+ });
86
+ test('converts nested tables without flattening', () {
87
+ const html = '''
88
+ <table>
89
+ <tr>
90
+ <td>Outer 1</td>
91
+ <td>
92
+ <table>
93
+ <tr><td>Inner 1</td></tr>
94
+ </table>
95
+ </td>
96
+ </tr>
97
+ </table>
98
+ ''';
99
+ final markdown = converter.convert(html);
100
+ // Outer row should contain both outer text and inner table result.
101
+ expect(markdown, contains('| Outer 1 |'));
102
+ expect(markdown, contains('| Inner 1 |'));
103
+ // The outer table should not have inner cells as separate columns of the outer structure.
104
+ });
105
+ test('handles empty table gracefully', () {
106
+ const html = '<table></table>';
107
+ final markdown = converter.convert(html);
108
+ expect(markdown, isEmpty);
109
+ });
110
+
111
+ test('converts table with multiple tbodies', () {
112
+ const html = '''
113
+ <table>
114
+ <thead>
115
+ <tr><th>Header</th></tr>
116
+ </thead>
117
+ <tbody>
118
+ <tr><td>Row 1</td></tr>
119
+ </tbody>
120
+ <tbody>
121
+ <tr><td>Row 2</td></tr>
122
+ </tbody>
123
+ </table>
124
+ ''';
125
+ final markdown = converter.convert(html);
126
+ expect(markdown, contains('| Header |'));
127
+ expect(markdown, contains('| Row 1 |'));
128
+ expect(markdown, contains('| Row 2 |'));
129
+ });
130
+ });
131
+ }
@@ -0,0 +1,37 @@
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:skills/src/models/skill_params.dart';
6
+ import 'package:test/test.dart';
7
+
8
+ void main() {
9
+ group('SkillParams', () {
10
+ test('fromJson parses correctly without instructions', () {
11
+ final json = {
12
+ 'name': 'test-skill',
13
+ 'description': 'Test Description',
14
+ 'resources': ['http://example.com'],
15
+ };
16
+ final skill = SkillParams.fromJson(json);
17
+ expect(skill.name, 'test-skill');
18
+ expect(skill.description, 'Test Description');
19
+ expect(skill.resources, ['http://example.com']);
20
+ expect(skill.instructions, isNull);
21
+ });
22
+
23
+ test('fromJson parses correctly with instructions', () {
24
+ final json = {
25
+ 'name': 'test-skill',
26
+ 'description': 'Test Description',
27
+ 'instructions': 'Do not hallucinate.',
28
+ 'resources': ['http://example.com'],
29
+ };
30
+ final skill = SkillParams.fromJson(json);
31
+ expect(skill.name, 'test-skill');
32
+ expect(skill.description, 'Test Description');
33
+ expect(skill.resources, ['http://example.com']);
34
+ expect(skill.instructions, 'Do not hallucinate.');
35
+ });
36
+ });
37
+ }
@@ -0,0 +1,291 @@
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
+
7
+ import 'package:http/http.dart' as http;
8
+ import 'package:http/testing.dart';
9
+
10
+ import 'package:skills/src/services/gemini_service.dart';
11
+ import 'package:test/test.dart';
12
+
13
+ void main() {
14
+ group('GeminiService', () {
15
+ late GeminiService service;
16
+
17
+ setUp(() {
18
+ service = GeminiService(
19
+ apiKey: 'test-api-key',
20
+ httpClient: http.Client(),
21
+ );
22
+ });
23
+
24
+ group('cleanContent', () {
25
+ test('returns null for null content', () {
26
+ expect(service.cleanContent(null), isNull);
27
+ });
28
+
29
+ test('removes markdown code blocks around content', () {
30
+ const content = '''
31
+ ```markdown
32
+ # Title
33
+ Some content
34
+ ```
35
+ ''';
36
+ const expected = '''
37
+ # Title
38
+ Some content
39
+ ''';
40
+ expect(service.cleanContent(content), expected);
41
+ });
42
+
43
+ test('removes markdown code blocks with other languages', () {
44
+ const content = '''
45
+ ```text
46
+ # Title
47
+ Some content
48
+ ```
49
+ ''';
50
+ const expected = '''
51
+ # Title
52
+ Some content
53
+ ''';
54
+ expect(service.cleanContent(content), expected);
55
+ });
56
+
57
+ test('ignores trailing markdown code block if no start block', () {
58
+ const content = '''
59
+ # Title
60
+ Some content
61
+ ```
62
+ ''';
63
+ const expected = '''
64
+ # Title
65
+ Some content
66
+ ```
67
+ ''';
68
+ expect(service.cleanContent(content), expected);
69
+ });
70
+
71
+ test('strips possible frontmatter at start', () {
72
+ const content = '''
73
+ ---
74
+ key: value
75
+ ---
76
+ # Title
77
+ Some content
78
+ ''';
79
+ const expected = '''
80
+ # Title
81
+ Some content
82
+ ''';
83
+ expect(service.cleanContent(content), expected);
84
+ });
85
+
86
+ test('strips possible frontmatter after some noise', () {
87
+ const content = '''
88
+ Here is the content:
89
+ ---
90
+ key: value
91
+ ---
92
+ # Title
93
+ Some content
94
+ ''';
95
+ const expected = '''
96
+ # Title
97
+ Some content
98
+ ''';
99
+ expect(service.cleanContent(content), expected);
100
+ });
101
+
102
+ test('preserves internal code blocks', () {
103
+ const content = '''
104
+ # Title
105
+ Here is some code:
106
+ ```dart
107
+ void main() {}
108
+ ```
109
+ ''';
110
+ const expected = '''
111
+ # Title
112
+ Here is some code:
113
+ ```dart
114
+ void main() {}
115
+ ```
116
+ ''';
117
+ expect(service.cleanContent(content), expected);
118
+ });
119
+
120
+ test('ensures content ends with newline', () {
121
+ const content = 'Some content';
122
+ const expected = 'Some content\n';
123
+ expect(service.cleanContent(content), expected);
124
+ });
125
+
126
+ test('handles complex nested structure', () {
127
+ const content = '''
128
+ ```markdown
129
+ ---
130
+ key: value
131
+ ---
132
+ # Title
133
+ Content with code:
134
+ ```dart
135
+ print('hello');
136
+ ```
137
+ ```
138
+ ''';
139
+ const expected = '''
140
+ # Title
141
+ Content with code:
142
+ ```dart
143
+ print('hello');
144
+ ```
145
+ ''';
146
+ expect(service.cleanContent(content), expected);
147
+ });
148
+ test('removes markdown code blocks with leading whitespace', () {
149
+ const content = '''
150
+ ```markdown
151
+ # Title
152
+ Some content
153
+ ```
154
+ ''';
155
+ const expected = '''
156
+ # Title
157
+ Some content
158
+ ''';
159
+ expect(service.cleanContent(content), expected);
160
+ });
161
+
162
+ test(
163
+ 'removes markdown code blocks with trailing whitespace on fence',
164
+ () {
165
+ const content = '''
166
+ ```markdown
167
+ # Title
168
+ Some content
169
+ ```
170
+ ''';
171
+ const expected = '''
172
+ # Title
173
+ Some content
174
+ ''';
175
+ expect(service.cleanContent(content), expected);
176
+ },
177
+ );
178
+
179
+ test('removes markdown code blocks with uppercase language', () {
180
+ const content = '''
181
+ ```MARKDOWN
182
+ # Title
183
+ Some content
184
+ ```
185
+ ''';
186
+ const expected = '''
187
+ # Title
188
+ Some content
189
+ ''';
190
+ expect(service.cleanContent(content), expected);
191
+ });
192
+ });
193
+
194
+ group('generateSkillContent front matter', () {
195
+ test('does not wrap metadata values with quotes', () async {
196
+ final mockClient = MockClient((request) async {
197
+ return http.Response(
198
+ jsonEncode({
199
+ 'candidates': [
200
+ {
201
+ 'content': {
202
+ 'parts': [
203
+ {'text': 'Markdown body'},
204
+ ],
205
+ },
206
+ },
207
+ ],
208
+ }),
209
+ 200,
210
+ );
211
+ });
212
+
213
+ final serviceWithMock = GeminiService(
214
+ apiKey: 'test-api-key',
215
+ httpClient: mockClient,
216
+ model: 'models/test-model',
217
+ );
218
+
219
+ final result = await serviceWithMock.generateSkillContent(
220
+ 'Raw Content',
221
+ 'test-skill',
222
+ 'Test description without quotes',
223
+ );
224
+
225
+ expect(result, isNotNull);
226
+ expect(result, contains('name: test-skill'));
227
+ expect(
228
+ result,
229
+ contains('description: Test description without quotes'),
230
+ );
231
+ expect(result, contains('model: models/test-model'));
232
+ // Make sure it doesn't contain double quotes for the fields
233
+ expect(result, isNot(contains('name: "test-skill"')));
234
+ expect(
235
+ result,
236
+ isNot(contains('description: "Test description without quotes"')),
237
+ );
238
+ expect(result, isNot(contains('model: "models/test-model"')));
239
+ });
240
+ });
241
+
242
+ group('updateSkillContent front matter', () {
243
+ test('does not wrap metadata values with quotes', () async {
244
+ final mockClient = MockClient((request) async {
245
+ return http.Response(
246
+ jsonEncode({
247
+ 'candidates': [
248
+ {
249
+ 'content': {
250
+ 'parts': [
251
+ {'text': 'Markdown body'},
252
+ ],
253
+ },
254
+ },
255
+ ],
256
+ }),
257
+ 200,
258
+ );
259
+ });
260
+
261
+ final serviceWithMock = GeminiService(
262
+ apiKey: 'test-api-key',
263
+ httpClient: mockClient,
264
+ model: 'models/test-model',
265
+ );
266
+
267
+ final result = await serviceWithMock.updateSkillContent(
268
+ 'Existing Content',
269
+ 'Raw Content',
270
+ 'test-skill',
271
+ 'Test description without quotes',
272
+ );
273
+
274
+ expect(result, isNotNull);
275
+ expect(result, contains('name: test-skill'));
276
+ expect(
277
+ result,
278
+ contains('description: Test description without quotes'),
279
+ );
280
+ expect(result, contains('model: models/test-model'));
281
+ // Make sure it doesn't contain double quotes for the fields
282
+ expect(result, isNot(contains('name: "test-skill"')));
283
+ expect(
284
+ result,
285
+ isNot(contains('description: "Test description without quotes"')),
286
+ );
287
+ expect(result, isNot(contains('model: "models/test-model"')));
288
+ });
289
+ });
290
+ });
291
+ }
@@ -0,0 +1,156 @@
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:skills/src/services/markdown_converter.dart';
6
+ import 'package:test/test.dart';
7
+
8
+ void main() {
9
+ group('MarkdownConverter', () {
10
+ late MarkdownConverter converter;
11
+
12
+ setUp(() {
13
+ converter = MarkdownConverter();
14
+ });
15
+
16
+ test('converts headers', () {
17
+ expect(converter.convert('<h1>Title</h1>'), contains('# Title'));
18
+ expect(converter.convert('<h2>Subtitle</h2>'), contains('## Subtitle'));
19
+ expect(converter.convert('<h3>Section</h3>'), contains('### Section'));
20
+ });
21
+
22
+ test('converts paragraphs', () {
23
+ expect(converter.convert('<p>Hello World</p>'), contains('Hello World'));
24
+ });
25
+
26
+ test('converts links', () {
27
+ expect(
28
+ converter.convert('<a href="https://example.com">Link</a>'),
29
+ contains('[Link](https://example.com)'),
30
+ );
31
+ });
32
+
33
+ test('converts bold and italic', () {
34
+ expect(converter.convert('<b>Bold</b>'), contains('**Bold**'));
35
+ expect(
36
+ converter.convert('<strong>Strong</strong>'),
37
+ contains('**Strong**'),
38
+ );
39
+ expect(converter.convert('<i>Italic</i>'), contains('*Italic*'));
40
+ expect(
41
+ converter.convert('<em>Emphasized</em>'),
42
+ contains('*Emphasized*'),
43
+ );
44
+ });
45
+
46
+ test('converts code', () {
47
+ expect(
48
+ converter.convert('<code>print("hello")</code>'),
49
+ contains('`print("hello")`'),
50
+ );
51
+ expect(
52
+ converter.convert('<pre>void main() {}</pre>'),
53
+ allOf(contains('```'), contains('void main() {}')),
54
+ );
55
+ });
56
+
57
+ test('converts lists', () {
58
+ const html = '''
59
+ <ul>
60
+ <li>Item 1</li>
61
+ <li>Item 2</li>
62
+ </ul>
63
+ ''';
64
+ final md = converter.convert(html);
65
+ expect(md, contains('- Item 1'));
66
+ expect(md, contains('- Item 2'));
67
+
68
+ const htmlOl = '''
69
+ <ol>
70
+ <li>First</li>
71
+ <li>Second</li>
72
+ </ol>
73
+ ''';
74
+ final mdOl = converter.convert(htmlOl);
75
+ // Currently the converter uses simplified list handling (returning - for both)
76
+ expect(mdOl, contains('- First'));
77
+ expect(mdOl, contains('- Second'));
78
+ });
79
+
80
+ test('converts line breaks', () {
81
+ expect(converter.convert('Line 1<br>Line 2'), contains('Line 1\nLine 2'));
82
+ });
83
+
84
+ test('converts structural elements', () {
85
+ expect(
86
+ converter.convert('<div>Div Content</div>'),
87
+ contains('Div Content'),
88
+ );
89
+ expect(
90
+ converter.convert('<section>Section Content</section>'),
91
+ contains('Section Content'),
92
+ );
93
+ expect(
94
+ converter.convert('<main>Main Content</main>'),
95
+ contains('Main Content'),
96
+ );
97
+ expect(
98
+ converter.convert('<article>Article Content</article>'),
99
+ contains('Article Content'),
100
+ );
101
+ });
102
+
103
+ test('handles empty body', () {
104
+ expect(converter.convert(''), isEmpty);
105
+ });
106
+
107
+ test('converts table without thead or tbody', () {
108
+ const html = '''
109
+ <table>
110
+ <tr><td>Data 1</td><td>Data 2</td></tr>
111
+ <tr><td>Data 3</td><td>Data 4</td></tr>
112
+ </table>
113
+ ''';
114
+ expect(converter.convert(html), contains('| Data 1 | Data 2 |'));
115
+ expect(converter.convert(html), contains('| Data 3 | Data 4 |'));
116
+ });
117
+
118
+ test('converts video tag with nested source', () {
119
+ expect(
120
+ converter.convert(
121
+ '<video><source src="https://example.com/video.mp4"></video>',
122
+ ),
123
+ contains('[Video](https://example.com/video.mp4)'),
124
+ );
125
+ });
126
+
127
+ test('converts definition lists (dl, dt, dd)', () {
128
+ const html = '''
129
+ <dl>
130
+ <dt>Term 1</dt>
131
+ <dd>Definition 1</dd>
132
+ </dl>
133
+ ''';
134
+ final md = converter.convert(html);
135
+ expect(md, contains('**Term 1**'));
136
+ expect(md, contains('Definition 1'));
137
+ });
138
+
139
+ test('converts iframes', () {
140
+ expect(
141
+ converter.convert('<iframe src="https://example.com/embed"></iframe>'),
142
+ contains('[Iframe](https://example.com/embed)'),
143
+ );
144
+ });
145
+ test('retains details and summary as HTML', () {
146
+ const html = '''
147
+ <details>
148
+ <summary>Click to expand</summary>
149
+ Hidden content
150
+ </details>
151
+ ''';
152
+ final md = converter.convert(html);
153
+ expect(md, contains('<details>'));
154
+ });
155
+ });
156
+ }