@cleocode/skills 2.0.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 (171) hide show
  1. package/dispatch-config.json +404 -0
  2. package/index.d.ts +178 -0
  3. package/index.js +405 -0
  4. package/package.json +14 -0
  5. package/profiles/core.json +7 -0
  6. package/profiles/full.json +10 -0
  7. package/profiles/minimal.json +7 -0
  8. package/profiles/recommended.json +7 -0
  9. package/provider-skills-map.json +97 -0
  10. package/skills/_shared/cleo-style-guide.md +84 -0
  11. package/skills/_shared/manifest-operations.md +810 -0
  12. package/skills/_shared/placeholders.json +433 -0
  13. package/skills/_shared/skill-chaining-patterns.md +237 -0
  14. package/skills/_shared/subagent-protocol-base.md +223 -0
  15. package/skills/_shared/task-system-integration.md +232 -0
  16. package/skills/_shared/testing-framework-config.md +110 -0
  17. package/skills/ct-cleo/SKILL.md +490 -0
  18. package/skills/ct-cleo/references/anti-patterns.md +19 -0
  19. package/skills/ct-cleo/references/loom-lifecycle.md +136 -0
  20. package/skills/ct-cleo/references/orchestrator-constraints.md +55 -0
  21. package/skills/ct-cleo/references/session-protocol.md +162 -0
  22. package/skills/ct-codebase-mapper/SKILL.md +82 -0
  23. package/skills/ct-contribution/SKILL.md +521 -0
  24. package/skills/ct-contribution/templates/contribution-init.json +21 -0
  25. package/skills/ct-dev-workflow/SKILL.md +423 -0
  26. package/skills/ct-docs-lookup/SKILL.md +66 -0
  27. package/skills/ct-docs-review/SKILL.md +175 -0
  28. package/skills/ct-docs-write/SKILL.md +108 -0
  29. package/skills/ct-documentor/SKILL.md +231 -0
  30. package/skills/ct-epic-architect/SKILL.md +305 -0
  31. package/skills/ct-epic-architect/references/bug-epic-example.md +172 -0
  32. package/skills/ct-epic-architect/references/commands.md +201 -0
  33. package/skills/ct-epic-architect/references/feature-epic-example.md +210 -0
  34. package/skills/ct-epic-architect/references/migration-epic-example.md +244 -0
  35. package/skills/ct-epic-architect/references/output-format.md +92 -0
  36. package/skills/ct-epic-architect/references/patterns.md +284 -0
  37. package/skills/ct-epic-architect/references/refactor-epic-example.md +412 -0
  38. package/skills/ct-epic-architect/references/research-epic-example.md +226 -0
  39. package/skills/ct-epic-architect/references/shell-escaping.md +86 -0
  40. package/skills/ct-epic-architect/references/skill-aware-execution.md +195 -0
  41. package/skills/ct-grade/SKILL.md +230 -0
  42. package/skills/ct-grade/agents/analysis-reporter.md +203 -0
  43. package/skills/ct-grade/agents/blind-comparator.md +157 -0
  44. package/skills/ct-grade/agents/scenario-runner.md +134 -0
  45. package/skills/ct-grade/eval-viewer/__pycache__/generate_grade_review.cpython-314.pyc +0 -0
  46. package/skills/ct-grade/eval-viewer/generate_grade_review.py +1138 -0
  47. package/skills/ct-grade/eval-viewer/generate_grade_viewer.py +544 -0
  48. package/skills/ct-grade/eval-viewer/generate_review.py +283 -0
  49. package/skills/ct-grade/eval-viewer/grade-review.html +1574 -0
  50. package/skills/ct-grade/eval-viewer/viewer.html +219 -0
  51. package/skills/ct-grade/evals/evals.json +94 -0
  52. package/skills/ct-grade/references/ab-test-methodology.md +150 -0
  53. package/skills/ct-grade/references/domains.md +137 -0
  54. package/skills/ct-grade/references/grade-spec.md +236 -0
  55. package/skills/ct-grade/references/scenario-playbook.md +234 -0
  56. package/skills/ct-grade/references/token-tracking.md +120 -0
  57. package/skills/ct-grade/scripts/__pycache__/audit_analyzer.cpython-314.pyc +0 -0
  58. package/skills/ct-grade/scripts/__pycache__/run_ab_test.cpython-314.pyc +0 -0
  59. package/skills/ct-grade/scripts/__pycache__/run_all.cpython-314.pyc +0 -0
  60. package/skills/ct-grade/scripts/__pycache__/token_tracker.cpython-314.pyc +0 -0
  61. package/skills/ct-grade/scripts/audit_analyzer.py +279 -0
  62. package/skills/ct-grade/scripts/generate_report.py +283 -0
  63. package/skills/ct-grade/scripts/run_ab_test.py +504 -0
  64. package/skills/ct-grade/scripts/run_all.py +287 -0
  65. package/skills/ct-grade/scripts/setup_run.py +183 -0
  66. package/skills/ct-grade/scripts/token_tracker.py +630 -0
  67. package/skills/ct-grade-v2-1/SKILL.md +237 -0
  68. package/skills/ct-grade-v2-1/agents/analysis-reporter.md +203 -0
  69. package/skills/ct-grade-v2-1/agents/blind-comparator.md +157 -0
  70. package/skills/ct-grade-v2-1/agents/scenario-runner.md +179 -0
  71. package/skills/ct-grade-v2-1/evals/evals.json +74 -0
  72. package/skills/ct-grade-v2-1/grade-viewer/__pycache__/build_op_stats.cpython-314.pyc +0 -0
  73. package/skills/ct-grade-v2-1/grade-viewer/__pycache__/generate_grade_review.cpython-314.pyc +0 -0
  74. package/skills/ct-grade-v2-1/grade-viewer/build_op_stats.py +174 -0
  75. package/skills/ct-grade-v2-1/grade-viewer/eval-analysis.json +41 -0
  76. package/skills/ct-grade-v2-1/grade-viewer/eval-report.md +34 -0
  77. package/skills/ct-grade-v2-1/grade-viewer/generate_grade_review.py +1023 -0
  78. package/skills/ct-grade-v2-1/grade-viewer/generate_grade_viewer.py +548 -0
  79. package/skills/ct-grade-v2-1/grade-viewer/grade-review-eval.html +613 -0
  80. package/skills/ct-grade-v2-1/grade-viewer/grade-review.html +1532 -0
  81. package/skills/ct-grade-v2-1/grade-viewer/viewer.html +620 -0
  82. package/skills/ct-grade-v2-1/manifest-entry.json +31 -0
  83. package/skills/ct-grade-v2-1/references/ab-testing.md +233 -0
  84. package/skills/ct-grade-v2-1/references/domains-ssot.md +156 -0
  85. package/skills/ct-grade-v2-1/references/grade-spec-v2.md +167 -0
  86. package/skills/ct-grade-v2-1/references/playbook-v2.md +393 -0
  87. package/skills/ct-grade-v2-1/references/token-tracking.md +202 -0
  88. package/skills/ct-grade-v2-1/scripts/generate_report.py +419 -0
  89. package/skills/ct-grade-v2-1/scripts/run_ab_test.py +493 -0
  90. package/skills/ct-grade-v2-1/scripts/run_scenario.py +396 -0
  91. package/skills/ct-grade-v2-1/scripts/setup_run.py +207 -0
  92. package/skills/ct-grade-v2-1/scripts/token_tracker.py +175 -0
  93. package/skills/ct-memory/SKILL.md +84 -0
  94. package/skills/ct-orchestrator/INSTALL.md +61 -0
  95. package/skills/ct-orchestrator/README.md +69 -0
  96. package/skills/ct-orchestrator/SKILL.md +380 -0
  97. package/skills/ct-orchestrator/manifest-entry.json +19 -0
  98. package/skills/ct-orchestrator/orchestrator-prompt.txt +17 -0
  99. package/skills/ct-orchestrator/references/SUBAGENT-PROTOCOL-BLOCK.md +66 -0
  100. package/skills/ct-orchestrator/references/autonomous-operation.md +167 -0
  101. package/skills/ct-orchestrator/references/lifecycle-gates.md +98 -0
  102. package/skills/ct-orchestrator/references/orchestrator-compliance.md +271 -0
  103. package/skills/ct-orchestrator/references/orchestrator-handoffs.md +85 -0
  104. package/skills/ct-orchestrator/references/orchestrator-patterns.md +164 -0
  105. package/skills/ct-orchestrator/references/orchestrator-recovery.md +113 -0
  106. package/skills/ct-orchestrator/references/orchestrator-spawning.md +271 -0
  107. package/skills/ct-orchestrator/references/orchestrator-tokens.md +180 -0
  108. package/skills/ct-research-agent/SKILL.md +226 -0
  109. package/skills/ct-skill-creator/.cleo/.context-state.json +13 -0
  110. package/skills/ct-skill-creator/.cleo/logs/cleo.2026-03-07.1.log +24 -0
  111. package/skills/ct-skill-creator/.cleo/tasks.db +0 -0
  112. package/skills/ct-skill-creator/SKILL.md +356 -0
  113. package/skills/ct-skill-creator/agents/analyzer.md +276 -0
  114. package/skills/ct-skill-creator/agents/comparator.md +204 -0
  115. package/skills/ct-skill-creator/agents/grader.md +225 -0
  116. package/skills/ct-skill-creator/assets/eval_review.html +146 -0
  117. package/skills/ct-skill-creator/eval-viewer/__pycache__/generate_review.cpython-314.pyc +0 -0
  118. package/skills/ct-skill-creator/eval-viewer/generate_review.py +471 -0
  119. package/skills/ct-skill-creator/eval-viewer/viewer.html +1325 -0
  120. package/skills/ct-skill-creator/manifest-entry.json +17 -0
  121. package/skills/ct-skill-creator/references/dynamic-context.md +228 -0
  122. package/skills/ct-skill-creator/references/frontmatter.md +83 -0
  123. package/skills/ct-skill-creator/references/invocation-control.md +165 -0
  124. package/skills/ct-skill-creator/references/output-patterns.md +86 -0
  125. package/skills/ct-skill-creator/references/provider-deployment.md +175 -0
  126. package/skills/ct-skill-creator/references/schemas.md +430 -0
  127. package/skills/ct-skill-creator/references/workflows.md +28 -0
  128. package/skills/ct-skill-creator/scripts/__init__.py +1 -0
  129. package/skills/ct-skill-creator/scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  130. package/skills/ct-skill-creator/scripts/__pycache__/aggregate_benchmark.cpython-314.pyc +0 -0
  131. package/skills/ct-skill-creator/scripts/__pycache__/generate_report.cpython-314.pyc +0 -0
  132. package/skills/ct-skill-creator/scripts/__pycache__/improve_description.cpython-314.pyc +0 -0
  133. package/skills/ct-skill-creator/scripts/__pycache__/init_skill.cpython-314.pyc +0 -0
  134. package/skills/ct-skill-creator/scripts/__pycache__/quick_validate.cpython-314.pyc +0 -0
  135. package/skills/ct-skill-creator/scripts/__pycache__/run_eval.cpython-314.pyc +0 -0
  136. package/skills/ct-skill-creator/scripts/__pycache__/run_loop.cpython-314.pyc +0 -0
  137. package/skills/ct-skill-creator/scripts/__pycache__/utils.cpython-314.pyc +0 -0
  138. package/skills/ct-skill-creator/scripts/aggregate_benchmark.py +401 -0
  139. package/skills/ct-skill-creator/scripts/generate_report.py +326 -0
  140. package/skills/ct-skill-creator/scripts/improve_description.py +247 -0
  141. package/skills/ct-skill-creator/scripts/init_skill.py +306 -0
  142. package/skills/ct-skill-creator/scripts/package_skill.py +110 -0
  143. package/skills/ct-skill-creator/scripts/quick_validate.py +97 -0
  144. package/skills/ct-skill-creator/scripts/run_eval.py +310 -0
  145. package/skills/ct-skill-creator/scripts/run_loop.py +328 -0
  146. package/skills/ct-skill-creator/scripts/utils.py +47 -0
  147. package/skills/ct-skill-validator/SKILL.md +178 -0
  148. package/skills/ct-skill-validator/agents/ecosystem-checker.md +151 -0
  149. package/skills/ct-skill-validator/assets/valid-skill-example.md +13 -0
  150. package/skills/ct-skill-validator/evals/eval_set.json +14 -0
  151. package/skills/ct-skill-validator/evals/evals.json +52 -0
  152. package/skills/ct-skill-validator/manifest-entry.json +20 -0
  153. package/skills/ct-skill-validator/references/cleo-ecosystem-rules.md +163 -0
  154. package/skills/ct-skill-validator/references/validation-rules.md +168 -0
  155. package/skills/ct-skill-validator/scripts/__init__.py +0 -0
  156. package/skills/ct-skill-validator/scripts/__pycache__/audit_body.cpython-314.pyc +0 -0
  157. package/skills/ct-skill-validator/scripts/__pycache__/check_ecosystem.cpython-314.pyc +0 -0
  158. package/skills/ct-skill-validator/scripts/__pycache__/generate_validation_report.cpython-314.pyc +0 -0
  159. package/skills/ct-skill-validator/scripts/__pycache__/validate.cpython-314.pyc +0 -0
  160. package/skills/ct-skill-validator/scripts/audit_body.py +242 -0
  161. package/skills/ct-skill-validator/scripts/check_ecosystem.py +169 -0
  162. package/skills/ct-skill-validator/scripts/check_manifest.py +172 -0
  163. package/skills/ct-skill-validator/scripts/generate_validation_report.py +442 -0
  164. package/skills/ct-skill-validator/scripts/validate.py +422 -0
  165. package/skills/ct-spec-writer/SKILL.md +189 -0
  166. package/skills/ct-stickynote/README.md +14 -0
  167. package/skills/ct-stickynote/SKILL.md +46 -0
  168. package/skills/ct-task-executor/SKILL.md +296 -0
  169. package/skills/ct-validator/SKILL.md +216 -0
  170. package/skills/manifest.json +469 -0
  171. package/skills.json +281 -0
