@amsterdamdatalabs/enact-extensions 0.1.42 → 0.1.48

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 (596) hide show
  1. package/README.md +67 -108
  2. package/bundled-extensions.json +3 -0
  3. package/dist/create/claude.d.ts.map +1 -1
  4. package/dist/create/claude.js +1 -1
  5. package/dist/create/claude.js.map +1 -1
  6. package/dist/create/codex.d.ts.map +1 -1
  7. package/dist/create/codex.js +2 -3
  8. package/dist/create/codex.js.map +1 -1
  9. package/dist/create/cursor.d.ts.map +1 -1
  10. package/dist/create/cursor.js +3 -7
  11. package/dist/create/cursor.js.map +1 -1
  12. package/dist/create/enact.d.ts.map +1 -1
  13. package/dist/create/enact.js +2 -0
  14. package/dist/create/enact.js.map +1 -1
  15. package/dist/create/index.d.ts +4 -1
  16. package/dist/create/index.d.ts.map +1 -1
  17. package/dist/create/index.js +9 -3
  18. package/dist/create/index.js.map +1 -1
  19. package/dist/create/kimi.d.ts +16 -0
  20. package/dist/create/kimi.d.ts.map +1 -0
  21. package/dist/create/kimi.js +86 -0
  22. package/dist/create/kimi.js.map +1 -0
  23. package/dist/create/opencode.d.ts +129 -0
  24. package/dist/create/opencode.d.ts.map +1 -0
  25. package/dist/create/opencode.js +689 -0
  26. package/dist/create/opencode.js.map +1 -0
  27. package/dist/index.d.ts +14 -19
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +11 -11
  30. package/dist/index.js.map +1 -1
  31. package/dist/internal/agents.d.ts +11 -1
  32. package/dist/internal/agents.d.ts.map +1 -1
  33. package/dist/internal/agents.js +120 -4
  34. package/dist/internal/agents.js.map +1 -1
  35. package/dist/internal/extensions-table.d.ts +87 -0
  36. package/dist/internal/extensions-table.d.ts.map +1 -0
  37. package/dist/internal/extensions-table.js +195 -0
  38. package/dist/internal/extensions-table.js.map +1 -0
  39. package/dist/internal/file-ops.d.ts +36 -0
  40. package/dist/internal/file-ops.d.ts.map +1 -0
  41. package/dist/internal/file-ops.js +89 -0
  42. package/dist/internal/file-ops.js.map +1 -0
  43. package/dist/internal/hooks.d.ts +56 -0
  44. package/dist/internal/hooks.d.ts.map +1 -0
  45. package/dist/internal/hooks.js +128 -0
  46. package/dist/internal/hooks.js.map +1 -0
  47. package/dist/internal/io.d.ts.map +1 -1
  48. package/dist/internal/io.js.map +1 -1
  49. package/dist/internal/json-merge.d.ts +63 -0
  50. package/dist/internal/json-merge.d.ts.map +1 -0
  51. package/dist/internal/json-merge.js +124 -0
  52. package/dist/internal/json-merge.js.map +1 -0
  53. package/dist/internal/lock-ownership.d.ts +41 -0
  54. package/dist/internal/lock-ownership.d.ts.map +1 -0
  55. package/dist/internal/lock-ownership.js +24 -0
  56. package/dist/internal/lock-ownership.js.map +1 -0
  57. package/dist/internal/pick.d.ts.map +1 -1
  58. package/dist/internal/pick.js.map +1 -1
  59. package/dist/internal/platform.d.ts.map +1 -1
  60. package/dist/internal/platform.js +82 -42
  61. package/dist/internal/platform.js.map +1 -1
  62. package/dist/internal/schema.d.ts.map +1 -1
  63. package/dist/internal/schema.js.map +1 -1
  64. package/dist/internal/toml-block.d.ts +59 -0
  65. package/dist/internal/toml-block.d.ts.map +1 -0
  66. package/dist/internal/toml-block.js +113 -0
  67. package/dist/internal/toml-block.js.map +1 -0
  68. package/dist/internal/types.d.ts +2 -1
  69. package/dist/internal/types.d.ts.map +1 -1
  70. package/dist/projection.d.ts +47 -0
  71. package/dist/projection.d.ts.map +1 -0
  72. package/dist/projection.js +180 -0
  73. package/dist/projection.js.map +1 -0
  74. package/dist/validate/index.d.ts +10 -5
  75. package/dist/validate/index.d.ts.map +1 -1
  76. package/dist/validate/index.js +59 -10
  77. package/dist/validate/index.js.map +1 -1
  78. package/dist/validate/kimi.d.ts +3 -0
  79. package/dist/validate/kimi.d.ts.map +1 -0
  80. package/dist/validate/kimi.js +11 -0
  81. package/dist/validate/kimi.js.map +1 -0
  82. package/dist/validate/opencode.d.ts +3 -0
  83. package/dist/validate/opencode.d.ts.map +1 -0
  84. package/dist/validate/opencode.js +11 -0
  85. package/dist/validate/opencode.js.map +1 -0
  86. package/dist/validate/run.d.ts +39 -0
  87. package/dist/validate/run.d.ts.map +1 -0
  88. package/dist/validate/run.js +60 -0
  89. package/dist/validate/run.js.map +1 -0
  90. package/extensions/enact-loop/.agents/plugin.json +12 -8
  91. package/extensions/enact-loop/scripts/validate.mjs +84 -2
  92. package/extensions/enact-loop/skills/enact-loop/SKILL.md +36 -466
  93. package/extensions/enact-loop/skills/enact-loop/references/loop-contract.md +114 -0
  94. package/extensions/enact-loop/skills/loop-budget/SKILL.md +43 -26
  95. package/extensions/enact-loop/skills/loop-intake/SKILL.md +44 -32
  96. package/extensions/enact-repo/.agents/plugin.json +55 -0
  97. package/extensions/enact-repo/.mcp.json +20 -0
  98. package/extensions/enact-repo/README.md +90 -0
  99. package/extensions/enact-repo/hooks/hooks.json +98 -0
  100. package/extensions/enact-repo/lean-ctx/config.toml +35 -0
  101. package/extensions/enact-repo/lean-ctx/layout.toml +5 -0
  102. package/extensions/enact-repo/skills/aca-sandboxes/SKILL.md +53 -0
  103. package/extensions/enact-repo/skills/aca-sandboxes/references/capabilities.md +52 -0
  104. package/extensions/enact-repo/skills/aca-sandboxes/references/install.md +87 -0
  105. package/extensions/enact-repo/skills/aca-sandboxes/references/prerequisites.md +52 -0
  106. package/extensions/enact-repo/skills/aca-sandboxes/references/quickstart.md +164 -0
  107. package/extensions/enact-repo/skills/aca-sandboxes/references/reference-advanced.md +251 -0
  108. package/extensions/enact-repo/skills/aca-sandboxes/references/reference.md +287 -0
  109. package/extensions/enact-repo/skills/aca-sandboxes/references/response-cues.md +71 -0
  110. package/extensions/enact-repo/skills/aca-sandboxes/references/scenarios.md +219 -0
  111. package/extensions/enact-repo/skills/aca-sandboxes/version.json +4 -0
  112. package/extensions/enact-repo/skills/backend-remove-deadcode/SKILL.md +96 -0
  113. package/extensions/enact-repo/skills/cmux-leader/SKILL.md +61 -0
  114. package/extensions/enact-repo/skills/cmux-send/SKILL.md +61 -0
  115. package/extensions/enact-repo/skills/code-review/SKILL.md +100 -0
  116. package/extensions/enact-repo/skills/codebase-design/SKILL.md +98 -0
  117. package/extensions/enact-repo/skills/codebase-design/references/DEEPENING.md +37 -0
  118. package/extensions/enact-repo/skills/codebase-design/references/DESIGN-IT-TWICE.md +44 -0
  119. package/extensions/enact-repo/skills/codebase-design/references/TESTABILITY.md +29 -0
  120. package/extensions/enact-repo/skills/computer-use/SKILL.md +76 -0
  121. package/extensions/enact-repo/skills/debug-issue/SKILL.md +40 -0
  122. package/extensions/enact-repo/skills/diagnosing-bugs/SKILL.md +100 -0
  123. package/extensions/enact-repo/skills/diagnosing-bugs/references/FEEDBACK-LOOP-TACTICS.md +34 -0
  124. package/extensions/enact-repo/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
  125. package/extensions/enact-repo/skills/domain-modeling/SKILL.md +86 -0
  126. package/extensions/enact-repo/skills/domain-modeling/references/ADR-FORMAT.md +47 -0
  127. package/extensions/enact-repo/skills/domain-modeling/references/CONTEXT-FORMAT.md +60 -0
  128. package/extensions/enact-repo/skills/explore-codebase/SKILL.md +41 -0
  129. package/extensions/enact-repo/skills/frontend-design/LICENSE.txt +177 -0
  130. package/extensions/enact-repo/skills/frontend-design/SKILL.md +56 -0
  131. package/extensions/enact-repo/skills/grill-me/SKILL.md +20 -0
  132. package/extensions/enact-repo/skills/grill-with-docs/SKILL.md +20 -0
  133. package/extensions/enact-repo/skills/handoff/SKILL.md +28 -0
  134. package/extensions/enact-repo/skills/http-contract-migration/SKILL.md +74 -0
  135. package/extensions/enact-repo/skills/http-contract-migration/references/compatibility-and-gates.md +26 -0
  136. package/extensions/enact-repo/skills/http-contract-migration/references/hexagonal-boundary.md +16 -0
  137. package/extensions/enact-repo/skills/http-contract-migration/references/publication-and-adoption.md +31 -0
  138. package/extensions/enact-repo/skills/http-contract-migration/references/wire-fidelity-and-dto.md +31 -0
  139. package/extensions/enact-repo/skills/implement/SKILL.md +28 -0
  140. package/extensions/enact-repo/skills/improve-codebase-architecture/SKILL.md +80 -0
  141. package/extensions/enact-repo/skills/improve-codebase-architecture/references/HTML-REPORT.md +123 -0
  142. package/extensions/enact-repo/skills/interaction-design/SKILL.md +30 -0
  143. package/extensions/enact-repo/skills/interaction-design/references/Workflow.md +146 -0
  144. package/extensions/enact-repo/skills/interaction-design/references/animation-libraries-gsap.md +219 -0
  145. package/extensions/enact-repo/skills/interaction-design/references/animation-libraries-native.md +110 -0
  146. package/extensions/enact-repo/skills/interaction-design/references/animation-libraries.md +177 -0
  147. package/extensions/enact-repo/skills/interaction-design/references/interaction-patterns.md +166 -0
  148. package/extensions/enact-repo/skills/interaction-design/references/microinteraction-patterns-advanced.md +285 -0
  149. package/extensions/enact-repo/skills/interaction-design/references/microinteraction-patterns.md +254 -0
  150. package/extensions/enact-repo/skills/interaction-design/references/scroll-animations-reveals.md +213 -0
  151. package/extensions/enact-repo/skills/interaction-design/references/scroll-animations.md +208 -0
  152. package/extensions/enact-repo/skills/lean-ctx/SKILL.md +81 -0
  153. package/extensions/enact-repo/skills/orca-cli/SKILL.md +88 -0
  154. package/extensions/enact-repo/skills/orchestration/SKILL.md +80 -0
  155. package/extensions/enact-repo/skills/prototype/SKILL.md +37 -0
  156. package/extensions/enact-repo/skills/prototype/references/LOGIC.md +79 -0
  157. package/extensions/enact-repo/skills/prototype/references/UI.md +112 -0
  158. package/extensions/enact-repo/skills/refactor-safely/SKILL.md +44 -0
  159. package/extensions/enact-repo/skills/remove-deadcode/SKILL.md +54 -0
  160. package/extensions/enact-repo/skills/research/SKILL.md +27 -0
  161. package/extensions/enact-repo/skills/resolving-merge-conflicts/SKILL.md +28 -0
  162. package/extensions/enact-repo/skills/review-changes/SKILL.md +45 -0
  163. package/extensions/enact-repo/skills/tailwind-4-docs/SKILL.md +88 -0
  164. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/accent-color.mdx +149 -0
  165. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/adding-custom-styles.mdx +635 -0
  166. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/align-content.mdx +300 -0
  167. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/align-items.mdx +241 -0
  168. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/align-self.mdx +294 -0
  169. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/animation.mdx +286 -0
  170. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/appearance.mdx +180 -0
  171. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/aspect-ratio.mdx +98 -0
  172. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-blur.mdx +109 -0
  173. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-brightness.mdx +82 -0
  174. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-contrast.mdx +82 -0
  175. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-grayscale.mdx +99 -0
  176. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-hue-rotate.mdx +173 -0
  177. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-invert.mdx +99 -0
  178. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-opacity.mdx +103 -0
  179. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-saturate.mdx +98 -0
  180. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter-sepia.mdx +95 -0
  181. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backdrop-filter.mdx +113 -0
  182. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/backface-visibility.mdx +110 -0
  183. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-attachment.mdx +126 -0
  184. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-blend-mode.mdx +79 -0
  185. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-clip.mdx +85 -0
  186. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-color.mdx +176 -0
  187. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-image.mdx +349 -0
  188. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-origin.mdx +64 -0
  189. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-position.mdx +171 -0
  190. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-repeat.mdx +127 -0
  191. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/background-size.mdx +100 -0
  192. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/block-size.mdx +190 -0
  193. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/border-collapse.mdx +181 -0
  194. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/border-color.mdx +316 -0
  195. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/border-radius.mdx +347 -0
  196. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/border-spacing.mdx +117 -0
  197. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/border-style.mdx +129 -0
  198. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/border-width.mdx +344 -0
  199. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/box-decoration-break.mdx +71 -0
  200. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/box-shadow.mdx +497 -0
  201. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/box-sizing.mdx +175 -0
  202. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/break-after.mdx +38 -0
  203. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/break-before.mdx +38 -0
  204. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/break-inside.mdx +34 -0
  205. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/caption-side.mdx +190 -0
  206. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/caret-color.mdx +66 -0
  207. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/clear.mdx +224 -0
  208. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/color-scheme.mdx +81 -0
  209. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/color.mdx +125 -0
  210. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/colors.mdx +927 -0
  211. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/columns.mdx +310 -0
  212. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/compatibility.mdx +193 -0
  213. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/content.mdx +129 -0
  214. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/cursor.mdx +104 -0
  215. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/dark-mode.mdx +191 -0
  216. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/detecting-classes-in-source-files.mdx +315 -0
  217. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/display.mdx +523 -0
  218. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/editor-setup.mdx +90 -0
  219. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/field-sizing.mdx +75 -0
  220. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/fill.mdx +100 -0
  221. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-blur.mdx +114 -0
  222. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-brightness.mdx +102 -0
  223. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-contrast.mdx +102 -0
  224. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-drop-shadow.mdx +230 -0
  225. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-grayscale.mdx +101 -0
  226. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-hue-rotate.mdx +171 -0
  227. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-invert.mdx +93 -0
  228. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-saturate.mdx +109 -0
  229. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter-sepia.mdx +92 -0
  230. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/filter.mdx +113 -0
  231. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/flex-basis.mdx +144 -0
  232. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/flex-direction.mdx +130 -0
  233. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/flex-grow.mdx +129 -0
  234. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/flex-shrink.mdx +95 -0
  235. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/flex-wrap.mdx +118 -0
  236. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/flex.mdx +164 -0
  237. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/float.mdx +197 -0
  238. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-family.mdx +119 -0
  239. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-feature-settings.mdx +52 -0
  240. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-size.mdx +184 -0
  241. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-smoothing.mdx +67 -0
  242. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-stretch.mdx +138 -0
  243. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-style.mdx +62 -0
  244. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-variant-numeric.mdx +228 -0
  245. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/font-weight.mdx +105 -0
  246. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/forced-color-adjust.mdx +152 -0
  247. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/functions-and-directives.mdx +322 -0
  248. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/gap.mdx +100 -0
  249. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-auto-columns.mdx +43 -0
  250. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-auto-flow.mdx +58 -0
  251. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-auto-rows.mdx +43 -0
  252. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-column.mdx +124 -0
  253. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-row.mdx +115 -0
  254. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-template-columns.mdx +101 -0
  255. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/grid-template-rows.mdx +108 -0
  256. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/height.mdx +260 -0
  257. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/hover-focus-and-other-states.mdx +4935 -0
  258. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/hyphens.mdx +112 -0
  259. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/inline-size.mdx +191 -0
  260. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/isolation.mdx +30 -0
  261. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/justify-content.mdx +348 -0
  262. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/justify-items.mdx +286 -0
  263. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/justify-self.mdx +282 -0
  264. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/letter-spacing.mdx +100 -0
  265. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/line-clamp.mdx +110 -0
  266. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/line-height.mdx +147 -0
  267. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/list-style-image.mdx +80 -0
  268. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/list-style-position.mdx +67 -0
  269. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/list-style-type.mdx +86 -0
  270. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/margin.mdx +397 -0
  271. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-clip.mdx +66 -0
  272. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-composite.mdx +70 -0
  273. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-image.mdx +933 -0
  274. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-mode.mdx +114 -0
  275. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-origin.mdx +66 -0
  276. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-position.mdx +117 -0
  277. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-repeat.mdx +169 -0
  278. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-size.mdx +104 -0
  279. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mask-type.mdx +98 -0
  280. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/max-block-size.mdx +153 -0
  281. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/max-height.mdx +164 -0
  282. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/max-inline-size.mdx +185 -0
  283. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/max-width.mdx +214 -0
  284. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/min-block-size.mdx +146 -0
  285. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/min-height.mdx +150 -0
  286. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/min-inline-size.mdx +153 -0
  287. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/min-width.mdx +153 -0
  288. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/mix-blend-mode.mdx +103 -0
  289. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/object-fit.mdx +147 -0
  290. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/object-position.mdx +182 -0
  291. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/opacity.mdx +84 -0
  292. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/order.mdx +94 -0
  293. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/outline-color.mdx +130 -0
  294. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/outline-offset.mdx +72 -0
  295. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/outline-style.mdx +134 -0
  296. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/outline-width.mdx +91 -0
  297. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/overflow-wrap.mdx +130 -0
  298. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/overflow.mdx +665 -0
  299. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/overscroll-behavior.mdx +151 -0
  300. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/padding.mdx +232 -0
  301. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/perspective-origin.mdx +127 -0
  302. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/perspective.mdx +137 -0
  303. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/place-content.mdx +257 -0
  304. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/place-items.mdx +269 -0
  305. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/place-self.mdx +257 -0
  306. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/pointer-events.mdx +101 -0
  307. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/position.mdx +399 -0
  308. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/preflight.mdx +291 -0
  309. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/resize.mdx +110 -0
  310. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/responsive-design.mdx +666 -0
  311. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/rotate.mdx +291 -0
  312. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scale.mdx +360 -0
  313. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scroll-behavior.mdx +39 -0
  314. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scroll-margin.mdx +268 -0
  315. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scroll-padding.mdx +255 -0
  316. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scroll-snap-align.mdx +281 -0
  317. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scroll-snap-stop.mdx +198 -0
  318. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/scroll-snap-type.mdx +284 -0
  319. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/skew.mdx +352 -0
  320. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/stroke-width.mdx +74 -0
  321. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/stroke.mdx +103 -0
  322. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/styling-with-utility-classes.mdx +1020 -0
  323. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/table-layout.mdx +212 -0
  324. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-align.mdx +162 -0
  325. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-decoration-color.mdx +155 -0
  326. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-decoration-line.mdx +143 -0
  327. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-decoration-style.mdx +75 -0
  328. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-decoration-thickness.mdx +75 -0
  329. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-indent.mdx +74 -0
  330. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-overflow.mdx +103 -0
  331. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-shadow.mdx +177 -0
  332. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-transform.mdx +106 -0
  333. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-underline-offset.mdx +74 -0
  334. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/text-wrap.mdx +144 -0
  335. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/theme.mdx +1142 -0
  336. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/top-right-bottom-left.mdx +296 -0
  337. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/touch-action.mdx +95 -0
  338. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transform-origin.mdx +130 -0
  339. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transform-style.mdx +110 -0
  340. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transform.mdx +51 -0
  341. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transition-behavior.mdx +76 -0
  342. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transition-delay.mdx +76 -0
  343. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transition-duration.mdx +82 -0
  344. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transition-property.mdx +137 -0
  345. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/transition-timing-function.mdx +89 -0
  346. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/translate.mdx +487 -0
  347. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/upgrade-guide.mdx +910 -0
  348. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/user-select.mdx +114 -0
  349. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/vertical-align.mdx +194 -0
  350. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/visibility.mdx +287 -0
  351. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/white-space.mdx +211 -0
  352. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/width.mdx +264 -0
  353. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/will-change.mdx +47 -0
  354. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/word-break.mdx +94 -0
  355. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs/z-index.mdx +104 -0
  356. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs-catalog.md +251 -0
  357. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs-index.tsx +237 -0
  358. package/extensions/enact-repo/skills/tailwind-4-docs/references/docs-source.txt +6 -0
  359. package/extensions/enact-repo/skills/tailwind-4-docs/references/gotchas.md +18 -0
  360. package/extensions/enact-repo/skills/tailwind-4-docs/scripts/sync_tailwind_docs.py +183 -0
  361. package/extensions/enact-repo/skills/tdd/SKILL.md +49 -0
  362. package/extensions/enact-repo/skills/tdd/references/mocking.md +59 -0
  363. package/extensions/enact-repo/skills/tdd/references/tests.md +77 -0
  364. package/extensions/enact-repo/skills/teach/SKILL.md +88 -0
  365. package/extensions/enact-repo/skills/teach/references/GLOSSARY-FORMAT.md +35 -0
  366. package/extensions/enact-repo/skills/teach/references/LEARNING-RECORD-FORMAT.md +46 -0
  367. package/extensions/enact-repo/skills/teach/references/MISSION-FORMAT.md +31 -0
  368. package/extensions/enact-repo/skills/teach/references/PHILOSOPHY.md +65 -0
  369. package/extensions/enact-repo/skills/teach/references/RESOURCES-FORMAT.md +32 -0
  370. package/extensions/enact-repo/skills/testing/SKILL.md +54 -0
  371. package/extensions/enact-repo/skills/to-spec/SKILL.md +85 -0
  372. package/extensions/enact-repo/skills/to-tickets/SKILL.md +75 -0
  373. package/extensions/enact-repo/skills/to-tickets/references/TICKET-TEMPLATES.md +39 -0
  374. package/extensions/enact-repo/skills/triage/SKILL.md +99 -0
  375. package/extensions/enact-repo/skills/triage/references/AGENT-BRIEF.md +207 -0
  376. package/extensions/enact-repo/skills/triage/references/NEEDS-INFO-TEMPLATE.md +17 -0
  377. package/extensions/enact-repo/skills/triage/references/OUT-OF-SCOPE.md +105 -0
  378. package/extensions/enact-repo/skills/ui-ux-pro-max/SKILL.md +32 -0
  379. package/extensions/enact-repo/skills/ui-ux-pro-max/references/Workflow.md +207 -0
  380. package/extensions/enact-repo/skills/ui-ux-pro-max/references/workflow-reference.md +175 -0
  381. package/extensions/enact-repo/skills/wayfinder/SKILL.md +100 -0
  382. package/extensions/enact-repo/skills/wayfinder/references/TEMPLATES.md +35 -0
  383. package/extensions/enact-repo/skills/wayfinder/references/TICKET-TYPES.md +8 -0
  384. package/extensions/enact-repo/skills/writing-great-skills/SKILL.md +93 -0
  385. package/extensions/enact-repo/skills/writing-great-skills/references/GLOSSARY.md +201 -0
  386. package/package.json +22 -17
  387. package/scripts/enact-extensions.mjs +411 -1006
  388. package/scripts/lib/allowed-plugins.mjs +52 -0
  389. package/scripts/lib/build-index.mjs +1 -3
  390. package/scripts/lib/resolve-bundle.mjs +1 -3
  391. package/scripts/lib/run-repo-doctor.mjs +604 -0
  392. package/scripts/lib/run-repo-install.mjs +1651 -0
  393. package/scripts/lib/run-sync.mjs +4 -10
  394. package/scripts/lib/run-validate.mjs +23 -39
  395. package/scripts/lib/skill-validator.mjs +1096 -0
  396. package/spec/enact.json +21 -3
  397. package/spec/enact.md +13 -12
  398. package/spec/kimi.json +100 -0
  399. package/spec/opencode.json +64 -0
  400. package/dist/install.d.ts +0 -235
  401. package/dist/install.d.ts.map +0 -1
  402. package/dist/install.js +0 -697
  403. package/dist/install.js.map +0 -1
  404. package/dist/internal/claude.d.ts +0 -65
  405. package/dist/internal/claude.d.ts.map +0 -1
  406. package/dist/internal/claude.js +0 -200
  407. package/dist/internal/claude.js.map +0 -1
  408. package/dist/internal/codex.d.ts +0 -43
  409. package/dist/internal/codex.d.ts.map +0 -1
  410. package/dist/internal/codex.js +0 -211
  411. package/dist/internal/codex.js.map +0 -1
  412. package/dist/internal/cursor.d.ts +0 -2
  413. package/dist/internal/cursor.d.ts.map +0 -1
  414. package/dist/internal/cursor.js +0 -6
  415. package/dist/internal/cursor.js.map +0 -1
  416. package/dist/principles.d.ts +0 -28
  417. package/dist/principles.d.ts.map +0 -1
  418. package/dist/principles.js +0 -159
  419. package/dist/principles.js.map +0 -1
  420. package/dist/provision.d.ts +0 -30
  421. package/dist/provision.d.ts.map +0 -1
  422. package/dist/provision.js +0 -202
  423. package/dist/provision.js.map +0 -1
  424. package/extensions/dev-state/.agents/plugin.json +0 -36
  425. package/extensions/dev-state/skills/dev-state-plan-graduation/SKILL.md +0 -194
  426. package/extensions/dev-state/skills/dev-state-plan-graduation/agents/openai.yaml +0 -4
  427. package/extensions/dev-state/skills/dev-state-plan-graduation/references/reference.md +0 -130
  428. package/extensions/enact-context/.agents/plugin.json +0 -41
  429. package/extensions/enact-context/.mcp.json +0 -8
  430. package/extensions/enact-context/README.md +0 -25
  431. package/extensions/enact-context/assets/icon.png +0 -0
  432. package/extensions/enact-context/assets/logo.png +0 -0
  433. package/extensions/enact-context/hooks/hooks.json +0 -115
  434. package/extensions/enact-context/skills/enact-context/SKILL.md +0 -151
  435. package/extensions/enact-context/skills/enact-context/scripts/install.sh +0 -69
  436. package/extensions/enact-core/.agents/plugin.json +0 -40
  437. package/extensions/enact-core/OPERATING-PRINCIPLES.md +0 -7
  438. package/extensions/enact-core/hooks/hooks.json +0 -26
  439. package/extensions/enact-evolve/.agents/plugin.json +0 -47
  440. package/extensions/enact-evolve/agents/evolve-session-analyst.toml +0 -37
  441. package/extensions/enact-evolve/assets/icon.png +0 -0
  442. package/extensions/enact-evolve/assets/logo.png +0 -0
  443. package/extensions/enact-evolve/skills/mined-investigate-loop/SKILL.md +0 -68
  444. package/extensions/enact-evolve/skills/session-analysis/SKILL.md +0 -98
  445. package/extensions/enact-evolve/skills/session-analysis/scripts/run-evolve-analysis.sh +0 -343
  446. package/extensions/enact-factory/.agents/plugin.json +0 -49
  447. package/extensions/enact-factory/.mcp.json +0 -8
  448. package/extensions/enact-factory/agents/architect.toml +0 -34
  449. package/extensions/enact-factory/agents/code-reviewer.toml +0 -33
  450. package/extensions/enact-factory/agents/critic.toml +0 -39
  451. package/extensions/enact-factory/agents/executor.toml +0 -26
  452. package/extensions/enact-factory/agents/explore.toml +0 -25
  453. package/extensions/enact-factory/agents/planner.toml +0 -26
  454. package/extensions/enact-factory/agents/verifier.toml +0 -33
  455. package/extensions/enact-factory/assets/icon.png +0 -0
  456. package/extensions/enact-factory/assets/logo.png +0 -0
  457. package/extensions/enact-factory/skills/advisor/SKILL.md +0 -82
  458. package/extensions/enact-factory/skills/ai-slop-cleaner/SKILL.md +0 -57
  459. package/extensions/enact-factory/skills/autonomous-runner/SKILL.md +0 -353
  460. package/extensions/enact-factory/skills/azdo-ci-strategy/SKILL.md +0 -309
  461. package/extensions/enact-factory/skills/azdo-ci-strategy/references/build-failures.md +0 -60
  462. package/extensions/enact-factory/skills/azdo-ci-strategy/references/cli-reference.md +0 -87
  463. package/extensions/enact-factory/skills/azdo-ci-strategy/references/policies-and-pipelines.md +0 -132
  464. package/extensions/enact-factory/skills/azdo-ci-strategy/references/troubleshooting.md +0 -53
  465. package/extensions/enact-factory/skills/cmux-leader/SKILL.md +0 -74
  466. package/extensions/enact-factory/skills/cmux-send/SKILL.md +0 -50
  467. package/extensions/enact-factory/skills/committee/SKILL.md +0 -80
  468. package/extensions/enact-factory/skills/deep-interview/SKILL.md +0 -68
  469. package/extensions/enact-factory/skills/deslop-handover/SKILL.md +0 -97
  470. package/extensions/enact-factory/skills/deslop-handover/deslop.py +0 -181
  471. package/extensions/enact-factory/skills/drive-loop/SKILL.md +0 -394
  472. package/extensions/enact-factory/skills/drive-loop/references/contract-schema.md +0 -127
  473. package/extensions/enact-factory/skills/handoff/SKILL.md +0 -75
  474. package/extensions/enact-factory/skills/hyperplan/SKILL.md +0 -59
  475. package/extensions/enact-factory/skills/looplan/SKILL.md +0 -120
  476. package/extensions/enact-factory/skills/paseo-dispatch/SKILL.md +0 -141
  477. package/extensions/enact-factory/skills/plan/SKILL.md +0 -103
  478. package/extensions/enact-factory/skills/remove-deadcode/SKILL.md +0 -46
  479. package/extensions/enact-factory/skills/research/SKILL.md +0 -83
  480. package/extensions/enact-factory/skills/review/SKILL.md +0 -67
  481. package/extensions/enact-factory/skills/security-research/SKILL.md +0 -57
  482. package/extensions/enact-factory/skills/tdd/SKILL.md +0 -62
  483. package/extensions/enact-factory/skills/testing-strategy/SKILL.md +0 -172
  484. package/extensions/enact-factory/skills/trace/SKILL.md +0 -42
  485. package/extensions/enact-factory/skills/ultraqa/SKILL.md +0 -85
  486. package/extensions/enact-factory/skills/work-with-workitem/SKILL.md +0 -56
  487. package/extensions/enact-factory/skills/workitem-triage/SKILL.md +0 -42
  488. package/extensions/enact-wiki/.agents/plugin.json +0 -43
  489. package/extensions/enact-wiki/.mcp.json +0 -15
  490. package/extensions/enact-wiki/README.md +0 -44
  491. package/extensions/enact-wiki/assets/icon.png +0 -0
  492. package/extensions/enact-wiki/assets/logo.png +0 -0
  493. package/extensions/enact-wiki/skills/document-parser/SKILL.md +0 -17
  494. package/extensions/enact-wiki/skills/document-parser/scripts/parse.sh +0 -60
  495. package/extensions/enact-wiki/skills/document-parser/skill.json +0 -9
  496. package/extensions/enact-wiki/skills/enact-wiki/SKILL.md +0 -30
  497. package/extensions/enact-wiki/skills/enact-wiki/references/ingest.md +0 -62
  498. package/extensions/enact-wiki/skills/enact-wiki/references/manage.md +0 -34
  499. package/extensions/enact-wiki/skills/enact-wiki/references/query.md +0 -59
  500. package/extensions/enact-wiki/skills/search-lab/SKILL.md +0 -57
  501. package/extensions/enact-wiki/skills/search-lab/scripts/analyze.ts +0 -23
  502. package/extensions/enact-wiki/skills/wiki/SKILL.md +0 -42
  503. package/extensions/net-revenue-management/.agents/plugin.json +0 -40
  504. package/extensions/net-revenue-management/.mcp.json +0 -9
  505. package/extensions/net-revenue-management/skills/net-revenue-risks/SKILL.md +0 -30
  506. package/extensions/net-revenue-management/skills/net-revenue-scenario/SKILL.md +0 -31
  507. package/extensions/plugin-dev/.agents/plugin.json +0 -41
  508. package/extensions/plugin-dev/agents/agent-creator.md +0 -199
  509. package/extensions/plugin-dev/agents/plugin-validator.md +0 -91
  510. package/extensions/plugin-dev/agents/skill-reviewer.md +0 -212
  511. package/extensions/plugin-dev/skills/agent-development/SKILL.md +0 -641
  512. package/extensions/plugin-dev/skills/agent-development/examples/agent-creation-prompt.md +0 -250
  513. package/extensions/plugin-dev/skills/agent-development/examples/complete-agent-examples.md +0 -461
  514. package/extensions/plugin-dev/skills/agent-development/references/advanced-agent-fields.md +0 -246
  515. package/extensions/plugin-dev/skills/agent-development/references/agent-creation-system-prompt.md +0 -216
  516. package/extensions/plugin-dev/skills/agent-development/references/permission-modes-rules.md +0 -226
  517. package/extensions/plugin-dev/skills/agent-development/references/system-prompt-design.md +0 -464
  518. package/extensions/plugin-dev/skills/agent-development/references/triggering-examples.md +0 -474
  519. package/extensions/plugin-dev/skills/agent-development/scripts/create-agent-skeleton.sh +0 -176
  520. package/extensions/plugin-dev/skills/agent-development/scripts/test-agent-trigger.sh +0 -227
  521. package/extensions/plugin-dev/skills/agent-development/scripts/validate-agent.sh +0 -227
  522. package/extensions/plugin-dev/skills/create-plugin/SKILL.md +0 -505
  523. package/extensions/plugin-dev/skills/hook-development/SKILL.md +0 -861
  524. package/extensions/plugin-dev/skills/hook-development/examples/load-context.sh +0 -55
  525. package/extensions/plugin-dev/skills/hook-development/examples/validate-bash.sh +0 -57
  526. package/extensions/plugin-dev/skills/hook-development/examples/validate-write.sh +0 -48
  527. package/extensions/plugin-dev/skills/hook-development/references/advanced.md +0 -871
  528. package/extensions/plugin-dev/skills/hook-development/references/hook-input-schemas.md +0 -145
  529. package/extensions/plugin-dev/skills/hook-development/references/migration.md +0 -392
  530. package/extensions/plugin-dev/skills/hook-development/references/patterns.md +0 -430
  531. package/extensions/plugin-dev/skills/hook-development/scripts/README.md +0 -181
  532. package/extensions/plugin-dev/skills/hook-development/scripts/hook-linter.sh +0 -153
  533. package/extensions/plugin-dev/skills/hook-development/scripts/test-hook.sh +0 -276
  534. package/extensions/plugin-dev/skills/hook-development/scripts/validate-hook-schema.sh +0 -159
  535. package/extensions/plugin-dev/skills/mcp-integration/SKILL.md +0 -775
  536. package/extensions/plugin-dev/skills/mcp-integration/examples/http-server.json +0 -20
  537. package/extensions/plugin-dev/skills/mcp-integration/examples/sse-server.json +0 -19
  538. package/extensions/plugin-dev/skills/mcp-integration/examples/stdio-server.json +0 -38
  539. package/extensions/plugin-dev/skills/mcp-integration/examples/ws-server.json +0 -26
  540. package/extensions/plugin-dev/skills/mcp-integration/references/authentication.md +0 -601
  541. package/extensions/plugin-dev/skills/mcp-integration/references/server-discovery.md +0 -190
  542. package/extensions/plugin-dev/skills/mcp-integration/references/server-types.md +0 -572
  543. package/extensions/plugin-dev/skills/mcp-integration/references/tool-usage.md +0 -623
  544. package/extensions/plugin-dev/skills/plugin-dev-guide/SKILL.md +0 -221
  545. package/extensions/plugin-dev/skills/plugin-structure/SKILL.md +0 -705
  546. package/extensions/plugin-dev/skills/plugin-structure/examples/advanced-plugin.md +0 -774
  547. package/extensions/plugin-dev/skills/plugin-structure/examples/minimal-plugin.md +0 -83
  548. package/extensions/plugin-dev/skills/plugin-structure/examples/standard-plugin.md +0 -611
  549. package/extensions/plugin-dev/skills/plugin-structure/references/advanced-topics.md +0 -289
  550. package/extensions/plugin-dev/skills/plugin-structure/references/component-patterns.md +0 -592
  551. package/extensions/plugin-dev/skills/plugin-structure/references/github-actions.md +0 -233
  552. package/extensions/plugin-dev/skills/plugin-structure/references/headless-ci-mode.md +0 -193
  553. package/extensions/plugin-dev/skills/plugin-structure/references/manifest-reference.md +0 -625
  554. package/extensions/plugin-dev/skills/plugin-structure/references/output-styles.md +0 -116
  555. package/extensions/plugin-dev/skills/skill-development/SKILL.md +0 -562
  556. package/extensions/plugin-dev/skills/skill-development/examples/complete-skill.md +0 -465
  557. package/extensions/plugin-dev/skills/skill-development/examples/frontmatter-templates.md +0 -167
  558. package/extensions/plugin-dev/skills/skill-development/examples/minimal-skill.md +0 -111
  559. package/extensions/plugin-dev/skills/skill-development/references/advanced-frontmatter.md +0 -225
  560. package/extensions/plugin-dev/skills/skill-development/references/commands-vs-skills.md +0 -39
  561. package/extensions/plugin-dev/skills/skill-development/references/skill-creation-workflow.md +0 -379
  562. package/extensions/plugin-dev/skills/skill-development/references/skill-creator-original.md +0 -210
  563. package/extensions/plugin-dev/skills/start/SKILL.md +0 -82
  564. package/scripts/check-hooks.mjs +0 -174
  565. package/scripts/check-principles.mjs +0 -116
  566. package/scripts/hooks/session-start-drift-check.mjs +0 -49
  567. package/scripts/install.sh +0 -70
  568. package/scripts/lib/auto-update.mjs +0 -88
  569. package/scripts/lib/bundle-hash.mjs +0 -137
  570. package/scripts/lib/hooks.mjs +0 -585
  571. package/scripts/lib/ledger.mjs +0 -163
  572. package/scripts/lib/migrate-artifacts.mjs +0 -144
  573. package/scripts/lib/outdated.mjs +0 -144
  574. package/scripts/lib/postinstall-offer.mjs +0 -83
  575. package/scripts/lib/principles.mjs +0 -109
  576. package/scripts/lib/provision-mcp.mjs +0 -16
  577. package/scripts/lib/resolve-plugin-root.mjs +0 -9
  578. package/scripts/lib/run-install.mjs +0 -507
  579. package/scripts/lib/run-prune.mjs +0 -94
  580. package/scripts/lib/run-uninstall.mjs +0 -244
  581. package/scripts/lib/run-update.mjs +0 -165
  582. package/scripts/lib/serve.mjs +0 -472
  583. package/scripts/lib/ups-router.mjs +0 -392
  584. package/scripts/postinstall.mjs +0 -101
  585. package/scripts/setup-enact-context.sh +0 -28
  586. package/scripts/version-bump.sh +0 -484
  587. package/web/assets/README.md +0 -111
  588. package/web/assets/logo-full.png +0 -0
  589. package/web/assets/logo-slim.png +0 -0
  590. package/web/assets/tokens/base.css +0 -45
  591. package/web/assets/tokens/colors.css +0 -248
  592. package/web/assets/tokens/effects.css +0 -24
  593. package/web/assets/tokens/fonts.css +0 -8
  594. package/web/assets/tokens/index.css +0 -18
  595. package/web/assets/tokens/spacing.css +0 -50
  596. package/web/index.html +0 -1188
