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,188 +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:http/testing.dart';
9
- import 'package:logging/logging.dart';
10
- import 'package:path/path.dart' as p;
11
-
12
- import 'package:skills/src/services/resource_fetcher_service.dart';
13
- import 'package:test/test.dart';
14
-
15
- void main() {
16
- group('ResourceFetcherService', () {
17
- late Logger logger;
18
-
19
- setUp(() {
20
- logger = Logger('test');
21
- });
22
-
23
- test('fetches and converts content successfully on 200 OK', () async {
24
- final client = MockClient((request) async {
25
- if (request.url.toString() == 'https://example.com/doc1') {
26
- return http.Response('<h1>Doc 1</h1>', 200);
27
- } else if (request.url.toString() == 'https://example.com/doc2') {
28
- return http.Response('<p>Doc 2 content</p>', 200);
29
- }
30
- return http.Response('Not found', 404);
31
- });
32
-
33
- final fetcher = ResourceFetcherService(
34
- httpClient: client,
35
- logger: logger,
36
- );
37
-
38
- final result = await fetcher.fetchAndConvertContent([
39
- 'https://example.com/doc1',
40
- 'https://example.com/doc2',
41
- ]);
42
-
43
- expect(result, contains('Doc 1'));
44
- expect(result, contains('Doc 2 content'));
45
- });
46
-
47
- test('throws Exception on non-200 status code to save tokens', () async {
48
- final client = MockClient((request) async {
49
- return http.Response('Not found', 404);
50
- });
51
-
52
- final fetcher = ResourceFetcherService(
53
- httpClient: client,
54
- logger: logger,
55
- );
56
-
57
- expect(
58
- () => fetcher.fetchAndConvertContent(['https://example.com/missing']),
59
- throwsA(
60
- isA<Exception>().having(
61
- (e) => e.toString(),
62
- 'message',
63
- contains('HTTP 404'),
64
- ),
65
- ),
66
- );
67
- });
68
-
69
- test('throws exception on network error to save tokens', () async {
70
- final client = MockClient((request) async {
71
- throw http.ClientException('Connection failed');
72
- });
73
-
74
- final fetcher = ResourceFetcherService(
75
- httpClient: client,
76
- logger: logger,
77
- );
78
-
79
- expect(
80
- () => fetcher.fetchAndConvertContent(['https://example.com/error']),
81
- throwsA(isA<http.ClientException>()),
82
- );
83
- });
84
-
85
- test('throws Exception for insecure http:// URL', () async {
86
- final client = MockClient((request) async {
87
- return http.Response('content', 200);
88
- });
89
-
90
- final fetcher = ResourceFetcherService(
91
- httpClient: client,
92
- logger: logger,
93
- );
94
-
95
- expect(
96
- () => fetcher.fetchAndConvertContent(['http://example.com/doc1']),
97
- throwsA(
98
- isA<Exception>().having(
99
- (e) => e.toString(),
100
- 'message',
101
- contains('Insecure HTTP URL found'),
102
- ),
103
- ),
104
- );
105
- });
106
-
107
- test('fetches local file correctly relative to configDir', () async {
108
- final tempDir = io.Directory.systemTemp.createTempSync('gemini_test');
109
- try {
110
- io.File(
111
- p.join(tempDir.path, 'local_doc.md'),
112
- ).writeAsStringSync('# Local Doc\ncontent');
113
-
114
- final client = MockClient((request) async {
115
- return http.Response('Not found', 404);
116
- });
117
-
118
- final fetcher = ResourceFetcherService(
119
- httpClient: client,
120
- logger: logger,
121
- );
122
-
123
- final result = await fetcher.fetchAndConvertContent([
124
- 'local_doc.md',
125
- ], configDir: tempDir);
126
-
127
- expect(result, contains('Local Doc'));
128
- expect(result, contains('content'));
129
- } finally {
130
- tempDir.deleteSync(recursive: true);
131
- }
132
- });
133
-
134
- test('throws Exception for missing local file', () async {
135
- final tempDir = io.Directory.systemTemp.createTempSync('gemini_test');
136
- try {
137
- final client = MockClient((request) async {
138
- return http.Response('Not found', 404);
139
- });
140
-
141
- final fetcher = ResourceFetcherService(
142
- httpClient: client,
143
- logger: logger,
144
- );
145
-
146
- expect(
147
- () => fetcher.fetchAndConvertContent([
148
- 'missing.md',
149
- ], configDir: tempDir),
150
- throwsA(
151
- isA<Exception>().having(
152
- (e) => e.toString(),
153
- 'message',
154
- contains('Local resource file not found'),
155
- ),
156
- ),
157
- );
158
- } finally {
159
- tempDir.deleteSync(recursive: true);
160
- }
161
- });
162
-
163
- test(
164
- 'throws Exception for local file when no configDir is provided',
165
- () async {
166
- final client = MockClient((request) async {
167
- return http.Response('Not found', 404);
168
- });
169
-
170
- final fetcher = ResourceFetcherService(
171
- httpClient: client,
172
- logger: logger,
173
- );
174
-
175
- expect(
176
- () => fetcher.fetchAndConvertContent(['local_doc.md']),
177
- throwsA(
178
- isA<Exception>().having(
179
- (e) => e.toString(),
180
- 'message',
181
- contains('no configuration directory was provided to resolve it'),
182
- ),
183
- ),
184
- );
185
- },
186
- );
187
- });
188
- }
@@ -1,241 +0,0 @@
1
- // Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
2
- // for details. All rights reserved. Use of this source code is governed by a
3
- // BSD-style license that can be found in the LICENSE file.
4
-
5
- import 'dart:convert';
6
- import 'dart:io';
7
-
8
- import 'package:args/command_runner.dart';
9
- import 'package:http/http.dart' as http;
10
- import 'package:http/testing.dart';
11
- import 'package:logging/logging.dart';
12
- import 'package:path/path.dart' as p;
13
- import 'package:skills/src/commands/update_skill_command.dart';
14
- import 'package:test/test.dart';
15
-
16
- void main() {
17
- group('UpdateSkillCommand', () {
18
- late CommandRunner<void> runner;
19
- late Directory tempDir;
20
- late File inputYamlFile;
21
-
22
- setUp(() async {
23
- tempDir = await Directory.systemTemp.createTemp('skills_update_test');
24
- inputYamlFile = File(p.join(tempDir.path, 'dart_dev.yaml'));
25
- runner = CommandRunner<void>('skills', 'Test runner');
26
- });
27
-
28
- tearDown(() async {
29
- await tempDir.delete(recursive: true);
30
- });
31
-
32
- test('updates skill from YAML input preserving existing content', () async {
33
- final inputData = [
34
- {
35
- 'name': 'foo',
36
- 'description': 'Foo description',
37
- 'resources': ['https://example.com/foo.html'],
38
- },
39
- ];
40
- inputYamlFile.writeAsStringSync(jsonEncode(inputData));
41
-
42
- // Create existing skill
43
- final skillDirFoo = Directory(p.join(tempDir.path, 'foo'))..createSync();
44
- final skillFile = File(p.join(skillDirFoo.path, 'SKILL.md'))
45
- ..writeAsStringSync('# Existing Content\n');
46
-
47
- final geminiRequests = <String>[];
48
- // Mock HTTP Client
49
- final mockClient = MockClient((request) async {
50
- final url = request.url.toString();
51
-
52
- // 1. Mock content fetch
53
- if (url.startsWith('https://example.com')) {
54
- return http.Response(
55
- '<html><body><h1>New Content</h1></body></html>',
56
- 200,
57
- );
58
- }
59
-
60
- // 2. Mock Gemini API
61
- if (url.contains('generativelanguage.googleapis.com')) {
62
- geminiRequests.add(request.body);
63
- return http.Response(
64
- jsonEncode({
65
- 'candidates': [
66
- {
67
- 'content': {
68
- 'parts': [
69
- {'text': 'Updated Content with # Existing Content'},
70
- ],
71
- },
72
- },
73
- ],
74
- }),
75
- 200,
76
- );
77
- }
78
-
79
- return http.Response('Not Found', 404);
80
- });
81
-
82
- final command = UpdateSkillCommand(
83
- environment: {'GEMINI_API_KEY': 'test-key'},
84
- httpClient: mockClient,
85
- outputDir: tempDir,
86
- );
87
- runner.addCommand(command);
88
-
89
- // Run command
90
- await runner.run(['update-skill', inputYamlFile.path]);
91
-
92
- expect(skillFile.existsSync(), isTrue);
93
-
94
- // Verify the updated content was written
95
- final updatedText = skillFile.readAsStringSync();
96
- expect(updatedText, contains('Updated Content with # Existing Content'));
97
-
98
- // Verify source header was sent to Gemini
99
- expect(geminiRequests, isNotEmpty);
100
- expect(geminiRequests.first, contains('# Existing Content'));
101
- expect(geminiRequests.first, contains('New Content'));
102
- });
103
-
104
- test('fails gracefully when SKILL.md does not exist', () async {
105
- final inputData = [
106
- {
107
- 'name': 'missing',
108
- 'description': 'Description',
109
- 'resources': ['https://example.com/source'],
110
- },
111
- ];
112
- inputYamlFile.writeAsStringSync(jsonEncode(inputData));
113
-
114
- final logs = <String>[];
115
- final sub = Logger.root.onRecord.listen((record) {
116
- logs.add(record.message);
117
- });
118
- addTearDown(sub.cancel);
119
-
120
- final mockClient = MockClient((request) async {
121
- return http.Response('Error', 500);
122
- });
123
-
124
- final command = UpdateSkillCommand(
125
- environment: {'GEMINI_API_KEY': 'test-key'},
126
- httpClient: mockClient,
127
- outputDir: tempDir,
128
- );
129
- runner.addCommand(command);
130
-
131
- await runner.run(['update-skill', inputYamlFile.path]);
132
-
133
- expect(logs, contains(contains('Skill file not found at')));
134
- expect(logs, contains(contains('Cannot update an non-existent skill.')));
135
- });
136
-
137
- test(
138
- 'logs warning when fetchAndConvertContent returns empty string',
139
- () async {
140
- final inputData = [
141
- {
142
- 'name': 'empty-fetch',
143
- 'description': 'Description',
144
- 'resources': <String>[],
145
- },
146
- ];
147
- inputYamlFile.writeAsStringSync(jsonEncode(inputData));
148
-
149
- final skillDir = Directory(p.join(tempDir.path, 'empty-fetch'))
150
- ..createSync();
151
- File(p.join(skillDir.path, 'SKILL.md')).writeAsStringSync('Existing');
152
-
153
- final logs = <String>[];
154
- final sub = Logger.root.onRecord.listen(
155
- (record) => logs.add(record.message),
156
- );
157
- addTearDown(sub.cancel);
158
-
159
- final mockClient = MockClient((request) async {
160
- return http.Response('', 200); // Empty HTML body
161
- });
162
-
163
- final command = UpdateSkillCommand(
164
- environment: {'GEMINI_API_KEY': 'test-key'},
165
- httpClient: mockClient,
166
- outputDir: tempDir,
167
- );
168
- runner.addCommand(command);
169
-
170
- await runner.run(['update-skill', inputYamlFile.path]);
171
- expect(
172
- logs,
173
- contains(' No content fetched for empty-fetch. Skipping.'),
174
- );
175
- },
176
- );
177
-
178
- test('dry run fetches content but skips Gemini and file writes', () async {
179
- final inputData = [
180
- {
181
- 'name': 'dry-update',
182
- 'description': 'Description',
183
- 'resources': ['https://example.com/source'],
184
- },
185
- ];
186
- inputYamlFile.writeAsStringSync(jsonEncode(inputData));
187
-
188
- final skillDir = Directory(p.join(tempDir.path, 'dry-update'))
189
- ..createSync();
190
- final skillFile = File(p.join(skillDir.path, 'SKILL.md'))
191
- ..writeAsStringSync('Existing content');
192
-
193
- final logs = <String>[];
194
- final sub = Logger.root.onRecord.listen((record) {
195
- logs.add(record.message);
196
- });
197
- addTearDown(sub.cancel);
198
-
199
- var geminiCalled = false;
200
- final mockClient = MockClient((request) async {
201
- if (request.url.toString() == 'https://example.com/source') {
202
- return http.Response('<html>New Content</html>', 200);
203
- }
204
- if (request.url.toString().contains('generativelanguage')) {
205
- geminiCalled = true;
206
- return http.Response(
207
- jsonEncode({
208
- 'candidates': [
209
- {
210
- 'content': {
211
- 'parts': [
212
- {'text': 'Gen'},
213
- ],
214
- },
215
- },
216
- ],
217
- }),
218
- 200,
219
- );
220
- }
221
- return http.Response('Error', 500);
222
- });
223
-
224
- final command = UpdateSkillCommand(
225
- environment: {'GEMINI_API_KEY': 'test-key'},
226
- httpClient: mockClient,
227
- outputDir: tempDir,
228
- );
229
- runner.addCommand(command);
230
-
231
- await runner.run(['update-skill', '--dry-run', inputYamlFile.path]);
232
-
233
- expect(geminiCalled, isFalse);
234
- expect(
235
- logs,
236
- contains(contains('[DRY RUN] Would update skill: dry-update')),
237
- );
238
- expect(skillFile.readAsStringSync(), equals('Existing content'));
239
- });
240
- });
241
- }