plugins 1.2.4 → 1.2.7
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/dist/index.js +337 -36
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { parseArgs } from "util";
|
|
|
5
5
|
import { resolve, join as join4 } from "path";
|
|
6
6
|
import { execSync as execSync3 } from "child_process";
|
|
7
7
|
import { existsSync as existsSync3, rmSync, mkdirSync } from "fs";
|
|
8
|
+
import { homedir as homedir3 } from "os";
|
|
8
9
|
import { createInterface } from "readline";
|
|
9
10
|
|
|
10
11
|
// lib/discover.ts
|
|
@@ -16,7 +17,8 @@ async function discover(repoPath) {
|
|
|
16
17
|
join(repoPath, "marketplace.json"),
|
|
17
18
|
join(repoPath, ".plugin", "marketplace.json"),
|
|
18
19
|
join(repoPath, ".claude-plugin", "marketplace.json"),
|
|
19
|
-
join(repoPath, ".cursor-plugin", "marketplace.json")
|
|
20
|
+
join(repoPath, ".cursor-plugin", "marketplace.json"),
|
|
21
|
+
join(repoPath, ".codex-plugin", "marketplace.json")
|
|
20
22
|
];
|
|
21
23
|
for (const mp of marketplacePaths) {
|
|
22
24
|
if (await fileExists(mp)) {
|
|
@@ -86,7 +88,7 @@ async function discoverFromMarketplace(repoPath, marketplace) {
|
|
|
86
88
|
skills = await discoverSkills(sourcePath);
|
|
87
89
|
}
|
|
88
90
|
let manifest = null;
|
|
89
|
-
for (const manifestDir of [".plugin", ".claude-plugin", ".cursor-plugin"]) {
|
|
91
|
+
for (const manifestDir of [".plugin", ".claude-plugin", ".cursor-plugin", ".codex-plugin"]) {
|
|
90
92
|
const manifestPath = join(sourcePath, manifestDir, "plugin.json");
|
|
91
93
|
if (await fileExists(manifestPath)) {
|
|
92
94
|
manifest = await readJson(manifestPath);
|
|
@@ -127,6 +129,7 @@ async function isPluginDir(dirPath) {
|
|
|
127
129
|
join(dirPath, ".plugin", "plugin.json"),
|
|
128
130
|
join(dirPath, ".claude-plugin", "plugin.json"),
|
|
129
131
|
join(dirPath, ".cursor-plugin", "plugin.json"),
|
|
132
|
+
join(dirPath, ".codex-plugin", "plugin.json"),
|
|
130
133
|
join(dirPath, "skills"),
|
|
131
134
|
join(dirPath, "commands"),
|
|
132
135
|
join(dirPath, "agents"),
|
|
@@ -139,7 +142,7 @@ async function isPluginDir(dirPath) {
|
|
|
139
142
|
}
|
|
140
143
|
async function inspectPlugin(pluginPath) {
|
|
141
144
|
let manifest = null;
|
|
142
|
-
for (const manifestDir of [".plugin", ".claude-plugin", ".cursor-plugin"]) {
|
|
145
|
+
for (const manifestDir of [".plugin", ".claude-plugin", ".cursor-plugin", ".codex-plugin"]) {
|
|
143
146
|
const manifestPath = join(pluginPath, manifestDir, "plugin.json");
|
|
144
147
|
if (await fileExists(manifestPath)) {
|
|
145
148
|
manifest = await readJson(manifestPath);
|
|
@@ -331,6 +334,12 @@ var TARGET_DEFS = [
|
|
|
331
334
|
name: "Cursor",
|
|
332
335
|
description: "AI-powered code editor",
|
|
333
336
|
configPath: join2(HOME, ".cursor")
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
id: "codex",
|
|
340
|
+
name: "Codex",
|
|
341
|
+
description: "OpenAI's coding agent",
|
|
342
|
+
configPath: join2(HOME, ".codex")
|
|
334
343
|
}
|
|
335
344
|
// Future targets can be added here:
|
|
336
345
|
// {
|
|
@@ -353,7 +362,9 @@ function detectTarget(def) {
|
|
|
353
362
|
case "claude-code":
|
|
354
363
|
return detectBinary("claude");
|
|
355
364
|
case "cursor":
|
|
356
|
-
return detectBinary("cursor")
|
|
365
|
+
return detectBinary("cursor");
|
|
366
|
+
case "codex":
|
|
367
|
+
return detectBinary("codex");
|
|
357
368
|
default:
|
|
358
369
|
return false;
|
|
359
370
|
}
|
|
@@ -441,6 +452,13 @@ function barLine(content = "") {
|
|
|
441
452
|
function barEmpty() {
|
|
442
453
|
console.log(`${c.gray(S.bar)}`);
|
|
443
454
|
}
|
|
455
|
+
var _debug = false;
|
|
456
|
+
function setDebug(enabled) {
|
|
457
|
+
_debug = enabled;
|
|
458
|
+
}
|
|
459
|
+
function barDebug(content = "") {
|
|
460
|
+
if (_debug) barLine(content);
|
|
461
|
+
}
|
|
444
462
|
function step(content) {
|
|
445
463
|
console.log(`${c.gray(S.step)} ${content}`);
|
|
446
464
|
}
|
|
@@ -663,29 +681,21 @@ function banner() {
|
|
|
663
681
|
}
|
|
664
682
|
|
|
665
683
|
// lib/install.ts
|
|
666
|
-
|
|
667
|
-
switch (targetId) {
|
|
668
|
-
case "claude-code":
|
|
669
|
-
case "cursor":
|
|
670
|
-
return "claude-code";
|
|
671
|
-
default:
|
|
672
|
-
return targetId;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
var completedInstallers = /* @__PURE__ */ new Set();
|
|
684
|
+
var cachePopulated = false;
|
|
676
685
|
async function installPlugins(plugins, target, scope, repoPath, source) {
|
|
677
|
-
|
|
678
|
-
if (completedInstallers.has(key)) {
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
switch (key) {
|
|
686
|
+
switch (target.id) {
|
|
682
687
|
case "claude-code":
|
|
683
688
|
await installToClaudeCode(plugins, scope, repoPath, source);
|
|
684
689
|
break;
|
|
690
|
+
case "cursor":
|
|
691
|
+
await installToCursor(plugins, scope, repoPath, source);
|
|
692
|
+
break;
|
|
693
|
+
case "codex":
|
|
694
|
+
await installToCodex(plugins, scope, repoPath, source);
|
|
695
|
+
break;
|
|
685
696
|
default:
|
|
686
697
|
throw new Error(`Unsupported target: ${target.id}`);
|
|
687
698
|
}
|
|
688
|
-
completedInstallers.add(key);
|
|
689
699
|
}
|
|
690
700
|
async function installToClaudeCode(plugins, scope, repoPath, source) {
|
|
691
701
|
const marketplaceName = plugins[0]?.marketplace ?? deriveMarketplaceName(source);
|
|
@@ -695,19 +705,19 @@ async function installToClaudeCode(plugins, scope, repoPath, source) {
|
|
|
695
705
|
const marketplaceSource = isAnthropicSource(source) ? normalizeGitUrl(source) : repoPath;
|
|
696
706
|
const claudePath = findClaude();
|
|
697
707
|
step("Adding marketplace");
|
|
698
|
-
|
|
708
|
+
barDebug(c.dim(`Binary: ${claudePath}`));
|
|
699
709
|
try {
|
|
700
710
|
const version = execSync2(`${claudePath} --version`, { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
701
|
-
|
|
711
|
+
barDebug(c.dim(`Version: ${version}`));
|
|
702
712
|
} catch {
|
|
703
|
-
|
|
713
|
+
barDebug(c.dim(`Warning: could not get claude version`));
|
|
704
714
|
}
|
|
705
715
|
try {
|
|
706
716
|
const result = execSync2(`${claudePath} plugin marketplace add ${marketplaceSource}`, {
|
|
707
717
|
encoding: "utf-8",
|
|
708
718
|
stdio: "pipe"
|
|
709
719
|
});
|
|
710
|
-
if (result.trim())
|
|
720
|
+
if (result.trim()) barDebug(c.dim(result.trim()));
|
|
711
721
|
stepDone("Marketplace added");
|
|
712
722
|
} catch (err) {
|
|
713
723
|
const stderr = err.stderr?.toString().trim() ?? "";
|
|
@@ -747,6 +757,285 @@ async function installToClaudeCode(plugins, scope, repoPath, source) {
|
|
|
747
757
|
}
|
|
748
758
|
}
|
|
749
759
|
}
|
|
760
|
+
cachePopulated = true;
|
|
761
|
+
}
|
|
762
|
+
async function installToCursor(plugins, scope, repoPath, source) {
|
|
763
|
+
if (cachePopulated) return;
|
|
764
|
+
const claudePath = findClaudeOrNull();
|
|
765
|
+
if (claudePath) {
|
|
766
|
+
await installToClaudeCode(plugins, scope, repoPath, source);
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
await installToCursorExtensions(plugins, scope, repoPath, source);
|
|
770
|
+
}
|
|
771
|
+
async function installToPluginCache(plugins, scope, repoPath, source) {
|
|
772
|
+
const marketplaceName = plugins[0]?.marketplace ?? deriveMarketplaceName(source);
|
|
773
|
+
const home = homedir2();
|
|
774
|
+
const pluginsDir = join3(home, ".claude", "plugins");
|
|
775
|
+
const cacheDir = join3(pluginsDir, "cache");
|
|
776
|
+
step("Preparing plugins for Cursor...");
|
|
777
|
+
barEmpty();
|
|
778
|
+
await prepareForClaudeCode(plugins, repoPath, marketplaceName);
|
|
779
|
+
step("Registering marketplace");
|
|
780
|
+
await mkdir(pluginsDir, { recursive: true });
|
|
781
|
+
const knownPath = join3(pluginsDir, "known_marketplaces.json");
|
|
782
|
+
let knownMarketplaces = {};
|
|
783
|
+
if (existsSync2(knownPath)) {
|
|
784
|
+
try {
|
|
785
|
+
knownMarketplaces = JSON.parse(await readFile2(knownPath, "utf-8"));
|
|
786
|
+
} catch {
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (knownMarketplaces[marketplaceName]) {
|
|
790
|
+
stepDone(`Marketplace ${c.dim("'" + marketplaceName + "'")} already registered`);
|
|
791
|
+
} else {
|
|
792
|
+
knownMarketplaces[marketplaceName] = {
|
|
793
|
+
source: { source: "directory", path: repoPath },
|
|
794
|
+
installLocation: repoPath,
|
|
795
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
796
|
+
};
|
|
797
|
+
await writeFile(knownPath, JSON.stringify(knownMarketplaces, null, 2));
|
|
798
|
+
stepDone("Marketplace registered");
|
|
799
|
+
}
|
|
800
|
+
barEmpty();
|
|
801
|
+
const installedPath = join3(pluginsDir, "installed_plugins.json");
|
|
802
|
+
let installedData = { version: 2, plugins: {} };
|
|
803
|
+
if (existsSync2(installedPath)) {
|
|
804
|
+
try {
|
|
805
|
+
installedData = JSON.parse(await readFile2(installedPath, "utf-8"));
|
|
806
|
+
} catch {
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
let gitSha;
|
|
810
|
+
try {
|
|
811
|
+
gitSha = execSync2("git rev-parse HEAD", { cwd: repoPath, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
812
|
+
} catch {
|
|
813
|
+
}
|
|
814
|
+
for (const plugin of plugins) {
|
|
815
|
+
const pluginRef = `${plugin.name}@${marketplaceName}`;
|
|
816
|
+
const version = plugin.version ?? "0.0.0";
|
|
817
|
+
step(`Installing ${c.bold(pluginRef)}...`);
|
|
818
|
+
const cacheDest = join3(cacheDir, marketplaceName, plugin.name, version);
|
|
819
|
+
await mkdir(cacheDest, { recursive: true });
|
|
820
|
+
await cp(plugin.path, cacheDest, { recursive: true });
|
|
821
|
+
barDebug(c.dim(`Cached to ${cacheDest}`));
|
|
822
|
+
const pluginKey = `${plugin.name}@${marketplaceName}`;
|
|
823
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
824
|
+
const entry = {
|
|
825
|
+
scope,
|
|
826
|
+
installPath: cacheDest,
|
|
827
|
+
version,
|
|
828
|
+
installedAt: now,
|
|
829
|
+
lastUpdated: now
|
|
830
|
+
};
|
|
831
|
+
if (gitSha) entry.gitCommitSha = gitSha;
|
|
832
|
+
installedData.plugins[pluginKey] = [entry];
|
|
833
|
+
stepDone(`Installed ${c.cyan(pluginRef)}`);
|
|
834
|
+
}
|
|
835
|
+
await writeFile(installedPath, JSON.stringify(installedData, null, 2));
|
|
836
|
+
barDebug(c.dim("Updated installed_plugins.json"));
|
|
837
|
+
cachePopulated = true;
|
|
838
|
+
}
|
|
839
|
+
async function installToCursorExtensions(plugins, scope, repoPath, source) {
|
|
840
|
+
if (process.platform !== "win32") {
|
|
841
|
+
await installToPluginCache(plugins, scope, repoPath, source);
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
const marketplaceName = plugins[0]?.marketplace ?? deriveMarketplaceName(source);
|
|
845
|
+
const home = homedir2();
|
|
846
|
+
const extensionsDir = join3(home, ".cursor", "extensions");
|
|
847
|
+
step("Preparing plugins for Cursor...");
|
|
848
|
+
barEmpty();
|
|
849
|
+
await prepareForClaudeCode(plugins, repoPath, marketplaceName);
|
|
850
|
+
await mkdir(extensionsDir, { recursive: true });
|
|
851
|
+
const extensionsJsonPath = join3(extensionsDir, "extensions.json");
|
|
852
|
+
let extensions = [];
|
|
853
|
+
if (existsSync2(extensionsJsonPath)) {
|
|
854
|
+
try {
|
|
855
|
+
const parsed = JSON.parse(await readFile2(extensionsJsonPath, "utf-8"));
|
|
856
|
+
if (Array.isArray(parsed)) extensions = parsed;
|
|
857
|
+
} catch {
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
let gitSha;
|
|
861
|
+
try {
|
|
862
|
+
gitSha = execSync2("git rev-parse HEAD", { cwd: repoPath, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
863
|
+
} catch {
|
|
864
|
+
}
|
|
865
|
+
for (const plugin of plugins) {
|
|
866
|
+
const pluginRef = `${plugin.name}@${marketplaceName}`;
|
|
867
|
+
const version = plugin.version ?? "0.0.0";
|
|
868
|
+
const folderName = `${marketplaceName}.${plugin.name}-${version}`;
|
|
869
|
+
const destDir = join3(extensionsDir, folderName);
|
|
870
|
+
step(`Installing ${c.bold(pluginRef)}...`);
|
|
871
|
+
await mkdir(destDir, { recursive: true });
|
|
872
|
+
await cp(plugin.path, destDir, { recursive: true });
|
|
873
|
+
barDebug(c.dim(`Copied to ${destDir}`));
|
|
874
|
+
const identifier = `${marketplaceName}.${plugin.name}`;
|
|
875
|
+
extensions = extensions.filter(
|
|
876
|
+
(e) => e?.identifier?.id !== identifier
|
|
877
|
+
);
|
|
878
|
+
const uriPath = "/" + destDir.replace(/\\/g, "/");
|
|
879
|
+
extensions.push({
|
|
880
|
+
identifier: { id: identifier },
|
|
881
|
+
version,
|
|
882
|
+
location: { $mid: 1, path: uriPath, scheme: "file" },
|
|
883
|
+
relativeLocation: folderName,
|
|
884
|
+
metadata: {
|
|
885
|
+
installedTimestamp: Date.now(),
|
|
886
|
+
...gitSha ? { gitCommitSha: gitSha } : {}
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
stepDone(`Installed ${c.cyan(pluginRef)}`);
|
|
890
|
+
}
|
|
891
|
+
await writeFile(extensionsJsonPath, JSON.stringify(extensions, null, 2));
|
|
892
|
+
barDebug(c.dim("Updated extensions.json"));
|
|
893
|
+
cachePopulated = true;
|
|
894
|
+
}
|
|
895
|
+
async function installToCodex(plugins, scope, repoPath, source) {
|
|
896
|
+
const marketplaceName = plugins[0]?.marketplace ?? deriveMarketplaceName(source);
|
|
897
|
+
const home = homedir2();
|
|
898
|
+
const cacheDir = join3(home, ".codex", "plugins", "cache");
|
|
899
|
+
const configPath = join3(home, ".codex", "config.toml");
|
|
900
|
+
const marketplaceDir = join3(home, ".agents", "plugins");
|
|
901
|
+
const marketplacePath = join3(marketplaceDir, "marketplace.json");
|
|
902
|
+
const marketplaceRoot = home;
|
|
903
|
+
step("Preparing plugins for Codex...");
|
|
904
|
+
barEmpty();
|
|
905
|
+
for (const plugin of plugins) {
|
|
906
|
+
await preparePluginDirForVendor(plugin, ".codex-plugin", "CODEX_PLUGIN_ROOT");
|
|
907
|
+
await enrichForCodex(plugin);
|
|
908
|
+
}
|
|
909
|
+
let gitSha;
|
|
910
|
+
try {
|
|
911
|
+
gitSha = execSync2("git rev-parse HEAD", { cwd: repoPath, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
912
|
+
} catch {
|
|
913
|
+
}
|
|
914
|
+
const versionKey = gitSha ?? "local";
|
|
915
|
+
const pluginPaths = {};
|
|
916
|
+
for (const plugin of plugins) {
|
|
917
|
+
const pluginRef = `${plugin.name}@${marketplaceName}`;
|
|
918
|
+
step(`Installing ${c.bold(pluginRef)}...`);
|
|
919
|
+
const cacheDest = join3(cacheDir, marketplaceName, plugin.name, versionKey);
|
|
920
|
+
await mkdir(cacheDest, { recursive: true });
|
|
921
|
+
await cp(plugin.path, cacheDest, { recursive: true });
|
|
922
|
+
pluginPaths[plugin.name] = cacheDest;
|
|
923
|
+
barDebug(c.dim(`Cached to ${cacheDest}`));
|
|
924
|
+
stepDone(`Installed ${c.cyan(pluginRef)}`);
|
|
925
|
+
}
|
|
926
|
+
step("Updating marketplace...");
|
|
927
|
+
await mkdir(marketplaceDir, { recursive: true });
|
|
928
|
+
let marketplace = {
|
|
929
|
+
name: "plugins-cli",
|
|
930
|
+
interface: { displayName: "Plugins CLI" },
|
|
931
|
+
plugins: []
|
|
932
|
+
};
|
|
933
|
+
if (existsSync2(marketplacePath)) {
|
|
934
|
+
try {
|
|
935
|
+
const existing = JSON.parse(await readFile2(marketplacePath, "utf-8"));
|
|
936
|
+
if (existing && typeof existing === "object" && Array.isArray(existing.plugins)) {
|
|
937
|
+
marketplace = existing;
|
|
938
|
+
}
|
|
939
|
+
} catch {
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
for (const plugin of plugins) {
|
|
943
|
+
const cacheDest = pluginPaths[plugin.name];
|
|
944
|
+
const relPath = relative(marketplaceRoot, cacheDest);
|
|
945
|
+
marketplace.plugins = marketplace.plugins.filter(
|
|
946
|
+
(e) => e.name !== plugin.name
|
|
947
|
+
);
|
|
948
|
+
marketplace.plugins.push({
|
|
949
|
+
name: plugin.name,
|
|
950
|
+
source: {
|
|
951
|
+
source: "local",
|
|
952
|
+
path: `./${relPath}`
|
|
953
|
+
},
|
|
954
|
+
policy: {
|
|
955
|
+
installation: "AVAILABLE",
|
|
956
|
+
authentication: "ON_INSTALL"
|
|
957
|
+
},
|
|
958
|
+
category: "Coding"
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
await writeFile(marketplacePath, JSON.stringify(marketplace, null, 2));
|
|
962
|
+
stepDone("Marketplace updated");
|
|
963
|
+
step("Updating config.toml...");
|
|
964
|
+
await mkdir(join3(home, ".codex"), { recursive: true });
|
|
965
|
+
let configContent = "";
|
|
966
|
+
if (existsSync2(configPath)) {
|
|
967
|
+
configContent = await readFile2(configPath, "utf-8");
|
|
968
|
+
}
|
|
969
|
+
let configChanged = false;
|
|
970
|
+
for (const plugin of plugins) {
|
|
971
|
+
const pluginKey = `${plugin.name}@plugins-cli`;
|
|
972
|
+
const tomlSection = `[plugins."${pluginKey}"]`;
|
|
973
|
+
if (configContent.includes(tomlSection)) {
|
|
974
|
+
barDebug(c.dim(`${pluginKey} already in config.toml`));
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
const entry = `
|
|
978
|
+
${tomlSection}
|
|
979
|
+
enabled = true
|
|
980
|
+
`;
|
|
981
|
+
configContent += entry;
|
|
982
|
+
configChanged = true;
|
|
983
|
+
barDebug(c.dim(`Added ${pluginKey} to config.toml`));
|
|
984
|
+
}
|
|
985
|
+
if (configChanged) {
|
|
986
|
+
await writeFile(configPath, configContent);
|
|
987
|
+
}
|
|
988
|
+
stepDone("Config updated");
|
|
989
|
+
}
|
|
990
|
+
async function enrichForCodex(plugin) {
|
|
991
|
+
const codexManifestPath = join3(plugin.path, ".codex-plugin", "plugin.json");
|
|
992
|
+
if (!existsSync2(codexManifestPath)) return;
|
|
993
|
+
let manifest;
|
|
994
|
+
try {
|
|
995
|
+
manifest = JSON.parse(await readFile2(codexManifestPath, "utf-8"));
|
|
996
|
+
} catch {
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
if (manifest.interface) return;
|
|
1000
|
+
let changed = false;
|
|
1001
|
+
if (!manifest.skills && existsSync2(join3(plugin.path, "skills"))) {
|
|
1002
|
+
manifest.skills = "./skills/";
|
|
1003
|
+
changed = true;
|
|
1004
|
+
}
|
|
1005
|
+
if (!manifest.mcpServers && existsSync2(join3(plugin.path, ".mcp.json"))) {
|
|
1006
|
+
manifest.mcpServers = "./.mcp.json";
|
|
1007
|
+
changed = true;
|
|
1008
|
+
}
|
|
1009
|
+
if (!manifest.apps && existsSync2(join3(plugin.path, ".app.json"))) {
|
|
1010
|
+
manifest.apps = "./.app.json";
|
|
1011
|
+
changed = true;
|
|
1012
|
+
}
|
|
1013
|
+
const name = manifest.name ?? plugin.name;
|
|
1014
|
+
const description = manifest.description ?? plugin.description ?? "";
|
|
1015
|
+
const author = manifest.author;
|
|
1016
|
+
const iface = {
|
|
1017
|
+
displayName: name.charAt(0).toUpperCase() + name.slice(1),
|
|
1018
|
+
shortDescription: description,
|
|
1019
|
+
developerName: author?.name ?? "Unknown",
|
|
1020
|
+
category: "Coding",
|
|
1021
|
+
capabilities: ["Interactive", "Write"]
|
|
1022
|
+
};
|
|
1023
|
+
if (manifest.homepage) iface.websiteURL = manifest.homepage;
|
|
1024
|
+
else if (manifest.repository) iface.websiteURL = manifest.repository;
|
|
1025
|
+
const assetCandidates = ["assets/app-icon.png", "assets/icon.png", "assets/logo.png", "assets/logo.svg"];
|
|
1026
|
+
for (const candidate of assetCandidates) {
|
|
1027
|
+
if (existsSync2(join3(plugin.path, candidate))) {
|
|
1028
|
+
iface.logo = `./${candidate}`;
|
|
1029
|
+
iface.composerIcon = `./${candidate}`;
|
|
1030
|
+
break;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
manifest.interface = iface;
|
|
1034
|
+
changed = true;
|
|
1035
|
+
if (changed) {
|
|
1036
|
+
await writeFile(codexManifestPath, JSON.stringify(manifest, null, 2));
|
|
1037
|
+
barDebug(c.dim(`${plugin.name}: enriched .codex-plugin/plugin.json for Codex`));
|
|
1038
|
+
}
|
|
750
1039
|
}
|
|
751
1040
|
async function prepareForClaudeCode(plugins, repoPath, marketplaceName) {
|
|
752
1041
|
const claudePluginDir = join3(repoPath, ".claude-plugin");
|
|
@@ -773,12 +1062,12 @@ async function prepareForClaudeCode(plugins, repoPath, marketplaceName) {
|
|
|
773
1062
|
join3(claudePluginDir, "marketplace.json"),
|
|
774
1063
|
JSON.stringify(marketplaceJson, null, 2)
|
|
775
1064
|
);
|
|
776
|
-
|
|
1065
|
+
barDebug(c.dim("Generated .claude-plugin/marketplace.json"));
|
|
777
1066
|
for (const plugin of plugins) {
|
|
778
1067
|
await preparePluginDirForVendor(plugin, ".claude-plugin", "CLAUDE_PLUGIN_ROOT");
|
|
779
1068
|
}
|
|
780
1069
|
}
|
|
781
|
-
function
|
|
1070
|
+
function findClaudeOrNull() {
|
|
782
1071
|
try {
|
|
783
1072
|
const path = execSync2("which claude", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
784
1073
|
if (path) return path;
|
|
@@ -793,7 +1082,10 @@ function findClaude() {
|
|
|
793
1082
|
for (const candidate of candidates) {
|
|
794
1083
|
if (existsSync2(candidate)) return candidate;
|
|
795
1084
|
}
|
|
796
|
-
return
|
|
1085
|
+
return null;
|
|
1086
|
+
}
|
|
1087
|
+
function findClaude() {
|
|
1088
|
+
return findClaudeOrNull() ?? "claude";
|
|
797
1089
|
}
|
|
798
1090
|
async function preparePluginDirForVendor(plugin, vendorDir, envVar) {
|
|
799
1091
|
const pluginPath = plugin.path;
|
|
@@ -803,7 +1095,7 @@ async function preparePluginDirForVendor(plugin, vendorDir, envVar) {
|
|
|
803
1095
|
const hasVendorPlugin = existsSync2(join3(vendorPluginDir, "plugin.json"));
|
|
804
1096
|
if (hasOpenPlugin && !hasVendorPlugin) {
|
|
805
1097
|
await cp(openPluginDir, vendorPluginDir, { recursive: true });
|
|
806
|
-
|
|
1098
|
+
barDebug(c.dim(`${plugin.name}: translated .plugin/ \u2192 ${vendorDir}/`));
|
|
807
1099
|
}
|
|
808
1100
|
if (!hasOpenPlugin && !hasVendorPlugin) {
|
|
809
1101
|
await mkdir(vendorPluginDir, { recursive: true });
|
|
@@ -819,14 +1111,15 @@ async function preparePluginDirForVendor(plugin, vendorDir, envVar) {
|
|
|
819
1111
|
2
|
|
820
1112
|
)
|
|
821
1113
|
);
|
|
822
|
-
|
|
1114
|
+
barDebug(c.dim(`${plugin.name}: generated ${vendorDir}/plugin.json`));
|
|
823
1115
|
}
|
|
824
1116
|
await translateEnvVars(pluginPath, plugin.name, envVar);
|
|
825
1117
|
}
|
|
826
1118
|
var KNOWN_PLUGIN_ROOT_VARS = [
|
|
827
1119
|
"PLUGIN_ROOT",
|
|
828
1120
|
"CLAUDE_PLUGIN_ROOT",
|
|
829
|
-
"CURSOR_PLUGIN_ROOT"
|
|
1121
|
+
"CURSOR_PLUGIN_ROOT",
|
|
1122
|
+
"CODEX_PLUGIN_ROOT"
|
|
830
1123
|
];
|
|
831
1124
|
async function translateEnvVars(pluginPath, pluginName, envVar) {
|
|
832
1125
|
const configFiles = [
|
|
@@ -848,7 +1141,7 @@ async function translateEnvVars(pluginPath, pluginName, envVar) {
|
|
|
848
1141
|
}
|
|
849
1142
|
if (changed) {
|
|
850
1143
|
await writeFile(filePath, content);
|
|
851
|
-
|
|
1144
|
+
barDebug(
|
|
852
1145
|
c.dim(`${pluginName}: translated plugin root \u2192 \${${envVar}} in ${filePath.split("/").pop()}`)
|
|
853
1146
|
);
|
|
854
1147
|
}
|
|
@@ -935,12 +1228,14 @@ var { values, positionals } = parseArgs({
|
|
|
935
1228
|
target: { type: "string", short: "t" },
|
|
936
1229
|
scope: { type: "string", short: "s", default: "user" },
|
|
937
1230
|
yes: { type: "boolean", short: "y" },
|
|
938
|
-
remote: { type: "boolean" }
|
|
1231
|
+
remote: { type: "boolean" },
|
|
1232
|
+
debug: { type: "boolean" }
|
|
939
1233
|
},
|
|
940
1234
|
allowPositionals: true,
|
|
941
1235
|
strict: true
|
|
942
1236
|
});
|
|
943
1237
|
var [command, ...rest] = positionals;
|
|
1238
|
+
if (values.debug) setDebug(true);
|
|
944
1239
|
if (values.help || !command) {
|
|
945
1240
|
printUsage();
|
|
946
1241
|
process.exit(0);
|
|
@@ -973,6 +1268,7 @@ ${c.dim("Options:")}
|
|
|
973
1268
|
${c.yellow("-s, --scope")} <scope> Install scope: user, project, local. Default: user
|
|
974
1269
|
${c.yellow("-y, --yes")} Skip confirmation prompts
|
|
975
1270
|
${c.yellow("--remote")} Include remote-source plugins in output
|
|
1271
|
+
${c.yellow("--debug")} Show verbose installation output
|
|
976
1272
|
${c.yellow("-h, --help")} Show this help
|
|
977
1273
|
`);
|
|
978
1274
|
}
|
|
@@ -1070,11 +1366,16 @@ async function cmdInstall(source, opts) {
|
|
|
1070
1366
|
footer();
|
|
1071
1367
|
process.exit(1);
|
|
1072
1368
|
}
|
|
1369
|
+
if (!found.detected) {
|
|
1370
|
+
barEmpty();
|
|
1371
|
+
barLine(c.yellow(`Warning: ${found.name} was not detected on this system.`));
|
|
1372
|
+
}
|
|
1073
1373
|
installTargets = [found];
|
|
1074
1374
|
} else if (detectedTargets.length === 0) {
|
|
1075
1375
|
barEmpty();
|
|
1076
1376
|
stepError("No supported targets detected.");
|
|
1077
|
-
barLine(c.dim("
|
|
1377
|
+
barLine(c.dim("Neither 'claude', 'cursor', nor 'codex' binaries were found on PATH."));
|
|
1378
|
+
barLine(c.dim("Use --target to specify one manually."));
|
|
1078
1379
|
footer();
|
|
1079
1380
|
process.exit(1);
|
|
1080
1381
|
} else {
|
|
@@ -1203,7 +1504,7 @@ function sshToHttps(sshUrl) {
|
|
|
1203
1504
|
function resolveSource(source) {
|
|
1204
1505
|
if (source.startsWith("https://") || source.startsWith("git@") || source.match(/^[\w-]+\/[\w.-]+$/)) {
|
|
1205
1506
|
const url = source.match(/^[\w-]+\/[\w.-]+$/) ? `https://github.com/${source}` : source;
|
|
1206
|
-
const cacheDir = join4(
|
|
1507
|
+
const cacheDir = join4(homedir3(), ".cache", "plugins");
|
|
1207
1508
|
mkdirSync(cacheDir, { recursive: true });
|
|
1208
1509
|
const slug = url.replace(/^https?:\/\//, "").replace(/^git@/, "").replace(/\.git$/, "").replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1209
1510
|
const tmpDir = join4(cacheDir, slug);
|
|
@@ -1213,7 +1514,7 @@ function resolveSource(source) {
|
|
|
1213
1514
|
step(`Source: ${c.dim(url)}`);
|
|
1214
1515
|
barEmpty();
|
|
1215
1516
|
try {
|
|
1216
|
-
execSync3(`git clone --depth 1 -q ${url} ${tmpDir}`, { stdio: "pipe" });
|
|
1517
|
+
execSync3(`git clone --depth 1 -q "${url}" "${tmpDir}"`, { stdio: "pipe" });
|
|
1217
1518
|
} catch (err) {
|
|
1218
1519
|
const stderr = err.stderr?.toString() ?? "";
|
|
1219
1520
|
if (url.startsWith("git@") && stderr.includes("Permission denied")) {
|
|
@@ -1223,7 +1524,7 @@ function resolveSource(source) {
|
|
|
1223
1524
|
step(`Source: ${c.dim(httpsUrl)}`);
|
|
1224
1525
|
barEmpty();
|
|
1225
1526
|
try {
|
|
1226
|
-
execSync3(`git clone --depth 1 -q ${httpsUrl} ${tmpDir}`, { stdio: "inherit" });
|
|
1527
|
+
execSync3(`git clone --depth 1 -q "${httpsUrl}" "${tmpDir}"`, { stdio: "inherit" });
|
|
1227
1528
|
stepDone("Repository cloned");
|
|
1228
1529
|
barEmpty();
|
|
1229
1530
|
return tmpDir;
|