@amsterdamdatalabs/enact-extensions 0.1.37 → 0.1.47

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 (577) hide show
  1. package/README.md +67 -108
  2. package/bundled-extensions.json +6 -0
  3. package/dist/create/enact.d.ts.map +1 -1
  4. package/dist/create/enact.js +2 -0
  5. package/dist/create/enact.js.map +1 -1
  6. package/dist/create/index.d.ts +3 -0
  7. package/dist/create/index.d.ts.map +1 -1
  8. package/dist/create/index.js +6 -0
  9. package/dist/create/index.js.map +1 -1
  10. package/dist/create/kimi.d.ts +16 -0
  11. package/dist/create/kimi.d.ts.map +1 -0
  12. package/dist/create/kimi.js +87 -0
  13. package/dist/create/kimi.js.map +1 -0
  14. package/dist/create/opencode.d.ts +129 -0
  15. package/dist/create/opencode.d.ts.map +1 -0
  16. package/dist/create/opencode.js +696 -0
  17. package/dist/create/opencode.js.map +1 -0
  18. package/dist/index.d.ts +12 -16
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +10 -10
  21. package/dist/index.js.map +1 -1
  22. package/dist/internal/agents.d.ts +12 -2
  23. package/dist/internal/agents.d.ts.map +1 -1
  24. package/dist/internal/agents.js +119 -3
  25. package/dist/internal/agents.js.map +1 -1
  26. package/dist/internal/extensions-table.d.ts +58 -0
  27. package/dist/internal/extensions-table.d.ts.map +1 -0
  28. package/dist/internal/extensions-table.js +134 -0
  29. package/dist/internal/extensions-table.js.map +1 -0
  30. package/dist/internal/file-ops.d.ts +36 -0
  31. package/dist/internal/file-ops.d.ts.map +1 -0
  32. package/dist/internal/file-ops.js +89 -0
  33. package/dist/internal/file-ops.js.map +1 -0
  34. package/dist/internal/hooks.d.ts +56 -0
  35. package/dist/internal/hooks.d.ts.map +1 -0
  36. package/dist/internal/hooks.js +128 -0
  37. package/dist/internal/hooks.js.map +1 -0
  38. package/dist/internal/json-merge.d.ts +63 -0
  39. package/dist/internal/json-merge.d.ts.map +1 -0
  40. package/dist/internal/json-merge.js +122 -0
  41. package/dist/internal/json-merge.js.map +1 -0
  42. package/dist/internal/lock-ownership.d.ts +41 -0
  43. package/dist/internal/lock-ownership.d.ts.map +1 -0
  44. package/dist/internal/lock-ownership.js +24 -0
  45. package/dist/internal/lock-ownership.js.map +1 -0
  46. package/dist/internal/platform.d.ts.map +1 -1
  47. package/dist/internal/platform.js +90 -4
  48. package/dist/internal/platform.js.map +1 -1
  49. package/dist/internal/schema.js.map +1 -1
  50. package/dist/internal/toml-block.d.ts +59 -0
  51. package/dist/internal/toml-block.d.ts.map +1 -0
  52. package/dist/internal/toml-block.js +113 -0
  53. package/dist/internal/toml-block.js.map +1 -0
  54. package/dist/internal/types.d.ts +2 -1
  55. package/dist/internal/types.d.ts.map +1 -1
  56. package/dist/projection.d.ts +47 -0
  57. package/dist/projection.d.ts.map +1 -0
  58. package/dist/projection.js +180 -0
  59. package/dist/projection.js.map +1 -0
  60. package/dist/validate/index.d.ts +9 -4
  61. package/dist/validate/index.d.ts.map +1 -1
  62. package/dist/validate/index.js +56 -7
  63. package/dist/validate/index.js.map +1 -1
  64. package/dist/validate/kimi.d.ts +3 -0
  65. package/dist/validate/kimi.d.ts.map +1 -0
  66. package/dist/validate/kimi.js +11 -0
  67. package/dist/validate/kimi.js.map +1 -0
  68. package/dist/validate/opencode.d.ts +3 -0
  69. package/dist/validate/opencode.d.ts.map +1 -0
  70. package/dist/validate/opencode.js +11 -0
  71. package/dist/validate/opencode.js.map +1 -0
  72. package/dist/validate/run.d.ts +39 -0
  73. package/dist/validate/run.d.ts.map +1 -0
  74. package/dist/validate/run.js +63 -0
  75. package/dist/validate/run.js.map +1 -0
  76. package/extensions/enact-loop/.agents/plugin.json +12 -8
  77. package/extensions/enact-loop/scripts/validate.mjs +113 -2
  78. package/extensions/enact-loop/skills/enact-loop/SKILL.md +36 -466
  79. package/extensions/enact-loop/skills/enact-loop/references/loop-contract.md +114 -0
  80. package/extensions/enact-loop/skills/loop-budget/SKILL.md +57 -0
  81. package/extensions/enact-loop/skills/loop-intake/SKILL.md +59 -0
  82. package/extensions/enact-repo/.agents/plugin.json +55 -0
  83. package/extensions/enact-repo/.mcp.json +20 -0
  84. package/extensions/enact-repo/README.md +90 -0
  85. package/extensions/enact-repo/hooks/hooks.json +98 -0
  86. package/extensions/enact-repo/lean-ctx/config.toml +35 -0
  87. package/extensions/enact-repo/lean-ctx/layout.toml +5 -0
  88. package/extensions/enact-repo/skills/aca-sandboxes/SKILL.md +53 -0
  89. package/extensions/enact-repo/skills/aca-sandboxes/references/capabilities.md +52 -0
  90. package/extensions/enact-repo/skills/aca-sandboxes/references/install.md +87 -0
  91. package/extensions/enact-repo/skills/aca-sandboxes/references/prerequisites.md +52 -0
  92. package/extensions/enact-repo/skills/aca-sandboxes/references/quickstart.md +164 -0
  93. package/extensions/enact-repo/skills/aca-sandboxes/references/reference-advanced.md +251 -0
  94. package/extensions/enact-repo/skills/aca-sandboxes/references/reference.md +287 -0
  95. package/extensions/enact-repo/skills/aca-sandboxes/references/response-cues.md +71 -0
  96. package/extensions/enact-repo/skills/aca-sandboxes/references/scenarios.md +219 -0
  97. package/extensions/enact-repo/skills/aca-sandboxes/version.json +4 -0
  98. package/extensions/enact-repo/skills/backend-remove-deadcode/SKILL.md +96 -0
  99. package/extensions/enact-repo/skills/cmux-leader/SKILL.md +61 -0
  100. package/extensions/enact-repo/skills/cmux-send/SKILL.md +61 -0
  101. package/extensions/enact-repo/skills/code-review/SKILL.md +100 -0
  102. package/extensions/enact-repo/skills/codebase-design/SKILL.md +98 -0
  103. package/extensions/enact-repo/skills/codebase-design/references/DEEPENING.md +37 -0
  104. package/extensions/enact-repo/skills/codebase-design/references/DESIGN-IT-TWICE.md +44 -0
  105. package/extensions/enact-repo/skills/codebase-design/references/TESTABILITY.md +29 -0
  106. package/extensions/enact-repo/skills/computer-use/SKILL.md +76 -0
  107. package/extensions/enact-repo/skills/debug-issue/SKILL.md +40 -0
  108. package/extensions/enact-repo/skills/diagnosing-bugs/SKILL.md +100 -0
  109. package/extensions/enact-repo/skills/diagnosing-bugs/references/FEEDBACK-LOOP-TACTICS.md +34 -0
  110. package/extensions/enact-repo/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
  111. package/extensions/enact-repo/skills/domain-modeling/SKILL.md +86 -0
  112. package/extensions/enact-repo/skills/domain-modeling/references/ADR-FORMAT.md +47 -0
  113. package/extensions/enact-repo/skills/domain-modeling/references/CONTEXT-FORMAT.md +60 -0
  114. package/extensions/enact-repo/skills/explore-codebase/SKILL.md +41 -0
  115. package/extensions/enact-repo/skills/frontend-design/LICENSE.txt +177 -0
  116. package/extensions/enact-repo/skills/frontend-design/SKILL.md +56 -0
  117. package/extensions/enact-repo/skills/grill-me/SKILL.md +20 -0
  118. package/extensions/enact-repo/skills/grill-with-docs/SKILL.md +20 -0
  119. package/extensions/enact-repo/skills/handoff/SKILL.md +28 -0
  120. package/extensions/enact-repo/skills/http-contract-migration/SKILL.md +74 -0
  121. package/extensions/enact-repo/skills/http-contract-migration/references/compatibility-and-gates.md +26 -0
  122. package/extensions/enact-repo/skills/http-contract-migration/references/hexagonal-boundary.md +16 -0
  123. package/extensions/enact-repo/skills/http-contract-migration/references/publication-and-adoption.md +31 -0
  124. package/extensions/enact-repo/skills/http-contract-migration/references/wire-fidelity-and-dto.md +31 -0
  125. package/extensions/enact-repo/skills/implement/SKILL.md +28 -0
  126. package/extensions/enact-repo/skills/improve-codebase-architecture/SKILL.md +80 -0
  127. package/extensions/enact-repo/skills/improve-codebase-architecture/references/HTML-REPORT.md +123 -0
  128. package/extensions/enact-repo/skills/interaction-design/SKILL.md +30 -0
  129. package/extensions/enact-repo/skills/interaction-design/references/Workflow.md +146 -0
  130. package/extensions/enact-repo/skills/interaction-design/references/animation-libraries-gsap.md +220 -0
  131. package/extensions/enact-repo/skills/interaction-design/references/animation-libraries-native.md +110 -0
  132. package/extensions/enact-repo/skills/interaction-design/references/animation-libraries.md +178 -0
  133. package/extensions/enact-repo/skills/interaction-design/references/interaction-patterns.md +166 -0
  134. package/extensions/enact-repo/skills/interaction-design/references/microinteraction-patterns-advanced.md +285 -0
  135. package/extensions/enact-repo/skills/interaction-design/references/microinteraction-patterns.md +254 -0
  136. package/extensions/enact-repo/skills/interaction-design/references/scroll-animations-reveals.md +213 -0
  137. package/extensions/enact-repo/skills/interaction-design/references/scroll-animations.md +209 -0
  138. package/extensions/enact-repo/skills/lean-ctx/SKILL.md +81 -0
  139. package/extensions/enact-repo/skills/orca-cli/SKILL.md +88 -0
  140. package/extensions/enact-repo/skills/orchestration/SKILL.md +80 -0
  141. package/extensions/enact-repo/skills/prototype/SKILL.md +37 -0
  142. package/extensions/enact-repo/skills/prototype/references/LOGIC.md +79 -0
  143. package/extensions/enact-repo/skills/prototype/references/UI.md +112 -0
  144. package/extensions/enact-repo/skills/refactor-safely/SKILL.md +44 -0
  145. package/extensions/enact-repo/skills/remove-deadcode/SKILL.md +54 -0
  146. package/extensions/enact-repo/skills/research/SKILL.md +27 -0
  147. package/extensions/enact-repo/skills/resolving-merge-conflicts/SKILL.md +28 -0
  148. package/extensions/enact-repo/skills/review-changes/SKILL.md +45 -0
  149. package/extensions/enact-repo/skills/tailwind-4-docs/SKILL.md +88 -0
  150. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/accent-color.mdx +149 -0
  151. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/adding-custom-styles.mdx +635 -0
  152. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/align-content.mdx +300 -0
  153. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/align-items.mdx +241 -0
  154. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/align-self.mdx +294 -0
  155. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/animation.mdx +286 -0
  156. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/appearance.mdx +180 -0
  157. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/aspect-ratio.mdx +98 -0
  158. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-blur.mdx +109 -0
  159. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-brightness.mdx +82 -0
  160. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-contrast.mdx +82 -0
  161. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-grayscale.mdx +99 -0
  162. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-hue-rotate.mdx +173 -0
  163. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-invert.mdx +99 -0
  164. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-opacity.mdx +103 -0
  165. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-saturate.mdx +98 -0
  166. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-sepia.mdx +95 -0
  167. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter.mdx +113 -0
  168. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backface-visibility.mdx +110 -0
  169. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-attachment.mdx +126 -0
  170. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-blend-mode.mdx +79 -0
  171. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-clip.mdx +85 -0
  172. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-color.mdx +176 -0
  173. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-image.mdx +349 -0
  174. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-origin.mdx +64 -0
  175. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-position.mdx +171 -0
  176. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-repeat.mdx +127 -0
  177. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-size.mdx +100 -0
  178. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/block-size.mdx +190 -0
  179. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/border-collapse.mdx +181 -0
  180. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/border-color.mdx +316 -0
  181. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/border-radius.mdx +347 -0
  182. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/border-spacing.mdx +117 -0
  183. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/border-style.mdx +129 -0
  184. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/border-width.mdx +344 -0
  185. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/box-decoration-break.mdx +71 -0
  186. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/box-shadow.mdx +497 -0
  187. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/box-sizing.mdx +175 -0
  188. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/break-after.mdx +38 -0
  189. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/break-before.mdx +38 -0
  190. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/break-inside.mdx +34 -0
  191. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/caption-side.mdx +190 -0
  192. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/caret-color.mdx +66 -0
  193. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/clear.mdx +224 -0
  194. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/color-scheme.mdx +81 -0
  195. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/color.mdx +125 -0
  196. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/colors.mdx +927 -0
  197. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/columns.mdx +310 -0
  198. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/compatibility.mdx +193 -0
  199. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/content.mdx +129 -0
  200. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/cursor.mdx +104 -0
  201. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/dark-mode.mdx +191 -0
  202. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/detecting-classes-in-source-files.mdx +315 -0
  203. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/display.mdx +523 -0
  204. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/editor-setup.mdx +90 -0
  205. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/field-sizing.mdx +75 -0
  206. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/fill.mdx +100 -0
  207. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-blur.mdx +114 -0
  208. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-brightness.mdx +102 -0
  209. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-contrast.mdx +102 -0
  210. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-drop-shadow.mdx +230 -0
  211. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-grayscale.mdx +101 -0
  212. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-hue-rotate.mdx +171 -0
  213. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-invert.mdx +93 -0
  214. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-saturate.mdx +109 -0
  215. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-sepia.mdx +92 -0
  216. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter.mdx +113 -0
  217. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/flex-basis.mdx +144 -0
  218. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/flex-direction.mdx +130 -0
  219. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/flex-grow.mdx +129 -0
  220. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/flex-shrink.mdx +95 -0
  221. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/flex-wrap.mdx +118 -0
  222. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/flex.mdx +164 -0
  223. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/float.mdx +197 -0
  224. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-family.mdx +119 -0
  225. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-feature-settings.mdx +52 -0
  226. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-size.mdx +184 -0
  227. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-smoothing.mdx +67 -0
  228. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-stretch.mdx +138 -0
  229. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-style.mdx +62 -0
  230. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-variant-numeric.mdx +228 -0
  231. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-weight.mdx +105 -0
  232. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/forced-color-adjust.mdx +152 -0
  233. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/functions-and-directives.mdx +322 -0
  234. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/gap.mdx +100 -0
  235. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-auto-columns.mdx +43 -0
  236. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-auto-flow.mdx +58 -0
  237. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-auto-rows.mdx +43 -0
  238. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-column.mdx +124 -0
  239. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-row.mdx +115 -0
  240. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-template-columns.mdx +101 -0
  241. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-template-rows.mdx +108 -0
  242. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/height.mdx +260 -0
  243. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/hover-focus-and-other-states.mdx +4935 -0
  244. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/hyphens.mdx +112 -0
  245. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/inline-size.mdx +191 -0
  246. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/isolation.mdx +30 -0
  247. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/justify-content.mdx +348 -0
  248. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/justify-items.mdx +286 -0
  249. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/justify-self.mdx +282 -0
  250. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/letter-spacing.mdx +100 -0
  251. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/line-clamp.mdx +110 -0
  252. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/line-height.mdx +147 -0
  253. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/list-style-image.mdx +80 -0
  254. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/list-style-position.mdx +67 -0
  255. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/list-style-type.mdx +86 -0
  256. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/margin.mdx +397 -0
  257. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-clip.mdx +66 -0
  258. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-composite.mdx +70 -0
  259. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-image.mdx +933 -0
  260. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-mode.mdx +114 -0
  261. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-origin.mdx +66 -0
  262. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-position.mdx +117 -0
  263. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-repeat.mdx +169 -0
  264. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-size.mdx +104 -0
  265. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-type.mdx +98 -0
  266. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/max-block-size.mdx +153 -0
  267. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/max-height.mdx +164 -0
  268. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/max-inline-size.mdx +185 -0
  269. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/max-width.mdx +214 -0
  270. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/min-block-size.mdx +146 -0
  271. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/min-height.mdx +150 -0
  272. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/min-inline-size.mdx +153 -0
  273. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/min-width.mdx +153 -0
  274. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mix-blend-mode.mdx +103 -0
  275. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/object-fit.mdx +147 -0
  276. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/object-position.mdx +182 -0
  277. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/opacity.mdx +84 -0
  278. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/order.mdx +94 -0
  279. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/outline-color.mdx +130 -0
  280. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/outline-offset.mdx +72 -0
  281. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/outline-style.mdx +134 -0
  282. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/outline-width.mdx +91 -0
  283. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/overflow-wrap.mdx +130 -0
  284. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/overflow.mdx +665 -0
  285. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/overscroll-behavior.mdx +151 -0
  286. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/padding.mdx +232 -0
  287. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/perspective-origin.mdx +127 -0
  288. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/perspective.mdx +137 -0
  289. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/place-content.mdx +257 -0
  290. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/place-items.mdx +269 -0
  291. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/place-self.mdx +257 -0
  292. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/pointer-events.mdx +101 -0
  293. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/position.mdx +399 -0
  294. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/preflight.mdx +291 -0
  295. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/resize.mdx +110 -0
  296. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/responsive-design.mdx +666 -0
  297. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/rotate.mdx +291 -0
  298. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scale.mdx +360 -0
  299. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scroll-behavior.mdx +39 -0
  300. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scroll-margin.mdx +268 -0
  301. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scroll-padding.mdx +255 -0
  302. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scroll-snap-align.mdx +281 -0
  303. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scroll-snap-stop.mdx +198 -0
  304. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scroll-snap-type.mdx +284 -0
  305. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/skew.mdx +352 -0
  306. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/stroke-width.mdx +74 -0
  307. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/stroke.mdx +103 -0
  308. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/styling-with-utility-classes.mdx +1020 -0
  309. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/table-layout.mdx +212 -0
  310. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-align.mdx +162 -0
  311. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-decoration-color.mdx +155 -0
  312. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-decoration-line.mdx +143 -0
  313. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-decoration-style.mdx +75 -0
  314. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-decoration-thickness.mdx +75 -0
  315. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-indent.mdx +74 -0
  316. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-overflow.mdx +103 -0
  317. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-shadow.mdx +177 -0
  318. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-transform.mdx +106 -0
  319. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-underline-offset.mdx +74 -0
  320. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-wrap.mdx +144 -0
  321. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/theme.mdx +1142 -0
  322. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/top-right-bottom-left.mdx +296 -0
  323. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/touch-action.mdx +95 -0
  324. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transform-origin.mdx +130 -0
  325. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transform-style.mdx +110 -0
  326. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transform.mdx +51 -0
  327. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transition-behavior.mdx +76 -0
  328. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transition-delay.mdx +76 -0
  329. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transition-duration.mdx +82 -0
  330. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transition-property.mdx +137 -0
  331. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transition-timing-function.mdx +89 -0
  332. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/translate.mdx +487 -0
  333. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/upgrade-guide.mdx +910 -0
  334. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/user-select.mdx +114 -0
  335. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/vertical-align.mdx +194 -0
  336. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/visibility.mdx +287 -0
  337. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/white-space.mdx +211 -0
  338. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/width.mdx +264 -0
  339. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/will-change.mdx +47 -0
  340. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/word-break.mdx +94 -0
  341. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/z-index.mdx +104 -0
  342. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs-catalog.md +251 -0
  343. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs-index.tsx +237 -0
  344. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs-source.txt +6 -0
  345. package/extensions/enact-repo/skills/tailwind-4-docs/references/gotchas.md +18 -0
  346. package/extensions/enact-repo/skills/tailwind-4-docs/scripts/sync_tailwind_docs.py +183 -0
  347. package/extensions/enact-repo/skills/tdd/SKILL.md +49 -0
  348. package/extensions/enact-repo/skills/tdd/references/mocking.md +59 -0
  349. package/extensions/enact-repo/skills/tdd/references/tests.md +77 -0
  350. package/extensions/enact-repo/skills/teach/SKILL.md +88 -0
  351. package/extensions/enact-repo/skills/teach/references/GLOSSARY-FORMAT.md +35 -0
  352. package/extensions/enact-repo/skills/teach/references/LEARNING-RECORD-FORMAT.md +46 -0
  353. package/extensions/enact-repo/skills/teach/references/MISSION-FORMAT.md +31 -0
  354. package/extensions/enact-repo/skills/teach/references/PHILOSOPHY.md +65 -0
  355. package/extensions/enact-repo/skills/teach/references/RESOURCES-FORMAT.md +32 -0
  356. package/extensions/enact-repo/skills/testing/SKILL.md +54 -0
  357. package/extensions/enact-repo/skills/to-spec/SKILL.md +85 -0
  358. package/extensions/enact-repo/skills/to-tickets/SKILL.md +75 -0
  359. package/extensions/enact-repo/skills/to-tickets/references/TICKET-TEMPLATES.md +39 -0
  360. package/extensions/enact-repo/skills/triage/SKILL.md +99 -0
  361. package/extensions/enact-repo/skills/triage/references/AGENT-BRIEF.md +207 -0
  362. package/extensions/enact-repo/skills/triage/references/NEEDS-INFO-TEMPLATE.md +17 -0
  363. package/extensions/enact-repo/skills/triage/references/OUT-OF-SCOPE.md +105 -0
  364. package/extensions/enact-repo/skills/ui-ux-pro-max/SKILL.md +32 -0
  365. package/extensions/enact-repo/skills/ui-ux-pro-max/references/Workflow.md +207 -0
  366. package/extensions/enact-repo/skills/ui-ux-pro-max/references/workflow-reference.md +175 -0
  367. package/extensions/enact-repo/skills/wayfinder/SKILL.md +100 -0
  368. package/extensions/enact-repo/skills/wayfinder/references/TEMPLATES.md +35 -0
  369. package/extensions/enact-repo/skills/wayfinder/references/TICKET-TYPES.md +8 -0
  370. package/extensions/enact-repo/skills/writing-great-skills/SKILL.md +93 -0
  371. package/extensions/enact-repo/skills/writing-great-skills/references/GLOSSARY.md +201 -0
  372. package/package.json +18 -14
  373. package/scripts/enact-extensions.mjs +255 -873
  374. package/scripts/lib/allowed-plugins.mjs +52 -0
  375. package/scripts/lib/run-repo-doctor.mjs +571 -0
  376. package/scripts/lib/run-repo-install.mjs +1579 -0
  377. package/scripts/lib/run-sync.mjs +1 -1
  378. package/scripts/lib/run-validate.mjs +23 -37
  379. package/scripts/lib/skill-validator.mjs +872 -0
  380. package/spec/enact.json +21 -3
  381. package/spec/enact.md +13 -12
  382. package/spec/kimi.json +100 -0
  383. package/spec/opencode.json +64 -0
  384. package/dist/install.d.ts +0 -235
  385. package/dist/install.d.ts.map +0 -1
  386. package/dist/install.js +0 -697
  387. package/dist/install.js.map +0 -1
  388. package/dist/internal/claude.d.ts +0 -65
  389. package/dist/internal/claude.d.ts.map +0 -1
  390. package/dist/internal/claude.js +0 -200
  391. package/dist/internal/claude.js.map +0 -1
  392. package/dist/internal/codex.d.ts +0 -43
  393. package/dist/internal/codex.d.ts.map +0 -1
  394. package/dist/internal/codex.js +0 -211
  395. package/dist/internal/codex.js.map +0 -1
  396. package/dist/internal/cursor.d.ts +0 -2
  397. package/dist/internal/cursor.d.ts.map +0 -1
  398. package/dist/internal/cursor.js +0 -6
  399. package/dist/internal/cursor.js.map +0 -1
  400. package/dist/principles.d.ts +0 -28
  401. package/dist/principles.d.ts.map +0 -1
  402. package/dist/principles.js +0 -159
  403. package/dist/principles.js.map +0 -1
  404. package/dist/provision.d.ts +0 -30
  405. package/dist/provision.d.ts.map +0 -1
  406. package/dist/provision.js +0 -202
  407. package/dist/provision.js.map +0 -1
  408. package/extensions/dev-state/.agents/plugin.json +0 -36
  409. package/extensions/dev-state/skills/dev-state-plan-graduation/SKILL.md +0 -194
  410. package/extensions/dev-state/skills/dev-state-plan-graduation/agents/openai.yaml +0 -4
  411. package/extensions/dev-state/skills/dev-state-plan-graduation/references/reference.md +0 -130
  412. package/extensions/enact-context/.agents/plugin.json +0 -41
  413. package/extensions/enact-context/.mcp.json +0 -8
  414. package/extensions/enact-context/README.md +0 -25
  415. package/extensions/enact-context/assets/icon.png +0 -0
  416. package/extensions/enact-context/assets/logo.png +0 -0
  417. package/extensions/enact-context/hooks/hooks.json +0 -115
  418. package/extensions/enact-context/skills/enact-context/SKILL.md +0 -151
  419. package/extensions/enact-context/skills/enact-context/scripts/install.sh +0 -69
  420. package/extensions/enact-core/.agents/plugin.json +0 -40
  421. package/extensions/enact-core/OPERATING-PRINCIPLES.md +0 -7
  422. package/extensions/enact-core/hooks/hooks.json +0 -26
  423. package/extensions/enact-evolve/.agents/plugin.json +0 -47
  424. package/extensions/enact-evolve/agents/evolve-session-analyst.toml +0 -37
  425. package/extensions/enact-evolve/assets/icon.png +0 -0
  426. package/extensions/enact-evolve/assets/logo.png +0 -0
  427. package/extensions/enact-evolve/skills/mined-investigate-loop/SKILL.md +0 -68
  428. package/extensions/enact-evolve/skills/session-analysis/SKILL.md +0 -98
  429. package/extensions/enact-evolve/skills/session-analysis/scripts/run-evolve-analysis.sh +0 -343
  430. package/extensions/enact-factory/.agents/plugin.json +0 -49
  431. package/extensions/enact-factory/.mcp.json +0 -8
  432. package/extensions/enact-factory/agents/architect.toml +0 -34
  433. package/extensions/enact-factory/agents/code-reviewer.toml +0 -33
  434. package/extensions/enact-factory/agents/critic.toml +0 -39
  435. package/extensions/enact-factory/agents/executor.toml +0 -26
  436. package/extensions/enact-factory/agents/explore.toml +0 -25
  437. package/extensions/enact-factory/agents/planner.toml +0 -26
  438. package/extensions/enact-factory/agents/verifier.toml +0 -33
  439. package/extensions/enact-factory/assets/icon.png +0 -0
  440. package/extensions/enact-factory/assets/logo.png +0 -0
  441. package/extensions/enact-factory/skills/advisor/SKILL.md +0 -82
  442. package/extensions/enact-factory/skills/ai-slop-cleaner/SKILL.md +0 -57
  443. package/extensions/enact-factory/skills/autonomous-runner/SKILL.md +0 -353
  444. package/extensions/enact-factory/skills/azdo-ci-strategy/SKILL.md +0 -309
  445. package/extensions/enact-factory/skills/azdo-ci-strategy/references/build-failures.md +0 -60
  446. package/extensions/enact-factory/skills/azdo-ci-strategy/references/cli-reference.md +0 -87
  447. package/extensions/enact-factory/skills/azdo-ci-strategy/references/policies-and-pipelines.md +0 -132
  448. package/extensions/enact-factory/skills/azdo-ci-strategy/references/troubleshooting.md +0 -53
  449. package/extensions/enact-factory/skills/cmux-leader/SKILL.md +0 -74
  450. package/extensions/enact-factory/skills/cmux-send/SKILL.md +0 -50
  451. package/extensions/enact-factory/skills/committee/SKILL.md +0 -80
  452. package/extensions/enact-factory/skills/deep-interview/SKILL.md +0 -68
  453. package/extensions/enact-factory/skills/drive-loop/SKILL.md +0 -394
  454. package/extensions/enact-factory/skills/drive-loop/references/contract-schema.md +0 -127
  455. package/extensions/enact-factory/skills/handoff/SKILL.md +0 -75
  456. package/extensions/enact-factory/skills/hyperplan/SKILL.md +0 -59
  457. package/extensions/enact-factory/skills/looplan/SKILL.md +0 -120
  458. package/extensions/enact-factory/skills/plan/SKILL.md +0 -103
  459. package/extensions/enact-factory/skills/remove-deadcode/SKILL.md +0 -46
  460. package/extensions/enact-factory/skills/research/SKILL.md +0 -83
  461. package/extensions/enact-factory/skills/review/SKILL.md +0 -67
  462. package/extensions/enact-factory/skills/security-research/SKILL.md +0 -57
  463. package/extensions/enact-factory/skills/tdd/SKILL.md +0 -62
  464. package/extensions/enact-factory/skills/testing-strategy/SKILL.md +0 -172
  465. package/extensions/enact-factory/skills/trace/SKILL.md +0 -42
  466. package/extensions/enact-factory/skills/ultraqa/SKILL.md +0 -85
  467. package/extensions/enact-factory/skills/work-with-workitem/SKILL.md +0 -56
  468. package/extensions/enact-factory/skills/workitem-triage/SKILL.md +0 -42
  469. package/extensions/enact-wiki/.agents/plugin.json +0 -43
  470. package/extensions/enact-wiki/.mcp.json +0 -15
  471. package/extensions/enact-wiki/README.md +0 -44
  472. package/extensions/enact-wiki/assets/icon.png +0 -0
  473. package/extensions/enact-wiki/assets/logo.png +0 -0
  474. package/extensions/enact-wiki/skills/document-parser/SKILL.md +0 -17
  475. package/extensions/enact-wiki/skills/document-parser/scripts/parse.sh +0 -60
  476. package/extensions/enact-wiki/skills/document-parser/skill.json +0 -9
  477. package/extensions/enact-wiki/skills/enact-wiki/SKILL.md +0 -30
  478. package/extensions/enact-wiki/skills/enact-wiki/references/ingest.md +0 -62
  479. package/extensions/enact-wiki/skills/enact-wiki/references/manage.md +0 -34
  480. package/extensions/enact-wiki/skills/enact-wiki/references/query.md +0 -59
  481. package/extensions/enact-wiki/skills/search-lab/SKILL.md +0 -57
  482. package/extensions/enact-wiki/skills/search-lab/scripts/analyze.ts +0 -23
  483. package/extensions/enact-wiki/skills/wiki/SKILL.md +0 -42
  484. package/extensions/net-revenue-management/.agents/plugin.json +0 -40
  485. package/extensions/net-revenue-management/.mcp.json +0 -9
  486. package/extensions/net-revenue-management/skills/net-revenue-risks/SKILL.md +0 -30
  487. package/extensions/net-revenue-management/skills/net-revenue-scenario/SKILL.md +0 -31
  488. package/extensions/plugin-dev/.agents/plugin.json +0 -41
  489. package/extensions/plugin-dev/agents/agent-creator.md +0 -199
  490. package/extensions/plugin-dev/agents/plugin-validator.md +0 -91
  491. package/extensions/plugin-dev/agents/skill-reviewer.md +0 -212
  492. package/extensions/plugin-dev/skills/agent-development/SKILL.md +0 -641
  493. package/extensions/plugin-dev/skills/agent-development/examples/agent-creation-prompt.md +0 -250
  494. package/extensions/plugin-dev/skills/agent-development/examples/complete-agent-examples.md +0 -461
  495. package/extensions/plugin-dev/skills/agent-development/references/advanced-agent-fields.md +0 -246
  496. package/extensions/plugin-dev/skills/agent-development/references/agent-creation-system-prompt.md +0 -216
  497. package/extensions/plugin-dev/skills/agent-development/references/permission-modes-rules.md +0 -226
  498. package/extensions/plugin-dev/skills/agent-development/references/system-prompt-design.md +0 -464
  499. package/extensions/plugin-dev/skills/agent-development/references/triggering-examples.md +0 -474
  500. package/extensions/plugin-dev/skills/agent-development/scripts/create-agent-skeleton.sh +0 -176
  501. package/extensions/plugin-dev/skills/agent-development/scripts/test-agent-trigger.sh +0 -227
  502. package/extensions/plugin-dev/skills/agent-development/scripts/validate-agent.sh +0 -227
  503. package/extensions/plugin-dev/skills/create-plugin/SKILL.md +0 -505
  504. package/extensions/plugin-dev/skills/hook-development/SKILL.md +0 -861
  505. package/extensions/plugin-dev/skills/hook-development/examples/load-context.sh +0 -55
  506. package/extensions/plugin-dev/skills/hook-development/examples/validate-bash.sh +0 -57
  507. package/extensions/plugin-dev/skills/hook-development/examples/validate-write.sh +0 -48
  508. package/extensions/plugin-dev/skills/hook-development/references/advanced.md +0 -871
  509. package/extensions/plugin-dev/skills/hook-development/references/hook-input-schemas.md +0 -145
  510. package/extensions/plugin-dev/skills/hook-development/references/migration.md +0 -392
  511. package/extensions/plugin-dev/skills/hook-development/references/patterns.md +0 -430
  512. package/extensions/plugin-dev/skills/hook-development/scripts/README.md +0 -181
  513. package/extensions/plugin-dev/skills/hook-development/scripts/hook-linter.sh +0 -153
  514. package/extensions/plugin-dev/skills/hook-development/scripts/test-hook.sh +0 -276
  515. package/extensions/plugin-dev/skills/hook-development/scripts/validate-hook-schema.sh +0 -159
  516. package/extensions/plugin-dev/skills/mcp-integration/SKILL.md +0 -775
  517. package/extensions/plugin-dev/skills/mcp-integration/examples/http-server.json +0 -20
  518. package/extensions/plugin-dev/skills/mcp-integration/examples/sse-server.json +0 -19
  519. package/extensions/plugin-dev/skills/mcp-integration/examples/stdio-server.json +0 -38
  520. package/extensions/plugin-dev/skills/mcp-integration/examples/ws-server.json +0 -26
  521. package/extensions/plugin-dev/skills/mcp-integration/references/authentication.md +0 -601
  522. package/extensions/plugin-dev/skills/mcp-integration/references/server-discovery.md +0 -190
  523. package/extensions/plugin-dev/skills/mcp-integration/references/server-types.md +0 -572
  524. package/extensions/plugin-dev/skills/mcp-integration/references/tool-usage.md +0 -623
  525. package/extensions/plugin-dev/skills/plugin-dev-guide/SKILL.md +0 -221
  526. package/extensions/plugin-dev/skills/plugin-structure/SKILL.md +0 -705
  527. package/extensions/plugin-dev/skills/plugin-structure/examples/advanced-plugin.md +0 -774
  528. package/extensions/plugin-dev/skills/plugin-structure/examples/minimal-plugin.md +0 -83
  529. package/extensions/plugin-dev/skills/plugin-structure/examples/standard-plugin.md +0 -611
  530. package/extensions/plugin-dev/skills/plugin-structure/references/advanced-topics.md +0 -289
  531. package/extensions/plugin-dev/skills/plugin-structure/references/component-patterns.md +0 -592
  532. package/extensions/plugin-dev/skills/plugin-structure/references/github-actions.md +0 -233
  533. package/extensions/plugin-dev/skills/plugin-structure/references/headless-ci-mode.md +0 -193
  534. package/extensions/plugin-dev/skills/plugin-structure/references/manifest-reference.md +0 -625
  535. package/extensions/plugin-dev/skills/plugin-structure/references/output-styles.md +0 -116
  536. package/extensions/plugin-dev/skills/skill-development/SKILL.md +0 -562
  537. package/extensions/plugin-dev/skills/skill-development/examples/complete-skill.md +0 -465
  538. package/extensions/plugin-dev/skills/skill-development/examples/frontmatter-templates.md +0 -167
  539. package/extensions/plugin-dev/skills/skill-development/examples/minimal-skill.md +0 -111
  540. package/extensions/plugin-dev/skills/skill-development/references/advanced-frontmatter.md +0 -225
  541. package/extensions/plugin-dev/skills/skill-development/references/commands-vs-skills.md +0 -39
  542. package/extensions/plugin-dev/skills/skill-development/references/skill-creation-workflow.md +0 -379
  543. package/extensions/plugin-dev/skills/skill-development/references/skill-creator-original.md +0 -210
  544. package/extensions/plugin-dev/skills/start/SKILL.md +0 -82
  545. package/scripts/check-hooks.mjs +0 -174
  546. package/scripts/check-principles.mjs +0 -116
  547. package/scripts/hooks/session-start-drift-check.mjs +0 -48
  548. package/scripts/install.sh +0 -70
  549. package/scripts/lib/auto-update.mjs +0 -75
  550. package/scripts/lib/bundle-hash.mjs +0 -137
  551. package/scripts/lib/hooks.mjs +0 -585
  552. package/scripts/lib/ledger.mjs +0 -163
  553. package/scripts/lib/migrate-artifacts.mjs +0 -144
  554. package/scripts/lib/outdated.mjs +0 -144
  555. package/scripts/lib/postinstall-offer.mjs +0 -83
  556. package/scripts/lib/principles.mjs +0 -109
  557. package/scripts/lib/provision-mcp.mjs +0 -16
  558. package/scripts/lib/resolve-plugin-root.mjs +0 -9
  559. package/scripts/lib/run-install.mjs +0 -500
  560. package/scripts/lib/run-prune.mjs +0 -94
  561. package/scripts/lib/run-uninstall.mjs +0 -244
  562. package/scripts/lib/run-update.mjs +0 -152
  563. package/scripts/lib/serve.mjs +0 -472
  564. package/scripts/lib/ups-router.mjs +0 -392
  565. package/scripts/postinstall.mjs +0 -101
  566. package/scripts/setup-enact-context.sh +0 -28
  567. package/scripts/version-bump.sh +0 -484
  568. package/web/assets/README.md +0 -111
  569. package/web/assets/logo-full.png +0 -0
  570. package/web/assets/logo-slim.png +0 -0
  571. package/web/assets/tokens/base.css +0 -45
  572. package/web/assets/tokens/colors.css +0 -248
  573. package/web/assets/tokens/effects.css +0 -24
  574. package/web/assets/tokens/fonts.css +0 -8
  575. package/web/assets/tokens/index.css +0 -18
  576. package/web/assets/tokens/spacing.css +0 -50
  577. package/web/index.html +0 -1188
