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.
- package/bin.js +1 -1
- package/index.js +18 -12
- package/package.json +1 -1
- package/skills/VERSION.md +61 -175
- package/skills/design-planning/ai-coding-rules/SKILL.md +13 -5
- package/skills/design-planning/design-to-code/SKILL.md +14 -5
- package/skills/design-planning/enterprise-spec/SKILL.md +13 -5
- package/skills/design-planning/flutter-av/SKILL.md +16 -5
- package/skills/design-planning/flutter-map/SKILL.md +14 -5
- package/skills/design-planning/function-sdd/SKILL.md +13 -5
- package/skills/design-planning/global-overlay-stack-standard/SKILL.md +14 -4
- package/skills/design-planning/ui-motion-interaction-standard/SKILL.md +14 -4
- package/skills/design-planning/ui-sdd-specialized/SKILL.md +14 -5
- package/skills/development-execution/flutter-errors/SKILL.md +15 -5
- package/skills/quality-assurance/bdd-acceptance/SKILL.md +14 -5
- package/skills/quality-assurance/flutter-test/SKILL.md +16 -5
- package/skills/requirement-analysis/sdd/mock_sdd.md +156 -0
- package/skills/rules/project_rules.md +127 -538
- package/skills/rules/user_rules.md +263 -0
- package/skills/special-tools/env-check/SKILL.md +13 -5
- package/skills/special-tools/ios-full-auto-debug/SKILL.md +15 -5
- package/skills/flutter-skills/.github/dependabot.yaml +0 -15
- package/skills/flutter-skills/.github/workflows/dart_skills_lint_workflow.yaml +0 -68
- package/skills/flutter-skills/.github/workflows/skills_tool.yaml +0 -51
- package/skills/flutter-skills/CODE_OF_CONDUCT.md +0 -3
- package/skills/flutter-skills/CONTRIBUTING.md +0 -36
- package/skills/flutter-skills/LICENSE +0 -26
- package/skills/flutter-skills/README.md +0 -50
- package/skills/flutter-skills/pubspec.yaml +0 -9
- package/skills/flutter-skills/resources/flutter_skills.yaml +0 -434
- package/skills/flutter-skills/skills/flutter-add-integration-test/SKILL.md +0 -163
- package/skills/flutter-skills/skills/flutter-add-widget-preview/SKILL.md +0 -145
- package/skills/flutter-skills/skills/flutter-add-widget-test/SKILL.md +0 -154
- package/skills/flutter-skills/skills/flutter-apply-architecture-best-practices/SKILL.md +0 -162
- package/skills/flutter-skills/skills/flutter-build-responsive-layout/SKILL.md +0 -139
- package/skills/flutter-skills/skills/flutter-fix-layout-issues/SKILL.md +0 -130
- package/skills/flutter-skills/skills/flutter-implement-json-serialization/SKILL.md +0 -153
- package/skills/flutter-skills/skills/flutter-setup-declarative-routing/SKILL.md +0 -255
- package/skills/flutter-skills/skills/flutter-setup-localization/SKILL.md +0 -210
- package/skills/flutter-skills/skills/flutter-use-http-package/SKILL.md +0 -175
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/add-dart-lint-validation-rule/SKILL.md +0 -196
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-best-practices/SKILL.md +0 -65
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-checks-migration/SKILL.md +0 -158
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-cli-app-best-practices/SKILL.md +0 -168
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-doc-validation/SKILL.md +0 -87
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-long-lines/SKILL.md +0 -101
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-matcher-best-practices/SKILL.md +0 -136
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-modern-features/SKILL.md +0 -266
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-package-maintenance/SKILL.md +0 -92
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/SKILL.md +0 -92
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/lib/src/calculator.dart +0 -7
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/pubspec.yaml +0 -8
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/test/calculator_test.dart +0 -11
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/interpret_coverage.dart +0 -95
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/pubspec.yaml +0 -6
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/test/interpret_coverage_test.dart +0 -93
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-fundamentals/SKILL.md +0 -173
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/definition-of-done/SKILL.md +0 -27
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/flutter_skills_ignore.json +0 -3
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/grill-me/SKILL.md +0 -10
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/ignore.json +0 -3
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/test-driven-development/SKILL.md +0 -371
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/test-driven-development/testing-anti-patterns.md +0 -299
- package/skills/flutter-skills/tool/dart_skills_lint/AUTHORS +0 -7
- package/skills/flutter-skills/tool/dart_skills_lint/CHANGELOG.md +0 -12
- package/skills/flutter-skills/tool/dart_skills_lint/CONTRIBUTING.md +0 -51
- package/skills/flutter-skills/tool/dart_skills_lint/LICENSE +0 -27
- package/skills/flutter-skills/tool/dart_skills_lint/README.md +0 -203
- package/skills/flutter-skills/tool/dart_skills_lint/analysis_options.yaml +0 -296
- package/skills/flutter-skills/tool/dart_skills_lint/bench/README.md +0 -23
- package/skills/flutter-skills/tool/dart_skills_lint/bench/baseline_throughput.dart +0 -230
- package/skills/flutter-skills/tool/dart_skills_lint/bin/cli.dart +0 -10
- package/skills/flutter-skills/tool/dart_skills_lint/dart_skills_lint.yaml +0 -14
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/PRODUCTION_READYNESS.md +0 -48
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/completion_migration_plan.md +0 -99
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/legacy_patterns_report.md +0 -110
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/pub_vs_skill_report.md +0 -56
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/knowledge/SPECIFICATION.md +0 -79
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/knowledge/architecture_overview.md +0 -64
- package/skills/flutter-skills/tool/dart_skills_lint/lib/dart_skills_lint.dart +0 -11
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/config_parser.dart +0 -156
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/entry_point.dart +0 -354
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/fixable_rule.dart +0 -20
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/analysis_severity.dart +0 -15
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/check_type.dart +0 -17
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/ignore_entry.dart +0 -34
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/ignore_entry.g.dart +0 -19
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skill_context.dart +0 -27
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skill_rule.dart +0 -27
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skills_ignores.dart +0 -26
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skills_ignores.g.dart +0 -24
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/validation_error.dart +0 -31
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rule_registry.dart +0 -79
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/absolute_paths_rule.dart +0 -74
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/description_length_rule.dart +0 -49
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/disallowed_field_rule.dart +0 -61
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/name_format_rule.dart +0 -167
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/relative_paths_rule.dart +0 -72
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/trailing_whitespace_rule.dart +0 -93
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/valid_yaml_metadata_rule.dart +0 -74
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/skills_ignores_storage.dart +0 -36
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/validation_session.dart +0 -559
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/validator.dart +0 -238
- package/skills/flutter-skills/tool/dart_skills_lint/pubspec.yaml +0 -28
- package/skills/flutter-skills/tool/dart_skills_lint/skills/README.md +0 -10
- package/skills/flutter-skills/tool/dart_skills_lint/skills/dart-skills-lint-validation/SKILL.md +0 -195
- package/skills/flutter-skills/tool/dart_skills_lint/skills-lock.json +0 -75
- package/skills/flutter-skills/tool/dart_skills_lint/test/absolute_paths_test.dart +0 -167
- package/skills/flutter-skills/tool/dart_skills_lint/test/cli_integration_test.dart +0 -683
- package/skills/flutter-skills/tool/dart_skills_lint/test/config_file_test.dart +0 -292
- package/skills/flutter-skills/tool/dart_skills_lint/test/custom_rule_test.dart +0 -122
- package/skills/flutter-skills/tool/dart_skills_lint/test/directory_structure_test.dart +0 -163
- package/skills/flutter-skills/tool/dart_skills_lint/test/field_constraints_test.dart +0 -178
- package/skills/flutter-skills/tool/dart_skills_lint/test/fixer_test.dart +0 -172
- package/skills/flutter-skills/tool/dart_skills_lint/test/ignore_models_test.dart +0 -63
- package/skills/flutter-skills/tool/dart_skills_lint/test/metadata_validation_test.dart +0 -116
- package/skills/flutter-skills/tool/dart_skills_lint/test/relative_path_flag_test.dart +0 -70
- package/skills/flutter-skills/tool/dart_skills_lint/test/relative_paths_test.dart +0 -172
- package/skills/flutter-skills/tool/dart_skills_lint/test/resolve_rules_test.dart +0 -82
- package/skills/flutter-skills/tool/dart_skills_lint/test/rule_naming_test.dart +0 -29
- package/skills/flutter-skills/tool/dart_skills_lint/test/skills_ignores_storage_test.dart +0 -89
- package/skills/flutter-skills/tool/dart_skills_lint/test/test_utils.dart +0 -19
- package/skills/flutter-skills/tool/dart_skills_lint/test/trailing_whitespace_test.dart +0 -152
- package/skills/flutter-skills/tool/generator/README.md +0 -150
- package/skills/flutter-skills/tool/generator/analysis_options.yaml +0 -143
- package/skills/flutter-skills/tool/generator/bin/skills.dart +0 -73
- package/skills/flutter-skills/tool/generator/lib/src/commands/base_skill_command.dart +0 -87
- package/skills/flutter-skills/tool/generator/lib/src/commands/base_yaml_command.dart +0 -83
- package/skills/flutter-skills/tool/generator/lib/src/commands/generate_skill_command.dart +0 -92
- package/skills/flutter-skills/tool/generator/lib/src/commands/update_readme_command.dart +0 -150
- package/skills/flutter-skills/tool/generator/lib/src/commands/update_skill_command.dart +0 -97
- package/skills/flutter-skills/tool/generator/lib/src/commands/validate_skill_command.dart +0 -284
- package/skills/flutter-skills/tool/generator/lib/src/models/skill_params.dart +0 -41
- package/skills/flutter-skills/tool/generator/lib/src/services/gemini_service.dart +0 -310
- package/skills/flutter-skills/tool/generator/lib/src/services/markdown_converter.dart +0 -226
- package/skills/flutter-skills/tool/generator/lib/src/services/prompts.dart +0 -72
- package/skills/flutter-skills/tool/generator/lib/src/services/resource_fetcher_service.dart +0 -84
- package/skills/flutter-skills/tool/generator/lib/src/services/skill_instructions.dart +0 -30
- package/skills/flutter-skills/tool/generator/pubspec.yaml +0 -32
- package/skills/flutter-skills/tool/generator/test/commands/base_skill_command_test.dart +0 -131
- package/skills/flutter-skills/tool/generator/test/commands/validate_skills_input_test.dart +0 -263
- package/skills/flutter-skills/tool/generator/test/custom_skill_rules/last_modified_rule.dart +0 -32
- package/skills/flutter-skills/tool/generator/test/generate_skills_retry_test.dart +0 -105
- package/skills/flutter-skills/tool/generator/test/generate_skills_test.dart +0 -519
- package/skills/flutter-skills/tool/generator/test/lint_skills_test.dart +0 -34
- package/skills/flutter-skills/tool/generator/test/markdown_converter_test.dart +0 -103
- package/skills/flutter-skills/tool/generator/test/markdown_table_test.dart +0 -131
- package/skills/flutter-skills/tool/generator/test/models/skill_params_test.dart +0 -37
- package/skills/flutter-skills/tool/generator/test/services/gemini_service_test.dart +0 -291
- package/skills/flutter-skills/tool/generator/test/services/markdown_converter_test.dart +0 -156
- package/skills/flutter-skills/tool/generator/test/services/resource_fetcher_service_test.dart +0 -188
- package/skills/flutter-skills/tool/generator/test/update_skills_test.dart +0 -241
- package/skills/flutter-skills/tool/generator/test/validate_skills_test.dart +0 -728
- /package/skills/{.agents → flutter}/skills/flutter-add-integration-test/SKILL.md +0 -0
- /package/skills/{.agents → flutter}/skills/flutter-add-widget-preview/SKILL.md +0 -0
- /package/skills/{.agents → flutter}/skills/flutter-add-widget-test/SKILL.md +0 -0
- /package/skills/{.agents → flutter}/skills/flutter-apply-architecture-best-practices/SKILL.md +0 -0
- /package/skills/{.agents → flutter}/skills/flutter-build-responsive-layout/SKILL.md +0 -0
- /package/skills/{.agents → flutter}/skills/flutter-fix-layout-issues/SKILL.md +0 -0
- /package/skills/{.agents → flutter}/skills/flutter-implement-json-serialization/SKILL.md +0 -0
- /package/skills/{.agents → flutter}/skills/flutter-setup-declarative-routing/SKILL.md +0 -0
- /package/skills/{.agents → flutter}/skills/flutter-setup-localization/SKILL.md +0 -0
- /package/skills/{.agents → flutter}/skills/flutter-use-http-package/SKILL.md +0 -0
package/skills/flutter-skills/tool/generator/test/services/resource_fetcher_service_test.dart
DELETED
|
@@ -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
|
-
}
|