@skill-graph/cli 0.5.6

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 (330) hide show
  1. package/CHANGELOG.md +247 -0
  2. package/LICENSE +200 -0
  3. package/NOTICE +62 -0
  4. package/README.md +398 -0
  5. package/SKILL_GRAPH.md +443 -0
  6. package/bin/skill-graph.js +374 -0
  7. package/docs/ADOPTION.md +117 -0
  8. package/docs/CONFORMANCE.md +66 -0
  9. package/docs/PRIMER.md +384 -0
  10. package/docs/QUICKSTART-30MIN.md +333 -0
  11. package/docs/ROUTING-METRICS.md +120 -0
  12. package/docs/SKILL-MD-FORMAT-COMPATIBILITY.md +127 -0
  13. package/docs/SKILL_AUDIT_CHECKLIST.md +199 -0
  14. package/docs/SKILL_AUDIT_LOOP.md +195 -0
  15. package/docs/SKILL_METADATA_PROTOCOL.md +609 -0
  16. package/docs/_archived/marketplace-publication-priority-2026-05-18.md +239 -0
  17. package/docs/adr/0001-predicate-set.md +69 -0
  18. package/docs/adr/0002-json-ld-context.md +82 -0
  19. package/docs/adr/0003-ontoclean-rigidity-tags.md +65 -0
  20. package/docs/adr/0004-persistent-identifiers.md +74 -0
  21. package/docs/adr/0005-freshness-consolidation.md +70 -0
  22. package/docs/adr/0006-revise-predicate-rename.md +105 -0
  23. package/docs/adr/0007-audit-loop-cadence.md +99 -0
  24. package/docs/adr/0008-skill-surface-split-and-curation-policy.md +93 -0
  25. package/docs/category-consumers.md +168 -0
  26. package/docs/concept-map.md +194 -0
  27. package/docs/diagrams/drift-states.mmd +21 -0
  28. package/docs/diagrams/manifest-pipeline.mmd +25 -0
  29. package/docs/diagrams/routing-harness.mmd +41 -0
  30. package/docs/diagrams/starter-graph.mmd +53 -0
  31. package/docs/field-decision-guide.md +315 -0
  32. package/docs/field-rationale.md +211 -0
  33. package/docs/field-reference.generated.md +624 -0
  34. package/docs/field-reference.md +1426 -0
  35. package/docs/glossary.md +190 -0
  36. package/docs/head-noun-glossary.md +63 -0
  37. package/docs/images/audit-phases.png +0 -0
  38. package/docs/images/drift-states.png +0 -0
  39. package/docs/images/graded-mode.png +0 -0
  40. package/docs/images/manifest-pipeline.png +0 -0
  41. package/docs/images/routing-harness.png +0 -0
  42. package/docs/images/skill-anatomy.png +0 -0
  43. package/docs/images/starter-graph.png +0 -0
  44. package/docs/images/system-model.png +0 -0
  45. package/docs/integrations/github-actions.md +155 -0
  46. package/docs/manifest-field-mapping.md +443 -0
  47. package/docs/marketplace-publication-queue.generated.md +240 -0
  48. package/docs/marketplace-release-agent-prompt.md +82 -0
  49. package/docs/marketplace-skill-candidate-list.md +272 -0
  50. package/docs/marketplace-syndication.md +222 -0
  51. package/docs/migration-sample-review.md +155 -0
  52. package/docs/migrations/v4-to-v5.md +168 -0
  53. package/docs/migrations/v5-to-v6.md +221 -0
  54. package/docs/name-exceptions.yaml +37 -0
  55. package/docs/plans/marketplace-p1-public-migration-plan.md +41 -0
  56. package/docs/plans/multi-root-workspace.md +148 -0
  57. package/docs/plans/scripts-roadmap.md +107 -0
  58. package/docs/plans/v4-schema-bump.md +160 -0
  59. package/docs/plans/wave-2-extraction.md +122 -0
  60. package/docs/positioning-vs-marketplaces.md +175 -0
  61. package/docs/proposals/skill-audit-loop-positioning.md +160 -0
  62. package/docs/quality-doctrine.md +138 -0
  63. package/docs/recommended-skills.md +150 -0
  64. package/docs/research/skill-comprehension-eval-research.md +1830 -0
  65. package/docs/research/skill-retrieval-evidence.md +66 -0
  66. package/docs/skill-metadata-protocol.md +471 -0
  67. package/docs/skills-sh-maintainer-cleanup-request.md +80 -0
  68. package/examples/audits/a11y/findings.md +52 -0
  69. package/examples/audits/a11y/scorecard.md +21 -0
  70. package/examples/audits/a11y/verdict.md +44 -0
  71. package/examples/audits/debugging/findings.md +59 -0
  72. package/examples/audits/debugging/scorecard.md +22 -0
  73. package/examples/audits/debugging/verdict.md +33 -0
  74. package/examples/audits/documentation/findings.md +59 -0
  75. package/examples/audits/documentation/scorecard.md +22 -0
  76. package/examples/audits/documentation/verdict.md +33 -0
  77. package/examples/evals/a11y.json +140 -0
  78. package/examples/evals/api-design.json +52 -0
  79. package/examples/evals/code-review.json +52 -0
  80. package/examples/evals/data-modeling.json +52 -0
  81. package/examples/evals/database-migration.json +52 -0
  82. package/examples/evals/debugging.json +118 -0
  83. package/examples/evals/dependency-architecture.json +52 -0
  84. package/examples/evals/design-system-architecture.json +52 -0
  85. package/examples/evals/error-tracking.json +52 -0
  86. package/examples/evals/event-contract-design.json +52 -0
  87. package/examples/evals/form-ux-architecture.json +52 -0
  88. package/examples/evals/framework-fit-analysis.json +52 -0
  89. package/examples/evals/graph-audit.json +139 -0
  90. package/examples/evals/information-architecture.json +52 -0
  91. package/examples/evals/interaction-feedback.json +52 -0
  92. package/examples/evals/interaction-patterns.json +52 -0
  93. package/examples/evals/layout-composition.json +52 -0
  94. package/examples/evals/lint-overlay.json +117 -0
  95. package/examples/evals/microcopy.json +52 -0
  96. package/examples/evals/observability-modeling.json +52 -0
  97. package/examples/evals/pattern-recognition.json +96 -0
  98. package/examples/evals/performance-engineering.json +52 -0
  99. package/examples/evals/refactor.json +128 -0
  100. package/examples/evals/semiotics.json +52 -0
  101. package/examples/evals/skill-infrastructure.json +96 -0
  102. package/examples/evals/skill-router.json +140 -0
  103. package/examples/evals/skill-router.routing.json +113 -0
  104. package/examples/evals/system-interface-contracts.json +52 -0
  105. package/examples/evals/task-analysis.json +52 -0
  106. package/examples/evals/testing-strategy.json +118 -0
  107. package/examples/evals/type-safety.json +249 -0
  108. package/examples/evals/visual-design-foundations.json +52 -0
  109. package/examples/evals/webhook-integration.json +52 -0
  110. package/examples/exports/a11y.skill-md.md +80 -0
  111. package/examples/exports/debugging.skill-md.md +80 -0
  112. package/examples/exports/refactor.skill-md.md +78 -0
  113. package/examples/exports/testing-strategy.skill-md.md +81 -0
  114. package/examples/projects/markdown-static-site/README.md +115 -0
  115. package/examples/projects/markdown-static-site/skills/content-source-router/SKILL.md +131 -0
  116. package/examples/projects/markdown-static-site/skills/image-optimization-pipeline-config/SKILL.md +132 -0
  117. package/examples/projects/markdown-static-site/skills/link-rot-detection/SKILL.md +103 -0
  118. package/examples/projects/markdown-static-site/skills/markdown-post-frontmatter-validation/SKILL.md +133 -0
  119. package/examples/projects/markdown-static-site/skills/migrate-posts-to-v2-frontmatter/SKILL.md +140 -0
  120. package/examples/projects/saas-stripe-postgres/README.md +208 -0
  121. package/examples/projects/saas-stripe-postgres/db/migrations/0004_canonicalize_orders.sql +37 -0
  122. package/examples/projects/saas-stripe-postgres/db/schema.sql +112 -0
  123. package/examples/projects/saas-stripe-postgres/skills/migrate-orders-to-canonical-schema/SKILL.md +149 -0
  124. package/examples/projects/saas-stripe-postgres/skills/nextjs-server-action-validation/SKILL.md +154 -0
  125. package/examples/projects/saas-stripe-postgres/skills/payment-provider-router/SKILL.md +153 -0
  126. package/examples/projects/saas-stripe-postgres/skills/postgres-rls-pattern/SKILL.md +163 -0
  127. package/examples/projects/saas-stripe-postgres/skills/stripe-webhook-signature-verification/SKILL.md +137 -0
  128. package/examples/protocol/skill-metadata-template.md +301 -0
  129. package/examples/protocol/skills.manifest.sample.json +13245 -0
  130. package/examples/skill-metadata-template.md +317 -0
  131. package/examples/skills.manifest.sample.json +13519 -0
  132. package/examples/tests/v3-1-skos-fixture/SKILL.md +93 -0
  133. package/marketplace/README.md +17 -0
  134. package/marketplace/skills/a11y/SKILL.md +66 -0
  135. package/marketplace/skills/acid-fundamentals/SKILL.md +106 -0
  136. package/marketplace/skills/agent-engineering/SKILL.md +386 -0
  137. package/marketplace/skills/agent-eval-design/SKILL.md +55 -0
  138. package/marketplace/skills/ai-native-development/SKILL.md +294 -0
  139. package/marketplace/skills/api-design/SKILL.md +60 -0
  140. package/marketplace/skills/architecture-decision-records/SKILL.md +55 -0
  141. package/marketplace/skills/background-jobs/SKILL.md +265 -0
  142. package/marketplace/skills/bounded-context-mapping/SKILL.md +55 -0
  143. package/marketplace/skills/cap-theorem-tradeoffs/SKILL.md +127 -0
  144. package/marketplace/skills/client-server-boundary/SKILL.md +187 -0
  145. package/marketplace/skills/code-review/SKILL.md +120 -0
  146. package/marketplace/skills/color-system-design/SKILL.md +43 -0
  147. package/marketplace/skills/component-architecture/SKILL.md +126 -0
  148. package/marketplace/skills/compression/SKILL.md +112 -0
  149. package/marketplace/skills/conceptual-modeling/SKILL.md +181 -0
  150. package/marketplace/skills/connection-pooling/SKILL.md +105 -0
  151. package/marketplace/skills/constraint-awareness/SKILL.md +287 -0
  152. package/marketplace/skills/content-monitor/SKILL.md +209 -0
  153. package/marketplace/skills/context-engineering/SKILL.md +320 -0
  154. package/marketplace/skills/context-graph/SKILL.md +174 -0
  155. package/marketplace/skills/context-management/SKILL.md +174 -0
  156. package/marketplace/skills/context-window/SKILL.md +239 -0
  157. package/marketplace/skills/contract-testing/SKILL.md +120 -0
  158. package/marketplace/skills/cron-scheduling/SKILL.md +223 -0
  159. package/marketplace/skills/dark-mode-implementation/SKILL.md +47 -0
  160. package/marketplace/skills/data-modeling/SKILL.md +59 -0
  161. package/marketplace/skills/data-modeling-fundamentals/SKILL.md +117 -0
  162. package/marketplace/skills/database-migration/SKILL.md +429 -0
  163. package/marketplace/skills/debugging/SKILL.md +67 -0
  164. package/marketplace/skills/dependency-architecture/SKILL.md +58 -0
  165. package/marketplace/skills/design-module-composition/SKILL.md +43 -0
  166. package/marketplace/skills/design-system-architecture/SKILL.md +61 -0
  167. package/marketplace/skills/design-thinking/SKILL.md +44 -0
  168. package/marketplace/skills/diagnosis/SKILL.md +296 -0
  169. package/marketplace/skills/diff-analysis/SKILL.md +188 -0
  170. package/marketplace/skills/e2e-test-design/SKILL.md +113 -0
  171. package/marketplace/skills/entity-relationship-modeling/SKILL.md +218 -0
  172. package/marketplace/skills/epistemic-grounding/SKILL.md +112 -0
  173. package/marketplace/skills/error-boundary/SKILL.md +235 -0
  174. package/marketplace/skills/error-tracking/SKILL.md +261 -0
  175. package/marketplace/skills/eval-driven-development/SKILL.md +147 -0
  176. package/marketplace/skills/evaluation/SKILL.md +113 -0
  177. package/marketplace/skills/event-contract-design/SKILL.md +60 -0
  178. package/marketplace/skills/event-storming/SKILL.md +56 -0
  179. package/marketplace/skills/form-ux-architecture/SKILL.md +60 -0
  180. package/marketplace/skills/framework-fit-analysis/SKILL.md +59 -0
  181. package/marketplace/skills/frontend-architecture/SKILL.md +43 -0
  182. package/marketplace/skills/generative-ui/SKILL.md +118 -0
  183. package/marketplace/skills/graph-audit/SKILL.md +81 -0
  184. package/marketplace/skills/guardrails/SKILL.md +118 -0
  185. package/marketplace/skills/hooks-patterns/SKILL.md +185 -0
  186. package/marketplace/skills/http-semantics/SKILL.md +136 -0
  187. package/marketplace/skills/ideation/SKILL.md +41 -0
  188. package/marketplace/skills/indexing-strategy/SKILL.md +108 -0
  189. package/marketplace/skills/information-architecture/SKILL.md +59 -0
  190. package/marketplace/skills/integration-test-design/SKILL.md +111 -0
  191. package/marketplace/skills/intent-recognition/SKILL.md +136 -0
  192. package/marketplace/skills/interaction-feedback/SKILL.md +59 -0
  193. package/marketplace/skills/interaction-patterns/SKILL.md +59 -0
  194. package/marketplace/skills/journey-mapping/SKILL.md +41 -0
  195. package/marketplace/skills/keywords/SKILL.md +213 -0
  196. package/marketplace/skills/knowledge-modeling/SKILL.md +232 -0
  197. package/marketplace/skills/layout-composition/SKILL.md +59 -0
  198. package/marketplace/skills/linguistics/SKILL.md +429 -0
  199. package/marketplace/skills/lint-overlay/SKILL.md +76 -0
  200. package/marketplace/skills/mental-models/SKILL.md +126 -0
  201. package/marketplace/skills/merge-queue/SKILL.md +94 -0
  202. package/marketplace/skills/methodology/SKILL.md +317 -0
  203. package/marketplace/skills/microcopy/SKILL.md +232 -0
  204. package/marketplace/skills/middleware-patterns/SKILL.md +363 -0
  205. package/marketplace/skills/mobile-responsive-ux/SKILL.md +287 -0
  206. package/marketplace/skills/mutation-testing/SKILL.md +112 -0
  207. package/marketplace/skills/naming-conventions/SKILL.md +112 -0
  208. package/marketplace/skills/observability-modeling/SKILL.md +59 -0
  209. package/marketplace/skills/ontology-modeling/SKILL.md +67 -0
  210. package/marketplace/skills/owasp-security/SKILL.md +153 -0
  211. package/marketplace/skills/pattern-recognition/SKILL.md +472 -0
  212. package/marketplace/skills/performance-budgets/SKILL.md +185 -0
  213. package/marketplace/skills/performance-engineering/SKILL.md +58 -0
  214. package/marketplace/skills/performance-testing/SKILL.md +125 -0
  215. package/marketplace/skills/printify/SKILL.md +42 -0
  216. package/marketplace/skills/prioritization/SKILL.md +118 -0
  217. package/marketplace/skills/problem-framing/SKILL.md +41 -0
  218. package/marketplace/skills/problem-locating-solving/SKILL.md +203 -0
  219. package/marketplace/skills/project-knowledge-extraction/SKILL.md +54 -0
  220. package/marketplace/skills/prompt-craft/SKILL.md +134 -0
  221. package/marketplace/skills/prompt-injection-defense/SKILL.md +132 -0
  222. package/marketplace/skills/property-based-testing/SKILL.md +100 -0
  223. package/marketplace/skills/prototyping/SKILL.md +43 -0
  224. package/marketplace/skills/query-optimization/SKILL.md +144 -0
  225. package/marketplace/skills/real-time-updates/SKILL.md +324 -0
  226. package/marketplace/skills/ref-patterns/SKILL.md +284 -0
  227. package/marketplace/skills/refactor/SKILL.md +65 -0
  228. package/marketplace/skills/rendering-models/SKILL.md +142 -0
  229. package/marketplace/skills/replication-patterns/SKILL.md +110 -0
  230. package/marketplace/skills/research-synthesis/SKILL.md +41 -0
  231. package/marketplace/skills/route-handler-design/SKILL.md +347 -0
  232. package/marketplace/skills/schema-evolution/SKILL.md +140 -0
  233. package/marketplace/skills/security-fundamentals/SKILL.md +139 -0
  234. package/marketplace/skills/semantic-center/SKILL.md +194 -0
  235. package/marketplace/skills/semantic-relations/SKILL.md +250 -0
  236. package/marketplace/skills/semantics/SKILL.md +366 -0
  237. package/marketplace/skills/semiotics/SKILL.md +230 -0
  238. package/marketplace/skills/seo-strategy/SKILL.md +260 -0
  239. package/marketplace/skills/server-actions-design/SKILL.md +243 -0
  240. package/marketplace/skills/server-components-design/SKILL.md +190 -0
  241. package/marketplace/skills/sharding-strategy/SKILL.md +123 -0
  242. package/marketplace/skills/shopify/SKILL.md +42 -0
  243. package/marketplace/skills/skill-infrastructure/SKILL.md +320 -0
  244. package/marketplace/skills/skill-router/SKILL.md +71 -0
  245. package/marketplace/skills/skill-scaffold/SKILL.md +105 -0
  246. package/marketplace/skills/snapshot-testing/SKILL.md +120 -0
  247. package/marketplace/skills/spec-driven-development/SKILL.md +148 -0
  248. package/marketplace/skills/state-machine-modeling/SKILL.md +56 -0
  249. package/marketplace/skills/state-management/SKILL.md +134 -0
  250. package/marketplace/skills/streaming-architecture/SKILL.md +194 -0
  251. package/marketplace/skills/summarization/SKILL.md +156 -0
  252. package/marketplace/skills/suspense-patterns/SKILL.md +265 -0
  253. package/marketplace/skills/system-interface-contracts/SKILL.md +59 -0
  254. package/marketplace/skills/task-analysis/SKILL.md +201 -0
  255. package/marketplace/skills/taxonomy-design/SKILL.md +66 -0
  256. package/marketplace/skills/test-coverage-strategy/SKILL.md +108 -0
  257. package/marketplace/skills/test-doubles-design/SKILL.md +98 -0
  258. package/marketplace/skills/test-driven-development/SKILL.md +96 -0
  259. package/marketplace/skills/testing-strategy/SKILL.md +67 -0
  260. package/marketplace/skills/theme-system-design/SKILL.md +43 -0
  261. package/marketplace/skills/tool-call-flow/SKILL.md +229 -0
  262. package/marketplace/skills/tool-call-strategy/SKILL.md +292 -0
  263. package/marketplace/skills/transaction-isolation/SKILL.md +98 -0
  264. package/marketplace/skills/type-safety/SKILL.md +177 -0
  265. package/marketplace/skills/typography-system/SKILL.md +43 -0
  266. package/marketplace/skills/usability-testing/SKILL.md +43 -0
  267. package/marketplace/skills/user-research/SKILL.md +43 -0
  268. package/marketplace/skills/vercel-composition-patterns/SKILL.md +157 -0
  269. package/marketplace/skills/version-control/SKILL.md +233 -0
  270. package/marketplace/skills/visual-design-foundations/SKILL.md +59 -0
  271. package/marketplace/skills/visual-hierarchy/SKILL.md +43 -0
  272. package/marketplace/skills/webhook-integration/SKILL.md +331 -0
  273. package/marketplace/skills/writing-humanizer/SKILL.md +380 -0
  274. package/package.json +67 -0
  275. package/schemas/manifest.schema.json +811 -0
  276. package/schemas/manifest.v2.schema.json +164 -0
  277. package/schemas/manifest.v3.schema.json +758 -0
  278. package/schemas/manifest.v4.schema.json +755 -0
  279. package/schemas/manifest.v5.schema.json +755 -0
  280. package/schemas/manifest.v6.schema.json +811 -0
  281. package/schemas/skill.context.jsonld +279 -0
  282. package/schemas/skill.schema.json +919 -0
  283. package/schemas/skill.v2.schema.json +201 -0
  284. package/schemas/skill.v3.schema.json +827 -0
  285. package/schemas/skill.v4.schema.json +822 -0
  286. package/schemas/skill.v5.schema.json +830 -0
  287. package/schemas/skill.v6.schema.json +946 -0
  288. package/schemas/vocabulary/keywords.json +180 -0
  289. package/schemas/vocabulary/workspace_tags.json +23 -0
  290. package/scripts/__tests__/migrate-skill-v2-to-v3.test.js +161 -0
  291. package/scripts/__tests__/migrate-skill-v3-to-v4.test.js +158 -0
  292. package/scripts/__tests__/test-export-parser-drift.js +149 -0
  293. package/scripts/__tests__/test-marketplace-export.js +114 -0
  294. package/scripts/__tests__/test-router-paths.js +82 -0
  295. package/scripts/__tests__/test-stability-promotion.js +244 -0
  296. package/scripts/__tests__/test-v3-1-alias-contract.js +109 -0
  297. package/scripts/__tests__/test-v3-1-skos-runtime.js +116 -0
  298. package/scripts/backfill-schema-version.js +198 -0
  299. package/scripts/build-field-reference.js +160 -0
  300. package/scripts/build-retrieval-baseline.js +511 -0
  301. package/scripts/check-markdown-links.js +211 -0
  302. package/scripts/check-protocol-consistency.js +979 -0
  303. package/scripts/export-marketplace-skills.js +610 -0
  304. package/scripts/export-skill.js +374 -0
  305. package/scripts/generate-manifest.js +787 -0
  306. package/scripts/lib/alias-contract.js +83 -0
  307. package/scripts/lib/audit-prompt-builder.js +771 -0
  308. package/scripts/lib/mock-grader.js +134 -0
  309. package/scripts/lib/parse-frontmatter.js +429 -0
  310. package/scripts/lib/roots.js +119 -0
  311. package/scripts/lint/check-archetype-sections.js +185 -0
  312. package/scripts/lint/check-category-enum.js +83 -0
  313. package/scripts/lint/check-routing-eval.js +146 -0
  314. package/scripts/lint/check-routing-quality.js +211 -0
  315. package/scripts/lint/check-stability-promotion.js +220 -0
  316. package/scripts/lint/format-code-frame.js +206 -0
  317. package/scripts/marketplace-install.js +125 -0
  318. package/scripts/migrate-category-to-enum.js +169 -0
  319. package/scripts/migrate-skill-v2-to-v3.js +424 -0
  320. package/scripts/migrate-skill-v3-to-v4.js +200 -0
  321. package/scripts/migrate-skill-v5-to-v6.js +304 -0
  322. package/scripts/restructure-by-category.js +85 -0
  323. package/scripts/seed-publication-classification.js +282 -0
  324. package/scripts/skill-audit.js +893 -0
  325. package/scripts/skill-graph-drift.js +483 -0
  326. package/scripts/skill-graph-route.js +766 -0
  327. package/scripts/skill-graph-routing-eval.js +393 -0
  328. package/scripts/skill-lint.js +1317 -0
  329. package/scripts/skill-overlap.js +213 -0
  330. package/scripts/verify-skill-md-export.js +201 -0
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Skill Graph overlap detector.
4
+ *
5
+ * Detects collisions across the three activation surfaces that a router
6
+ * consults to decide which skill claims an inbound request:
7
+ *
8
+ * 1. triggers — exact label match. A duplicate is a routing ambiguity
9
+ * the router cannot resolve (no semantic fallback for identical
10
+ * labels). Duplicates are hard errors.
11
+ *
12
+ * 2. keywords — semantic match. Some overlap is natural (e.g. both an
13
+ * a11y skill and a testing-strategy skill might reference "keyboard").
14
+ * Excessive overlap is a signal that activation will mis-fire;
15
+ * emitted as warnings for author review.
16
+ *
17
+ * 3. paths — glob match. Two skills claiming the same file surface
18
+ * will both activate when that surface is touched. Exact-string
19
+ * glob duplicates are flagged as warnings; partial-overlap
20
+ * (e.g. `src/**` vs `src/auth/**`) is out of scope for a
21
+ * dependency-free implementation.
22
+ *
23
+ * Complementary to skill-lint.js, which validates per-skill correctness.
24
+ * This script validates cross-skill routing hygiene.
25
+ *
26
+ * Self-contained. Only uses Node built-ins — no external dependencies.
27
+ * Exit 0 on success (no errors), 1 on any trigger/keyword/path error.
28
+ * With --strict, warnings are promoted to errors.
29
+ *
30
+ * Usage:
31
+ * node scripts/skill-overlap.js
32
+ * node scripts/skill-overlap.js --include-template # also scan skill-metadata-template.md
33
+ * node scripts/skill-overlap.js --strict # warnings become errors
34
+ * node scripts/skill-overlap.js --json # machine-readable output
35
+ * node scripts/skill-overlap.js --no-color # plain CI output
36
+ */
37
+
38
+ 'use strict';
39
+
40
+ const fs = require('fs');
41
+ const path = require('path');
42
+ const { parseFrontmatter } = require('./lib/parse-frontmatter');
43
+ const { loadWorkspaceConfig, resolveSkillRoots, workspaceRoot } = require('./lib/roots');
44
+
45
+ const REPO_ROOT = workspaceRoot();
46
+ const TEMPLATE_PATH = path.join(REPO_ROOT, 'examples', 'skill-metadata-template.md');
47
+ const WORKSPACE_CONFIG = loadWorkspaceConfig(REPO_ROOT, msg => process.stderr.write(`WARN ${msg}\n`));
48
+ const SKILL_ROOTS = resolveSkillRoots(REPO_ROOT, WORKSPACE_CONFIG);
49
+
50
+ // ANSI color helpers — matched to scripts/lint/format-code-frame.js palette.
51
+ const C = {
52
+ red: '\x1b[31m',
53
+ yellow: '\x1b[33m',
54
+ green: '\x1b[32m',
55
+ dim: '\x1b[2m',
56
+ bold: '\x1b[1m',
57
+ reset: '\x1b[0m',
58
+ };
59
+ function paint(s, color, enabled) {
60
+ return enabled ? color + s + C.reset : s;
61
+ }
62
+
63
+ // ---------------------------------------------------------------------------
64
+ // Skill loading
65
+ // ---------------------------------------------------------------------------
66
+
67
+ function loadSkills(includeTemplate) {
68
+ const skills = [];
69
+ for (const root of SKILL_ROOTS) {
70
+ if (!fs.existsSync(root.absPath)) continue;
71
+ for (const name of fs.readdirSync(root.absPath).sort()) {
72
+ const skillMd = path.join(root.absPath, name, 'SKILL.md');
73
+ if (!fs.existsSync(skillMd)) continue;
74
+ const text = fs.readFileSync(skillMd, 'utf8');
75
+ const fm = parseFrontmatter(text);
76
+ if (fm && fm.name) skills.push({ file: skillMd, fm });
77
+ }
78
+ }
79
+ if (includeTemplate && fs.existsSync(TEMPLATE_PATH)) {
80
+ const text = fs.readFileSync(TEMPLATE_PATH, 'utf8');
81
+ const fm = parseFrontmatter(text);
82
+ if (fm && fm.name) skills.push({ file: TEMPLATE_PATH, fm });
83
+ }
84
+ return skills;
85
+ }
86
+
87
+ // ---------------------------------------------------------------------------
88
+ // Overlap detection helpers
89
+ // ---------------------------------------------------------------------------
90
+
91
+ /**
92
+ * Build a map of value → [skill-name, ...] for a given frontmatter array field.
93
+ * Case-insensitive (activation surfaces are case-insensitive in practice).
94
+ * Returns only entries that appear in 2+ skills.
95
+ */
96
+ function detectDuplicates(skills, field) {
97
+ const map = new Map();
98
+ for (const { fm } of skills) {
99
+ const list = Array.isArray(fm[field]) ? fm[field] : [];
100
+ for (const raw of list) {
101
+ if (typeof raw !== 'string') continue;
102
+ const key = raw.trim().toLowerCase();
103
+ if (!key) continue;
104
+ if (!map.has(key)) map.set(key, { value: raw, owners: [] });
105
+ // Avoid double-listing a skill that has the same value twice in its own array.
106
+ const entry = map.get(key);
107
+ if (!entry.owners.includes(fm.name)) entry.owners.push(fm.name);
108
+ }
109
+ }
110
+ const dups = [];
111
+ for (const [, entry] of map) {
112
+ if (entry.owners.length >= 2) dups.push(entry);
113
+ }
114
+ return dups.sort((a, b) => a.value.localeCompare(b.value));
115
+ }
116
+
117
+ // ---------------------------------------------------------------------------
118
+ // Reporting
119
+ // ---------------------------------------------------------------------------
120
+
121
+ function formatDuplicateLine(kind, entry, useColor) {
122
+ const label = paint(kind, C.bold, useColor);
123
+ const value = paint(JSON.stringify(entry.value), C.yellow, useColor);
124
+ const owners = entry.owners.map(o => paint(o, C.dim, useColor)).join(', ');
125
+ return ` ${label} ${value} — in: ${owners}`;
126
+ }
127
+
128
+ function printReport({ triggerDups, keywordDups, pathDups, strict, useColor }) {
129
+ // Triggers — always error.
130
+ if (triggerDups.length === 0) {
131
+ console.log(paint('OK ', C.green, useColor) + '[triggers] no duplicate trigger labels');
132
+ } else {
133
+ console.error(paint('FAIL ', C.red, useColor) + `[triggers] ${triggerDups.length} duplicate label(s)`);
134
+ for (const d of triggerDups) console.error(formatDuplicateLine('trigger', d, useColor));
135
+ }
136
+
137
+ // Keywords — warning (or error in --strict).
138
+ if (keywordDups.length === 0) {
139
+ console.log(paint('OK ', C.green, useColor) + '[keywords] no duplicate keyword entries');
140
+ } else {
141
+ const level = strict ? 'FAIL ' : 'WARN ';
142
+ const levelColor = strict ? C.red : C.yellow;
143
+ const stream = strict ? console.error : console.log;
144
+ stream.call(console, paint(level, levelColor, useColor) + `[keywords] ${keywordDups.length} duplicate keyword(s)`);
145
+ for (const d of keywordDups) stream.call(console, formatDuplicateLine('keyword', d, useColor));
146
+ }
147
+
148
+ // Paths — warning (or error in --strict).
149
+ if (pathDups.length === 0) {
150
+ console.log(paint('OK ', C.green, useColor) + '[paths] no duplicate path globs');
151
+ } else {
152
+ const level = strict ? 'FAIL ' : 'WARN ';
153
+ const levelColor = strict ? C.red : C.yellow;
154
+ const stream = strict ? console.error : console.log;
155
+ stream.call(console, paint(level, levelColor, useColor) + `[paths] ${pathDups.length} duplicate glob(s)`);
156
+ for (const d of pathDups) stream.call(console, formatDuplicateLine('path', d, useColor));
157
+ }
158
+ }
159
+
160
+ // ---------------------------------------------------------------------------
161
+ // Main
162
+ // ---------------------------------------------------------------------------
163
+
164
+ function main() {
165
+ const argv = process.argv.slice(2);
166
+ const strict = argv.includes('--strict');
167
+ const includeTemplate = argv.includes('--include-template');
168
+ const jsonOut = argv.includes('--json');
169
+ const noColor = argv.includes('--no-color');
170
+ const useColor = !noColor && process.stdout.isTTY;
171
+
172
+ const skills = loadSkills(includeTemplate);
173
+ if (skills.length === 0) {
174
+ console.error('No skills found to analyze.');
175
+ process.exit(1);
176
+ }
177
+
178
+ const triggerDups = detectDuplicates(skills, 'triggers');
179
+ const keywordDups = detectDuplicates(skills, 'keywords');
180
+ const pathDups = detectDuplicates(skills, 'paths');
181
+
182
+ const triggerErrors = triggerDups.length;
183
+ const warnCount = keywordDups.length + pathDups.length;
184
+ const totalErrors = triggerErrors + (strict ? warnCount : 0);
185
+
186
+ if (jsonOut) {
187
+ const payload = {
188
+ skills_scanned: skills.length,
189
+ triggers: triggerDups,
190
+ keywords: keywordDups,
191
+ paths: pathDups,
192
+ summary: {
193
+ errors: triggerErrors,
194
+ warnings: strict ? 0 : warnCount,
195
+ strict,
196
+ },
197
+ };
198
+ process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
199
+ process.exit(totalErrors > 0 ? 1 : 0);
200
+ }
201
+
202
+ printReport({ triggerDups, keywordDups, pathDups, strict, useColor });
203
+
204
+ const tail = strict && warnCount > 0
205
+ ? ` (--strict: ${warnCount} warning(s) promoted to errors)`
206
+ : warnCount > 0
207
+ ? `, ${warnCount} warning(s)`
208
+ : '';
209
+ console.log(`\n${skills.length} skill(s) analyzed, ${totalErrors} error(s)${tail}.`);
210
+ process.exit(totalErrors > 0 ? 1 : 0);
211
+ }
212
+
213
+ main();
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Verify that Skill Graph skills export to plain SKILL.md frontmatter.
4
+ *
5
+ * This is intentionally stricter than "export script exits 0": it rebuilds the
6
+ * exported frontmatter in memory and validates the structural shape that broad
7
+ * SKILL.md-compatible runtimes expect at the top level.
8
+ *
9
+ * Usage:
10
+ * node scripts/verify-skill-md-export.js
11
+ * node scripts/verify-skill-md-export.js skills/documentation
12
+ * node scripts/verify-skill-md-export.js --plain marketplace/skills
13
+ * node scripts/verify-skill-md-export.js --json
14
+ *
15
+ * Self-contained. Only uses Node built-ins.
16
+ */
17
+
18
+ 'use strict';
19
+
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+ const { parseFrontmatter } = require('./lib/parse-frontmatter');
23
+ const { workspaceRoot, loadWorkspaceConfig, resolveSkillRoots } = require('./lib/roots');
24
+ const { buildExportedSkill } = require('./export-skill');
25
+
26
+ const REPO_ROOT = workspaceRoot();
27
+ const WORKSPACE_CONFIG = loadWorkspaceConfig(REPO_ROOT, msg => process.stderr.write(`WARN ${msg}\n`));
28
+ const SKILL_ROOTS = resolveSkillRoots(REPO_ROOT, WORKSPACE_CONFIG);
29
+ // Primary skill root — first configured root, or local skills/ as fallback.
30
+ const DEFAULT_SKILLS_DIR = SKILL_ROOTS[0].absPath;
31
+ const TOP_LEVEL_FIELDS = new Set(['name', 'description', 'license', 'compatibility', 'metadata', 'allowed-tools']);
32
+
33
+ function repoRelative(filePath) {
34
+ return path.relative(REPO_ROOT, filePath).split(path.sep).join('/');
35
+ }
36
+
37
+ function collectSkillFiles(inputs) {
38
+ const files = [];
39
+ const roots = inputs.length > 0 ? inputs : [DEFAULT_SKILLS_DIR];
40
+
41
+ for (const input of roots) {
42
+ const abs = path.resolve(input);
43
+ if (!fs.existsSync(abs)) continue;
44
+ const stat = fs.statSync(abs);
45
+ if (stat.isFile() && path.basename(abs) === 'SKILL.md') {
46
+ files.push(abs);
47
+ continue;
48
+ }
49
+ if (!stat.isDirectory()) continue;
50
+
51
+ const direct = path.join(abs, 'SKILL.md');
52
+ if (fs.existsSync(direct)) {
53
+ files.push(direct);
54
+ continue;
55
+ }
56
+
57
+ for (const entry of fs.readdirSync(abs, { withFileTypes: true })) {
58
+ if (!entry.isDirectory()) continue;
59
+ const skillMd = path.join(abs, entry.name, 'SKILL.md');
60
+ if (fs.existsSync(skillMd)) files.push(skillMd);
61
+ }
62
+ }
63
+
64
+ return files.sort((a, b) => repoRelative(a).localeCompare(repoRelative(b)));
65
+ }
66
+
67
+ function validateName(name) {
68
+ if (typeof name !== 'string' || name.length === 0) {
69
+ return ['name must be a non-empty string'];
70
+ }
71
+ if (/[/:]/.test(name)) {
72
+ return ['name must not contain "/" or ":" in exported SKILL.md frontmatter'];
73
+ }
74
+ return [];
75
+ }
76
+
77
+ function validateExportedFrontmatter(fm) {
78
+ const errors = [];
79
+
80
+ for (const key of Object.keys(fm)) {
81
+ if (!TOP_LEVEL_FIELDS.has(key)) {
82
+ errors.push(`unexpected top-level field "${key}"`);
83
+ }
84
+ }
85
+
86
+ errors.push(...validateName(fm.name));
87
+
88
+ if (typeof fm.description !== 'string' || fm.description.length === 0) {
89
+ errors.push('description must be a non-empty string');
90
+ }
91
+
92
+ if (fm.license !== undefined && typeof fm.license !== 'string') {
93
+ errors.push('license must be a string when present');
94
+ }
95
+
96
+ if (fm.compatibility !== undefined) {
97
+ if (typeof fm.compatibility !== 'string' || fm.compatibility.length === 0) {
98
+ errors.push('compatibility must be a non-empty string when present');
99
+ }
100
+ }
101
+
102
+ if (fm['allowed-tools'] !== undefined && typeof fm['allowed-tools'] !== 'string') {
103
+ errors.push('allowed-tools must be a space-separated string when present');
104
+ }
105
+
106
+ if (fm.metadata !== undefined) {
107
+ if (!fm.metadata || typeof fm.metadata !== 'object' || Array.isArray(fm.metadata)) {
108
+ errors.push('metadata must be a key-value object when present');
109
+ } else {
110
+ for (const [key, value] of Object.entries(fm.metadata)) {
111
+ if (key.length === 0) errors.push('metadata keys must be non-empty strings');
112
+ if (typeof value !== 'string') errors.push(`metadata.${key} must be a string`);
113
+ }
114
+ }
115
+ }
116
+
117
+ return { errors };
118
+ }
119
+
120
+ function verifySkillFile(skillMd, options = {}) {
121
+ const sourceText = fs.readFileSync(skillMd, 'utf8');
122
+ const sourceFm = parseFrontmatter(sourceText);
123
+ if (!sourceFm) {
124
+ return {
125
+ file: repoRelative(skillMd),
126
+ ok: false,
127
+ errors: ['source SKILL.md has no parseable frontmatter'],
128
+ };
129
+ }
130
+
131
+ if (options.plain) {
132
+ const validation = validateExportedFrontmatter(sourceFm);
133
+ return {
134
+ file: repoRelative(skillMd),
135
+ ok: validation.errors.length === 0,
136
+ errors: validation.errors,
137
+ };
138
+ }
139
+
140
+ const exportedText = buildExportedSkill(sourceText);
141
+ if (!exportedText) {
142
+ return {
143
+ file: repoRelative(skillMd),
144
+ ok: false,
145
+ errors: ['exported SKILL.md could not be built from source'],
146
+ };
147
+ }
148
+ const exportedFm = parseFrontmatter(exportedText);
149
+ const validation = exportedFm
150
+ ? validateExportedFrontmatter(exportedFm)
151
+ : { errors: ['exported SKILL.md has no parseable frontmatter'] };
152
+
153
+ return {
154
+ file: repoRelative(skillMd),
155
+ ok: validation.errors.length === 0,
156
+ errors: validation.errors,
157
+ };
158
+ }
159
+
160
+ function printText(results) {
161
+ for (const r of results) {
162
+ if (r.ok) continue;
163
+ process.stdout.write(`FAIL ${r.file}\n`);
164
+ for (const e of r.errors) process.stdout.write(` - ${e}\n`);
165
+ }
166
+ const passing = results.filter(r => r.ok).length;
167
+ const failing = results.length - passing;
168
+ process.stdout.write(`${results.length} skill export(s): ${passing} PASS, ${failing} FAIL.\n`);
169
+ }
170
+
171
+ function main() {
172
+ const args = process.argv.slice(2);
173
+ const outputJson = args.includes('--json');
174
+ const quiet = args.includes('--quiet');
175
+ const plain = args.includes('--plain') || args.includes('--as-is');
176
+ const inputs = args.filter(a => !a.startsWith('--'));
177
+ const skillFiles = collectSkillFiles(inputs);
178
+
179
+ if (skillFiles.length === 0) {
180
+ process.stderr.write('ERROR no SKILL.md files found to verify.\n');
181
+ process.exit(1);
182
+ }
183
+
184
+ const results = skillFiles.map(skillFile => verifySkillFile(skillFile, { plain }));
185
+
186
+ if (outputJson) {
187
+ process.stdout.write(JSON.stringify({ results }, null, 2) + '\n');
188
+ } else if (!quiet) {
189
+ printText(results);
190
+ }
191
+
192
+ process.exit(results.some(r => !r.ok) ? 1 : 0);
193
+ }
194
+
195
+ module.exports = {
196
+ collectSkillFiles,
197
+ validateExportedFrontmatter,
198
+ verifySkillFile,
199
+ };
200
+
201
+ if (require.main === module) main();