@@ -1,32 +1,17 @@
1
1
  #!/usr/bin/env node
2
- import { resolvePluginRoot } from "./lib/resolve-plugin-root.mjs";
3
- import { resolveBundlePath } from "./lib/resolve-bundle.mjs";
4
- import { listBundles } from "./lib/list-bundles.mjs";
5
- import { buildIndex } from "./lib/build-index.mjs";
6
- import { runValidate } from "./lib/run-validate.mjs";
7
- import { runSync } from "./lib/run-sync.mjs";
8
- import { INSTALL_DRY_RUN_UNSUPPORTED_MESSAGE, runInstall } from "./lib/run-install.mjs";
9
- import { runUpdate } from "./lib/run-update.mjs";
10
- import { runPrune } from "./lib/run-prune.mjs";
11
- import { runUninstall } from "./lib/run-uninstall.mjs";
12
- import { readLedger } from "./lib/ledger.mjs";
13
- import { computeOutdated } from "./lib/outdated.mjs";
14
- import { migrateGlobalArtifacts } from "./lib/migrate-artifacts.mjs";
15
- import { startServer, resolveServePort } from "./lib/serve.mjs";
16
- import {
17
- setupPrinciples,
18
- removePrinciples,
19
- checkPrinciples,
20
- runDoctor,
21
- SUPPORTED_PRINCIPLES_SURFACES,
22
- } from "./lib/principles.mjs";
23
- import { dirname, join, resolve } from "node:path";
24
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
25
- import { fileURLToPath } from "node:url";
3
+ import { dirname, join, resolve } from "node:path";
26
4
  import { cwd as processCwd } from "node:process";
