sdd-full 4.6.2 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin.js +1 -1
- package/package.json +1 -1
- package/skills/.agents/skills/flutter-add-integration-test/SKILL.md +165 -0
- package/skills/.agents/skills/flutter-add-widget-preview/SKILL.md +147 -0
- package/skills/.agents/skills/flutter-add-widget-test/SKILL.md +156 -0
- package/skills/.agents/skills/flutter-apply-architecture-best-practices/SKILL.md +164 -0
- package/skills/.agents/skills/flutter-build-responsive-layout/SKILL.md +141 -0
- package/skills/.agents/skills/flutter-fix-layout-issues/SKILL.md +132 -0
- package/skills/.agents/skills/flutter-implement-json-serialization/SKILL.md +155 -0
- package/skills/.agents/skills/flutter-setup-declarative-routing/SKILL.md +257 -0
- package/skills/.agents/skills/flutter-setup-localization/SKILL.md +212 -0
- package/skills/.agents/skills/flutter-use-http-package/SKILL.md +177 -0
- package/skills/VERSION.md +176 -62
- package/skills/design-planning/ai-coding-rules/SKILL.md +5 -13
- package/skills/design-planning/design-to-code/SKILL.md +5 -14
- package/skills/design-planning/enterprise-spec/SKILL.md +5 -13
- package/skills/design-planning/flutter-av/SKILL.md +5 -16
- package/skills/design-planning/flutter-map/SKILL.md +5 -14
- package/skills/design-planning/function-sdd/SKILL.md +5 -13
- package/skills/design-planning/global-overlay-stack-standard/SKILL.md +73 -0
- package/skills/design-planning/ui-motion-interaction-standard/SKILL.md +69 -0
- package/skills/design-planning/ui-sdd-specialized/SKILL.md +5 -14
- package/skills/development-execution/flutter-errors/SKILL.md +5 -15
- package/skills/flutter-skills/.github/dependabot.yaml +15 -0
- package/skills/flutter-skills/.github/workflows/dart_skills_lint_workflow.yaml +68 -0
- package/skills/flutter-skills/.github/workflows/skills_tool.yaml +51 -0
- package/skills/flutter-skills/CODE_OF_CONDUCT.md +3 -0
- package/skills/flutter-skills/CONTRIBUTING.md +36 -0
- package/skills/flutter-skills/LICENSE +26 -0
- package/skills/flutter-skills/README.md +50 -0
- package/skills/flutter-skills/pubspec.yaml +9 -0
- package/skills/flutter-skills/resources/flutter_skills.yaml +434 -0
- package/skills/flutter-skills/skills/flutter-add-integration-test/SKILL.md +163 -0
- package/skills/flutter-skills/skills/flutter-add-widget-preview/SKILL.md +145 -0
- package/skills/flutter-skills/skills/flutter-add-widget-test/SKILL.md +154 -0
- package/skills/flutter-skills/skills/flutter-apply-architecture-best-practices/SKILL.md +162 -0
- package/skills/flutter-skills/skills/flutter-build-responsive-layout/SKILL.md +139 -0
- package/skills/flutter-skills/skills/flutter-fix-layout-issues/SKILL.md +130 -0
- package/skills/flutter-skills/skills/flutter-implement-json-serialization/SKILL.md +153 -0
- package/skills/flutter-skills/skills/flutter-setup-declarative-routing/SKILL.md +255 -0
- package/skills/flutter-skills/skills/flutter-setup-localization/SKILL.md +210 -0
- package/skills/flutter-skills/skills/flutter-use-http-package/SKILL.md +175 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/add-dart-lint-validation-rule/SKILL.md +196 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-best-practices/SKILL.md +65 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-checks-migration/SKILL.md +158 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-cli-app-best-practices/SKILL.md +168 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-doc-validation/SKILL.md +87 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-long-lines/SKILL.md +101 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-matcher-best-practices/SKILL.md +136 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-modern-features/SKILL.md +266 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-package-maintenance/SKILL.md +92 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/SKILL.md +92 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/lib/src/calculator.dart +7 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/pubspec.yaml +8 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/test/calculator_test.dart +11 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/interpret_coverage.dart +95 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/pubspec.yaml +6 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/test/interpret_coverage_test.dart +93 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-fundamentals/SKILL.md +173 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/definition-of-done/SKILL.md +27 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/flutter_skills_ignore.json +3 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/grill-me/SKILL.md +10 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/ignore.json +3 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/test-driven-development/SKILL.md +371 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/skills/flutter-skills/tool/dart_skills_lint/AUTHORS +7 -0
- package/skills/flutter-skills/tool/dart_skills_lint/CHANGELOG.md +12 -0
- package/skills/flutter-skills/tool/dart_skills_lint/CONTRIBUTING.md +51 -0
- package/skills/flutter-skills/tool/dart_skills_lint/LICENSE +27 -0
- package/skills/flutter-skills/tool/dart_skills_lint/README.md +203 -0
- package/skills/flutter-skills/tool/dart_skills_lint/analysis_options.yaml +296 -0
- package/skills/flutter-skills/tool/dart_skills_lint/bench/README.md +23 -0
- package/skills/flutter-skills/tool/dart_skills_lint/bench/baseline_throughput.dart +230 -0
- package/skills/flutter-skills/tool/dart_skills_lint/bin/cli.dart +10 -0
- package/skills/flutter-skills/tool/dart_skills_lint/dart_skills_lint.yaml +14 -0
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/PRODUCTION_READYNESS.md +48 -0
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/completion_migration_plan.md +99 -0
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/legacy_patterns_report.md +110 -0
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/pub_vs_skill_report.md +56 -0
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/knowledge/SPECIFICATION.md +79 -0
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/knowledge/architecture_overview.md +64 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/dart_skills_lint.dart +11 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/config_parser.dart +156 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/entry_point.dart +354 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/fixable_rule.dart +20 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/analysis_severity.dart +15 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/check_type.dart +17 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/ignore_entry.dart +34 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/ignore_entry.g.dart +19 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skill_context.dart +27 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skill_rule.dart +27 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skills_ignores.dart +26 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skills_ignores.g.dart +24 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/validation_error.dart +31 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rule_registry.dart +79 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/absolute_paths_rule.dart +74 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/description_length_rule.dart +49 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/disallowed_field_rule.dart +61 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/name_format_rule.dart +167 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/relative_paths_rule.dart +72 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/trailing_whitespace_rule.dart +93 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/valid_yaml_metadata_rule.dart +74 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/skills_ignores_storage.dart +36 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/validation_session.dart +559 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/validator.dart +238 -0
- package/skills/flutter-skills/tool/dart_skills_lint/pubspec.yaml +28 -0
- package/skills/flutter-skills/tool/dart_skills_lint/skills/README.md +10 -0
- package/skills/flutter-skills/tool/dart_skills_lint/skills/dart-skills-lint-validation/SKILL.md +195 -0
- package/skills/flutter-skills/tool/dart_skills_lint/skills-lock.json +75 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/absolute_paths_test.dart +167 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/cli_integration_test.dart +683 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/config_file_test.dart +292 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/custom_rule_test.dart +122 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/directory_structure_test.dart +163 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/field_constraints_test.dart +178 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/fixer_test.dart +172 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/ignore_models_test.dart +63 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/metadata_validation_test.dart +116 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/relative_path_flag_test.dart +70 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/relative_paths_test.dart +172 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/resolve_rules_test.dart +82 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/rule_naming_test.dart +29 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/skills_ignores_storage_test.dart +89 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/test_utils.dart +19 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/trailing_whitespace_test.dart +152 -0
- package/skills/flutter-skills/tool/generator/README.md +150 -0
- package/skills/flutter-skills/tool/generator/analysis_options.yaml +143 -0
- package/skills/flutter-skills/tool/generator/bin/skills.dart +73 -0
- package/skills/flutter-skills/tool/generator/lib/src/commands/base_skill_command.dart +87 -0
- package/skills/flutter-skills/tool/generator/lib/src/commands/base_yaml_command.dart +83 -0
- package/skills/flutter-skills/tool/generator/lib/src/commands/generate_skill_command.dart +92 -0
- package/skills/flutter-skills/tool/generator/lib/src/commands/update_readme_command.dart +150 -0
- package/skills/flutter-skills/tool/generator/lib/src/commands/update_skill_command.dart +97 -0
- package/skills/flutter-skills/tool/generator/lib/src/commands/validate_skill_command.dart +284 -0
- package/skills/flutter-skills/tool/generator/lib/src/models/skill_params.dart +41 -0
- package/skills/flutter-skills/tool/generator/lib/src/services/gemini_service.dart +310 -0
- package/skills/flutter-skills/tool/generator/lib/src/services/markdown_converter.dart +226 -0
- package/skills/flutter-skills/tool/generator/lib/src/services/prompts.dart +72 -0
- package/skills/flutter-skills/tool/generator/lib/src/services/resource_fetcher_service.dart +84 -0
- package/skills/flutter-skills/tool/generator/lib/src/services/skill_instructions.dart +30 -0
- package/skills/flutter-skills/tool/generator/pubspec.yaml +32 -0
- package/skills/flutter-skills/tool/generator/test/commands/base_skill_command_test.dart +131 -0
- package/skills/flutter-skills/tool/generator/test/commands/validate_skills_input_test.dart +263 -0
- package/skills/flutter-skills/tool/generator/test/custom_skill_rules/last_modified_rule.dart +32 -0
- package/skills/flutter-skills/tool/generator/test/generate_skills_retry_test.dart +105 -0
- package/skills/flutter-skills/tool/generator/test/generate_skills_test.dart +519 -0
- package/skills/flutter-skills/tool/generator/test/lint_skills_test.dart +34 -0
- package/skills/flutter-skills/tool/generator/test/markdown_converter_test.dart +103 -0
- package/skills/flutter-skills/tool/generator/test/markdown_table_test.dart +131 -0
- package/skills/flutter-skills/tool/generator/test/models/skill_params_test.dart +37 -0
- package/skills/flutter-skills/tool/generator/test/services/gemini_service_test.dart +291 -0
- package/skills/flutter-skills/tool/generator/test/services/markdown_converter_test.dart +156 -0
- package/skills/flutter-skills/tool/generator/test/services/resource_fetcher_service_test.dart +188 -0
- package/skills/flutter-skills/tool/generator/test/update_skills_test.dart +241 -0
- package/skills/flutter-skills/tool/generator/test/validate_skills_test.dart +728 -0
- package/skills/quality-assurance/bdd-acceptance/SKILL.md +5 -14
- package/skills/quality-assurance/flutter-test/SKILL.md +5 -16
- package/skills/rules/project_rules.md +538 -127
- package/skills/special-tools/env-check/SKILL.md +5 -13
- package/skills/special-tools/ios-full-auto-debug/SKILL.md +5 -15
- package/skills/writing-skills/SKILL.md +654 -0
- package/skills/writing-skills/anthropic-best-practices.md +1149 -0
- package/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/skills/writing-skills/persuasion-principles.md +187 -0
- package/skills/writing-skills/render-graphs.js +168 -0
- package/skills/writing-skills/testing-skills-with-subagents.md +384 -0
- package/skills/checklist.md +0 -154
- package/skills/rules/user_rules.md +0 -263
- package/skills//345/256/214/346/225/264/345/274/200/345/217/221/346/265/201/347/250/213/346/211/213/345/206/214.md +0 -454
- package/skills//346/212/200/350/203/275/344/275/223/347/263/273/345/256/214/345/226/204/345/273/272/350/256/256.md +0 -308
- package/skills//346/212/200/350/203/275/344/275/277/347/224/250/346/214/207/345/215/227.md +0 -309
- package/skills//346/212/200/350/203/275/345/206/263/347/255/226/346/240/221.md +0 -338
|
@@ -0,0 +1,683 @@
|
|
|
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:dart_skills_lint/src/entry_point.dart';
|
|
9
|
+
import 'package:dart_skills_lint/src/models/check_type.dart';
|
|
10
|
+
import 'package:dart_skills_lint/src/models/ignore_entry.dart';
|
|
11
|
+
import 'package:dart_skills_lint/src/models/skills_ignores.dart';
|
|
12
|
+
import 'package:dart_skills_lint/src/rule_registry.dart';
|
|
13
|
+
import 'package:dart_skills_lint/src/validator.dart';
|
|
14
|
+
import 'package:path/path.dart' as p;
|
|
15
|
+
import 'package:test/test.dart';
|
|
16
|
+
import 'package:test_process/test_process.dart';
|
|
17
|
+
|
|
18
|
+
import 'test_utils.dart';
|
|
19
|
+
|
|
20
|
+
void main() {
|
|
21
|
+
group('CLI Integration', () {
|
|
22
|
+
late Directory tempDir;
|
|
23
|
+
|
|
24
|
+
setUp(() async {
|
|
25
|
+
tempDir = await Directory.systemTemp.createTemp('cli_test.');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
tearDown(() async {
|
|
29
|
+
if (tempDir.existsSync()) {
|
|
30
|
+
await tempDir.delete(recursive: true);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('de-duplicates baseline entries for multiple identical rule failures', () async {
|
|
35
|
+
final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
|
|
36
|
+
await File('${skillDir.path}/SKILL.md').writeAsString(
|
|
37
|
+
'${buildFrontmatter(name: 'test-skill')}[Link 1](missing1.md)\n[Link 2](missing2.md)\n',
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Run with --generate-baseline
|
|
41
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
42
|
+
'bin/cli.dart',
|
|
43
|
+
'-s',
|
|
44
|
+
skillDir.path,
|
|
45
|
+
'--generate-baseline',
|
|
46
|
+
]);
|
|
47
|
+
await process.shouldExit(0);
|
|
48
|
+
|
|
49
|
+
final ignoreFile = File('${skillDir.parent.path}/$defaultIgnoreFileName');
|
|
50
|
+
expect(ignoreFile.existsSync(), isTrue);
|
|
51
|
+
|
|
52
|
+
final String content = await ignoreFile.readAsString();
|
|
53
|
+
final json = jsonDecode(content) as Map<String, dynamic>;
|
|
54
|
+
final skills = json[SkillsIgnores.skillsKey] as Map<String, dynamic>;
|
|
55
|
+
final ignores = skills['test-skill'] as List;
|
|
56
|
+
|
|
57
|
+
// Should be 1 entry only! Both relative link failures utilize the same ruleId/fileName de-duplication.
|
|
58
|
+
expect(ignores.length, equals(1));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('individual skill baseline is loaded on subsequent runs', () async {
|
|
62
|
+
final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
|
|
63
|
+
await File(
|
|
64
|
+
'${skillDir.path}/SKILL.md',
|
|
65
|
+
).writeAsString('${buildFrontmatter(name: 'test-skill')}[Link](missing.md)\n');
|
|
66
|
+
|
|
67
|
+
final TestProcess genProcess = await TestProcess.start('dart', [
|
|
68
|
+
'bin/cli.dart',
|
|
69
|
+
'-s',
|
|
70
|
+
skillDir.path,
|
|
71
|
+
'--generate-baseline',
|
|
72
|
+
]);
|
|
73
|
+
await genProcess.shouldExit(0);
|
|
74
|
+
|
|
75
|
+
final ignoreFile = File('${tempDir.path}/$defaultIgnoreFileName');
|
|
76
|
+
expect(ignoreFile.existsSync(), isTrue);
|
|
77
|
+
|
|
78
|
+
final TestProcess runProcess = await TestProcess.start('dart', [
|
|
79
|
+
'bin/cli.dart',
|
|
80
|
+
'-s',
|
|
81
|
+
skillDir.path,
|
|
82
|
+
]);
|
|
83
|
+
await runProcess.shouldExit(0);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test(
|
|
87
|
+
'cross-skill baseline de-duplicates and suppresses all errors across different skills',
|
|
88
|
+
() async {
|
|
89
|
+
final Directory skillsDir = await Directory('${tempDir.path}/skills').create();
|
|
90
|
+
|
|
91
|
+
// Create skill-one with a broken link
|
|
92
|
+
final Directory skill1Dir = await Directory('${skillsDir.path}/skill-one').create();
|
|
93
|
+
await File('${skill1Dir.path}/SKILL.md').writeAsString(
|
|
94
|
+
'${buildFrontmatter(name: 'skill-one', description: 'Skill one with a broken link')}[Link to nowhere](../nowhere/SKILL.md)\n',
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// Create skill-two with a broken link
|
|
98
|
+
final Directory skill2Dir = await Directory('${skillsDir.path}/skill-two').create();
|
|
99
|
+
await File('${skill2Dir.path}/SKILL.md').writeAsString(
|
|
100
|
+
'${buildFrontmatter(name: 'skill-two', description: 'Skill two with a broken link')}[Link to nowhere](../nowhere/SKILL.md)\n',
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
final configFile = File('${tempDir.path}/dart_skills_lint.yaml');
|
|
104
|
+
await configFile.writeAsString('''
|
|
105
|
+
dart_skills_lint:
|
|
106
|
+
directories:
|
|
107
|
+
- path: "skills"
|
|
108
|
+
rules:
|
|
109
|
+
check-relative-paths: error
|
|
110
|
+
ignore_file: "$defaultIgnoreFileName"
|
|
111
|
+
''');
|
|
112
|
+
|
|
113
|
+
// 1. Run with --generate-baseline. It should evaluate all skills and write both to the baseline!
|
|
114
|
+
final TestProcess genProcess = await TestProcess.start('dart', [
|
|
115
|
+
p.normalize(p.absolute('bin/cli.dart')),
|
|
116
|
+
'-d',
|
|
117
|
+
'skills',
|
|
118
|
+
'--generate-baseline',
|
|
119
|
+
], workingDirectory: tempDir.path);
|
|
120
|
+
await genProcess.shouldExit(0); // Exits 0 if --generate-baseline is passed
|
|
121
|
+
|
|
122
|
+
final ignoreFile = File('${tempDir.path}/$defaultIgnoreFileName');
|
|
123
|
+
expect(ignoreFile.existsSync(), isTrue);
|
|
124
|
+
|
|
125
|
+
final String content = await ignoreFile.readAsString();
|
|
126
|
+
final json = jsonDecode(content) as Map<String, dynamic>;
|
|
127
|
+
final skills = json[SkillsIgnores.skillsKey] as Map<String, dynamic>;
|
|
128
|
+
|
|
129
|
+
expect(skills.containsKey('skill-one'), isTrue);
|
|
130
|
+
expect(skills.containsKey('skill-two'), isTrue);
|
|
131
|
+
|
|
132
|
+
// 2. Run again silently. It should succeed with exit 0 because all errors are ignored!
|
|
133
|
+
final TestProcess runProcess = await TestProcess.start('dart', [
|
|
134
|
+
p.normalize(p.absolute('bin/cli.dart')),
|
|
135
|
+
'-d',
|
|
136
|
+
'skills',
|
|
137
|
+
'-q',
|
|
138
|
+
], workingDirectory: tempDir.path);
|
|
139
|
+
await runProcess.shouldExit(0);
|
|
140
|
+
},
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
test('exits with 0 and success message for valid skill', () async {
|
|
144
|
+
final Directory skillDir = await Directory('${tempDir.path}/valid-skill').create();
|
|
145
|
+
await File(
|
|
146
|
+
'${skillDir.path}/SKILL.md',
|
|
147
|
+
).writeAsString('${buildFrontmatter(name: 'valid-skill', description: 'A valid skill')}Body');
|
|
148
|
+
|
|
149
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
150
|
+
'bin/cli.dart',
|
|
151
|
+
'-s',
|
|
152
|
+
skillDir.path,
|
|
153
|
+
]);
|
|
154
|
+
|
|
155
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
156
|
+
expect(stdout.join('\n'), contains(skillIsValidMsg));
|
|
157
|
+
await process.shouldExit(0);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test('exits with 1 and error message for invalid skill', () async {
|
|
161
|
+
final Directory skillDir = await Directory('${tempDir.path}/invalid-skill').create();
|
|
162
|
+
// SKILL.md is missing
|
|
163
|
+
|
|
164
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
165
|
+
'bin/cli.dart',
|
|
166
|
+
'-s',
|
|
167
|
+
skillDir.path,
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
final List<String> stderr = await process.stderr.rest.toList();
|
|
171
|
+
final String stderrStr = stderr.join('\n');
|
|
172
|
+
expect(stderrStr, contains(skillIsInvalidMsg));
|
|
173
|
+
expect(stderrStr, contains('SKILL.md is missing'));
|
|
174
|
+
await process.shouldExit(1);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('exits with 0 and validates subdirectories if named "skills"', () async {
|
|
178
|
+
final Directory skillsDir = await Directory('${tempDir.path}/skills').create();
|
|
179
|
+
final Directory skill1 = await Directory('${skillsDir.path}/skill-a').create();
|
|
180
|
+
await File(
|
|
181
|
+
'${skill1.path}/SKILL.md',
|
|
182
|
+
).writeAsString('${buildFrontmatter(name: 'skill-a', description: 'Skill A')}Body');
|
|
183
|
+
|
|
184
|
+
final Directory skill2 = await Directory('${skillsDir.path}/skill-b').create();
|
|
185
|
+
await File(
|
|
186
|
+
'${skill2.path}/SKILL.md',
|
|
187
|
+
).writeAsString('${buildFrontmatter(name: 'skill-b', description: 'Skill B')}Body');
|
|
188
|
+
|
|
189
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
190
|
+
'bin/cli.dart',
|
|
191
|
+
'-d',
|
|
192
|
+
skillsDir.path,
|
|
193
|
+
]);
|
|
194
|
+
|
|
195
|
+
// Verify outputs for both skills (sorted order)
|
|
196
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
197
|
+
final String stdoutStr = stdout.join('\n');
|
|
198
|
+
expect(stdoutStr, contains(evaluatingDirMsg));
|
|
199
|
+
expect(stdoutStr, contains('--- Validating skill: skill-a ---'));
|
|
200
|
+
expect(stdoutStr, contains(skillIsValidMsg));
|
|
201
|
+
|
|
202
|
+
expect(stdoutStr, contains('--- Validating skill: skill-b ---'));
|
|
203
|
+
expect(stdoutStr, contains(skillIsValidMsg));
|
|
204
|
+
|
|
205
|
+
await process.shouldExit(0);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test('ignores subdirectories starting with a dot "." in "skills" folder', () async {
|
|
209
|
+
final Directory skillsDir = await Directory('${tempDir.path}/skills').create();
|
|
210
|
+
final Directory skill1 = await Directory('${skillsDir.path}/skill-a').create();
|
|
211
|
+
await File(
|
|
212
|
+
'${skill1.path}/SKILL.md',
|
|
213
|
+
).writeAsString('${buildFrontmatter(name: 'skill-a', description: 'Skill A')}Body');
|
|
214
|
+
|
|
215
|
+
await Directory('${skillsDir.path}/.dart_tool').create();
|
|
216
|
+
|
|
217
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
218
|
+
'bin/cli.dart',
|
|
219
|
+
'-d',
|
|
220
|
+
skillsDir.path,
|
|
221
|
+
]);
|
|
222
|
+
|
|
223
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
224
|
+
final String stdoutStr = stdout.join('\n');
|
|
225
|
+
expect(stdoutStr, contains('--- Validating skill: skill-a ---'));
|
|
226
|
+
expect(stdoutStr, contains(skillIsValidMsg));
|
|
227
|
+
expect(stdoutStr, isNot(contains('.dart_tool')));
|
|
228
|
+
|
|
229
|
+
await process.shouldExit(0);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test('exits with 1 if any subdirectory skill fails in "skills" folder', () async {
|
|
233
|
+
final Directory skillsDir = await Directory('${tempDir.path}/skills').create();
|
|
234
|
+
final Directory skill1 = await Directory('${skillsDir.path}/skill-a').create();
|
|
235
|
+
await File(
|
|
236
|
+
'${skill1.path}/SKILL.md',
|
|
237
|
+
).writeAsString('${buildFrontmatter(name: 'skill-a', description: 'Skill A')}Body');
|
|
238
|
+
|
|
239
|
+
await Directory('${skillsDir.path}/skill-b').create(); // No SKILL.md
|
|
240
|
+
|
|
241
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
242
|
+
'bin/cli.dart',
|
|
243
|
+
'-d',
|
|
244
|
+
skillsDir.path,
|
|
245
|
+
]);
|
|
246
|
+
|
|
247
|
+
// Verify outputs
|
|
248
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
249
|
+
expect(stdout.join('\n'), contains('--- Validating skill: skill-a ---'));
|
|
250
|
+
expect(stdout.join('\n'), contains(skillIsValidMsg));
|
|
251
|
+
|
|
252
|
+
expect(stdout.join('\n'), contains('--- Validating skill: skill-b ---'));
|
|
253
|
+
final List<String> stderr = await process.stderr.rest.toList();
|
|
254
|
+
expect(stderr.join('\n'), contains(skillIsInvalidMsg));
|
|
255
|
+
await process.shouldExit(1);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test(
|
|
259
|
+
'exits with 1 early and does not process subsequent skills if --fast-fail is passed',
|
|
260
|
+
() async {
|
|
261
|
+
final Directory skillsDir = await Directory('${tempDir.path}/skills').create();
|
|
262
|
+
|
|
263
|
+
await Directory('${skillsDir.path}/skill-a').create();
|
|
264
|
+
// skill-a does not create SKILL.md, so it is invalid and will fail first (sorted order)
|
|
265
|
+
|
|
266
|
+
await Directory('${skillsDir.path}/skill-b').create();
|
|
267
|
+
await File(
|
|
268
|
+
'${p.join(tempDir.path, 'skills', 'skill-b')}/SKILL.md',
|
|
269
|
+
).writeAsString('${buildFrontmatter(name: 'skill-b', description: 'Skill B')}Body');
|
|
270
|
+
|
|
271
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
272
|
+
'bin/cli.dart',
|
|
273
|
+
'-d',
|
|
274
|
+
skillsDir.path,
|
|
275
|
+
'--fast-fail',
|
|
276
|
+
]);
|
|
277
|
+
|
|
278
|
+
// Verify outputs for skill-a
|
|
279
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
280
|
+
final String stdoutStr = stdout.join('\n');
|
|
281
|
+
expect(stdoutStr, contains(evaluatingDirMsg));
|
|
282
|
+
expect(stdoutStr, contains('--- Validating skill: skill-a ---'));
|
|
283
|
+
|
|
284
|
+
final List<String> stderr = await process.stderr.rest.toList();
|
|
285
|
+
expect(stderr.join('\n'), contains(skillIsInvalidMsg));
|
|
286
|
+
|
|
287
|
+
// Since process exits after skill-a, stdout should be closed and no further lines (like skill-b) should appear.
|
|
288
|
+
await process.shouldExit(1);
|
|
289
|
+
},
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
test('exits with 0 and suppresses success messages if --quiet is passed', () async {
|
|
293
|
+
final Directory skillDir = await Directory('${tempDir.path}/valid-skill').create();
|
|
294
|
+
await File(
|
|
295
|
+
'${skillDir.path}/SKILL.md',
|
|
296
|
+
).writeAsString('${buildFrontmatter(name: 'valid-skill', description: 'A valid skill')}Body');
|
|
297
|
+
|
|
298
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
299
|
+
'bin/cli.dart',
|
|
300
|
+
'-s',
|
|
301
|
+
skillDir.path,
|
|
302
|
+
'--quiet',
|
|
303
|
+
]);
|
|
304
|
+
|
|
305
|
+
await process.shouldExit(0);
|
|
306
|
+
|
|
307
|
+
// Stdout should be empty for a valid skill in quiet mode
|
|
308
|
+
final List<String> rest = await process.stdout.rest.toList();
|
|
309
|
+
expect(rest, isEmpty);
|
|
310
|
+
});
|
|
311
|
+
test('fails with 64 when no flags passed and both defaults are missing', () async {
|
|
312
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
313
|
+
p.normalize(p.absolute('bin/cli.dart')),
|
|
314
|
+
], workingDirectory: tempDir.path);
|
|
315
|
+
|
|
316
|
+
final List<String> stderr = await process.stderr.rest.toList();
|
|
317
|
+
expect(stderr.join('\n'), contains('Missing skills directory. Checked defaults:'));
|
|
318
|
+
await process.shouldExit(64);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test('picks up .claude/skills when no flags passed and it exists', () async {
|
|
322
|
+
final Directory claudeDir = await Directory(
|
|
323
|
+
'${tempDir.path}/.claude/skills',
|
|
324
|
+
).create(recursive: true);
|
|
325
|
+
final Directory skillDir = await Directory('${claudeDir.path}/valid-skill').create();
|
|
326
|
+
await File(
|
|
327
|
+
'${skillDir.path}/SKILL.md',
|
|
328
|
+
).writeAsString('${buildFrontmatter(name: 'valid-skill', description: 'A valid skill')}Body');
|
|
329
|
+
|
|
330
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
331
|
+
p.normalize(p.absolute('bin/cli.dart')),
|
|
332
|
+
], workingDirectory: tempDir.path);
|
|
333
|
+
|
|
334
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
335
|
+
expect(stdout.join('\n'), contains('Skill is valid.'));
|
|
336
|
+
await process.shouldExit(0);
|
|
337
|
+
});
|
|
338
|
+
test('expands ~/ to HOME environment variable', () async {
|
|
339
|
+
final Directory skillDir = await Directory('${tempDir.path}/some-skill').create();
|
|
340
|
+
await File(
|
|
341
|
+
'${skillDir.path}/SKILL.md',
|
|
342
|
+
).writeAsString('${buildFrontmatter(name: 'some-skill')}Body');
|
|
343
|
+
|
|
344
|
+
final TestProcess process = await TestProcess.start(
|
|
345
|
+
'dart',
|
|
346
|
+
[p.normalize(p.absolute('bin/cli.dart')), '-s', '~/some-skill'],
|
|
347
|
+
environment: {'HOME': tempDir.path},
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
351
|
+
expect(stdout.join('\n'), contains('Skill is valid.'));
|
|
352
|
+
await process.shouldExit(0);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
test('overrides valid-yaml-metadata flag to disabled', () async {
|
|
356
|
+
final Directory skillDir = await Directory('${tempDir.path}/invalid-yaml').create();
|
|
357
|
+
await File('${skillDir.path}/SKILL.md').writeAsString('Invalid YAML No Frontmatter');
|
|
358
|
+
|
|
359
|
+
// 1. Run normally. Should fail because valid-yaml-metadata defaults to true (error).
|
|
360
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
361
|
+
'bin/cli.dart',
|
|
362
|
+
'-s',
|
|
363
|
+
skillDir.path,
|
|
364
|
+
]);
|
|
365
|
+
await process.shouldExit(1);
|
|
366
|
+
|
|
367
|
+
// 2. Run with --no-valid-yaml-metadata. Should pass because the check is disabled!
|
|
368
|
+
final TestProcess noYamlProcess = await TestProcess.start('dart', [
|
|
369
|
+
'bin/cli.dart',
|
|
370
|
+
'-s',
|
|
371
|
+
skillDir.path,
|
|
372
|
+
'--no-valid-yaml-metadata',
|
|
373
|
+
]);
|
|
374
|
+
await noYamlProcess.shouldExit(0);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test('fails if -d specifies a directory with zero skills', () async {
|
|
378
|
+
final Directory emptyDir = await Directory('${tempDir.path}/empty-root').create();
|
|
379
|
+
|
|
380
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
381
|
+
'bin/cli.dart',
|
|
382
|
+
'-d',
|
|
383
|
+
emptyDir.path,
|
|
384
|
+
]);
|
|
385
|
+
|
|
386
|
+
await process.shouldExit(1);
|
|
387
|
+
final List<String> stderr = await process.stderr.rest.toList();
|
|
388
|
+
expect(
|
|
389
|
+
stderr.join('\n'),
|
|
390
|
+
contains('No skills found to validate in the specified directories.'),
|
|
391
|
+
);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
test('fails if -d specifies a single skill directory (no sub-folders found)', () async {
|
|
395
|
+
final Directory skillAsRoot = await Directory('${tempDir.path}/single-skill-root').create();
|
|
396
|
+
await File('${skillAsRoot.path}/SKILL.md').writeAsString(
|
|
397
|
+
'${buildFrontmatter(name: 'single-skill-root', description: 'Not a root, but a skill folder.')}Body',
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
401
|
+
'bin/cli.dart',
|
|
402
|
+
'-d',
|
|
403
|
+
skillAsRoot.path,
|
|
404
|
+
]);
|
|
405
|
+
|
|
406
|
+
await process.shouldExit(1);
|
|
407
|
+
final List<String> stderr = await process.stderr.rest.toList();
|
|
408
|
+
expect(
|
|
409
|
+
stderr.join('\n'),
|
|
410
|
+
contains(
|
|
411
|
+
'appears to be an individual skill. Use --skill / -s instead of -d / --skills-directory.',
|
|
412
|
+
),
|
|
413
|
+
);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test('validates multiple skills with multiple -s flags', () async {
|
|
417
|
+
final Directory skill1 = await Directory('${tempDir.path}/skill-1').create();
|
|
418
|
+
await File(
|
|
419
|
+
'${skill1.path}/SKILL.md',
|
|
420
|
+
).writeAsString('${buildFrontmatter(name: 'skill-1', description: 'Skill 1')}Body');
|
|
421
|
+
|
|
422
|
+
final Directory skill2 = await Directory('${tempDir.path}/skill-2').create();
|
|
423
|
+
await File(
|
|
424
|
+
'${skill2.path}/SKILL.md',
|
|
425
|
+
).writeAsString('${buildFrontmatter(name: 'skill-2', description: 'Skill 2')}Body');
|
|
426
|
+
|
|
427
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
428
|
+
'bin/cli.dart',
|
|
429
|
+
'-s',
|
|
430
|
+
skill1.path,
|
|
431
|
+
'-s',
|
|
432
|
+
skill2.path,
|
|
433
|
+
]);
|
|
434
|
+
|
|
435
|
+
await process.shouldExit(0);
|
|
436
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
437
|
+
final String stdoutStr = stdout.join('\n');
|
|
438
|
+
expect(stdoutStr, contains('--- Validating skill: skill-1 ---'));
|
|
439
|
+
expect(stdoutStr, contains('--- Validating skill: skill-2 ---'));
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
test('handles malformed JSON ignore-file gracefully by falling back', () async {
|
|
443
|
+
final malformedFile = File('${tempDir.path}/malformed.json');
|
|
444
|
+
await malformedFile.writeAsString('{ malformed json }');
|
|
445
|
+
|
|
446
|
+
final Directory skillFolder = await Directory('${tempDir.path}/skill-x').create();
|
|
447
|
+
await File(
|
|
448
|
+
'${skillFolder.path}/SKILL.md',
|
|
449
|
+
).writeAsString('${buildFrontmatter(name: 'skill-x', description: 'Valid skill')}Body');
|
|
450
|
+
|
|
451
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
452
|
+
'bin/cli.dart',
|
|
453
|
+
'-s',
|
|
454
|
+
skillFolder.path,
|
|
455
|
+
'--ignore-file',
|
|
456
|
+
malformedFile.path,
|
|
457
|
+
]);
|
|
458
|
+
|
|
459
|
+
await process.shouldExit(0); // Valid skill should still pass
|
|
460
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
461
|
+
expect(stdout.join('\n'), contains('Evaluating directory:'));
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
test('CLI help displays all registered rules', () async {
|
|
465
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
466
|
+
p.normalize(p.absolute('bin/cli.dart')),
|
|
467
|
+
'--help',
|
|
468
|
+
]);
|
|
469
|
+
await process.shouldExit(0);
|
|
470
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
471
|
+
final String stdoutStr = stdout.join('\n');
|
|
472
|
+
|
|
473
|
+
for (final CheckType check in RuleRegistry.allChecks) {
|
|
474
|
+
expect(stdoutStr, contains(check.name));
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
test('CLI help does not display path-does-not-exist', () async {
|
|
479
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
480
|
+
p.normalize(p.absolute('bin/cli.dart')),
|
|
481
|
+
'--help',
|
|
482
|
+
]);
|
|
483
|
+
await process.shouldExit(0);
|
|
484
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
485
|
+
final String stdoutStr = stdout.join('\n');
|
|
486
|
+
|
|
487
|
+
expect(stdoutStr, isNot(contains(Validator.pathDoesNotExist)));
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
test('ignores directory missing SKILL.md if listed in ignore file', () async {
|
|
491
|
+
final Directory skillsDir = await Directory('${tempDir.path}/skills').create();
|
|
492
|
+
|
|
493
|
+
// Create a valid skill
|
|
494
|
+
final Directory skillDir = await Directory('${skillsDir.path}/valid-skill').create();
|
|
495
|
+
await File(
|
|
496
|
+
'${skillDir.path}/SKILL.md',
|
|
497
|
+
).writeAsString('---\nname: valid-skill\ndescription: A valid skill\n---\nBody');
|
|
498
|
+
|
|
499
|
+
// Create a non-skill directory
|
|
500
|
+
await Directory('${skillsDir.path}/contributing').create();
|
|
501
|
+
|
|
502
|
+
// Create ignore file
|
|
503
|
+
final ignoreFile = File('${tempDir.path}/$defaultIgnoreFileName');
|
|
504
|
+
await ignoreFile.writeAsString(
|
|
505
|
+
jsonEncode({
|
|
506
|
+
SkillsIgnores.skillsKey: {
|
|
507
|
+
'contributing': [
|
|
508
|
+
{
|
|
509
|
+
IgnoreEntry.ruleIdKey: Validator.pathDoesNotExist,
|
|
510
|
+
IgnoreEntry.fileNameKey: 'skills/contributing',
|
|
511
|
+
},
|
|
512
|
+
],
|
|
513
|
+
},
|
|
514
|
+
}),
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
final configFile = File('${tempDir.path}/dart_skills_lint.yaml');
|
|
518
|
+
await configFile.writeAsString('''
|
|
519
|
+
dart_skills_lint:
|
|
520
|
+
directories:
|
|
521
|
+
- path: "skills"
|
|
522
|
+
ignore_file: "$defaultIgnoreFileName"
|
|
523
|
+
''');
|
|
524
|
+
|
|
525
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
526
|
+
p.normalize(p.absolute('bin/cli.dart')),
|
|
527
|
+
'-d',
|
|
528
|
+
'skills',
|
|
529
|
+
], workingDirectory: tempDir.path);
|
|
530
|
+
|
|
531
|
+
await process.shouldExit(0);
|
|
532
|
+
|
|
533
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
534
|
+
final String stdoutStr = stdout.join('\n');
|
|
535
|
+
expect(stdoutStr, contains('--- Validating skill: valid-skill ---'));
|
|
536
|
+
expect(stdoutStr, contains('--- Validating skill: contributing ---'));
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
test('CLI reports trailing whitespace as error when enabled via config', () async {
|
|
540
|
+
final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
|
|
541
|
+
await File(
|
|
542
|
+
'${skillDir.path}/SKILL.md',
|
|
543
|
+
).writeAsString('${buildFrontmatter(name: 'test-skill')}Line with 1 space \n');
|
|
544
|
+
|
|
545
|
+
final configFile = File('${tempDir.path}/dart_skills_lint.yaml');
|
|
546
|
+
await configFile.writeAsString('''
|
|
547
|
+
dart_skills_lint:
|
|
548
|
+
directories:
|
|
549
|
+
- path: "test-skill"
|
|
550
|
+
rules:
|
|
551
|
+
check-trailing-whitespace: error
|
|
552
|
+
''');
|
|
553
|
+
|
|
554
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
555
|
+
p.normalize(p.absolute('bin/cli.dart')),
|
|
556
|
+
'-s',
|
|
557
|
+
'test-skill',
|
|
558
|
+
], workingDirectory: tempDir.path);
|
|
559
|
+
|
|
560
|
+
final List<String> stderr = await process.stderr.rest.toList();
|
|
561
|
+
final String stderrStr = stderr.join('\n');
|
|
562
|
+
expect(stderrStr, contains('has 1 trailing space(s)'));
|
|
563
|
+
await process.shouldExit(1);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
test('--fix dry-runs and shows diff but does not modify file', () async {
|
|
567
|
+
final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
|
|
568
|
+
await File(
|
|
569
|
+
'${skillDir.path}/SKILL.md',
|
|
570
|
+
).writeAsString('${buildFrontmatter(name: 'test-skill')}Line with 1 space \n');
|
|
571
|
+
|
|
572
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
573
|
+
'bin/cli.dart',
|
|
574
|
+
'-s',
|
|
575
|
+
skillDir.path,
|
|
576
|
+
'--fix',
|
|
577
|
+
'--check-trailing-whitespace',
|
|
578
|
+
]);
|
|
579
|
+
|
|
580
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
581
|
+
final String stdoutStr = stdout.join('\n');
|
|
582
|
+
expect(stdoutStr, contains('[Dry Run] Proposed changes for test-skill (SKILL.md):'));
|
|
583
|
+
|
|
584
|
+
await process.shouldExit(1);
|
|
585
|
+
|
|
586
|
+
// Verify file was not modified
|
|
587
|
+
final String content = await File('${skillDir.path}/SKILL.md').readAsString();
|
|
588
|
+
expect(content, contains('Line with 1 space \n'));
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
test('--fix-apply modifies file', () async {
|
|
592
|
+
final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
|
|
593
|
+
await File(
|
|
594
|
+
'${skillDir.path}/SKILL.md',
|
|
595
|
+
).writeAsString('${buildFrontmatter(name: 'test-skill')}Line with 1 space \n');
|
|
596
|
+
|
|
597
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
598
|
+
'bin/cli.dart',
|
|
599
|
+
'-s',
|
|
600
|
+
skillDir.path,
|
|
601
|
+
'--fix-apply',
|
|
602
|
+
'--check-trailing-whitespace',
|
|
603
|
+
]);
|
|
604
|
+
|
|
605
|
+
final List<String> stdout = await process.stdout.rest.toList();
|
|
606
|
+
expect(stdout.join('\n'), contains('Applied fixes for test-skill'));
|
|
607
|
+
|
|
608
|
+
await process.shouldExit(0);
|
|
609
|
+
|
|
610
|
+
// Verify file was modified
|
|
611
|
+
final String content = await File('${skillDir.path}/SKILL.md').readAsString();
|
|
612
|
+
expect(content, isNot(contains('Line with 1 space \n')));
|
|
613
|
+
expect(content, contains('Line with 1 space\n'));
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
test('--fix-apply does not modify file if lint is ignored', () async {
|
|
617
|
+
final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
|
|
618
|
+
await File(
|
|
619
|
+
'${skillDir.path}/SKILL.md',
|
|
620
|
+
).writeAsString('${buildFrontmatter(name: 'test-skill')}Line with 1 space \n');
|
|
621
|
+
|
|
622
|
+
final ignoreFile = File('${tempDir.path}/$defaultIgnoreFileName');
|
|
623
|
+
await ignoreFile.writeAsString(
|
|
624
|
+
jsonEncode({
|
|
625
|
+
SkillsIgnores.skillsKey: {
|
|
626
|
+
'test-skill': [
|
|
627
|
+
{
|
|
628
|
+
IgnoreEntry.ruleIdKey: 'check-trailing-whitespace',
|
|
629
|
+
IgnoreEntry.fileNameKey: 'SKILL.md',
|
|
630
|
+
},
|
|
631
|
+
],
|
|
632
|
+
},
|
|
633
|
+
}),
|
|
634
|
+
);
|
|
635
|
+
|
|
636
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
637
|
+
'bin/cli.dart',
|
|
638
|
+
'-s',
|
|
639
|
+
skillDir.path,
|
|
640
|
+
'--fix-apply',
|
|
641
|
+
'--check-trailing-whitespace',
|
|
642
|
+
]);
|
|
643
|
+
|
|
644
|
+
await process.shouldExit(0);
|
|
645
|
+
|
|
646
|
+
final String content = await File('${skillDir.path}/SKILL.md').readAsString();
|
|
647
|
+
expect(content, contains('Line with 1 space \n'));
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
test('--fix-apply does not modify file if invalid-skill-name is ignored', () async {
|
|
651
|
+
final Directory skillDir = await Directory('${tempDir.path}/my_skill').create();
|
|
652
|
+
await File('${skillDir.path}/SKILL.md').writeAsString('''
|
|
653
|
+
---
|
|
654
|
+
name: wrong-name
|
|
655
|
+
description: A test skill
|
|
656
|
+
---
|
|
657
|
+
Body''');
|
|
658
|
+
|
|
659
|
+
final ignoreFile = File('${tempDir.path}/$defaultIgnoreFileName');
|
|
660
|
+
await ignoreFile.writeAsString(
|
|
661
|
+
jsonEncode({
|
|
662
|
+
SkillsIgnores.skillsKey: {
|
|
663
|
+
'my_skill': [
|
|
664
|
+
{IgnoreEntry.ruleIdKey: 'invalid-skill-name', IgnoreEntry.fileNameKey: 'SKILL.md'},
|
|
665
|
+
],
|
|
666
|
+
},
|
|
667
|
+
}),
|
|
668
|
+
);
|
|
669
|
+
|
|
670
|
+
final TestProcess process = await TestProcess.start('dart', [
|
|
671
|
+
'bin/cli.dart',
|
|
672
|
+
'-s',
|
|
673
|
+
skillDir.path,
|
|
674
|
+
'--fix-apply',
|
|
675
|
+
]);
|
|
676
|
+
|
|
677
|
+
await process.shouldExit(0);
|
|
678
|
+
|
|
679
|
+
final String content = await File('${skillDir.path}/SKILL.md').readAsString();
|
|
680
|
+
expect(content, contains('name: wrong-name'));
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
}
|