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
|
@@ -1,519 +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/generate_skill_command.dart';
|
|
14
|
-
import 'package:test/test.dart';
|
|
15
|
-
|
|
16
|
-
void main() {
|
|
17
|
-
group('GenerateSkillsCommand', () {
|
|
18
|
-
late CommandRunner<void> runner;
|
|
19
|
-
late Directory tempDir;
|
|
20
|
-
late File inputYamlFile;
|
|
21
|
-
|
|
22
|
-
setUp(() async {
|
|
23
|
-
tempDir = await Directory.systemTemp.createTemp('skills_gen_test');
|
|
24
|
-
inputYamlFile = File(p.join(tempDir.path, 'input.yaml'));
|
|
25
|
-
runner = CommandRunner<void>('skills', 'Test runner');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
tearDown(() async {
|
|
29
|
-
await tempDir.delete(recursive: true);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('generates skill from YAML input with dart-docs- prefix', () async {
|
|
33
|
-
// Create input YAML in a file named dart_dev.yaml to trigger prefixing
|
|
34
|
-
inputYamlFile = File(p.join(tempDir.path, 'dart_dev.yaml'));
|
|
35
|
-
final inputData = [
|
|
36
|
-
{
|
|
37
|
-
'name': 'foo',
|
|
38
|
-
'description': 'Foo description',
|
|
39
|
-
'resources': ['https://example.com/foo.html'],
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
'name': 'example',
|
|
43
|
-
'description': 'Example description',
|
|
44
|
-
'resources': ['https://example.com/'],
|
|
45
|
-
},
|
|
46
|
-
];
|
|
47
|
-
inputYamlFile.writeAsStringSync(jsonEncode(inputData));
|
|
48
|
-
|
|
49
|
-
final geminiRequests = <String>[];
|
|
50
|
-
// Mock HTTP Client
|
|
51
|
-
final mockClient = MockClient((request) async {
|
|
52
|
-
final url = request.url.toString();
|
|
53
|
-
|
|
54
|
-
// 1. Mock content fetch
|
|
55
|
-
if (url.startsWith('https://example.com')) {
|
|
56
|
-
return http.Response(
|
|
57
|
-
'<html><body><h1>Skill</h1><p>Content for $url</p></body></html>',
|
|
58
|
-
200,
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 2. Mock Gemini API
|
|
63
|
-
if (url.contains('generativelanguage.googleapis.com')) {
|
|
64
|
-
geminiRequests.add(request.body);
|
|
65
|
-
// ... strict mock ...
|
|
66
|
-
return http.Response(
|
|
67
|
-
jsonEncode({
|
|
68
|
-
'candidates': [
|
|
69
|
-
{
|
|
70
|
-
'content': {
|
|
71
|
-
'parts': [
|
|
72
|
-
{'text': 'Generated Content'},
|
|
73
|
-
],
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
}),
|
|
78
|
-
200,
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return http.Response('Not Found', 404);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
final command = GenerateSkillCommand(
|
|
86
|
-
environment: {'GEMINI_API_KEY': 'test-key'},
|
|
87
|
-
httpClient: mockClient,
|
|
88
|
-
outputDir: tempDir,
|
|
89
|
-
);
|
|
90
|
-
runner.addCommand(command);
|
|
91
|
-
|
|
92
|
-
// Run command
|
|
93
|
-
await runner.run(['generate-skill', inputYamlFile.path]);
|
|
94
|
-
|
|
95
|
-
// Just verify file creation for now
|
|
96
|
-
final skillDirFoo = Directory(p.join(tempDir.path, 'foo'));
|
|
97
|
-
expect(skillDirFoo.existsSync(), isTrue);
|
|
98
|
-
|
|
99
|
-
final skillFile = File(p.join(skillDirFoo.path, 'SKILL.md'));
|
|
100
|
-
expect(skillFile.existsSync(), isTrue);
|
|
101
|
-
|
|
102
|
-
// Verify source header was sent to Gemini
|
|
103
|
-
expect(geminiRequests, isNotEmpty);
|
|
104
|
-
expect(
|
|
105
|
-
geminiRequests.first,
|
|
106
|
-
contains('--- Raw content from https://example.com/foo.html ---'),
|
|
107
|
-
);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test('logs progress and summary', () async {
|
|
111
|
-
inputYamlFile = File(p.join(tempDir.path, 'dart_dev.yaml'));
|
|
112
|
-
final inputData = [
|
|
113
|
-
{
|
|
114
|
-
'name': 'success',
|
|
115
|
-
'description': 'Desc',
|
|
116
|
-
'resources': ['https://example.com/success'],
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
'name': 'fail',
|
|
120
|
-
'description': 'Desc',
|
|
121
|
-
'resources': ['https://example.com/fail_404'],
|
|
122
|
-
},
|
|
123
|
-
];
|
|
124
|
-
inputYamlFile.writeAsStringSync(jsonEncode(inputData));
|
|
125
|
-
|
|
126
|
-
final logs = <String>[];
|
|
127
|
-
final sub = Logger.root.onRecord.listen((record) {
|
|
128
|
-
logs.add(record.message);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
addTearDown(sub.cancel);
|
|
132
|
-
|
|
133
|
-
final mockClient = MockClient((request) async {
|
|
134
|
-
final url = request.url.toString();
|
|
135
|
-
if (url == 'https://example.com/success') {
|
|
136
|
-
return http.Response('<html>Content</html>', 200);
|
|
137
|
-
}
|
|
138
|
-
if (url == 'https://example.com/fail_404') {
|
|
139
|
-
return http.Response('Not Found', 404);
|
|
140
|
-
}
|
|
141
|
-
// Mock Gemini
|
|
142
|
-
if (url.contains('generativelanguage')) {
|
|
143
|
-
return http.Response(
|
|
144
|
-
jsonEncode({
|
|
145
|
-
'candidates': [
|
|
146
|
-
{
|
|
147
|
-
'content': {
|
|
148
|
-
'parts': [
|
|
149
|
-
{'text': 'Generated Content'},
|
|
150
|
-
],
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
],
|
|
154
|
-
}),
|
|
155
|
-
200,
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
return http.Response('Error', 500);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
final command = GenerateSkillCommand(
|
|
162
|
-
environment: {'GEMINI_API_KEY': 'test-key'},
|
|
163
|
-
httpClient: mockClient,
|
|
164
|
-
outputDir: tempDir,
|
|
165
|
-
);
|
|
166
|
-
runner.addCommand(command);
|
|
167
|
-
|
|
168
|
-
await runner.run(['generate-skill', inputYamlFile.path]);
|
|
169
|
-
|
|
170
|
-
// Verify Logs
|
|
171
|
-
expect(logs, contains(contains('Generating skill: success...')));
|
|
172
|
-
expect(
|
|
173
|
-
logs,
|
|
174
|
-
contains(contains('Fetching https://example.com/success...')),
|
|
175
|
-
);
|
|
176
|
-
expect(logs, contains(contains('Generating skill: fail...')));
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
test('accepts thinking-budget option', () async {
|
|
180
|
-
// Setup for this test requires a configFile and skillsDir,
|
|
181
|
-
// which are not defined in the provided context.
|
|
182
|
-
// Assuming these would be defined in a real scenario or
|
|
183
|
-
// this test is incomplete without them.
|
|
184
|
-
// For now, I'll use tempDir for skillsDir and create a dummy configFile.
|
|
185
|
-
|
|
186
|
-
final skillsDir = Directory(p.join(tempDir.path, 'skills_output'));
|
|
187
|
-
await skillsDir.create();
|
|
188
|
-
|
|
189
|
-
final configFile = File(p.join(tempDir.path, 'config.yaml'));
|
|
190
|
-
final inputData = [
|
|
191
|
-
{
|
|
192
|
-
'name': 'budget_test',
|
|
193
|
-
'description': 'Budget test description',
|
|
194
|
-
'resources': ['https://example.com/budget'],
|
|
195
|
-
},
|
|
196
|
-
];
|
|
197
|
-
configFile.writeAsStringSync(jsonEncode(inputData));
|
|
198
|
-
|
|
199
|
-
final logs = <String>[];
|
|
200
|
-
final sub = Logger.root.onRecord.listen((record) {
|
|
201
|
-
logs.add(record.message);
|
|
202
|
-
});
|
|
203
|
-
addTearDown(sub.cancel);
|
|
204
|
-
|
|
205
|
-
final mockClient = MockClient((request) async {
|
|
206
|
-
final url = request.url.toString();
|
|
207
|
-
if (url == 'https://example.com/budget') {
|
|
208
|
-
return http.Response('<html>Budget Content</html>', 200);
|
|
209
|
-
}
|
|
210
|
-
if (url.contains('generativelanguage')) {
|
|
211
|
-
return http.Response(
|
|
212
|
-
jsonEncode({
|
|
213
|
-
'candidates': [
|
|
214
|
-
{
|
|
215
|
-
'content': {
|
|
216
|
-
'parts': [
|
|
217
|
-
{'text': 'Generated Budget Content'},
|
|
218
|
-
],
|
|
219
|
-
},
|
|
220
|
-
},
|
|
221
|
-
],
|
|
222
|
-
}),
|
|
223
|
-
200,
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
return http.Response('Error', 500);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
final command = GenerateSkillCommand(
|
|
230
|
-
environment: {'GEMINI_API_KEY': 'test-key'},
|
|
231
|
-
httpClient: mockClient,
|
|
232
|
-
outputDir: skillsDir,
|
|
233
|
-
);
|
|
234
|
-
runner.addCommand(command);
|
|
235
|
-
|
|
236
|
-
await runner.run([
|
|
237
|
-
'generate-skill',
|
|
238
|
-
configFile.path,
|
|
239
|
-
'--directory',
|
|
240
|
-
skillsDir.path,
|
|
241
|
-
'--thinking-budget',
|
|
242
|
-
'4000',
|
|
243
|
-
]);
|
|
244
|
-
|
|
245
|
-
expect(
|
|
246
|
-
logs,
|
|
247
|
-
contains(
|
|
248
|
-
contains(
|
|
249
|
-
'Model: models/gemini-3.1-pro-preview, Max Output Tokens: 8192, Thinking Budget: 4000',
|
|
250
|
-
),
|
|
251
|
-
),
|
|
252
|
-
);
|
|
253
|
-
final skillDirBudget = Directory(p.join(skillsDir.path, 'budget_test'));
|
|
254
|
-
expect(skillDirBudget.existsSync(), isTrue);
|
|
255
|
-
final skillFile = File(p.join(skillDirBudget.path, 'SKILL.md'));
|
|
256
|
-
expect(skillFile.existsSync(), isTrue);
|
|
257
|
-
expect(
|
|
258
|
-
skillFile.readAsStringSync(),
|
|
259
|
-
contains('Generated Budget Content\n'),
|
|
260
|
-
);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
test('handles invalid thinking-budget option gracefully', () async {
|
|
264
|
-
final skillsDir = await Directory.systemTemp.createTemp('skills_output');
|
|
265
|
-
addTearDown(() => skillsDir.delete(recursive: true));
|
|
266
|
-
|
|
267
|
-
final configFile = File(p.join(tempDir.path, 'config.yaml'));
|
|
268
|
-
final inputData = [
|
|
269
|
-
{
|
|
270
|
-
'name': 'budget_test',
|
|
271
|
-
'description': 'Budget test description',
|
|
272
|
-
'resources': ['https://example.com/budget'],
|
|
273
|
-
},
|
|
274
|
-
];
|
|
275
|
-
configFile.writeAsStringSync(jsonEncode(inputData));
|
|
276
|
-
|
|
277
|
-
final logs = <String>[];
|
|
278
|
-
final sub = Logger.root.onRecord.listen((record) {
|
|
279
|
-
logs.add(record.message);
|
|
280
|
-
});
|
|
281
|
-
addTearDown(sub.cancel);
|
|
282
|
-
|
|
283
|
-
final mockClient = MockClient((request) async {
|
|
284
|
-
final url = request.url.toString();
|
|
285
|
-
if (url == 'https://example.com/budget') {
|
|
286
|
-
return http.Response('<html>Budget Content</html>', 200);
|
|
287
|
-
}
|
|
288
|
-
if (url.contains('generativelanguage')) {
|
|
289
|
-
return http.Response(
|
|
290
|
-
jsonEncode({
|
|
291
|
-
'candidates': [
|
|
292
|
-
{
|
|
293
|
-
'content': {
|
|
294
|
-
'parts': [
|
|
295
|
-
{'text': 'Generated Budget Content'},
|
|
296
|
-
],
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
],
|
|
300
|
-
}),
|
|
301
|
-
200,
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
return http.Response('Error', 500);
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
final command = GenerateSkillCommand(
|
|
308
|
-
environment: {'GEMINI_API_KEY': 'test-key'},
|
|
309
|
-
httpClient: mockClient,
|
|
310
|
-
outputDir: skillsDir,
|
|
311
|
-
);
|
|
312
|
-
runner.addCommand(command);
|
|
313
|
-
|
|
314
|
-
await runner.run([
|
|
315
|
-
'generate-skill',
|
|
316
|
-
configFile.path,
|
|
317
|
-
'--directory',
|
|
318
|
-
skillsDir.path,
|
|
319
|
-
'--thinking-budget',
|
|
320
|
-
'invalid',
|
|
321
|
-
]);
|
|
322
|
-
|
|
323
|
-
expect(
|
|
324
|
-
logs,
|
|
325
|
-
contains(contains('Invalid thinking-budget: invalid. Skipping.')),
|
|
326
|
-
);
|
|
327
|
-
final skillDirBudget = Directory(p.join(skillsDir.path, 'budget_test'));
|
|
328
|
-
expect(skillDirBudget.existsSync(), isFalse);
|
|
329
|
-
});
|
|
330
|
-
test(
|
|
331
|
-
'logs warning when fetchAndConvertContent returns empty string',
|
|
332
|
-
() async {
|
|
333
|
-
final inputData = [
|
|
334
|
-
{
|
|
335
|
-
'name': 'empty-fetch',
|
|
336
|
-
'description': 'Description',
|
|
337
|
-
'resources': <String>[],
|
|
338
|
-
},
|
|
339
|
-
];
|
|
340
|
-
final inputYamlFile = File(p.join(tempDir.path, 'empty_fetch.yaml'))
|
|
341
|
-
..writeAsStringSync(jsonEncode(inputData));
|
|
342
|
-
|
|
343
|
-
final logs = <String>[];
|
|
344
|
-
final sub = Logger.root.onRecord.listen(
|
|
345
|
-
(record) => logs.add(record.message),
|
|
346
|
-
);
|
|
347
|
-
addTearDown(sub.cancel);
|
|
348
|
-
|
|
349
|
-
final mockClient = MockClient((request) async {
|
|
350
|
-
return http.Response('', 200); // Empty HTML body
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
final command = GenerateSkillCommand(
|
|
354
|
-
environment: {'GEMINI_API_KEY': 'test-key'},
|
|
355
|
-
httpClient: mockClient,
|
|
356
|
-
outputDir: tempDir,
|
|
357
|
-
);
|
|
358
|
-
runner.addCommand(command);
|
|
359
|
-
|
|
360
|
-
await runner.run(['generate-skill', inputYamlFile.path]);
|
|
361
|
-
expect(
|
|
362
|
-
logs,
|
|
363
|
-
contains(' No content fetched for empty-fetch. Skipping.'),
|
|
364
|
-
);
|
|
365
|
-
},
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
test('logs severe error when Gemini returns null or empty', () async {
|
|
369
|
-
final inputData = [
|
|
370
|
-
{
|
|
371
|
-
'name': 'empty-gemini',
|
|
372
|
-
'description': 'Description',
|
|
373
|
-
'resources': ['https://example.com/source'],
|
|
374
|
-
},
|
|
375
|
-
];
|
|
376
|
-
final inputYamlFile = File(p.join(tempDir.path, 'empty_gemini.yaml'))
|
|
377
|
-
..writeAsStringSync(jsonEncode(inputData));
|
|
378
|
-
|
|
379
|
-
final logs = <String>[];
|
|
380
|
-
final sub = Logger.root.onRecord.listen(
|
|
381
|
-
(record) => logs.add(record.message),
|
|
382
|
-
);
|
|
383
|
-
addTearDown(sub.cancel);
|
|
384
|
-
|
|
385
|
-
final mockClient = MockClient((request) async {
|
|
386
|
-
if (request.url.toString() == 'https://example.com/source') {
|
|
387
|
-
return http.Response('<html>Content</html>', 200);
|
|
388
|
-
}
|
|
389
|
-
if (request.url.toString().contains('generativelanguage')) {
|
|
390
|
-
return http.Response(
|
|
391
|
-
jsonEncode({
|
|
392
|
-
'candidates': [
|
|
393
|
-
{
|
|
394
|
-
'content': {
|
|
395
|
-
'parts': [
|
|
396
|
-
{'text': ''},
|
|
397
|
-
],
|
|
398
|
-
},
|
|
399
|
-
},
|
|
400
|
-
],
|
|
401
|
-
}),
|
|
402
|
-
200,
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
return http.Response('Error', 500);
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
final command = GenerateSkillCommand(
|
|
409
|
-
environment: {'GEMINI_API_KEY': 'test-key'},
|
|
410
|
-
httpClient: mockClient,
|
|
411
|
-
outputDir: tempDir,
|
|
412
|
-
);
|
|
413
|
-
runner.addCommand(command);
|
|
414
|
-
|
|
415
|
-
await runner.run(['generate-skill', inputYamlFile.path]);
|
|
416
|
-
expect(logs, contains(' Failed to generate content for empty-gemini'));
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
test('logs severe error on generic exception during generation', () async {
|
|
420
|
-
final inputData = [
|
|
421
|
-
{
|
|
422
|
-
'name': 'exception-gemini',
|
|
423
|
-
'description': 'Description',
|
|
424
|
-
'resources': ['https://example.com/source'],
|
|
425
|
-
},
|
|
426
|
-
];
|
|
427
|
-
final inputYamlFile = File(p.join(tempDir.path, 'exception_gemini.yaml'))
|
|
428
|
-
..writeAsStringSync(jsonEncode(inputData));
|
|
429
|
-
|
|
430
|
-
final logs = <String>[];
|
|
431
|
-
final sub = Logger.root.onRecord.listen(
|
|
432
|
-
(record) => logs.add(record.message),
|
|
433
|
-
);
|
|
434
|
-
addTearDown(sub.cancel);
|
|
435
|
-
|
|
436
|
-
final mockClient = MockClient((request) async {
|
|
437
|
-
throw Exception('Generic Error');
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
final command = GenerateSkillCommand(
|
|
441
|
-
environment: {'GEMINI_API_KEY': 'test-key'},
|
|
442
|
-
httpClient: mockClient,
|
|
443
|
-
outputDir: tempDir,
|
|
444
|
-
);
|
|
445
|
-
runner.addCommand(command);
|
|
446
|
-
|
|
447
|
-
await runner.run(['generate-skill', inputYamlFile.path]);
|
|
448
|
-
expect(
|
|
449
|
-
logs,
|
|
450
|
-
contains(
|
|
451
|
-
contains(
|
|
452
|
-
'Error processing exception-gemini: Exception: Generic Error',
|
|
453
|
-
),
|
|
454
|
-
),
|
|
455
|
-
);
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
test('dry run fetches content but skips Gemini and file writes', () async {
|
|
459
|
-
final inputData = [
|
|
460
|
-
{
|
|
461
|
-
'name': 'dry-run-skill',
|
|
462
|
-
'description': 'Description',
|
|
463
|
-
'resources': ['https://example.com/source'],
|
|
464
|
-
},
|
|
465
|
-
];
|
|
466
|
-
final inputYamlFile = File(p.join(tempDir.path, 'dry_run.yaml'))
|
|
467
|
-
..writeAsStringSync(jsonEncode(inputData));
|
|
468
|
-
|
|
469
|
-
final logs = <String>[];
|
|
470
|
-
final sub = Logger.root.onRecord.listen(
|
|
471
|
-
(record) => logs.add(record.message),
|
|
472
|
-
);
|
|
473
|
-
addTearDown(sub.cancel);
|
|
474
|
-
|
|
475
|
-
var geminiCalled = false;
|
|
476
|
-
final mockClient = MockClient((request) async {
|
|
477
|
-
if (request.url.toString() == 'https://example.com/source') {
|
|
478
|
-
return http.Response('<html>Content</html>', 200);
|
|
479
|
-
}
|
|
480
|
-
if (request.url.toString().contains('generativelanguage')) {
|
|
481
|
-
geminiCalled = true;
|
|
482
|
-
return http.Response(
|
|
483
|
-
jsonEncode({
|
|
484
|
-
'candidates': [
|
|
485
|
-
{
|
|
486
|
-
'content': {
|
|
487
|
-
'parts': [
|
|
488
|
-
{'text': 'Gen'},
|
|
489
|
-
],
|
|
490
|
-
},
|
|
491
|
-
},
|
|
492
|
-
],
|
|
493
|
-
}),
|
|
494
|
-
200,
|
|
495
|
-
);
|
|
496
|
-
}
|
|
497
|
-
return http.Response('Error', 500);
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
final command = GenerateSkillCommand(
|
|
501
|
-
environment: {'GEMINI_API_KEY': 'test-key'},
|
|
502
|
-
httpClient: mockClient,
|
|
503
|
-
outputDir: tempDir,
|
|
504
|
-
);
|
|
505
|
-
runner.addCommand(command);
|
|
506
|
-
|
|
507
|
-
await runner.run(['generate-skill', '--dry-run', inputYamlFile.path]);
|
|
508
|
-
|
|
509
|
-
expect(geminiCalled, isFalse);
|
|
510
|
-
expect(
|
|
511
|
-
logs,
|
|
512
|
-
contains(contains('[DRY RUN] Would generate skill: dry-run-skill')),
|
|
513
|
-
);
|
|
514
|
-
|
|
515
|
-
final skillDir = Directory(p.join(tempDir.path, 'dry-run-skill'));
|
|
516
|
-
expect(skillDir.existsSync(), isFalse);
|
|
517
|
-
});
|
|
518
|
-
});
|
|
519
|
-
}
|
|
@@ -1,34 +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:dart_skills_lint/dart_skills_lint.dart';
|
|
6
|
-
import 'package:logging/logging.dart';
|
|
7
|
-
import 'package:test/test.dart';
|
|
8
|
-
|
|
9
|
-
import 'custom_skill_rules/last_modified_rule.dart';
|
|
10
|
-
|
|
11
|
-
void main() {
|
|
12
|
-
test('Run skills linter', () async {
|
|
13
|
-
Logger.root.level = Level.ALL;
|
|
14
|
-
final subscription = Logger.root.onRecord.listen((record) {
|
|
15
|
-
printOnFailure('${record.level.name}: ${record.message}');
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
expect(
|
|
20
|
-
await validateSkills(
|
|
21
|
-
skillDirPaths: ['../../skills'],
|
|
22
|
-
resolvedRules: {
|
|
23
|
-
'check-relative-paths': AnalysisSeverity.error,
|
|
24
|
-
'check-absolute-paths': AnalysisSeverity.error,
|
|
25
|
-
},
|
|
26
|
-
customRules: [LastModifiedRule()],
|
|
27
|
-
),
|
|
28
|
-
isTrue,
|
|
29
|
-
);
|
|
30
|
-
} finally {
|
|
31
|
-
await subscription.cancel();
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
}
|
|
@@ -1,103 +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:skills/src/services/markdown_converter.dart';
|
|
6
|
-
import 'package:test/test.dart';
|
|
7
|
-
|
|
8
|
-
void main() {
|
|
9
|
-
group('MarkdownConverter', () {
|
|
10
|
-
late MarkdownConverter converter;
|
|
11
|
-
|
|
12
|
-
setUp(() {
|
|
13
|
-
converter = MarkdownConverter();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test('converts basic HTML to Markdown', () {
|
|
17
|
-
const html = '<h1>Title</h1><p>Paragraph</p>';
|
|
18
|
-
final markdown = converter.convert(html);
|
|
19
|
-
expect(markdown, contains('# Title'));
|
|
20
|
-
expect(markdown, contains('Paragraph'));
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test('converts images', () {
|
|
24
|
-
const html = '<img src="image.png" alt="Alt Text">';
|
|
25
|
-
final markdown = converter.convert(html);
|
|
26
|
-
expect(markdown, equals(''));
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test('converts images without alt text', () {
|
|
30
|
-
const html = '<img src="image.png">';
|
|
31
|
-
final markdown = converter.convert(html);
|
|
32
|
-
expect(markdown, equals(''));
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('converts unordered lists', () {
|
|
36
|
-
const html = '<ul><li>Item 1</li><li>Item 2</li></ul>';
|
|
37
|
-
final markdown = converter.convert(html);
|
|
38
|
-
expect(markdown, contains('- Item 1'));
|
|
39
|
-
expect(markdown, contains('- Item 2'));
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test('converts nested elements', () {
|
|
43
|
-
const html = '<div><p>Paragraph <strong>Bold</strong></p></div>';
|
|
44
|
-
final markdown = converter.convert(html);
|
|
45
|
-
expect(markdown, contains('Paragraph **Bold**'));
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test('converts blockquotes', () {
|
|
49
|
-
const html = '<blockquote>Quote</blockquote>';
|
|
50
|
-
final markdown = converter.convert(html);
|
|
51
|
-
expect(markdown, contains('> Quote'));
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('converts horizontal rules', () {
|
|
55
|
-
const html = '<hr>';
|
|
56
|
-
final markdown = converter.convert(html);
|
|
57
|
-
expect(markdown, contains('---'));
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test('converts strikethrough', () {
|
|
61
|
-
const html = '<del>Deleted</del> <s>Struck</s> <strike>Strike</strike>';
|
|
62
|
-
final markdown = converter.convert(html);
|
|
63
|
-
expect(markdown, contains('~~Deleted~~'));
|
|
64
|
-
expect(markdown, contains('~~Struck~~'));
|
|
65
|
-
expect(markdown, contains('~~Strike~~'));
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('converts headers', () {
|
|
69
|
-
const html = '<h4>H4</h4><h5>H5</h5><h6>H6</h6>';
|
|
70
|
-
final markdown = converter.convert(html);
|
|
71
|
-
expect(markdown, contains('#### H4'));
|
|
72
|
-
expect(markdown, contains('##### H5'));
|
|
73
|
-
expect(markdown, contains('###### H6'));
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test('converts video with src', () {
|
|
77
|
-
const html = '<video src="video.mp4" title="Video Title"></video>';
|
|
78
|
-
final markdown = converter.convert(html);
|
|
79
|
-
expect(markdown, equals('[Video Title](video.mp4)'));
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test('converts video with source child', () {
|
|
83
|
-
const html =
|
|
84
|
-
'<video title="Video Title"><source src="video.mp4"></video>';
|
|
85
|
-
final markdown = converter.convert(html);
|
|
86
|
-
expect(markdown, equals('[Video Title](video.mp4)'));
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('converts video with poster', () {
|
|
90
|
-
const html =
|
|
91
|
-
'<video src="video.mp4" poster="poster.jpg" title="Video Title"></video>';
|
|
92
|
-
final markdown = converter.convert(html);
|
|
93
|
-
expect(markdown, equals('[](video.mp4)'));
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('converts iframe', () {
|
|
97
|
-
const html =
|
|
98
|
-
'<iframe src="https://example.com" title="Example Iframe"></iframe>';
|
|
99
|
-
final markdown = converter.convert(html);
|
|
100
|
-
expect(markdown, equals('[Example Iframe](https://example.com)'));
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
}
|