@amsterdamdatalabs/enact-extensions 0.1.0 → 0.1.3

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 (245) hide show
  1. package/README.md +96 -21
  2. package/dist/index.d.ts +5 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +3 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/install.d.ts +171 -1
  7. package/dist/install.d.ts.map +1 -1
  8. package/dist/install.js +402 -49
  9. package/dist/install.js.map +1 -1
  10. package/dist/internal/codex.d.ts.map +1 -1
  11. package/dist/internal/codex.js +7 -1
  12. package/dist/internal/codex.js.map +1 -1
  13. package/dist/internal/platform.d.ts +8 -0
  14. package/dist/internal/platform.d.ts.map +1 -1
  15. package/dist/internal/platform.js +46 -2
  16. package/dist/internal/platform.js.map +1 -1
  17. package/dist/provision.d.ts +30 -0
  18. package/dist/provision.d.ts.map +1 -0
  19. package/dist/provision.js +202 -0
  20. package/dist/provision.js.map +1 -0
  21. package/dist/validate/index.d.ts +44 -0
  22. package/dist/validate/index.d.ts.map +1 -1
  23. package/dist/validate/index.js +157 -0
  24. package/dist/validate/index.js.map +1 -1
  25. package/extensions/cmux/.agents/plugin.json +37 -0
  26. package/extensions/cmux/skills/cmux/SKILL.md +82 -0
  27. package/extensions/cmux/skills/cmux/agents/openai.yaml +4 -0
  28. package/extensions/cmux/skills/cmux/references/handles-and-identify.md +35 -0
  29. package/extensions/cmux/skills/cmux/references/panes-surfaces.md +37 -0
  30. package/extensions/cmux/skills/cmux/references/trigger-flash-and-health.md +23 -0
  31. package/extensions/cmux/skills/cmux/references/windows-workspaces.md +31 -0
  32. package/extensions/cmux/skills/cmux-vm-monitor/SKILL.md +122 -0
  33. package/extensions/cmux/skills/cmux-vm-monitor/agents/openai.yaml +4 -0
  34. package/extensions/cmux/skills/cmux-vm-monitor/references/cmux-commands.md +66 -0
  35. package/extensions/cmux/skills/cmux-vm-monitor/scripts/codex_vm_monitor.sh +45 -0
  36. package/extensions/cmux/skills/cmux-workspace/SKILL.md +93 -0
  37. package/extensions/dev-state/.agents/plugin.json +35 -0
  38. package/extensions/dev-state/skills/dev-state-plan-graduation/SKILL.md +194 -0
  39. package/extensions/dev-state/skills/dev-state-plan-graduation/agents/openai.yaml +4 -0
  40. package/extensions/dev-state/skills/dev-state-plan-graduation/references/reference.md +130 -0
  41. package/extensions/devops/.agents/plugin.json +36 -0
  42. package/extensions/devops/skills/azure-devops-cli/SKILL.md +431 -0
  43. package/extensions/devops/skills/azure-devops-cli/agents/openai.yaml +4 -0
  44. package/extensions/devops/skills/ci-pipeline-strategy/SKILL.md +217 -0
  45. package/extensions/devops/skills/ci-pipeline-strategy/agents/openai.yaml +4 -0
  46. package/extensions/enact-context/.agents/plugin.json +40 -0
  47. package/extensions/enact-context/.mcp.json +8 -0
  48. package/extensions/enact-context/README.md +25 -0
  49. package/extensions/enact-context/assets/icon.png +0 -0
  50. package/extensions/enact-context/assets/logo.png +0 -0
  51. package/extensions/enact-context/hooks/hooks.json +115 -0
  52. package/extensions/enact-context/skills/enact-context/SKILL.md +149 -0
  53. package/extensions/enact-context/skills/enact-context/scripts/install.sh +69 -0
  54. package/extensions/enact-factory/.agents/plugin.json +42 -0
  55. package/extensions/enact-factory/.mcp.json +8 -0
  56. package/extensions/enact-factory/assets/icon.png +0 -0
  57. package/extensions/enact-factory/assets/logo.png +0 -0
  58. package/extensions/enact-factory/hooks/user-prompt-submit.mjs +67 -0
  59. package/extensions/enact-factory/skills/testing-strategy/SKILL.md +167 -0
  60. package/extensions/enact-factory/skills/workitem-triage/SKILL.md +22 -0
  61. package/extensions/enact-operator/.agents/plugin.json +57 -0
  62. package/extensions/enact-operator/.app.json +3 -0
  63. package/extensions/enact-operator/.mcp.json +10 -0
  64. package/extensions/enact-operator/_taxonomy.md +86 -0
  65. package/extensions/enact-operator/agents/README.md +5 -0
  66. package/extensions/enact-operator/agents/architect.toml +25 -0
  67. package/extensions/enact-operator/agents/code-reviewer.toml +24 -0
  68. package/extensions/enact-operator/agents/critic.toml +30 -0
  69. package/extensions/enact-operator/agents/executor.toml +24 -0
  70. package/extensions/enact-operator/agents/explore.toml +23 -0
  71. package/extensions/enact-operator/agents/planner.toml +24 -0
  72. package/extensions/enact-operator/agents/verifier.toml +24 -0
  73. package/extensions/enact-operator/assets/icon.png +0 -0
  74. package/extensions/enact-operator/assets/logo.png +0 -0
  75. package/extensions/enact-operator/commands/doctor.md +39 -0
  76. package/extensions/enact-operator/commands/setup.md +51 -0
  77. package/extensions/enact-operator/hooks/hooks.json +146 -0
  78. package/extensions/enact-operator/skills/_variants.md +44 -0
  79. package/extensions/enact-operator/skills/ai-slop-cleaner/SKILL.md +50 -0
  80. package/extensions/enact-operator/skills/analyze/SKILL.md +91 -0
  81. package/extensions/enact-operator/skills/ask/SKILL.md +47 -0
  82. package/extensions/enact-operator/skills/autopilot/SKILL.md +170 -0
  83. package/extensions/enact-operator/skills/autoresearch-goal/SKILL.md +79 -0
  84. package/extensions/enact-operator/skills/cancel/SKILL.md +99 -0
  85. package/extensions/enact-operator/skills/configure-notifications/SKILL.md +77 -0
  86. package/extensions/enact-operator/skills/deep-interview/SKILL.md +80 -0
  87. package/extensions/enact-operator/skills/doctor/SKILL.md +48 -0
  88. package/extensions/enact-operator/skills/hud/SKILL.md +49 -0
  89. package/extensions/enact-operator/skills/hyperplan/SKILL.md +47 -0
  90. package/extensions/enact-operator/skills/plan/SKILL.md +78 -0
  91. package/extensions/enact-operator/skills/ralph/SKILL.md +201 -0
  92. package/extensions/enact-operator/skills/ralph/gemini.md +18 -0
  93. package/extensions/enact-operator/skills/ralplan/SKILL.md +151 -0
  94. package/extensions/enact-operator/skills/remove-deadcode/SKILL.md +45 -0
  95. package/extensions/enact-operator/skills/research/SKILL.md +74 -0
  96. package/extensions/enact-operator/skills/review/SKILL.md +58 -0
  97. package/extensions/enact-operator/skills/security-research/SKILL.md +54 -0
  98. package/extensions/enact-operator/skills/setup/SKILL.md +91 -0
  99. package/extensions/enact-operator/skills/setup/scripts/install.sh +50 -0
  100. package/extensions/enact-operator/skills/skill/SKILL.md +82 -0
  101. package/extensions/enact-operator/skills/tdd/SKILL.md +59 -0
  102. package/extensions/enact-operator/skills/team/SKILL.md +199 -0
  103. package/extensions/enact-operator/skills/trace/SKILL.md +41 -0
  104. package/extensions/enact-operator/skills/ultragoal/SKILL.md +99 -0
  105. package/extensions/enact-operator/skills/ultraqa/SKILL.md +113 -0
  106. package/extensions/enact-operator/skills/ultrawork/SKILL.md +145 -0
  107. package/extensions/enact-operator/skills/ultrawork/planner.md +28 -0
  108. package/extensions/enact-operator/skills/wiki/SKILL.md +41 -0
  109. package/extensions/enact-operator/skills/work-with-workitem/SKILL.md +51 -0
  110. package/extensions/enact-wiki/.agents/plugin.json +42 -0
  111. package/extensions/enact-wiki/.mcp.json +15 -0
  112. package/extensions/enact-wiki/README.md +44 -0
  113. package/extensions/enact-wiki/assets/icon.png +0 -0
  114. package/extensions/enact-wiki/assets/logo.png +0 -0
  115. package/extensions/enact-wiki/skills/document-parser/SKILL.md +17 -0
  116. package/extensions/enact-wiki/skills/document-parser/scripts/parse.sh +60 -0
  117. package/extensions/enact-wiki/skills/document-parser/skill.json +9 -0
  118. package/extensions/enact-wiki/skills/enact-wiki/SKILL.md +30 -0
  119. package/extensions/enact-wiki/skills/enact-wiki/references/ingest.md +62 -0
  120. package/extensions/enact-wiki/skills/enact-wiki/references/manage.md +34 -0
  121. package/extensions/enact-wiki/skills/enact-wiki/references/query.md +59 -0
  122. package/extensions/enact-wiki/skills/search-lab/SKILL.md +57 -0
  123. package/extensions/enact-wiki/skills/search-lab/scripts/analyze.ts +23 -0
  124. package/{plugins/net-revenue-management/.codex-plugin → extensions/net-revenue-management/.agents}/plugin.json +10 -6
  125. package/extensions/plugin-dev/.agents/plugin.json +42 -0
  126. package/extensions/plugin-dev/.mcp.json +3 -0
  127. package/extensions/plugin-dev/agents/agent-creator.md +199 -0
  128. package/extensions/plugin-dev/agents/plugin-validator.md +91 -0
  129. package/extensions/plugin-dev/agents/skill-reviewer.md +212 -0
  130. package/extensions/plugin-dev/commands/_archive/create-marketplace.md +427 -0
  131. package/extensions/plugin-dev/commands/_archive/plugin-dev-guide.md +12 -0
  132. package/extensions/plugin-dev/commands/create-plugin.md +498 -0
  133. package/extensions/plugin-dev/commands/start.md +81 -0
  134. package/extensions/plugin-dev/hooks/hooks.json +3 -0
  135. package/extensions/plugin-dev/skills/agent-development/SKILL.md +641 -0
  136. package/extensions/plugin-dev/skills/agent-development/examples/agent-creation-prompt.md +250 -0
  137. package/extensions/plugin-dev/skills/agent-development/examples/complete-agent-examples.md +461 -0
  138. package/extensions/plugin-dev/skills/agent-development/references/advanced-agent-fields.md +246 -0
  139. package/extensions/plugin-dev/skills/agent-development/references/agent-creation-system-prompt.md +216 -0
  140. package/extensions/plugin-dev/skills/agent-development/references/permission-modes-rules.md +226 -0
  141. package/extensions/plugin-dev/skills/agent-development/references/system-prompt-design.md +464 -0
  142. package/extensions/plugin-dev/skills/agent-development/references/triggering-examples.md +474 -0
  143. package/extensions/plugin-dev/skills/agent-development/scripts/create-agent-skeleton.sh +176 -0
  144. package/extensions/plugin-dev/skills/agent-development/scripts/test-agent-trigger.sh +227 -0
  145. package/extensions/plugin-dev/skills/agent-development/scripts/validate-agent.sh +227 -0
  146. package/extensions/plugin-dev/skills/command-development/SKILL.md +763 -0
  147. package/extensions/plugin-dev/skills/command-development/examples/plugin-commands.md +612 -0
  148. package/extensions/plugin-dev/skills/command-development/examples/simple-commands.md +527 -0
  149. package/extensions/plugin-dev/skills/command-development/references/advanced-workflows.md +762 -0
  150. package/extensions/plugin-dev/skills/command-development/references/documentation-patterns.md +769 -0
  151. package/extensions/plugin-dev/skills/command-development/references/frontmatter-reference.md +508 -0
  152. package/extensions/plugin-dev/skills/command-development/references/interactive-commands.md +966 -0
  153. package/extensions/plugin-dev/skills/command-development/references/marketplace-considerations.md +943 -0
  154. package/extensions/plugin-dev/skills/command-development/references/plugin-features-reference.md +637 -0
  155. package/extensions/plugin-dev/skills/command-development/references/plugin-integration.md +191 -0
  156. package/extensions/plugin-dev/skills/command-development/references/skill-tool.md +447 -0
  157. package/extensions/plugin-dev/skills/command-development/references/testing-strategies.md +723 -0
  158. package/extensions/plugin-dev/skills/command-development/scripts/check-frontmatter.sh +234 -0
  159. package/extensions/plugin-dev/skills/command-development/scripts/validate-command.sh +160 -0
  160. package/extensions/plugin-dev/skills/hook-development/SKILL.md +861 -0
  161. package/extensions/plugin-dev/skills/hook-development/examples/load-context.sh +55 -0
  162. package/extensions/plugin-dev/skills/hook-development/examples/validate-bash.sh +57 -0
  163. package/extensions/plugin-dev/skills/hook-development/examples/validate-write.sh +48 -0
  164. package/extensions/plugin-dev/skills/hook-development/references/advanced.md +871 -0
  165. package/extensions/plugin-dev/skills/hook-development/references/hook-input-schemas.md +145 -0
  166. package/extensions/plugin-dev/skills/hook-development/references/migration.md +392 -0
  167. package/extensions/plugin-dev/skills/hook-development/references/patterns.md +430 -0
  168. package/extensions/plugin-dev/skills/hook-development/scripts/README.md +181 -0
  169. package/extensions/plugin-dev/skills/hook-development/scripts/hook-linter.sh +153 -0
  170. package/extensions/plugin-dev/skills/hook-development/scripts/test-hook.sh +276 -0
  171. package/extensions/plugin-dev/skills/hook-development/scripts/validate-hook-schema.sh +159 -0
  172. package/extensions/plugin-dev/skills/mcp-integration/SKILL.md +775 -0
  173. package/extensions/plugin-dev/skills/mcp-integration/examples/http-server.json +20 -0
  174. package/extensions/plugin-dev/skills/mcp-integration/examples/sse-server.json +19 -0
  175. package/extensions/plugin-dev/skills/mcp-integration/examples/stdio-server.json +38 -0
  176. package/extensions/plugin-dev/skills/mcp-integration/examples/ws-server.json +26 -0
  177. package/extensions/plugin-dev/skills/mcp-integration/references/authentication.md +601 -0
  178. package/extensions/plugin-dev/skills/mcp-integration/references/server-discovery.md +190 -0
  179. package/extensions/plugin-dev/skills/mcp-integration/references/server-types.md +572 -0
  180. package/extensions/plugin-dev/skills/mcp-integration/references/tool-usage.md +623 -0
  181. package/extensions/plugin-dev/skills/plugin-dev-guide/SKILL.md +222 -0
  182. package/extensions/plugin-dev/skills/plugin-structure/SKILL.md +705 -0
  183. package/extensions/plugin-dev/skills/plugin-structure/examples/advanced-plugin.md +774 -0
  184. package/extensions/plugin-dev/skills/plugin-structure/examples/minimal-plugin.md +83 -0
  185. package/extensions/plugin-dev/skills/plugin-structure/examples/standard-plugin.md +611 -0
  186. package/extensions/plugin-dev/skills/plugin-structure/references/advanced-topics.md +289 -0
  187. package/extensions/plugin-dev/skills/plugin-structure/references/component-patterns.md +592 -0
  188. package/extensions/plugin-dev/skills/plugin-structure/references/github-actions.md +233 -0
  189. package/extensions/plugin-dev/skills/plugin-structure/references/headless-ci-mode.md +193 -0
  190. package/extensions/plugin-dev/skills/plugin-structure/references/manifest-reference.md +625 -0
  191. package/extensions/plugin-dev/skills/plugin-structure/references/output-styles.md +116 -0
  192. package/extensions/plugin-dev/skills/skill-development/SKILL.md +564 -0
  193. package/extensions/plugin-dev/skills/skill-development/examples/complete-skill.md +465 -0
  194. package/extensions/plugin-dev/skills/skill-development/examples/frontmatter-templates.md +167 -0
  195. package/extensions/plugin-dev/skills/skill-development/examples/minimal-skill.md +111 -0
  196. package/extensions/plugin-dev/skills/skill-development/references/advanced-frontmatter.md +225 -0
  197. package/extensions/plugin-dev/skills/skill-development/references/commands-vs-skills.md +39 -0
  198. package/extensions/plugin-dev/skills/skill-development/references/skill-creation-workflow.md +379 -0
  199. package/extensions/plugin-dev/skills/skill-development/references/skill-creator-original.md +210 -0
  200. package/package.json +8 -11
  201. package/scripts/enact-extensions.mjs +823 -21
  202. package/scripts/hooks/session-start-drift-check.mjs +58 -0
  203. package/scripts/lib/build-index.mjs +50 -0
  204. package/scripts/lib/bundle-hash.mjs +137 -0
  205. package/scripts/lib/hooks.mjs +741 -0
  206. package/scripts/lib/ledger.mjs +163 -0
  207. package/scripts/lib/list-bundles.mjs +70 -0
  208. package/scripts/lib/outdated.mjs +144 -0
  209. package/scripts/lib/provision-mcp.mjs +16 -0
  210. package/scripts/lib/resolve-bundle.mjs +121 -0
  211. package/scripts/lib/run-install.mjs +402 -38
  212. package/scripts/lib/run-prune.mjs +73 -0
  213. package/scripts/lib/run-sync.mjs +9 -1
  214. package/scripts/lib/run-uninstall.mjs +244 -0
  215. package/scripts/lib/run-update.mjs +152 -0
  216. package/scripts/lib/run-validate.mjs +21 -18
  217. package/scripts/lib/serve.mjs +472 -0
  218. package/scripts/postinstall.mjs +63 -0
  219. package/scripts/setup-enact-context.sh +2 -2
  220. package/scripts/version-bump.sh +463 -0
  221. package/spec/codex.json +1 -11
  222. package/spec/index.json +59 -0
  223. package/web/assets/README.md +111 -0
  224. package/web/assets/logo-full.png +0 -0
  225. package/web/assets/logo-slim.png +0 -0
  226. package/web/assets/tokens/base.css +45 -0
  227. package/web/assets/tokens/colors.css +248 -0
  228. package/web/assets/tokens/effects.css +24 -0
  229. package/web/assets/tokens/fonts.css +8 -0
  230. package/web/assets/tokens/index.css +18 -0
  231. package/web/assets/tokens/spacing.css +50 -0
  232. package/web/index.html +1188 -0
  233. package/.agents/plugins/marketplace.json +0 -20
  234. package/catalog/enact-context.json +0 -9
  235. package/catalog/enact-factory.json +0 -7
  236. package/catalog/enact-operator.json +0 -7
  237. package/catalog/enact-wiki.json +0 -7
  238. package/catalog/net-revenue-management.json +0 -8
  239. package/scripts/rename-supervisor-to-operator.pl +0 -66
  240. package/scripts/sync-manifests.mjs +0 -23
  241. package/scripts/validate-catalog.mjs +0 -37
  242. package/scripts/validate-plugin.mjs +0 -10
  243. /package/{plugins → extensions}/net-revenue-management/.mcp.json +0 -0
  244. /package/{plugins → extensions}/net-revenue-management/skills/net-revenue-risks/SKILL.md +0 -0
  245. /package/{plugins → extensions}/net-revenue-management/skills/net-revenue-scenario/SKILL.md +0 -0
