@rfxlamia/skillkit 1.1.0 → 1.2.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/agents/agents/creative-copywriter.md +212 -0
- package/agents/agents/dario-amodei.md +135 -0
- package/agents/agents/doc-simplifier.md +63 -0
- package/agents/agents/kotlin-pro.md +433 -0
- package/agents/agents/red-team.md +136 -0
- package/agents/agents/sam-altman.md +121 -0
- package/agents/agents/seo-manager.md +184 -0
- package/package.json +1 -1
- package/skills/skillkit-help/SKILL.md +81 -0
- package/skills/skillkit-help/knowledge/application/09-case-studies.md +257 -0
- package/skills/skillkit-help/knowledge/application/12-testing-and-validation.md +276 -0
- package/skills/skillkit-help/knowledge/foundation/01-why-skills-exist.md +246 -0
- package/skills/skillkit-help/knowledge/foundation/02-skills-vs-subagents-comparison.md +312 -0
- package/skills/skillkit-help/knowledge/foundation/03-skills-vs-subagents-decision-tree.md +346 -0
- package/skills/skillkit-help/knowledge/foundation/06-platform-constraints.md +237 -0
- package/skills/skillkit-help/knowledge/foundation/08-when-not-to-use-skills.md +270 -0
- package/skills/skillkit-help/template/SKILL.md +52 -0
- package/skills/skills/adversarial-review/SKILL.md +219 -0
- package/skills/skills/baby-education/SKILL.md +260 -0
- package/skills/skills/baby-education/references/advanced-techniques.md +323 -0
- package/skills/skills/baby-education/references/transformations.md +345 -0
- package/skills/skills/been-there-done-that/SKILL.md +455 -0
- package/skills/skills/been-there-done-that/references/analysis-patterns.md +162 -0
- package/skills/skills/been-there-done-that/references/git-commands.md +132 -0
- package/skills/skills/been-there-done-that/references/tree-insertion-logic.md +145 -0
- package/skills/skills/coolhunter/SKILL.md +270 -0
- package/skills/skills/coolhunter/assets/elicitation-methods.csv +51 -0
- package/skills/skills/coolhunter/knowledge/elicitation-methods.md +312 -0
- package/skills/skills/coolhunter/references/workflow-execution.md +238 -0
- package/skills/skills/coolhunter/workflow-plan-coolhunter.md +232 -0
- package/skills/skills/creative-copywriting/SKILL.md +324 -0
- package/skills/skills/creative-copywriting/databases/README.md +60 -0
- package/skills/skills/creative-copywriting/databases/carousel-structures.csv +16 -0
- package/skills/skills/creative-copywriting/databases/emotional-arcs.csv +11 -0
- package/skills/skills/creative-copywriting/databases/hook-formulas.csv +51 -0
- package/skills/skills/creative-copywriting/databases/power-words.csv +201 -0
- package/skills/skills/creative-copywriting/databases/psychological-triggers.csv +21 -0
- package/skills/skills/creative-copywriting/databases/read-more-patterns.csv +26 -0
- package/skills/skills/creative-copywriting/databases/swipe-triggers.csv +31 -0
- package/skills/skills/creative-copywriting/references/carousel-psychology.md +223 -0
- package/skills/skills/creative-copywriting/references/hook-anatomy.md +169 -0
- package/skills/skills/creative-copywriting/references/power-word-science.md +134 -0
- package/skills/skills/creative-copywriting/references/storytelling-frameworks.md +157 -0
- package/skills/skills/diverse-content-gen/SKILL.md +201 -0
- package/skills/skills/diverse-content-gen/references/advanced-techniques.md +320 -0
- package/skills/skills/diverse-content-gen/references/research-findings.md +379 -0
- package/skills/skills/diverse-content-gen/references/task-workflows.md +241 -0
- package/skills/skills/diverse-content-gen/references/tool-integration.md +419 -0
- package/skills/skills/diverse-content-gen/references/troubleshooting.md +426 -0
- package/skills/skills/diverse-content-gen/references/vs-core-technique.md +240 -0
- package/skills/skills/framework-critical-thinking/SKILL.md +220 -0
- package/skills/skills/framework-critical-thinking/references/bias_detector.md +375 -0
- package/skills/skills/framework-critical-thinking/references/fallback_handler.md +239 -0
- package/skills/skills/framework-critical-thinking/references/memory_curator.md +161 -0
- package/skills/skills/framework-critical-thinking/references/metacognitive_monitor.md +297 -0
- package/skills/skills/framework-critical-thinking/references/producer_critic_orchestrator.md +333 -0
- package/skills/skills/framework-critical-thinking/references/reasoning_router.md +235 -0
- package/skills/skills/framework-critical-thinking/references/reasoning_validator.md +97 -0
- package/skills/skills/framework-critical-thinking/references/reflection_trigger.md +78 -0
- package/skills/skills/framework-critical-thinking/references/self_verification.md +388 -0
- package/skills/skills/framework-critical-thinking/references/uncertainty_quantifier.md +207 -0
- package/skills/skills/framework-initiative/SKILL.md +231 -0
- package/skills/skills/framework-initiative/references/examples.md +150 -0
- package/skills/skills/framework-initiative/references/impact-analysis.md +157 -0
- package/skills/skills/framework-initiative/references/intent-patterns.md +145 -0
- package/skills/skills/framework-initiative/references/star-framework.md +165 -0
- package/skills/skills/humanize-docs/SKILL.md +203 -0
- package/skills/skills/humanize-docs/references/advanced-techniques.md +13 -0
- package/skills/skills/humanize-docs/references/core-transformations.md +368 -0
- package/skills/skills/humanize-docs/references/detection-patterns.md +400 -0
- package/skills/skills/humanize-docs/references/examples-gallery.md +374 -0
- package/skills/skills/imagine/SKILL.md +190 -0
- package/skills/skills/imagine/references/artstyle-corporate-memphis.md +625 -0
- package/skills/skills/imagine/references/artstyle-crewdson-hyperrealism.md +295 -0
- package/skills/skills/imagine/references/artstyle-iphone-social-media.md +426 -0
- package/skills/skills/imagine/references/artstyle-sciencesaru.md +276 -0
- package/skills/skills/pre-deploy-checklist/README.md +26 -0
- package/skills/skills/pre-deploy-checklist/SKILL.md +153 -0
- package/skills/skills/pre-deploy-checklist/references/checklist-categories.md +174 -0
- package/skills/skills/pre-deploy-checklist/references/domain-prompts.md +216 -0
- package/skills/skills/prompt-engineering/SKILL.md +209 -0
- package/skills/skills/prompt-engineering/references/advanced-combinations.md +444 -0
- package/skills/skills/prompt-engineering/references/chain-of-thought.md +140 -0
- package/skills/skills/prompt-engineering/references/decision_matrix.md +220 -0
- package/skills/skills/prompt-engineering/references/few-shot.md +346 -0
- package/skills/skills/prompt-engineering/references/json-format.md +270 -0
- package/skills/skills/prompt-engineering/references/natural-language.md +420 -0
- package/skills/skills/prompt-engineering/references/pitfalls.md +365 -0
- package/skills/skills/prompt-engineering/references/prompt-chaining.md +498 -0
- package/skills/skills/prompt-engineering/references/react.md +108 -0
- package/skills/skills/prompt-engineering/references/self-consistency.md +322 -0
- package/skills/skills/prompt-engineering/references/tree-of-thoughts.md +386 -0
- package/skills/skills/prompt-engineering/references/xml-format.md +220 -0
- package/skills/skills/prompt-engineering/references/yaml-format.md +488 -0
- package/skills/skills/prompt-engineering/references/zero-shot.md +74 -0
- package/skills/skills/quick-spec/SKILL.md +280 -0
- package/skills/skills/quick-spec/assets/tech-spec-template.md +74 -0
- package/skills/skills/quick-spec/references/step-01-understand.md +189 -0
- package/skills/skills/quick-spec/references/step-02-investigate.md +144 -0
- package/skills/skills/quick-spec/references/step-03-generate.md +128 -0
- package/skills/skills/quick-spec/references/step-04-review.md +173 -0
- package/skills/skills/quick-spec/tests/__pycache__/test_skill.cpython-314-pytest-9.0.2.pyc +0 -0
- package/skills/skills/quick-spec/tests/test_scenarios.md +83 -0
- package/skills/skills/quick-spec/tests/test_skill.py +136 -0
- package/skills/skills/readme-expert/SKILL.md +538 -0
- package/skills/skills/readme-expert/knowledge/INDEX.md +192 -0
- package/skills/skills/readme-expert/knowledge/application/quality-standards.md +470 -0
- package/skills/skills/readme-expert/knowledge/application/script-executor.md +604 -0
- package/skills/skills/readme-expert/knowledge/application/template-library.md +822 -0
- package/skills/skills/readme-expert/knowledge/foundation/codebase-scanner.md +361 -0
- package/skills/skills/readme-expert/knowledge/foundation/validation-checklist.md +481 -0
- package/skills/skills/red-teaming/SKILL.md +321 -0
- package/skills/skills/red-teaming/references/ai-llm-redteam.md +517 -0
- package/skills/skills/red-teaming/references/attack-techniques.md +410 -0
- package/skills/skills/red-teaming/references/cybersecurity-redteam.md +383 -0
- package/skills/skills/red-teaming/references/tools-frameworks.md +446 -0
- package/skills/skills/releasing/.skillkit-mode +1 -0
- package/skills/skills/releasing/SKILL.md +225 -0
- package/skills/skills/releasing/references/version-detection.md +108 -0
- package/skills/skills/screenwriter/SKILL.md +273 -0
- package/skills/skills/screenwriter/references/advanced-techniques.md +216 -0
- package/skills/skills/screenwriter/references/pipeline-integration.md +266 -0
- package/skills/skills/skillkit/.claude/settings.local.json +7 -0
- package/skills/skills/skillkit/.claude-plugin/plugin.json +27 -0
- package/skills/skills/skillkit/CHANGELOG.md +484 -0
- package/skills/skills/skillkit/SKILL.md +511 -0
- package/skills/skills/skillkit/commands/skillkit.md +6 -0
- package/skills/skills/skillkit/commands/validate-plan.md +6 -0
- package/skills/skills/skillkit/commands/verify.md +6 -0
- package/skills/skills/skillkit/knowledge/INDEX.md +352 -0
- package/skills/skills/skillkit/knowledge/application/09-case-studies.md +257 -0
- package/skills/skills/skillkit/knowledge/application/10-technical-architecture.md +324 -0
- package/skills/skills/skillkit/knowledge/application/11-adoption-strategy.md +267 -0
- package/skills/skills/skillkit/knowledge/application/12-testing-and-validation.md +276 -0
- package/skills/skills/skillkit/knowledge/application/13-competitive-landscape.md +198 -0
- package/skills/skills/skillkit/knowledge/foundation/01-why-skills-exist.md +246 -0
- package/skills/skills/skillkit/knowledge/foundation/02-skills-vs-subagents-comparison.md +312 -0
- package/skills/skills/skillkit/knowledge/foundation/03-skills-vs-subagents-decision-tree.md +346 -0
- package/skills/skills/skillkit/knowledge/foundation/04-hybrid-patterns.md +308 -0
- package/skills/skills/skillkit/knowledge/foundation/05-token-economics.md +275 -0
- package/skills/skills/skillkit/knowledge/foundation/06-platform-constraints.md +237 -0
- package/skills/skills/skillkit/knowledge/foundation/07-security-concerns.md +322 -0
- package/skills/skills/skillkit/knowledge/foundation/08-when-not-to-use-skills.md +270 -0
- package/skills/skills/skillkit/knowledge/plugin-guide.md +614 -0
- package/skills/skills/skillkit/knowledge/tools/14-validation-tools-guide.md +150 -0
- package/skills/skills/skillkit/knowledge/tools/15-cost-tools-guide.md +157 -0
- package/skills/skills/skillkit/knowledge/tools/16-security-tools-guide.md +122 -0
- package/skills/skills/skillkit/knowledge/tools/17-pattern-tools-guide.md +161 -0
- package/skills/skills/skillkit/knowledge/tools/18-decision-helper-guide.md +243 -0
- package/skills/skills/skillkit/knowledge/tools/19-test-generator-guide.md +275 -0
- package/skills/skills/skillkit/knowledge/tools/20-split-skill-guide.md +149 -0
- package/skills/skills/skillkit/knowledge/tools/21-quality-scorer-guide.md +226 -0
- package/skills/skills/skillkit/knowledge/tools/22-migration-helper-guide.md +356 -0
- package/skills/skills/skillkit/knowledge/tools/23-subagent-creation-guide.md +448 -0
- package/skills/skills/skillkit/knowledge/tools/24-behavioral-testing-guide.md +122 -0
- package/skills/skills/skillkit/references/proposal-generation.md +982 -0
- package/skills/skills/skillkit/references/rationalization-catalog.md +75 -0
- package/skills/skills/skillkit/references/research-methodology.md +661 -0
- package/skills/skills/skillkit/references/section-2-full-creation-workflow.md +452 -0
- package/skills/skills/skillkit/references/section-3-validation-workflow-existing-skill.md +63 -0
- package/skills/skills/skillkit/references/section-4-decision-workflow-skills-vs-subagents.md +64 -0
- package/skills/skills/skillkit/references/section-5-migration-workflow-doc-to-skill.md +58 -0
- package/skills/skills/skillkit/references/section-6-subagent-creation-workflow.md +499 -0
- package/skills/skills/skillkit/references/section-7-knowledge-reference-map.md +72 -0
- package/skills/skills/skillkit/scripts/__pycache__/decision_helper.cpython-314.pyc +0 -0
- package/skills/skills/skillkit/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
- package/skills/skills/skillkit/scripts/__pycache__/quick_validate.cpython-314.pyc +0 -0
- package/skills/skills/skillkit/scripts/__pycache__/test_generator.cpython-314-pytest-9.0.2.pyc +0 -0
- package/skills/skills/skillkit/scripts/decision_helper.py +799 -0
- package/skills/skills/skillkit/scripts/init_skill.py +400 -0
- package/skills/skills/skillkit/scripts/init_subagent.py +231 -0
- package/skills/skills/skillkit/scripts/migration_helper.py +669 -0
- package/skills/skills/skillkit/scripts/package_skill.py +211 -0
- package/skills/skills/skillkit/scripts/pattern_detector.py +381 -0
- package/skills/skills/skillkit/scripts/pattern_detector_new.py +382 -0
- package/skills/skills/skillkit/scripts/pressure_tester.py +157 -0
- package/skills/skills/skillkit/scripts/quality_scorer.py +999 -0
- package/skills/skills/skillkit/scripts/quick_validate.py +100 -0
- package/skills/skills/skillkit/scripts/security_scanner.py +474 -0
- package/skills/skills/skillkit/scripts/split_skill.py +540 -0
- package/skills/skills/skillkit/scripts/test_generator.py +695 -0
- package/skills/skills/skillkit/scripts/token_estimator.py +493 -0
- package/skills/skills/skillkit/scripts/utils/__init__.py +49 -0
- package/skills/skills/skillkit/scripts/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- package/skills/skills/skillkit/scripts/utils/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/skills/skillkit/scripts/utils/__pycache__/budget_tracker.cpython-312.pyc +0 -0
- package/skills/skills/skillkit/scripts/utils/__pycache__/budget_tracker.cpython-314.pyc +0 -0
- package/skills/skills/skillkit/scripts/utils/__pycache__/output_formatter.cpython-312.pyc +0 -0
- package/skills/skills/skillkit/scripts/utils/__pycache__/output_formatter.cpython-314.pyc +0 -0
- package/skills/skills/skillkit/scripts/utils/__pycache__/reference_validator.cpython-312.pyc +0 -0
- package/skills/skills/skillkit/scripts/utils/__pycache__/reference_validator.cpython-314.pyc +0 -0
- package/skills/skills/skillkit/scripts/utils/budget_tracker.py +388 -0
- package/skills/skills/skillkit/scripts/utils/output_formatter.py +263 -0
- package/skills/skills/skillkit/scripts/utils/reference_validator.py +401 -0
- package/skills/skills/skillkit/scripts/validate_skill.py +594 -0
- package/skills/skills/skillkit/tests/test_behavioral.py +39 -0
- package/skills/skills/skillkit/tests/test_scenarios.md +83 -0
- package/skills/skills/skillkit/tests/test_skill.py +136 -0
- package/skills/skills/skillkit-help/SKILL.md +81 -0
- package/skills/skills/skillkit-help/knowledge/application/09-case-studies.md +257 -0
- package/skills/skills/skillkit-help/knowledge/application/12-testing-and-validation.md +276 -0
- package/skills/skills/skillkit-help/knowledge/foundation/01-why-skills-exist.md +246 -0
- package/skills/skills/skillkit-help/knowledge/foundation/02-skills-vs-subagents-comparison.md +312 -0
- package/skills/skills/skillkit-help/knowledge/foundation/03-skills-vs-subagents-decision-tree.md +346 -0
- package/skills/skills/skillkit-help/knowledge/foundation/06-platform-constraints.md +237 -0
- package/skills/skills/skillkit-help/knowledge/foundation/08-when-not-to-use-skills.md +270 -0
- package/skills/skills/skillkit-help/template/SKILL.md +52 -0
- package/skills/skills/social-media-seo/SKILL.md +278 -0
- package/skills/skills/social-media-seo/databases/caption-styles.csv +31 -0
- package/skills/skills/social-media-seo/databases/engagement-tactics.csv +16 -0
- package/skills/skills/social-media-seo/databases/hashtag-strategies.csv +21 -0
- package/skills/skills/social-media-seo/databases/hook-formulas.csv +26 -0
- package/skills/skills/social-media-seo/databases/keyword-clusters.csv +11 -0
- package/skills/skills/social-media-seo/databases/thread-structures.csv +26 -0
- package/skills/skills/social-media-seo/databases/viral-patterns.csv +21 -0
- package/skills/skills/social-media-seo/references/analytics-guide.md +321 -0
- package/skills/skills/social-media-seo/references/instagram-seo.md +235 -0
- package/skills/skills/social-media-seo/references/threads-seo.md +305 -0
- package/skills/skills/social-media-seo/references/x-twitter-seo.md +337 -0
- package/skills/skills/social-media-seo/scripts/query_database.py +191 -0
- package/skills/skills/storyteller/SKILL.md +241 -0
- package/skills/skills/storyteller/references/transformation-methodology.md +293 -0
- package/skills/skills/storyteller/references/visual-vocabulary.md +177 -0
- package/skills/skills/thread-pro/SKILL.md +162 -0
- package/skills/skills/thread-pro/anti-ai-patterns.md +120 -0
- package/skills/skills/thread-pro/hook-formulas.md +138 -0
- package/skills/skills/thread-pro/references/anti-ai-patterns.md +120 -0
- package/skills/skills/thread-pro/references/hook-formulas.md +138 -0
- package/skills/skills/thread-pro/references/thread-structures.md +240 -0
- package/skills/skills/thread-pro/references/voice-injection.md +130 -0
- package/skills/skills/thread-pro/thread-structures.md +240 -0
- package/skills/skills/thread-pro/voice-injection.md +130 -0
- package/skills/skills/tinkering/SKILL.md +251 -0
- package/skills/skills/tinkering/references/graduation-checklist.md +100 -0
- package/skills/skills/validate-plan/.skillkit-mode +1 -0
- package/skills/skills/validate-plan/SKILL.md +406 -0
- package/skills/skills/validate-plan/references/dry-principles.md +251 -0
- package/skills/skills/validate-plan/references/gap-analysis-guide.md +320 -0
- package/skills/skills/validate-plan/references/tdd-patterns.md +413 -0
- package/skills/skills/validate-plan/references/yagni-checklist.md +330 -0
- package/skills/skills/verify-before-ship/.skillkit-mode +1 -0
- package/skills/skills/verify-before-ship/SKILL.md +116 -0
- package/skills/skills/verify-before-ship/references/anti-rationalization.md +212 -0
- package/skills/skills/verify-before-ship/references/verification-gates.md +305 -0
- package/skills-manifest.json +8 -2
- package/src/picker.js +11 -5
- package/src/picker.test.js +36 -1
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Auto-generate comprehensive test scenarios for skills.
|
|
4
|
+
Based on skill description and validation requirements.
|
|
5
|
+
|
|
6
|
+
v1.2 Update: Parameter standardization
|
|
7
|
+
- Renamed --format to --test-format (test framework choice)
|
|
8
|
+
- Renamed --output to --format (output style)
|
|
9
|
+
- Backward compatibility maintained for --output (deprecated)
|
|
10
|
+
|
|
11
|
+
v2 Update: Legacy mode deprecation
|
|
12
|
+
- Structural-only generation (without --behavioral) is deprecated
|
|
13
|
+
- Preferred: --behavioral with --test-format pytest
|
|
14
|
+
|
|
15
|
+
v2.1 Note: --behavioral generates test scaffolds only (no pressure_tester.py dependency).
|
|
16
|
+
Real behavioral validation uses the subagent dispatch protocol in section-2.
|
|
17
|
+
|
|
18
|
+
References: File 12 (testing best practices)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import re
|
|
22
|
+
import json
|
|
23
|
+
import argparse
|
|
24
|
+
import warnings
|
|
25
|
+
import sys
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import List, Dict, Optional
|
|
28
|
+
|
|
29
|
+
class TestGenerator:
|
|
30
|
+
"""Generate test scenarios from skill description."""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
skill_path: str,
|
|
35
|
+
coverage: str = 'standard',
|
|
36
|
+
test_format: str = 'pytest',
|
|
37
|
+
output_format: str = 'text',
|
|
38
|
+
behavioral: bool = False
|
|
39
|
+
):
|
|
40
|
+
"""
|
|
41
|
+
Initialize test generator.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
skill_path: Path to skill directory
|
|
45
|
+
coverage: 'basic', 'standard', or 'comprehensive'
|
|
46
|
+
test_format: 'pytest', 'unittest', or 'plain'
|
|
47
|
+
output_format: 'text' or 'json' (agent-layer)
|
|
48
|
+
|
|
49
|
+
References: File 12 (testing best practices)
|
|
50
|
+
"""
|
|
51
|
+
self.skill_path = Path(skill_path)
|
|
52
|
+
self.coverage = coverage
|
|
53
|
+
self.test_format = test_format
|
|
54
|
+
self.output_format = output_format
|
|
55
|
+
self.skill_md_content = None
|
|
56
|
+
self.behavioral = behavioral
|
|
57
|
+
self.skill_name = None
|
|
58
|
+
self.capabilities = []
|
|
59
|
+
self.test_scenarios = []
|
|
60
|
+
|
|
61
|
+
# ========== PARSING ==========
|
|
62
|
+
|
|
63
|
+
def parse_skill_description(self) -> Dict:
|
|
64
|
+
"""
|
|
65
|
+
Parse SKILL.md to extract testable capabilities.
|
|
66
|
+
|
|
67
|
+
Extracts:
|
|
68
|
+
- Skill name (from frontmatter)
|
|
69
|
+
- Main capabilities (from description)
|
|
70
|
+
- Trigger conditions (WHEN clauses)
|
|
71
|
+
- Example usages (if present)
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Dict with parsed information
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
FileNotFoundError: If SKILL.md doesn't exist
|
|
78
|
+
|
|
79
|
+
References: File 02 (description structure)
|
|
80
|
+
"""
|
|
81
|
+
skill_md = self.skill_path / "SKILL.md"
|
|
82
|
+
if not skill_md.exists():
|
|
83
|
+
raise FileNotFoundError(f"SKILL.md not found in {self.skill_path}")
|
|
84
|
+
|
|
85
|
+
with open(skill_md, encoding='utf-8') as f:
|
|
86
|
+
self.skill_md_content = f.read()
|
|
87
|
+
|
|
88
|
+
# Parse YAML frontmatter
|
|
89
|
+
frontmatter = self._extract_frontmatter()
|
|
90
|
+
self.skill_name = frontmatter.get('name', 'unknown')
|
|
91
|
+
|
|
92
|
+
# Parse capabilities
|
|
93
|
+
description = frontmatter.get('description', '')
|
|
94
|
+
self.capabilities = self._extract_capabilities(description, self.skill_md_content)
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
'name': self.skill_name,
|
|
98
|
+
'capabilities': self.capabilities,
|
|
99
|
+
'description': description
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
def _extract_frontmatter(self) -> Dict:
|
|
103
|
+
"""
|
|
104
|
+
Extract YAML frontmatter from SKILL.md.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Dict with frontmatter key-value pairs
|
|
108
|
+
"""
|
|
109
|
+
pattern = r'^---\n(.*?)\n---'
|
|
110
|
+
match = re.search(pattern, self.skill_md_content, re.DOTALL)
|
|
111
|
+
if not match:
|
|
112
|
+
return {}
|
|
113
|
+
|
|
114
|
+
# Simple YAML parsing (key: value)
|
|
115
|
+
frontmatter = {}
|
|
116
|
+
for line in match.group(1).split('\n'):
|
|
117
|
+
if ':' in line:
|
|
118
|
+
key, value = line.split(':', 1)
|
|
119
|
+
frontmatter[key.strip()] = value.strip().strip('"\'')
|
|
120
|
+
|
|
121
|
+
return frontmatter
|
|
122
|
+
|
|
123
|
+
def _extract_capabilities(self, description: str, content: str) -> List[str]:
|
|
124
|
+
"""
|
|
125
|
+
Extract individual capabilities from description and content.
|
|
126
|
+
|
|
127
|
+
Looks for:
|
|
128
|
+
- Bulleted capabilities in description
|
|
129
|
+
- WHEN/IF trigger clauses
|
|
130
|
+
- Section headers indicating features
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
description: Skill description from frontmatter
|
|
134
|
+
content: Full SKILL.md content
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
List of capability descriptions
|
|
138
|
+
"""
|
|
139
|
+
capabilities = []
|
|
140
|
+
|
|
141
|
+
# Extract from description bullets
|
|
142
|
+
for line in description.split('\n'):
|
|
143
|
+
if line.strip().startswith(('-', '*', '•')):
|
|
144
|
+
cap = line.strip().lstrip('-*• ')
|
|
145
|
+
if cap and len(cap) > 10: # Meaningful capability
|
|
146
|
+
capabilities.append(cap)
|
|
147
|
+
|
|
148
|
+
# Extract WHEN clauses
|
|
149
|
+
when_pattern = r'(?:when|if|for)\s+(.{20,100})'
|
|
150
|
+
for match in re.finditer(when_pattern, content, re.IGNORECASE):
|
|
151
|
+
cap = match.group(1).strip()
|
|
152
|
+
if cap not in capabilities:
|
|
153
|
+
capabilities.append(cap)
|
|
154
|
+
|
|
155
|
+
# If no capabilities found, use generic
|
|
156
|
+
if not capabilities:
|
|
157
|
+
capabilities = ["Basic functionality"]
|
|
158
|
+
|
|
159
|
+
return capabilities[:10] # Limit to 10 capabilities
|
|
160
|
+
|
|
161
|
+
# ========== TEST SCENARIO GENERATION ==========
|
|
162
|
+
|
|
163
|
+
def generate_test_scenarios(self) -> List[Dict]:
|
|
164
|
+
"""
|
|
165
|
+
Generate test scenarios based on coverage level.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
List of test scenario dicts with:
|
|
169
|
+
- description: What to test
|
|
170
|
+
- priority: P0 (critical), P1 (high), P2 (medium)
|
|
171
|
+
- category: functional, edge_case, error_handling
|
|
172
|
+
- expected_result: Expected outcome
|
|
173
|
+
|
|
174
|
+
References: File 12 (test prioritization)
|
|
175
|
+
"""
|
|
176
|
+
scenarios = []
|
|
177
|
+
|
|
178
|
+
# P0: Critical functional tests (always included)
|
|
179
|
+
scenarios.extend(self._generate_functional_tests())
|
|
180
|
+
|
|
181
|
+
if self.coverage in ['standard', 'comprehensive']:
|
|
182
|
+
# P1: Edge cases
|
|
183
|
+
scenarios.extend(self._generate_edge_case_tests())
|
|
184
|
+
|
|
185
|
+
if self.coverage == 'comprehensive':
|
|
186
|
+
# P2: Error handling and performance
|
|
187
|
+
scenarios.extend(self._generate_error_tests())
|
|
188
|
+
scenarios.extend(self._generate_performance_tests())
|
|
189
|
+
|
|
190
|
+
self.test_scenarios = scenarios
|
|
191
|
+
return scenarios
|
|
192
|
+
|
|
193
|
+
def _generate_functional_tests(self) -> List[Dict]:
|
|
194
|
+
"""Generate P0 critical functional tests."""
|
|
195
|
+
tests = []
|
|
196
|
+
|
|
197
|
+
# One functional test per capability
|
|
198
|
+
for cap in self.capabilities:
|
|
199
|
+
tests.append({
|
|
200
|
+
'description': f"Test {cap}",
|
|
201
|
+
'priority': 'P0',
|
|
202
|
+
'category': 'functional',
|
|
203
|
+
'expected_result': f"Skill successfully handles: {cap}",
|
|
204
|
+
'test_data': 'valid_input'
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
# Always test basic invocation
|
|
208
|
+
if not any('invocation' in t['description'].lower() for t in tests):
|
|
209
|
+
tests.insert(0, {
|
|
210
|
+
'description': "Test basic skill invocation",
|
|
211
|
+
'priority': 'P0',
|
|
212
|
+
'category': 'functional',
|
|
213
|
+
'expected_result': "Skill loads and responds to trigger",
|
|
214
|
+
'test_data': 'minimal_valid'
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
return tests
|
|
218
|
+
|
|
219
|
+
def _generate_edge_case_tests(self) -> List[Dict]:
|
|
220
|
+
"""Generate P1 edge case tests."""
|
|
221
|
+
return [
|
|
222
|
+
{
|
|
223
|
+
'description': "Test with minimal input",
|
|
224
|
+
'priority': 'P1',
|
|
225
|
+
'category': 'edge_case',
|
|
226
|
+
'expected_result': "Graceful handling of minimal valid input",
|
|
227
|
+
'test_data': 'minimal'
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
'description': "Test with maximum/complex input",
|
|
231
|
+
'priority': 'P1',
|
|
232
|
+
'category': 'edge_case',
|
|
233
|
+
'expected_result': "Proper handling of complex scenarios",
|
|
234
|
+
'test_data': 'complex'
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
def _generate_error_tests(self) -> List[Dict]:
|
|
239
|
+
"""Generate P2 error handling tests."""
|
|
240
|
+
return [
|
|
241
|
+
{
|
|
242
|
+
'description': "Test invalid input handling",
|
|
243
|
+
'priority': 'P2',
|
|
244
|
+
'category': 'error_handling',
|
|
245
|
+
'expected_result': "Clear error message, no crash",
|
|
246
|
+
'test_data': 'invalid'
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
'description': "Test missing data handling",
|
|
250
|
+
'priority': 'P2',
|
|
251
|
+
'category': 'error_handling',
|
|
252
|
+
'expected_result': "Appropriate fallback behavior",
|
|
253
|
+
'test_data': 'missing'
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
def _generate_performance_tests(self) -> List[Dict]:
|
|
258
|
+
"""Generate P2 performance tests."""
|
|
259
|
+
return [
|
|
260
|
+
{
|
|
261
|
+
'description': "Test response time",
|
|
262
|
+
'priority': 'P2',
|
|
263
|
+
'category': 'performance',
|
|
264
|
+
'expected_result': "Response within acceptable time (<2s typical)",
|
|
265
|
+
'test_data': 'typical'
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
|
|
269
|
+
# ========== OUTPUT GENERATION ==========
|
|
270
|
+
|
|
271
|
+
def generate_test_documentation(self, output_path: Path):
|
|
272
|
+
"""
|
|
273
|
+
Generate human-readable test documentation.
|
|
274
|
+
|
|
275
|
+
Creates a markdown file with:
|
|
276
|
+
- Test overview
|
|
277
|
+
- Scenarios by priority
|
|
278
|
+
- Setup instructions
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
output_path: Path to write documentation
|
|
282
|
+
"""
|
|
283
|
+
lines = []
|
|
284
|
+
lines.append(f"# Test Scenarios: {self.skill_name}\n")
|
|
285
|
+
lines.append(f"**Generated:** Auto-generated from SKILL.md")
|
|
286
|
+
lines.append(f"**Coverage:** {self.coverage}\n")
|
|
287
|
+
|
|
288
|
+
# Group by priority
|
|
289
|
+
for priority in ['P0', 'P1', 'P2']:
|
|
290
|
+
priority_scenarios = [s for s in self.test_scenarios if s['priority'] == priority]
|
|
291
|
+
if not priority_scenarios:
|
|
292
|
+
continue
|
|
293
|
+
|
|
294
|
+
lines.append(f"\n## {priority} Tests ({len(priority_scenarios)})\n")
|
|
295
|
+
for scenario in priority_scenarios:
|
|
296
|
+
lines.append(f"### {scenario['description']}")
|
|
297
|
+
lines.append(f"- **Category:** {scenario['category']}")
|
|
298
|
+
lines.append(f"- **Expected:** {scenario['expected_result']}")
|
|
299
|
+
lines.append(f"- **Test Data:** {scenario['test_data']}\n")
|
|
300
|
+
|
|
301
|
+
# Setup instructions
|
|
302
|
+
lines.append("\n## Setup\n")
|
|
303
|
+
lines.append("1. Install dependencies: `pip install pytest` (or unittest)")
|
|
304
|
+
lines.append("2. Review test scenarios above")
|
|
305
|
+
lines.append("3. Implement test logic in test files")
|
|
306
|
+
lines.append("4. Run tests: `pytest tests/`\n")
|
|
307
|
+
|
|
308
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
309
|
+
f.write('\n'.join(lines))
|
|
310
|
+
|
|
311
|
+
def generate_pytest_implementation(self, output_path: Path):
|
|
312
|
+
"""Generate pytest test implementation."""
|
|
313
|
+
lines = []
|
|
314
|
+
lines.append("\"\"\"")
|
|
315
|
+
lines.append(f"Pytest tests for {self.skill_name}")
|
|
316
|
+
lines.append("Auto-generated - customize as needed")
|
|
317
|
+
lines.append("\"\"\"")
|
|
318
|
+
lines.append("\nimport pytest\n")
|
|
319
|
+
|
|
320
|
+
# Generate test functions
|
|
321
|
+
for scenario in self.test_scenarios:
|
|
322
|
+
test_name = self._sanitize_test_name(scenario['description'])
|
|
323
|
+
lines.append(f"def test_{test_name}():")
|
|
324
|
+
lines.append(f" \"\"\"")
|
|
325
|
+
lines.append(f" {scenario['description']}")
|
|
326
|
+
lines.append(f" Priority: {scenario['priority']}")
|
|
327
|
+
lines.append(f" Expected: {scenario['expected_result']}")
|
|
328
|
+
lines.append(f" \"\"\"")
|
|
329
|
+
lines.append(f" # TODO: Implement test logic")
|
|
330
|
+
lines.append(f" # Test data: {scenario['test_data']}")
|
|
331
|
+
lines.append(f" assert True, 'Test not implemented yet'\n")
|
|
332
|
+
|
|
333
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
334
|
+
f.write('\n'.join(lines))
|
|
335
|
+
|
|
336
|
+
def generate_unittest_implementation(self, output_path: Path):
|
|
337
|
+
"""Generate unittest test implementation."""
|
|
338
|
+
lines = []
|
|
339
|
+
lines.append("\"\"\"")
|
|
340
|
+
lines.append(f"Unittest tests for {self.skill_name}")
|
|
341
|
+
lines.append("Auto-generated - customize as needed")
|
|
342
|
+
lines.append("\"\"\"")
|
|
343
|
+
lines.append("\nimport unittest\n")
|
|
344
|
+
|
|
345
|
+
lines.append(f"class Test{self.skill_name.replace('-', '_').title()}(unittest.TestCase):")
|
|
346
|
+
lines.append(f" \"\"\"Test suite for {self.skill_name}.\"\"\"")
|
|
347
|
+
lines.append("")
|
|
348
|
+
|
|
349
|
+
# Generate test methods
|
|
350
|
+
for scenario in self.test_scenarios:
|
|
351
|
+
test_name = self._sanitize_test_name(scenario['description'])
|
|
352
|
+
lines.append(f" def test_{test_name}(self):")
|
|
353
|
+
lines.append(f" \"\"\"")
|
|
354
|
+
lines.append(f" {scenario['description']}")
|
|
355
|
+
lines.append(f" Priority: {scenario['priority']}")
|
|
356
|
+
lines.append(f" Expected: {scenario['expected_result']}")
|
|
357
|
+
lines.append(f" \"\"\"")
|
|
358
|
+
lines.append(f" # TODO: Implement test logic")
|
|
359
|
+
lines.append(f" # Test data: {scenario['test_data']}")
|
|
360
|
+
lines.append(f" self.assertTrue(True, 'Test not implemented yet')\n")
|
|
361
|
+
|
|
362
|
+
lines.append("\nif __name__ == '__main__':")
|
|
363
|
+
lines.append(" unittest.main()")
|
|
364
|
+
|
|
365
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
366
|
+
f.write('\n'.join(lines))
|
|
367
|
+
|
|
368
|
+
def generate_plain_documentation(self, output_path: Path):
|
|
369
|
+
"""Generate plain text test plan."""
|
|
370
|
+
lines = []
|
|
371
|
+
lines.append(f"TEST PLAN: {self.skill_name}")
|
|
372
|
+
lines.append("=" * 60)
|
|
373
|
+
lines.append(f"Coverage: {self.coverage}")
|
|
374
|
+
lines.append(f"Total scenarios: {len(self.test_scenarios)}\n")
|
|
375
|
+
|
|
376
|
+
for i, scenario in enumerate(self.test_scenarios, 1):
|
|
377
|
+
lines.append(f"\n{i}. {scenario['description']}")
|
|
378
|
+
lines.append(f" Priority: {scenario['priority']}")
|
|
379
|
+
lines.append(f" Category: {scenario['category']}")
|
|
380
|
+
lines.append(f" Expected: {scenario['expected_result']}")
|
|
381
|
+
lines.append(f" Test Data: {scenario['test_data']}")
|
|
382
|
+
|
|
383
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
384
|
+
f.write('\n'.join(lines))
|
|
385
|
+
|
|
386
|
+
def generate_behavioral_tests(self, skill_path: str, skill_type: str) -> str:
|
|
387
|
+
"""
|
|
388
|
+
Generate pressure-test scenarios for a skill.
|
|
389
|
+
|
|
390
|
+
Returns pytest-compatible test template content.
|
|
391
|
+
Behavioral templates are only emitted for pytest mode.
|
|
392
|
+
"""
|
|
393
|
+
_ = skill_type
|
|
394
|
+
if self.test_format != 'pytest':
|
|
395
|
+
return ""
|
|
396
|
+
|
|
397
|
+
template = '''"""
|
|
398
|
+
Behavioral tests for {skill_name}
|
|
399
|
+
Generated by test_generator.py --behavioral
|
|
400
|
+
"""
|
|
401
|
+
|
|
402
|
+
import pytest
|
|
403
|
+
|
|
404
|
+
# RED Phase: Baseline tests (run WITHOUT skill)
|
|
405
|
+
class TestBaselineBehavior:
|
|
406
|
+
"""Document how agents behave without skill."""
|
|
407
|
+
|
|
408
|
+
def test_time_pressure_rationalization(self):
|
|
409
|
+
"""Under time pressure, agents typically..."""
|
|
410
|
+
# Document expected failure
|
|
411
|
+
pass
|
|
412
|
+
|
|
413
|
+
def test_sunk_cost_rationalization(self):
|
|
414
|
+
"""With sunk cost, agents typically..."""
|
|
415
|
+
pass
|
|
416
|
+
|
|
417
|
+
# GREEN Phase: Verification tests (run WITH skill)
|
|
418
|
+
class TestSkillCompliance:
|
|
419
|
+
"""Verify agents comply WITH skill."""
|
|
420
|
+
|
|
421
|
+
def test_resists_time_pressure(self):
|
|
422
|
+
"""Skill helps agent resist time pressure."""
|
|
423
|
+
pass
|
|
424
|
+
|
|
425
|
+
def test_resists_sunk_cost(self):
|
|
426
|
+
"""Skill helps agent resist sunk cost fallacy."""
|
|
427
|
+
pass
|
|
428
|
+
|
|
429
|
+
# REFACTOR Phase: Combined pressure
|
|
430
|
+
class TestCombinedPressure:
|
|
431
|
+
"""All pressures at once."""
|
|
432
|
+
|
|
433
|
+
def test_combined_pressure_compliance(self):
|
|
434
|
+
"""Agent complies under maximum pressure."""
|
|
435
|
+
pass
|
|
436
|
+
'''
|
|
437
|
+
skill_name = Path(skill_path).name
|
|
438
|
+
return template.format(skill_name=skill_name, skill_type=skill_type)
|
|
439
|
+
|
|
440
|
+
def _sanitize_test_name(self, description: str) -> str:
|
|
441
|
+
"""
|
|
442
|
+
Convert description to valid Python function name.
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
description: Test description
|
|
446
|
+
|
|
447
|
+
Returns:
|
|
448
|
+
Sanitized function name
|
|
449
|
+
"""
|
|
450
|
+
# Remove non-alphanumeric, convert spaces to underscores
|
|
451
|
+
name = re.sub(r'[^a-zA-Z0-9\s]', '', description)
|
|
452
|
+
name = name.lower().replace(' ', '_')
|
|
453
|
+
# Limit length and remove extra underscores
|
|
454
|
+
name = re.sub(r'_+', '_', name)
|
|
455
|
+
return name[:50].strip('_')
|
|
456
|
+
|
|
457
|
+
# ========== MAIN EXECUTION ==========
|
|
458
|
+
|
|
459
|
+
def generate(self):
|
|
460
|
+
"""
|
|
461
|
+
Main generation workflow.
|
|
462
|
+
|
|
463
|
+
1. Parse skill description
|
|
464
|
+
2. Generate test scenarios
|
|
465
|
+
3. Write test documentation
|
|
466
|
+
4. Write test implementation
|
|
467
|
+
|
|
468
|
+
References: File 12 (testing workflow)
|
|
469
|
+
"""
|
|
470
|
+
# Parse
|
|
471
|
+
skill_info = self.parse_skill_description()
|
|
472
|
+
|
|
473
|
+
# Generate scenarios
|
|
474
|
+
scenarios = self.generate_test_scenarios()
|
|
475
|
+
|
|
476
|
+
# Calculate stats
|
|
477
|
+
behavioral_generated = self.behavioral and self.test_format == 'pytest'
|
|
478
|
+
|
|
479
|
+
stats = {
|
|
480
|
+
'skill_name': skill_info['name'],
|
|
481
|
+
'capabilities_found': len(skill_info['capabilities']),
|
|
482
|
+
'total_scenarios': len(scenarios),
|
|
483
|
+
'by_priority': {
|
|
484
|
+
'P0': sum(1 for s in scenarios if s['priority'] == 'P0'),
|
|
485
|
+
'P1': sum(1 for s in scenarios if s['priority'] == 'P1'),
|
|
486
|
+
'P2': sum(1 for s in scenarios if s['priority'] == 'P2')
|
|
487
|
+
},
|
|
488
|
+
'coverage': self.coverage,
|
|
489
|
+
'test_format': self.test_format,
|
|
490
|
+
'behavioral': self.behavioral,
|
|
491
|
+
'behavioral_generated': behavioral_generated
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
# Create tests/ directory
|
|
495
|
+
tests_dir = self.skill_path / "tests"
|
|
496
|
+
tests_dir.mkdir(exist_ok=True)
|
|
497
|
+
|
|
498
|
+
# Write outputs
|
|
499
|
+
self.generate_test_documentation(tests_dir / "test_scenarios.md")
|
|
500
|
+
|
|
501
|
+
if self.test_format == 'pytest':
|
|
502
|
+
self.generate_pytest_implementation(tests_dir / "test_skill.py")
|
|
503
|
+
stats['output_files'] = [
|
|
504
|
+
str(tests_dir / 'test_scenarios.md'),
|
|
505
|
+
str(tests_dir / 'test_skill.py')
|
|
506
|
+
]
|
|
507
|
+
elif self.test_format == 'unittest':
|
|
508
|
+
self.generate_unittest_implementation(tests_dir / "test_skill.py")
|
|
509
|
+
stats['output_files'] = [
|
|
510
|
+
str(tests_dir / 'test_scenarios.md'),
|
|
511
|
+
str(tests_dir / 'test_skill.py')
|
|
512
|
+
]
|
|
513
|
+
else: # plain
|
|
514
|
+
self.generate_plain_documentation(tests_dir / "test_plan.txt")
|
|
515
|
+
stats['output_files'] = [
|
|
516
|
+
str(tests_dir / 'test_scenarios.md'),
|
|
517
|
+
str(tests_dir / 'test_plan.txt')
|
|
518
|
+
]
|
|
519
|
+
|
|
520
|
+
if behavioral_generated:
|
|
521
|
+
behavioral_file = tests_dir / "test_behavioral.py"
|
|
522
|
+
behavioral_content = self.generate_behavioral_tests(
|
|
523
|
+
str(self.skill_path),
|
|
524
|
+
"discipline"
|
|
525
|
+
)
|
|
526
|
+
with open(behavioral_file, 'w', encoding='utf-8') as f:
|
|
527
|
+
f.write(behavioral_content)
|
|
528
|
+
stats['output_files'].append(str(behavioral_file))
|
|
529
|
+
|
|
530
|
+
# Output based on format
|
|
531
|
+
if self.output_format == 'json':
|
|
532
|
+
self._output_json(stats, scenarios)
|
|
533
|
+
else:
|
|
534
|
+
self._output_text(stats, tests_dir)
|
|
535
|
+
|
|
536
|
+
return stats
|
|
537
|
+
|
|
538
|
+
def _output_json(self, stats: Dict, scenarios: List[Dict]):
|
|
539
|
+
"""Output in JSON format for agent-layer."""
|
|
540
|
+
output = {
|
|
541
|
+
'status': 'success',
|
|
542
|
+
'skill_name': stats['skill_name'],
|
|
543
|
+
'capabilities_found': stats['capabilities_found'],
|
|
544
|
+
'test_scenarios': {
|
|
545
|
+
'total': stats['total_scenarios'],
|
|
546
|
+
'by_priority': stats['by_priority'],
|
|
547
|
+
'scenarios': scenarios
|
|
548
|
+
},
|
|
549
|
+
'configuration': {
|
|
550
|
+
'coverage': stats['coverage'],
|
|
551
|
+
'test_format': stats['test_format'],
|
|
552
|
+
'behavioral': stats['behavioral'],
|
|
553
|
+
'behavioral_generated': stats['behavioral_generated']
|
|
554
|
+
},
|
|
555
|
+
'output_files': stats['output_files'],
|
|
556
|
+
'next_steps': [
|
|
557
|
+
'Review test scenarios in test_scenarios.md',
|
|
558
|
+
'Implement test logic in test files',
|
|
559
|
+
f"Run: pytest {self.skill_path / 'tests'}" if self.test_format == 'pytest' else f"Run: python -m unittest discover {self.skill_path / 'tests'}"
|
|
560
|
+
]
|
|
561
|
+
}
|
|
562
|
+
print(json.dumps(output, indent=2))
|
|
563
|
+
|
|
564
|
+
def _output_text(self, stats: Dict, tests_dir: Path):
|
|
565
|
+
"""Output in human-readable text format."""
|
|
566
|
+
print(f"Generating tests for {self.skill_path}...")
|
|
567
|
+
print(f"Skill: {stats['skill_name']}")
|
|
568
|
+
print(f"Capabilities found: {stats['capabilities_found']}")
|
|
569
|
+
print(f"Test scenarios generated: {stats['total_scenarios']}")
|
|
570
|
+
print(f" - P0 (Critical): {stats['by_priority']['P0']}")
|
|
571
|
+
print(f" - P1 (High): {stats['by_priority']['P1']}")
|
|
572
|
+
print(f" - P2 (Medium): {stats['by_priority']['P2']}")
|
|
573
|
+
|
|
574
|
+
print(f"\n✔ Test documentation: {tests_dir / 'test_scenarios.md'}")
|
|
575
|
+
|
|
576
|
+
if self.test_format == 'pytest':
|
|
577
|
+
print(f"✔ Pytest implementation: {tests_dir / 'test_skill.py'}")
|
|
578
|
+
elif self.test_format == 'unittest':
|
|
579
|
+
print(f"✔ Unittest implementation: {tests_dir / 'test_skill.py'}")
|
|
580
|
+
else:
|
|
581
|
+
print(f"✔ Plain test plan: {tests_dir / 'test_plan.txt'}")
|
|
582
|
+
if stats['behavioral_generated']:
|
|
583
|
+
print(f"✔ Behavioral template: {tests_dir / 'test_behavioral.py'}")
|
|
584
|
+
elif stats['behavioral']:
|
|
585
|
+
print("! Behavioral template skipped (only supported with --test-format pytest)")
|
|
586
|
+
|
|
587
|
+
print(f"\nNext steps:")
|
|
588
|
+
print(f" 1. Review test scenarios in test_scenarios.md")
|
|
589
|
+
print(f" 2. Implement test logic in test files")
|
|
590
|
+
if self.test_format == 'pytest':
|
|
591
|
+
print(f" 3. Run: pytest {tests_dir}")
|
|
592
|
+
elif self.test_format == 'unittest':
|
|
593
|
+
print(f" 3. Run: python -m unittest discover {tests_dir}")
|
|
594
|
+
print(f"\n✔ Tests generated successfully!")
|
|
595
|
+
|
|
596
|
+
def main():
|
|
597
|
+
"""CLI entry point with v1.2 parameter standardization."""
|
|
598
|
+
parser = argparse.ArgumentParser(
|
|
599
|
+
description="Auto-generate test scenarios for skills",
|
|
600
|
+
epilog="References: File 12 for testing best practices | v1.2: Standardized parameters"
|
|
601
|
+
)
|
|
602
|
+
parser.add_argument(
|
|
603
|
+
'skill_path',
|
|
604
|
+
type=str,
|
|
605
|
+
help='Path to skill directory'
|
|
606
|
+
)
|
|
607
|
+
parser.add_argument(
|
|
608
|
+
'--coverage',
|
|
609
|
+
choices=['basic', 'standard', 'comprehensive'],
|
|
610
|
+
default='standard',
|
|
611
|
+
help='Test coverage level (default: standard)'
|
|
612
|
+
)
|
|
613
|
+
# v1.2: Renamed from --format (test framework choice)
|
|
614
|
+
parser.add_argument(
|
|
615
|
+
'--test-format',
|
|
616
|
+
choices=['pytest', 'unittest', 'plain'],
|
|
617
|
+
default='pytest',
|
|
618
|
+
help='Test framework format (default: pytest)'
|
|
619
|
+
)
|
|
620
|
+
# v1.2: Renamed from --output (standardized to --format for all tools)
|
|
621
|
+
parser.add_argument(
|
|
622
|
+
'--format',
|
|
623
|
+
choices=['text', 'json'],
|
|
624
|
+
default='text',
|
|
625
|
+
help='Output format: text (human-readable) or json (agent-layer)'
|
|
626
|
+
)
|
|
627
|
+
# v1.2: Backward compatibility for deprecated --output parameter
|
|
628
|
+
parser.add_argument(
|
|
629
|
+
'--output',
|
|
630
|
+
choices=['text', 'json'],
|
|
631
|
+
default=None,
|
|
632
|
+
help='[DEPRECATED v1.2] Use --format instead. This parameter will be removed in v2.0.'
|
|
633
|
+
)
|
|
634
|
+
parser.add_argument(
|
|
635
|
+
'--behavioral',
|
|
636
|
+
action='store_true',
|
|
637
|
+
help='Generate behavioral test scaffolds (v2.1: scaffolds only, real testing uses subagent protocol)'
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
args = parser.parse_args()
|
|
641
|
+
|
|
642
|
+
# v1.2: Handle backward compatibility for --output parameter
|
|
643
|
+
if args.output is not None:
|
|
644
|
+
warnings.warn(
|
|
645
|
+
"Parameter '--output' is deprecated (v1.2). Use '--format' instead. "
|
|
646
|
+
"The '--output' parameter will be removed in v2.0.0.",
|
|
647
|
+
DeprecationWarning,
|
|
648
|
+
stacklevel=2
|
|
649
|
+
)
|
|
650
|
+
output_format = args.output
|
|
651
|
+
else:
|
|
652
|
+
output_format = args.format
|
|
653
|
+
|
|
654
|
+
# v2 deprecation: legacy structural-only mode
|
|
655
|
+
if not args.behavioral:
|
|
656
|
+
warnings.warn(
|
|
657
|
+
"Legacy structural-only test generation mode is deprecated in v2. "
|
|
658
|
+
"Use '--behavioral --test-format pytest' for v2 workflow.",
|
|
659
|
+
DeprecationWarning,
|
|
660
|
+
stacklevel=2,
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
try:
|
|
664
|
+
generator = TestGenerator(
|
|
665
|
+
args.skill_path,
|
|
666
|
+
coverage=args.coverage,
|
|
667
|
+
test_format=args.test_format,
|
|
668
|
+
output_format=output_format,
|
|
669
|
+
behavioral=args.behavioral
|
|
670
|
+
)
|
|
671
|
+
generator.generate()
|
|
672
|
+
return 0
|
|
673
|
+
except FileNotFoundError as e:
|
|
674
|
+
if output_format == 'json':
|
|
675
|
+
print(json.dumps({
|
|
676
|
+
'status': 'error',
|
|
677
|
+
'error_type': 'FileNotFoundError',
|
|
678
|
+
'message': str(e)
|
|
679
|
+
}))
|
|
680
|
+
else:
|
|
681
|
+
print(f"✗ Error: {e}")
|
|
682
|
+
return 1
|
|
683
|
+
except Exception as e:
|
|
684
|
+
if output_format == 'json':
|
|
685
|
+
print(json.dumps({
|
|
686
|
+
'status': 'error',
|
|
687
|
+
'error_type': type(e).__name__,
|
|
688
|
+
'message': str(e)
|
|
689
|
+
}))
|
|
690
|
+
else:
|
|
691
|
+
print(f"✗ Unexpected error: {e}")
|
|
692
|
+
return 2
|
|
693
|
+
|
|
694
|
+
if __name__ == "__main__":
|
|
695
|
+
exit(main())
|