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,310 +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' as io;
6
-
7
- import 'package:google_cloud_ai_generativelanguage_v1beta/generativelanguage.dart';
8
- import 'package:http/http.dart' as http;
9
- import 'package:logging/logging.dart';
10
- import 'package:meta/meta.dart';
11
- import 'package:retry/retry.dart';
12
-
13
- import 'prompts.dart';
14
- import 'skill_instructions.dart';
15
-
16
- /// Service for interacting with the Gemini API to generate and validate skills.
17
- class GeminiService {
18
- /// Creates a new [GeminiService].
19
- GeminiService({
20
- required String apiKey,
21
- http.Client? httpClient,
22
- String? model,
23
- }) : _model = model ?? defaultModel,
24
- _client = _ApiKeyClient(httpClient ?? http.Client(), apiKey);
25
-
26
- /// 0.2 is a good temperature for technical material.
27
- static const double defaultTemperature = 0.2;
28
-
29
- /// The default model to use for generation.
30
- static const String defaultModel = 'models/gemini-3.1-pro-preview';
31
-
32
- /// The default token budget for thinking.
33
- static const int defaultThinkingBudget = 4096;
34
-
35
- /// The default max output tokens for generation.
36
- static const int defaultMaxOutputTokens = 8192;
37
-
38
- /// The default safety settings to use for generation.
39
- static final List<SafetySetting> defaultSafetySettings = [
40
- SafetySetting(
41
- category: HarmCategory.harmCategoryDangerousContent,
42
- threshold: SafetySetting_HarmBlockThreshold.blockOnlyHigh,
43
- ),
44
- SafetySetting(
45
- category: HarmCategory.harmCategoryHateSpeech,
46
- threshold: SafetySetting_HarmBlockThreshold.blockOnlyHigh,
47
- ),
48
- SafetySetting(
49
- category: HarmCategory.harmCategoryHarassment,
50
- threshold: SafetySetting_HarmBlockThreshold.blockOnlyHigh,
51
- ),
52
- SafetySetting(
53
- category: HarmCategory.harmCategorySexuallyExplicit,
54
- threshold: SafetySetting_HarmBlockThreshold.blockOnlyHigh,
55
- ),
56
- ];
57
-
58
- final String _model;
59
- final http.Client _client;
60
- final Logger _logger = Logger('GeminiService');
61
-
62
- /// Generates the content for a skill based on raw markdown input.
63
- Future<String?> generateSkillContent(
64
- String rawMarkdown,
65
- String skillName,
66
- String description, {
67
- String? instructions,
68
- int thinkingBudget = defaultThinkingBudget,
69
- }) async {
70
- final service = GenerativeService(client: _client);
71
- final lastModified = io.HttpDate.format(DateTime.now());
72
- final prompt = Prompts.createSkillPrompt(rawMarkdown, instructions);
73
-
74
- final request = _createRequest(
75
- prompt,
76
- systemInstruction: skillInstructions,
77
- thinkingBudget: thinkingBudget,
78
- );
79
-
80
- _logger.info(
81
- ' Model: $_model, Max Output Tokens: $defaultMaxOutputTokens, Thinking Budget: $thinkingBudget',
82
- );
83
-
84
- try {
85
- const r = RetryOptions(maxAttempts: 3);
86
- final response = await r.retry(() async {
87
- final res = await service.generateContent(request);
88
- final text = res.candidates.first.content?.parts
89
- .where((part) => !part.thought)
90
- .map((part) => part.text)
91
- .where((text) => text != null)
92
- .join('\n');
93
-
94
- if (text == null || text.isEmpty) {
95
- throw const FormatException('Empty response from Gemini');
96
- }
97
-
98
- return text;
99
- }, onRetry: (e) => _logger.warning('Retrying Gemini generation: $e'));
100
-
101
- final content = response;
102
-
103
- final frontmatter =
104
- '''---
105
- name: $skillName
106
- description: $description
107
- metadata:
108
- model: $_model
109
- last_modified: $lastModified
110
- ---
111
- ''';
112
-
113
- return frontmatter + (cleanContent(content) ?? '');
114
- } on Object catch (e) {
115
- _logger.severe('Gemini generation failed: $e');
116
- return null;
117
- }
118
- }
119
-
120
- /// Updates the content for a skill based on raw markdown input and existing content.
121
- Future<String?> updateSkillContent(
122
- String existingContent,
123
- String rawMarkdown,
124
- String skillName,
125
- String description, {
126
- String? instructions,
127
- int thinkingBudget = defaultThinkingBudget,
128
- }) async {
129
- final service = GenerativeService(client: _client);
130
- final lastModified = io.HttpDate.format(DateTime.now());
131
- final prompt = Prompts.updateSkillPrompt(
132
- existingContent,
133
- rawMarkdown,
134
- instructions,
135
- );
136
-
137
- final request = _createRequest(
138
- prompt,
139
- systemInstruction: skillInstructions,
140
- thinkingBudget: thinkingBudget,
141
- );
142
-
143
- _logger.info(
144
- ' Model: $_model, Max Output Tokens: $defaultMaxOutputTokens, Thinking Budget: $thinkingBudget',
145
- );
146
-
147
- try {
148
- const r = RetryOptions(maxAttempts: 3);
149
- final response = await r.retry(() async {
150
- final res = await service.generateContent(request);
151
- final text = res.candidates.first.content?.parts
152
- .where((part) => !part.thought)
153
- .map((part) => part.text)
154
- .where((text) => text != null)
155
- .join('\n');
156
-
157
- if (text == null || text.isEmpty) {
158
- throw const FormatException('Empty response from Gemini');
159
- }
160
-
161
- return text;
162
- }, onRetry: (e) => _logger.warning('Retrying Gemini generation: $e'));
163
-
164
- final content = response;
165
-
166
- final frontmatter =
167
- '''---
168
- name: $skillName
169
- description: $description
170
- metadata:
171
- model: $_model
172
- last_modified: $lastModified
173
- ---
174
- ''';
175
-
176
- return frontmatter + (cleanContent(content) ?? '');
177
- } on Object catch (e) {
178
- _logger.severe('Gemini update failed: $e');
179
- return null;
180
- }
181
- }
182
-
183
- /// Validates an existing skill
184
- Future<String?> validateExistingSkillContent(
185
- String markdown,
186
- String skillName,
187
- String instructions,
188
- String generationDate,
189
- String modelName,
190
- String currentSkillContent, {
191
- int thinkingBudget = defaultThinkingBudget,
192
- }) async {
193
- final service = GenerativeService(client: _client);
194
- final validationPrompt = Prompts.validateExistingSkillContentPrompt(
195
- markdown,
196
- instructions,
197
- generationDate,
198
- modelName,
199
- currentSkillContent,
200
- );
201
-
202
- final request = _createRequest(
203
- validationPrompt,
204
- systemInstruction: skillInstructions,
205
- thinkingBudget: thinkingBudget,
206
- );
207
-
208
- _logger.info(
209
- ' Model: $_model, Max Output Tokens: $defaultMaxOutputTokens, Thinking Budget: $thinkingBudget',
210
- );
211
-
212
- try {
213
- const r = RetryOptions(maxAttempts: 3);
214
- final response = await r.retry(() async {
215
- final res = await service.generateContent(request);
216
- final text = res.candidates.first.content?.parts
217
- .where((part) => !part.thought)
218
- .map((part) => part.text)
219
- .where((text) => text != null)
220
- .join('\n');
221
-
222
- if (text == null || text.isEmpty) {
223
- throw const FormatException('Empty response from Gemini');
224
- }
225
-
226
- return text;
227
- }, onRetry: (e) => _logger.warning('Retrying Gemini validation: $e'));
228
-
229
- return response;
230
- } on Object catch (e) {
231
- _logger.severe('Gemini validation failed: $e');
232
- return null;
233
- }
234
- }
235
-
236
- /// Cleans the generated content by removing markdown code blocks and frontmatter.
237
- @visibleForTesting
238
- String? cleanContent(String? content) {
239
- if (content == null) return null;
240
- var cleaned = content;
241
- final startMatch = RegExp(
242
- r'^\s*```[a-zA-Z]*\s*\n',
243
- caseSensitive: false,
244
- ).firstMatch(cleaned);
245
- if (startMatch != null) {
246
- cleaned = cleaned.substring(startMatch.end);
247
- // Remove the last triple backticks if they exist
248
- cleaned = cleaned.replaceAll(RegExp(r'\n```\s*$'), '');
249
- }
250
-
251
- final yamlStartIndex = cleaned.indexOf('---');
252
- if (yamlStartIndex == 0) {
253
- // Possible frontmatter, skip it
254
- final end = cleaned.indexOf('---', 3);
255
- if (end != -1) {
256
- cleaned = cleaned.substring(end + 3).trim();
257
- }
258
- } else if (yamlStartIndex > 0) {
259
- // Maybe noise before frontmatter, try to strip it if it looks like frontmatter
260
- final end = cleaned.indexOf('---', yamlStartIndex + 3);
261
- if (end != -1) {
262
- cleaned = cleaned.substring(end + 3).trim();
263
- }
264
- }
265
-
266
- // Ensure one trailing newline
267
- return '${cleaned.trim()}\n';
268
- }
269
-
270
- GenerateContentRequest _createRequest(
271
- String prompt, {
272
- String? systemInstruction,
273
- int thinkingBudget = defaultThinkingBudget,
274
- }) {
275
- return GenerateContentRequest(
276
- model: _model,
277
- systemInstruction: systemInstruction != null
278
- ? Content(parts: [Part(text: systemInstruction)])
279
- : null,
280
- contents: [
281
- Content(parts: [Part(text: prompt)]),
282
- ],
283
- // See [GenerationConfig] in package:google_cloud_ai_generativelanguage_v1beta
284
- generationConfig: GenerationConfig(
285
- temperature: defaultTemperature,
286
- maxOutputTokens: defaultMaxOutputTokens,
287
- thinkingConfig: thinkingBudget > 0
288
- ? ThinkingConfig(
289
- includeThoughts: true,
290
- thinkingBudget: thinkingBudget,
291
- )
292
- : null,
293
- ),
294
- safetySettings: defaultSafetySettings,
295
- );
296
- }
297
- }
298
-
299
- class _ApiKeyClient extends http.BaseClient {
300
- _ApiKeyClient(this._inner, this._apiKey);
301
-
302
- final http.Client _inner;
303
- final String _apiKey;
304
-
305
- @override
306
- Future<http.StreamedResponse> send(http.BaseRequest request) {
307
- request.headers['x-goog-api-key'] = _apiKey;
308
- return _inner.send(request);
309
- }
310
- }
@@ -1,226 +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 'package:html/dom.dart';
6
- import 'package:html/parser.dart';
7
-
8
- /// Converts HTML content to Markdown.
9
- class MarkdownConverter {
10
- /// Converts HTML content to Markdown.
11
- String convert(String htmlContent) {
12
- final document = parse(htmlContent);
13
- final body = document.body;
14
- if (body == null) return '';
15
- return _convertElement(body).trim();
16
- }
17
-
18
- String _convertElement(Element element) {
19
- final buffer = StringBuffer();
20
-
21
- for (final node in element.nodes) {
22
- if (node is Text) {
23
- buffer.write(node.text);
24
- } else if (node is Element) {
25
- buffer.write(_processTag(node));
26
- }
27
- }
28
-
29
- return buffer.toString();
30
- }
31
-
32
- String _processTag(Element element) {
33
- final content = _convertElement(element);
34
-
35
- switch (element.localName) {
36
- case 'h1':
37
- return '\n# $content\n\n';
38
- case 'h2':
39
- return '\n## $content\n\n';
40
- case 'h3':
41
- return '\n### $content\n\n';
42
- case 'h4':
43
- return '\n#### $content\n\n';
44
- case 'h5':
45
- return '\n##### $content\n\n';
46
- case 'h6':
47
- return '\n###### $content\n\n';
48
- case 'p':
49
- return '$content\n\n';
50
- case 'a':
51
- final href = element.attributes['href'];
52
- return '[$content]($href)';
53
- case 'strong':
54
- case 'b':
55
- return '**$content**';
56
- case 'em':
57
- case 'i':
58
- return '*$content*';
59
- case 'del':
60
- case 's':
61
- case 'strike':
62
- return '~~$content~~';
63
- case 'code':
64
- return '`$content`';
65
- case 'pre':
66
- return '\n```\n${element.text}\n```\n\n';
67
- case 'blockquote':
68
- return '\n> $content\n\n';
69
- case 'hr':
70
- return '\n---\n\n';
71
- case 'ul':
72
- return '\n$content\n';
73
- case 'ol':
74
- return '\n$content\n';
75
- case 'li':
76
- return '- $content\n';
77
- case 'img':
78
- final src = element.attributes['src'] ?? '';
79
- final alt = element.attributes['alt'] ?? '';
80
- return '![$alt]($src)';
81
- case 'video':
82
- final src = element.attributes['src'] ?? '';
83
- final poster = element.attributes['poster'] ?? '';
84
- final title = element.attributes['title'] ?? 'Video';
85
-
86
- String? videoUrl;
87
- if (src.isNotEmpty) {
88
- videoUrl = src;
89
- } else {
90
- // Fallback for source elements
91
- videoUrl = element.children
92
- .where((e) => e.localName == 'source')
93
- .map((e) => e.attributes['src'])
94
- .firstWhere((s) => s != null, orElse: () => null);
95
- }
96
-
97
- if (videoUrl != null) {
98
- if (poster.isNotEmpty) {
99
- return '[![$title]($poster)]($videoUrl)';
100
- }
101
- return '[$title]($videoUrl)';
102
- }
103
- return '';
104
- case 'iframe':
105
- final src = element.attributes['src'] ?? '';
106
- final title = element.attributes['title'] ?? 'Iframe';
107
- if (src.isNotEmpty) {
108
- return '[$title]($src)';
109
- }
110
- return '';
111
- case 'table':
112
- return _processTable(element);
113
- case 'dl':
114
- return _processDefinitionList(element);
115
- case 'dt':
116
- return '\n**$content**\n';
117
- case 'dd':
118
- return ': $content\n';
119
- case 'details':
120
- // Preserve details as HTML, but convert children to markdown?
121
- // Or just preserve the tag structure and convert internal content.
122
- // Let's try to preserve the tag but convert content.
123
- return '\n<details>\n$content\n</details>\n';
124
- case 'summary':
125
- return '<summary>$content</summary>';
126
- case 'br':
127
- return '\n';
128
- case 'div':
129
- case 'section':
130
- case 'main':
131
- case 'article':
132
- return '$content\n';
133
- default:
134
- return content;
135
- }
136
- }
137
-
138
- String _processTable(Element table) {
139
- // Simple table converter
140
- // 1. Find headers (th)
141
- // 2. Find rows (tr)
142
- // 3. Construct markdown table
143
-
144
- final buffer = StringBuffer('\n');
145
- var headerCells = <Element>[];
146
- final bodyRows = <Element>[];
147
-
148
- final theads = table.children.where((e) => e.localName == 'thead').toList();
149
- if (theads.isNotEmpty) {
150
- final headerRows = theads.first.children
151
- .where((e) => e.localName == 'tr')
152
- .toList();
153
- if (headerRows.isNotEmpty) {
154
- final headerRow = headerRows.first;
155
- headerCells = headerRow.children
156
- .where((e) => e.localName == 'th')
157
- .toList();
158
- if (headerCells.isEmpty) {
159
- headerCells = headerRow.children
160
- .where((e) => e.localName == 'td')
161
- .toList();
162
- }
163
- }
164
- }
165
-
166
- for (final child in table.children) {
167
- if (child.localName == 'tbody' || child.localName == 'tfoot') {
168
- bodyRows.addAll(child.children.where((e) => e.localName == 'tr'));
169
- } else if (child.localName == 'tr') {
170
- bodyRows.add(child);
171
- }
172
- }
173
-
174
- // Promote first row to header if needed
175
- if (headerCells.isEmpty && bodyRows.isNotEmpty) {
176
- final firstRow = bodyRows.first;
177
- headerCells = firstRow.children
178
- .where((e) => e.localName == 'th')
179
- .toList();
180
- if (headerCells.isEmpty) {
181
- headerCells = firstRow.children
182
- .where((e) => e.localName == 'td')
183
- .toList();
184
- }
185
- bodyRows.removeAt(0);
186
- }
187
-
188
- if (headerCells.isEmpty && bodyRows.isEmpty) return '';
189
-
190
- // Write Header
191
- buffer.write('|');
192
- for (final cell in headerCells) {
193
- buffer.write(' ${_convertElement(cell).trim()} |');
194
- }
195
- buffer.write('\n|');
196
- for (var i = 0; i < headerCells.length; i++) {
197
- buffer.write('---|');
198
- }
199
- buffer.write('\n');
200
-
201
- // Write Body
202
- for (final row in bodyRows) {
203
- final cells = row.children
204
- .where((e) => e.localName == 'td' || e.localName == 'th')
205
- .toList();
206
- if (cells.isEmpty) continue;
207
- buffer.write('|');
208
- for (final cell in cells) {
209
- buffer.write(' ${_convertElement(cell).trim()} |');
210
- }
211
- buffer.write('\n');
212
- }
213
- buffer.write('\n');
214
-
215
- return buffer.toString();
216
- }
217
-
218
- String _processDefinitionList(Element dl) {
219
- final buffer = StringBuffer('\n');
220
- for (final child in dl.children) {
221
- buffer.write(_processTag(child));
222
- }
223
- buffer.write('\n\n');
224
- return buffer.toString();
225
- }
226
- }
@@ -1,72 +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
- // ignore_for_file: avoid_classes_with_only_static_members
6
-
7
- /// Manages prompts used by the GeminiService.
8
- class Prompts {
9
- /// Creates the prompt for generating a new skill.
10
- static String createSkillPrompt(String markdown, String? instructions) {
11
- return '''
12
- Rewrite the following technical documentation into a high-quality "SKILL.md" file.
13
-
14
- ${instructions != null && instructions.isNotEmpty ? 'Special Instructions: $instructions' : ''}
15
-
16
- Raw Content:
17
- $markdown
18
- ''';
19
- }
20
-
21
- /// Creates the prompt for updating an existing skill.
22
- static String updateSkillPrompt(
23
- String existingContent,
24
- String markdown,
25
- String? instructions,
26
- ) {
27
- return '''
28
- Update the following existing "SKILL.md" file using the provided new technical documentation.
29
- Carefully integrate the new information without losing valuable existing instructions or context.
30
-
31
- ${instructions != null && instructions.isNotEmpty ? 'Special Instructions: $instructions' : ''}
32
-
33
- Existing SKILL.md Content:
34
- $existingContent
35
-
36
- New Technical Documentation (Raw Content):
37
- $markdown
38
- ''';
39
- }
40
-
41
- /// Creates the prompt for validating an existing skill.
42
- static String validateExistingSkillContentPrompt(
43
- String markdown,
44
- String instructions,
45
- String generationDate,
46
- String modelName,
47
- String currentSkillContent,
48
- ) {
49
- return '''
50
- Validate the following skill document against the provided source material and verify if it is valid.
51
- Focus on accuracy, structure, and completeness based on the Source Material and the system instructions.
52
-
53
- Context:
54
- - The skill was originally generated on: $generationDate
55
- - The current evaluation is using model: $modelName
56
- - The instructions used to generate the skill were:
57
- $instructions
58
-
59
- Source Material:
60
- $markdown
61
-
62
- Current Skill Content:
63
- "$currentSkillContent"
64
- ---
65
-
66
- Grade the current output based on the instructions and the comparison to current website content and instructions today.
67
- Establish a conclusion on whether the new skill is valid or not.
68
- Reasons for a good or bad quality grade should be provided including concepts such as missing content, different model used, more than a few months old, etc.
69
- On the very last line, output "Grade: [0-100]" representing overall quality of the skill compared to the assumed value if it were generated again today.
70
- ''';
71
- }
72
- }
@@ -1,84 +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' as io;
6
-
7
- import 'package:http/http.dart' as http;
8
- import 'package:logging/logging.dart';
9
- import 'package:path/path.dart' as p;
10
-
11
- import 'markdown_converter.dart';
12
-
13
- /// Fetches and converts content from diverse resources.
14
- class ResourceFetcherService {
15
- /// Creates a new [ResourceFetcherService].
16
- ResourceFetcherService({
17
- required http.Client httpClient,
18
- required Logger logger,
19
- }) : _httpClient = httpClient,
20
- _logger = logger;
21
-
22
- final http.Client _httpClient;
23
- final Logger _logger;
24
-
25
- /// Fetches and converts content from a list of resources.
26
- ///
27
- /// Throws an [Exception] if fetching any resource fails. This strict behavior
28
- /// prevents wasting tokens on generating low-quality skills when
29
- /// source material is missing.
30
- Future<String> fetchAndConvertContent(
31
- List<String> resources, {
32
- io.Directory? configDir,
33
- }) async {
34
- final converter = MarkdownConverter();
35
- final sb = StringBuffer();
36
- for (final resource in resources) {
37
- _logger.info(' Fetching $resource...');
38
-
39
- if (resource.startsWith('http://')) {
40
- throw Exception(
41
- 'Insecure HTTP URL found: $resource. '
42
- 'Only HTTPS URLs or relative file paths are allowed.',
43
- );
44
- }
45
-
46
- if (resource.startsWith('https://')) {
47
- final response = await _httpClient.get(Uri.parse(resource));
48
- if (response.statusCode == 200) {
49
- sb
50
- ..writeln('--- Raw content from $resource ---')
51
- ..writeln(converter.convert(response.body));
52
- } else {
53
- throw Exception(
54
- 'Failed to fetch $resource: HTTP ${response.statusCode}. '
55
- 'Failing fast to save Gemini tokens.',
56
- );
57
- }
58
- } else {
59
- if (configDir == null) {
60
- throw Exception(
61
- 'Relative resource "$resource" found, but no configuration '
62
- 'directory was provided to resolve it.',
63
- );
64
- }
65
- final file = io.File(p.join(configDir.path, resource));
66
- if (!file.existsSync()) {
67
- throw Exception('Local resource file not found: ${file.path}');
68
- }
69
-
70
- final String content;
71
- try {
72
- content = file.readAsStringSync();
73
- } on io.FileSystemException {
74
- throw Exception('Local resource file is not readable: ${file.path}');
75
- }
76
-
77
- sb
78
- ..writeln('--- Raw content from $resource ---')
79
- ..writeln(content);
80
- }
81
- }
82
- return sb.toString();
83
- }
84
- }