package/dist/install.js CHANGED
@@ -1,10 +1,13 @@
1
- import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
1
+ import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join, resolve } from "node:path";
3
- import { defaultCodexHome, ensureMarketplace, readCodexConfig, resolveCodexInstallHomes, setPluginEnabled, writeCodexConfig, } from "./internal/codex.js";
4
- import { syncPlatformManifests } from "./create/index.js";
5
- import { checkPluginBundleComponentPaths, readManifestFile, validateClaudeManifest, validateCodexManifest, validateCursorManifest, } from "./validate/index.js";
6
- import { defaultClaudeHome, registerLocalMarketplace } from "./internal/claude.js";
3
+ import { homedir } from "node:os";
4
+ import { defaultCodexHome, ensureMarketplace, isPluginEnabled, readCodexConfig, removePluginConfig, resolveCodexInstallHomes, setPluginEnabled, writeCodexConfig, } from "./internal/codex.js";
5
+ import { manifestForPlatform } from "./create/index.js";
6
+ import { PROJECTION_COMPONENT_FIELDS } from "./internal/platform.js";
7
+ import { checkComponentPaths, readManifestFile, validateClaudeManifest, validateCodexManifest, validateCursorManifest, } from "./validate/index.js";
8
+ import { defaultClaudeHome, readKnownMarketplaces, registerLocalMarketplace } from "./internal/claude.js";
7
9
  import { defaultCursorHome } from "./internal/cursor.js";
