@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.
Files changed (247) hide show
  1. package/agents/agents/creative-copywriter.md +212 -0
  2. package/agents/agents/dario-amodei.md +135 -0
  3. package/agents/agents/doc-simplifier.md +63 -0
  4. package/agents/agents/kotlin-pro.md +433 -0
  5. package/agents/agents/red-team.md +136 -0
  6. package/agents/agents/sam-altman.md +121 -0
  7. package/agents/agents/seo-manager.md +184 -0
  8. package/package.json +1 -1
  9. package/skills/skillkit-help/SKILL.md +81 -0
  10. package/skills/skillkit-help/knowledge/application/09-case-studies.md +257 -0
  11. package/skills/skillkit-help/knowledge/application/12-testing-and-validation.md +276 -0
  12. package/skills/skillkit-help/knowledge/foundation/01-why-skills-exist.md +246 -0
  13. package/skills/skillkit-help/knowledge/foundation/02-skills-vs-subagents-comparison.md +312 -0
  14. package/skills/skillkit-help/knowledge/foundation/03-skills-vs-subagents-decision-tree.md +346 -0
  15. package/skills/skillkit-help/knowledge/foundation/06-platform-constraints.md +237 -0
  16. package/skills/skillkit-help/knowledge/foundation/08-when-not-to-use-skills.md +270 -0
  17. package/skills/skillkit-help/template/SKILL.md +52 -0
  18. package/skills/skills/adversarial-review/SKILL.md +219 -0
  19. package/skills/skills/baby-education/SKILL.md +260 -0
  20. package/skills/skills/baby-education/references/advanced-techniques.md +323 -0
  21. package/skills/skills/baby-education/references/transformations.md +345 -0
  22. package/skills/skills/been-there-done-that/SKILL.md +455 -0
  23. package/skills/skills/been-there-done-that/references/analysis-patterns.md +162 -0
  24. package/skills/skills/been-there-done-that/references/git-commands.md +132 -0
  25. package/skills/skills/been-there-done-that/references/tree-insertion-logic.md +145 -0
  26. package/skills/skills/coolhunter/SKILL.md +270 -0
  27. package/skills/skills/coolhunter/assets/elicitation-methods.csv +51 -0
  28. package/skills/skills/coolhunter/knowledge/elicitation-methods.md +312 -0
  29. package/skills/skills/coolhunter/references/workflow-execution.md +238 -0
  30. package/skills/skills/coolhunter/workflow-plan-coolhunter.md +232 -0
  31. package/skills/skills/creative-copywriting/SKILL.md +324 -0
  32. package/skills/skills/creative-copywriting/databases/README.md +60 -0
  33. package/skills/skills/creative-copywriting/databases/carousel-structures.csv +16 -0
  34. package/skills/skills/creative-copywriting/databases/emotional-arcs.csv +11 -0
  35. package/skills/skills/creative-copywriting/databases/hook-formulas.csv +51 -0
  36. package/skills/skills/creative-copywriting/databases/power-words.csv +201 -0
  37. package/skills/skills/creative-copywriting/databases/psychological-triggers.csv +21 -0
  38. package/skills/skills/creative-copywriting/databases/read-more-patterns.csv +26 -0
  39. package/skills/skills/creative-copywriting/databases/swipe-triggers.csv +31 -0
  40. package/skills/skills/creative-copywriting/references/carousel-psychology.md +223 -0
  41. package/skills/skills/creative-copywriting/references/hook-anatomy.md +169 -0
  42. package/skills/skills/creative-copywriting/references/power-word-science.md +134 -0
  43. package/skills/skills/creative-copywriting/references/storytelling-frameworks.md +157 -0
  44. package/skills/skills/diverse-content-gen/SKILL.md +201 -0
  45. package/skills/skills/diverse-content-gen/references/advanced-techniques.md +320 -0
  46. package/skills/skills/diverse-content-gen/references/research-findings.md +379 -0
  47. package/skills/skills/diverse-content-gen/references/task-workflows.md +241 -0
  48. package/skills/skills/diverse-content-gen/references/tool-integration.md +419 -0
  49. package/skills/skills/diverse-content-gen/references/troubleshooting.md +426 -0
  50. package/skills/skills/diverse-content-gen/references/vs-core-technique.md +240 -0
  51. package/skills/skills/framework-critical-thinking/SKILL.md +220 -0
  52. package/skills/skills/framework-critical-thinking/references/bias_detector.md +375 -0
  53. package/skills/skills/framework-critical-thinking/references/fallback_handler.md +239 -0
  54. package/skills/skills/framework-critical-thinking/references/memory_curator.md +161 -0
  55. package/skills/skills/framework-critical-thinking/references/metacognitive_monitor.md +297 -0
  56. package/skills/skills/framework-critical-thinking/references/producer_critic_orchestrator.md +333 -0
  57. package/skills/skills/framework-critical-thinking/references/reasoning_router.md +235 -0
  58. package/skills/skills/framework-critical-thinking/references/reasoning_validator.md +97 -0
  59. package/skills/skills/framework-critical-thinking/references/reflection_trigger.md +78 -0
  60. package/skills/skills/framework-critical-thinking/references/self_verification.md +388 -0
  61. package/skills/skills/framework-critical-thinking/references/uncertainty_quantifier.md +207 -0
  62. package/skills/skills/framework-initiative/SKILL.md +231 -0
  63. package/skills/skills/framework-initiative/references/examples.md +150 -0
  64. package/skills/skills/framework-initiative/references/impact-analysis.md +157 -0
  65. package/skills/skills/framework-initiative/references/intent-patterns.md +145 -0
  66. package/skills/skills/framework-initiative/references/star-framework.md +165 -0
  67. package/skills/skills/humanize-docs/SKILL.md +203 -0
  68. package/skills/skills/humanize-docs/references/advanced-techniques.md +13 -0
  69. package/skills/skills/humanize-docs/references/core-transformations.md +368 -0
  70. package/skills/skills/humanize-docs/references/detection-patterns.md +400 -0
  71. package/skills/skills/humanize-docs/references/examples-gallery.md +374 -0
  72. package/skills/skills/imagine/SKILL.md +190 -0
  73. package/skills/skills/imagine/references/artstyle-corporate-memphis.md +625 -0
  74. package/skills/skills/imagine/references/artstyle-crewdson-hyperrealism.md +295 -0
  75. package/skills/skills/imagine/references/artstyle-iphone-social-media.md +426 -0
  76. package/skills/skills/imagine/references/artstyle-sciencesaru.md +276 -0
  77. package/skills/skills/pre-deploy-checklist/README.md +26 -0
  78. package/skills/skills/pre-deploy-checklist/SKILL.md +153 -0
  79. package/skills/skills/pre-deploy-checklist/references/checklist-categories.md +174 -0
  80. package/skills/skills/pre-deploy-checklist/references/domain-prompts.md +216 -0
  81. package/skills/skills/prompt-engineering/SKILL.md +209 -0
  82. package/skills/skills/prompt-engineering/references/advanced-combinations.md +444 -0
  83. package/skills/skills/prompt-engineering/references/chain-of-thought.md +140 -0
  84. package/skills/skills/prompt-engineering/references/decision_matrix.md +220 -0
  85. package/skills/skills/prompt-engineering/references/few-shot.md +346 -0
  86. package/skills/skills/prompt-engineering/references/json-format.md +270 -0
  87. package/skills/skills/prompt-engineering/references/natural-language.md +420 -0
  88. package/skills/skills/prompt-engineering/references/pitfalls.md +365 -0
  89. package/skills/skills/prompt-engineering/references/prompt-chaining.md +498 -0
  90. package/skills/skills/prompt-engineering/references/react.md +108 -0
  91. package/skills/skills/prompt-engineering/references/self-consistency.md +322 -0
  92. package/skills/skills/prompt-engineering/references/tree-of-thoughts.md +386 -0
  93. package/skills/skills/prompt-engineering/references/xml-format.md +220 -0
  94. package/skills/skills/prompt-engineering/references/yaml-format.md +488 -0
  95. package/skills/skills/prompt-engineering/references/zero-shot.md +74 -0
  96. package/skills/skills/quick-spec/SKILL.md +280 -0
  97. package/skills/skills/quick-spec/assets/tech-spec-template.md +74 -0
  98. package/skills/skills/quick-spec/references/step-01-understand.md +189 -0
  99. package/skills/skills/quick-spec/references/step-02-investigate.md +144 -0
  100. package/skills/skills/quick-spec/references/step-03-generate.md +128 -0
  101. package/skills/skills/quick-spec/references/step-04-review.md +173 -0
  102. package/skills/skills/quick-spec/tests/__pycache__/test_skill.cpython-314-pytest-9.0.2.pyc +0 -0
  103. package/skills/skills/quick-spec/tests/test_scenarios.md +83 -0
  104. package/skills/skills/quick-spec/tests/test_skill.py +136 -0
  105. package/skills/skills/readme-expert/SKILL.md +538 -0
  106. package/skills/skills/readme-expert/knowledge/INDEX.md +192 -0
  107. package/skills/skills/readme-expert/knowledge/application/quality-standards.md +470 -0
  108. package/skills/skills/readme-expert/knowledge/application/script-executor.md +604 -0
  109. package/skills/skills/readme-expert/knowledge/application/template-library.md +822 -0
  110. package/skills/skills/readme-expert/knowledge/foundation/codebase-scanner.md +361 -0
  111. package/skills/skills/readme-expert/knowledge/foundation/validation-checklist.md +481 -0
  112. package/skills/skills/red-teaming/SKILL.md +321 -0
  113. package/skills/skills/red-teaming/references/ai-llm-redteam.md +517 -0
  114. package/skills/skills/red-teaming/references/attack-techniques.md +410 -0
  115. package/skills/skills/red-teaming/references/cybersecurity-redteam.md +383 -0
  116. package/skills/skills/red-teaming/references/tools-frameworks.md +446 -0
  117. package/skills/skills/releasing/.skillkit-mode +1 -0
  118. package/skills/skills/releasing/SKILL.md +225 -0
  119. package/skills/skills/releasing/references/version-detection.md +108 -0
  120. package/skills/skills/screenwriter/SKILL.md +273 -0
  121. package/skills/skills/screenwriter/references/advanced-techniques.md +216 -0
  122. package/skills/skills/screenwriter/references/pipeline-integration.md +266 -0
  123. package/skills/skills/skillkit/.claude/settings.local.json +7 -0
  124. package/skills/skills/skillkit/.claude-plugin/plugin.json +27 -0
  125. package/skills/skills/skillkit/CHANGELOG.md +484 -0
  126. package/skills/skills/skillkit/SKILL.md +511 -0
  127. package/skills/skills/skillkit/commands/skillkit.md +6 -0
  128. package/skills/skills/skillkit/commands/validate-plan.md +6 -0
  129. package/skills/skills/skillkit/commands/verify.md +6 -0
  130. package/skills/skills/skillkit/knowledge/INDEX.md +352 -0
  131. package/skills/skills/skillkit/knowledge/application/09-case-studies.md +257 -0
  132. package/skills/skills/skillkit/knowledge/application/10-technical-architecture.md +324 -0
  133. package/skills/skills/skillkit/knowledge/application/11-adoption-strategy.md +267 -0
  134. package/skills/skills/skillkit/knowledge/application/12-testing-and-validation.md +276 -0
  135. package/skills/skills/skillkit/knowledge/application/13-competitive-landscape.md +198 -0
  136. package/skills/skills/skillkit/knowledge/foundation/01-why-skills-exist.md +246 -0
  137. package/skills/skills/skillkit/knowledge/foundation/02-skills-vs-subagents-comparison.md +312 -0
  138. package/skills/skills/skillkit/knowledge/foundation/03-skills-vs-subagents-decision-tree.md +346 -0
  139. package/skills/skills/skillkit/knowledge/foundation/04-hybrid-patterns.md +308 -0
  140. package/skills/skills/skillkit/knowledge/foundation/05-token-economics.md +275 -0
  141. package/skills/skills/skillkit/knowledge/foundation/06-platform-constraints.md +237 -0
  142. package/skills/skills/skillkit/knowledge/foundation/07-security-concerns.md +322 -0
  143. package/skills/skills/skillkit/knowledge/foundation/08-when-not-to-use-skills.md +270 -0
  144. package/skills/skills/skillkit/knowledge/plugin-guide.md +614 -0
  145. package/skills/skills/skillkit/knowledge/tools/14-validation-tools-guide.md +150 -0
  146. package/skills/skills/skillkit/knowledge/tools/15-cost-tools-guide.md +157 -0
  147. package/skills/skills/skillkit/knowledge/tools/16-security-tools-guide.md +122 -0
  148. package/skills/skills/skillkit/knowledge/tools/17-pattern-tools-guide.md +161 -0
  149. package/skills/skills/skillkit/knowledge/tools/18-decision-helper-guide.md +243 -0
  150. package/skills/skills/skillkit/knowledge/tools/19-test-generator-guide.md +275 -0
  151. package/skills/skills/skillkit/knowledge/tools/20-split-skill-guide.md +149 -0
  152. package/skills/skills/skillkit/knowledge/tools/21-quality-scorer-guide.md +226 -0
  153. package/skills/skills/skillkit/knowledge/tools/22-migration-helper-guide.md +356 -0
  154. package/skills/skills/skillkit/knowledge/tools/23-subagent-creation-guide.md +448 -0
  155. package/skills/skills/skillkit/knowledge/tools/24-behavioral-testing-guide.md +122 -0
  156. package/skills/skills/skillkit/references/proposal-generation.md +982 -0
  157. package/skills/skills/skillkit/references/rationalization-catalog.md +75 -0
  158. package/skills/skills/skillkit/references/research-methodology.md +661 -0
  159. package/skills/skills/skillkit/references/section-2-full-creation-workflow.md +452 -0
  160. package/skills/skills/skillkit/references/section-3-validation-workflow-existing-skill.md +63 -0
  161. package/skills/skills/skillkit/references/section-4-decision-workflow-skills-vs-subagents.md +64 -0
  162. package/skills/skills/skillkit/references/section-5-migration-workflow-doc-to-skill.md +58 -0
  163. package/skills/skills/skillkit/references/section-6-subagent-creation-workflow.md +499 -0
  164. package/skills/skills/skillkit/references/section-7-knowledge-reference-map.md +72 -0
  165. package/skills/skills/skillkit/scripts/__pycache__/decision_helper.cpython-314.pyc +0 -0
  166. package/skills/skills/skillkit/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
  167. package/skills/skills/skillkit/scripts/__pycache__/quick_validate.cpython-314.pyc +0 -0
  168. package/skills/skills/skillkit/scripts/__pycache__/test_generator.cpython-314-pytest-9.0.2.pyc +0 -0
  169. package/skills/skills/skillkit/scripts/decision_helper.py +799 -0
  170. package/skills/skills/skillkit/scripts/init_skill.py +400 -0
  171. package/skills/skills/skillkit/scripts/init_subagent.py +231 -0
  172. package/skills/skills/skillkit/scripts/migration_helper.py +669 -0
  173. package/skills/skills/skillkit/scripts/package_skill.py +211 -0
  174. package/skills/skills/skillkit/scripts/pattern_detector.py +381 -0
  175. package/skills/skills/skillkit/scripts/pattern_detector_new.py +382 -0
  176. package/skills/skills/skillkit/scripts/pressure_tester.py +157 -0
  177. package/skills/skills/skillkit/scripts/quality_scorer.py +999 -0
  178. package/skills/skills/skillkit/scripts/quick_validate.py +100 -0
  179. package/skills/skills/skillkit/scripts/security_scanner.py +474 -0
  180. package/skills/skills/skillkit/scripts/split_skill.py +540 -0
  181. package/skills/skills/skillkit/scripts/test_generator.py +695 -0
  182. package/skills/skills/skillkit/scripts/token_estimator.py +493 -0
  183. package/skills/skills/skillkit/scripts/utils/__init__.py +49 -0
  184. package/skills/skills/skillkit/scripts/utils/__pycache__/__init__.cpython-312.pyc +0 -0
  185. package/skills/skills/skillkit/scripts/utils/__pycache__/__init__.cpython-314.pyc +0 -0
  186. package/skills/skills/skillkit/scripts/utils/__pycache__/budget_tracker.cpython-312.pyc +0 -0
  187. package/skills/skills/skillkit/scripts/utils/__pycache__/budget_tracker.cpython-314.pyc +0 -0
  188. package/skills/skills/skillkit/scripts/utils/__pycache__/output_formatter.cpython-312.pyc +0 -0
  189. package/skills/skills/skillkit/scripts/utils/__pycache__/output_formatter.cpython-314.pyc +0 -0
  190. package/skills/skills/skillkit/scripts/utils/__pycache__/reference_validator.cpython-312.pyc +0 -0
  191. package/skills/skills/skillkit/scripts/utils/__pycache__/reference_validator.cpython-314.pyc +0 -0
  192. package/skills/skills/skillkit/scripts/utils/budget_tracker.py +388 -0
  193. package/skills/skills/skillkit/scripts/utils/output_formatter.py +263 -0
  194. package/skills/skills/skillkit/scripts/utils/reference_validator.py +401 -0
  195. package/skills/skills/skillkit/scripts/validate_skill.py +594 -0
  196. package/skills/skills/skillkit/tests/test_behavioral.py +39 -0
  197. package/skills/skills/skillkit/tests/test_scenarios.md +83 -0
  198. package/skills/skills/skillkit/tests/test_skill.py +136 -0
  199. package/skills/skills/skillkit-help/SKILL.md +81 -0
  200. package/skills/skills/skillkit-help/knowledge/application/09-case-studies.md +257 -0
  201. package/skills/skills/skillkit-help/knowledge/application/12-testing-and-validation.md +276 -0
  202. package/skills/skills/skillkit-help/knowledge/foundation/01-why-skills-exist.md +246 -0
  203. package/skills/skills/skillkit-help/knowledge/foundation/02-skills-vs-subagents-comparison.md +312 -0
  204. package/skills/skills/skillkit-help/knowledge/foundation/03-skills-vs-subagents-decision-tree.md +346 -0
  205. package/skills/skills/skillkit-help/knowledge/foundation/06-platform-constraints.md +237 -0
  206. package/skills/skills/skillkit-help/knowledge/foundation/08-when-not-to-use-skills.md +270 -0
  207. package/skills/skills/skillkit-help/template/SKILL.md +52 -0
  208. package/skills/skills/social-media-seo/SKILL.md +278 -0
  209. package/skills/skills/social-media-seo/databases/caption-styles.csv +31 -0
  210. package/skills/skills/social-media-seo/databases/engagement-tactics.csv +16 -0
  211. package/skills/skills/social-media-seo/databases/hashtag-strategies.csv +21 -0
  212. package/skills/skills/social-media-seo/databases/hook-formulas.csv +26 -0
  213. package/skills/skills/social-media-seo/databases/keyword-clusters.csv +11 -0
  214. package/skills/skills/social-media-seo/databases/thread-structures.csv +26 -0
  215. package/skills/skills/social-media-seo/databases/viral-patterns.csv +21 -0
  216. package/skills/skills/social-media-seo/references/analytics-guide.md +321 -0
  217. package/skills/skills/social-media-seo/references/instagram-seo.md +235 -0
  218. package/skills/skills/social-media-seo/references/threads-seo.md +305 -0
  219. package/skills/skills/social-media-seo/references/x-twitter-seo.md +337 -0
  220. package/skills/skills/social-media-seo/scripts/query_database.py +191 -0
  221. package/skills/skills/storyteller/SKILL.md +241 -0
  222. package/skills/skills/storyteller/references/transformation-methodology.md +293 -0
  223. package/skills/skills/storyteller/references/visual-vocabulary.md +177 -0
  224. package/skills/skills/thread-pro/SKILL.md +162 -0
  225. package/skills/skills/thread-pro/anti-ai-patterns.md +120 -0
  226. package/skills/skills/thread-pro/hook-formulas.md +138 -0
  227. package/skills/skills/thread-pro/references/anti-ai-patterns.md +120 -0
  228. package/skills/skills/thread-pro/references/hook-formulas.md +138 -0
  229. package/skills/skills/thread-pro/references/thread-structures.md +240 -0
  230. package/skills/skills/thread-pro/references/voice-injection.md +130 -0
  231. package/skills/skills/thread-pro/thread-structures.md +240 -0
  232. package/skills/skills/thread-pro/voice-injection.md +130 -0
  233. package/skills/skills/tinkering/SKILL.md +251 -0
  234. package/skills/skills/tinkering/references/graduation-checklist.md +100 -0
  235. package/skills/skills/validate-plan/.skillkit-mode +1 -0
  236. package/skills/skills/validate-plan/SKILL.md +406 -0
  237. package/skills/skills/validate-plan/references/dry-principles.md +251 -0
  238. package/skills/skills/validate-plan/references/gap-analysis-guide.md +320 -0
  239. package/skills/skills/validate-plan/references/tdd-patterns.md +413 -0
  240. package/skills/skills/validate-plan/references/yagni-checklist.md +330 -0
  241. package/skills/skills/verify-before-ship/.skillkit-mode +1 -0
  242. package/skills/skills/verify-before-ship/SKILL.md +116 -0
  243. package/skills/skills/verify-before-ship/references/anti-rationalization.md +212 -0
  244. package/skills/skills/verify-before-ship/references/verification-gates.md +305 -0
  245. package/skills-manifest.json +8 -2
  246. package/src/picker.js +11 -5
  247. package/src/picker.test.js +36 -1