@@ -0,0 +1,422 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ CLEO Skill Validator — Full compliance gauntlet.
4
+ Validates a skill directory against the complete CLEO skill standard.
5
+
6
+ Usage:
7
+ validate.py <skill-directory>
8
+ validate.py <skill-directory> --manifest path/to/manifest.json
9
+ validate.py <skill-directory> --manifest path/to/manifest.json --dispatch-config path/to/dispatch-config.json
10
+ validate.py <skill-directory> --provider-map path/to/provider-skills-map.json
11
+ validate.py <skill-directory> --json
12
+ """
13
+ import sys
14
+ import re
15
+ import json
16
+ import yaml
17
+ import argparse
18
+ from pathlib import Path
19
+
20
+ V2_STANDARD = {
21
+ "name", "description", "argument-hint", "disable-model-invocation",
22
+ "user-invocable", "allowed-tools", "model", "context", "agent", "hooks",
23
+ "license",
24
+ }
25
+ CLEO_ONLY = {
26
+ "version", "tier", "core", "category", "protocol",
27
+ "dependencies", "sharedResources", "compatibility",
28
+ "token_budget", "capabilities", "constraints",
29
+ "metadata", "tags", "triggers", "mvi_scope", "requires_tiers",
30
+ }
31
+
32
+ MANIFEST_REQUIRED_FIELDS = [
33
+ "name", "version", "description", "path", "status",
34
+ "tier", "token_budget", "capabilities", "constraints",
35
+ ]
36
+
37
+
38
+ def validate_skill(skill_path, manifest_path=None, dispatch_config_path=None, provider_map_path=None):
39
+ """Run the full v2 validation gauntlet on a skill directory.
40
+
41
+ Returns (results, errors, warnings) where results is a list of
42
+ (tier, severity, message) tuples.
43
+ """
44
+ skill_dir = Path(skill_path).resolve()
45
+ skill_name = skill_dir.name
46
+ errors = 0
47
+ warnings = 0
48
+ results = []
49
+
50
+ def error(tier, msg):
51
+ nonlocal errors
52
+ errors += 1
53
+ results.append((tier, "ERROR", msg))
54
+
55
+ def warn(tier, msg):
56
+ nonlocal warnings
57
+ warnings += 1
58
+ results.append((tier, "WARN", msg))
59
+
60
+ def ok(tier, msg):
61
+ results.append((tier, "OK", msg))
62
+
63
+ # ── Tier 1 — Structure ──────────────────────────────────────────────
64
+ tier = 1
65
+ skill_md = skill_dir / "SKILL.md"
66
+
67
+ if not skill_md.exists():
68
+ error(tier, "SKILL.md does not exist")
69
+ return results, errors, warnings
70
+
71
+ ok(tier, "SKILL.md exists")
72
+
73
+ raw_content = skill_md.read_text(encoding="utf-8")
74
+
75
+ if not raw_content.startswith("---"):
76
+ error(tier, "SKILL.md does not start with '---' (no frontmatter block)")
77
+ return results, errors, warnings
78
+
79
+ ok(tier, "Content starts with '---'")
80
+
81
+ fm_match = re.match(r"^---\n(.*?)\n---", raw_content, re.DOTALL)
82
+ if not fm_match:
83
+ error(tier, "Could not extract frontmatter (missing closing '---')")
84
+ return results, errors, warnings
85
+
86
+ ok(tier, "Frontmatter block extracted")
87
+
88
+ raw_frontmatter = fm_match.group(1)
89
+ try:
90
+ frontmatter = yaml.safe_load(raw_frontmatter)
91
+ except yaml.YAMLError as e:
92
+ error(tier, f"Frontmatter is not valid YAML: {e}")
93
+ return results, errors, warnings
94
+
95
+ ok(tier, "Frontmatter is valid YAML")
96
+
97
+ if not isinstance(frontmatter, dict):
98
+ error(tier, "Frontmatter is not a dictionary (key: value pairs expected)")
99
+ return results, errors, warnings
100
+
101
+ ok(tier, "Frontmatter is a dict")
102
+
103
+ for key in frontmatter:
104
+ if key in CLEO_ONLY:
105
+ error(tier, f"Move '{key}' to manifest.json (CLEO-only field)")
106
+ else:
107
+ pass # valid or unknown keys checked in tier 2
108
+
109
+ if not any(r[1] == "ERROR" and "CLEO-only" in r[2] for r in results):
110
+ ok(tier, "No CLEO-only fields in frontmatter")
111
+
112
+ # ── Tier 2 — Frontmatter Quality ────────────────────────────────────
113
+ tier = 2
114
+
115
+ # name checks
116
+ name_val = frontmatter.get("name")
117
+ if name_val is None:
118
+ error(tier, "'name' field is missing")
119
+ else:
120
+ if not isinstance(name_val, str):
121
+ error(tier, "'name' must be a string")
122
+ else:
123
+ if not re.match(r"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$", name_val):
124
+ error(tier, f"'name' must be hyphen-case (got: '{name_val}')")
125
+ if "--" in name_val:
126
+ error(tier, "'name' must not contain consecutive hyphens")
127
+ if name_val.startswith("-") or name_val.endswith("-"):
128
+ error(tier, "'name' must not start or end with a hyphen")
129
+ if len(name_val) > 64:
130
+ error(tier, f"'name' exceeds 64 characters (got: {len(name_val)})")
131
+ if name_val != skill_name:
132
+ warn(tier, f"'name' field ('{name_val}') does not match directory name ('{skill_name}')")
133
+ if not any(r[1] == "ERROR" and "'name'" in r[2] for r in results if r[0] == 2):
134
+ ok(tier, "'name' is valid")
135
+
136
+ # description checks
137
+ desc_val = frontmatter.get("description")
138
+ if desc_val is None:
139
+ error(tier, "'description' field is missing")
140
+ else:
141
+ if not isinstance(desc_val, str):
142
+ error(tier, "'description' must be a string")
143
+ else:
144
+ if "<" in desc_val or ">" in desc_val:
145
+ error(tier, "'description' must not contain '<' or '>' characters")
146
+ if len(desc_val) > 1024:
147
+ error(tier, f"'description' exceeds 1024 characters (got: {len(desc_val)})")
148
+ if len(desc_val) < 50:
149
+ warn(tier, f"'description' is shorter than 50 characters (got: {len(desc_val)})")
150
+ trigger_indicators = ["when", "use when", "use for"]
151
+ has_trigger = any(ind in desc_val.lower() for ind in trigger_indicators)
152
+ if not has_trigger:
153
+ warn(tier, "'description' should contain a trigger indicator ('when', 'use when', 'use for')")
154
+ if desc_val.startswith("I "):
155
+ warn(tier, "'description' should not start with 'I ' (use third person)")
156
+ if not any(r[1] == "ERROR" and "'description'" in r[2] for r in results if r[0] == 2):
157
+ ok(tier, "'description' is valid")
158
+
159
+ # YAML multiline pitfall check
160
+ if "description: >" in raw_frontmatter or "description: |" in raw_frontmatter:
161
+ warn(tier, "'description' uses YAML multiline syntax (> or |) which can cause unexpected whitespace")
162
+
163
+ # context checks
164
+ context_val = frontmatter.get("context")
165
+ if context_val is not None:
166
+ if context_val != "fork":
167
+ error(tier, f"'context' must be 'fork' if present (got: '{context_val}')")
168
+ else:
169
+ if "agent" not in frontmatter:
170
+ warn(tier, "'context' is 'fork' but no 'agent' field specified")
171
+ ok(tier, "'context' is valid")
172
+
173
+ # boolean field checks
174
+ dmi_val = frontmatter.get("disable-model-invocation")
175
+ if dmi_val is not None and not isinstance(dmi_val, bool):
176
+ error(tier, "'disable-model-invocation' must be a boolean")
177
+
178
+ ui_val = frontmatter.get("user-invocable")
179
+ if ui_val is not None and not isinstance(ui_val, bool):
180
+ error(tier, "'user-invocable' must be a boolean")
181
+
182
+ # contradictory flags
183
+ if (isinstance(dmi_val, bool) and dmi_val is True and
184
+ isinstance(ui_val, bool) and ui_val is False):
185
+ error(tier, "Contradictory: 'disable-model-invocation' is true AND 'user-invocable' is false (skill cannot be invoked at all)")
186
+
187
+ # argument-hint checks
188
+ ah_val = frontmatter.get("argument-hint")
189
+ if ah_val is not None:
190
+ if not isinstance(ah_val, str):
191
+ error(tier, "'argument-hint' must be a string")
192
+ elif len(ah_val) > 100:
193
+ error(tier, f"'argument-hint' exceeds 100 characters (got: {len(ah_val)})")
194
+
195
+ # allowed-tools checks
196
+ at_val = frontmatter.get("allowed-tools")
197
+ if at_val is not None:
198
+ if not isinstance(at_val, (str, list)):
199
+ error(tier, "'allowed-tools' must be a string or list")
200
+
201
+ # model checks
202
+ model_val = frontmatter.get("model")
203
+ if model_val is not None and not isinstance(model_val, str):
204
+ error(tier, "'model' must be a string")
205
+
206
+ # agent checks
207
+ agent_val = frontmatter.get("agent")
208
+ if agent_val is not None and not isinstance(agent_val, str):
209
+ error(tier, "'agent' must be a string")
210
+
211
+ # hooks checks
212
+ hooks_val = frontmatter.get("hooks")
213
+ if hooks_val is not None and not isinstance(hooks_val, dict):
214
+ error(tier, "'hooks' must be a dict")
215
+
216
+ # ── Tier 3 — Body Quality ───────────────────────────────────────────
217
+ tier = 3
218
+
219
+ # Extract body (content after second ---)
220
+ parts = raw_content.split("---", 2)
221
+ body = parts[2].strip() if len(parts) >= 3 else ""
222
+
223
+ if not body:
224
+ warn(tier, "Body is empty (no content after frontmatter)")
225
+ else:
226
+ ok(tier, "Body is present")
227
+
228
+ body_lines = body.split("\n")
229
+ line_count = len(body_lines)
230
+
231
+ if line_count >= 600:
232
+ error(tier, f"Body is too long: {line_count} lines (max 600)")
233
+ elif line_count >= 400:
234
+ warn(tier, f"Body is getting long: {line_count} lines (warn threshold: 400)")
235
+ else:
236
+ ok(tier, f"Body length OK ({line_count} lines)")
237
+
238
+ # Placeholder scan — case-sensitive to avoid matching "todo app", "replace with X", etc.
239
+ placeholders = [r"\[Required:", r"\bTODO\b", r"\bREPLACE\b", r"\[Add content", r"\bFIXME\b", r"\bTBD\b"]
240
+ for pattern in placeholders:
241
+ matches = re.findall(pattern, body)
242
+ if matches:
243
+ clean_pattern = re.sub(r"[\\(?\[\])]", "", pattern).strip("\\b")
244
+ warn(tier, f"Placeholder text found: '{clean_pattern}' ({len(matches)} occurrence(s))")
245
+
246
+ # Section headers check for long bodies
247
+ if line_count > 200:
248
+ section_headers = re.findall(r"^## ", body, re.MULTILINE)
249
+ if not section_headers:
250
+ warn(tier, "Body exceeds 200 lines but has no '## ' section headers")
251
+ else:
252
+ ok(tier, f"Body has {len(section_headers)} section header(s)")
253
+
254
+ # File reference existence checks
255
+ # Strip fenced code blocks first — paths inside ``` are examples, not live references
256
+ body_no_fences = re.sub(r"```[\s\S]*?```", "", body)
257
+ refs = re.findall(r"(?:references|scripts)/[\w./-]+", body_no_fences)
258
+ for ref in refs:
259
+ # Skip cross-skill paths (preceded by / or ${ anywhere in the body)
260
+ escaped = re.escape(ref)
261
+ if re.search(r"[/$]" + escaped, body_no_fences):
262
+ continue
263
+ # Skip example prose: line contains illustrative markers
264
+ ref_line = next((l for l in body_no_fences.split("\n") if ref in l), "")
265
+ if re.search(r"\b(examples?|e\.g\.|such as|like `|would be|illustrat)\b", ref_line, re.IGNORECASE):
266
+ continue
267
+ ref_path = skill_dir / ref
268
+ if not ref_path.exists():
269
+ warn(tier, f"Referenced file does not exist: {ref}")
270
+
271
+ # ── Tier 4 — CLEO Integration ──────────────────────────────────────
272
+ tier = 4
273
+
274
+ if manifest_path:
275
+ manifest_file = Path(manifest_path).resolve()
276
+ if not manifest_file.exists():
277
+ error(tier, f"Manifest file not found: {manifest_path}")
278
+ else:
279
+ try:
280
+ manifest_data = json.loads(manifest_file.read_text(encoding="utf-8"))
281
+ except json.JSONDecodeError as e:
282
+ error(tier, f"Manifest is not valid JSON: {e}")
283
+ manifest_data = None
284
+
285
+ if manifest_data is not None:
286
+ skills_list = manifest_data.get("skills", [])
287
+ matching = [s for s in skills_list if s.get("name") == skill_name]
288
+
289
+ if not matching:
290
+ warn(tier, f"Skill '{skill_name}' not found in manifest.json skills[]")
291
+ else:
292
+ ok(tier, f"Skill '{skill_name}' found in manifest.json")
293
+ entry = matching[0]
294
+ for field in MANIFEST_REQUIRED_FIELDS:
295
+ if field not in entry:
296
+ warn(tier, f"Manifest entry missing required field: '{field}'")
297
+
298
+ if dispatch_config_path:
299
+ dc_file = Path(dispatch_config_path).resolve()
300
+ if not dc_file.exists():
301
+ error(tier, f"Dispatch config file not found: {dispatch_config_path}")
302
+ else:
303
+ try:
304
+ dc_data = json.loads(dc_file.read_text(encoding="utf-8"))
305
+ except json.JSONDecodeError as e:
306
+ error(tier, f"Dispatch config is not valid JSON: {e}")
307
+ dc_data = None
308
+
309
+ if dc_data is not None:
310
+ overrides = dc_data.get("skill_overrides", {})
311
+ if skill_name not in overrides:
312
+ warn(tier, f"Skill '{skill_name}' not found in dispatch-config.json skill_overrides")
313
+ else:
314
+ ok(tier, f"Skill '{skill_name}' found in dispatch-config.json")
315
+
316
+ # ── Tier 5 — Provider Compatibility ─────────────────────────────────
317
+ tier = 5
318
+
319
+ if provider_map_path:
320
+ pm_file = Path(provider_map_path).resolve()
321
+ if not pm_file.exists():
322
+ error(tier, f"Provider map file not found: {provider_map_path}")
323
+ else:
324
+ try:
325
+ pm_data = json.loads(pm_file.read_text(encoding="utf-8"))
326
+ except json.JSONDecodeError as e:
327
+ error(tier, f"Provider map is not valid JSON: {e}")
328
+ pm_data = None
329
+
330
+ if pm_data is not None:
331
+ # Check if skill is referenced anywhere in the provider map
332
+ pm_text = json.dumps(pm_data)
333
+ if skill_name not in pm_text:
334
+ warn(tier, f"Skill '{skill_name}' not referenced in provider-skills-map.json")
335
+ else:
336
+ ok(tier, f"Skill '{skill_name}' found in provider-skills-map.json")
337
+
338
+ return results, errors, warnings
339
+
340
+
341
+ def _print_report(skill_name, results, errors, warnings):
342
+ """Print the structured validation report."""
343
+ print(f"\n=== CLEO Skill Validator: {skill_name} ===\n")
344
+
345
+ tier_names = {
346
+ 1: "Tier 1 — Structure",
347
+ 2: "Tier 2 — Frontmatter Quality",
348
+ 3: "Tier 3 — Body Quality",
349
+ 4: "Tier 4 — CLEO Integration",
350
+ 5: "Tier 5 — Provider Compatibility",
351
+ }
352
+
353
+ current_tier = None
354
+ for tier_num, severity, msg in results:
355
+ if tier_num != current_tier:
356
+ current_tier = tier_num
357
+ print(f"{tier_names.get(tier_num, f'Tier {tier_num}')}")
358
+
359
+ if severity == "OK":
360
+ print(f" \u2705 {msg}")
361
+ elif severity == "ERROR":
362
+ print(f" \u274c ERROR: {msg}")
363
+ elif severity == "WARN":
364
+ print(f" \u26a0\ufe0f WARN: {msg}")
365
+
366
+ print(f"\n=== SUMMARY ===")
367
+ print(f"Errors: {errors}")
368
+ print(f"Warnings: {warnings}")
369
+
370
+ if errors > 0:
371
+ print(f"Result: FAIL")
372
+ elif warnings > 0:
373
+ print(f"Result: PASS (with warnings)")
374
+ else:
375
+ print(f"Result: PASS")
376
+
377
+
378
+ def main():
379
+ parser = argparse.ArgumentParser(
380
+ description="CLEO Skill Validator — Full compliance gauntlet"
381
+ )
382
+ parser.add_argument("skill_dir", help="Path to the skill directory to validate")
383
+ parser.add_argument("--manifest", help="Path to manifest.json for CLEO integration check")
384
+ parser.add_argument("--dispatch-config", help="Path to dispatch-config.json for dispatch override check")
385
+ parser.add_argument("--provider-map", help="Path to provider-skills-map.json for provider compatibility check")
386
+ parser.add_argument("--json", action="store_true", help="Output results as JSON instead of human-readable text")
387
+
388
+ args = parser.parse_args()
389
+
390
+ skill_path = Path(args.skill_dir).resolve()
391
+ if not skill_path.is_dir():
392
+ print(f"Error: '{args.skill_dir}' is not a directory", file=sys.stderr)
393
+ sys.exit(1)
394
+
395
+ skill_name = skill_path.name
396
+ results, errors, warnings = validate_skill(
397
+ skill_path,
398
+ manifest_path=args.manifest,
399
+ dispatch_config_path=args.dispatch_config,
400
+ provider_map_path=args.provider_map,
401
+ )
402
+
403
+ if getattr(args, "json"):
404
+ output = {
405
+ "skill_name": skill_name,
406
+ "results": [
407
+ {"tier": t, "severity": s, "message": m}
408
+ for t, s, m in results
409
+ ],
410
+ "errors": errors,
411
+ "warnings": warnings,
412
+ "passed": errors == 0,
413
+ }
414
+ print(json.dumps(output, indent=2))
415
+ else:
416
+ _print_report(skill_name, results, errors, warnings)
417
+
418
+ sys.exit(1 if errors > 0 else 0)
419
+
420
+
421
+ if __name__ == "__main__":
422
+ main()
@@ -0,0 +1,189 @@
1
+ ---
2
+ name: ct-spec-writer
3
+ description: Technical specification writing using RFC 2119 language for clear, unambiguous requirements. Creates protocol specifications, technical requirements, API specifications, and architecture documents with testable requirements and compliance criteria. Use when writing specifications, defining protocols, documenting requirements, or creating API contracts. Triggers on specification tasks, protocol definition needs, or requirement documentation.
4
+ version: 2.0.0
5
+ tier: 2
6
+ core: false
7
+ category: recommended
8
+ protocol: specification
9
+ dependencies: []
10
+ sharedResources:
11
+ - subagent-protocol-base
12
+ - task-system-integration
13
+ compatibility:
14
+ - claude-code
15
+ - cursor
16
+ - windsurf
17
+ - gemini-cli
18
+ license: MIT
19
+ ---
20
+
21
+ # Specification Writer Context Injection
22
+
23
+ **Protocol**: @src/protocols/specification.md
24
+ **Type**: Context Injection (cleo-subagent)
25
+ **Version**: 2.0.0
26
+
27
+ ---
28
+
29
+ ## Purpose
30
+
31
+ Context injection for specification writing tasks spawned via cleo-subagent. Provides domain expertise for creating clear, unambiguous technical specifications using RFC 2119 language.
32
+
33
+ ---
34
+
35
+ ## Capabilities
36
+
37
+ 1. **Protocol Specifications** - Define behavior rules with RFC 2119 keywords
38
+ 2. **Technical Requirements** - Document system requirements with constraints
39
+ 3. **API Specifications** - Define interfaces, schemas, and contracts
40
+ 4. **Architecture Documents** - Document system design decisions
41
+
42
+ ---
43
+
44
+ ## RFC 2119 Keywords (MANDATORY)
45
+
46
+ Use these keywords with their precise meanings:
47
+
48
+ | Keyword | Meaning | Compliance |
49
+ |---------|---------|------------|
50
+ | **MUST** | Absolute requirement | 95-98% |
51
+ | **MUST NOT** | Absolute prohibition | 93-97% |
52
+ | **SHOULD** | Recommended unless good reason exists | 75-85% |
53
+ | **SHOULD NOT** | Discouraged unless good reason exists | 75-85% |
54
+ | **MAY** | Truly optional | 40-60% |
55
+
56
+ ---
57
+
58
+ ## Specification Structure
59
+
60
+ ### Standard Layout
61
+
62
+ ```markdown
63
+ # {Specification Title} v{X.Y.Z}
64
+
65
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHOULD",
66
+ "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document
67
+ are to be interpreted as described in RFC 2119.
68
+
69
+ ---
70
+
71
+ ## Overview
72
+
73
+ {2-3 sentence summary of what this spec defines}
74
+
75
+ ---
76
+
77
+ ## Definitions
78
+
79
+ | Term | Definition |
80
+ |------|------------|
81
+ | {term} | {definition} |
82
+
83
+ ---
84
+
85
+ ## Requirements
86
+
87
+ ### {Category 1}
88
+
89
+ **REQ-001**: {Requirement description}
90
+ - Rationale: {Why this requirement exists}
91
+ - Verification: {How to verify compliance}
92
+
93
+ ### {Category 2}
94
+
95
+ **REQ-002**: {Requirement description}
96
+ ...
97
+
98
+ ---
99
+
100
+ ## Constraints
101
+
102
+ | ID | Constraint | Enforcement |
103
+ |----|------------|-------------|
104
+ | CON-001 | {constraint} | {how enforced} |
105
+
106
+ ---
107
+
108
+ ## Compliance
109
+
110
+ A system is compliant if:
111
+ 1. {condition 1}
112
+ 2. {condition 2}
113
+ 3. {condition 3}
114
+
115
+ Non-compliant implementations SHOULD {remediation}.
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Writing Guidelines
121
+
122
+ ### Be Precise
123
+ - Every requirement MUST be testable
124
+ - Avoid ambiguous terms ("appropriate", "reasonable", "adequate")
125
+ - Use specific values, not ranges when possible
126
+
127
+ ### Be Complete
128
+ - Define all terms that might be misunderstood
129
+ - Cover error cases and edge conditions
130
+ - Specify what happens when requirements conflict
131
+
132
+ ### Be Organized
133
+ - Group related requirements
134
+ - Use consistent numbering (REQ-XXX, CON-XXX)
135
+ - Cross-reference related sections
136
+
137
+ ---
138
+
139
+ ## Output Location
140
+
141
+ Specifications go in: `docs/specs/{{SPEC_NAME}}.md`
142
+
143
+ ---
144
+
145
+ ## Task System Integration
146
+
147
+ @skills/_shared/task-system-integration.md
148
+
149
+ ### Execution Sequence
150
+
151
+ 1. Read task: `{{TASK_SHOW_CMD}} {{TASK_ID}}`
152
+ 2. Start task: `{{TASK_START_CMD}} {{TASK_ID}}` (if not already started by orchestrator)
153
+ 3. Write specification to `docs/specs/{{SPEC_NAME}}.md`
154
+ 4. Append manifest entry to `{{MANIFEST_PATH}}`
155
+ 5. Complete task: `{{TASK_COMPLETE_CMD}} {{TASK_ID}}`
156
+ 6. Return summary message
157
+
158
+ ---
159
+
160
+ ## Subagent Protocol
161
+
162
+ @skills/_shared/subagent-protocol-base.md
163
+
164
+ ### Output Requirements
165
+
166
+ 1. MUST write specification to: `docs/specs/{{SPEC_NAME}}.md`
167
+ 2. MUST append ONE line to: `{{MANIFEST_PATH}}`
168
+ 3. MUST return ONLY: "Specification complete. See MANIFEST.jsonl for summary."
169
+ 4. MUST NOT return specification content in response
170
+
171
+ ### Manifest Entry Format
172
+
173
+ ```json
174
+ {"id":"spec-{{SPEC_NAME}}-{{DATE}}","file":"{{DATE}}_spec-{{SPEC_NAME}}.md","title":"Specification: {{TITLE}}","date":"{{DATE}}","status":"complete","agent_type":"specification","topics":["specification","{{DOMAIN}}"],"key_findings":["Defined N requirements in M categories","Established X constraints with enforcement rules","Compliance criteria: summary"],"actionable":true,"needs_followup":["{{IMPLEMENTATION_TASK_IDS}}"],"linked_tasks":["{{TASK_ID}}"]}
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Completion Checklist
180
+
181
+ - [ ] Task started via `{{TASK_START_CMD}}` (if not already started)
182
+ - [ ] RFC 2119 header included
183
+ - [ ] All requirements numbered (REQ-XXX)
184
+ - [ ] All constraints numbered (CON-XXX)
185
+ - [ ] Compliance section defines pass/fail
186
+ - [ ] Specification written to docs/specs/
187
+ - [ ] Manifest entry appended
188
+ - [ ] Task completed via `{{TASK_COMPLETE_CMD}}`
189
+ - [ ] Return summary message only
@@ -0,0 +1,14 @@
1
+ # ct-stickynote
2
+
3
+ Enhanced sticky note workflows for CLEO.
4
+
5
+ ## Features
6
+
7
+ - Templates for common note types
8
+ - Visual board view
9
+ - Batch operations
10
+ - Weekly digest
11
+
12
+ ## Usage
13
+
14
+ See SKILL.md for full documentation.
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: ct-stickynote
3
+ description: Quick ephemeral sticky notes for project-wide capture before formal classification
4
+ version: 1.0.0
5
+ category: productivity
6
+ tier: 0
7
+ protocol: null
8
+ tags: [sticky, notes, capture, quick, ephemeral]
9
+ triggers: [note, sticky, jot, capture]
10
+ compatibility: [claude-code, gemini-cli, codex-cli, opencode]
11
+ dependencies: []
12
+ sharedResources: []
13
+ license: MIT
14
+ ---
15
+
16
+ # Sticky Notes Skill
17
+
18
+ Quick capture ephemeral notes that fill the gap between session notes and formal tasks.
19
+
20
+ ## When to Use
21
+
22
+ Use sticky notes for:
23
+ - Quick thoughts that don't fit a formal task yet
24
+ - Temporary reminders
25
+ - Ideas that need refinement before becoming tasks
26
+ - Notes that span multiple sessions
27
+
28
+ ## Operations
29
+
30
+ | Operation | Usage | Example |
31
+ |-----------|-------|---------|
32
+ | `sticky.add` | Create sticky | `cleo sticky add "Refactor auth middleware" --tag bug --color red` |
33
+ | `sticky.list` | List active | `cleo sticky list --tag bug` |
34
+ | `sticky.show` | Show details | `cleo sticky show SN-001` |
35
+ | `sticky.convert` | Promote to task/memory | `cleo sticky convert SN-001 --to-task` |
36
+ | `sticky.archive` | Archive | `cleo sticky archive SN-001` |
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ cleo skill install library:ct-stickynote
42
+ ```
43
+
44
+ ## Auto-Archive
45
+
46
+ Stickies auto-archive after 30 days if not converted.