10
+ import { provisionMcp } from "./provision.js";
8
11
  function readJson(path, fallback) {
9
12
  if (!existsSync(path))
10
13
  return fallback;
@@ -14,14 +17,19 @@ function writeJson(path, value) {
14
17
  mkdirSync(dirname(path), { recursive: true });
15
18
  writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf8");
16
19
  }
17
- function pathFieldsFromManifest(manifest) {
18
- const fields = ["skills", "mcpServers", "apps", "commands", "hooks", "agents", "_agents", "rules"];
20
+ function pathFieldsFromManifest(platform, manifest) {
21
+ const fields = PROJECTION_COMPONENT_FIELDS[platform];
19
22
  const results = [];
20
23
  for (const field of fields) {
21
24
  const value = manifest[field];
22
25
  if (typeof value === "string" && value.trim())
23
26
  results.push(value);
24
27
  }
28
+ if (platform === "cursor") {
29
+ const logo = manifest.logo;
30
+ if (typeof logo === "string" && logo.trim())
31
+ results.push(logo);
32
+ }
25
33
  const iface = manifest.interface;
26
34
  if (iface && typeof iface === "object" && !Array.isArray(iface)) {
27
35
  for (const field of ["composerIcon", "logo"]) {
@@ -32,9 +40,9 @@ function pathFieldsFromManifest(manifest) {
32
40
  }
33
41
  return results;
34
42
  }
35
- function normalizedProjectionEntries(manifest, platformDir) {
43
+ function normalizedProjectionEntries(platform, manifest, platformDir) {
36
44
  const entries = new Set([platformDir, "README.md"]);
37
- for (const raw of pathFieldsFromManifest(manifest)) {
45
+ for (const raw of pathFieldsFromManifest(platform, manifest)) {
38
46
  const cleaned = raw.replace(/^\.\//, "").replace(/\/+$/, "");
39
47
  if (!cleaned || cleaned.startsWith("../"))
40
48
  continue;
@@ -50,10 +58,10 @@ function normalizedProjectionEntries(manifest, platformDir) {
50
58
  }
51
59
  return [...entries];
52
60
  }
53
- function projectBundle(sourceRoot, targetRoot, manifest, platformDir) {
61
+ function projectBundle(sourceRoot, targetRoot, platform, manifest, platformDir) {
54
62
  rmSync(targetRoot, { recursive: true, force: true });
55
63
  mkdirSync(targetRoot, { recursive: true });
56
- for (const entry of normalizedProjectionEntries(manifest, platformDir)) {
64
+ for (const entry of normalizedProjectionEntries(platform, manifest, platformDir)) {
57
65
  const sourcePath = resolve(sourceRoot, entry);
58
66
  if (!existsSync(sourcePath))
59
67
  continue;
@@ -61,32 +69,70 @@ function projectBundle(sourceRoot, targetRoot, manifest, platformDir) {
61
69
  }
62
70
  writeJson(join(targetRoot, platformDir, "plugin.json"), manifest);
63
71
  }
64
- function maybeSync(pluginRoot, platform) {
72
+ /**
73
+ * Derive a per-surface plugin manifest IN MEMORY from the bundle's canonical
74
+ * Enact manifest. Installs must never write a `.<platform>-plugin/` projection
75
+ * back into the SOURCE bundle dir — the derived manifest is handed to
76
+ * `projectBundle`, which writes it ONLY to the install target.
77
+ *
78
+ * Backward-compat: some legacy bundles ship a pre-derived per-surface manifest
79
+ * (`.codex-plugin/plugin.json`) with NO canonical `.agents/plugin.json`. In that
80
+ * case we fall back to reading the existing per-surface manifest from source —
81
+ * this is a read, not a write, so the source stays clean.
82
+ */
83
+ function deriveTargetManifest(pluginRoot, platform) {
65
84
  const enactManifestPath = join(pluginRoot, ".agents", "plugin.json");
66
- if (!existsSync(enactManifestPath))
67
- return [];
68
- const enact = readManifestFile(pluginRoot, "enact");
69
- return syncPlatformManifests(pluginRoot, enact, [platform]);
85
+ if (existsSync(enactManifestPath)) {
86
+ const enact = readManifestFile(pluginRoot, "enact");
87
+ return manifestForPlatform(platform, enact);
88
+ }
89
+ // Legacy fallback: a per-surface-only bundle (no canonical Enact manifest).
90
+ const surfaceManifestPath = join(pluginRoot, `.${platform}-plugin`, "plugin.json");
91
+ if (existsSync(surfaceManifestPath)) {
92
+ return readManifestFile(pluginRoot, platform);
93
+ }
94
+ throw new Error(`Cannot derive ${platform} manifest: no canonical manifest at ${enactManifestPath} ` +
95
+ `and no ${platform} manifest at ${surfaceManifestPath}`);
70
96
  }
71
97
  function stringField(manifest, key) {
72
98
  const value = manifest[key];
73
99
  return typeof value === "string" && value.trim() ? value : undefined;
74
100
  }
75
- function marketplaceRoot(codexHome, marketplaceName) {
101
+ export function codexMarketplaceRoot(codexHome, marketplaceName = "enact-os-plugins") {
76
102
  return join(codexHome, "marketplaces", marketplaceName);
77
103
  }
78
- function marketplacePath(codexHome, marketplaceName) {
79
- return join(marketplaceRoot(codexHome, marketplaceName), ".agents", "plugins", "marketplace.json");
104
+ export function codexMarketplacePath(codexHome, marketplaceName = "enact-os-plugins") {
105
+ return join(codexMarketplaceRoot(codexHome, marketplaceName), ".agents", "plugins", "marketplace.json");
80
106
  }
81
- function marketplacePluginPath(codexHome, marketplaceName, pluginName) {
82
- return join(marketplaceRoot(codexHome, marketplaceName), "plugins", pluginName);
107
+ export function codexMarketplacePluginPath(codexHome, marketplaceName, pluginName) {
108
+ return join(codexMarketplaceRoot(codexHome, marketplaceName), "plugins", pluginName);
83
109
  }
84
- function installedPluginPath(codexHome, pluginName) {
110
+ export function codexInstalledPluginPath(codexHome, pluginName) {
85
111
  return join(codexHome, "plugins", pluginName);
86
112
  }
87
- function cachePluginPath(codexHome, marketplaceName, pluginName, version) {
113
+ export function codexPluginCachePath(codexHome, marketplaceName, pluginName, version) {
88
114
  return join(codexHome, "plugins", "cache", marketplaceName, pluginName, version);
89
115
  }
116
+ export function codexPluginCacheRoot(codexHome, marketplaceName, pluginName) {
117
+ return join(codexHome, "plugins", "cache", marketplaceName, pluginName);
118
+ }
119
+ export function readCodexPluginManifest(pluginRoot) {
120
+ const manifestPath = join(pluginRoot, ".codex-plugin", "plugin.json");
121
+ return readJson(manifestPath, null);
122
+ }
123
+ export function claudeMarketplaceDir(claudeHome, marketplaceName = "enact-os-plugins") {
124
+ return join(claudeHome, "plugins", "marketplaces", marketplaceName);
125
+ }
126
+ export function claudeInstalledPluginPath(claudeHome, marketplaceName, pluginName) {
127
+ return join(claudeMarketplaceDir(claudeHome, marketplaceName), "plugins", pluginName);
128
+ }
129
+ export function claudeKnownMarketplacesPath(claudeHome) {
130
+ return join(claudeHome, "plugins", "known_marketplaces.json");
131
+ }
132
+ export function readClaudePluginManifest(pluginRoot) {
133
+ const manifestPath = join(pluginRoot, ".claude-plugin", "plugin.json");
134
+ return readJson(manifestPath, null);
135
+ }
90
136
  function marketplaceDisplayName(marketplaceName) {
91
137
  return marketplaceName
92
138
  .split("-")
@@ -104,15 +150,10 @@ function categoryFrom(manifest) {
104
150
  return "Developer Tools";
105
151
  }
106
152
  function manifestForCodexInstall(manifest) {
107
- const name = stringField(manifest, "name");
108
- if (name !== "enact-operator" && name !== "enact-extensions")
109
- return manifest;
110
- const clone = { ...manifest };
111
- delete clone.hooks;
112
- return clone;
153
+ return manifest;
113
154
  }
114
155
  function upsertMarketplaceEntry(codexHome, marketplaceName, manifest, pluginName) {
115
- const path = marketplacePath(codexHome, marketplaceName);
156
+ const path = codexMarketplacePath(codexHome, marketplaceName);
116
157
  const current = readJson(path, {
117
158
  name: marketplaceName,
118
159
  interface: { displayName: marketplaceDisplayName(marketplaceName) },
@@ -138,13 +179,14 @@ function upsertMarketplaceEntry(codexHome, marketplaceName, manifest, pluginName
138
179
  export function installCodexPluginBundle(options) {
139
180
  const codexHome = options.codexHome ?? defaultCodexHome();
140
181
  const marketplaceName = options.marketplaceName ?? "enact-os-plugins";
141
- const syncResults = options.sync !== false ? maybeSync(options.pluginRoot, "codex") : [];
142
- const codexManifest = readManifestFile(options.pluginRoot, "codex");
182
+ // Per-surface manifests are derived IN MEMORY; install never writes to source.
183
+ const codexManifest = deriveTargetManifest(options.pluginRoot, "codex");
143
184
  const validation = validateCodexManifest(codexManifest);
144
185
  if (!validation.ok) {
145
186
  throw new Error(`Invalid Codex plugin manifest:\n${validation.errors.join("\n")}`);
146
187
  }
147
- const componentIssues = checkPluginBundleComponentPaths(options.pluginRoot, ["codex"]);
188
+ const componentIssues = checkComponentPaths(options.pluginRoot, codexManifest)
189
+ .map((issue) => `codex ${issue}`);
148
190
  if (componentIssues.length > 0) {
149
191
  throw new Error(`Plugin component paths are not installable:\n${componentIssues.join("\n")}`);
150
192
  }
@@ -157,7 +199,7 @@ export function installCodexPluginBundle(options) {
157
199
  const pluginConfigKey = `${name}@${marketplaceName}`;
158
200
  const config = readCodexConfig(codexHome);
159
201
  ensureMarketplace(config, marketplaceName, {
160
- source: marketplaceRoot(codexHome, marketplaceName),
202
+ source: codexMarketplaceRoot(codexHome, marketplaceName),
161
203
  source_type: "local",
162
204
  });
163
205
  if (options.enable !== false) {
@@ -165,12 +207,12 @@ export function installCodexPluginBundle(options) {
165
207
  }
166
208
  const configPath = writeCodexConfig(codexHome, config);
167
209
  const mktPath = upsertMarketplaceEntry(codexHome, marketplaceName, installManifest, name);
168
- const mktPluginPath = marketplacePluginPath(codexHome, marketplaceName, name);
169
- const installedPath = installedPluginPath(codexHome, name);
170
- const cachePath = cachePluginPath(codexHome, marketplaceName, name, version);
171
- projectBundle(options.pluginRoot, mktPluginPath, installManifest, ".codex-plugin");
172
- projectBundle(options.pluginRoot, installedPath, installManifest, ".codex-plugin");
173
- projectBundle(options.pluginRoot, cachePath, installManifest, ".codex-plugin");
210
+ const mktPluginPath = codexMarketplacePluginPath(codexHome, marketplaceName, name);
211
+ const installedPath = codexInstalledPluginPath(codexHome, name);
212
+ const cachePath = codexPluginCachePath(codexHome, marketplaceName, name, version);
213
+ projectBundle(options.pluginRoot, mktPluginPath, "codex", installManifest, ".codex-plugin");
214
+ projectBundle(options.pluginRoot, installedPath, "codex", installManifest, ".codex-plugin");
215
+ projectBundle(options.pluginRoot, cachePath, "codex", installManifest, ".codex-plugin");
174
216
  return {
175
217
  name,
176
218
  version,
@@ -182,7 +224,7 @@ export function installCodexPluginBundle(options) {
182
224
  installedPluginPath: installedPath,
183
225
  cachePluginPath: cachePath,
184
226
  refreshedPaths: [mktPluginPath, installedPath, cachePath],
185
- syncResults,
227
+ syncResults: [],
186
228
  };
187
229
  }
188
230
  export function installPluginBundle(options) {
@@ -196,11 +238,88 @@ export function installPluginBundle(options) {
196
238
  }));
197
239
  return { codexHomes, results };
198
240
  }
241
+ export function installCodexPluginBundleWithProvision(options) {
242
+ const result = installCodexPluginBundle(options);
243
+ const provision = provisionMcp(options.pluginRoot, {
244
+ noProvision: options.noProvision,
245
+ exec: options.provisionExec,
246
+ });
247
+ return {
248
+ ...result,
249
+ provision,
250
+ };
251
+ }
252
+ export function installPluginBundleWithProvision(options) {
253
+ const result = installPluginBundle(options);
254
+ const provision = provisionMcp(options.pluginRoot, {
255
+ noProvision: options.noProvision,
256
+ exec: options.provisionExec,
257
+ });
258
+ return {
259
+ ...result,
260
+ provision,
261
+ };
262
+ }
263
+ export function codexPluginStatus(codexHome, pluginName, marketplaceName = "enact-os-plugins") {
264
+ const pluginConfigKey = `${pluginName}@${marketplaceName}`;
265
+ const installedPath = codexInstalledPluginPath(codexHome, pluginName);
266
+ const cacheRoot = codexPluginCacheRoot(codexHome, marketplaceName, pluginName);
267
+ const config = readCodexConfig(codexHome);
268
+ const cachedVersions = existsSync(cacheRoot)
269
+ ? readdirSync(cacheRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort()
270
+ : [];
271
+ const manifest = existsSync(installedPath) ? readCodexPluginManifest(installedPath) : null;
272
+ return {
273
+ name: pluginName,
274
+ enabled: isPluginEnabled(config, pluginConfigKey),
275
+ installed: existsSync(installedPath),
276
+ cachedVersions,
277
+ manifest,
278
+ pluginConfigKey,
279
+ marketplaceName,
280
+ installedPluginPath: installedPath,
281
+ marketplacePluginPath: codexMarketplacePluginPath(codexHome, marketplaceName, pluginName),
282
+ cacheRoot,
283
+ };
284
+ }
285
+ export function setInstalledCodexPluginEnabled(codexHome, pluginName, enabled, marketplaceName = "enact-os-plugins") {
286
+ const config = readCodexConfig(codexHome);
287
+ setPluginEnabled(config, `${pluginName}@${marketplaceName}`, enabled);
288
+ writeCodexConfig(codexHome, config);
289
+ return codexPluginStatus(codexHome, pluginName, marketplaceName);
290
+ }
291
+ export function runCodexPluginsDoctor(codexHome = defaultCodexHome(), marketplaceName = "enact-os-plugins") {
292
+ const config = readCodexConfig(codexHome);
293
+ const marketplace = readJson(codexMarketplacePath(codexHome, marketplaceName), {
294
+ name: marketplaceName,
295
+ interface: { displayName: marketplaceDisplayName(marketplaceName) },
296
+ plugins: [],
297
+ });
298
+ const installedPluginsDir = join(codexMarketplaceRoot(codexHome, marketplaceName), "plugins");
299
+ const installedPlugins = existsSync(installedPluginsDir)
300
+ ? readdirSync(installedPluginsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name)
301
+ : [];
302
+ const warnings = [];
303
+ for (const entry of marketplace.plugins) {
304
+ if (!installedPlugins.includes(entry.name) && isPluginEnabled(config, `${entry.name}@${marketplaceName}`)) {
305
+ warnings.push(`plugin ${entry.name} enabled in config but not installed locally`);
306
+ }
307
+ }
308
+ return {
309
+ configPlugins: Object.keys(config.plugins ?? {}).sort(),
310
+ marketplaceConfigured: Boolean(config.marketplaces?.[marketplaceName]),
311
+ marketplaceRoot: codexMarketplaceRoot(codexHome, marketplaceName),
312
+ marketplacePath: codexMarketplacePath(codexHome, marketplaceName),
313
+ marketplacePlugins: marketplace.plugins.map((plugin) => plugin.name).sort(),
314
+ installedPlugins: installedPlugins.sort(),
315
+ warnings,
316
+ };
317
+ }
199
318
  export function installClaudePluginBundle(options) {
200
319
  const claudeHome = options.claudeHome ?? defaultClaudeHome();
201
320
  const marketplaceName = options.marketplaceName ?? "enact-os-plugins";
202
- const syncResults = options.sync !== false ? maybeSync(options.pluginRoot, "claude") : [];
203
- const claudeManifest = readManifestFile(options.pluginRoot, "claude");
321
+ // Per-surface manifest derived IN MEMORY; install never writes to source.
322
+ const claudeManifest = deriveTargetManifest(options.pluginRoot, "claude");
204
323
  const validation = validateClaudeManifest(claudeManifest);
205
324
  if (!validation.ok) {
206
325
  throw new Error(`Invalid Claude plugin manifest:\n${validation.errors.join("\n")}`);
@@ -212,7 +331,7 @@ export function installClaudePluginBundle(options) {
212
331
  const version = stringField(claudeManifest, "version") ?? "0.0.0";
213
332
  const marketplaceDir = join(claudeHome, "plugins", "marketplaces", marketplaceName);
214
333
  const installedPath = join(marketplaceDir, "plugins", name);
215
- projectBundle(options.pluginRoot, installedPath, claudeManifest, ".claude-plugin");
334
+ projectBundle(options.pluginRoot, installedPath, "claude", claudeManifest, ".claude-plugin");
216
335
  registerLocalMarketplace(claudeHome, marketplaceName, marketplaceDir);
217
336
  return {
218
337
  name,
@@ -221,13 +340,66 @@ export function installClaudePluginBundle(options) {
221
340
  marketplaceDir,
222
341
  installedPluginPath: installedPath,
223
342
  refreshedPaths: [installedPath],
224
- syncResults,
343
+ syncResults: [],
344
+ };
345
+ }
346
+ export function installClaudePluginBundleWithProvision(options) {
347
+ const result = installClaudePluginBundle(options);
348
+ const provision = provisionMcp(options.pluginRoot, {
349
+ noProvision: options.noProvision,
350
+ exec: options.provisionExec,
351
+ });
352
+ return {
353
+ ...result,
354
+ provision,
355
+ };
356
+ }
357
+ export function claudePluginStatus(claudeHome, pluginName, marketplaceName = "enact-os-plugins") {
358
+ const marketplaceDir = claudeMarketplaceDir(claudeHome, marketplaceName);
359
+ const knownMarketplaces = readKnownMarketplaces(claudeHome);
360
+ const installedPath = claudeInstalledPluginPath(claudeHome, marketplaceName, pluginName);
361
+ const manifest = existsSync(installedPath) ? readClaudePluginManifest(installedPath) : null;
362
+ return {
363
+ name: pluginName,
364
+ installed: existsSync(installedPath),
365
+ manifest,
366
+ marketplaceName,
367
+ marketplaceDir,
368
+ installedPluginPath: installedPath,
369
+ knownMarketplaceRegistered: Boolean(knownMarketplaces[marketplaceName]),
370
+ knownMarketplacesPath: claudeKnownMarketplacesPath(claudeHome),
371
+ };
372
+ }
373
+ export function runClaudePluginsDoctor(claudeHome = defaultClaudeHome(), marketplaceName = "enact-os-plugins") {
374
+ const marketplaceDir = claudeMarketplaceDir(claudeHome, marketplaceName);
375
+ const knownMarketplaces = readKnownMarketplaces(claudeHome);
376
+ const installedPluginsDir = join(marketplaceDir, "plugins");
377
+ const installedPlugins = existsSync(installedPluginsDir)
378
+ ? readdirSync(installedPluginsDir, { withFileTypes: true })
379
+ .filter((entry) => entry.isDirectory())
380
+ .map((entry) => entry.name)
381
+ : [];
382
+ const warnings = [];
383
+ if (!knownMarketplaces[marketplaceName] && installedPlugins.length > 0) {
384
+ warnings.push(`marketplace ${marketplaceName} has installed plugins but is not registered in known_marketplaces.json`);
385
+ }
386
+ if (knownMarketplaces[marketplaceName] && !existsSync(marketplaceDir)) {
387
+ warnings.push(`marketplace ${marketplaceName} registered in known_marketplaces.json but install directory is missing`);
388
+ }
389
+ return {
390
+ marketplaceName,
391
+ marketplaceDir,
392
+ knownMarketplacesPath: claudeKnownMarketplacesPath(claudeHome),
393
+ marketplaceRegistered: Boolean(knownMarketplaces[marketplaceName]),
394
+ marketplacePlugins: installedPlugins.slice().sort(),
395
+ installedPlugins: installedPlugins.sort(),
396
+ warnings,
225
397
  };
226
398
  }
227
399
  export function installCursorPluginBundle(options) {
228
400
  const cursorHome = options.cursorHome ?? defaultCursorHome();
229
- const syncResults = options.sync !== false ? maybeSync(options.pluginRoot, "cursor") : [];
230
- const cursorManifest = readManifestFile(options.pluginRoot, "cursor");
401
+ // Per-surface manifest derived IN MEMORY; install never writes to source.
402
+ const cursorManifest = deriveTargetManifest(options.pluginRoot, "cursor");
231
403
  const validation = validateCursorManifest(cursorManifest);
232
404
  if (!validation.ok) {
233
405
  throw new Error(`Invalid Cursor plugin manifest:\n${validation.errors.join("\n")}`);
@@ -238,13 +410,194 @@ export function installCursorPluginBundle(options) {
238
410
  }
239
411
  const version = stringField(cursorManifest, "version") ?? "0.0.0";
240
412
  const installedPath = join(cursorHome, "plugins", "local", name);
241
- projectBundle(options.pluginRoot, installedPath, cursorManifest, ".cursor-plugin");
413
+ projectBundle(options.pluginRoot, installedPath, "cursor", cursorManifest, ".cursor-plugin");
242
414
  return {
243
415
  name,
244
416
  version,
245
417
  installedPluginPath: installedPath,
246
418
  refreshedPaths: [installedPath],
247
- syncResults,
419
+ syncResults: [],
420
+ };
421
+ }
422
+ /**
423
+ * Guard: ensure the target path stays within the expected base directory
424
+ * to prevent accidental deletion outside a known home.
425
+ */
426
+ function assertWithinBase(base, target) {
427
+ const resolvedBase = resolve(base);
428
+ const resolvedTarget = resolve(target);
429
+ if (!resolvedTarget.startsWith(resolvedBase + "/") && resolvedTarget !== resolvedBase) {
430
+ throw new Error(`Safety check failed: resolved path ${resolvedTarget} is not within base ${resolvedBase}`);
431
+ }
432
+ }
433
+ /**
434
+ * Remove a marketplace.json entry for `pluginName`.
435
+ * If the file doesn't exist or the entry isn't there, this is a no-op.
436
+ * The marketplace registration entry (top-level name/interface fields) is preserved.
437
+ * Other plugin entries are preserved.
438
+ */
439
+ function removeMarketplaceEntry(codexHome, marketplaceName, pluginName) {
440
+ const path = codexMarketplacePath(codexHome, marketplaceName);
441
+ if (!existsSync(path))
442
+ return;
443
+ const current = readJson(path, { name: marketplaceName, plugins: [] });
444
+ const filtered = (current.plugins ?? []).filter((p) => p.name !== pluginName);
445
+ writeJson(path, {
446
+ name: current.name || marketplaceName,
447
+ ...(current.interface ? { interface: current.interface } : {}),
448
+ plugins: filtered,
449
+ });
450
+ }
451
+ /**
452
+ * Fully uninstall a Codex/Enact plugin from the given home, reversing installCodexPluginBundle:
453
+ * - removes <home>/plugins/<name>/ (installed dir)
454
+ * - removes <home>/marketplaces/<marketplace>/plugins/<name>/ (marketplace plugin dir)
455
+ * - removes <home>/plugins/cache/<marketplace>/<name>/ (cache dir)
456
+ * - removes the plugin's entry from marketplace.json (preserves other entries + marketplace reg)
457
+ * - removes [plugins."<name>@<marketplace>"] from config.toml (preserves [marketplaces.*] etc.)
458
+ */
459
+ export function uninstallCodexPluginBundle(name, options = {}) {
460
+ const codexHome = options.codexHome ?? defaultCodexHome();
461
+ const mktName = options.marketplaceName ?? "enact-os-plugins";
462
+ const pluginConfigKey = `${name}@${mktName}`;
463
+ const installedPath = codexInstalledPluginPath(codexHome, name);
464
+ const mktPluginPath = codexMarketplacePluginPath(codexHome, mktName, name);
465
+ // Cache dir is versioned; remove the entire <cache>/<marketplace>/<name>/ subtree
466
+ const cacheDirForPlugin = join(codexHome, "plugins", "cache", mktName, name);
467
+ const noop = !existsSync(installedPath) &&
468
+ !existsSync(mktPluginPath) &&
469
+ !existsSync(cacheDirForPlugin);
470
+ const removedPaths = [];
471
+ if (existsSync(installedPath)) {
472
+ assertWithinBase(join(codexHome, "plugins"), installedPath);
473
+ rmSync(installedPath, { recursive: true, force: true });
474
+ removedPaths.push(installedPath);
475
+ }
476
+ if (existsSync(mktPluginPath)) {
477
+ assertWithinBase(join(codexHome, "marketplaces"), mktPluginPath);
478
+ rmSync(mktPluginPath, { recursive: true, force: true });
479
+ removedPaths.push(mktPluginPath);
480
+ }
481
+ if (existsSync(cacheDirForPlugin)) {
482
+ assertWithinBase(join(codexHome, "plugins", "cache"), cacheDirForPlugin);
483
+ rmSync(cacheDirForPlugin, { recursive: true, force: true });
484
+ removedPaths.push(cacheDirForPlugin);
485
+ }
486
+ // Remove plugin entry from marketplace.json (leave file + other entries intact)
487
+ removeMarketplaceEntry(codexHome, mktName, name);
488
+ // Remove [plugins."<name>@<marketplace>"] from config.toml (leave marketplaces.* intact)
489
+ const config = readCodexConfig(codexHome);
490
+ removePluginConfig(config, pluginConfigKey);
491
+ if (existsSync(join(codexHome, "config.toml"))) {
492
+ writeCodexConfig(codexHome, config);
493
+ }
494
+ return { name, marketplaceName: mktName, removedPaths, noop };
495
+ }
496
+ /**
497
+ * Fully uninstall a Claude plugin, reversing installClaudePluginBundle:
498
+ * - removes <claudeHome>/plugins/marketplaces/<marketplace>/plugins/<name>/
499
+ * Note: the marketplace registration in known_marketplaces.json is LEFT intact
500
+ * (the marketplace itself persists even after its last plugin is removed).
501
+ */
502
+ export function uninstallClaudePluginBundle(name, options = {}) {
503
+ const claudeHome = options.claudeHome ?? defaultClaudeHome();
504
+ const mktName = options.marketplaceName ?? "enact-os-plugins";
505
+ const marketplaceDir = join(claudeHome, "plugins", "marketplaces", mktName);
506
+ const installedPath = join(marketplaceDir, "plugins", name);
507
+ const noop = !existsSync(installedPath);
508
+ const removedPaths = [];
509
+ if (existsSync(installedPath)) {
510
+ assertWithinBase(marketplaceDir, installedPath);
511
+ rmSync(installedPath, { recursive: true, force: true });
512
+ removedPaths.push(installedPath);
513
+ }
514
+ return { name, removedPaths, noop };
515
+ }
516
+ /**
517
+ * Fully uninstall a Cursor plugin, reversing installCursorPluginBundle:
518
+ * - removes <cursorHome>/plugins/local/<name>/
519
+ */
520
+ export function uninstallCursorPluginBundle(name, options = {}) {
521
+ const cursorHome = options.cursorHome ?? defaultCursorHome();
522
+ const installedPath = join(cursorHome, "plugins", "local", name);
523
+ const noop = !existsSync(installedPath);
524
+ const removedPaths = [];
525
+ if (existsSync(installedPath)) {
526
+ assertWithinBase(join(cursorHome, "plugins", "local"), installedPath);
527
+ rmSync(installedPath, { recursive: true, force: true });
528
+ removedPaths.push(installedPath);
529
+ }
530
+ return { name, removedPaths, noop };
531
+ }
532
+ /**
533
+ * Default global shared home base. The convention is that skills live at
534
+ * `~/.agents/skills/<plugin>/`, so this returns `homedir()` and the install
535
+ * function appends `.agents/skills/<name>`.
536
+ *
537
+ * Pass an explicit `sharedHome` to override (useful in tests or local scope).
538
+ */
539
+ export function defaultSharedHome() {
540
+ return homedir();
541
+ }
542
+ /**
543
+ * Install the bundle's skills tree into <sharedHome>/.agents/skills/<plugin>/.
544
+ * Does NOT write a marketplace entry, config.toml, or any platform-specific
545
+ * manifest — this is a pure host-neutral skills drop that any host reading
546
+ * `.agents/skills/` (Codex, Cursor, Gemini, etc.) can pick up.
547
+ */
548
+ export function installSharedPluginBundle(options) {
549
+ const sharedHome = options.sharedHome ?? defaultSharedHome();
550
+ // Read the enact manifest for name/version; fall back to codex if no enact manifest.
551
+ const enactManifestPath = join(options.pluginRoot, ".agents", "plugin.json");
552
+ let manifest;
553
+ if (existsSync(enactManifestPath)) {
554
+ manifest = readManifestFile(options.pluginRoot, "enact");
555
+ }
556
+ else {
557
+ manifest = readManifestFile(options.pluginRoot, "codex");
558
+ }
559
+ const name = stringField(manifest, "name");
560
+ if (!name) {
561
+ throw new Error("Plugin manifest is missing name");
562
+ }
563
+ const version = stringField(manifest, "version") ?? "0.0.0";
564
+ // Resolve the skills source path from the manifest
565
+ const rawSkills = stringField(manifest, "skills");
566
+ if (!rawSkills) {
567
+ throw new Error(`Plugin manifest for '${name}' has no 'skills' field — nothing to install for shared target`);
568
+ }
569
+ const cleanedSkills = rawSkills.replace(/^\.\//, "").replace(/\/+$/, "");
570
+ const skillsSource = resolve(options.pluginRoot, cleanedSkills);
571
+ // shared is a skills-only surface: a missing skills source is misuse.
572
+ // Fail loudly before touching the target rather than producing an empty dir.
573
+ if (!existsSync(skillsSource)) {
574
+ throw new Error(`Cannot install shared bundle "${name}": skills source not found at ${skillsSource}`);
575
+ }
576
+ // Target: <sharedHome>/.agents/skills/<name>/
577
+ const installedSkillsPath = join(sharedHome, ".agents", "skills", name);
578
+ rmSync(installedSkillsPath, { recursive: true, force: true });
579
+ mkdirSync(installedSkillsPath, { recursive: true });
580
+ cpSync(skillsSource, installedSkillsPath, { recursive: true, force: true });
581
+ return {
582
+ name,
583
+ version,
584
+ installedSkillsPath,
248
585
  };
249
586
  }
587
+ /**
588
+ * Uninstall a shared plugin by removing its skills directory.
589
+ * Reverses installSharedPluginBundle.
590
+ */
591
+ export function uninstallSharedPluginBundle(name, options = {}) {
592
+ const sharedHome = options.sharedHome ?? defaultSharedHome();
593
+ const installedSkillsPath = join(sharedHome, ".agents", "skills", name);
594
+ const noop = !existsSync(installedSkillsPath);
595
+ const removedPaths = [];
596
+ if (existsSync(installedSkillsPath)) {
597
+ assertWithinBase(join(sharedHome, ".agents", "skills"), installedSkillsPath);
598
+ rmSync(installedSkillsPath, { recursive: true, force: true });
599
+ removedPaths.push(installedSkillsPath);
600
+ }
601
+ return { name, installedSkillsPath, removedPaths, noop };
602
+ }
250
603
  //# sourceMappingURL=install.js.map