27
- import { homedir } from "node:os";
5
+ import { fileURLToPath } from "node:url";
28
6
  import { Ajv } from "ajv";
29
7
  import addFormatsModule from "ajv-formats";
8
+ import { buildIndex } from "./lib/build-index.mjs";
9
+ import { listBundles } from "./lib/list-bundles.mjs";
10
+ import { resolveBundlePath } from "./lib/resolve-bundle.mjs";
11
+ import { runRepoDoctor } from "./lib/run-repo-doctor.mjs";
12
+ import { runRepoInstall, runRepoUninstall } from "./lib/run-repo-install.mjs";
13
+ import { runSync } from "./lib/run-sync.mjs";
14
+ import { runValidate } from "./lib/run-validate.mjs";
30
15
 
31
16
  const __dirname = dirname(fileURLToPath(import.meta.url));
32
17
  const PACKAGE_ROOT = resolve(__dirname, "..");
@@ -36,57 +21,48 @@ const VERSION = PACKAGE_JSON.version ?? "unknown";
36
21
  const HELP = `enact-extensions — Enact multi-platform plugin manifests
37
22
  Version: ${VERSION}
38
23
 
24
+ enact-extensions manages REPO-LOCAL agent setup only. It never installs
25
+ anything into host global homes; global agent wiring belongs to enact-vps.
26
+ install/uninstall/doctor only accept plugin names listed in
27
+ bundled-extensions.json, which names the DEFAULT bundles. Any bundle resolvable
28
+ by name or path can be installed or uninstalled.
29
+
39
30
  Usage:
40
31
  enact-extensions list [dir] List available plugin bundles (default: bundled + cwd/extensions/)
41
32
  enact-extensions list [dir] --json Emit machine-readable JSON array
42
- enact-extensions log Print the install ledger (single global file), newest first
43
- enact-extensions log --json Emit the raw ledger array as JSON
44
- enact-extensions outdated Report freshness drift: installed vs canonical source (per plugin+surface)
45
- enact-extensions outdated --json Emit JSON array [{name, platform, scope, home, status, installedHash, currentHash, version}]
46
- enact-extensions migrate-artifacts [--dry-run] [--json]
47
- Rename legacy factory/loop markdown artifacts to YYMMDDHH-<slug>.md
48
- enact-extensions update <name> Refresh an outdated plugin on every surface it is installed on (ledger-driven)
49
- enact-extensions update --all Refresh EVERY outdated plugin/surface
50
- enact-extensions update <name> --dry-run Show what would be refreshed; change nothing
51
- enact-extensions update --all --dry-run Show every outdated surface that would be refreshed
52
- enact-extensions prune --platform all Remove ledger-installed plugin surfaces whose local source bundle is gone
53
- enact-extensions prune --platform shared Remove orphaned shared skill drops (explicit only)
54
- enact-extensions prune --dry-run Show orphaned surfaces that would be removed
55
33
  enact-extensions index [--out <path>] Generate a discovery index of all bundles (generated artifact, never committed)
56
34
  enact-extensions index --out - Print the discovery index JSON to stdout
57
35
  enact-extensions index --stdout Alias for --out -
58
36
  enact-extensions validate [path|name] Validate manifests (default: cwd)
59
37
  enact-extensions sync [path|name] Sync host manifests from .agents/plugin.json (default: cwd)
60
38
  enact-extensions sync [path|name] --name <id> Create .agents/plugin.json then sync (new plugin)
61
- enact-extensions install [path|name] Sync and install plugin into Codex-compatible homes (default: cwd)
62
- enact-extensions install [path|name] --platform claude Install to Claude Code
63
- enact-extensions install [path|name] --platform cursor Install to Cursor
64
- enact-extensions install [path|name] --platform enact Install to Enact (Codex fork)
65
- enact-extensions install [path|name] --platform codex Install to Codex (explicit)
66
- enact-extensions install [path|name] --platform shared Install skills to host-neutral .agents/skills/
67
- enact-extensions install [path|name] --platform all Install to every plugin platform (codex, claude, cursor, enact)
68
- enact-extensions install [path|name] --platform claude,cursor Install to a subset of platforms
69
- enact-extensions install [path|name] --dry-run Unsupported; exits non-zero without writing anything
70
- enact-extensions install [path|name] --global Install into the default agent home (default)
71
- enact-extensions install [path|name] --local Install into a project home under cwd (./.codex, ...)
72
- enact-extensions uninstall [path|name] Uninstall plugin from Codex-compatible homes (default: enact)
73
- enact-extensions uninstall [path|name] --platform claude Uninstall from Claude Code
74
- enact-extensions uninstall [path|name] --platform cursor Uninstall from Cursor
75
- enact-extensions uninstall [path|name] --platform enact Uninstall from Enact (Codex fork)
76
- enact-extensions uninstall [path|name] --platform codex Uninstall from Codex (explicit)
77
- enact-extensions uninstall [path|name] --platform shared Remove skills from host-neutral .agents/skills/
78
- enact-extensions uninstall [path|name] --platform all Uninstall from every plugin platform
79
- enact-extensions uninstall [path|name] --platform claude,cursor Uninstall from a subset of platforms
80
- enact-extensions serve [--port N] [--prod] [--host 127.0.0.1] [--open] Start the localhost management UI + API (dev 43217 / prod 53217)
81
- enact-extensions principles setup [--surfaces <list>|--all] [--remove] [--local]
82
- Inject (or remove) the enact-core operating-principles block
83
- into host agent files (CLAUDE.md / AGENTS.md).
84
- enact-extensions principles check [--surfaces <list>|--all] [--local] [--json]
85
- Report principles block drift per surface (present/missing/stale).
86
- Use principles setup or doctor --fix to remediate. Not npm run check:principles.
87
- enact-extensions doctor [--fix] [--surfaces <list>|--all] [--local] [--json]
88
- Check operating-principles block drift per surface; --fix applies blocks.
89
- enact-extensions -v | --version Print version and exit
39
+ enact-extensions install <name> --repo <path> [--host claude,codex,cursor,kimi,opencode] [--force]
40
+ Install a bundle INTO a git repo as committed, relative files
41
+ (per-host project standard; state in .agents/<name>.lock.json)
42
+ enact-extensions install <name> --repo <path> --dry-run Print exactly what install would write/merge, per
43
+ file (action + reason), and exit with the same success/failure
44
+ the real run would have. Touches nothing.
45
+ enact-extensions uninstall <name> --repo <path> [--purge] [--force]
46
+ Remove exactly what the repo lock records (--purge also removes
47
+ the [extensions] block install wrote to enact-config.toml, and the
48
+ file itself if install created it and only version = 1 remains)
49
+ enact-extensions uninstall <name> --repo <path> --dry-run [--purge]
50
+ Print exactly what uninstall would remove/restore, per file
51
+ (action + reason), and exit with the same success/failure the
52
+ real run would have. Touches nothing.
53
+ enact-extensions doctor enact-repo --repo <path> [--json]
54
+ Read-only health check for a repo-local enact-repo install:
55
+ installed?, version vs bundle (outdated?), lock integrity
56
+ (missing/drifted managed files), per-host status (claude,
57
+ codex incl. trust_level, cursor, kimi incl. workspace-trust,
58
+ opencode), required global binaries on PATH, the
59
+ enact-config.toml marker ([extensions] present; a stale
60
+ .agents/enact-repo.toml is reported), an "enact-hook doctor
61
+ --repo" passthrough when available, and the enact-hook
62
+ deny/activity log paths, existence, row counts, and (deny
63
+ log) the last 5 rows. Never writes anything; exits non-zero
64
+ only when a hard requirement fails.
65
+ enact-extensions -v | --version Print version and exit
90
66
 
91
67
  Options:
92
68
  path|name Plugin root path, or a bare plugin name resolved from bundled extensions.
@@ -94,65 +70,77 @@ Options:
94
70
  --out <path> (index only) Output file path. Use - or --stdout to print to stdout.
95
71
  Default: generated/index.json under the package root.
96
72
  --stdout (index only) Alias for --out -.
97
- --json (list only) Emit a JSON array to stdout instead of a human table.
98
- --platform <name> Target platform: codex (default), claude, cursor, enact, shared.
99
- Use "all" to install to every plugin platform, or a comma-separated list
100
- (e.g. "claude,cursor") to install to a named subset.
101
- --global Install into the agent's default home (default)
102
- --local Install into a project-scoped home under the current dir
103
- --codex-home <path> Install only into the given Codex-compatible home
104
- --enact-home <path> Target a specific local Enact home (default: ~/.enact/agent)
105
- --claude-home <path> Target a specific local Claude home (default: ~/.claude)
106
- --cursor-home <path> Target a specific local Cursor home (default: ~/.cursor)
107
- --shared-home <path> Base dir for shared install (skills land at <path>/.agents/skills/)
108
- --marketplace <name> Marketplace name (default: enact-os-plugins)
109
- --no-enable Install files without enabling the Codex/Enact plugin
110
- --skip-sync Install current manifests without regenerating them
111
- --no-provision Skip auto-provisioning the bundle's MCP-server packages.
112
- By default, install resolves each declared MCP server
113
- (.mcp.json) and provisions its package: uvx → uv tool
114
- install, npx → npm install -g, pipx → pipx install,
115
- pip/pip3 → pip install. Best-effort; never fails install.
73
+ --json (list/doctor) Emit JSON to stdout instead of human output.
74
+ --name <id> (sync only) Plugin name for a new .agents/plugin.json.
75
+ --repo <path> (install/uninstall/doctor) Target the git repository root at <path>.
76
+ Never writes under $HOME; never writes absolute paths.
77
+ --host <list> (install --repo) Hosts to install for: claude,codex,cursor,kimi,opencode
78
+ (default: every repo host the bundle targets).
79
+ --dry-run (install/uninstall --repo) Print exactly what would change, per file, and
80
+ exit with the same success/failure the real run would have. Writes nothing.
81
+ --force (install/uninstall --repo) Overwrite user-edited managed content and take over
82
+ same-named entries not owned by the bundle. Never overwrites a user skill dir.
83
+ --purge (uninstall --repo) Also remove the [extensions] block install wrote to enact-config.toml.
116
84
 
117
85
  Name resolution (bare plugin names):
118
86
  When a bare name (no "/" in it) is given, it is resolved in this order:
119
- 1. <this-package-root>/extensions/<name> (bundled extensions in this repo)
87
+ 1. <this-package-root>/extensions/<name> (bundled extensions in this package)
120
88
  2. <cwd>/extensions/<name> (extensions in a sibling product repo)
121
89
  3. <npm-global-root>/@amsterdamdatalabs/enact-extensions/extensions/<name>
122
90
 
123
- Multi-platform notes:
124
- - Platforms are installed sequentially in deterministic order: codex, claude, cursor, enact.
125
- - Per-platform --<platform>-home flags apply to their respective platform in a multi-target run.
126
- - If any platform fails, the others still proceed; a non-zero exit is returned and the summary
127
- of succeeded/failed platforms is printed to stderr.
128
-
129
91
  Examples:
130
92
  enact-extensions list
131
93
  enact-extensions list --json
132
- enact-extensions list /path/to/extensions
133
- enact-extensions index Generate generated/index.json (gitignored, local discovery artifact)
134
94
  enact-extensions index --out - Print discovery index JSON to stdout (pipe-friendly)
135
- enact-extensions index --stdout Same as --out -
136
- enact-extensions index --out /tmp/my-index.json
137
- cd extensions/enact-factory && enact-extensions validate
138
- enact-extensions sync . --name my-plugin
139
- enact-extensions install net-revenue-management --platform enact --global
140
- enact-extensions install net-revenue-management --platform enact --local
141
- enact-extensions install extensions/net-revenue-management --platform enact --global
142
- enact-extensions install enact-factory --platform claude
143
- enact-extensions install enact-factory --platform cursor
144
- enact-extensions install net-revenue-management --platform shared
145
- enact-extensions install net-revenue-management --platform shared --local
146
- enact-extensions install net-revenue-management --platform all
147
- enact-extensions install net-revenue-management --platform claude,cursor
148
- enact-extensions install net-revenue-management --platform all --enact-home /path/to/enact
95
+ cd extensions/enact-repo && enact-extensions validate
149
96
  enact-extensions validate extensions/plugin-dev
150
- enact-extensions validate plugin-dev
97
+ enact-extensions sync . --name my-plugin
98
+ enact-extensions install enact-repo --repo .
99
+ enact-extensions install enact-repo --repo ~/code/my-service --host claude,cursor
100
+ enact-extensions install enact-repo --repo . --dry-run Preview the plan; writes nothing
101
+ enact-extensions uninstall enact-repo --repo . --purge
102
+ enact-extensions uninstall enact-repo --repo . --dry-run Preview what would be removed/restored
103
+ enact-extensions doctor enact-repo --repo . Read-only health check
104
+ enact-extensions doctor enact-repo --repo . --json
151
105
  `;