@@ -0,0 +1,872 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { existsSync, lstatSync, readdirSync, readFileSync, statSync } from "node:fs";
3
+ import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
4
+ import { Ajv } from "ajv";
5
+ import addFormats from "ajv-formats";
6
+ import { parseDocument } from "yaml";
7
+
8
+ export const ALL_SURFACE_TARGETS = ["claude", "codex", "cursor", "enact", "kimi"];
9
+ /** Opt-in surface a bundle may append after the required targets. */
10
+ export const OPT_IN_TARGET = "opencode";
11
+ export const SKILL_ROOT_DIRECTORIES = new Set(["scripts", "references", "assets"]);
12
+ /** Non-SKILL.md files still allowed directly under a skill root: license metadata and a version marker. */
13
+ export const SKILL_ROOT_ALLOWED_EXTRA_FILES = /^(LICENSE(\..+)?|version\.json)$/;
14
+ /**
15
+ * The only H2 headings SKILL.md is required to carry: `Use When` before
16
+ * `Do Not Use When`. Any other H2 headings are allowed, in any order.
17
+ */
18
+ export const REQUIRED_SKILL_HEADINGS = ["Use When", "Do Not Use When"];
19
+ export const REQUIRED_BOUNDARY_HEADINGS = ["In scope", "Out of scope", "Fog of war"];
20
+
21
+ /**
22
+ * Plugins exempt from the fixed section sequence and the SKILL.md line cap.
23
+ *
24
+ * The eight-heading contract exists so an autonomous runner finds Purpose,
25
+ * State Contract, and Completion in a known place in every governance skill. It
26
+ * is a poor fit for imported reference material, whose own headings carry the
27
+ * method — forcing `tdd` into Purpose/Use When/Output destroys the thing that
28
+ * makes it useful. Listed plugins still get every other rule: frontmatter shape,
29
+ * in-plugin route reachability, resource layout, and slop detection.
30
+ */
31
+ export const FREEFORM_SECTION_PLUGINS = new Set(["engineering"]);
32
+ export const CANONICAL_FACTORY_STORAGE =
33
+ "Factory artifacts go under `~/.enact/factory/projects/<key>/…`.";
34
+
35
+ const ALLOWED_FRONTMATTER_KEYS = new Set([
36
+ "name",
37
+ "description",
38
+ "disable-model-invocation",
39
+ "compatibility",
40
+ ]);
41
+ const COMPONENT_FIELDS = [
42
+ "skills",
43
+ "agents",
44
+ "commands",
45
+ "hooks",
46
+ "mcpServers",
47
+ "apps",
48
+ "lsp",
49
+ "monitors",
50
+ "settings",
51
+ "references",
52
+ "bin",
53
+ "rules",
54
+ "_agents",
55
+ ];
56
+ const COMMAND_SHAPED_LINE = /`[^`]*(\b(npm|git)\s+(run|test|start|build|install|ci|status|diff|log|show|stash|commit|push|pull|checkout|merge-base|rev-parse)\b|\b(node|npx|paseo|cargo|az|tsc|grep)\s+\S|loop_[a-z]+|factory_[a-z]+|exit 0)[^`]*`/i;
57
+ const EMOJI = /[\u{1F300}-\u{1FAFF}\u{2600}-\u{26FF}\u{2705}\u{274C}\u{2713}-\u{2716}]/u;
58
+ const SLOP_PATTERNS = [
59
+ ["slop-storage-banner", /Storage is GLOBAL|never write to a repo-local `?\.enact/i],
60
+ ["slop-workspace-recipe", /~\/\.enact\/workspaces\.json|Resolve the project'?s global dir/i],
61
+ ["slop-marketing", /\b(seamless|best practices|dive in|game[- ]changer|supercharge|effortless|cutting-edge|comprehensive|powerful|leverage(?:d|s|ing)?|elegant|closes? the gap|unlock|elevate|world-class|next-level|revolutionary|synerg(?:y|ies|ize)|utilize)\b/i],
62
+ ["slop-filler", /\b(it'?s worth noting|it'?s important to note|note that|in summary|in conclusion|obviously|clearly)\b/i],
63
+ ["slop-self-reference", /\b(generated by an AI|LLM-authored|as an AI)\b/i],
64
+ ["slop-edit-scar", /as of \)|(?<![A-Za-z0-9_])\(\s*\)|\(,\s|^#{1,6}\s+.* —\s*$/im],
65
+ ];
66
+ const manifestSchemaValidators = new Map();
67
+ const VALIDATOR_ROOT = resolve(import.meta.dirname, "..", "..");
68
+
69
+ function diagnostic(code, path, message) {
70
+ return { code, path: toPosix(path), message };
71
+ }
72
+
73
+ function toPosix(value) {
74
+ return value.split(sep).join("/");
75
+ }
76
+
77
+ function lineCount(text) {
78
+ if (!text) return 0;
79
+ return text.split(/\r?\n/).length - (text.endsWith("\n") ? 1 : 0);
80
+ }
81
+
82
+ function isIgnoredNoise(name) {
83
+ return name === ".DS_Store" || name === ".donor";
84
+ }
85
+
86
+ function isDirectory(path) {
87
+ try {
88
+ return statSync(path).isDirectory();
89
+ } catch {
90
+ return false;
91
+ }
92
+ }
93
+
94
+ function isFile(path) {
95
+ try {
96
+ return statSync(path).isFile();
97
+ } catch {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ function isWithin(root, candidate) {
103
+ const rel = relative(root, candidate);
104
+ return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !rel.includes(`..${sep}`));
105
+ }
106
+
107
+ function safeRelative(root, raw) {
108
+ if (typeof raw !== "string" || !raw.trim()) return null;
109
+ const cleaned = raw.trim().replace(/^\.\//, "");
110
+ if (cleaned.startsWith("/") || cleaned.split("/").includes("..")) return null;
111
+ const absolute = resolve(root, cleaned);
112
+ return isWithin(root, absolute) ? { absolute, relative: toPosix(cleaned.replace(/\/+$/, "")) } : null;
113
+ }
114
+
115
+ function listFiles(root) {
116
+ const files = [];
117
+ function walk(current) {
118
+ for (const entry of readdirSync(current, { withFileTypes: true })) {
119
+ if (isIgnoredNoise(entry.name)) continue;
120
+ const absolute = join(current, entry.name);
121
+ const rel = toPosix(relative(root, absolute));
122
+ const stat = lstatSync(absolute);
123
+ if (stat.isSymbolicLink()) {
124
+ files.push({ absolute, relative: rel, symlink: true });
125
+ } else if (stat.isDirectory()) {
126
+ walk(absolute);
127
+ } else if (stat.isFile()) {
128
+ files.push({ absolute, relative: rel, symlink: false });
129
+ }
130
+ }
131
+ }
132
+ if (isDirectory(root)) walk(root);
133
+ return files.sort((a, b) => a.relative.localeCompare(b.relative));
134
+ }
135
+
136
+ function findGitRoot(start) {
137
+ try {
138
+ return execFileSync("git", ["-C", start, "rev-parse", "--show-toplevel"], {
139
+ encoding: "utf8",
140
+ stdio: ["ignore", "pipe", "pipe"],
141
+ }).trim();
142
+ } catch {
143
+ return null;
144
+ }
145
+ }
146
+
147
+ export function trackedFiles(repoRoot, treeish) {
148
+ const args = treeish
149
+ ? ["-C", repoRoot, "ls-tree", "-r", "-z", "--name-only", treeish]
150
+ : ["-C", repoRoot, "ls-files", "-z"];
151
+ try {
152
+ const raw = execFileSync("git", args, {
153
+ encoding: "buffer",
154
+ stdio: ["ignore", "pipe", "pipe"],
155
+ });
156
+ return raw.toString("utf8").split("\0").filter(Boolean).map(toPosix);
157
+ } catch (err) {
158
+ throw new Error(`Cannot read tracked files: ${err instanceof Error ? err.message : String(err)}`);
159
+ }
160
+ }
161
+
162
+ export function gitFile(repoRoot, treeish, path) {
163
+ try {
164
+ return execFileSync("git", ["-C", repoRoot, "show", `${treeish}:${path}`], {
165
+ encoding: "utf8",
166
+ stdio: ["ignore", "pipe", "pipe"],
167
+ });
168
+ } catch {
169
+ return null;
170
+ }
171
+ }
172
+
173
+ export function changedFiles(repoRoot, base, staged = false) {
174
+ const args = ["-C", repoRoot, "diff", "--name-only", "-z"];
175
+ if (staged) args.push("--cached");
176
+ else args.push(`${base}...HEAD`);
177
+ try {
178
+ return execFileSync("git", args, { encoding: "buffer", stdio: ["ignore", "pipe", "pipe"] })
179
+ .toString("utf8")
180
+ .split("\0")
181
+ .filter(Boolean)
182
+ .map(toPosix);
183
+ } catch (err) {
184
+ throw new Error(`Cannot read changed files: ${err instanceof Error ? err.message : String(err)}`);
185
+ }
186
+ }
187
+
188
+ function frontmatter(text, path, diagnostics) {
189
+ if (!text.startsWith("---\n") && !text.startsWith("---\r\n")) {
190
+ diagnostics.push(diagnostic("frontmatter-missing", path, "SKILL.md must start with YAML frontmatter."));
191
+ return { metadata: null, body: text };
192
+ }
193
+ const lines = text.split(/\r?\n/);
194
+ const closing = lines.findIndex((line, index) => index > 0 && line === "---");
195
+ if (closing === -1) {
196
+ diagnostics.push(diagnostic("frontmatter-unterminated", path, "YAML frontmatter must end with a closing --- line."));
197
+ return { metadata: null, body: text };
198
+ }
199
+ const yaml = lines.slice(1, closing).join("\n");
200
+ const after = lines.slice(closing + 1).join("\n");
201
+ const document = parseDocument(yaml, { prettyErrors: true, uniqueKeys: true });
202
+ if (document.errors.length > 0) {
203
+ for (const error of document.errors) {
204
+ diagnostics.push(diagnostic("frontmatter-invalid-yaml", path, error.message));
205
+ }
206
+ return { metadata: null, body: after };
207
+ }
208
+ const metadata = document.toJS();
209
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
210
+ diagnostics.push(diagnostic("frontmatter-shape", path, "YAML frontmatter must be a mapping."));
211
+ return { metadata: null, body: after };
212
+ }
213
+ return { metadata, body: after };
214
+ }
215
+
216
+ function headings(body) {
217
+ const result = [];
218
+ let fenced = false;
219
+ for (const [index, line] of body.split(/\r?\n/).entries()) {
220
+ if (/^\s*(```|~~~)/.test(line)) {
221
+ fenced = !fenced;
222
+ continue;
223
+ }
224
+ if (fenced) continue;
225
+ const match = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
226
+ if (match) result.push({ level: match[1].length, text: match[2], line: index + 1 });
227
+ }
228
+ return result;
229
+ }
230
+
231
+ function sectionText(body, heading) {
232
+ const lines = body.split(/\r?\n/);
233
+ const start = heading.line;
234
+ const rest = lines.slice(start);
235
+ const end = rest.findIndex((line) => /^#{1,2}\s+/.test(line));
236
+ return (end === -1 ? rest : rest.slice(0, end)).join("\n");
237
+ }
238
+
239
+ function withoutFencedCode(text) {
240
+ const lines = [];
241
+ let fenced = false;
242
+ for (const line of text.split(/\r?\n/)) {
243
+ if (/^\s*(```|~~~)/.test(line)) {
244
+ fenced = !fenced;
245
+ lines.push("");
246
+ } else {
247
+ lines.push(fenced ? "" : line);
248
+ }
249
+ }
250
+ return lines.join("\n");
251
+ }
252
+
253
+ function resourceReferences(text, knownResources) {
254
+ const references = new Set();
255
+ for (const resource of knownResources) {
256
+ if (text.includes(resource)) references.add(resource);
257
+ }
258
+ for (const match of text.matchAll(/(?:scripts|references|assets)\/[A-Za-z0-9._/-]+/g)) {
259
+ const candidate = match[0].replace(/[),.;:]+$/, "");
260
+ if (!candidate || candidate.endsWith("/") || candidate.endsWith(".")) continue;
261
+ const preceding = match.index > 0 ? text[match.index - 1] : "";
262
+ if (preceding && /[A-Za-z0-9_./-]/.test(preceding)) continue;
263
+ const lineStart = text.lastIndexOf("\n", match.index) + 1;
264
+ const prefix = text.slice(lineStart, match.index);
265
+ if (candidate.startsWith("scripts/") && /(?:node|npx)\s+$/i.test(prefix)) continue;
266
+ references.add(candidate);
267
+ }
268
+ for (const match of text.matchAll(/\[[^\]]*]\((?:\.\/)?((?:scripts|references|assets)\/[A-Za-z0-9._/-]+)(?:#[^)\s]+)?\)/g)) {
269
+ const candidate = match[1].replace(/[),.;:]+$/, "");
270
+ if (!candidate.endsWith("/") && !candidate.endsWith(".")) references.add(candidate);
271
+ }
272
+ for (const match of text.matchAll(/`(?:\.\/)?((?:scripts|references|assets)\/[A-Za-z0-9._/-]+)`/g)) {
273
+ const candidate = match[1].replace(/[),.;:]+$/, "");
274
+ if (!candidate.endsWith("/") && !candidate.endsWith(".")) references.add(candidate);
275
+ }
276
+ return references;
277
+ }
278
+
279
+ function textResource(path) {
280
+ const extension = extname(path).toLowerCase();
281
+ if (!new Set([".md", ".mdx", ".txt", ".js", ".mjs", ".cjs", ".py", ".sh", ".json", ".yaml", ".yml"]).has(extension)) {
282
+ return null;
283
+ }
284
+ return readFileSync(path, "utf8");
285
+ }
286
+
287
+ function validateScript(path, relativePath, diagnostics) {
288
+ const extension = extname(path).toLowerCase();
289
+ const command = extension === ".py"
290
+ ? ["python3", ["-c", "import ast,pathlib,sys; ast.parse(pathlib.Path(sys.argv[1]).read_text())", path]]
291
+ : extension === ".sh"
292
+ ? ["bash", ["-n", path]]
293
+ : new Set([".js", ".mjs", ".cjs"]).has(extension)
294
+ ? [process.execPath, ["--check", path]]
295
+ : null;
296
+ if (!command) {
297
+ diagnostics.push(diagnostic("script-extension", relativePath, `Unsupported script extension ${extension || "(none)"}.`));
298
+ return;
299
+ }
300
+ try {
301
+ execFileSync(command[0], command[1], { stdio: ["ignore", "pipe", "pipe"] });
302
+ } catch (err) {
303
+ const stderr = err && typeof err === "object" && "stderr" in err ? String(err.stderr).trim() : "";
304
+ diagnostics.push(diagnostic("script-syntax", relativePath, stderr || "Script syntax check failed."));
305
+ }
306
+ }
307
+
308
+ function normalizedParagraphs(body) {
309
+ return withoutFencedCode(body)
310
+ .split(/\n\s*\n/)
311
+ .map((part) => part.replace(/^#{1,6}\s+.*$/gm, "").replace(/[`*_>#-]/g, " ").replace(/\s+/g, " ").trim().toLowerCase())
312
+ .filter((part) => part.length >= 120 && part !== CANONICAL_FACTORY_STORAGE.toLowerCase());
313
+ }
314
+
315
+ function bundleOf(path) {
316
+ return /^extensions\/([^/]+)\//.exec(path)?.[1] ?? null;
317
+ }
318
+
319
+ /**
320
+ * Flag a duplicate paragraph only when another occurrence of it sits in a
321
+ * skill belonging to the same bundle (same extensions/<bundle>/ prefix).
322
+ * A paragraph repeated only across different bundles is not a violation.
323
+ */
324
+ function pushDuplicateParagraphDiagnostics(paragraphsByText, diagnostics) {
325
+ for (const paths of paragraphsByText.values()) {
326
+ if (paths.length < 2) continue;
327
+ for (const path of paths) {
328
+ const bundle = bundleOf(path);
329
+ const sameBundleOthers = paths.filter((other) => other !== path && bundleOf(other) === bundle);
330
+ if (sameBundleOthers.length === 0) continue;
331
+ diagnostics.push(diagnostic("slop-duplicate-paragraph", path, `Duplicate prose paragraph also appears in: ${sameBundleOthers.join(", ")}.`));
332
+ }
333
+ }
334
+ }
335
+
336
+ function validateSlop(text, path, diagnostics) {
337
+ const prose = withoutFencedCode(text);
338
+ for (const [code, pattern] of SLOP_PATTERNS) {
339
+ if (pattern.test(prose)) diagnostics.push(diagnostic(code, path, `AI-authoring residue matched ${pattern}.`));
340
+ }
341
+ if (/^\s*(?:[-*]\s+)?\*\*[^*\n]{60,}\*\*[.!?]?\s*$/m.test(prose)) {
342
+ diagnostics.push(diagnostic("slop-overbold", path, "Do not bold a whole sentence for emphasis."));
343
+ }
344
+ if (EMOJI.test(prose)) diagnostics.push(diagnostic("slop-emoji", path, "Emoji and decorative symbols are not allowed."));
345
+ }
346
+
347
+ /**
348
+ * A genuine route reference names another skill in one of two documented
349
+ * shapes (see each skill's "Do Not Use When" bullets and closing routing
350
+ * sentences): a clause separated by a semicolon, e.g. "...; use
351
+ * `skill-name`.", or an explicit "belongs to `skill-name`" ownership
352
+ * statement. Plain prose that happens to mention a backticked, hyphenated
353
+ * token after "use" (e.g. naming a CLI executable like `orca-dev`) is not a
354
+ * route: it is not semicolon-delimited, so it does not match.
355
+ */
356
+ const SKILL_ROUTE_REFERENCE = /(?:(?<=;\s+)use|\bbelongs to)\s+`([a-z][a-z0-9-]+)`/gi;
357
+
358
+ function validateSkillRoutes(skills, diagnostics) {
359
+ const names = new Set(skills.map((skill) => skill.skillName));
360
+ for (const skill of skills) {
361
+ for (const match of skill.body.matchAll(SKILL_ROUTE_REFERENCE)) {
362
+ const target = match[1];
363
+ if (target.includes("-") && !names.has(target)) {
364
+ diagnostics.push(diagnostic("skill-route-missing", skill.path, `Referenced skill does not exist in this plugin: ${target}.`));
365
+ }
366
+ }
367
+ }
368
+ }
369
+
370
+ function validateCanonicalManifest(schemaRoot, manifest, pluginRelative, diagnostics) {
371
+ const schemaPath = join(schemaRoot, "spec", "enact.json");
372
+ if (!isFile(schemaPath)) {
373
+ diagnostics.push(diagnostic("manifest-schema", `${pluginRelative}/.agents/plugin.json`, "Canonical schema spec/enact.json is missing."));
374
+ return;
375
+ }
376
+ let validator = manifestSchemaValidators.get(schemaPath);
377
+ try {
378
+ if (!validator) {
379
+ const ajv = new Ajv({ allErrors: true, strict: false });
380
+ addFormats(ajv);
381
+ validator = ajv.compile(JSON.parse(readFileSync(schemaPath, "utf8")));
382
+ manifestSchemaValidators.set(schemaPath, validator);
383
+ }
384
+ if (!validator(manifest)) {
385
+ for (const error of validator.errors ?? []) {
386
+ diagnostics.push(diagnostic(
387
+ "manifest-schema",
388
+ `${pluginRelative}/.agents/plugin.json`,
389
+ `${error.instancePath || "/"}: ${error.message ?? "invalid"}.`,
390
+ ));
391
+ }
392
+ }
393
+ } catch (err) {
394
+ diagnostics.push(diagnostic("manifest-schema", `${pluginRelative}/.agents/plugin.json`, `Cannot validate canonical manifest: ${err instanceof Error ? err.message : String(err)}`));
395
+ }
396
+ }
397
+
398
+ function validateSkill(skillRoot, pluginName) {
399
+ const diagnostics = [];
400
+ const skillName = basename(skillRoot);
401
+ const rootRelative = `extensions/${pluginName}/skills/${skillName}`;
402
+ const entries = readdirSync(skillRoot, { withFileTypes: true });
403
+ const resources = [];
404
+
405
+ for (const entry of entries) {
406
+ if (isIgnoredNoise(entry.name)) continue;
407
+ const path = join(skillRoot, entry.name);
408
+ const relativePath = `${rootRelative}/${entry.name}`;
409
+ const stat = lstatSync(path);
410
+ if (stat.isSymbolicLink()) {
411
+ diagnostics.push(diagnostic("skill-symlink", relativePath, "Symlinks are not allowed in a skill."));
412
+ continue;
413
+ }
414
+ if (entry.isFile() && entry.name !== "SKILL.md") {
415
+ if (!SKILL_ROOT_ALLOWED_EXTRA_FILES.test(entry.name)) {
416
+ diagnostics.push(diagnostic("skill-root-file", relativePath, "Only SKILL.md, LICENSE, LICENSE.*, and version.json may be files directly under a skill root."));
417
+ }
418
+ continue;
419
+ }
420
+ if (entry.isDirectory() && !SKILL_ROOT_DIRECTORIES.has(entry.name)) {
421
+ diagnostics.push(diagnostic("skill-root-directory", relativePath, "Only scripts/, references/, and assets/ are allowed under a skill root."));
422
+ continue;
423
+ }
424
+ if (entry.isDirectory()) {
425
+ for (const file of listFiles(path)) {
426
+ const fullRelative = `${rootRelative}/${entry.name}/${file.relative}`;
427
+ if (file.symlink) diagnostics.push(diagnostic("skill-symlink", fullRelative, "Symlinks are not allowed in a skill."));
428
+ else resources.push({ ...file, relative: `${entry.name}/${file.relative}`, fullRelative });
429
+ }
430
+ }
431
+ }
432
+
433
+ const skillPath = join(skillRoot, "SKILL.md");
434
+ if (!isFile(skillPath)) {
435
+ diagnostics.push(diagnostic("skill-missing", `${rootRelative}/SKILL.md`, "Every skill directory requires SKILL.md."));
436
+ return { diagnostics, skillName, path: `${rootRelative}/SKILL.md`, body: "", paragraphs: [] };
437
+ }
438
+ const text = readFileSync(skillPath, "utf8");
439
+ const skillRelative = `${rootRelative}/SKILL.md`;
440
+ const freeformSections = FREEFORM_SECTION_PLUGINS.has(pluginName);
441
+ if (!freeformSections && lineCount(text) > 100) {
442
+ diagnostics.push(diagnostic("skill-line-limit", skillRelative, `SKILL.md has ${lineCount(text)} lines; maximum is 100.`));
443
+ }
444
+
445
+ const parsed = frontmatter(text, skillRelative, diagnostics);
446
+ if (parsed.metadata) {
447
+ for (const key of Object.keys(parsed.metadata)) {
448
+ if (!ALLOWED_FRONTMATTER_KEYS.has(key)) {
449
+ diagnostics.push(diagnostic("frontmatter-key", skillRelative, `Unexpected frontmatter key ${JSON.stringify(key)}.`));
450
+ }
451
+ }
452
+ if (parsed.metadata.name !== skillName) {
453
+ diagnostics.push(diagnostic("frontmatter-name", skillRelative, `name must equal directory name ${JSON.stringify(skillName)}.`));
454
+ }
455
+ if (typeof parsed.metadata.description !== "string" || !parsed.metadata.description.trim()) {
456
+ diagnostics.push(diagnostic("frontmatter-description", skillRelative, "description must be a non-empty string."));
457
+ } else if (/^this skill should be used when\b/i.test(parsed.metadata.description.trim())) {
458
+ diagnostics.push(diagnostic("frontmatter-description-slop", skillRelative, "Use a direct description; do not start with generic trigger boilerplate."));
459
+ }
460
+ if (typeof parsed.metadata["disable-model-invocation"] !== "boolean") {
461
+ diagnostics.push(diagnostic("frontmatter-invocation", skillRelative, "disable-model-invocation must be an explicit boolean."));
462
+ }
463
+ if (parsed.metadata.compatibility !== undefined && typeof parsed.metadata.compatibility !== "string") {
464
+ diagnostics.push(diagnostic("frontmatter-compatibility", skillRelative, "compatibility must be a string when present."));
465
+ }
466
+ }
467
+
468
+ const body = parsed.body;
469
+ const allHeadings = headings(body);
470
+ const h1 = allHeadings.filter((entry) => entry.level === 1);
471
+ if (h1.length !== 1 || allHeadings[0] !== h1[0]) {
472
+ diagnostics.push(diagnostic("heading-h1", skillRelative, "SKILL.md must start its body with exactly one H1."));
473
+ }
474
+ const h2 = allHeadings.filter((entry) => entry.level === 2);
475
+ const actualH2 = h2.map((entry) => entry.text);
476
+ if (!freeformSections) {
477
+ const [useWhen, doNotUseWhen] = REQUIRED_SKILL_HEADINGS;
478
+ const useWhenIndex = actualH2.indexOf(useWhen);
479
+ const doNotUseWhenIndex = actualH2.indexOf(doNotUseWhen);
480
+ if (useWhenIndex === -1 || doNotUseWhenIndex === -1 || useWhenIndex > doNotUseWhenIndex) {
481
+ diagnostics.push(diagnostic(
482
+ "heading-contract",
483
+ skillRelative,
484
+ `SKILL.md must contain H2 headings \`${useWhen}\` and \`${doNotUseWhen}\`, with \`${useWhen}\` before \`${doNotUseWhen}\`. Any other H2 headings are allowed, in any order.`,
485
+ ));
486
+ }
487
+ }
488
+ for (const heading of h2) {
489
+ const content = sectionText(body, heading).trim();
490
+ if (!content) diagnostics.push(diagnostic("heading-empty", skillRelative, `## ${heading.text} must not be empty.`));
491
+ }
492
+ const boundaries = h2.find((entry) => entry.text === "Boundaries");
493
+ if (boundaries) {
494
+ const nextH2 = h2.find((entry) => entry.line > boundaries.line);
495
+ const boundaryH3 = allHeadings
496
+ .filter((entry) => entry.level === 3 && entry.line > boundaries.line && (!nextH2 || entry.line < nextH2.line))
497
+ .map((entry) => entry.text);
498
+ if (boundaryH3.join("\u0000") !== REQUIRED_BOUNDARY_HEADINGS.join("\u0000")) {
499
+ diagnostics.push(diagnostic("boundaries-contract", skillRelative, `## Boundaries must contain exact H3 headings: ${REQUIRED_BOUNDARY_HEADINGS.join(" → ")}.`));
500
+ }
501
+ }
502
+ const completion = h2.find((entry) => entry.text === "Completion");
503
+ if (completion && !COMMAND_SHAPED_LINE.test(sectionText(body, completion))) {
504
+ diagnostics.push(diagnostic("completion-proof", skillRelative, "## Completion requires a command-shaped proof line."));
505
+ }
506
+
507
+ const storageCount = body.split(CANONICAL_FACTORY_STORAGE).length - 1;
508
+ const factoryPathMentioned = /~\/\.enact\/factory\/projects\/[A-Za-z0-9_<>.-]+\//.test(body);
509
+ if (storageCount > 1) {
510
+ diagnostics.push(diagnostic("storage-duplicate", skillRelative, "The canonical Factory storage sentence may appear at most once."));
511
+ }
512
+ const h1Line = h1[0]?.line;
513
+ const firstLineAfterH1 = h1Line === undefined
514
+ ? null
515
+ : body.split(/\r?\n/).slice(h1Line).find((line) => line.trim())?.trim() ?? null;
516
+ if (factoryPathMentioned && (storageCount !== 1 || firstLineAfterH1 !== CANONICAL_FACTORY_STORAGE)) {
517
+ diagnostics.push(diagnostic("storage-contract", skillRelative, `Factory path users must place exactly this sentence immediately after the H1: ${CANONICAL_FACTORY_STORAGE}`));
518
+ }
519
+ const noncanonicalStorage = withoutFencedCode(body)
520
+ .replaceAll(CANONICAL_FACTORY_STORAGE, "")
521
+ .replace(/`[^`]*`/g, "")
522
+ .match(/~\/\.enact\/factory\/projects\/|\bfactory\s+(?:artifacts?|storage|workspace)\s+(?:are|is|go|live|reside|belong|located|stored)\b|\b(?:store|write|save)\b[^\n.]{0,100}\b~\/\.enact\b/i);
523
+ if (noncanonicalStorage) {
524
+ diagnostics.push(diagnostic("storage-noncanonical", skillRelative, "Use only the canonical Factory storage sentence."));
525
+ }
526
+ validateSlop(body, skillRelative, diagnostics);
527
+
528
+ const resourceByRelative = new Map(resources.map((resource) => [resource.relative, resource]));
529
+ const reachable = new Set(["SKILL.md"]);
530
+ const pending = [{ relative: "SKILL.md", fullRelative: skillRelative, path: skillPath, text }];
531
+ const missingResources = new Set();
532
+ while (pending.length > 0) {
533
+ const current = pending.pop();
534
+ const currentText = current.text ?? textResource(current.path);
535
+ if (currentText === null || currentText === undefined) continue;
536
+ for (const reference of resourceReferences(currentText, resourceByRelative.keys())) {
537
+ const resource = resourceByRelative.get(reference);
538
+ if (!resource) {
539
+ const key = `${current.relative}\u0000${reference}`;
540
+ if (!missingResources.has(key)) {
541
+ diagnostics.push(diagnostic("resource-missing", current.fullRelative, `Local resource reference does not exist: ${reference}.`));
542
+ missingResources.add(key);
543
+ }
544
+ continue;
545
+ }
546
+ if (reachable.has(reference)) continue;
547
+ reachable.add(reference);
548
+ pending.push({ relative: reference, fullRelative: resource.fullRelative, path: resource.absolute });
549
+ }
550
+ }
551
+ for (const resource of resources) {
552
+ const resourceText = textResource(resource.absolute);
553
+ if (/\.md$/i.test(resource.relative) && resourceText !== null && lineCount(resourceText) > 300) {
554
+ diagnostics.push(diagnostic("resource-line-limit", resource.fullRelative, `Markdown resource has ${lineCount(resourceText)} lines; maximum is 300.`));
555
+ }
556
+ if (/\.md$/i.test(resource.relative) && resourceText !== null) {
557
+ validateSlop(resourceText, resource.fullRelative, diagnostics);
558
+ }
559
+ if (!reachable.has(resource.relative)) {
560
+ diagnostics.push(diagnostic("resource-orphan", resource.fullRelative, "Resource is not reachable from SKILL.md."));
561
+ }
562
+ if (resource.relative.startsWith("scripts/")) validateScript(resource.absolute, resource.fullRelative, diagnostics);
563
+ }
564
+
565
+ return {
566
+ diagnostics,
567
+ skillName,
568
+ path: skillRelative,
569
+ paragraphs: normalizedParagraphs(body).map((paragraph) => ({ paragraph, path: skillRelative })),
570
+ metadata: parsed.metadata,
571
+ body,
572
+ };
573
+ }
574
+
575
+ function manifestPaths(pluginRoot, manifest, pluginRelative, diagnostics) {
576
+ const allowed = new Set([".agents/plugin.json", "README.md"]);
577
+ const paths = [];
578
+ for (const field of COMPONENT_FIELDS) {
579
+ const raw = manifest[field];
580
+ if (raw === undefined) continue;
581
+ const resolved = safeRelative(pluginRoot, raw);
582
+ if (!resolved) {
583
+ diagnostics.push(diagnostic("manifest-path", `${pluginRelative}/.agents/plugin.json`, `${field} must be a safe plugin-relative path.`));
584
+ continue;
585
+ }
586
+ if (!existsSync(resolved.absolute)) {
587
+ diagnostics.push(diagnostic("manifest-path-missing", `${pluginRelative}/.agents/plugin.json`, `${field} path does not exist: ${raw}`));
588
+ continue;
589
+ }
590
+ paths.push(resolved.relative);
591
+ }
592
+ const iface = manifest.interface;
593
+ if (iface && typeof iface === "object" && !Array.isArray(iface)) {
594
+ for (const field of ["composerIcon", "logo"]) {
595
+ const raw = iface[field];
596
+ if (raw === undefined) continue;
597
+ const resolved = safeRelative(pluginRoot, raw);
598
+ if (!resolved || !isFile(resolved.absolute)) {
599
+ diagnostics.push(diagnostic("manifest-asset", `${pluginRelative}/.agents/plugin.json`, `${field} must reference an existing plugin-relative file.`));
600
+ } else {
601
+ paths.push(resolved.relative);
602
+ }
603
+ }
604
+ }
605
+ if (manifest.assets !== undefined) {
606
+ if (!Array.isArray(manifest.assets)) {
607
+ diagnostics.push(diagnostic("assets-path", `${pluginRelative}/.agents/plugin.json`, "assets must be an array of plugin-relative paths."));
608
+ } else {
609
+ for (const raw of manifest.assets) {
610
+ const resolved = safeRelative(pluginRoot, raw);
611
+ if (!resolved || !existsSync(resolved.absolute)) {
612
+ diagnostics.push(diagnostic("assets-path", `${pluginRelative}/.agents/plugin.json`, `assets entry must be a safe, existing plugin-relative path: ${JSON.stringify(raw)}`));
613
+ } else {
614
+ paths.push(resolved.relative);
615
+ }
616
+ }
617
+ }
618
+ }
619
+ if (manifest.principles !== undefined) {
620
+ const resolved = safeRelative(pluginRoot, manifest.principles);
621
+ if (!resolved || !isFile(resolved.absolute)) {
622
+ diagnostics.push(diagnostic("principles-config", `${pluginRelative}/.agents/plugin.json`, "principles must reference an existing plugin-relative config file."));
623
+ } else {
624
+ paths.push(resolved.relative);
625
+ try {
626
+ const config = JSON.parse(readFileSync(resolved.absolute, "utf8"));
627
+ if (!Array.isArray(config.fragments)) {
628
+ diagnostics.push(diagnostic("principles-config", `${pluginRelative}/${resolved.relative}`, "Prompt config must declare a fragments array."));
629
+ } else {
630
+ for (const fragment of config.fragments) {
631
+ const fragmentPath = safeRelative(dirname(resolved.absolute), fragment?.file);
632
+ if (!fragmentPath || !isFile(fragmentPath.absolute) || !isWithin(pluginRoot, fragmentPath.absolute)) {
633
+ diagnostics.push(diagnostic("principles-fragment", `${pluginRelative}/${resolved.relative}`, "Each prompt fragment must reference an existing file inside the plugin."));
634
+ } else {
635
+ paths.push(toPosix(relative(pluginRoot, fragmentPath.absolute)));
636
+ }
637
+ }
638
+ }
639
+ } catch (err) {
640
+ diagnostics.push(diagnostic("principles-config", `${pluginRelative}/${resolved.relative}`, `Cannot parse prompt config: ${err instanceof Error ? err.message : String(err)}`));
641
+ }
642
+ }
643
+ }
644
+ if (typeof manifest.validate === "string") {
645
+ const localScript = /(?:^|\s)(?:node\s+)?(?:\.\/)?([A-Za-z0-9_./-]+\.(?:mjs|cjs|js|py|sh))(?:\s|$)/.exec(manifest.validate);
646
+ if (localScript) {
647
+ const resolved = safeRelative(pluginRoot, localScript[1]);
648
+ if (!resolved || !isFile(resolved.absolute)) {
649
+ diagnostics.push(diagnostic("manifest-validate-script", `${pluginRelative}/.agents/plugin.json`, `validate references a missing local script: ${localScript[1]}`));
650
+ } else {
651
+ paths.push(resolved.relative);
652
+ }
653
+ }
654
+ }
655
+ for (const path of paths) allowed.add(path);
656
+ return allowed;
657
+ }
658
+
659
+ function validatePluginRoot(pluginRoot, pluginName, tracked, diagnostics, schemaRoot) {
660
+ const pluginRelative = `extensions/${pluginName}`;
661
+ const manifestPath = join(pluginRoot, ".agents", "plugin.json");
662
+ if (!isFile(manifestPath)) {
663
+ diagnostics.push(diagnostic("manifest-missing", `${pluginRelative}/.agents/plugin.json`, "Plugin requires a canonical .agents/plugin.json."));
664
+ return { skills: [] };
665
+ }
666
+ let manifest;
667
+ try {
668
+ manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
669
+ } catch (err) {
670
+ diagnostics.push(diagnostic("manifest-invalid-json", `${pluginRelative}/.agents/plugin.json`, err instanceof Error ? err.message : String(err)));
671
+ return { skills: [] };
672
+ }
673
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
674
+ diagnostics.push(diagnostic("manifest-shape", `${pluginRelative}/.agents/plugin.json`, "Plugin manifest must be an object."));
675
+ return { skills: [] };
676
+ }
677
+ validateCanonicalManifest(schemaRoot, manifest, pluginRelative, diagnostics);
678
+ const requiredTargets = Array.isArray(manifest.targets) && manifest.targets.at(-1) === OPT_IN_TARGET
679
+ ? manifest.targets.slice(0, -1)
680
+ : manifest.targets;
681
+ if (!Array.isArray(requiredTargets) || requiredTargets.join("\u0000") !== ALL_SURFACE_TARGETS.join("\u0000")) {
682
+ diagnostics.push(diagnostic("manifest-targets", `${pluginRelative}/.agents/plugin.json`, `targets must be exactly ${JSON.stringify(ALL_SURFACE_TARGETS)}, optionally followed by "${OPT_IN_TARGET}".`));
683
+ }
684
+ if (typeof manifest.version !== "string" || !/^\d+\.\d+\.\d+$/.test(manifest.version)) {
685
+ diagnostics.push(diagnostic("manifest-version", `${pluginRelative}/.agents/plugin.json`, "version must be semantic MAJOR.MINOR.PATCH."));
686
+ }
687
+ const allowed = manifestPaths(pluginRoot, manifest, pluginRelative, diagnostics);
688
+ const files = tracked ?? listFiles(pluginRoot).map((file) => `${pluginRelative}/${file.relative}`);
689
+ const extras = new Set();
690
+ for (const path of files) {
691
+ if (!path.startsWith(`${pluginRelative}/`)) continue;
692
+ const rel = path.slice(pluginRelative.length + 1);
693
+ const isAllowed = [...allowed].some((prefix) => rel === prefix || rel.startsWith(`${prefix}/`));
694
+ if (!isAllowed) extras.add(rel.split("/")[0]);
695
+ }
696
+ for (const extra of [...extras].sort()) {
697
+ diagnostics.push(diagnostic("plugin-root-entry", `${pluginRelative}/${extra}`, "Tracked plugin-root content must be manifest-declared, README.md, or an exact manifest asset reference."));
698
+ }
699
+
700
+ const skillResults = [];
701
+ const skillsRoot = join(pluginRoot, "skills");
702
+ if (isDirectory(skillsRoot)) {
703
+ for (const entry of readdirSync(skillsRoot, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
704
+ if (isIgnoredNoise(entry.name)) continue;
705
+ if (entry.name.startsWith(".")) {
706
+ diagnostics.push(diagnostic("skills-hidden-entry", `${pluginRelative}/skills/${entry.name}`, "skills/ may not contain hidden entries."));
707
+ continue;
708
+ }
709
+ if (!entry.isDirectory()) {
710
+ diagnostics.push(diagnostic("skills-loose-file", `${pluginRelative}/skills/${entry.name}`, "skills/ may contain only skill directories."));
711
+ continue;
712
+ }
713
+ skillResults.push(validateSkill(join(skillsRoot, entry.name), pluginName));
714
+ }
715
+ }
716
+ validateSkillRoutes(skillResults, diagnostics);
717
+ return { skills: skillResults };
718
+ }
719
+
720
+ function parseVersion(text) {
721
+ try {
722
+ const value = JSON.parse(text)?.version;
723
+ if (typeof value !== "string" || !/^\d+\.\d+\.\d+$/.test(value)) return null;
724
+ return value.split(".").map(Number);
725
+ } catch {
726
+ return null;
727
+ }
728
+ }
729
+
730
+ function manifestWithoutVersion(text) {
731
+ try {
732
+ const manifest = JSON.parse(text);
733
+ delete manifest.version;
734
+ return JSON.stringify(manifest);
735
+ } catch {
736
+ return null;
737
+ }
738
+ }
739
+
740
+ function versionIncreased(before, after) {
741
+ for (let index = 0; index < before.length; index += 1) {
742
+ if (after[index] !== before[index]) return after[index] > before[index];
743
+ }
744
+ return false;
745
+ }
746
+
747
+ export function validateVersionChanges({ repoRoot, currentRoot, base, staged = false }) {
748
+ const diagnostics = [];
749
+ const changed = changedFiles(repoRoot, base, staged);
750
+ const pluginNames = new Set(
751
+ changed
752
+ .map((path) => /^extensions\/([^/]+)\//.exec(path)?.[1])
753
+ .filter(Boolean),
754
+ );
755
+ for (const pluginName of pluginNames) {
756
+ const prefix = `extensions/${pluginName}/`;
757
+ const manifestRel = `${prefix}.agents/plugin.json`;
758
+ const pluginChanges = changed.filter((path) => path.startsWith(prefix));
759
+ const previousManifest = gitFile(repoRoot, base, manifestRel);
760
+ const currentManifestPath = join(currentRoot, manifestRel);
761
+ const currentManifest = isFile(currentManifestPath) ? readFileSync(currentManifestPath, "utf8") : null;
762
+ if (!previousManifest || !currentManifest) continue;
763
+ const nonVersionPluginChange = pluginChanges.some((path) => path !== manifestRel) ||
764
+ manifestWithoutVersion(previousManifest) !== manifestWithoutVersion(currentManifest);
765
+ if (!nonVersionPluginChange) continue;
766
+ const before = parseVersion(previousManifest);
767
+ const after = parseVersion(currentManifest);
768
+ if (!before || !after || !versionIncreased(before, after)) {
769
+ diagnostics.push(diagnostic("plugin-version-not-bumped", manifestRel, `Plugin content changed but version did not increase from ${before?.join(".") ?? "invalid"} to ${after?.join(".") ?? "invalid"}.`));
770
+ }
771
+ }
772
+ return diagnostics;
773
+ }
774
+
775
+ // Bundles kept in `extensions/` for REFERENCE only -- never installed, so their
776
+ // skill names cannot collide with anything at runtime. `engineering` is a fork
777
+ // of a subset of enact-repo's catalog, retained to compare against; it shares 19
778
+ // skill names with enact-repo by construction and exempting it is what makes the
779
+ // global-uniqueness rule enforceable rather than permanently red.
780
+ const REFERENCE_ONLY_PLUGINS = new Set(["engineering"]);
781
+
782
+ /**
783
+ * One skill name, one owner, across every installable extension.
784
+ *
785
+ * Skill identity is the directory name on every host, and directory nesting
786
+ * does NOT namespace it (Cursor: "The category folder is purely organizational";
787
+ * OpenCode v2 derives the id from the last path segment). Where a host reads a
788
+ * flat skills directory, two extensions shipping one name silently shadow each
789
+ * other. Keeping names globally unique is the only mitigation that works
790
+ * everywhere, so it is enforced at authoring time rather than discovered at
791
+ * install time.
792
+ */
793
+ function pushSkillNameCollisionDiagnostics(skillOwners, diagnostics) {
794
+ for (const [skillName, owners] of skillOwners) {
795
+ if (owners.length < 2) continue;
796
+ const plugins = owners.map((o) => o.plugin).sort();
797
+ for (const owner of owners) {
798
+ diagnostics.push(
799
+ diagnostic(
800
+ "skill-name-collision",
801
+ owner.path,
802
+ `Skill name ${JSON.stringify(skillName)} is shipped by ${plugins.length} extensions (${plugins.join(", ")}). ` +
803
+ `Skill names are a global namespace -- on a host that reads a flat skills directory one silently shadows the other. Rename all but one.`,
804
+ ),
805
+ );
806
+ }
807
+ }
808
+ }
809
+
810
+ export function validateRepository({ root, tracked = null }) {
811
+ const diagnostics = [];
812
+ const extensionsRoot = join(root, "extensions");
813
+ if (!isDirectory(extensionsRoot)) {
814
+ return [diagnostic("extensions-missing", "extensions", "Repository requires an extensions directory.")];
815
+ }
816
+ const presentTracked = tracked?.filter((path) => isFile(join(root, path))) ?? null;
817
+ const allParagraphs = new Map();
818
+ // Skill directory name -> the plugins shipping it. A skill name is a GLOBAL
819
+ // identifier: hosts that read a flat skills directory (OpenCode reads
820
+ // `.opencode/skills/<name>/SKILL.md`) key on the directory name alone, and
821
+ // nesting does not namespace it. Two extensions shipping one name there is
822
+ // last-writer-wins, silently.
823
+ const skillOwners = new Map();
824
+ for (const entry of readdirSync(extensionsRoot, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
825
+ if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
826
+ const plugin = validatePluginRoot(join(extensionsRoot, entry.name), entry.name, presentTracked, diagnostics, root);
827
+ for (const skill of plugin.skills) {
828
+ diagnostics.push(...skill.diagnostics);
829
+ if (!REFERENCE_ONLY_PLUGINS.has(entry.name) && typeof skill.skillName === "string") {
830
+ const owners = skillOwners.get(skill.skillName) ?? [];
831
+ owners.push({ plugin: entry.name, path: skill.path });
832
+ skillOwners.set(skill.skillName, owners);
833
+ }
834
+ for (const record of skill.paragraphs) {
835
+ const existing = allParagraphs.get(record.paragraph) ?? [];
836
+ existing.push(record.path);
837
+ allParagraphs.set(record.paragraph, existing);
838
+ }
839
+ }
840
+ }
841
+ pushSkillNameCollisionDiagnostics(skillOwners, diagnostics);
842
+ pushDuplicateParagraphDiagnostics(allParagraphs, diagnostics);
843
+ return diagnostics.sort((a, b) => a.path.localeCompare(b.path) || a.code.localeCompare(b.code));
844
+ }
845
+
846
+ export function validatePlugin({ pluginRoot, tracked = null }) {
847
+ const root = findGitRoot(pluginRoot);
848
+ const pluginName = basename(pluginRoot);
849
+ const repoRelative = root ? toPosix(relative(root, pluginRoot)) : null;
850
+ const selectedTracked = tracked ?? (root && repoRelative?.startsWith("extensions/")
851
+ ? trackedFiles(root)
852
+ .filter((path) => path.startsWith(`${repoRelative}/`))
853
+ .filter((path) => isFile(join(root, path)))
854
+ : null);
855
+ const diagnostics = [];
856
+ const result = validatePluginRoot(pluginRoot, pluginName, selectedTracked, diagnostics, VALIDATOR_ROOT);
857
+ for (const skill of result.skills) diagnostics.push(...skill.diagnostics);
858
+ const paragraphs = new Map();
859
+ for (const skill of result.skills) {
860
+ for (const record of skill.paragraphs) {
861
+ const existing = paragraphs.get(record.paragraph) ?? [];
862
+ existing.push(record.path);
863
+ paragraphs.set(record.paragraph, existing);
864
+ }
865
+ }
866
+ pushDuplicateParagraphDiagnostics(paragraphs, diagnostics);
867
+ return diagnostics.sort((a, b) => a.path.localeCompare(b.path) || a.code.localeCompare(b.code));
868
+ }
869
+
870
+ export function formatDiagnostics(diagnostics) {
871
+ return diagnostics.map((item) => `[${item.code}] ${item.path}: ${item.message}`);
872
+ }