@@ -0,0 +1,999 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Score skill quality against best practices.
4
+ Based on comprehensive checklist from Files 01-13.
5
+
6
+ AGENT-LAYER TOOL: Called by Claude via bash_tool, outputs JSON for parsing.
7
+
8
+ This script evaluates skills across 5 categories:
9
+ - Structure (20 pts): YAML, organization, progressive disclosure
10
+ - Content (30 pts): Description, triggers, writing style, examples
11
+ - Efficiency (20 pts): Line count, token estimate, bloat detection
12
+ - Security (15 pts): No secrets, safe patterns, input validation
13
+ - Style (15 pts): Imperative form, conciseness, clear headers
14
+
15
+ Total: 100 points scoring system
16
+
17
+ References:
18
+ File 01: Why Skills Exist
19
+ File 02: Skills vs Subagents comparison
20
+ File 05: Token economics
21
+ File 07: Security concerns
22
+ File 10: Technical architecture
23
+ Files 01-13: Comprehensive best practices
24
+
25
+ Usage (Agent-Layer):
26
+ python quality_scorer.py /path/to/skill --format json
27
+ # Returns JSON: {"status": "success", "overall": {...}, "categories": {...}}
28
+
29
+ Usage (Human-Readable):
30
+ python quality_scorer.py /path/to/skill
31
+ python quality_scorer.py /path/to/skill --detailed
32
+ python quality_scorer.py /path/to/skill --export report.json
33
+ """
34
+
35
+ import argparse
36
+ import json
37
+ import re
38
+ from pathlib import Path
39
+ from typing import Dict, List, Optional
40
+
41
+
42
+ class QualityScorer:
43
+ """Score skill against best practices."""
44
+
45
+ def __init__(self, skill_path: Optional[str] = None, detailed: bool = False):
46
+ """
47
+ Initialize quality scorer.
48
+
49
+ Args:
50
+ skill_path: Path to skill directory
51
+ detailed: If True, show detailed category breakdown
52
+
53
+ References: Files 01-13 (all best practices)
54
+ """
55
+ self.skill_path = Path(skill_path) if skill_path is not None else None
56
+ self.detailed = detailed
57
+ self.scores = {}
58
+ self.recommendations = []
59
+
60
+ # Validate skill path
61
+ if self.skill_path is None:
62
+ return
63
+
64
+ if not self.skill_path.exists():
65
+ raise FileNotFoundError(f"Skill directory not found: {skill_path}")
66
+
67
+ if not (self.skill_path / "SKILL.md").exists():
68
+ raise FileNotFoundError(f"SKILL.md not found in {skill_path}")
69
+
70
+ def calculate_final_score(
71
+ self,
72
+ structural_score: float,
73
+ behavioral_score: Optional[float] = None,
74
+ ) -> Dict:
75
+ """
76
+ Calculate final quality score with optional behavioral component.
77
+
78
+ Formula:
79
+ - fast mode: 100% structural
80
+ - full mode: 60% structural + 40% behavioral
81
+ """
82
+ if behavioral_score is None:
83
+ return {
84
+ "final_score": structural_score,
85
+ "structural_score": structural_score,
86
+ "behavioral_score": None,
87
+ "weights": {"structural": 1.0, "behavioral": 0.0},
88
+ "mode": "fast",
89
+ }
90
+
91
+ final = (structural_score * 0.6) + (behavioral_score * 0.4)
92
+ return {
93
+ "final_score": round(final, 2),
94
+ "structural_score": structural_score,
95
+ "behavioral_score": behavioral_score,
96
+ "weights": {"structural": 0.6, "behavioral": 0.4},
97
+ "mode": "full",
98
+ }
99
+
100
+ def run_behavioral_tests(self, skill_path: str, skill_type: str = None) -> float:
101
+ """
102
+ DEPRECATED in v2.1: Behavioral testing is now done via subagent dispatch.
103
+
104
+ Raises:
105
+ NotImplementedError: Always. Use Full Mode Behavioral Testing Protocol instead.
106
+ """
107
+ raise NotImplementedError(
108
+ "Behavioral scoring via pressure_tester.py is deprecated in v2.1.\n"
109
+ "Use the Full Mode Behavioral Testing Protocol instead:\n"
110
+ "Load: skills/skillkit/references/section-2-full-creation-workflow.md\n"
111
+ "Section: 'Full Mode Behavioral Testing Protocol'"
112
+ )
113
+
114
+ # ========== SCORING CATEGORIES ==========
115
+
116
+ def score_structure(self) -> Dict:
117
+ """
118
+ Score skill structure.
119
+
120
+ Criteria (20 points):
121
+ - YAML frontmatter valid (5 pts)
122
+ - File organization correct (5 pts)
123
+ - Progressive disclosure implemented (5 pts)
124
+ - Reference files properly organized (5 pts)
125
+
126
+ References: File 10 (architecture)
127
+ """
128
+ score = 0
129
+ issues = []
130
+
131
+ # Check YAML frontmatter
132
+ if self._has_valid_yaml():
133
+ score += 5
134
+ else:
135
+ issues.append("YAML frontmatter invalid or missing")
136
+
137
+ # Check file organization
138
+ if self._has_proper_structure():
139
+ score += 5
140
+ else:
141
+ issues.append("File structure not optimal")
142
+
143
+ # Check progressive disclosure
144
+ if self._uses_progressive_disclosure():
145
+ score += 5
146
+ else:
147
+ issues.append("Progressive disclosure not implemented")
148
+
149
+ # Check references
150
+ if self._references_organized():
151
+ score += 5
152
+ else:
153
+ issues.append("References not properly organized")
154
+
155
+ return {
156
+ 'score': score,
157
+ 'max': 20,
158
+ 'percentage': (score / 20) * 100,
159
+ 'issues': issues
160
+ }
161
+
162
+ def score_content(self) -> Dict:
163
+ """
164
+ Score content quality.
165
+
166
+ Criteria (30 points):
167
+ - Description includes WHAT + WHEN (10 pts)
168
+ - Clear trigger conditions (5 pts)
169
+ - Agent-layer writing style (10 pts)
170
+ - Examples inline and relevant (5 pts)
171
+
172
+ References: File 02 (description best practices)
173
+ """
174
+ score = 0
175
+ issues = []
176
+
177
+ # Check description quality
178
+ desc_score = self._score_description()
179
+ score += desc_score
180
+ if desc_score < 10:
181
+ issues.append("Description missing WHAT or WHEN")
182
+
183
+ # Check trigger conditions
184
+ if self._has_clear_triggers():
185
+ score += 5
186
+ else:
187
+ issues.append("Trigger conditions unclear")
188
+
189
+ # Check writing style
190
+ style_score = self._score_writing_style()
191
+ score += style_score
192
+ if style_score < 10:
193
+ issues.append("Writing style not agent-optimized")
194
+
195
+ # Check examples
196
+ if self._has_inline_examples():
197
+ score += 5
198
+ else:
199
+ issues.append("Examples missing or in separate section")
200
+
201
+ return {
202
+ 'score': score,
203
+ 'max': 30,
204
+ 'percentage': (score / 30) * 100,
205
+ 'issues': issues
206
+ }
207
+
208
+ def score_efficiency(self) -> Dict:
209
+ """
210
+ Score token efficiency.
211
+
212
+ Criteria (20 points):
213
+ - SKILL.md under 500 lines (10 pts)
214
+ - Estimated tokens < 5,000 (5 pts)
215
+ - No bloat detected (5 pts)
216
+
217
+ References: File 05 (token economics)
218
+ """
219
+ score = 0
220
+ issues = []
221
+
222
+ skill_md = self.skill_path / "SKILL.md"
223
+ with open(skill_md) as f:
224
+ lines = f.readlines()
225
+ line_count = len(lines)
226
+ content = ''.join(lines)
227
+
228
+ # Line count
229
+ if line_count < 500:
230
+ score += 10
231
+ elif line_count < 800:
232
+ score += 5
233
+ issues.append(f"SKILL.md longer than ideal ({line_count} lines)")
234
+ else:
235
+ issues.append(f"SKILL.md too long ({line_count} lines)")
236
+
237
+ # Token estimate (simplified: ~4 chars per token)
238
+ estimated_tokens = len(content) // 4
239
+ if estimated_tokens < 5000:
240
+ score += 5
241
+ else:
242
+ issues.append(f"Estimated tokens high (~{estimated_tokens})")
243
+
244
+ # Bloat detection (check for repetition)
245
+ if not self._detect_bloat(content):
246
+ score += 5
247
+ else:
248
+ issues.append("Content bloat detected")
249
+
250
+ return {
251
+ 'score': score,
252
+ 'max': 20,
253
+ 'percentage': (score / 20) * 100,
254
+ 'issues': issues
255
+ }
256
+
257
+ def score_security(self) -> Dict:
258
+ """
259
+ Score security practices.
260
+
261
+ Criteria (15 points):
262
+ - No hardcoded secrets (5 pts)
263
+ - No dangerous patterns (5 pts)
264
+ - Input validation present (5 pts)
265
+
266
+ References: File 07 (security concerns)
267
+ """
268
+ score = 0
269
+ issues = []
270
+
271
+ # Check for hardcoded secrets
272
+ if not self._has_hardcoded_secrets():
273
+ score += 5
274
+ else:
275
+ issues.append("Hardcoded secrets detected")
276
+
277
+ # Check for dangerous patterns
278
+ if not self._has_dangerous_patterns():
279
+ score += 5
280
+ else:
281
+ issues.append("Dangerous code patterns detected")
282
+
283
+ # Check input validation
284
+ if self._has_input_validation():
285
+ score += 5
286
+ else:
287
+ issues.append("Input validation missing or weak")
288
+
289
+ return {
290
+ 'score': score,
291
+ 'max': 15,
292
+ 'percentage': (score / 15) * 100,
293
+ 'issues': issues
294
+ }
295
+
296
+ def score_style(self) -> Dict:
297
+ """
298
+ Score writing style.
299
+
300
+ Criteria (15 points):
301
+ - Imperative form used (5 pts)
302
+ - Concise instructions (5 pts)
303
+ - Clear section headers (5 pts)
304
+
305
+ References: All micro-modules (agent-layer style)
306
+ """
307
+ score = 0
308
+ issues = []
309
+
310
+ skill_md = self.skill_path / "SKILL.md"
311
+ with open(skill_md) as f:
312
+ content = f.read()
313
+
314
+ # Check imperative form
315
+ imperative_ratio = self._count_imperative_sentences(content)
316
+ if imperative_ratio > 0.5:
317
+ score += 5
318
+ elif imperative_ratio > 0.3:
319
+ score += 3
320
+ issues.append("More imperative voice recommended")
321
+ else:
322
+ issues.append("Not using imperative voice")
323
+
324
+ # Check conciseness (sentence length)
325
+ avg_sentence_length = self._calculate_avg_sentence_length(content)
326
+ if avg_sentence_length < 20:
327
+ score += 5
328
+ elif avg_sentence_length < 30:
329
+ score += 3
330
+ issues.append("Sentences could be more concise")
331
+ else:
332
+ issues.append("Sentences too verbose")
333
+
334
+ # Check headers
335
+ if self._has_clear_headers(content):
336
+ score += 5
337
+ else:
338
+ issues.append("Headers not descriptive enough")
339
+
340
+ return {
341
+ 'score': score,
342
+ 'max': 15,
343
+ 'percentage': (score / 15) * 100,
344
+ 'issues': issues
345
+ }
346
+
347
+ # ========== HELPER METHODS ==========
348
+
349
+ def _has_valid_yaml(self) -> bool:
350
+ """Check if YAML frontmatter is valid."""
351
+ skill_md = self.skill_path / "SKILL.md"
352
+ with open(skill_md) as f:
353
+ content = f.read()
354
+
355
+ # Check for frontmatter delimiters
356
+ if not content.startswith('---\n'):
357
+ return False
358
+
359
+ # Find end of frontmatter
360
+ end_idx = content.find('\n---\n', 4)
361
+ if end_idx == -1:
362
+ return False
363
+
364
+ frontmatter = content[4:end_idx]
365
+
366
+ # Check for required fields
367
+ has_name = 'name:' in frontmatter
368
+ has_description = 'description:' in frontmatter
369
+
370
+ return has_name and has_description
371
+
372
+ def _has_proper_structure(self) -> bool:
373
+ """Check if file structure is proper."""
374
+ # Check for SKILL.md
375
+ has_skill_md = (self.skill_path / "SKILL.md").exists()
376
+
377
+ # Check if references directory exists and is used properly
378
+ refs_dir = self.skill_path / "references"
379
+ if refs_dir.exists():
380
+ # Should have .md files
381
+ has_ref_files = any(refs_dir.glob('*.md'))
382
+ return has_skill_md and has_ref_files
383
+
384
+ return has_skill_md
385
+
386
+ def _uses_progressive_disclosure(self) -> bool:
387
+ """Check if progressive disclosure is implemented."""
388
+ skill_md = self.skill_path / "SKILL.md"
389
+ with open(skill_md) as f:
390
+ lines = f.readlines()
391
+
392
+ # Check if SKILL.md is reasonably sized
393
+ refs_dir = self.skill_path / "references"
394
+ has_refs = refs_dir.exists() and any(refs_dir.glob('*.md'))
395
+
396
+ # If file is short, progressive disclosure not needed
397
+ # If file is long, should have references
398
+ return len(lines) < 500 or has_refs
399
+
400
+ def _references_organized(self) -> bool:
401
+ """Check if references are organized."""
402
+ refs_dir = self.skill_path / "references"
403
+ if not refs_dir.exists():
404
+ return True # OK if no references needed
405
+
406
+ # Check for proper filenames (lowercase, no spaces)
407
+ for ref_file in refs_dir.glob('*.md'):
408
+ if not ref_file.stem.replace('-', '').replace('_', '').isalnum():
409
+ return False
410
+ if ' ' in ref_file.stem:
411
+ return False
412
+
413
+ return True
414
+
415
+ def _score_description(self) -> int:
416
+ """Score description quality (0-10)."""
417
+ skill_md = self.skill_path / "SKILL.md"
418
+ with open(skill_md) as f:
419
+ content = f.read()
420
+
421
+ # Get frontmatter and first 500 chars
422
+ relevant_section = content[:1000].lower()
423
+
424
+ # Check for WHAT (task/functionality description)
425
+ what_keywords = ['comprehensive', 'provides', 'enables', 'supports', 'tools for',
426
+ 'guide', 'system', 'framework', 'utility']
427
+ has_what = any(kw in relevant_section for kw in what_keywords)
428
+
429
+ # Check for WHEN (trigger conditions)
430
+ when_keywords = ['when', 'use when', 'trigger', 'for tasks', 'invoke',
431
+ 'if', 'needs to', 'requires', 'working with']
432
+ has_when = any(kw in relevant_section for kw in when_keywords)
433
+
434
+ if has_what and has_when:
435
+ return 10
436
+ elif has_what or has_when:
437
+ return 5
438
+ else:
439
+ return 0
440
+
441
+ def _has_clear_triggers(self) -> bool:
442
+ """Check for clear trigger conditions."""
443
+ skill_md = self.skill_path / "SKILL.md"
444
+ with open(skill_md) as f:
445
+ content = f.read()[:1500] # Check first 1500 chars
446
+
447
+ trigger_keywords = ['use when', 'trigger', 'invoke', 'activate',
448
+ 'when claude needs', 'use this skill']
449
+ return any(kw in content.lower() for kw in trigger_keywords)
450
+
451
+ def _score_writing_style(self) -> int:
452
+ """Score writing style (0-10)."""
453
+ skill_md = self.skill_path / "SKILL.md"
454
+ with open(skill_md) as f:
455
+ content = f.read()
456
+
457
+ score = 0
458
+
459
+ # Check for structured formatting (lists, code blocks, tables)
460
+ has_code_blocks = '```' in content
461
+ has_lists = bool(re.search(r'^[\-\*]\s', content, re.MULTILINE))
462
+ has_tables = '|' in content
463
+
464
+ if has_code_blocks or has_lists:
465
+ score += 3
466
+ if has_tables:
467
+ score += 2
468
+
469
+ # Check for direct, actionable language
470
+ action_verbs = ['use', 'run', 'execute', 'create', 'configure',
471
+ 'install', 'check', 'validate', 'ensure']
472
+ verb_count = sum(content.lower().count(verb) for verb in action_verbs)
473
+ if verb_count > 10:
474
+ score += 3
475
+ elif verb_count > 5:
476
+ score += 2
477
+
478
+ # Check for proper section organization
479
+ section_headers = re.findall(r'^##\s+(.+)$', content, re.MULTILINE)
480
+ if len(section_headers) >= 3:
481
+ score += 2
482
+
483
+ return min(score, 10)
484
+
485
+ def _has_inline_examples(self) -> bool:
486
+ """Check if examples are inline."""
487
+ skill_md = self.skill_path / "SKILL.md"
488
+ with open(skill_md) as f:
489
+ content = f.read()
490
+
491
+ # Check for code blocks (indicates examples present)
492
+ has_code_blocks = '```' in content
493
+
494
+ # Check if "Examples" is a separate section (bad practice)
495
+ has_separate_examples = bool(re.search(r'^##\s+Examples?\s*$', content, re.MULTILINE))
496
+
497
+ return has_code_blocks and not has_separate_examples
498
+
499
+ def _detect_bloat(self, content: str) -> bool:
500
+ """Detect content bloat."""
501
+ # Check for very long sections
502
+ sections = content.split('\n## ')
503
+ for section in sections:
504
+ section_lines = section.split('\n')
505
+ if len(section_lines) > 150:
506
+ return True # Section too long
507
+
508
+ # Check for excessive repetition
509
+ lines = content.split('\n')
510
+ if len(lines) > 100:
511
+ # Sample check: look for repeated patterns
512
+ line_set = set(lines)
513
+ repetition_ratio = len(line_set) / len(lines)
514
+ if repetition_ratio < 0.7: # Less than 70% unique lines
515
+ return True
516
+
517
+ return False
518
+
519
+ def _has_hardcoded_secrets(self) -> bool:
520
+ """Check for hardcoded secrets."""
521
+ patterns = [
522
+ r'api[_-]?key\s*=\s*["\'][^"\']+["\']',
523
+ r'password\s*=\s*["\'][^"\']+["\']',
524
+ r'secret\s*=\s*["\'][^"\']+["\']',
525
+ r'token\s*=\s*["\'][^"\']+["\']',
526
+ r'bearer\s+[A-Za-z0-9\-._~+/]+=*',
527
+ ]
528
+
529
+ # Scan all files in skill directory
530
+ for file in self.skill_path.rglob('*'):
531
+ if file.is_file() and file.suffix in ['.md', '.py', '.sh', '.yml', '.yaml']:
532
+ try:
533
+ with open(file, encoding='utf-8') as f:
534
+ content = f.read()
535
+ for pattern in patterns:
536
+ if re.search(pattern, content, re.IGNORECASE):
537
+ return True
538
+ except:
539
+ continue
540
+
541
+ return False
542
+
543
+ def _has_dangerous_patterns(self) -> bool:
544
+ """Check for dangerous code patterns."""
545
+ dangerous_patterns = [
546
+ r'\beval\s*\(',
547
+ r'\bexec\s*\(',
548
+ r'shell\s*=\s*True',
549
+ r'os\.system\(',
550
+ r'subprocess\..*shell=True',
551
+ ]
552
+
553
+ # Scan Python scripts
554
+ for script in self.skill_path.rglob('*.py'):
555
+ try:
556
+ with open(script, encoding='utf-8') as f:
557
+ content = f.read()
558
+ for pattern in dangerous_patterns:
559
+ if re.search(pattern, content):
560
+ return True
561
+ except:
562
+ continue
563
+
564
+ return False
565
+
566
+ def _has_input_validation(self) -> bool:
567
+ """Check if input validation exists."""
568
+ validation_keywords = ['validate', 'check', 'verify', 'assert', 'raise',
569
+ 'isinstance', 'try:', 'except:', 'if not']
570
+
571
+ # Check SKILL.md for validation mentions
572
+ skill_md = self.skill_path / "SKILL.md"
573
+ with open(skill_md) as f:
574
+ skill_content = f.read()
575
+ has_validation_docs = any(kw in skill_content.lower() for kw in validation_keywords[:3])
576
+
577
+ # Check Python scripts for validation code
578
+ has_validation_code = False
579
+ for script in self.skill_path.rglob('*.py'):
580
+ try:
581
+ with open(script, encoding='utf-8') as f:
582
+ content = f.read()
583
+ if any(kw in content for kw in validation_keywords):
584
+ has_validation_code = True
585
+ break
586
+ except:
587
+ continue
588
+
589
+ return has_validation_docs or has_validation_code
590
+
591
+ def _count_imperative_sentences(self, content: str) -> float:
592
+ """Calculate ratio of imperative sentences."""
593
+ # Imperative verbs commonly used in documentation
594
+ imperative_verbs = [
595
+ 'use', 'run', 'execute', 'create', 'configure', 'install',
596
+ 'check', 'validate', 'ensure', 'verify', 'set', 'define',
597
+ 'specify', 'provide', 'include', 'add', 'remove', 'update',
598
+ 'follow', 'read', 'write', 'call', 'invoke', 'load', 'scan',
599
+ 'extract', 'detect', 'discover', 'generate', 'implement'
600
+ ]
601
+
602
+ # Remove YAML frontmatter
603
+ processed_content = content
604
+ if content.startswith('---\n'):
605
+ end_idx = content.find('\n---\n', 4)
606
+ if end_idx != -1:
607
+ processed_content = content[end_idx + 5:]
608
+
609
+ # Remove code blocks to avoid counting code as sentences
610
+ processed_content = re.sub(r'```.*?```', '', processed_content, flags=re.DOTALL)
611
+
612
+ # Split into sentences (approximate)
613
+ sentences = re.split(r'[.!?]\n', processed_content)
614
+ sentences = [s.strip() for s in sentences if len(s.strip()) > 10]
615
+
616
+ if not sentences:
617
+ return 0.0
618
+
619
+ # Count sentences with imperative verbs
620
+ imperative_count = 0
621
+ for sentence in sentences:
622
+ # Strip markdown formatting (bold, italic, inline code)
623
+ clean_sentence = re.sub(r'\*\*([^*]+)\*\*', r'\1', sentence)
624
+ clean_sentence = re.sub(r'\*([^*]+)\*', r'\1', clean_sentence)
625
+ clean_sentence = re.sub(r'`([^`]+)`', r'\1', clean_sentence)
626
+ clean_sentence = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', clean_sentence)
627
+
628
+ # Remove special markers and colons at start
629
+ clean_sentence = re.sub(r'^[\-\*\+]\s+', '', clean_sentence)
630
+ clean_sentence = clean_sentence.strip()
631
+
632
+ if not clean_sentence:
633
+ continue
634
+
635
+ # Get first 3 words to check for imperative
636
+ words = clean_sentence.lower().split()
637
+ first_words = words[:3] if len(words) >= 3 else words
638
+
639
+ # Check if any of first 3 words is imperative verb
640
+ has_imperative = any(
641
+ any(word.startswith(verb) for verb in imperative_verbs)
642
+ for word in first_words
643
+ )
644
+
645
+ if has_imperative:
646
+ imperative_count += 1
647
+
648
+ return imperative_count / len(sentences)
649
+
650
+ def _calculate_avg_sentence_length(self, content: str) -> float:
651
+ """Calculate average sentence length in words."""
652
+ # Remove code blocks to avoid skewing results
653
+ content_no_code = re.sub(r'```.*?```', '', content, flags=re.DOTALL)
654
+
655
+ sentences = re.split(r'[.!?]\s+', content_no_code)
656
+ sentences = [s.strip() for s in sentences if len(s.strip()) > 10]
657
+
658
+ if not sentences:
659
+ return 0.0
660
+
661
+ total_words = sum(len(s.split()) for s in sentences)
662
+ return total_words / len(sentences)
663
+
664
+ def _has_clear_headers(self, content: str) -> bool:
665
+ """Check if headers are clear and descriptive."""
666
+ headers = re.findall(r'^##\s+(.+)$', content, re.MULTILINE)
667
+
668
+ if not headers:
669
+ return False
670
+
671
+ # Check if headers are descriptive (more than 2 words or technical term)
672
+ descriptive_count = 0
673
+ for header in headers:
674
+ header_clean = header.strip()
675
+ words = header_clean.split()
676
+
677
+ # Consider descriptive if: >2 words OR has technical indicators
678
+ is_descriptive = (
679
+ len(words) > 2 or
680
+ any(char.isupper() for char in header_clean[1:]) or # CamelCase
681
+ '-' in header_clean or '_' in header_clean # technical terms
682
+ )
683
+
684
+ if is_descriptive:
685
+ descriptive_count += 1
686
+
687
+ # At least 70% of headers should be descriptive
688
+ return descriptive_count / len(headers) >= 0.7
689
+
690
+ # ========== MAIN SCORING ==========
691
+
692
+ def calculate_overall_score(self) -> Dict:
693
+ """
694
+ Calculate overall quality score.
695
+
696
+ Combines all category scores into overall rating.
697
+
698
+ Returns:
699
+ Dict with overall score and breakdown
700
+ """
701
+ # Score each category
702
+ self.scores['structure'] = self.score_structure()
703
+ self.scores['content'] = self.score_content()
704
+ self.scores['efficiency'] = self.score_efficiency()
705
+ self.scores['security'] = self.score_security()
706
+ self.scores['style'] = self.score_style()
707
+
708
+ # Calculate total
709
+ total_score = sum(s['score'] for s in self.scores.values())
710
+ total_max = sum(s['max'] for s in self.scores.values())
711
+ overall_percentage = (total_score / total_max) * 100
712
+
713
+ # Collect all issues
714
+ all_issues = []
715
+ for category, data in self.scores.items():
716
+ for issue in data['issues']:
717
+ all_issues.append(f"{category.capitalize()}: {issue}")
718
+
719
+ return {
720
+ 'score': total_score,
721
+ 'max': total_max,
722
+ 'percentage': overall_percentage,
723
+ 'grade': self._get_grade(overall_percentage),
724
+ 'issues': all_issues,
725
+ 'categories': self.scores
726
+ }
727
+
728
+ def _get_grade(self, percentage: float) -> str:
729
+ """Convert percentage to letter grade."""
730
+ if percentage >= 90:
731
+ return 'A (Excellent)'
732
+ elif percentage >= 80:
733
+ return 'B (Good)'
734
+ elif percentage >= 70:
735
+ return 'C (Fair)'
736
+ elif percentage >= 60:
737
+ return 'D (Needs Improvement)'
738
+ else:
739
+ return 'F (Poor)'
740
+
741
+ def display_report(self, overall: Dict):
742
+ """Display quality report to console."""
743
+ print("\n" + "="*60)
744
+ print("SKILL QUALITY REPORT")
745
+ print("="*60)
746
+ print(f"\nSkill: {self.skill_path.name}")
747
+ print(f"Overall Score: {overall['score']}/{overall['max']} ({overall['percentage']:.1f}%)")
748
+ print(f"Grade: {overall['grade']}")
749
+
750
+ if self.detailed:
751
+ print("\nCategory Breakdown:")
752
+ for category, data in self.scores.items():
753
+ print(f"\n {category.capitalize():12} {data['score']:2}/{data['max']:2} ({data['percentage']:5.1f}%)")
754
+ if data['issues']:
755
+ for issue in data['issues']:
756
+ print(f" ⚠️ {issue}")
757
+
758
+ if overall['issues']:
759
+ print("\n" + "-"*60)
760
+ print("Recommendations:")
761
+ for i, issue in enumerate(overall['issues'], 1):
762
+ print(f" {i}. {issue}")
763
+
764
+ print("\n" + "="*60)
765
+ print("References:")
766
+ print(" • Files 01-13: Best practices documentation")
767
+ print(" • File 05: Token optimization")
768
+ print(" • File 07: Security guidelines")
769
+ print(" • File 10: Architecture standards")
770
+ print("="*60 + "\n")
771
+
772
+ def export_json(self, filepath: str, overall: Dict):
773
+ """Export report as JSON."""
774
+ report = {
775
+ 'skill_path': str(self.skill_path),
776
+ 'skill_name': self.skill_path.name,
777
+ 'overall': {
778
+ 'score': overall['score'],
779
+ 'max': overall['max'],
780
+ 'percentage': round(overall['percentage'], 2),
781
+ 'grade': overall['grade']
782
+ },
783
+ 'categories': {
784
+ category: {
785
+ 'score': data['score'],
786
+ 'max': data['max'],
787
+ 'percentage': round(data['percentage'], 2),
788
+ 'issues': data['issues']
789
+ }
790
+ for category, data in overall['categories'].items()
791
+ },
792
+ 'recommendations': overall['issues']
793
+ }
794
+
795
+ with open(filepath, 'w') as f:
796
+ json.dump(report, f, indent=2)
797
+
798
+ def export_markdown(self, filepath: str, overall: Dict):
799
+ """Export report as Markdown."""
800
+ lines = []
801
+
802
+ lines.append("# Skill Quality Report")
803
+ lines.append("")
804
+ lines.append(f"**Skill:** {self.skill_path.name}")
805
+ lines.append(f"**Path:** `{self.skill_path}`")
806
+ lines.append("")
807
+
808
+ lines.append("## Overall Score")
809
+ lines.append("")
810
+ lines.append(f"- **Score:** {overall['score']}/{overall['max']} ({overall['percentage']:.1f}%)")
811
+ lines.append(f"- **Grade:** {overall['grade']}")
812
+ lines.append("")
813
+
814
+ lines.append("## Category Breakdown")
815
+ lines.append("")
816
+ lines.append("| Category | Score | Percentage | Status |")
817
+ lines.append("|----------|-------|------------|--------|")
818
+
819
+ for category, data in overall['categories'].items():
820
+ status = "✅" if data['percentage'] >= 80 else "⚠️" if data['percentage'] >= 60 else "❌"
821
+ lines.append(f"| {category.capitalize()} | {data['score']}/{data['max']} | {data['percentage']:.1f}% | {status} |")
822
+
823
+ lines.append("")
824
+
825
+ # Detailed issues per category
826
+ if any(data['issues'] for data in overall['categories'].values()):
827
+ lines.append("## Issues by Category")
828
+ lines.append("")
829
+
830
+ for category, data in overall['categories'].items():
831
+ if data['issues']:
832
+ lines.append(f"### {category.capitalize()}")
833
+ lines.append("")
834
+ for issue in data['issues']:
835
+ lines.append(f"- ⚠️ {issue}")
836
+ lines.append("")
837
+
838
+ # Recommendations
839
+ if overall['issues']:
840
+ lines.append("## Recommendations")
841
+ lines.append("")
842
+ for i, issue in enumerate(overall['issues'], 1):
843
+ lines.append(f"{i}. {issue}")
844
+ lines.append("")
845
+
846
+ lines.append("---")
847
+ lines.append("")
848
+ lines.append("**References:**")
849
+ lines.append("- Files 01-13: Best practices documentation")
850
+ lines.append("- File 05: Token optimization")
851
+ lines.append("- File 07: Security guidelines")
852
+ lines.append("- File 10: Architecture standards")
853
+
854
+ with open(filepath, 'w') as f:
855
+ f.write('\n'.join(lines))
856
+
857
+
858
+ def main():
859
+ """CLI entry point."""
860
+ parser = argparse.ArgumentParser(
861
+ description="Score skill quality against best practices",
862
+ epilog="References: Files 01-13 for comprehensive best practices"
863
+ )
864
+ parser.add_argument(
865
+ 'skill_path',
866
+ type=str,
867
+ help='Path to skill directory'
868
+ )
869
+ parser.add_argument(
870
+ '--format',
871
+ type=str,
872
+ choices=['text', 'json'],
873
+ default='text',
874
+ help='Output format: text (human-readable) or json (agent-layer)'
875
+ )
876
+ parser.add_argument(
877
+ '--detailed',
878
+ action='store_true',
879
+ help='Show detailed category breakdown (text format only)'
880
+ )
881
+ parser.add_argument(
882
+ '--export',
883
+ type=str,
884
+ help='Export report to file (JSON or MD format based on extension)'
885
+ )
886
+ parser.add_argument(
887
+ '--behavioral',
888
+ action='store_true',
889
+ help='Include behavioral pressure testing (full mode)'
890
+ )
891
+ parser.add_argument(
892
+ '--skill-type',
893
+ choices=['discipline', 'technique', 'pattern', 'reference'],
894
+ default='discipline',
895
+ help='Skill type for behavioral testing'
896
+ )
897
+
898
+ args = parser.parse_args()
899
+
900
+ try:
901
+ scorer = QualityScorer(args.skill_path, detailed=args.detailed)
902
+ overall = scorer.calculate_overall_score()
903
+ structural_score = round(overall['percentage'] / 10.0, 2)
904
+ behavioral_score = None
905
+ if args.behavioral:
906
+ import warnings
907
+ warnings.warn(
908
+ "DEPRECATED: --behavioral flag is deprecated in v2.1. "
909
+ "Behavioral scoring via pressure_tester.py has been removed. "
910
+ "Use the Full Mode Behavioral Testing Protocol instead: "
911
+ "skills/skillkit/references/section-2-full-creation-workflow.md",
912
+ DeprecationWarning,
913
+ stacklevel=2,
914
+ )
915
+ behavioral_score = scorer.run_behavioral_tests(args.skill_path, args.skill_type)
916
+ final_score = scorer.calculate_final_score(structural_score, behavioral_score)
917
+
918
+ # Agent-layer JSON output (stdout)
919
+ if args.format == 'json':
920
+ import sys
921
+ report = {
922
+ 'status': 'success',
923
+ 'skill_path': str(scorer.skill_path),
924
+ 'skill_name': scorer.skill_path.name,
925
+ 'mode': final_score['mode'],
926
+ 'final_score': final_score['final_score'],
927
+ 'structural_score': final_score['structural_score'],
928
+ 'behavioral_score': final_score['behavioral_score'],
929
+ 'weights': final_score['weights'],
930
+ 'overall': {
931
+ 'score': overall['score'],
932
+ 'max': overall['max'],
933
+ 'percentage': round(overall['percentage'], 2),
934
+ 'grade': overall['grade']
935
+ },
936
+ 'categories': {
937
+ category: {
938
+ 'score': data['score'],
939
+ 'max': data['max'],
940
+ 'percentage': round(data['percentage'], 2),
941
+ 'issues': data['issues']
942
+ }
943
+ for category, data in overall['categories'].items()
944
+ },
945
+ 'recommendations': overall['issues']
946
+ }
947
+ print(json.dumps(report, indent=2))
948
+ return 0
949
+
950
+ # Human-readable text output (console)
951
+ scorer.display_report(overall)
952
+
953
+ if args.export:
954
+ export_path = Path(args.export)
955
+
956
+ if export_path.suffix.lower() == '.json':
957
+ scorer.export_json(args.export, overall)
958
+ print(f"✅ Report exported to {args.export}")
959
+ elif export_path.suffix.lower() in ['.md', '.markdown']:
960
+ scorer.export_markdown(args.export, overall)
961
+ print(f"✅ Report exported to {args.export}")
962
+ else:
963
+ print(f"⚠️ Unknown export format. Use .json or .md extension.")
964
+ return 1
965
+
966
+ # Return exit code based on score
967
+ if final_score['final_score'] >= 7.0:
968
+ return 0
969
+ else:
970
+ return 1
971
+
972
+ except FileNotFoundError as e:
973
+ if args.format == 'json':
974
+ error_report = {
975
+ 'status': 'error',
976
+ 'error_type': 'FileNotFound',
977
+ 'message': str(e),
978
+ 'help': 'Ensure skill directory exists and contains SKILL.md'
979
+ }
980
+ print(json.dumps(error_report, indent=2))
981
+ else:
982
+ print(f"❌ Error: {e}")
983
+ return 1
984
+ except Exception as e:
985
+ if args.format == 'json':
986
+ error_report = {
987
+ 'status': 'error',
988
+ 'error_type': 'UnexpectedError',
989
+ 'message': str(e),
990
+ 'help': 'Check skill structure and permissions'
991
+ }
992
+ print(json.dumps(error_report, indent=2))
993
+ else:
994
+ print(f"❌ Unexpected error: {e}")
995
+ return 2
996
+
997
+
998
+ if __name__ == "__main__":
999
+ exit(main())