152
106
 
107
+ const COMMANDS = new Set(["list", "index", "validate", "sync", "install", "uninstall", "doctor"]);
108
+ const REPO_USAGE_HINT =
109
+ "enact-extensions only performs repo-local installs: install|uninstall <name> --repo <path> " +
110
+ "[--host claude,codex,cursor,kimi,opencode]; doctor enact-repo --repo <path>. " +
111
+ "Global host installs were removed (global agent wiring belongs to enact-vps).";
112
+
113
+ /**
114
+ * Unwinds to the top of the module so node can exit on its own.
115
+ *
116
+ * `exit()` must not be used here: `process.stdout` is ASYNCHRONOUS
117
+ * when it is a pipe -- under a test harness, under CI, or whenever a user
118
+ * pipes output into a file or a pager -- and exiting discards whatever is
119
+ * still queued. `install --repo --dry-run` prints ~1500 lines, and calling
120
+ * exit on the next statement silently cut the tail off on Linux agents while
121
+ * appearing fine on a fast local flush.
122
+ */
123
+ class ExitSignal extends Error {
124
+ constructor(code) {
125
+ super(`exit ${code}`);
126
+ this.name = "ExitSignal";
127
+ this.code = code;
128
+ }
129
+ }
130
+
131
+ function exit(code) {
132
+ throw new ExitSignal(code);
133
+ }
134
+
135
+ function fail(message) {
136
+ console.error(message);
137
+ exit(1);
138
+ }
139
+
153
140
  function parseArgs(argv) {
154
141
  const positional = [];
155
142
  const options = {};
143
+ const unknown = [];
156
144
 
157
145
  for (let i = 0; i < argv.length; i++) {
158
146
  const arg = argv[i];
@@ -162,988 +150,405 @@ function parseArgs(argv) {
162
150
  options.out = "-";
163
151
  } else if (arg === "--name" && argv[i + 1]) {
164
152
  options.name = argv[++i];
165
- } else if (arg === "--codex-home" && argv[i + 1]) {
166
- options.codexHome = argv[++i];
167
- } else if (arg === "--marketplace" && argv[i + 1]) {
168
- options.marketplaceName = argv[++i];
169
- } else if (arg === "--no-enable") {
170
- options.enable = false;
171
- } else if (arg === "--skip-sync") {
172
- options.sync = false;
173
- } else if (arg === "--no-provision") {
174
- options.noProvision = true;
175
- } else if (arg === "--platform" && argv[i + 1]) {
176
- options.platform = argv[++i];
177
- } else if (arg === "--claude-home" && argv[i + 1]) {
178
- options.claudeHome = argv[++i];
179
- } else if (arg === "--cursor-home" && argv[i + 1]) {
180
- options.cursorHome = argv[++i];
181
- } else if (arg === "--enact-home" && argv[i + 1]) {
182
- options.enactHome = argv[++i];
183
- } else if (arg === "--shared-home" && argv[i + 1]) {
184
- options.sharedHome = argv[++i];
185
- } else if (arg === "--local") {
186
- options.scope = "local";
187
- } else if (arg === "--global") {
188
- options.scope = "global";
189
- } else if (arg === "--all") {
190
- options.all = true;
153
+ } else if (arg === "--repo" && argv[i + 1]) {
154
+ options.repo = argv[++i];
155
+ } else if (arg === "--host" && argv[i + 1]) {
156
+ options.host = argv[++i];
157
+ } else if (arg === "--force") {
158
+ options.force = true;
159
+ } else if (arg === "--purge") {
160
+ options.purge = true;
191
161
  } else if (arg === "--dry-run") {
192
162
  options.dryRun = true;
193
163
  } else if (arg === "--json") {
194
164
  options.json = true;
195
- } else if (arg === "--port" && argv[i + 1]) {
196
- options.port = parseInt(argv[++i], 10);
197
- } else if (arg === "--host" && argv[i + 1]) {
198
- options.host = argv[++i];
199
- } else if (arg === "--open") {
200
- options.open = true;
201
- } else if (arg === "--prod") {
202
- options.prod = true;
203
- } else if (arg === "--surfaces" && argv[i + 1]) {
204
- // Comma-separated list of surfaces: claude,codex,cursor,enact
205
- options.surfaces = argv[++i].split(",").map((s) => s.trim()).filter(Boolean);
206
- } else if (arg === "--remove") {
207
- options.remove = true;
208
- } else if (arg === "--fix") {
209
- options.fix = true;
210
165
  } else if (arg === "-h" || arg === "--help") {
211
166
  positional.push("help");
212
- } else if (!arg.startsWith("-")) {
167
+ } else if (arg.startsWith("-")) {
168
+ unknown.push(arg);
169
+ } else {
213
170
  positional.push(arg);
214
171
  }
215
172
  }
216
173
 
217
- return { command: positional[0], path: positional[1], options };
218
- }
219
-
220
- const rawArgv = process.argv.slice(2);
221
- if (rawArgv.length === 1 && (rawArgv[0] === "-v" || rawArgv[0] === "--version")) {
222
- console.log(`enact-extensions ${VERSION}`);
223
- process.exit(0);
174
+ return { command: positional[0], path: positional[1], options, unknown };
224
175
  }
