@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.
- package/README.md +96 -21
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +171 -1
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +402 -49
- package/dist/install.js.map +1 -1
- package/dist/internal/codex.d.ts.map +1 -1
- package/dist/internal/codex.js +7 -1
- package/dist/internal/codex.js.map +1 -1
- package/dist/internal/platform.d.ts +8 -0
- package/dist/internal/platform.d.ts.map +1 -1
- package/dist/internal/platform.js +46 -2
- package/dist/internal/platform.js.map +1 -1
- package/dist/provision.d.ts +30 -0
- package/dist/provision.d.ts.map +1 -0
- package/dist/provision.js +202 -0
- package/dist/provision.js.map +1 -0
- package/dist/validate/index.d.ts +44 -0
- package/dist/validate/index.d.ts.map +1 -1
- package/dist/validate/index.js +157 -0
- package/dist/validate/index.js.map +1 -1
- package/extensions/cmux/.agents/plugin.json +37 -0
- package/extensions/cmux/skills/cmux/SKILL.md +82 -0
- package/extensions/cmux/skills/cmux/agents/openai.yaml +4 -0
- package/extensions/cmux/skills/cmux/references/handles-and-identify.md +35 -0
- package/extensions/cmux/skills/cmux/references/panes-surfaces.md +37 -0
- package/extensions/cmux/skills/cmux/references/trigger-flash-and-health.md +23 -0
- package/extensions/cmux/skills/cmux/references/windows-workspaces.md +31 -0
- package/extensions/cmux/skills/cmux-vm-monitor/SKILL.md +122 -0
- package/extensions/cmux/skills/cmux-vm-monitor/agents/openai.yaml +4 -0
- package/extensions/cmux/skills/cmux-vm-monitor/references/cmux-commands.md +66 -0
- package/extensions/cmux/skills/cmux-vm-monitor/scripts/codex_vm_monitor.sh +45 -0
- package/extensions/cmux/skills/cmux-workspace/SKILL.md +93 -0
- package/extensions/dev-state/.agents/plugin.json +35 -0
- package/extensions/dev-state/skills/dev-state-plan-graduation/SKILL.md +194 -0
- package/extensions/dev-state/skills/dev-state-plan-graduation/agents/openai.yaml +4 -0
- package/extensions/dev-state/skills/dev-state-plan-graduation/references/reference.md +130 -0
- package/extensions/devops/.agents/plugin.json +36 -0
- package/extensions/devops/skills/azure-devops-cli/SKILL.md +431 -0
- package/extensions/devops/skills/azure-devops-cli/agents/openai.yaml +4 -0
- package/extensions/devops/skills/ci-pipeline-strategy/SKILL.md +217 -0
- package/extensions/devops/skills/ci-pipeline-strategy/agents/openai.yaml +4 -0
- package/extensions/enact-context/.agents/plugin.json +40 -0
- package/extensions/enact-context/.mcp.json +8 -0
- package/extensions/enact-context/README.md +25 -0
- package/extensions/enact-context/assets/icon.png +0 -0
- package/extensions/enact-context/assets/logo.png +0 -0
- package/extensions/enact-context/hooks/hooks.json +115 -0
- package/extensions/enact-context/skills/enact-context/SKILL.md +149 -0
- package/extensions/enact-context/skills/enact-context/scripts/install.sh +69 -0
- package/extensions/enact-factory/.agents/plugin.json +42 -0
- package/extensions/enact-factory/.mcp.json +8 -0
- package/extensions/enact-factory/assets/icon.png +0 -0
- package/extensions/enact-factory/assets/logo.png +0 -0
- package/extensions/enact-factory/hooks/user-prompt-submit.mjs +67 -0
- package/extensions/enact-factory/skills/testing-strategy/SKILL.md +167 -0
- package/extensions/enact-factory/skills/workitem-triage/SKILL.md +22 -0
- package/extensions/enact-operator/.agents/plugin.json +57 -0
- package/extensions/enact-operator/.app.json +3 -0
- package/extensions/enact-operator/.mcp.json +10 -0
- package/extensions/enact-operator/_taxonomy.md +86 -0
- package/extensions/enact-operator/agents/README.md +5 -0
- package/extensions/enact-operator/agents/architect.toml +25 -0
- package/extensions/enact-operator/agents/code-reviewer.toml +24 -0
- package/extensions/enact-operator/agents/critic.toml +30 -0
- package/extensions/enact-operator/agents/executor.toml +24 -0
- package/extensions/enact-operator/agents/explore.toml +23 -0
- package/extensions/enact-operator/agents/planner.toml +24 -0
- package/extensions/enact-operator/agents/verifier.toml +24 -0
- package/extensions/enact-operator/assets/icon.png +0 -0
- package/extensions/enact-operator/assets/logo.png +0 -0
- package/extensions/enact-operator/commands/doctor.md +39 -0
- package/extensions/enact-operator/commands/setup.md +51 -0
- package/extensions/enact-operator/hooks/hooks.json +146 -0
- package/extensions/enact-operator/skills/_variants.md +44 -0
- package/extensions/enact-operator/skills/ai-slop-cleaner/SKILL.md +50 -0
- package/extensions/enact-operator/skills/analyze/SKILL.md +91 -0
- package/extensions/enact-operator/skills/ask/SKILL.md +47 -0
- package/extensions/enact-operator/skills/autopilot/SKILL.md +170 -0
- package/extensions/enact-operator/skills/autoresearch-goal/SKILL.md +79 -0
- package/extensions/enact-operator/skills/cancel/SKILL.md +99 -0
- package/extensions/enact-operator/skills/configure-notifications/SKILL.md +77 -0
- package/extensions/enact-operator/skills/deep-interview/SKILL.md +80 -0
- package/extensions/enact-operator/skills/doctor/SKILL.md +48 -0
- package/extensions/enact-operator/skills/hud/SKILL.md +49 -0
- package/extensions/enact-operator/skills/hyperplan/SKILL.md +47 -0
- package/extensions/enact-operator/skills/plan/SKILL.md +78 -0
- package/extensions/enact-operator/skills/ralph/SKILL.md +201 -0
- package/extensions/enact-operator/skills/ralph/gemini.md +18 -0
- package/extensions/enact-operator/skills/ralplan/SKILL.md +151 -0
- package/extensions/enact-operator/skills/remove-deadcode/SKILL.md +45 -0
- package/extensions/enact-operator/skills/research/SKILL.md +74 -0
- package/extensions/enact-operator/skills/review/SKILL.md +58 -0
- package/extensions/enact-operator/skills/security-research/SKILL.md +54 -0
- package/extensions/enact-operator/skills/setup/SKILL.md +91 -0
- package/extensions/enact-operator/skills/setup/scripts/install.sh +50 -0
- package/extensions/enact-operator/skills/skill/SKILL.md +82 -0
- package/extensions/enact-operator/skills/tdd/SKILL.md +59 -0
- package/extensions/enact-operator/skills/team/SKILL.md +199 -0
- package/extensions/enact-operator/skills/trace/SKILL.md +41 -0
- package/extensions/enact-operator/skills/ultragoal/SKILL.md +99 -0
- package/extensions/enact-operator/skills/ultraqa/SKILL.md +113 -0
- package/extensions/enact-operator/skills/ultrawork/SKILL.md +145 -0
- package/extensions/enact-operator/skills/ultrawork/planner.md +28 -0
- package/extensions/enact-operator/skills/wiki/SKILL.md +41 -0
- package/extensions/enact-operator/skills/work-with-workitem/SKILL.md +51 -0
- package/extensions/enact-wiki/.agents/plugin.json +42 -0
- package/extensions/enact-wiki/.mcp.json +15 -0
- package/extensions/enact-wiki/README.md +44 -0
- package/extensions/enact-wiki/assets/icon.png +0 -0
- package/extensions/enact-wiki/assets/logo.png +0 -0
- package/extensions/enact-wiki/skills/document-parser/SKILL.md +17 -0
- package/extensions/enact-wiki/skills/document-parser/scripts/parse.sh +60 -0
- package/extensions/enact-wiki/skills/document-parser/skill.json +9 -0
- package/extensions/enact-wiki/skills/enact-wiki/SKILL.md +30 -0
- package/extensions/enact-wiki/skills/enact-wiki/references/ingest.md +62 -0
- package/extensions/enact-wiki/skills/enact-wiki/references/manage.md +34 -0
- package/extensions/enact-wiki/skills/enact-wiki/references/query.md +59 -0
- package/extensions/enact-wiki/skills/search-lab/SKILL.md +57 -0
- package/extensions/enact-wiki/skills/search-lab/scripts/analyze.ts +23 -0
- package/{plugins/net-revenue-management/.codex-plugin → extensions/net-revenue-management/.agents}/plugin.json +10 -6
- package/extensions/plugin-dev/.agents/plugin.json +42 -0
- package/extensions/plugin-dev/.mcp.json +3 -0
- package/extensions/plugin-dev/agents/agent-creator.md +199 -0
- package/extensions/plugin-dev/agents/plugin-validator.md +91 -0
- package/extensions/plugin-dev/agents/skill-reviewer.md +212 -0
- package/extensions/plugin-dev/commands/_archive/create-marketplace.md +427 -0
- package/extensions/plugin-dev/commands/_archive/plugin-dev-guide.md +12 -0
- package/extensions/plugin-dev/commands/create-plugin.md +498 -0
- package/extensions/plugin-dev/commands/start.md +81 -0
- package/extensions/plugin-dev/hooks/hooks.json +3 -0
- package/extensions/plugin-dev/skills/agent-development/SKILL.md +641 -0
- package/extensions/plugin-dev/skills/agent-development/examples/agent-creation-prompt.md +250 -0
- package/extensions/plugin-dev/skills/agent-development/examples/complete-agent-examples.md +461 -0
- package/extensions/plugin-dev/skills/agent-development/references/advanced-agent-fields.md +246 -0
- package/extensions/plugin-dev/skills/agent-development/references/agent-creation-system-prompt.md +216 -0
- package/extensions/plugin-dev/skills/agent-development/references/permission-modes-rules.md +226 -0
- package/extensions/plugin-dev/skills/agent-development/references/system-prompt-design.md +464 -0
- package/extensions/plugin-dev/skills/agent-development/references/triggering-examples.md +474 -0
- package/extensions/plugin-dev/skills/agent-development/scripts/create-agent-skeleton.sh +176 -0
- package/extensions/plugin-dev/skills/agent-development/scripts/test-agent-trigger.sh +227 -0
- package/extensions/plugin-dev/skills/agent-development/scripts/validate-agent.sh +227 -0
- package/extensions/plugin-dev/skills/command-development/SKILL.md +763 -0
- package/extensions/plugin-dev/skills/command-development/examples/plugin-commands.md +612 -0
- package/extensions/plugin-dev/skills/command-development/examples/simple-commands.md +527 -0
- package/extensions/plugin-dev/skills/command-development/references/advanced-workflows.md +762 -0
- package/extensions/plugin-dev/skills/command-development/references/documentation-patterns.md +769 -0
- package/extensions/plugin-dev/skills/command-development/references/frontmatter-reference.md +508 -0
- package/extensions/plugin-dev/skills/command-development/references/interactive-commands.md +966 -0
- package/extensions/plugin-dev/skills/command-development/references/marketplace-considerations.md +943 -0
- package/extensions/plugin-dev/skills/command-development/references/plugin-features-reference.md +637 -0
- package/extensions/plugin-dev/skills/command-development/references/plugin-integration.md +191 -0
- package/extensions/plugin-dev/skills/command-development/references/skill-tool.md +447 -0
- package/extensions/plugin-dev/skills/command-development/references/testing-strategies.md +723 -0
- package/extensions/plugin-dev/skills/command-development/scripts/check-frontmatter.sh +234 -0
- package/extensions/plugin-dev/skills/command-development/scripts/validate-command.sh +160 -0
- package/extensions/plugin-dev/skills/hook-development/SKILL.md +861 -0
- package/extensions/plugin-dev/skills/hook-development/examples/load-context.sh +55 -0
- package/extensions/plugin-dev/skills/hook-development/examples/validate-bash.sh +57 -0
- package/extensions/plugin-dev/skills/hook-development/examples/validate-write.sh +48 -0
- package/extensions/plugin-dev/skills/hook-development/references/advanced.md +871 -0
- package/extensions/plugin-dev/skills/hook-development/references/hook-input-schemas.md +145 -0
- package/extensions/plugin-dev/skills/hook-development/references/migration.md +392 -0
- package/extensions/plugin-dev/skills/hook-development/references/patterns.md +430 -0
- package/extensions/plugin-dev/skills/hook-development/scripts/README.md +181 -0
- package/extensions/plugin-dev/skills/hook-development/scripts/hook-linter.sh +153 -0
- package/extensions/plugin-dev/skills/hook-development/scripts/test-hook.sh +276 -0
- package/extensions/plugin-dev/skills/hook-development/scripts/validate-hook-schema.sh +159 -0
- package/extensions/plugin-dev/skills/mcp-integration/SKILL.md +775 -0
- package/extensions/plugin-dev/skills/mcp-integration/examples/http-server.json +20 -0
- package/extensions/plugin-dev/skills/mcp-integration/examples/sse-server.json +19 -0
- package/extensions/plugin-dev/skills/mcp-integration/examples/stdio-server.json +38 -0
- package/extensions/plugin-dev/skills/mcp-integration/examples/ws-server.json +26 -0
- package/extensions/plugin-dev/skills/mcp-integration/references/authentication.md +601 -0
- package/extensions/plugin-dev/skills/mcp-integration/references/server-discovery.md +190 -0
- package/extensions/plugin-dev/skills/mcp-integration/references/server-types.md +572 -0
- package/extensions/plugin-dev/skills/mcp-integration/references/tool-usage.md +623 -0
- package/extensions/plugin-dev/skills/plugin-dev-guide/SKILL.md +222 -0
- package/extensions/plugin-dev/skills/plugin-structure/SKILL.md +705 -0
- package/extensions/plugin-dev/skills/plugin-structure/examples/advanced-plugin.md +774 -0
- package/extensions/plugin-dev/skills/plugin-structure/examples/minimal-plugin.md +83 -0
- package/extensions/plugin-dev/skills/plugin-structure/examples/standard-plugin.md +611 -0
- package/extensions/plugin-dev/skills/plugin-structure/references/advanced-topics.md +289 -0
- package/extensions/plugin-dev/skills/plugin-structure/references/component-patterns.md +592 -0
- package/extensions/plugin-dev/skills/plugin-structure/references/github-actions.md +233 -0
- package/extensions/plugin-dev/skills/plugin-structure/references/headless-ci-mode.md +193 -0
- package/extensions/plugin-dev/skills/plugin-structure/references/manifest-reference.md +625 -0
- package/extensions/plugin-dev/skills/plugin-structure/references/output-styles.md +116 -0
- package/extensions/plugin-dev/skills/skill-development/SKILL.md +564 -0
- package/extensions/plugin-dev/skills/skill-development/examples/complete-skill.md +465 -0
- package/extensions/plugin-dev/skills/skill-development/examples/frontmatter-templates.md +167 -0
- package/extensions/plugin-dev/skills/skill-development/examples/minimal-skill.md +111 -0
- package/extensions/plugin-dev/skills/skill-development/references/advanced-frontmatter.md +225 -0
- package/extensions/plugin-dev/skills/skill-development/references/commands-vs-skills.md +39 -0
- package/extensions/plugin-dev/skills/skill-development/references/skill-creation-workflow.md +379 -0
- package/extensions/plugin-dev/skills/skill-development/references/skill-creator-original.md +210 -0
- package/package.json +8 -11
- package/scripts/enact-extensions.mjs +823 -21
- package/scripts/hooks/session-start-drift-check.mjs +58 -0
- package/scripts/lib/build-index.mjs +50 -0
- package/scripts/lib/bundle-hash.mjs +137 -0
- package/scripts/lib/hooks.mjs +741 -0
- package/scripts/lib/ledger.mjs +163 -0
- package/scripts/lib/list-bundles.mjs +70 -0
- package/scripts/lib/outdated.mjs +144 -0
- package/scripts/lib/provision-mcp.mjs +16 -0
- package/scripts/lib/resolve-bundle.mjs +121 -0
- package/scripts/lib/run-install.mjs +402 -38
- package/scripts/lib/run-prune.mjs +73 -0
- package/scripts/lib/run-sync.mjs +9 -1
- package/scripts/lib/run-uninstall.mjs +244 -0
- package/scripts/lib/run-update.mjs +152 -0
- package/scripts/lib/run-validate.mjs +21 -18
- package/scripts/lib/serve.mjs +472 -0
- package/scripts/postinstall.mjs +63 -0
- package/scripts/setup-enact-context.sh +2 -2
- package/scripts/version-bump.sh +463 -0
- package/spec/codex.json +1 -11
- package/spec/index.json +59 -0
- package/web/assets/README.md +111 -0
- package/web/assets/logo-full.png +0 -0
- package/web/assets/logo-slim.png +0 -0
- package/web/assets/tokens/base.css +45 -0
- package/web/assets/tokens/colors.css +248 -0
- package/web/assets/tokens/effects.css +24 -0
- package/web/assets/tokens/fonts.css +8 -0
- package/web/assets/tokens/index.css +18 -0
- package/web/assets/tokens/spacing.css +50 -0
- package/web/index.html +1188 -0
- package/.agents/plugins/marketplace.json +0 -20
- package/catalog/enact-context.json +0 -9
- package/catalog/enact-factory.json +0 -7
- package/catalog/enact-operator.json +0 -7
- package/catalog/enact-wiki.json +0 -7
- package/catalog/net-revenue-management.json +0 -8
- package/scripts/rename-supervisor-to-operator.pl +0 -66
- package/scripts/sync-manifests.mjs +0 -23
- package/scripts/validate-catalog.mjs +0 -37
- package/scripts/validate-plugin.mjs +0 -10
- /package/{plugins → extensions}/net-revenue-management/.mcp.json +0 -0
- /package/{plugins → extensions}/net-revenue-management/skills/net-revenue-risks/SKILL.md +0 -0
- /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 {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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 = [
|
|
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
|
-
|
|
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 (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
101
|
+
export function codexMarketplaceRoot(codexHome, marketplaceName = "enact-os-plugins") {
|
|
76
102
|
return join(codexHome, "marketplaces", marketplaceName);
|
|
77
103
|
}
|
|
78
|
-
function
|
|
79
|
-
return join(
|
|
104
|
+
export function codexMarketplacePath(codexHome, marketplaceName = "enact-os-plugins") {
|
|
105
|
+
return join(codexMarketplaceRoot(codexHome, marketplaceName), ".agents", "plugins", "marketplace.json");
|
|
80
106
|
}
|
|
81
|
-
function
|
|
82
|
-
return join(
|
|
107
|
+
export function codexMarketplacePluginPath(codexHome, marketplaceName, pluginName) {
|
|
108
|
+
return join(codexMarketplaceRoot(codexHome, marketplaceName), "plugins", pluginName);
|
|
83
109
|
}
|
|
84
|
-
function
|
|
110
|
+
export function codexInstalledPluginPath(codexHome, pluginName) {
|
|
85
111
|
return join(codexHome, "plugins", pluginName);
|
|
86
112
|
}
|
|
87
|
-
function
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
142
|
-
const codexManifest =
|
|
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 =
|
|
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:
|
|
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 =
|
|
169
|
-
const installedPath =
|
|
170
|
-
const cachePath =
|
|
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
|
-
|
|
203
|
-
const claudeManifest =
|
|
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
|
-
|
|
230
|
-
const cursorManifest =
|
|
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
|