225
176
 
226
- const { command, path, options } = parseArgs(rawArgv);
227
-
228
- if (!command || command === "help") {
229
- console.log(HELP);
230
- process.exit(command ? 0 : 1);
231
- }
177
+ try {
178
+ const rawArgv = process.argv.slice(2);
179
+ if (rawArgv.length === 1 && (rawArgv[0] === "-v" || rawArgv[0] === "--version")) {
180
+ console.log(`enact-extensions ${VERSION}`);
181
+ exit(0);
182
+ }
232
183
 
233
- // ---------------------------------------------------------------------------
234
- // `hooks` command — register or remove the session-start drift-check hook.
235
- //
236
- // Surfaces: claude, codex, cursor, enact
237
- // Registration is idempotent and reversible.
238
- // ---------------------------------------------------------------------------
239
- // ---------------------------------------------------------------------------
240
- // `principles` command — inject or check operating-principles blocks in host files.
241
- //
242
- // Surfaces: claude, codex, cursor, enact
243
- // ---------------------------------------------------------------------------
244
- if (command === "principles") {
245
- const subCommand = path ?? "setup";
184
+ const { command, path, options, unknown } = parseArgs(rawArgv);
246
185
 
247
- if (subCommand !== "setup" && subCommand !== "check") {
248
- process.stderr.write(
249
- `[enact-extensions principles] Unknown sub-command: ${subCommand}. Use 'setup' or 'check'.\n`,
250
- );
251
- process.exit(1);
186
+ if (!command || command === "help") {
187
+ console.log(HELP);
188
+ exit(command ? 0 : 1);
252
189
  }
253
190
 
254
- const isRemove = options.remove ?? false;
255
- const isLocal = options.scope === "local";
256
- const cwd = processCwd();
257
- const surfacesList = SUPPORTED_PRINCIPLES_SURFACES;
258
-
259
- let surfaces = [];
260
- if (options.all) {
261
- surfaces = [...surfacesList];
262
- } else if (options.surfaces && options.surfaces.length > 0) {
263
- const invalid = options.surfaces.filter((s) => !surfacesList.includes(s));
264
- if (invalid.length > 0) {
265
- process.stderr.write(
266
- `[enact-extensions principles] Unknown surface(s): ${invalid.join(", ")}. Valid: ${surfacesList.join(", ")}\n`,
267
- );
268
- process.exit(1);
269
- }
270
- surfaces = options.surfaces;
271
- } else if (subCommand === "setup" && process.stdin.isTTY) {
272
- const { createInterface } = await import("node:readline");
273
- const rl = createInterface({ input: process.stdin, output: process.stdout });
274
- const question = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
191
+ if (!COMMANDS.has(command)) {
192
+ console.error(`Unknown command: ${command}\n\n${REPO_USAGE_HINT}\nRun enact-extensions --help for usage.`);
193
+ exit(1);
194
+ }
275
195
 
276
- process.stdout.write(`\nenact-extensions principles setup — interactive surface picker\n`);
277
- process.stdout.write(`Available surfaces: ${surfacesList.map((s, i) => `${i + 1}. ${s}`).join(", ")}\n`);
278
- process.stdout.write(`Enter surface names or numbers (comma-separated), or 'all':\n`);
196
+ if (unknown.length > 0) {
197
+ fail(`Unknown option(s) for ${command}: ${unknown.join(", ")}\n${REPO_USAGE_HINT}`);
198
+ }
279
199
 
280
- const answer = await question("> ");
281
- rl.close();
200
+ if ((command === "install" || command === "uninstall") && !options.repo) {
201
+ fail(`${command} requires --repo <path>.\n${REPO_USAGE_HINT}`);
202
+ }
282
203
 
283
- if (answer.trim().toLowerCase() === "all") {
284
- surfaces = [...surfacesList];
285
- } else {
286
- surfaces = answer
287
- .split(",")
288
- .map((s) => s.trim())
289
- .filter(Boolean)
290
- .map((s) => {
291
- const idx = parseInt(s, 10);
292
- if (!isNaN(idx) && idx >= 1 && idx <= surfacesList.length) {
293
- return surfacesList[idx - 1];
294
- }
295
- return s.toLowerCase();
296
- })
297
- .filter((s) => surfacesList.includes(s));
204
+ if (
205
+ !["install", "uninstall"].includes(command) &&
206
+ (options.force || options.purge || options.host || options.dryRun)
207
+ ) {
208
+ fail("--host/--force/--purge/--dry-run only apply to install|uninstall <name> --repo <path>");
209
+ }
298
210
 
299
- if (surfaces.length === 0) {
300
- process.stderr.write(`[enact-extensions principles] No valid surfaces selected. Exiting.\n`);
301
- process.exit(0);
302
- }
211
+ function printRepoDoctorReport(report) {
212
+ const ok = (b) => (b ? "OK" : "FAIL");
213
+ console.log(`[doctor enact-repo] repo: ${report.repo}`);
214
+ console.log(` installed: ${report.installed ? "yes" : "NO"}`);
215
+ if (!report.installed) {
216
+ for (const p of report.problems) console.error(` problem: ${p}`);
217
+ return;
303
218
  }
304
- } else {
305
- process.stdout.write(
306
- `[enact-extensions principles] No surface specified. Run with --surfaces or --all:\n` +
307
- ` enact-extensions principles ${subCommand} --surfaces claude,codex\n` +
308
- ` enact-extensions principles ${subCommand} --all\n`,
219
+ console.log(
220
+ ` version: installed=${report.version.installed} bundle=${report.version.bundle ?? "(unknown)"} outdated=${report.version.outdated ? "YES" : "no"}`,
309
221
  );
310
- process.exit(0);
311
- }
312
-
313
- const principleOpts = {
314
- claudeHome: options.claudeHome,
315
- codexHome: options.codexHome,
316
- cursorHome: options.cursorHome,
317
- enactHome: options.enactHome,
318
- local: isLocal,
319
- cwd,
320
- };
321
-
322
- let exitCode = 0;
323
-
324
- if (subCommand === "check") {
325
- const rows = [];
326
- for (const surface of surfaces) {
327
- try {
328
- const res = checkPrinciples(surface, principleOpts);
329
- rows.push(res);
330
- if (!options.json) {
331
- process.stdout.write(
332
- ` [principles] ${surface}: ${res.status} → ${res.path}\n`,
333
- );
334
- }
335
- } catch (err) {
336
- const message = err instanceof Error ? err.message : String(err);
337
- rows.push({ surface, path: "", status: "error", error: message });
338
- process.stderr.write(` [principles] ${surface}: error — ${message}\n`);
339
- }
222
+ if (report.version.contentOutdated.length > 0) {
223
+ console.log(
224
+ ` content drifted from current bundle (${report.version.contentOutdated.length}): ${report.version.contentOutdated.join(", ")}`,
225
+ );
340
226
  }
341
-
342
- if (options.json) {
343
- console.log(JSON.stringify(rows, null, 2));
227
+ if (report.version.note) console.log(` note: ${report.version.note}`);
228
+
229
+ console.log(` lock integrity: ${ok(report.lock.valid)}`);
230
+ if (!report.lock.valid) {
231
+ if (report.lock.missingFiles.length) console.log(` missing: ${report.lock.missingFiles.join(", ")}`);
232
+ if (report.lock.driftedFiles.length) console.log(` drifted: ${report.lock.driftedFiles.join(", ")}`);
233
+ if (report.lock.jsonIssues.length) console.log(` json: ${report.lock.jsonIssues.join(", ")}`);
234
+ if (report.lock.tomlIssues.length) console.log(` toml: ${report.lock.tomlIssues.join(", ")}`);
344
235
  }
345
236
 
346
- process.exit(0);
347
- }
348
-
349
- for (const surface of surfaces) {
350
- try {
351
- const res = isRemove
352
- ? removePrinciples(surface, principleOpts)
353
- : setupPrinciples(surface, principleOpts);
354
-
355
- if (res.result === "applied") {
356
- process.stdout.write(
357
- ` [principles] ${surface}: applied (${res.action}) → ${res.path}\n`,
358
- );
359
- } else if (res.result === "already_present") {
360
- process.stdout.write(
361
- ` [principles] ${surface}: already present (noop) → ${res.path}\n`,
362
- );
363
- } else if (res.result === "removed") {
364
- process.stdout.write(` [principles] ${surface}: removed → ${res.path}\n`);
365
- } else if (res.result === "not_found") {
366
- process.stdout.write(
367
- ` [principles] ${surface}: not found (nothing to remove) → ${res.path}\n`,
237
+ for (const [host, info] of Object.entries(report.hosts)) {
238
+ console.log(` host ${host}: targeted=${info.targeted} present=${info.targeted ? ok(info.present) : "n/a"}`);
239
+ if (info.note) console.log(` ${info.note}`);
240
+ if (info.trust) {
241
+ console.log(
242
+ ` codex trust_level: ${info.trust.trustLevel ?? "(none)"}${info.trust.note ? ` -- ${info.trust.note}` : ""}`,
368
243
  );
369
244
  }
370
- } catch (err) {
371
- exitCode = 1;
372
- const message = err instanceof Error ? err.message : String(err);
373
- process.stderr.write(` [principles] ${surface}: error — ${message}\n`);
374
- }
375
- }
376
-
377
- process.exit(exitCode);
378
- }
379
-
380
- // ---------------------------------------------------------------------------
381
- // `doctor` command — check (and optionally fix) operating-principles drift.
382
- //
383
- // Surfaces: claude, codex, cursor, enact (defaults to all when omitted)
384
- // ---------------------------------------------------------------------------
385
- if (command === "doctor") {
386
- const isFix = options.fix ?? false;
387
- const isLocal = options.scope === "local";
388
- const cwd = processCwd();
389
- const surfacesList = SUPPORTED_PRINCIPLES_SURFACES;
390
-
391
- let surfaces = [];
392
- if (options.all || !options.surfaces || options.surfaces.length === 0) {
393
- surfaces = [...surfacesList];
394
- } else {
395
- const invalid = options.surfaces.filter((s) => !surfacesList.includes(s));
396
- if (invalid.length > 0) {
397
- process.stderr.write(
398
- `[enact-extensions doctor] Unknown surface(s): ${invalid.join(", ")}. Valid: ${surfacesList.join(", ")}\n`,
399
- );
400
- process.exit(1);
401
- }
402
- surfaces = options.surfaces;
403
- }
404
-
405
- const principleOpts = {
406
- claudeHome: options.claudeHome,
407
- codexHome: options.codexHome,
408
- cursorHome: options.cursorHome,
409
- enactHome: options.enactHome,
410
- local: isLocal,
411
- cwd,
412
- fix: isFix,
413
- };
414
-
415
- const { rows, exitCode } = runDoctor(surfaces, principleOpts);
416
-
417
- if (!options.json) {
418
- for (const row of rows) {
419
- if (row.error) {
420
- process.stderr.write(` [doctor] ${row.surface}: error — ${row.error}\n`);
421
- continue;
245
+ if (info.globalHooksNote) console.log(` ${info.globalHooksNote}`);
246
+ if (info.workspaceTrust) {
247
+ console.log(
248
+ ` kimi workspace trust: ${info.workspaceTrust.trusted ? "TRUSTED" : "NOT TRUSTED"}${info.workspaceTrust.note ? ` -- ${info.workspaceTrust.note}` : ""}`,
249
+ );
422
250
  }
423
- if (isFix) {
424
- const note = row.result === "already_present"
425
- ? "already present (noop)"
426
- : `fixed (${row.action ?? row.result})`;
427
- process.stdout.write(` [doctor] ${row.surface}: ${row.status} — ${note} → ${row.path}\n`);
428
- } else {
429
- process.stdout.write(` [doctor] ${row.surface}: ${row.status} → ${row.path}\n`);
251
+ if (host === "opencode" && info.targeted) {
252
+ console.log(
253
+ ` generated from current bundle: ${info.generatedFromCurrentBundle === false ? "NO (stale)" : info.generatedFromCurrentBundle === true ? "yes" : "n/a"}`,
254
+ );
430
255
  }
431
256
  }
432
- if (!isFix && exitCode !== 0) {
433
- process.stderr.write(
434
- `[enact-extensions doctor] Drift detected. Fix with: enact-extensions doctor --fix --all\n`,
435
- );
436
- }
437
- } else {
438
- console.log(JSON.stringify(rows, null, 2));
439
- }
440
257
 
441
- process.exit(exitCode);
442
- }
443
-
444
- // ---------------------------------------------------------------------------
445
- // `list` command — enumerate available plugin bundles.
446
- // ---------------------------------------------------------------------------
447
- if (command === "list") {
448
- // The package's own extensions/ dir, resolved relative to this script.
449
- const PACKAGE_ROOT = resolve(__dirname, "..");
450
- const packageExtensions = join(PACKAGE_ROOT, "extensions");
451
-
452
- let roots;
453
- if (path) {
454
- // Explicit dir argument → scan only that dir.
455
- roots = [resolve(processCwd(), path)];
456
- } else {
457
- // Default: package-bundled extensions/ + cwd/extensions/ (if it differs).
458
- roots = [packageExtensions];
459
- const cwdExtensions = join(processCwd(), "extensions");
460
- if (resolve(cwdExtensions) !== resolve(packageExtensions) && existsSync(cwdExtensions)) {
461
- roots.push(cwdExtensions);
258
+ console.log(" binaries:");
259
+ for (const [bin, info] of Object.entries(report.binaries)) {
260
+ console.log(` ${bin}: ${info.found ? `${info.version ?? "(unknown version)"} @ ${info.path}` : "NOT FOUND"}`);
462
261
  }
463
- }
464
-
465
- const entries = listBundles(roots);
466
-
467
- if (options.json) {
468
- console.log(JSON.stringify(entries, null, 2));
469
- process.exit(0);
470
- }
471
-
472
- // Human-readable table.
473
- const TRUNC = 60;
474
- function trunc(str, len) {
475
- if (!str) return "";
476
- return str.length > len ? str.slice(0, len - 1) + "…" : str;
477
- }
478
262
 
479
- if (entries.length === 0) {
480
- console.log("No plugin bundles found.");
481
- process.exit(0);
482
- }
483
-
484
- // Header.
485
- const COL_NAME = 28;
486
- const COL_VER = 8;
487
- const COL_CAT = 18;
488
- const COL_SURF = 22;
489
-
490
- function padR(str, n) { return String(str ?? "").padEnd(n).slice(0, n); }
491
-
492
- const hr = "─".repeat(COL_NAME + COL_VER + COL_CAT + COL_SURF + 3 + TRUNC);
493
- console.log(hr);
494
- console.log(
495
- padR("Name", COL_NAME) + " " +
496
- padR("Ver", COL_VER) + " " +
497
- padR("Category", COL_CAT) + " " +
498
- padR("Surfaces", COL_SURF) + " " +
499
- "Description"
500
- );
501
- console.log(hr);
502
-
503
- for (const e of entries) {
504
- const surfaces = e.targets.join(", ");
505
263
  console.log(
506
- padR(e.name, COL_NAME) + " " +
507
- padR(e.version, COL_VER) + " " +
508
- padR(e.category, COL_CAT) + " " +
509
- padR(surfaces, COL_SURF) + " " +
510
- trunc(e.description, TRUNC)
264
+ ` marker (${report.marker.path}): ${ok(report.marker.valid)}${report.marker.note ? ` -- ${report.marker.note}` : ""}`,
511
265
  );
512
- }
513
- console.log(hr);
514
- console.log(`${entries.length} bundle${entries.length === 1 ? "" : "s"} found.`);
515
- process.exit(0);
516
- }
517
-
518
- // ---------------------------------------------------------------------------
519
- // `index` command — generate a discovery index of all plugin bundles.
520
- //
521
- // The index is a LOCAL GENERATED ARTIFACT (never committed). The generated/
522
- // directory is gitignored. The schema at spec/index.json is the committed
523
- // contract. No network, no marketplace, no catalog/ — purely local.
524
- // ---------------------------------------------------------------------------
525
- if (command === "index") {
526
- const PACKAGE_ROOT = resolve(__dirname, "..");
527
- const packageExtensions = join(PACKAGE_ROOT, "extensions");
528
-
529
- // Same default roots as `list`.
530
- const roots = [packageExtensions];
531
- const cwdExtensions = join(processCwd(), "extensions");
532
- if (resolve(cwdExtensions) !== resolve(packageExtensions) && existsSync(cwdExtensions)) {
533
- roots.push(cwdExtensions);
534
- }
535
-
536
- // Build the index. The wall-clock timestamp is computed here at the CLI
537
- // boundary and passed in — buildIndex itself stays pure (never calls Date).
538
- let index;
539
- try {
540
- index = buildIndex(roots, { now: new Date().toISOString(), packageRoot: PACKAGE_ROOT });
541
- } catch (err) {
542
- console.error(`[enact-extensions index] Failed to build index: ${err instanceof Error ? err.message : String(err)}`);
543
- process.exit(1);
544
- }
545
-
546
- // Validate against spec/index.json before writing.
547
- const schemaPath = join(PACKAGE_ROOT, "spec", "index.json");
548
- let schema;
549
- try {
550
- schema = JSON.parse(readFileSync(schemaPath, "utf8"));
551
- } catch (err) {
552
- console.error(`[enact-extensions index] Cannot read schema ${schemaPath}: ${err instanceof Error ? err.message : String(err)}`);
553
- process.exit(1);
554
- }
555
-
556
- const addFormats = addFormatsModule.default ?? addFormatsModule;
557
- const ajv = new Ajv({ allErrors: true, strict: false });
558
- addFormats(ajv);
559
- const validate = ajv.compile(schema);
560
- const valid = validate(index);
561
- if (!valid) {
562
- const errs = (validate.errors ?? []).map((e) => ` ${e.instancePath || "/"}: ${e.message}`).join("\n");
563
- console.error(`[enact-extensions index] Generated index failed schema validation:\n${errs}`);
564
- process.exit(1);
565
- }
566
-
567
- const json = JSON.stringify(index, null, 2);
568
- // --stdout is normalized to "-" during arg parsing, so this single check covers both.
569
- const toStdout = options.out === "-";
570
-
571
- if (toStdout) {
572
- // Write JSON to stdout; summary to stderr (keeps stdout pipe-clean).
573
- process.stdout.write(json + "\n");
574
- process.stderr.write(`[enact-extensions index] ${index.count} plugin${index.count === 1 ? "" : "s"} indexed.\n`);
575
- } else {
576
- const outPath = options.out
577
- ? resolve(processCwd(), options.out)
578
- : join(PACKAGE_ROOT, "generated", "index.json");
579
-
580
- // Ensure parent directory exists.
581
- const outDir = resolve(outPath, "..");
582
- mkdirSync(outDir, { recursive: true });
583
-
584
- writeFileSync(outPath, json + "\n", "utf8");
585
- process.stderr.write(`[enact-extensions index] Wrote ${index.count} plugin${index.count === 1 ? "" : "s"} to ${outPath}\n`);
586
- }
587
-
588
- process.exit(0);
589
- }
590
-
591
- // ---------------------------------------------------------------------------
592
- // `log` command — print the install ledger (single global file), newest first.
593
- //
594
- // Human table to stdout (chrome to stderr) or `--json` (raw ledger array).
595
- // Reading a missing ledger yields an empty list — never an error.
596
- // ---------------------------------------------------------------------------
597
- if (command === "log") {
598
- const entries = readLedger();
599
- // Most-recent-first.
600
- const sorted = [...entries].sort((a, b) =>
601
- String(b.ts ?? "").localeCompare(String(a.ts ?? "")),
602
- );
603
-
604
- if (options.json) {
605
- process.stdout.write(JSON.stringify(sorted, null, 2) + "\n");
606
- process.exit(0);
607
- }
608
-
609
- if (sorted.length === 0) {
610
- process.stderr.write("No install ledger entries found.\n");
611
- process.exit(0);
612
- }
266
+ if (report.staleMarker.present)
267
+ console.log(` stale marker (${report.staleMarker.path}): ${report.staleMarker.note}`);
613
268
 
614
- const COL_TS = 24;
615
- const COL_ACT = 10;
616
- const COL_NAME = 28;
617
- const COL_PLAT = 8;
618
- const COL_SCOPE = 7;
619
- function padR(str, n) { return String(str ?? "").padEnd(n).slice(0, n); }
620
-
621
- const hr = "─".repeat(COL_TS + COL_ACT + COL_NAME + COL_PLAT + COL_SCOPE + 4 + 40);
622
- console.log(hr);
623
- console.log(
624
- padR("Timestamp", COL_TS) + " " +
625
- padR("Action", COL_ACT) + " " +
626
- padR("Name", COL_NAME) + " " +
627
- padR("Platform", COL_PLAT) + " " +
628
- padR("Scope", COL_SCOPE) + " " +
629
- "Home",
630
- );
631
- console.log(hr);
632
- for (const e of sorted) {
633
- console.log(
634
- padR(e.ts, COL_TS) + " " +
635
- padR(e.action, COL_ACT) + " " +
636
- padR(e.name, COL_NAME) + " " +
637
- padR(e.platform, COL_PLAT) + " " +
638
- padR(e.scope, COL_SCOPE) + " " +
639
- String(e.home ?? ""),
640
- );
641
- }
642
- console.log(hr);
643
- process.stderr.write(`${sorted.length} ledger entr${sorted.length === 1 ? "y" : "ies"}.\n`);
644
- process.exit(0);
645
- }
646
-
647
- // ---------------------------------------------------------------------------
648
- // `outdated` command — report drift between installed plugins and canonical source.
649
- //
650
- // Classifies each installed (plugin, platform) surface as:
651
- // fresh → installed hash matches canonical source hash
652
- // outdated → hash mismatch (or recorded hash missing), needs re-install
653
- // orphaned → canonical source does not exist (can't refresh)
654
- //
655
- // Exit code: always 0 (it's a report, not a failure gate).
656
- // Human chrome → stderr; --json stdout is always pure JSON.
657
- // ---------------------------------------------------------------------------
658
- if (command === "outdated") {
659
- let entries;
660
- try {
661
- entries = computeOutdated({ cwd: processCwd() });
662
- } catch (err) {
663
- process.stderr.write(
664
- `[enact-extensions outdated] Error: ${err instanceof Error ? err.message : String(err)}\n`,
665
- );
666
- process.exit(1);
667
- }
668
-
669
- if (options.json) {
670
- process.stdout.write(JSON.stringify(entries, null, 2) + "\n");
671
- process.exit(0);
672
- }
673
-
674
- // Human-readable table.
675
- if (entries.length === 0) {
676
- process.stderr.write("No installed plugins found in the ledger.\n");
677
- process.exit(0);
678
- }
679
-
680
- const COL_NAME = 30;
681
- const COL_PLAT = 10;
682
- const COL_SCOPE = 8;
683
- const COL_VER = 10;
684
- const COL_STAT = 10;
685
-
686
- function padR(str, n) { return String(str ?? "").padEnd(n).slice(0, n); }
687
-
688
- const hr = "─".repeat(COL_NAME + COL_PLAT + COL_SCOPE + COL_VER + COL_STAT + 4 + 8);
689
- console.log(hr);
690
- console.log(
691
- padR("Plugin", COL_NAME) + " " +
692
- padR("Platform", COL_PLAT) + " " +
693
- padR("Scope", COL_SCOPE) + " " +
694
- padR("Version", COL_VER) + " " +
695
- padR("Status", COL_STAT),
696
- );
697
- console.log(hr);
698
-
699
- for (const e of entries) {
700
269
  console.log(
701
- padR(e.name, COL_NAME) + " " +
702
- padR(e.platform, COL_PLAT) + " " +
703
- padR(e.scope, COL_SCOPE) + " " +
704
- padR(e.version, COL_VER) + " " +
705
- padR(e.status, COL_STAT),
270
+ ` enact-hook doctor --repo: ${report.hookDoctor.available ? `exit ${report.hookDoctor.exitCode}` : `not available -- ${report.hookDoctor.note}`}`,
706
271
  );
707
- }
708
- console.log(hr);
709
- process.stderr.write(`${entries.length} surface${entries.length === 1 ? "" : "s"} checked.\n`);
710
- process.exit(0);
711
- }
712
-
713
- // ---------------------------------------------------------------------------
714
- // `migrate-artifacts` command — rename legacy global markdown artifacts.
715
- if (command === "migrate-artifacts") {
716
- try {
717
- const report = migrateGlobalArtifacts({ dryRun: Boolean(options.dryRun) });
718
- if (options.json) {
719
- process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
720
- } else {
721
- const factoryRenamed = report.factory.flatMap((entry) => entry.renamed);
722
- const loopRenamed = report.loop.flatMap((entry) => entry.renamed);
723
- if (factoryRenamed.length === 0 && loopRenamed.length === 0) {
724
- process.stdout.write("✓ no legacy markdown artifacts to migrate\n");
272
+ if (report.hookDoctor.available && report.hookDoctor.stdout) {
273
+ for (const line of report.hookDoctor.stdout.trim().split("\n")) {
274
+ if (line) console.log(` ${line}`);
725
275
  }
726
- for (const { from, to } of factoryRenamed) {
727
- process.stdout.write(`✓ factory ${from} → ${to}\n`);
728
- }
729
- for (const { from, to } of loopRenamed) {
730
- process.stdout.write(`✓ loop ${from} → ${to}\n`);
731
- }
732
- if (report.fallbacks.length > 0) {
733
- process.stderr.write(
734
- `· ${report.fallbacks.length} ISO-date rename(s) used hour fallback — see --json for details\n`,
735
- );
276
+ }
277
+
278
+ if (report.hookLogs) {
279
+ const { denyLog, activityLog } = report.hookLogs;
280
+ console.log(
281
+ ` hook logs: ${denyLog.path} ${denyLog.exists ? `(${denyLog.rowCount} row(s))` : "(not present)"}; ${activityLog.path} ${activityLog.exists ? `(${activityLog.rowCount} row(s))` : "(not present)"}`,
282
+ );
283
+ if (denyLog.lastRows.length > 0) {
284
+ console.log(` last ${denyLog.lastRows.length} deny row(s):`);
285
+ for (const row of denyLog.lastRows) {
286
+ console.log(` ${row.timestamp} ${row.surface} ${row.reason_id} ${row.command} ${row.agent_message}`);
287
+ }
736
288
  }
737
289
  }
738
- process.exit(0);
739
- } catch (err) {
740
- process.stderr.write(
741
- `[enact-extensions migrate-artifacts] Error: ${err instanceof Error ? err.message : String(err)}\n`,
742
- );
743
- process.exit(1);
744
- }
745
- }
746
290
 
747
- // `update` command — refresh outdated installed plugins, driven by the ledger.
748
- //
749
- // Re-installs each OUTDATED (plugin, surface, home) to the EXACT surface + home
750
- // recorded in the ledger — never to a surface the plugin isn't already on.
751
- //
752
- // update <name> → refresh only that plugin (across its installed surfaces)
753
- // update --all → refresh every outdated surface
754
- // update --dry-run→ report what WOULD change; touch nothing
755
- //
756
- // Exit code: non-zero ONLY on a real install failure or a missing target (no
757
- // name + no --all). Orphaned and fresh surfaces are never failures.
758
- // Human chrome → stderr; the ledger self-updates via runInstall.
759
- // ---------------------------------------------------------------------------
760
- if (command === "update") {
761
- let summary;
762
- try {
763
- summary = runUpdate({
764
- name: path, // positional[1]: a bare plugin name (or undefined)
765
- all: options.all,
766
- dryRun: options.dryRun,
767
- home: homedir(), // single global ledger home
768
- cwd: processCwd(), // canonical bundle resolution
769
- });
770
- } catch (err) {
771
- process.stderr.write(
772
- `[enact-extensions update] ${err instanceof Error ? err.message : String(err)}\n`,
773
- );
774
- process.exit(1);
291
+ if (report.problems.length > 0) {
292
+ console.error(`[doctor enact-repo] ${report.problems.length} problem(s):`);
293
+ for (const p of report.problems) console.error(` - ${p}`);
294
+ } else {
295
+ console.log("[doctor enact-repo] all hard requirements OK");
296
+ }
775
297
  }
776
298
 
777
- const verb = summary.dryRun ? "would update" : "updated";
778
-
779
- if (summary.dryRun) {
780
- if (summary.planned.length === 0) {
781
- process.stderr.write("Nothing to update — no outdated surfaces in scope.\n");
299
+ // ---------------------------------------------------------------------------
300
+ // `doctor <plugin> --repo <path>` — read-only repo-local health check.
301
+ // Any plugin, bundled or not: bundled-extensions.json is the DEFAULT set, not
302
+ // an allow-list. `<plugin>` is a bundled name or an explicit path, resolved
303
+ // exactly like install/uninstall.
304
+ // ---------------------------------------------------------------------------
305
+ if (command === "doctor") {
306
+ if (!path || !options.repo) {
307
+ fail(`doctor requires: doctor <plugin> --repo <path> [--json]\n${REPO_USAGE_HINT}`);
782
308
  }
783
- for (const e of summary.planned) {
784
- const from = e.installedHash ? String(e.installedHash).slice(0, 12) : "(none)";
785
- const to = e.currentHash ? String(e.currentHash).slice(0, 12) : "(unknown)";
786
- console.log(`${verb}: ${e.name} [${e.platform}] @ ${e.home} ${from} -> ${to}`);
309
+ let pluginRoot;
310
+ try {
311
+ pluginRoot = resolveBundlePath(path);
312
+ } catch (err) {
313
+ console.error(err instanceof Error ? err.message : String(err));
314
+ exit(1);
787
315
  }
788
- } else {
789
- for (const e of summary.updated) {
790
- console.log(`updated: ${e.name} [${e.platform}] @ ${e.home}`);
316
+ let report;
317
+ try {
318
+ report = runRepoDoctor(pluginRoot, { repo: options.repo });
319
+ } catch (err) {
320
+ console.error(err instanceof Error ? err.message : String(err));
321
+ exit(1);
791
322
  }
323
+ if (options.json) {
324
+ console.log(JSON.stringify(report, null, 2));
325
+ } else {
326
+ printRepoDoctorReport(report);
327
+ }
328
+ exit(report.exitCode);
792
329
  }
793
330
 
794
- for (const e of summary.skipped) {
795
- process.stderr.write(
796
- `skipped-orphaned: ${e.name} [${e.platform}] @ ${e.home} (canonical source gone)\n`,
797
- );
798
- }
799
- for (const f of summary.failed) {
800
- const e = f.entry;
801
- process.stderr.write(
802
- `failed: ${e.name} [${e.platform}] @ ${e.home}: ${f.error.message}\n`,
803
- );
804
- }
805
-
806
- const doneCount = summary.dryRun ? summary.planned.length : summary.updated.length;
807
- process.stderr.write(
808
- `${doneCount} ${summary.dryRun ? "would be updated" : "updated"}, ` +
809
- `${summary.skipped.length} skipped (orphaned), ${summary.failed.length} failed.\n`,
810
- );
811
-
812
- // Non-zero exit ONLY on a real install failure.
813
- process.exit(summary.failed.length > 0 ? 1 : 0);
814
- }
815
-
816
- // ---------------------------------------------------------------------------
817
- // `prune` command — remove orphaned installed surfaces, driven by the ledger.
818
- //
819
- // A surface is pruned only when:
820
- // - the ledger still says it is installed,
821
- // - it belongs to the requested marketplace,
822
- // - its platform is in scope, and
823
- // - no local source bundle exists for that plugin name.
824
- // ---------------------------------------------------------------------------
825
- if (command === "prune") {
826
- let summary;
827
- try {
828
- summary = runPrune({
829
- platform: options.platform ?? "all",
830
- dryRun: options.dryRun,
831
- ledgerHome: homedir(),
832
- cwd: processCwd(),
833
- marketplaceName: options.marketplaceName,
834
- });
835
- } catch (err) {
836
- process.stderr.write(
837
- `[enact-extensions prune] ${err instanceof Error ? err.message : String(err)}\n`,
838
- );
839
- process.exit(1);
840
- }
841
-
842
- if (options.json) {
843
- process.stdout.write(JSON.stringify(summary, null, 2) + "\n");
844
- process.exit(summary.failed.length > 0 ? 1 : 0);
845
- }
331
+ // ---------------------------------------------------------------------------
332
+ // `list` command — enumerate available plugin bundles.
333
+ // ---------------------------------------------------------------------------
334
+ if (command === "list") {
335
+ // The package's own extensions/ dir, resolved relative to this script.
336
+ const PACKAGE_ROOT = resolve(__dirname, "..");
337
+ const packageExtensions = join(PACKAGE_ROOT, "extensions");
846
338
 
847
- const rows = summary.dryRun ? summary.candidates : summary.pruned;
848
- const verb = summary.dryRun ? "would prune" : "pruned";
849
- if (rows.length === 0) {
850
- process.stderr.write("Nothing to prune — no orphaned installed surfaces in scope.\n");
851
- }
852
- for (const entry of rows) {
853
- console.log(`${verb}: ${entry.name} [${entry.platform}] @ ${entry.home}`);
854
- }
855
- for (const f of summary.failed) {
856
- process.stderr.write(
857
- `failed: ${f.entry.name} [${f.entry.platform}] @ ${f.entry.home}: ${f.error.message}\n`,
858
- );
859
- }
860
- process.stderr.write(
861
- `${rows.length} ${summary.dryRun ? "would be pruned" : "pruned"}, ${summary.failed.length} failed.\n`,
862
- );
863
- process.exit(summary.failed.length > 0 ? 1 : 0);
864
- }
865
-
866
- // ---------------------------------------------------------------------------
867
- // `serve` command — localhost API + static-file server (no pluginRoot needed).
868
- // Handled here (before resolveRoot) because it is async + long-running.
869
- // ---------------------------------------------------------------------------
870
- if (command === "serve") {
871
- const PACKAGE_ROOT = resolve(__dirname, "..");
872
- const port = resolveServePort(options);
873
- const host = options.host ?? "127.0.0.1";
339
+ let roots;
340
+ if (path) {
341
+ // Explicit dir argument → scan only that dir.
342
+ roots = [resolve(processCwd(), path)];
343
+ } else {
344
+ // Default: package-bundled extensions/ + cwd/extensions/ (if it differs).
345
+ roots = [packageExtensions];
346
+ const cwdExtensions = join(processCwd(), "extensions");
347
+ if (resolve(cwdExtensions) !== resolve(packageExtensions) && existsSync(cwdExtensions)) {
348
+ roots.push(cwdExtensions);
349
+ }
350
+ }
874
351
 
875
- if (host !== "127.0.0.1" && host !== "localhost" && host !== "::1") {
876
- process.stderr.write(
877
- `[enact-extensions serve] WARNING: binding to ${host} exposes the API to other machines on the network.\n`,
878
- );
879
- }
352
+ const entries = listBundles(roots);
880
353
 
881
- // Use async IIFE. The http.Server keeps the event loop alive after this IIFE resolves.
882
- (async () => {
883
- let serverInstance;
884
- let url;
885
- try {
886
- const started = await startServer({
887
- port,
888
- host,
889
- packageRoot: PACKAGE_ROOT,
890
- installDefaults: { ledgerHome: homedir() },
891
- });
892
- serverInstance = started.server;
893
- url = started.url;
894
- } catch (err) {
895
- process.stderr.write(`[enact-extensions serve] Failed to start server: ${err instanceof Error ? err.message : String(err)}\n`);
896
- process.exit(1);
354
+ if (options.json) {
355
+ console.log(JSON.stringify(entries, null, 2));
356
+ exit(0);
897
357
  }
898
358
 
899
- console.log(`enact-extensions serve: listening at ${url}`);
900
-
901
- if (options.open) {
902
- // Best-effort browser open; ignore all errors silently.
903
- const { spawn } = await import("node:child_process");
904
- const opener =
905
- process.platform === "darwin"
906
- ? "open"
907
- : process.platform === "win32"
908
- ? "start"
909
- : "xdg-open";
910
- try {
911
- spawn(opener, [url], { detached: true, stdio: "ignore" }).unref();
912
- } catch {
913
- // xdg-open / open not available in all environments — ignore
914
- }
359
+ // Human-readable table.
360
+ const TRUNC = 60;
361
+ function trunc(str, len) {
362
+ if (!str) return "";
363
+ return str.length > len ? str.slice(0, len - 1) + "…" : str;
915
364
  }
916
365
 
917
- // SIGINT / SIGTERM → graceful shutdown
918
- const shutdown = () => serverInstance.close(() => process.exit(0));
919
- process.on("SIGINT", shutdown);
920
- process.on("SIGTERM", shutdown);
921
- })();
366
+ if (entries.length === 0) {
367
+ console.log("No plugin bundles found.");
368
+ exit(0);
369
+ }
922
370
 
923
- // Do NOT fall through to resolveRoot / the plugin-root command block.
924
- // process.exit is intentionally NOT called — the http.Server ref keeps Node alive.
925
- // Use a throw inside a never-reached block OR just let the module end here.
926
- // The trick: throw a synthetic signal so the try/catch-less module scope terminates.
927
- // Actually: we use process.exitCode = 0 and allow the event loop to keep running.
928
- process.exitCode = 0; // signal intent; overridden to 1 on error inside IIFE
929
- }
371
+ // Header.
372
+ const COL_NAME = 28;
373
+ const COL_VER = 8;
374
+ const COL_CAT = 18;
375
+ const COL_SURF = 22;
930
376
 
931
- // ---------------------------------------------------------------------------
932
- // `hook` command — execute a hook handler by name, reading JSON from stdin.
933
- //
934
- // Currently supported sub-commands:
935
- // enact-extensions hook session-start
936
- // Runs the advisory drift-check and prints valid hook JSON only when plugin
937
- // surfaces are outdated or auto-updated.
938
- // enact-extensions hook user-prompt-submit
939
- // Reads a UserPromptSubmit hook payload from stdin, extracts the prompt,
940
- // builds the skill catalog from the bundled extensions dir, routes the
941
- // prompt, and writes the hook response to stdout.
942
- // Set ENACT_EXTENSIONS_UPS_ROUTER=0 to disable this hook locally without
943
- // removing the installed plugin.
944
- //
945
- // Output contract (Claude Code hooks):
946
- // Match: { continue: true, hookSpecificOutput: { hookEventName: "UserPromptSubmit", additionalContext: <string> } }
947
- // No match: { continue: true }
948
- // Error: { continue: true } (never throws/crashes — degrade gracefully)
949
- // ---------------------------------------------------------------------------
950
- if (command === "hook") {
951
- const subCommand = path; // positional[1]
377
+ function padR(str, n) {
378
+ return String(str ?? "")
379
+ .padEnd(n)
380
+ .slice(0, n);
381
+ }
952
382
 
953
- if (subCommand !== "user-prompt-submit" && subCommand !== "session-start") {
954
- process.stderr.write(
955
- `[enact-extensions hook] Unknown sub-command: ${subCommand ?? "(none)"}. ` +
956
- `Supported: session-start, user-prompt-submit\n`,
383
+ const hr = "─".repeat(COL_NAME + COL_VER + COL_CAT + COL_SURF + 3 + TRUNC);
384
+ console.log(hr);
385
+ console.log(
386
+ padR("Name", COL_NAME) +
387
+ " " +
388
+ padR("Ver", COL_VER) +
389
+ " " +
390
+ padR("Category", COL_CAT) +
391
+ " " +
392
+ padR("Surfaces", COL_SURF) +
393
+ " " +
394
+ "Description",
957
395
  );
958
- process.stdout.write(JSON.stringify({ continue: true }) + "\n");
959
- process.exit(0);
960
- }
961
-
962
- // Async IIFE: stdin reading and dynamic import require async context.
963
- (async () => {
964
- if (subCommand === "session-start") {
965
- try {
966
- const { handleSessionStartDrift, sessionStartHookJson } = await import("./lib/auto-update.mjs");
967
- // Opt-in is the USER's: gate auto-apply on the env var they set.
968
- const autoApply = !!process.env.ENACT_EXTENSIONS_AUTO_UPDATE;
969
- const result = handleSessionStartDrift({ autoApply });
970
- if (result.line) {
971
- process.stdout.write(`${sessionStartHookJson(result.line)}\n`);
972
- }
973
- } catch {
974
- // Fail-silent for session-start hooks.
975
- }
976
- process.exit(0);
396
+ console.log(hr);
397
+
398
+ for (const e of entries) {
399
+ const surfaces = e.targets.join(", ");
400
+ console.log(
401
+ padR(e.name, COL_NAME) +
402
+ " " +
403
+ padR(e.version, COL_VER) +
404
+ " " +
405
+ padR(e.category, COL_CAT) +
406
+ " " +
407
+ padR(surfaces, COL_SURF) +
408
+ " " +
409
+ trunc(e.description, TRUNC),
410
+ );
977
411
  }
978
-
979
- if (process.env.ENACT_EXTENSIONS_UPS_ROUTER === "0") {
980
- process.stdout.write(JSON.stringify({ continue: true }) + "\n");
981
- process.exit(0);
412
+ console.log(hr);
413
+ console.log(`${entries.length} bundle${entries.length === 1 ? "" : "s"} found.`);
414
+ exit(0);
415
+ }
416
+
417
+ // ---------------------------------------------------------------------------
418
+ // `index` command — generate a discovery index of all plugin bundles.
419
+ //
420
+ // The index is a LOCAL GENERATED ARTIFACT (never committed). The generated/
421
+ // directory is gitignored. The schema at spec/index.json is the committed
422
+ // contract. No network, no marketplace, no catalog/ — purely local.
423
+ // ---------------------------------------------------------------------------
424
+ if (command === "index") {
425
+ const PACKAGE_ROOT = resolve(__dirname, "..");
426
+ const packageExtensions = join(PACKAGE_ROOT, "extensions");
427
+
428
+ // Same default roots as `list`.
429
+ const roots = [packageExtensions];
430
+ const cwdExtensions = join(processCwd(), "extensions");
431
+ if (resolve(cwdExtensions) !== resolve(packageExtensions) && existsSync(cwdExtensions)) {
432
+ roots.push(cwdExtensions);
982
433
  }
983
434
 
984
- // Read stdin to get the hook payload JSON.
985
- let payload;
435
+ // Build the index. The wall-clock timestamp is computed here at the CLI
436
+ // boundary and passed in — buildIndex itself stays pure (never calls Date).
437
+ let index;
986
438
  try {
987
- const chunks = [];
988
- for await (const chunk of process.stdin) chunks.push(chunk);
989
- const raw = Buffer.concat(chunks).toString("utf8").trim();
990
- payload = raw ? JSON.parse(raw) : {};
991
- } catch {
992
- // Malformed or empty stdin — safe passthrough.
993
- process.stdout.write(JSON.stringify({ continue: true }) + "\n");
994
- process.exit(0);
439
+ index = buildIndex(roots, { now: new Date().toISOString(), packageRoot: PACKAGE_ROOT });
440
+ } catch (err) {
441
+ console.error(
442
+ `[enact-extensions index] Failed to build index: ${err instanceof Error ? err.message : String(err)}`,
443
+ );
444
+ exit(1);
995
445
  }
996
446
 
447
+ // Validate against spec/index.json before writing.
448
+ const schemaPath = join(PACKAGE_ROOT, "spec", "index.json");
449
+ let schema;
997
450
  try {
998
- // Extract the user prompt from the hook payload.
999
- // `prompt` is the canonical UserPromptSubmit field across surfaces:
1000
- // Claude Code (code.claude.com/docs/en/hooks) and Codex
1001
- // (developers.openai.com/codex/hooks) both send { prompt: string, ... };
1002
- // Cursor follows the same Claude-compatible schema. Single field, no fallback.
1003
- const prompt = payload && typeof payload.prompt === "string" ? payload.prompt : null;
1004
-
1005
- if (!prompt) {
1006
- process.stdout.write(JSON.stringify({ continue: true }) + "\n");
1007
- process.exit(0);
1008
- }
1009
-
1010
- // Resolve the extensions dir relative to the installed package root so
1011
- // the hook works whether invoked from the repo or the globally installed package.
1012
- const { buildSkillCatalog, routePrompt } = await import("./lib/ups-router.mjs");
1013
- const extensionsDir = join(PACKAGE_ROOT, "extensions");
1014
- const catalog = buildSkillCatalog(extensionsDir);
1015
-
1016
- const { additionalContext } = routePrompt(prompt, catalog);
451
+ schema = JSON.parse(readFileSync(schemaPath, "utf8"));
452
+ } catch (err) {
453
+ console.error(
454
+ `[enact-extensions index] Cannot read schema ${schemaPath}: ${err instanceof Error ? err.message : String(err)}`,
455
+ );
456
+ exit(1);
457
+ }
1017
458
 
1018
- if (additionalContext) {
1019
- process.stdout.write(
1020
- JSON.stringify({
1021
- continue: true,
1022
- hookSpecificOutput: {
1023
- hookEventName: "UserPromptSubmit",
1024
- additionalContext,
1025
- },
1026
- }) + "\n",
1027
- );
1028
- } else {
1029
- process.stdout.write(JSON.stringify({ continue: true }) + "\n");
1030
- }
1031
- } catch {
1032
- // Any unexpected error — safe passthrough, never crash.
1033
- process.stdout.write(JSON.stringify({ continue: true }) + "\n");
459
+ const addFormats = addFormatsModule.default ?? addFormatsModule;
460
+ const ajv = new Ajv({ allErrors: true, strict: false });
461
+ addFormats(ajv);
462
+ const validate = ajv.compile(schema);
463
+ const valid = validate(index);
464
+ if (!valid) {
465
+ const errs = (validate.errors ?? []).map((e) => ` ${e.instancePath || "/"}: ${e.message}`).join("\n");
466
+ console.error(`[enact-extensions index] Generated index failed schema validation:\n${errs}`);
467
+ exit(1);
1034
468
  }
1035
469
 
1036
- process.exit(0);
1037
- })();
1038
- }
470
+ const json = JSON.stringify(index, null, 2);
471
+ // --stdout is normalized to "-" during arg parsing, so this single check covers both.
472
+ const toStdout = options.out === "-";
1039
473
 
1040
- // ---------------------------------------------------------------------------
1041
- // Commands that require resolving a plugin root path.
1042
- // (Only run when command is not already handled above.)
1043
- // ---------------------------------------------------------------------------
1044
- if (command !== "serve" && command !== "hook") {
474
+ if (toStdout) {
475
+ // Write JSON to stdout; summary to stderr (keeps stdout pipe-clean).
476
+ process.stdout.write(json + "\n");
477
+ process.stderr.write(`[enact-extensions index] ${index.count} plugin${index.count === 1 ? "" : "s"} indexed.\n`);
478
+ } else {
479
+ const outPath = options.out ? resolve(processCwd(), options.out) : join(PACKAGE_ROOT, "generated", "index.json");
1045
480
 
1046
- // Resolve the plugin root:
1047
- // - If no path arg given → default to cwd (existing behaviour).
1048
- // - If a path arg is given → resolveBundlePath handles both explicit paths and
1049
- // bare plugin names, throwing meaningful errors when nothing resolves.
1050
- const resolveRoot = (pathArg) => {
1051
- if (!pathArg) return resolvePluginRoot(undefined);
1052
- return resolveBundlePath(pathArg);
1053
- };
481
+ // Ensure parent directory exists.
482
+ const outDir = resolve(outPath, "..");
483
+ mkdirSync(outDir, { recursive: true });
1054
484
 
1055
- // A bare name has no path separators (so it is not an explicit filesystem path).
1056
- const isBareName = (arg) =>
1057
- typeof arg === "string" && arg.length > 0 && !arg.includes("/") && !arg.includes("\\");
485
+ writeFileSync(outPath, json + "\n", "utf8");
486
+ process.stderr.write(
487
+ `[enact-extensions index] Wrote ${index.count} plugin${index.count === 1 ? "" : "s"} to ${outPath}\n`,
488
+ );
489
+ }
1058
490
 
1059
- let pluginRoot = null;
1060
- try {
1061
- pluginRoot = resolveRoot(path);
1062
- } catch (err) {
1063
- // UNINSTALL-ONLY FALLBACK: uninstall must work even when the source bundle is
1064
- // gone. If the arg is a BARE NAME (not an explicit path) that did not resolve
1065
- // to a bundle, fall back to using the arg verbatim as the plugin name. The
1066
- // uninstall handler reads the name from `path` when pluginRoot is null.
1067
- // For an explicit path that does not exist, or for any other command, this is
1068
- // still a hard error.
1069
- if (command === "uninstall" && isBareName(path)) {
1070
- pluginRoot = null; // signal: use the raw `path` as the plugin name
1071
- } else {
1072
- console.error(err instanceof Error ? err.message : String(err));
1073
- process.exit(1);
491
+ exit(0);
1074
492
  }
1075
- }
1076
493
 
1077
- try {
1078
- if (command === "validate") {
1079
- const ok = runValidate(pluginRoot, undefined, { pluginValidate: true });
1080
- process.exit(ok ? 0 : 1);
494
+ // ---------------------------------------------------------------------------
495
+ // Commands that resolve a plugin root path: validate, sync, install/uninstall --repo.
496
+ // ---------------------------------------------------------------------------
497
+ let pluginRoot = null;
498
+ try {
499
+ pluginRoot = path ? resolveBundlePath(path) : resolve(processCwd());
500
+ } catch (err) {
501
+ // uninstall --repo must work even when the source bundle is gone: a bare
502
+ // name that does not resolve is used verbatim as the plugin name.
503
+ const isBareName = typeof path === "string" && path.length > 0 && !path.includes("/") && !path.includes("\\");
504
+ if (!(command === "uninstall" && isBareName)) {
505
+ fail(err instanceof Error ? err.message : String(err));
506
+ }
1081
507
  }
1082
508
 
1083
- if (command === "sync") {
1084
- runSync(pluginRoot, { name: options.name });
1085
- process.exit(0);
509
+ function manifestName(root) {
510
+ const manifestPath = root ? join(root, ".agents", "plugin.json") : null;
511
+ return manifestPath && existsSync(manifestPath) ? JSON.parse(readFileSync(manifestPath, "utf8")).name : null;
1086
512
  }
1087
513
 
1088
- if (command === "install") {
1089
- if (options.dryRun) {
1090
- console.error(INSTALL_DRY_RUN_UNSUPPORTED_MESSAGE);
1091
- process.exit(1);
514
+ try {
515
+ if (command === "validate") {
516
+ const ok = runValidate(pluginRoot, undefined, { pluginValidate: true, skillValidate: true });
517
+ exit(ok ? 0 : 1);
1092
518
  }
1093
- // Single global ledger: every install (local or global scope) records to
1094
- // <homedir>/.enact/extensions/ledger.jsonl so `log` always finds it,
1095
- // regardless of which per-platform home the bundle was installed to. The
1096
- // local/global distinction is carried in each entry's `scope` field.
1097
- runInstall(pluginRoot, { ...options, ledgerHome: homedir() });
1098
- process.exit(0);
1099
- }
1100
519
 
1101
- if (command === "uninstall") {
1102
- // Resolve plugin name: if the source bundle resolved on disk, read its
1103
- // manifest name from .agents/plugin.json (or .codex-plugin/plugin.json).
1104
- // Otherwise (source bundle gone / bare-name fallback), treat the raw
1105
- // positional arg as the plugin name. This lets uninstall remove already-
1106
- // installed plugins even when their source bundle has been deleted.
1107
- let pluginName = null;
1108
- if (pluginRoot) {
1109
- try {
1110
- const enactManifestPath = join(pluginRoot, ".agents", "plugin.json");
1111
- if (existsSync(enactManifestPath)) {
1112
- const manifest = JSON.parse(readFileSync(enactManifestPath, "utf8"));
1113
- pluginName = typeof manifest.name === "string" ? manifest.name : null;
1114
- }
1115
- // Fall back to .codex-plugin/plugin.json
1116
- if (!pluginName) {
1117
- const codexManifestPath = join(pluginRoot, ".codex-plugin", "plugin.json");
1118
- if (existsSync(codexManifestPath)) {
1119
- const manifest = JSON.parse(readFileSync(codexManifestPath, "utf8"));
1120
- pluginName = typeof manifest.name === "string" ? manifest.name : null;
1121
- }
1122
- }
1123
- } catch {
1124
- // Source bundle unreadable — fall through to raw name.
1125
- }
520
+ if (command === "sync") {
521
+ runSync(pluginRoot, { name: options.name });
522
+ exit(0);
1126
523
  }
1127
- // Fall back: treat the raw positional arg as the plugin name.
1128
- // `path` is the raw positional from the CLI. When the source bundle was gone
1129
- // (pluginRoot === null) this is the only source of the name.
1130
- if (!pluginName) {
1131
- pluginName = path ?? (pluginRoot ? pluginRoot.split("/").pop() : null);
524
+
525
+ // install | uninstall --repo
526
+ const dryRun = options.dryRun === true;
527
+ const repoPluginName = manifestName(pluginRoot) ?? path;
528
+ if (!repoPluginName) {
529
+ fail(`${command} --repo requires a plugin name`);
1132
530
  }
1133
- if (!pluginName) {
1134
- console.error("uninstall requires a plugin name or path.");
1135
- process.exit(1);
531
+ const result =
532
+ command === "install"
533
+ ? runRepoInstall(pluginRoot, { repo: options.repo, host: options.host, force: options.force, dryRun })
534
+ : runRepoUninstall(repoPluginName, { repo: options.repo, purge: options.purge, force: options.force, dryRun });
535
+ if (dryRun) {
536
+ if (result.actions.length === 0) {
537
+ console.log("[dry-run] no filesystem changes — nothing touched");
538
+ } else {
539
+ for (const a of result.actions) {
540
+ console.log(`[dry-run] would ${a.action}: ${a.file} — ${a.reason}`);
541
+ }
542
+ }
1136
543
  }
1137
- runUninstall(pluginName, { ...options, ledgerHome: homedir() });
1138
- process.exit(0);
544
+ exit(0);
545
+ } catch (err) {
546
+ if (err instanceof ExitSignal) throw err;
547
+ fail(err instanceof Error ? err.message : String(err));
1139
548
  }
1140
-
1141
- console.error(`Unknown command: ${command}\n`);
1142
- console.log(HELP);
1143
- process.exit(1);
1144
549
  } catch (err) {
1145
- console.error(err instanceof Error ? err.message : String(err));
1146
- process.exit(1);
550
+ // The only expected escape: a command asked to finish with a status.
551
+ // Setting exitCode rather than exiting lets node drain stdout first.
552
+ if (!(err instanceof ExitSignal)) throw err;
553
+ process.exitCode = err.code;
1147
554
  }
1148
-
1149
- } // end if (command !== "serve")