@danieldeusing/seedr 0.1.97 → 0.1.99
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/{chunk-PEGEA2KN.js → chunk-3KPB5CCX.js} +183 -26
- package/dist/cli.js +881 -343
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
AGENT_COMPATIBILITY,
|
|
3
4
|
ALL_AGENTS,
|
|
5
|
+
ALL_TYPES,
|
|
4
6
|
CODING_AGENTS,
|
|
5
7
|
MAX_SLUG_LENGTH,
|
|
6
8
|
SLUG_PATTERN,
|
|
@@ -11,8 +13,12 @@ import {
|
|
|
11
13
|
brand,
|
|
12
14
|
canonicalAgent,
|
|
13
15
|
canonicalAgents,
|
|
16
|
+
claudeUserRoot,
|
|
17
|
+
codexUserRoot,
|
|
14
18
|
confirm,
|
|
19
|
+
copilotUserRoot,
|
|
15
20
|
copyDirectory,
|
|
21
|
+
deepMerge,
|
|
16
22
|
ensureDir,
|
|
17
23
|
error,
|
|
18
24
|
exists,
|
|
@@ -33,15 +39,18 @@ import {
|
|
|
33
39
|
intro,
|
|
34
40
|
isInteractive,
|
|
35
41
|
isLegacyAgent,
|
|
42
|
+
isTypeSupported,
|
|
36
43
|
isValidSlug,
|
|
37
44
|
listItems,
|
|
38
45
|
mainFileName,
|
|
39
46
|
moveDirectory,
|
|
47
|
+
openCodeUserConfigDir,
|
|
40
48
|
outro,
|
|
41
49
|
p,
|
|
42
50
|
parseAgentsArgStrict,
|
|
43
51
|
parseGitHubRepo,
|
|
44
52
|
printLogo,
|
|
53
|
+
readJson,
|
|
45
54
|
removePathEntry,
|
|
46
55
|
resolveContained,
|
|
47
56
|
restoreFile,
|
|
@@ -56,15 +65,16 @@ import {
|
|
|
56
65
|
success,
|
|
57
66
|
warn,
|
|
58
67
|
writeFileAtomic,
|
|
68
|
+
writeJson,
|
|
59
69
|
writeTextFile
|
|
60
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-3KPB5CCX.js";
|
|
61
71
|
|
|
62
72
|
// src/cli.ts
|
|
63
73
|
import { Command as Command5 } from "commander";
|
|
64
74
|
|
|
65
75
|
// src/commands/add.ts
|
|
66
76
|
import { Command } from "commander";
|
|
67
|
-
import
|
|
77
|
+
import chalk9 from "chalk";
|
|
68
78
|
|
|
69
79
|
// src/handlers/registry.ts
|
|
70
80
|
var handlers = /* @__PURE__ */ new Map();
|
|
@@ -104,15 +114,8 @@ function handleCommandError(error2) {
|
|
|
104
114
|
// src/utils/validate-options.ts
|
|
105
115
|
var SCOPES = ["project", "user", "local"];
|
|
106
116
|
var METHODS = ["symlink", "copy"];
|
|
107
|
-
var TYPES =
|
|
108
|
-
|
|
109
|
-
"hook",
|
|
110
|
-
"agent",
|
|
111
|
-
"plugin",
|
|
112
|
-
"command",
|
|
113
|
-
"settings",
|
|
114
|
-
"mcp"
|
|
115
|
-
];
|
|
117
|
+
var TYPES = ALL_TYPES;
|
|
118
|
+
var TYPE_LIST = TYPES.join(", ");
|
|
116
119
|
function validateScope(value) {
|
|
117
120
|
if (value === void 0) return null;
|
|
118
121
|
if (!SCOPES.includes(value)) {
|
|
@@ -144,7 +147,7 @@ function isTelemetryDisabled(env = process.env) {
|
|
|
144
147
|
return env[TELEMETRY_OPT_OUT_VARIABLE] !== void 0;
|
|
145
148
|
}
|
|
146
149
|
function cliVersion() {
|
|
147
|
-
return true ? "0.1.
|
|
150
|
+
return true ? "0.1.99" : "dev";
|
|
148
151
|
}
|
|
149
152
|
async function sendEvent(event) {
|
|
150
153
|
try {
|
|
@@ -165,26 +168,12 @@ function trackInstalls(slug, type, results, scope) {
|
|
|
165
168
|
}
|
|
166
169
|
|
|
167
170
|
// src/config/compatibility.ts
|
|
168
|
-
var AGENT_COMPATIBILITY = {
|
|
169
|
-
skill: ["claude", "copilot", "antigravity", "codex", "opencode"],
|
|
170
|
-
command: ["claude"],
|
|
171
|
-
agent: ["claude"],
|
|
172
|
-
hook: ["claude"],
|
|
173
|
-
plugin: ["claude"],
|
|
174
|
-
settings: ["claude"],
|
|
175
|
-
mcp: ["claude", "codex", "opencode"]
|
|
176
|
-
};
|
|
177
|
-
function isTypeSupported(type, agent) {
|
|
178
|
-
const canonical = canonicalAgent(agent);
|
|
179
|
-
return canonical !== null && AGENT_COMPATIBILITY[type].includes(canonical);
|
|
180
|
-
}
|
|
181
171
|
function filterCompatibleAgents(type, agents) {
|
|
182
172
|
const compatible = AGENT_COMPATIBILITY[type];
|
|
183
173
|
return canonicalAgents(agents).filter((a) => compatible.includes(a));
|
|
184
174
|
}
|
|
185
175
|
var MCP_UNSUPPORTED_REASONS = {
|
|
186
|
-
|
|
187
|
-
antigravity: "Google Antigravity's MCP configuration format could not be verified against primary documentation, so seedr does not write it"
|
|
176
|
+
antigravity: "Google Antigravity's MCP file name is documented but its schema has never been observed \u2014 the file is empty on every machine checked \u2014 so seedr does not guess at it"
|
|
188
177
|
};
|
|
189
178
|
function describeIncompatibility(type, agent) {
|
|
190
179
|
const canonical = canonicalAgent(agent);
|
|
@@ -201,11 +190,14 @@ import { lstat, readdir, unlink } from "fs/promises";
|
|
|
201
190
|
import chalk2 from "chalk";
|
|
202
191
|
import ora from "ora";
|
|
203
192
|
var SLUG_LABEL = "agent slug";
|
|
193
|
+
function subagentFileName(agent, slug) {
|
|
194
|
+
return (canonicalAgent(agent) ?? agent) === "copilot" ? `${slug}.agent.md` : `${slug}.md`;
|
|
195
|
+
}
|
|
204
196
|
async function resolveAgentFilePath(agent, slug, scope, cwd) {
|
|
205
197
|
const destDir = getContentPath(agent, "agent", scope, cwd);
|
|
206
198
|
if (!destDir) return null;
|
|
207
199
|
const scopeRoot = scope === "user" ? homedir() : cwd;
|
|
208
|
-
return resolveContained(scopeRoot, relative(scopeRoot, destDir),
|
|
200
|
+
return resolveContained(scopeRoot, relative(scopeRoot, destDir), subagentFileName(agent, slug));
|
|
209
201
|
}
|
|
210
202
|
async function installAgentForCodingAgent(item, agent, scope, method, force, cwd) {
|
|
211
203
|
const spinner = ora(
|
|
@@ -269,8 +261,9 @@ async function getInstalledAgents(agent, scope, cwd = process.cwd()) {
|
|
|
269
261
|
if (!destDir || !await exists(destDir)) {
|
|
270
262
|
return [];
|
|
271
263
|
}
|
|
264
|
+
const suffix = subagentFileName(agent, "");
|
|
272
265
|
const files = await readdir(destDir);
|
|
273
|
-
return files.filter((
|
|
266
|
+
return files.filter((file) => file.endsWith(suffix)).map((file) => file.slice(0, -suffix.length));
|
|
274
267
|
}
|
|
275
268
|
async function planAgent(item, agents, scope, method, cwd) {
|
|
276
269
|
assertValidSlug(item.slug, SLUG_LABEL);
|
|
@@ -413,60 +406,11 @@ var commandHandler = {
|
|
|
413
406
|
};
|
|
414
407
|
|
|
415
408
|
// src/handlers/hook.ts
|
|
416
|
-
import { join as join3, basename, dirname
|
|
409
|
+
import { join as join3, basename, dirname } from "path";
|
|
417
410
|
import { homedir as homedir3 } from "os";
|
|
418
|
-
import { lstat as lstat3, mkdir, mkdtemp, readFile
|
|
411
|
+
import { lstat as lstat3, mkdir, mkdtemp, readFile, realpath, rm, unlink as unlink3 } from "fs/promises";
|
|
419
412
|
import chalk4 from "chalk";
|
|
420
413
|
import ora3 from "ora";
|
|
421
|
-
|
|
422
|
-
// src/utils/json.ts
|
|
423
|
-
import { readFile, stat } from "fs/promises";
|
|
424
|
-
import { dirname } from "path";
|
|
425
|
-
async function readJson(path) {
|
|
426
|
-
try {
|
|
427
|
-
const content = await readFile(path, "utf-8");
|
|
428
|
-
return JSON.parse(content);
|
|
429
|
-
} catch (error2) {
|
|
430
|
-
if (error2.code === "ENOENT") {
|
|
431
|
-
return {};
|
|
432
|
-
}
|
|
433
|
-
if (error2 instanceof SyntaxError) {
|
|
434
|
-
throw new Error(`${path} is not valid JSON: ${error2.message}`, { cause: error2 });
|
|
435
|
-
}
|
|
436
|
-
throw error2;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
async function existingFileMode(path) {
|
|
440
|
-
try {
|
|
441
|
-
return (await stat(path)).mode & 511;
|
|
442
|
-
} catch (error2) {
|
|
443
|
-
if (error2.code === "ENOENT") return void 0;
|
|
444
|
-
throw error2;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
async function writeJson(path, data) {
|
|
448
|
-
await ensureDir(dirname(path));
|
|
449
|
-
const mode = await existingFileMode(path);
|
|
450
|
-
await writeFileAtomic(path, JSON.stringify(data, null, 2) + "\n", { mode });
|
|
451
|
-
}
|
|
452
|
-
function deepMerge(target, source) {
|
|
453
|
-
const result = { ...target };
|
|
454
|
-
for (const key of Object.keys(source)) {
|
|
455
|
-
const sourceValue = source[key];
|
|
456
|
-
const targetValue = result[key];
|
|
457
|
-
if (sourceValue !== null && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue !== null && typeof targetValue === "object" && !Array.isArray(targetValue)) {
|
|
458
|
-
result[key] = deepMerge(
|
|
459
|
-
targetValue,
|
|
460
|
-
sourceValue
|
|
461
|
-
);
|
|
462
|
-
} else if (sourceValue !== void 0) {
|
|
463
|
-
result[key] = sourceValue;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
return result;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// src/handlers/hook.ts
|
|
470
414
|
var CLAUDE_ONLY_ERROR = "Hooks are only supported for Claude Code";
|
|
471
415
|
var SCRIPT_NAME_LABEL = "hook script name";
|
|
472
416
|
var SCRIPT_MODE = 493;
|
|
@@ -474,7 +418,7 @@ function getScopeRoot(scope, cwd) {
|
|
|
474
418
|
return scope === "user" ? homedir3() : cwd;
|
|
475
419
|
}
|
|
476
420
|
function getHooksDir(scope, cwd) {
|
|
477
|
-
return join3(getScopeRoot(scope, cwd), ".claude", "hooks");
|
|
421
|
+
return scope === "user" ? join3(claudeUserRoot(), "hooks") : join3(getScopeRoot(scope, cwd), ".claude", "hooks");
|
|
478
422
|
}
|
|
479
423
|
function getScriptPath(scope, scriptName) {
|
|
480
424
|
switch (scope) {
|
|
@@ -520,13 +464,13 @@ async function assertRegularFileOrMissing(path) {
|
|
|
520
464
|
async function loadScriptBytes(item, scriptFile, hooksDir) {
|
|
521
465
|
const sourcePath = getItemSourcePath(item);
|
|
522
466
|
if (sourcePath && await exists(sourcePath)) {
|
|
523
|
-
return
|
|
467
|
+
return readFile(join3(sourcePath, scriptFile));
|
|
524
468
|
}
|
|
525
|
-
const fetchDir = await mkdtemp(join3(
|
|
469
|
+
const fetchDir = await mkdtemp(join3(dirname(hooksDir), ".seedr-hook-"));
|
|
526
470
|
try {
|
|
527
471
|
const contentDir = join3(fetchDir, "content");
|
|
528
472
|
await fetchItemToDestination(item, contentDir);
|
|
529
|
-
return await
|
|
473
|
+
return await readFile(join3(contentDir, scriptFile));
|
|
530
474
|
} finally {
|
|
531
475
|
await rm(fetchDir, { recursive: true, force: true });
|
|
532
476
|
}
|
|
@@ -703,6 +647,7 @@ async function planHook(item, agents, scope, _method, cwd) {
|
|
|
703
647
|
}
|
|
704
648
|
var hookHandler = {
|
|
705
649
|
type: "hook",
|
|
650
|
+
honoursMethod: false,
|
|
706
651
|
async install(item, agents, scope, method, force, cwd) {
|
|
707
652
|
return installHook(item, agents, scope, method, force, cwd);
|
|
708
653
|
},
|
|
@@ -716,8 +661,8 @@ var hookHandler = {
|
|
|
716
661
|
};
|
|
717
662
|
|
|
718
663
|
// src/handlers/mcp.ts
|
|
719
|
-
import { readFile as
|
|
720
|
-
import { dirname as
|
|
664
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
665
|
+
import { dirname as dirname2 } from "path";
|
|
721
666
|
import chalk5 from "chalk";
|
|
722
667
|
import ora4 from "ora";
|
|
723
668
|
|
|
@@ -1014,7 +959,7 @@ function jsonAdapter(agent, key, translate, defaults = {}) {
|
|
|
1014
959
|
}
|
|
1015
960
|
async function readTextOrEmpty(path) {
|
|
1016
961
|
try {
|
|
1017
|
-
return await
|
|
962
|
+
return await readFile2(path, "utf-8");
|
|
1018
963
|
} catch (error2) {
|
|
1019
964
|
if (error2.code === "ENOENT") return "";
|
|
1020
965
|
throw error2;
|
|
@@ -1028,7 +973,7 @@ var codexAdapter = {
|
|
|
1028
973
|
},
|
|
1029
974
|
async write(path, name, config) {
|
|
1030
975
|
const text = await readTextOrEmpty(path);
|
|
1031
|
-
await ensureDir(
|
|
976
|
+
await ensureDir(dirname2(path));
|
|
1032
977
|
await writeFileAtomic(path, upsertTomlTables(text, [CODEX_TABLE, name], toCodexTables(name, config)));
|
|
1033
978
|
},
|
|
1034
979
|
async remove(path, name) {
|
|
@@ -1044,7 +989,10 @@ var codexAdapter = {
|
|
|
1044
989
|
var ADAPTERS = {
|
|
1045
990
|
claude: jsonAdapter("claude", MCP_SERVERS_KEY, (config) => ({ ...config })),
|
|
1046
991
|
opencode: jsonAdapter("opencode", "mcp", toOpenCodeServer, { $schema: "https://opencode.ai/config.json" }),
|
|
1047
|
-
codex: codexAdapter
|
|
992
|
+
codex: codexAdapter,
|
|
993
|
+
// Copilot reads the same `mcpServers` map Claude does — verified from
|
|
994
|
+
// `copilot mcp --help` and from a populated `~/.copilot/mcp-config.json`.
|
|
995
|
+
copilot: jsonAdapter("copilot", MCP_SERVERS_KEY, (config) => ({ ...config }))
|
|
1048
996
|
};
|
|
1049
997
|
function adapterFor(agent) {
|
|
1050
998
|
const adapter = ADAPTERS[agent];
|
|
@@ -1129,6 +1077,7 @@ async function planMcp(item, agents, scope, _method, cwd) {
|
|
|
1129
1077
|
}
|
|
1130
1078
|
var mcpHandler = {
|
|
1131
1079
|
type: "mcp",
|
|
1080
|
+
honoursMethod: false,
|
|
1132
1081
|
async install(item, agents, scope, method, force, cwd) {
|
|
1133
1082
|
return installMcp(item, agents, scope, method, force, cwd);
|
|
1134
1083
|
},
|
|
@@ -1255,6 +1204,7 @@ async function planSettings(item, agents, scope, _method, cwd) {
|
|
|
1255
1204
|
}
|
|
1256
1205
|
var settingsHandler = {
|
|
1257
1206
|
type: "settings",
|
|
1207
|
+
honoursMethod: false,
|
|
1258
1208
|
async install(item, agents, scope, method, force, cwd) {
|
|
1259
1209
|
return installSettings(item, agents, scope, method, force, cwd);
|
|
1260
1210
|
},
|
|
@@ -1268,23 +1218,30 @@ var settingsHandler = {
|
|
|
1268
1218
|
};
|
|
1269
1219
|
|
|
1270
1220
|
// src/handlers/plugin.ts
|
|
1221
|
+
import { join as join5, relative as relative3 } from "path";
|
|
1222
|
+
import { mkdir as mkdir3, mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
|
|
1223
|
+
import chalk7 from "chalk";
|
|
1224
|
+
import ora6 from "ora";
|
|
1225
|
+
|
|
1226
|
+
// src/handlers/pluginStores.ts
|
|
1271
1227
|
import { homedir as homedir4 } from "os";
|
|
1272
|
-
import { join as join4
|
|
1273
|
-
import { mkdir as mkdir2,
|
|
1228
|
+
import { join as join4 } from "path";
|
|
1229
|
+
import { mkdir as mkdir2, readFile as readFile3, writeFile } from "fs/promises";
|
|
1274
1230
|
import { execFile } from "child_process";
|
|
1275
1231
|
import { promisify } from "util";
|
|
1276
|
-
import chalk7 from "chalk";
|
|
1277
|
-
import ora6 from "ora";
|
|
1278
1232
|
var home = homedir4();
|
|
1279
|
-
var
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1233
|
+
var CLAUDE_MANIFEST = ".claude-plugin/plugin.json";
|
|
1234
|
+
async function readTextOrEmpty2(path) {
|
|
1235
|
+
try {
|
|
1236
|
+
return await readFile3(path, "utf-8");
|
|
1237
|
+
} catch {
|
|
1238
|
+
return "";
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
async function writeText(path, text) {
|
|
1242
|
+
await mkdir2(join4(path, ".."), { recursive: true });
|
|
1243
|
+
await writeFile(path, text, "utf-8");
|
|
1244
|
+
}
|
|
1288
1245
|
function getPluginId(name, marketplace) {
|
|
1289
1246
|
return `${name}@${marketplace}`;
|
|
1290
1247
|
}
|
|
@@ -1293,6 +1250,420 @@ function splitPluginId(pluginId) {
|
|
|
1293
1250
|
if (at <= 0 || at === pluginId.length - 1) return null;
|
|
1294
1251
|
return { name: pluginId.slice(0, at), marketplace: pluginId.slice(at + 1) };
|
|
1295
1252
|
}
|
|
1253
|
+
function manifestsFor(ownManifest) {
|
|
1254
|
+
return ownManifest ? [ownManifest, CLAUDE_MANIFEST] : [CLAUDE_MANIFEST];
|
|
1255
|
+
}
|
|
1256
|
+
var CLAUDE_PLUGINS_DIR = join4(claudeUserRoot(), "plugins");
|
|
1257
|
+
var CLAUDE_CACHE_DIR = join4(CLAUDE_PLUGINS_DIR, "cache");
|
|
1258
|
+
var CLAUDE_INSTALLED_PATH = join4(CLAUDE_PLUGINS_DIR, "installed_plugins.json");
|
|
1259
|
+
var CLAUDE_KNOWN_MARKETPLACES_PATH = join4(CLAUDE_PLUGINS_DIR, "known_marketplaces.json");
|
|
1260
|
+
var CLAUDE_MARKETPLACES_DIR = join4(CLAUDE_PLUGINS_DIR, "marketplaces");
|
|
1261
|
+
function isScopeEntry(entry, scope, cwd) {
|
|
1262
|
+
return entry.scope === scope && (scope === "user" || entry.projectPath === cwd);
|
|
1263
|
+
}
|
|
1264
|
+
async function enableInJsonSettings(path, pluginId) {
|
|
1265
|
+
const settings = await readJson(path);
|
|
1266
|
+
settings.enabledPlugins = settings.enabledPlugins || {};
|
|
1267
|
+
settings.enabledPlugins[pluginId] = true;
|
|
1268
|
+
await writeJson(path, settings);
|
|
1269
|
+
}
|
|
1270
|
+
async function disableInJsonSettings(path, pluginId) {
|
|
1271
|
+
const settings = await readJson(path);
|
|
1272
|
+
if (!settings.enabledPlugins || !(pluginId in settings.enabledPlugins)) return false;
|
|
1273
|
+
delete settings.enabledPlugins[pluginId];
|
|
1274
|
+
await writeJson(path, settings);
|
|
1275
|
+
return true;
|
|
1276
|
+
}
|
|
1277
|
+
var claudeStore = {
|
|
1278
|
+
manifestPaths: manifestsFor(),
|
|
1279
|
+
userGlobal: false,
|
|
1280
|
+
cacheRoot: CLAUDE_CACHE_DIR,
|
|
1281
|
+
cachePath: (marketplace, name, version) => resolveContained(CLAUDE_CACHE_DIR, marketplace, name, version),
|
|
1282
|
+
/**
|
|
1283
|
+
* Claude Code reports a plugin as "orphaned" unless its marketplace is both
|
|
1284
|
+
* recorded and cloned, so this is the one store that fetches a second
|
|
1285
|
+
* repository. The other agents only record the source.
|
|
1286
|
+
*/
|
|
1287
|
+
async ensureMarketplace(marketplace, item) {
|
|
1288
|
+
const known = await readJson(CLAUDE_KNOWN_MARKETPLACES_PATH);
|
|
1289
|
+
if (known[marketplace]) return [];
|
|
1290
|
+
const repo = marketplaceRepo(item);
|
|
1291
|
+
if (!repo) return [];
|
|
1292
|
+
const installLocation = await resolveContained(CLAUDE_MARKETPLACES_DIR, marketplace);
|
|
1293
|
+
const alreadyCloned = await exists(installLocation);
|
|
1294
|
+
return [
|
|
1295
|
+
{
|
|
1296
|
+
path: CLAUDE_KNOWN_MARKETPLACES_PATH,
|
|
1297
|
+
detail: `marketplace "${marketplace}"`,
|
|
1298
|
+
...alreadyCloned ? {} : {
|
|
1299
|
+
creates: {
|
|
1300
|
+
path: installLocation,
|
|
1301
|
+
detail: `git clone https://github.com/${repo}.git`
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1304
|
+
apply: async () => {
|
|
1305
|
+
await mkdir2(CLAUDE_MARKETPLACES_DIR, { recursive: true });
|
|
1306
|
+
if (!await exists(installLocation)) {
|
|
1307
|
+
await promisify(execFile)("git", [
|
|
1308
|
+
"clone",
|
|
1309
|
+
"--depth",
|
|
1310
|
+
"1",
|
|
1311
|
+
`https://github.com/${repo}.git`,
|
|
1312
|
+
installLocation
|
|
1313
|
+
]);
|
|
1314
|
+
}
|
|
1315
|
+
const current = await readJson(
|
|
1316
|
+
CLAUDE_KNOWN_MARKETPLACES_PATH
|
|
1317
|
+
);
|
|
1318
|
+
current[marketplace] = {
|
|
1319
|
+
source: { source: "github", repo },
|
|
1320
|
+
installLocation,
|
|
1321
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
1322
|
+
};
|
|
1323
|
+
await writeJson(CLAUDE_KNOWN_MARKETPLACES_PATH, current);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
];
|
|
1327
|
+
},
|
|
1328
|
+
mutations: (context) => [
|
|
1329
|
+
{
|
|
1330
|
+
path: CLAUDE_INSTALLED_PATH,
|
|
1331
|
+
detail: `plugins["${context.pluginId}"] entry for ${context.scope} scope`,
|
|
1332
|
+
apply: async () => {
|
|
1333
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1334
|
+
const registry = await readJson(CLAUDE_INSTALLED_PATH);
|
|
1335
|
+
registry.version = registry.version || 2;
|
|
1336
|
+
registry.plugins = registry.plugins || {};
|
|
1337
|
+
const entry = {
|
|
1338
|
+
scope: context.scope,
|
|
1339
|
+
...context.scope !== "user" ? { projectPath: context.cwd } : {},
|
|
1340
|
+
installPath: context.cachePath ?? "",
|
|
1341
|
+
version: context.version,
|
|
1342
|
+
installedAt: now,
|
|
1343
|
+
lastUpdated: now,
|
|
1344
|
+
gitCommitSha: context.gitCommitSha
|
|
1345
|
+
};
|
|
1346
|
+
const existing = registry.plugins[context.pluginId] || [];
|
|
1347
|
+
registry.plugins[context.pluginId] = [
|
|
1348
|
+
...existing.filter((candidate) => !isScopeEntry(candidate, context.scope, context.cwd)),
|
|
1349
|
+
entry
|
|
1350
|
+
];
|
|
1351
|
+
await writeJson(CLAUDE_INSTALLED_PATH, registry);
|
|
1352
|
+
}
|
|
1353
|
+
},
|
|
1354
|
+
{
|
|
1355
|
+
path: getSettingsPath(context.scope, context.cwd),
|
|
1356
|
+
detail: `enabledPlugins["${context.pluginId}"] = true`,
|
|
1357
|
+
apply: () => enableInJsonSettings(getSettingsPath(context.scope, context.cwd), context.pluginId)
|
|
1358
|
+
}
|
|
1359
|
+
],
|
|
1360
|
+
async listInstalled(scope, cwd) {
|
|
1361
|
+
const registry = await readJson(CLAUDE_INSTALLED_PATH);
|
|
1362
|
+
if (!registry.plugins) return [];
|
|
1363
|
+
return Object.entries(registry.plugins).filter(([, entries]) => entries.some((entry) => isScopeEntry(entry, scope, cwd))).map(([pluginId]) => pluginId);
|
|
1364
|
+
},
|
|
1365
|
+
async remove(pluginId, scope, cwd) {
|
|
1366
|
+
const registry = await readJson(CLAUDE_INSTALLED_PATH);
|
|
1367
|
+
if (!registry.plugins?.[pluginId]) return false;
|
|
1368
|
+
const entries = registry.plugins[pluginId];
|
|
1369
|
+
const removed = entries.filter((entry) => isScopeEntry(entry, scope, cwd));
|
|
1370
|
+
if (removed.length === 0) return false;
|
|
1371
|
+
const remaining = entries.filter((entry) => !isScopeEntry(entry, scope, cwd));
|
|
1372
|
+
if (remaining.length === 0) delete registry.plugins[pluginId];
|
|
1373
|
+
else registry.plugins[pluginId] = remaining;
|
|
1374
|
+
await writeJson(CLAUDE_INSTALLED_PATH, registry);
|
|
1375
|
+
await disableInJsonSettings(getSettingsPath(scope, cwd), pluginId);
|
|
1376
|
+
for (const installPath of new Set(removed.map((entry) => entry.installPath))) {
|
|
1377
|
+
const stillUsed = Object.values(registry.plugins).some(
|
|
1378
|
+
(list) => list.some((entry) => entry.installPath === installPath)
|
|
1379
|
+
);
|
|
1380
|
+
if (stillUsed || installPath === "") continue;
|
|
1381
|
+
try {
|
|
1382
|
+
await removePathEntry(await resolveContained(CLAUDE_CACHE_DIR, installPath));
|
|
1383
|
+
} catch {
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
return true;
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
var COPILOT_DIR = copilotUserRoot();
|
|
1390
|
+
var COPILOT_SETTINGS_PATH = join4(COPILOT_DIR, "settings.json");
|
|
1391
|
+
var COPILOT_PLUGINS_DIR = join4(COPILOT_DIR, "installed-plugins");
|
|
1392
|
+
var copilotStore = {
|
|
1393
|
+
manifestPaths: manifestsFor(),
|
|
1394
|
+
userGlobal: true,
|
|
1395
|
+
cacheRoot: COPILOT_PLUGINS_DIR,
|
|
1396
|
+
cachePath: (marketplace, name) => resolveContained(COPILOT_PLUGINS_DIR, marketplace, name),
|
|
1397
|
+
async ensureMarketplace(marketplace, item) {
|
|
1398
|
+
const repo = marketplaceRepo(item);
|
|
1399
|
+
if (!repo) return [];
|
|
1400
|
+
const settings = await readJson(COPILOT_SETTINGS_PATH);
|
|
1401
|
+
if (settings.extraKnownMarketplaces?.[marketplace]) return [];
|
|
1402
|
+
return [
|
|
1403
|
+
{
|
|
1404
|
+
path: COPILOT_SETTINGS_PATH,
|
|
1405
|
+
detail: `extraKnownMarketplaces["${marketplace}"] = ${repo}`,
|
|
1406
|
+
apply: async () => {
|
|
1407
|
+
const current = await readJson(COPILOT_SETTINGS_PATH);
|
|
1408
|
+
current.extraKnownMarketplaces = current.extraKnownMarketplaces || {};
|
|
1409
|
+
current.extraKnownMarketplaces[marketplace] = { source: { source: "github", repo } };
|
|
1410
|
+
await writeJson(COPILOT_SETTINGS_PATH, current);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
];
|
|
1414
|
+
},
|
|
1415
|
+
mutations: (context) => [
|
|
1416
|
+
{
|
|
1417
|
+
path: COPILOT_SETTINGS_PATH,
|
|
1418
|
+
detail: `enabledPlugins["${context.pluginId}"] = true`,
|
|
1419
|
+
apply: () => enableInJsonSettings(COPILOT_SETTINGS_PATH, context.pluginId)
|
|
1420
|
+
}
|
|
1421
|
+
],
|
|
1422
|
+
async listInstalled() {
|
|
1423
|
+
const settings = await readJson(COPILOT_SETTINGS_PATH);
|
|
1424
|
+
return Object.entries(settings.enabledPlugins || {}).filter(([, enabled]) => enabled).map(([pluginId]) => pluginId);
|
|
1425
|
+
},
|
|
1426
|
+
async remove(pluginId) {
|
|
1427
|
+
const disabled = await disableInJsonSettings(COPILOT_SETTINGS_PATH, pluginId);
|
|
1428
|
+
if (!disabled) return false;
|
|
1429
|
+
const split = splitPluginId(pluginId);
|
|
1430
|
+
if (split) {
|
|
1431
|
+
try {
|
|
1432
|
+
await removePathEntry(await resolveContained(COPILOT_PLUGINS_DIR, split.marketplace, split.name));
|
|
1433
|
+
} catch {
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
return true;
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
var CODEX_DIR = codexUserRoot();
|
|
1440
|
+
var CODEX_CONFIG_PATH = join4(CODEX_DIR, "config.toml");
|
|
1441
|
+
var CODEX_CACHE_DIR = join4(CODEX_DIR, "plugins", "cache");
|
|
1442
|
+
function toCodexMarketplaceTable(marketplace, url, revision) {
|
|
1443
|
+
return {
|
|
1444
|
+
keyPath: ["marketplaces", marketplace],
|
|
1445
|
+
entries: {
|
|
1446
|
+
source_type: "git",
|
|
1447
|
+
source: url,
|
|
1448
|
+
...revision ? { last_revision: revision } : {},
|
|
1449
|
+
last_updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
function toCodexPluginTable(pluginId) {
|
|
1454
|
+
return { keyPath: ["plugins", pluginId], entries: { enabled: true } };
|
|
1455
|
+
}
|
|
1456
|
+
async function upsertCodex(table) {
|
|
1457
|
+
const text = await readTextOrEmpty2(CODEX_CONFIG_PATH);
|
|
1458
|
+
await writeText(CODEX_CONFIG_PATH, upsertTomlTables(text, table.keyPath, [table]));
|
|
1459
|
+
}
|
|
1460
|
+
var codexStore = {
|
|
1461
|
+
manifestPaths: manifestsFor(".codex-plugin/plugin.json"),
|
|
1462
|
+
userGlobal: true,
|
|
1463
|
+
cacheRoot: CODEX_CACHE_DIR,
|
|
1464
|
+
// Real cache entries on disk are <marketplace>/<name>/<version> — e.g.
|
|
1465
|
+
// claude-plugins-official/claude-code-setup/1.0.0. Dropping the version
|
|
1466
|
+
// segment writes a tree Codex does not look in.
|
|
1467
|
+
cachePath: (marketplace, name, version) => resolveContained(CODEX_CACHE_DIR, marketplace, name, version),
|
|
1468
|
+
async ensureMarketplace(marketplace, item) {
|
|
1469
|
+
const repo = marketplaceRepo(item);
|
|
1470
|
+
if (!repo) return [];
|
|
1471
|
+
const url = `https://github.com/${repo}.git`;
|
|
1472
|
+
return [
|
|
1473
|
+
{
|
|
1474
|
+
path: CODEX_CONFIG_PATH,
|
|
1475
|
+
detail: `[marketplaces.${marketplace}] source = "${url}"`,
|
|
1476
|
+
apply: () => upsertCodex(toCodexMarketplaceTable(marketplace, url, getEffectiveSourceRevision(item) ?? void 0))
|
|
1477
|
+
}
|
|
1478
|
+
];
|
|
1479
|
+
},
|
|
1480
|
+
mutations: (context) => [
|
|
1481
|
+
{
|
|
1482
|
+
path: CODEX_CONFIG_PATH,
|
|
1483
|
+
detail: `[plugins."${context.pluginId}"] enabled = true`,
|
|
1484
|
+
apply: () => upsertCodex(toCodexPluginTable(context.pluginId))
|
|
1485
|
+
}
|
|
1486
|
+
],
|
|
1487
|
+
async listInstalled() {
|
|
1488
|
+
const text = await readTextOrEmpty2(CODEX_CONFIG_PATH);
|
|
1489
|
+
const ids = [];
|
|
1490
|
+
for (const match of text.matchAll(/^\[plugins\."([^"]+)"\]/gm)) {
|
|
1491
|
+
if (match[1]) ids.push(match[1]);
|
|
1492
|
+
}
|
|
1493
|
+
return ids;
|
|
1494
|
+
},
|
|
1495
|
+
async remove(pluginId) {
|
|
1496
|
+
const text = await readTextOrEmpty2(CODEX_CONFIG_PATH);
|
|
1497
|
+
const { text: next, removed } = removeTomlTables(text, ["plugins", pluginId]);
|
|
1498
|
+
if (!removed) return false;
|
|
1499
|
+
await writeText(CODEX_CONFIG_PATH, next);
|
|
1500
|
+
const split = splitPluginId(pluginId);
|
|
1501
|
+
if (split) {
|
|
1502
|
+
try {
|
|
1503
|
+
await removePathEntry(await resolveContained(CODEX_CACHE_DIR, split.marketplace, split.name));
|
|
1504
|
+
} catch {
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
return true;
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
function openCodeConfigPath(scope, cwd) {
|
|
1511
|
+
return scope === "user" ? join4(openCodeUserConfigDir(), "opencode.json") : join4(cwd, "opencode.json");
|
|
1512
|
+
}
|
|
1513
|
+
function toOpenCodePluginSpec(item, name) {
|
|
1514
|
+
const source = item.pluginSource;
|
|
1515
|
+
if (source?.path) {
|
|
1516
|
+
throw new Error(
|
|
1517
|
+
`OpenCode cannot install "${name}": its content is the subdirectory "${source.path}" of ${source.url ?? "its repository"}, and an OpenCode plugin spec cannot name a subdirectory`
|
|
1518
|
+
);
|
|
1519
|
+
}
|
|
1520
|
+
const repo = githubRepoOf(source?.url) ?? githubRepoOf(item.externalUrl);
|
|
1521
|
+
if (!repo) return name;
|
|
1522
|
+
const pinned = source?.sha ?? item.sourceRevision;
|
|
1523
|
+
return `${name}@git+https://github.com/${repo}.git${pinned ? `#${pinned}` : ""}`;
|
|
1524
|
+
}
|
|
1525
|
+
function specName(spec) {
|
|
1526
|
+
const at = spec.indexOf("@", 1);
|
|
1527
|
+
return at > 0 ? spec.slice(0, at) : spec;
|
|
1528
|
+
}
|
|
1529
|
+
var openCodeStore = {
|
|
1530
|
+
manifestPaths: manifestsFor(),
|
|
1531
|
+
userGlobal: false,
|
|
1532
|
+
cacheRoot: null,
|
|
1533
|
+
// OpenCode installs the module from the spec; seedr writes no tree.
|
|
1534
|
+
cachePath: async () => null,
|
|
1535
|
+
mutations: (context) => {
|
|
1536
|
+
const path = openCodeConfigPath(context.scope, context.cwd);
|
|
1537
|
+
const spec = toOpenCodePluginSpec(context.item, context.name);
|
|
1538
|
+
return [
|
|
1539
|
+
{
|
|
1540
|
+
path,
|
|
1541
|
+
detail: `plugin[] += "${spec}"`,
|
|
1542
|
+
apply: async () => {
|
|
1543
|
+
const config = await readJson(path);
|
|
1544
|
+
config.$schema = config.$schema || "https://opencode.ai/config.json";
|
|
1545
|
+
const plugins = config.plugin || [];
|
|
1546
|
+
config.plugin = [...plugins.filter((entry) => specName(entry) !== context.name), spec];
|
|
1547
|
+
await writeJson(path, config);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
];
|
|
1551
|
+
},
|
|
1552
|
+
async listInstalled(scope, cwd) {
|
|
1553
|
+
const config = await readJson(openCodeConfigPath(scope, cwd));
|
|
1554
|
+
return (config.plugin || []).map(specName);
|
|
1555
|
+
},
|
|
1556
|
+
async remove(pluginId, scope, cwd) {
|
|
1557
|
+
const path = openCodeConfigPath(scope, cwd);
|
|
1558
|
+
const config = await readJson(path);
|
|
1559
|
+
const name = splitPluginId(pluginId)?.name ?? pluginId;
|
|
1560
|
+
const plugins = config.plugin || [];
|
|
1561
|
+
const remaining = plugins.filter((entry) => specName(entry) !== name);
|
|
1562
|
+
if (remaining.length === plugins.length) return false;
|
|
1563
|
+
config.plugin = remaining;
|
|
1564
|
+
await writeJson(path, config);
|
|
1565
|
+
return true;
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
var GEMINI_CONFIG_DIR = join4(home, ".gemini", "config");
|
|
1569
|
+
var GEMINI_PLUGINS_DIR = join4(GEMINI_CONFIG_DIR, "plugins");
|
|
1570
|
+
var GEMINI_IMPORT_MANIFEST_PATH = join4(GEMINI_CONFIG_DIR, "import_manifest.json");
|
|
1571
|
+
var antigravityStore = {
|
|
1572
|
+
manifestPaths: manifestsFor(),
|
|
1573
|
+
userGlobal: true,
|
|
1574
|
+
cacheRoot: GEMINI_PLUGINS_DIR,
|
|
1575
|
+
// Antigravity files plugins by name only — it has no marketplace dimension.
|
|
1576
|
+
cachePath: (_marketplace, name) => resolveContained(GEMINI_PLUGINS_DIR, name),
|
|
1577
|
+
/**
|
|
1578
|
+
* Antigravity discovers a plugin by a `plugin.json` at the tree ROOT, and
|
|
1579
|
+
* `.claude-plugin/plugin.json` is not a substitute: the cross-tool matrix
|
|
1580
|
+
* marks it a migration path (`agy plugin import claude`), never a reader.
|
|
1581
|
+
* `agy plugin validate` on a Claude-manifest-only tree answers
|
|
1582
|
+
* "missing plugin.json"; with a root manifest it answers "skills: 1 processed".
|
|
1583
|
+
*
|
|
1584
|
+
* Most plugins in the wild ship only the Claude manifest, so without this the
|
|
1585
|
+
* install lands a directory Antigravity refuses — reporting success while the
|
|
1586
|
+
* plugin does nothing. A tree that already carries its own root manifest
|
|
1587
|
+
* (compound-engineering does) is left exactly as it is.
|
|
1588
|
+
*/
|
|
1589
|
+
async prepareTree(contentPath, { name, version, item }) {
|
|
1590
|
+
const manifestPath = join4(contentPath, "plugin.json");
|
|
1591
|
+
if (await exists(manifestPath)) return;
|
|
1592
|
+
await writeJson(manifestPath, {
|
|
1593
|
+
name,
|
|
1594
|
+
version,
|
|
1595
|
+
...item.description ? { description: item.description } : {}
|
|
1596
|
+
});
|
|
1597
|
+
},
|
|
1598
|
+
mutations: (context) => [
|
|
1599
|
+
{
|
|
1600
|
+
path: GEMINI_IMPORT_MANIFEST_PATH,
|
|
1601
|
+
detail: `imports[] += "${context.name}"`,
|
|
1602
|
+
apply: async () => {
|
|
1603
|
+
const manifest = await readJson(GEMINI_IMPORT_MANIFEST_PATH);
|
|
1604
|
+
const imports = manifest.imports || [];
|
|
1605
|
+
manifest.imports = [
|
|
1606
|
+
...imports.filter((entry) => entry.name !== context.name),
|
|
1607
|
+
{
|
|
1608
|
+
name: context.name,
|
|
1609
|
+
source: "seedr",
|
|
1610
|
+
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1611
|
+
components: ["skills", "hooks"]
|
|
1612
|
+
}
|
|
1613
|
+
];
|
|
1614
|
+
await writeJson(GEMINI_IMPORT_MANIFEST_PATH, manifest);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
],
|
|
1618
|
+
async listInstalled() {
|
|
1619
|
+
const manifest = await readJson(GEMINI_IMPORT_MANIFEST_PATH);
|
|
1620
|
+
return (manifest.imports || []).map((entry) => entry.name);
|
|
1621
|
+
},
|
|
1622
|
+
async remove(pluginId) {
|
|
1623
|
+
const name = splitPluginId(pluginId)?.name ?? pluginId;
|
|
1624
|
+
const manifest = await readJson(GEMINI_IMPORT_MANIFEST_PATH);
|
|
1625
|
+
const imports = manifest.imports || [];
|
|
1626
|
+
const remaining = imports.filter((entry) => entry.name !== name);
|
|
1627
|
+
if (remaining.length === imports.length) return false;
|
|
1628
|
+
manifest.imports = remaining;
|
|
1629
|
+
await writeJson(GEMINI_IMPORT_MANIFEST_PATH, manifest);
|
|
1630
|
+
try {
|
|
1631
|
+
await removePathEntry(await resolveContained(GEMINI_PLUGINS_DIR, name));
|
|
1632
|
+
} catch {
|
|
1633
|
+
}
|
|
1634
|
+
return true;
|
|
1635
|
+
}
|
|
1636
|
+
};
|
|
1637
|
+
var GITHUB_REPO_SLUG_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
1638
|
+
function githubRepoOf(url) {
|
|
1639
|
+
if (!url) return null;
|
|
1640
|
+
const parsed = parseGitHubRepo(url);
|
|
1641
|
+
if (!parsed) return null;
|
|
1642
|
+
const repo = `${parsed.owner}/${parsed.repo}`;
|
|
1643
|
+
if (!GITHUB_REPO_SLUG_PATTERN.test(repo) || repo.includes("..")) return null;
|
|
1644
|
+
return repo;
|
|
1645
|
+
}
|
|
1646
|
+
function marketplaceRepo(item) {
|
|
1647
|
+
return githubRepoOf(item.marketplaceRef?.url ?? item.externalUrl);
|
|
1648
|
+
}
|
|
1649
|
+
var STORES = {
|
|
1650
|
+
claude: claudeStore,
|
|
1651
|
+
copilot: copilotStore,
|
|
1652
|
+
codex: codexStore,
|
|
1653
|
+
opencode: openCodeStore,
|
|
1654
|
+
antigravity: antigravityStore,
|
|
1655
|
+
gemini: antigravityStore
|
|
1656
|
+
};
|
|
1657
|
+
function pluginStoreFor(agent) {
|
|
1658
|
+
const store = STORES[canonicalAgent(agent) ?? agent];
|
|
1659
|
+
if (!store || !isTypeSupported("plugin", agent)) {
|
|
1660
|
+
throw new Error(`Plugins are not supported for ${CODING_AGENTS[agent]?.name ?? agent}`);
|
|
1661
|
+
}
|
|
1662
|
+
return store;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
// src/handlers/plugin.ts
|
|
1666
|
+
var SLUG_LABEL3 = "plugin slug";
|
|
1296
1667
|
function resolveMarketplaceName(item) {
|
|
1297
1668
|
const marketplace = item.marketplace || item.author?.name || "seedr";
|
|
1298
1669
|
assertSafePathSegment(marketplace, "plugin marketplace");
|
|
@@ -1305,48 +1676,29 @@ function resolvePluginIdentity(item, pluginJson) {
|
|
|
1305
1676
|
assertSafePathSegment(version, "plugin version");
|
|
1306
1677
|
return { name, version };
|
|
1307
1678
|
}
|
|
1308
|
-
function
|
|
1309
|
-
const
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
return
|
|
1314
|
-
}
|
|
1315
|
-
function marketplaceRepoUrl(item) {
|
|
1316
|
-
return item.marketplaceRef?.url ?? item.externalUrl;
|
|
1679
|
+
async function readManifestFromDir(dir, manifestPaths) {
|
|
1680
|
+
for (const manifest of manifestPaths) {
|
|
1681
|
+
const path = join5(dir, ...manifest.split("/"));
|
|
1682
|
+
if (await exists(path)) return readJson(path);
|
|
1683
|
+
}
|
|
1684
|
+
return {};
|
|
1317
1685
|
}
|
|
1318
|
-
async function
|
|
1319
|
-
const
|
|
1320
|
-
|
|
1321
|
-
)
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
if (!await exists(installLocation)) {
|
|
1329
|
-
const execFileAsync = promisify(execFile);
|
|
1330
|
-
await execFileAsync("git", [
|
|
1331
|
-
"clone",
|
|
1332
|
-
"--depth",
|
|
1333
|
-
"1",
|
|
1334
|
-
`https://github.com/${repo}.git`,
|
|
1335
|
-
installLocation
|
|
1336
|
-
]);
|
|
1337
|
-
}
|
|
1338
|
-
known[marketplace] = {
|
|
1339
|
-
source: { source: "github", repo },
|
|
1340
|
-
installLocation,
|
|
1341
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
1342
|
-
};
|
|
1343
|
-
await writeJson(KNOWN_MARKETPLACES_PATH, known);
|
|
1686
|
+
async function readManifestForPlan(item, manifestPaths) {
|
|
1687
|
+
const sourcePath = getItemSourcePath(item);
|
|
1688
|
+
if (sourcePath) return readManifestFromDir(sourcePath, manifestPaths);
|
|
1689
|
+
for (const manifest of manifestPaths) {
|
|
1690
|
+
try {
|
|
1691
|
+
return JSON.parse(await fetchItemFile(item, manifest));
|
|
1692
|
+
} catch {
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
return {};
|
|
1344
1696
|
}
|
|
1345
|
-
async function createStagingDir(slug) {
|
|
1346
|
-
const tmpRoot = await resolveContained(
|
|
1347
|
-
await
|
|
1348
|
-
const staging = await mkdtemp2(
|
|
1349
|
-
return resolveContained(
|
|
1697
|
+
async function createStagingDir(cacheRoot, slug) {
|
|
1698
|
+
const tmpRoot = await resolveContained(cacheRoot, ".tmp");
|
|
1699
|
+
await mkdir3(tmpRoot, { recursive: true });
|
|
1700
|
+
const staging = await mkdtemp2(join5(tmpRoot, `${slug}-`));
|
|
1701
|
+
return resolveContained(cacheRoot, relative3(cacheRoot, staging));
|
|
1350
1702
|
}
|
|
1351
1703
|
async function stageContent(item, contentPath) {
|
|
1352
1704
|
const sourcePath = getItemSourcePath(item);
|
|
@@ -1365,37 +1717,6 @@ function resolveGitCommitSha(item, fetched) {
|
|
|
1365
1717
|
}
|
|
1366
1718
|
return sha;
|
|
1367
1719
|
}
|
|
1368
|
-
function isScopeEntry(entry, scope, cwd) {
|
|
1369
|
-
return entry.scope === scope && (scope === "user" || entry.projectPath === cwd);
|
|
1370
|
-
}
|
|
1371
|
-
async function writeInstalledEntry(params) {
|
|
1372
|
-
const { cachePath, pluginId, version, gitCommitSha, scope, cwd } = params;
|
|
1373
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1374
|
-
const registry = await readJson(INSTALLED_PLUGINS_PATH);
|
|
1375
|
-
registry.version = registry.version || 2;
|
|
1376
|
-
registry.plugins = registry.plugins || {};
|
|
1377
|
-
const installInfo = {
|
|
1378
|
-
scope,
|
|
1379
|
-
...scope !== "user" ? { projectPath: cwd } : {},
|
|
1380
|
-
installPath: cachePath,
|
|
1381
|
-
version,
|
|
1382
|
-
installedAt: now,
|
|
1383
|
-
lastUpdated: now,
|
|
1384
|
-
gitCommitSha
|
|
1385
|
-
};
|
|
1386
|
-
const existingEntries = registry.plugins[pluginId] || [];
|
|
1387
|
-
registry.plugins[pluginId] = [
|
|
1388
|
-
...existingEntries.filter((entry) => !isScopeEntry(entry, scope, cwd)),
|
|
1389
|
-
installInfo
|
|
1390
|
-
];
|
|
1391
|
-
await writeJson(INSTALLED_PLUGINS_PATH, registry);
|
|
1392
|
-
}
|
|
1393
|
-
async function enableInSettings(settingsPath, pluginId) {
|
|
1394
|
-
const settings = await readJson(settingsPath);
|
|
1395
|
-
settings.enabledPlugins = settings.enabledPlugins || {};
|
|
1396
|
-
settings.enabledPlugins[pluginId] = true;
|
|
1397
|
-
await writeJson(settingsPath, settings);
|
|
1398
|
-
}
|
|
1399
1720
|
async function rollback(undo, error2) {
|
|
1400
1721
|
const failures = [];
|
|
1401
1722
|
for (const step2 of undo.reverse()) {
|
|
@@ -1411,70 +1732,78 @@ async function rollback(undo, error2) {
|
|
|
1411
1732
|
}
|
|
1412
1733
|
throw error2;
|
|
1413
1734
|
}
|
|
1414
|
-
async function commitInstall(
|
|
1415
|
-
const { contentPath, cachePath, pluginId, scope, cwd } = params;
|
|
1735
|
+
async function commitInstall(contentPath, cachePath, mutations) {
|
|
1416
1736
|
const undo = [];
|
|
1417
1737
|
try {
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1738
|
+
if (contentPath && cachePath) {
|
|
1739
|
+
await removePathEntry(cachePath);
|
|
1740
|
+
await moveDirectory(contentPath, cachePath);
|
|
1741
|
+
undo.push(() => removePathEntry(cachePath).then(() => void 0));
|
|
1742
|
+
}
|
|
1743
|
+
for (const mutation of mutations) {
|
|
1744
|
+
const snapshot = await snapshotFile(mutation.path);
|
|
1745
|
+
await mutation.apply();
|
|
1746
|
+
undo.push(() => restoreFile(mutation.path, snapshot));
|
|
1747
|
+
}
|
|
1428
1748
|
} catch (error2) {
|
|
1429
1749
|
await rollback(undo, error2);
|
|
1430
1750
|
}
|
|
1431
1751
|
}
|
|
1432
1752
|
async function installPluginForAgent(item, agent, scope, _method, force, cwd) {
|
|
1433
|
-
const spinner = ora6(
|
|
1434
|
-
`Installing ${item.name} for ${CODING_AGENTS[agent].name}...`
|
|
1435
|
-
).start();
|
|
1753
|
+
const spinner = ora6(`Installing ${item.name} for ${CODING_AGENTS[agent].name}...`).start();
|
|
1436
1754
|
try {
|
|
1437
|
-
|
|
1438
|
-
throw new Error(CLAUDE_ONLY_ERROR3);
|
|
1439
|
-
}
|
|
1755
|
+
const store = pluginStoreFor(agent);
|
|
1440
1756
|
assertValidSlug(item.slug, SLUG_LABEL3);
|
|
1441
1757
|
const marketplace = resolveMarketplaceName(item);
|
|
1442
|
-
const
|
|
1758
|
+
const cacheRoot = store.cacheRoot;
|
|
1759
|
+
const staging = cacheRoot ? await createStagingDir(cacheRoot, item.slug) : null;
|
|
1443
1760
|
try {
|
|
1444
|
-
const contentPath =
|
|
1445
|
-
const fetched = await stageContent(item, contentPath);
|
|
1446
|
-
const pluginJson = await
|
|
1761
|
+
const contentPath = staging ? join5(staging, "content") : null;
|
|
1762
|
+
const fetched = contentPath ? await stageContent(item, contentPath) : null;
|
|
1763
|
+
const pluginJson = contentPath ? await readManifestFromDir(contentPath, store.manifestPaths) : await readManifestForPlan(item, store.manifestPaths);
|
|
1447
1764
|
const { name, version } = resolvePluginIdentity(item, pluginJson);
|
|
1765
|
+
if (contentPath && store.prepareTree) {
|
|
1766
|
+
await store.prepareTree(contentPath, { name, version, item });
|
|
1767
|
+
}
|
|
1448
1768
|
const gitCommitSha = resolveGitCommitSha(item, fetched);
|
|
1449
1769
|
const pluginId = getPluginId(name, marketplace);
|
|
1450
|
-
await
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1770
|
+
const cachePath = await store.cachePath(marketplace, name, version);
|
|
1771
|
+
const marketplaceMutations = store.ensureMarketplace ? await store.ensureMarketplace(marketplace, item) : [];
|
|
1772
|
+
const context = {
|
|
1773
|
+
item,
|
|
1774
|
+
marketplace,
|
|
1775
|
+
name,
|
|
1776
|
+
version,
|
|
1777
|
+
pluginId,
|
|
1778
|
+
cachePath,
|
|
1779
|
+
gitCommitSha,
|
|
1780
|
+
scope,
|
|
1781
|
+
cwd
|
|
1782
|
+
};
|
|
1783
|
+
if (cachePath) await assertOverwritable(cachePath, force);
|
|
1784
|
+
await commitInstall(contentPath, cachePath, [
|
|
1785
|
+
...marketplaceMutations,
|
|
1786
|
+
...store.mutations(context)
|
|
1787
|
+
]);
|
|
1788
|
+
spinner.succeed(brand(`Installed ${item.name} for ${CODING_AGENTS[agent].name}`));
|
|
1789
|
+
return { agent, success: true, path: cachePath ?? "" };
|
|
1458
1790
|
} finally {
|
|
1459
|
-
await rm2(staging, { recursive: true, force: true });
|
|
1791
|
+
if (staging) await rm2(staging, { recursive: true, force: true });
|
|
1460
1792
|
}
|
|
1461
1793
|
} catch (error2) {
|
|
1462
1794
|
const errorMsg = error2 instanceof Error ? error2.message : "Unknown error";
|
|
1463
|
-
spinner.fail(
|
|
1464
|
-
chalk7.red(`Failed to install for ${CODING_AGENTS[agent].name}: ${errorMsg}`)
|
|
1465
|
-
);
|
|
1795
|
+
spinner.fail(chalk7.red(`Failed to install for ${CODING_AGENTS[agent].name}: ${errorMsg}`));
|
|
1466
1796
|
return { agent, success: false, path: "", error: errorMsg };
|
|
1467
1797
|
}
|
|
1468
1798
|
}
|
|
1469
1799
|
async function installPlugin(item, agents, scope, method, force, cwd = process.cwd()) {
|
|
1470
1800
|
const results = [];
|
|
1471
1801
|
for (const agent of agents) {
|
|
1472
|
-
|
|
1473
|
-
results.push(result);
|
|
1802
|
+
results.push(await installPluginForAgent(item, agent, scope, method, force, cwd));
|
|
1474
1803
|
}
|
|
1475
1804
|
return results;
|
|
1476
1805
|
}
|
|
1477
|
-
async function findInstalledPluginId(
|
|
1806
|
+
async function findInstalledPluginId(installed, slug) {
|
|
1478
1807
|
let registryMarketplace = null;
|
|
1479
1808
|
try {
|
|
1480
1809
|
const item = await getItem(slug, "plugin");
|
|
@@ -1484,70 +1813,34 @@ async function findInstalledPluginId(registry, slug) {
|
|
|
1484
1813
|
}
|
|
1485
1814
|
if (registryMarketplace !== null) {
|
|
1486
1815
|
const exact = getPluginId(slug, registryMarketplace);
|
|
1487
|
-
|
|
1816
|
+
if (installed.includes(exact)) return exact;
|
|
1817
|
+
return installed.includes(slug) ? slug : null;
|
|
1488
1818
|
}
|
|
1489
|
-
const candidates =
|
|
1819
|
+
const candidates = installed.filter((id) => (splitPluginId(id)?.name ?? id) === slug);
|
|
1490
1820
|
return candidates.length === 1 ? candidates[0] : null;
|
|
1491
1821
|
}
|
|
1492
|
-
async function
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
);
|
|
1496
|
-
if (stillReferenced) return;
|
|
1497
|
-
let contained;
|
|
1822
|
+
async function uninstallPlugin(slug, agent, scope, cwd = process.cwd()) {
|
|
1823
|
+
assertValidSlug(slug, SLUG_LABEL3);
|
|
1824
|
+
let store;
|
|
1498
1825
|
try {
|
|
1499
|
-
|
|
1826
|
+
store = pluginStoreFor(agent);
|
|
1500
1827
|
} catch {
|
|
1501
|
-
return;
|
|
1828
|
+
return false;
|
|
1502
1829
|
}
|
|
1503
|
-
await
|
|
1504
|
-
|
|
1505
|
-
async function uninstallPlugin(slug, agent, scope, cwd = process.cwd()) {
|
|
1506
|
-
assertValidSlug(slug, SLUG_LABEL3);
|
|
1507
|
-
if (agent !== "claude") return false;
|
|
1508
|
-
const registry = await readJson(INSTALLED_PLUGINS_PATH);
|
|
1509
|
-
if (!registry.plugins) return false;
|
|
1510
|
-
const pluginId = await findInstalledPluginId(registry, slug);
|
|
1830
|
+
const installed = await store.listInstalled(scope, cwd);
|
|
1831
|
+
const pluginId = await findInstalledPluginId(installed, slug);
|
|
1511
1832
|
if (!pluginId) return false;
|
|
1512
|
-
|
|
1513
|
-
const removedEntries = entries.filter((entry) => isScopeEntry(entry, scope, cwd));
|
|
1514
|
-
if (removedEntries.length === 0) return false;
|
|
1515
|
-
const remaining = entries.filter((entry) => !isScopeEntry(entry, scope, cwd));
|
|
1516
|
-
if (remaining.length === 0) {
|
|
1517
|
-
delete registry.plugins[pluginId];
|
|
1518
|
-
} else {
|
|
1519
|
-
registry.plugins[pluginId] = remaining;
|
|
1520
|
-
}
|
|
1521
|
-
await writeJson(INSTALLED_PLUGINS_PATH, registry);
|
|
1522
|
-
const settingsPath = getSettingsPath(scope, cwd);
|
|
1523
|
-
const settings = await readJson(settingsPath);
|
|
1524
|
-
if (settings.enabledPlugins && pluginId in settings.enabledPlugins) {
|
|
1525
|
-
delete settings.enabledPlugins[pluginId];
|
|
1526
|
-
await writeJson(settingsPath, settings);
|
|
1527
|
-
}
|
|
1528
|
-
for (const installPath of new Set(removedEntries.map((entry) => entry.installPath))) {
|
|
1529
|
-
await removeOrphanedCache(installPath, registry);
|
|
1530
|
-
}
|
|
1531
|
-
return true;
|
|
1833
|
+
return store.remove(pluginId, scope, cwd);
|
|
1532
1834
|
}
|
|
1533
1835
|
async function getInstalledPlugins(agent, scope, cwd = process.cwd()) {
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
if (entries.some((entry) => isScopeEntry(entry, scope, cwd))) {
|
|
1540
|
-
installed.push(splitPluginId(pluginId)?.name ?? pluginId);
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
return installed;
|
|
1544
|
-
}
|
|
1545
|
-
async function readPluginJsonForPlan(item) {
|
|
1546
|
-
const sourcePath = getItemSourcePath(item);
|
|
1547
|
-
if (sourcePath) {
|
|
1548
|
-
return readJson(join4(sourcePath, ...PLUGIN_JSON_PATH.split("/")));
|
|
1836
|
+
let store;
|
|
1837
|
+
try {
|
|
1838
|
+
store = pluginStoreFor(agent);
|
|
1839
|
+
} catch {
|
|
1840
|
+
return [];
|
|
1549
1841
|
}
|
|
1550
|
-
|
|
1842
|
+
const installed = await store.listInstalled(scope, cwd);
|
|
1843
|
+
return installed.map((pluginId) => splitPluginId(pluginId)?.name ?? pluginId);
|
|
1551
1844
|
}
|
|
1552
1845
|
async function kindFor(path) {
|
|
1553
1846
|
return await exists(path) ? "modify" : "create";
|
|
@@ -1555,40 +1848,53 @@ async function kindFor(path) {
|
|
|
1555
1848
|
async function planPlugin(item, agents, scope, _method, cwd) {
|
|
1556
1849
|
const changes = [];
|
|
1557
1850
|
for (const agent of agents) {
|
|
1558
|
-
|
|
1851
|
+
const store = pluginStoreFor(agent);
|
|
1559
1852
|
assertValidSlug(item.slug, SLUG_LABEL3);
|
|
1560
1853
|
const marketplace = resolveMarketplaceName(item);
|
|
1561
|
-
const { name, version } = resolvePluginIdentity(
|
|
1854
|
+
const { name, version } = resolvePluginIdentity(
|
|
1855
|
+
item,
|
|
1856
|
+
await readManifestForPlan(item, store.manifestPaths)
|
|
1857
|
+
);
|
|
1562
1858
|
const pluginId = getPluginId(name, marketplace);
|
|
1563
|
-
const cachePath = await
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
{
|
|
1567
|
-
{
|
|
1859
|
+
const cachePath = await store.cachePath(marketplace, name, version);
|
|
1860
|
+
resolveGitCommitSha(item, null);
|
|
1861
|
+
if (cachePath) {
|
|
1862
|
+
changes.push({
|
|
1568
1863
|
agent,
|
|
1569
|
-
kind: await kindFor(
|
|
1570
|
-
path:
|
|
1571
|
-
detail:
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
const
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1864
|
+
kind: await kindFor(cachePath),
|
|
1865
|
+
path: cachePath,
|
|
1866
|
+
detail: "plugin files (digest-verified download)"
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
const marketplaceMutations = store.ensureMarketplace ? await store.ensureMarketplace(marketplace, item) : [];
|
|
1870
|
+
const context = {
|
|
1871
|
+
item,
|
|
1872
|
+
marketplace,
|
|
1873
|
+
name,
|
|
1874
|
+
version,
|
|
1875
|
+
pluginId,
|
|
1876
|
+
cachePath,
|
|
1877
|
+
gitCommitSha: resolveGitCommitSha(item, null),
|
|
1878
|
+
scope,
|
|
1879
|
+
cwd
|
|
1880
|
+
};
|
|
1881
|
+
for (const mutation of [...marketplaceMutations, ...store.mutations(context)]) {
|
|
1882
|
+
changes.push({
|
|
1883
|
+
agent,
|
|
1884
|
+
kind: await kindFor(mutation.path),
|
|
1885
|
+
path: mutation.path,
|
|
1886
|
+
detail: store.userGlobal ? `${mutation.detail} \u2014 user-global, ${CODING_AGENTS[agent].name} has no project-scoped plugin store` : mutation.detail
|
|
1887
|
+
});
|
|
1888
|
+
if (mutation.creates) {
|
|
1889
|
+
changes.push({ agent, kind: "create", ...mutation.creates });
|
|
1584
1890
|
}
|
|
1585
1891
|
}
|
|
1586
|
-
changes.push({ agent, kind: await kindFor(settingsPath), path: settingsPath, detail: `enabledPlugins["${pluginId}"] = true` });
|
|
1587
1892
|
}
|
|
1588
1893
|
return changes;
|
|
1589
1894
|
}
|
|
1590
1895
|
var pluginHandler = {
|
|
1591
1896
|
type: "plugin",
|
|
1897
|
+
honoursMethod: false,
|
|
1592
1898
|
async install(item, agents, scope, method, force, cwd) {
|
|
1593
1899
|
return installPlugin(item, agents, scope, method, force, cwd);
|
|
1594
1900
|
},
|
|
@@ -1601,6 +1907,216 @@ var pluginHandler = {
|
|
|
1601
1907
|
plan: planPlugin
|
|
1602
1908
|
};
|
|
1603
1909
|
|
|
1910
|
+
// src/handlers/rule.ts
|
|
1911
|
+
import { dirname as dirname3 } from "path";
|
|
1912
|
+
import { readdir as readdir3, readFile as readFile4, writeFile as writeFile2, mkdir as mkdir4 } from "fs/promises";
|
|
1913
|
+
import chalk8 from "chalk";
|
|
1914
|
+
import ora7 from "ora";
|
|
1915
|
+
|
|
1916
|
+
// src/handlers/ruleTargets.ts
|
|
1917
|
+
import { homedir as homedir5 } from "os";
|
|
1918
|
+
import { join as join6 } from "path";
|
|
1919
|
+
var home2 = homedir5();
|
|
1920
|
+
var markdownFile = (slug) => `${slug}.md`;
|
|
1921
|
+
var claudeTarget = {
|
|
1922
|
+
kind: "file",
|
|
1923
|
+
dir: (scope, cwd) => join6(scope === "user" ? claudeUserRoot() : join6(cwd, ".claude"), "rules"),
|
|
1924
|
+
fileName: markdownFile,
|
|
1925
|
+
keepsFrontmatter: true
|
|
1926
|
+
};
|
|
1927
|
+
var antigravityTarget = {
|
|
1928
|
+
kind: "file",
|
|
1929
|
+
dir: (scope, cwd) => join6(scope === "user" ? join6(home2, ".gemini", "config") : join6(cwd, ".agents"), "rules"),
|
|
1930
|
+
fileName: markdownFile,
|
|
1931
|
+
keepsFrontmatter: true
|
|
1932
|
+
};
|
|
1933
|
+
var copilotTarget = {
|
|
1934
|
+
kind: "file",
|
|
1935
|
+
dir: (scope, cwd) => join6(scope === "user" ? copilotUserRoot() : join6(cwd, ".github"), "instructions"),
|
|
1936
|
+
fileName: (slug) => `${slug}.instructions.md`,
|
|
1937
|
+
keepsFrontmatter: true
|
|
1938
|
+
};
|
|
1939
|
+
var codexTarget = {
|
|
1940
|
+
kind: "section",
|
|
1941
|
+
file: (scope, cwd) => scope === "user" ? join6(codexUserRoot(), "AGENTS.md") : join6(cwd, "AGENTS.md")
|
|
1942
|
+
};
|
|
1943
|
+
var openCodeTarget = {
|
|
1944
|
+
kind: "section",
|
|
1945
|
+
file: (scope, cwd) => scope === "user" ? join6(openCodeUserConfigDir(), "AGENTS.md") : join6(cwd, "AGENTS.md")
|
|
1946
|
+
};
|
|
1947
|
+
var TARGETS = {
|
|
1948
|
+
claude: claudeTarget,
|
|
1949
|
+
antigravity: antigravityTarget,
|
|
1950
|
+
gemini: antigravityTarget,
|
|
1951
|
+
copilot: copilotTarget,
|
|
1952
|
+
codex: codexTarget,
|
|
1953
|
+
opencode: openCodeTarget
|
|
1954
|
+
};
|
|
1955
|
+
function ruleTargetFor(agent) {
|
|
1956
|
+
const target = TARGETS[canonicalAgent(agent) ?? agent];
|
|
1957
|
+
if (!target || !isTypeSupported("rule", agent)) {
|
|
1958
|
+
throw new Error(`Rules are not supported for ${CODING_AGENTS[agent]?.name ?? agent}`);
|
|
1959
|
+
}
|
|
1960
|
+
return target;
|
|
1961
|
+
}
|
|
1962
|
+
var sectionStart = (slug) => `<!-- seedr:rule:${slug} -->`;
|
|
1963
|
+
var sectionEnd = (slug) => `<!-- /seedr:rule:${slug} -->`;
|
|
1964
|
+
function sectionPattern(slug) {
|
|
1965
|
+
const escaped = slug.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1966
|
+
return new RegExp(`\\n*<!-- seedr:rule:${escaped} -->[\\s\\S]*?<!-- /seedr:rule:${escaped} -->\\n*`, "g");
|
|
1967
|
+
}
|
|
1968
|
+
function stripFrontmatter(content) {
|
|
1969
|
+
const match = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/.exec(content);
|
|
1970
|
+
return match ? content.slice(match[0].length) : content;
|
|
1971
|
+
}
|
|
1972
|
+
function upsertSection(document, slug, content) {
|
|
1973
|
+
const block = `${sectionStart(slug)}
|
|
1974
|
+
${stripFrontmatter(content).trim()}
|
|
1975
|
+
${sectionEnd(slug)}`;
|
|
1976
|
+
const existing = sectionPattern(slug);
|
|
1977
|
+
if (existing.test(document)) {
|
|
1978
|
+
return document.replace(sectionPattern(slug), `
|
|
1979
|
+
|
|
1980
|
+
${block}
|
|
1981
|
+
`);
|
|
1982
|
+
}
|
|
1983
|
+
const base = document.trimEnd();
|
|
1984
|
+
return base.length > 0 ? `${base}
|
|
1985
|
+
|
|
1986
|
+
${block}
|
|
1987
|
+
` : `${block}
|
|
1988
|
+
`;
|
|
1989
|
+
}
|
|
1990
|
+
function removeSection(document, slug) {
|
|
1991
|
+
if (!sectionPattern(slug).test(document)) return null;
|
|
1992
|
+
const stripped = document.replace(sectionPattern(slug), "\n\n").trimEnd();
|
|
1993
|
+
return stripped.length > 0 ? `${stripped}
|
|
1994
|
+
` : "";
|
|
1995
|
+
}
|
|
1996
|
+
function listSections(document) {
|
|
1997
|
+
return [...document.matchAll(/<!-- seedr:rule:([^\s>]+) -->/g)].map((match) => match[1]);
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
// src/handlers/rule.ts
|
|
2001
|
+
var SLUG_LABEL4 = "rule slug";
|
|
2002
|
+
var RULE_FILE = "rule.md";
|
|
2003
|
+
async function readTextOrEmpty3(path) {
|
|
2004
|
+
try {
|
|
2005
|
+
return await readFile4(path, "utf-8");
|
|
2006
|
+
} catch {
|
|
2007
|
+
return "";
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
async function readRuleContent(item) {
|
|
2011
|
+
const sourcePath = getItemSourcePath(item);
|
|
2012
|
+
if (sourcePath) {
|
|
2013
|
+
const local = await readTextOrEmpty3(`${sourcePath}/${RULE_FILE}`);
|
|
2014
|
+
if (local) return local;
|
|
2015
|
+
}
|
|
2016
|
+
return fetchItemFile(item, RULE_FILE);
|
|
2017
|
+
}
|
|
2018
|
+
async function destinationFor(target, slug, scope, cwd) {
|
|
2019
|
+
if (target.kind === "section") return target.file(scope, cwd);
|
|
2020
|
+
const dir = target.dir(scope, cwd);
|
|
2021
|
+
return resolveContained(dir, target.fileName(slug));
|
|
2022
|
+
}
|
|
2023
|
+
async function installRuleForAgent(item, agent, scope, force, cwd, content) {
|
|
2024
|
+
const spinner = ora7(`Installing ${item.name} for ${CODING_AGENTS[agent].name}...`).start();
|
|
2025
|
+
try {
|
|
2026
|
+
const target = ruleTargetFor(agent);
|
|
2027
|
+
const destination = await destinationFor(target, item.slug, scope, cwd);
|
|
2028
|
+
await mkdir4(dirname3(destination), { recursive: true });
|
|
2029
|
+
if (target.kind === "file") {
|
|
2030
|
+
await assertOverwritable(destination, force);
|
|
2031
|
+
await writeFile2(
|
|
2032
|
+
destination,
|
|
2033
|
+
target.keepsFrontmatter ? content : stripFrontmatter(content),
|
|
2034
|
+
"utf-8"
|
|
2035
|
+
);
|
|
2036
|
+
} else {
|
|
2037
|
+
const document = await readTextOrEmpty3(destination);
|
|
2038
|
+
await writeFile2(destination, upsertSection(document, item.slug, content), "utf-8");
|
|
2039
|
+
}
|
|
2040
|
+
spinner.succeed(brand(`Installed ${item.name} for ${CODING_AGENTS[agent].name}`));
|
|
2041
|
+
return { agent, success: true, path: destination };
|
|
2042
|
+
} catch (error2) {
|
|
2043
|
+
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
2044
|
+
spinner.fail(chalk8.red(`Failed to install for ${CODING_AGENTS[agent].name}: ${message}`));
|
|
2045
|
+
return { agent, success: false, path: "", error: message };
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
async function installRule(item, agents, scope, _method, force, cwd = process.cwd()) {
|
|
2049
|
+
assertValidSlug(item.slug, SLUG_LABEL4);
|
|
2050
|
+
const content = await readRuleContent(item);
|
|
2051
|
+
const results = [];
|
|
2052
|
+
for (const agent of agents) {
|
|
2053
|
+
results.push(await installRuleForAgent(item, agent, scope, force, cwd, content));
|
|
2054
|
+
}
|
|
2055
|
+
return results;
|
|
2056
|
+
}
|
|
2057
|
+
async function uninstallRule(slug, agent, scope, cwd = process.cwd()) {
|
|
2058
|
+
assertValidSlug(slug, SLUG_LABEL4);
|
|
2059
|
+
let target;
|
|
2060
|
+
try {
|
|
2061
|
+
target = ruleTargetFor(agent);
|
|
2062
|
+
} catch {
|
|
2063
|
+
return false;
|
|
2064
|
+
}
|
|
2065
|
+
const destination = await destinationFor(target, slug, scope, cwd);
|
|
2066
|
+
if (target.kind === "file") return removePathEntry(destination);
|
|
2067
|
+
const document = await readTextOrEmpty3(destination);
|
|
2068
|
+
const stripped = removeSection(document, slug);
|
|
2069
|
+
if (stripped === null) return false;
|
|
2070
|
+
await writeFile2(destination, stripped, "utf-8");
|
|
2071
|
+
return true;
|
|
2072
|
+
}
|
|
2073
|
+
async function getInstalledRules(agent, scope, cwd = process.cwd()) {
|
|
2074
|
+
let target;
|
|
2075
|
+
try {
|
|
2076
|
+
target = ruleTargetFor(agent);
|
|
2077
|
+
} catch {
|
|
2078
|
+
return [];
|
|
2079
|
+
}
|
|
2080
|
+
if (target.kind === "section") {
|
|
2081
|
+
return listSections(await readTextOrEmpty3(target.file(scope, cwd)));
|
|
2082
|
+
}
|
|
2083
|
+
const dir = target.dir(scope, cwd);
|
|
2084
|
+
if (!await exists(dir)) return [];
|
|
2085
|
+
const suffix = target.fileName("");
|
|
2086
|
+
const entries = await readdir3(dir, { withFileTypes: true });
|
|
2087
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(suffix) && !entry.name.startsWith(".")).map((entry) => entry.name.slice(0, -suffix.length));
|
|
2088
|
+
}
|
|
2089
|
+
async function planRule(item, agents, scope, _method, cwd) {
|
|
2090
|
+
assertValidSlug(item.slug, SLUG_LABEL4);
|
|
2091
|
+
const changes = [];
|
|
2092
|
+
for (const agent of agents) {
|
|
2093
|
+
const target = ruleTargetFor(agent);
|
|
2094
|
+
const destination = await destinationFor(target, item.slug, scope, cwd);
|
|
2095
|
+
const present = await exists(destination);
|
|
2096
|
+
changes.push({
|
|
2097
|
+
agent,
|
|
2098
|
+
kind: present ? "modify" : "create",
|
|
2099
|
+
path: destination,
|
|
2100
|
+
detail: target.kind === "file" ? "rule file" : `merged section <!-- seedr:rule:${item.slug} --> (the rest of the file is untouched)`
|
|
2101
|
+
});
|
|
2102
|
+
}
|
|
2103
|
+
return changes;
|
|
2104
|
+
}
|
|
2105
|
+
var ruleHandler = {
|
|
2106
|
+
type: "rule",
|
|
2107
|
+
honoursMethod: false,
|
|
2108
|
+
async install(item, agents, scope, method, force, cwd) {
|
|
2109
|
+
return installRule(item, agents, scope, method, force, cwd);
|
|
2110
|
+
},
|
|
2111
|
+
async uninstall(slug, agent, scope, cwd) {
|
|
2112
|
+
return uninstallRule(slug, agent, scope, cwd);
|
|
2113
|
+
},
|
|
2114
|
+
async listInstalled(agent, scope, cwd) {
|
|
2115
|
+
return getInstalledRules(agent, scope, cwd);
|
|
2116
|
+
},
|
|
2117
|
+
plan: planRule
|
|
2118
|
+
};
|
|
2119
|
+
|
|
1604
2120
|
// src/handlers/index.ts
|
|
1605
2121
|
registerHandler(skillHandler);
|
|
1606
2122
|
registerHandler(agentHandler);
|
|
@@ -1609,6 +2125,7 @@ registerHandler(hookHandler);
|
|
|
1609
2125
|
registerHandler(mcpHandler);
|
|
1610
2126
|
registerHandler(settingsHandler);
|
|
1611
2127
|
registerHandler(pluginHandler);
|
|
2128
|
+
registerHandler(ruleHandler);
|
|
1612
2129
|
|
|
1613
2130
|
// src/commands/add.ts
|
|
1614
2131
|
var CANCELLED_MESSAGE = "Operation cancelled";
|
|
@@ -1689,8 +2206,8 @@ function formatPlan(changes) {
|
|
|
1689
2206
|
for (const [label, group] of groups) {
|
|
1690
2207
|
lines.push(` ${label}:`);
|
|
1691
2208
|
for (const change of group) {
|
|
1692
|
-
const detail = change.detail ? ` ${
|
|
1693
|
-
lines.push(` ${
|
|
2209
|
+
const detail = change.detail ? ` ${chalk9.gray(`\u2014 ${change.detail}`)}` : "";
|
|
2210
|
+
lines.push(` ${chalk9.gray(`[${KIND_LABEL[change.kind]}]`)} ${change.path}${detail}`);
|
|
1694
2211
|
}
|
|
1695
2212
|
}
|
|
1696
2213
|
return lines;
|
|
@@ -1800,13 +2317,13 @@ function printInstallSummary(results) {
|
|
|
1800
2317
|
if (successful.length > 0) {
|
|
1801
2318
|
success(`Installed for ${successful.length} agent(s)`);
|
|
1802
2319
|
for (const r of successful) {
|
|
1803
|
-
console.log(
|
|
2320
|
+
console.log(chalk9.gray(` \u2192 ${r.path}`));
|
|
1804
2321
|
}
|
|
1805
2322
|
}
|
|
1806
2323
|
if (failed.length > 0) {
|
|
1807
2324
|
error(`Failed for ${failed.length} agent(s)`);
|
|
1808
2325
|
for (const r of failed) {
|
|
1809
|
-
console.log(
|
|
2326
|
+
console.log(chalk9.red(` \xD7 ${r.agent}: ${r.error}`));
|
|
1810
2327
|
}
|
|
1811
2328
|
}
|
|
1812
2329
|
return exitCode;
|
|
@@ -1820,7 +2337,7 @@ async function runAdd(name, options, cwd = process.cwd()) {
|
|
|
1820
2337
|
const item = await resolveItem(name, options.type);
|
|
1821
2338
|
if (item === CANCELLED) return 0;
|
|
1822
2339
|
if (!item) return 1;
|
|
1823
|
-
step(`Selected: ${brand(item.name)} ${
|
|
2340
|
+
step(`Selected: ${brand(item.name)} ${chalk9.gray(`(${item.type})`)} ${chalk9.gray(`- ${item.description}`)}`);
|
|
1824
2341
|
const handler = getHandler(item.type);
|
|
1825
2342
|
if (!handler) {
|
|
1826
2343
|
error(`No handler found for type "${item.type}"`);
|
|
@@ -1835,10 +2352,24 @@ async function runAdd(name, options, cwd = process.cwd()) {
|
|
|
1835
2352
|
step(`Scope: ${brand(scope)}`);
|
|
1836
2353
|
const method = await chooseMethod(options, item, agents, scope, cwd);
|
|
1837
2354
|
if (method === CANCELLED) return 0;
|
|
1838
|
-
|
|
2355
|
+
if (method === "symlink" && handler.honoursMethod === false) {
|
|
2356
|
+
step(`Method: ${brand("copy")} (${item.type} content cannot be symlinked)`);
|
|
2357
|
+
} else {
|
|
2358
|
+
step(`Method: ${brand(method)}`);
|
|
2359
|
+
}
|
|
1839
2360
|
if (options.dryRun) {
|
|
1840
2361
|
info("Dry run - no files will be written");
|
|
1841
|
-
|
|
2362
|
+
const planned = await planInstall(handler, item, agents, scope, method, cwd);
|
|
2363
|
+
printPlan("Would write:", planned);
|
|
2364
|
+
const blocked = decideForce(options) ? [] : planned.filter((change) => change.kind === "modify");
|
|
2365
|
+
if (blocked.length > 0) {
|
|
2366
|
+
warn(
|
|
2367
|
+
`${blocked.length} destination(s) already exist and would be refused without --force:`
|
|
2368
|
+
);
|
|
2369
|
+
for (const change of blocked) console.log(chalk9.gray(` ${change.path}`));
|
|
2370
|
+
outro("Dry run complete - the real run would fail without --force");
|
|
2371
|
+
return 1;
|
|
2372
|
+
}
|
|
1842
2373
|
outro("Dry run complete");
|
|
1843
2374
|
return 0;
|
|
1844
2375
|
}
|
|
@@ -1857,7 +2388,7 @@ async function runAdd(name, options, cwd = process.cwd()) {
|
|
|
1857
2388
|
if (exitCode === 0) outro("Installation complete");
|
|
1858
2389
|
return exitCode;
|
|
1859
2390
|
}
|
|
1860
|
-
var addCommand = new Command("add").description("Install a skill, agent, hook, or other configuration").argument("[name]", "Name of the item to install").option("-t, --type <type>",
|
|
2391
|
+
var addCommand = new Command("add").description("Install a skill, agent, hook, or other configuration").argument("[name]", "Name of the item to install").option("-t, --type <type>", `Content type: ${TYPE_LIST}`).option(
|
|
1861
2392
|
"-a, --agents <agents>",
|
|
1862
2393
|
"Comma-separated coding agents or 'all' (claude,copilot,antigravity,codex,opencode; 'gemini' is a deprecated alias of antigravity). Every named agent must support the item; 'all' means all compatible agents. MCP servers: claude, codex, opencode (copilot's and antigravity's formats are unverified)"
|
|
1863
2394
|
).option("-s, --scope <scope>", "Installation scope: project, user, or local").option("-m, --method <method>", "Installation method: symlink or copy").option("-y, --yes", "Skip confirmation prompts").option("-f, --force", "Overwrite existing files").option("-n, --dry-run", "Show exactly which files would be written, without writing or reporting anything").addHelpText("after", `
|
|
@@ -1874,17 +2405,18 @@ Telemetry: ${TELEMETRY_HELP_TEXT}.`).action(async (name, options) => {
|
|
|
1874
2405
|
|
|
1875
2406
|
// src/commands/list.ts
|
|
1876
2407
|
import { Command as Command2 } from "commander";
|
|
1877
|
-
import
|
|
2408
|
+
import chalk10 from "chalk";
|
|
1878
2409
|
var SEPARATOR_WIDTH = 40;
|
|
1879
2410
|
var SLUG_COLUMN_WIDTH = 24;
|
|
1880
2411
|
var TYPE_COLORS = {
|
|
1881
|
-
skill:
|
|
1882
|
-
hook:
|
|
1883
|
-
agent:
|
|
1884
|
-
plugin:
|
|
1885
|
-
command:
|
|
1886
|
-
settings:
|
|
1887
|
-
mcp:
|
|
2412
|
+
skill: chalk10.magenta,
|
|
2413
|
+
hook: chalk10.hex("#a855f7"),
|
|
2414
|
+
agent: chalk10.blue,
|
|
2415
|
+
plugin: chalk10.hex("#6366f1"),
|
|
2416
|
+
command: chalk10.hex("#f59e0b"),
|
|
2417
|
+
settings: chalk10.hex("#f97316"),
|
|
2418
|
+
mcp: chalk10.hex("#2dd4bf"),
|
|
2419
|
+
rule: chalk10.hex("#84cc16")
|
|
1888
2420
|
};
|
|
1889
2421
|
async function collectInstalledItems(params) {
|
|
1890
2422
|
const types = params.types ?? getRegisteredTypes();
|
|
@@ -1913,7 +2445,7 @@ function resolveListAgents(agentsArg) {
|
|
|
1913
2445
|
async function runList(options, cwd = process.cwd()) {
|
|
1914
2446
|
const optionError = validateType(options.type) || validateScope(options.scope);
|
|
1915
2447
|
if (optionError) {
|
|
1916
|
-
console.log(
|
|
2448
|
+
console.log(chalk10.red(optionError));
|
|
1917
2449
|
return 1;
|
|
1918
2450
|
}
|
|
1919
2451
|
const type = options.type;
|
|
@@ -1922,14 +2454,14 @@ async function runList(options, cwd = process.cwd()) {
|
|
|
1922
2454
|
}
|
|
1923
2455
|
const agents = resolveListAgents(options.agents);
|
|
1924
2456
|
if (typeof agents === "string") {
|
|
1925
|
-
console.log(
|
|
2457
|
+
console.log(chalk10.red(agents));
|
|
1926
2458
|
return 1;
|
|
1927
2459
|
}
|
|
1928
2460
|
const scope = options.scope ?? "project";
|
|
1929
2461
|
await listInstalled({ types: type ? [type] : void 0, agents, scope, cwd });
|
|
1930
2462
|
return 0;
|
|
1931
2463
|
}
|
|
1932
|
-
var listCommand = new Command2("list").alias("ls").description("List available or installed items").option("-t, --type <type>",
|
|
2464
|
+
var listCommand = new Command2("list").alias("ls").description("List available or installed items").option("-t, --type <type>", `Filter by type: ${TYPE_LIST}`).option("-i, --installed", "Show only installed items").option("-a, --agents <agents>", "Comma-separated coding agents or 'all' (installed check)", "all").option("--scope <scope>", "Scope for installed check (project, user, local)", "project").option("--label <label>", "Filter by label (see the registry's labels.json)").action(async (options) => {
|
|
1933
2465
|
try {
|
|
1934
2466
|
const exitCode = await runList(options);
|
|
1935
2467
|
if (exitCode !== 0) process.exit(exitCode);
|
|
@@ -1942,32 +2474,32 @@ async function listAvailable(type, label) {
|
|
|
1942
2474
|
const items = label ? all.filter((item) => item.label === label) : all;
|
|
1943
2475
|
if (items.length === 0 && label) {
|
|
1944
2476
|
const known = [...new Set(all.map((item) => item.label).filter(Boolean))].sort();
|
|
1945
|
-
console.log(
|
|
1946
|
-
if (known.length > 0) console.log(
|
|
2477
|
+
console.log(chalk10.red(`No item carries the label "${label}".`));
|
|
2478
|
+
if (known.length > 0) console.log(chalk10.gray(`Labels in use: ${known.join(", ")}`));
|
|
1947
2479
|
return 1;
|
|
1948
2480
|
}
|
|
1949
2481
|
if (items.length === 0) {
|
|
1950
|
-
console.log(
|
|
2482
|
+
console.log(chalk10.yellow("No items found in registry"));
|
|
1951
2483
|
return 0;
|
|
1952
2484
|
}
|
|
1953
2485
|
const grouped = groupByType(items);
|
|
1954
2486
|
for (const [itemType, typeItems] of Object.entries(grouped)) {
|
|
1955
|
-
const colorFn = TYPE_COLORS[itemType] ??
|
|
2487
|
+
const colorFn = TYPE_COLORS[itemType] ?? chalk10.white;
|
|
1956
2488
|
console.log(colorFn(`
|
|
1957
2489
|
${itemType.toUpperCase()}S`));
|
|
1958
|
-
console.log(
|
|
2490
|
+
console.log(chalk10.gray("\u2500".repeat(SEPARATOR_WIDTH)));
|
|
1959
2491
|
for (const item of typeItems) {
|
|
1960
2492
|
const compatIcons = [...new Set(item.compatibility.map((a) => CODING_AGENTS[a]?.shortName ?? a))].join(" ");
|
|
1961
|
-
const featured = item.featured ?
|
|
2493
|
+
const featured = item.featured ? chalk10.yellow("\u2605 ") : " ";
|
|
1962
2494
|
console.log(
|
|
1963
|
-
`${featured}${
|
|
2495
|
+
`${featured}${chalk10.white(item.slug.padEnd(SLUG_COLUMN_WIDTH))} ${chalk10.gray(compatIcons)}`
|
|
1964
2496
|
);
|
|
1965
|
-
console.log(` ${
|
|
2497
|
+
console.log(` ${chalk10.gray(item.description)}`);
|
|
1966
2498
|
}
|
|
1967
2499
|
}
|
|
1968
2500
|
console.log("");
|
|
1969
2501
|
console.log(
|
|
1970
|
-
|
|
2502
|
+
chalk10.gray(`Total: ${items.length} items. Use 'npx @danieldeusing/seedr add <name>' to install.`)
|
|
1971
2503
|
);
|
|
1972
2504
|
return 0;
|
|
1973
2505
|
}
|
|
@@ -1981,23 +2513,23 @@ Installed items (${params.scope} scope):
|
|
|
1981
2513
|
for (const group of groups) {
|
|
1982
2514
|
if (group.type !== currentType) {
|
|
1983
2515
|
currentType = group.type;
|
|
1984
|
-
const colorFn = TYPE_COLORS[group.type] ??
|
|
2516
|
+
const colorFn = TYPE_COLORS[group.type] ?? chalk10.white;
|
|
1985
2517
|
console.log(colorFn(`${group.type.toUpperCase()}S`));
|
|
1986
2518
|
}
|
|
1987
|
-
console.log(
|
|
2519
|
+
console.log(chalk10.blue(` ${CODING_AGENTS[group.agent].name}`));
|
|
1988
2520
|
for (const slug of group.slugs) {
|
|
1989
|
-
console.log(` ${
|
|
2521
|
+
console.log(` ${chalk10.white(slug)}`);
|
|
1990
2522
|
total++;
|
|
1991
2523
|
}
|
|
1992
2524
|
console.log("");
|
|
1993
2525
|
}
|
|
1994
2526
|
if (!params.types || params.types.includes("settings")) {
|
|
1995
|
-
console.log(
|
|
2527
|
+
console.log(chalk10.gray(`Note: ${SETTINGS_NOT_DISCOVERABLE}`));
|
|
1996
2528
|
}
|
|
1997
2529
|
if (total === 0) {
|
|
1998
|
-
console.log(
|
|
2530
|
+
console.log(chalk10.yellow("No items installed"));
|
|
1999
2531
|
} else {
|
|
2000
|
-
console.log(
|
|
2532
|
+
console.log(chalk10.gray(`Total: ${total} installed`));
|
|
2001
2533
|
}
|
|
2002
2534
|
}
|
|
2003
2535
|
function groupByType(items) {
|
|
@@ -2015,8 +2547,8 @@ function groupByType(items) {
|
|
|
2015
2547
|
|
|
2016
2548
|
// src/commands/remove.ts
|
|
2017
2549
|
import { Command as Command3 } from "commander";
|
|
2018
|
-
import
|
|
2019
|
-
import
|
|
2550
|
+
import chalk11 from "chalk";
|
|
2551
|
+
import ora8 from "ora";
|
|
2020
2552
|
async function findInstalledAgents(slug, type, scope, cwd) {
|
|
2021
2553
|
const handler = getHandler(type);
|
|
2022
2554
|
if (!handler) return [];
|
|
@@ -2034,13 +2566,19 @@ async function removeFromAgents(slug, type, agents, scope, cwd) {
|
|
|
2034
2566
|
if (!handler) return 0;
|
|
2035
2567
|
let successCount = 0;
|
|
2036
2568
|
for (const agent of agents) {
|
|
2037
|
-
|
|
2569
|
+
if (!isTypeSupported(type, agent)) {
|
|
2570
|
+
ora8().start().info(
|
|
2571
|
+
chalk11.gray(`Skipped ${CODING_AGENTS[agent].name}: ${describeIncompatibility(type, agent)}`)
|
|
2572
|
+
);
|
|
2573
|
+
continue;
|
|
2574
|
+
}
|
|
2575
|
+
const spinner = ora8(`Removing from ${CODING_AGENTS[agent].name}...`).start();
|
|
2038
2576
|
const removed = await handler.uninstall(slug, agent, scope, cwd);
|
|
2039
2577
|
if (removed) {
|
|
2040
2578
|
spinner.succeed(brand(`Removed from ${CODING_AGENTS[agent].name}`));
|
|
2041
2579
|
successCount++;
|
|
2042
2580
|
} else {
|
|
2043
|
-
spinner.info(
|
|
2581
|
+
spinner.info(chalk11.gray(`Not found in ${CODING_AGENTS[agent].name}`));
|
|
2044
2582
|
}
|
|
2045
2583
|
}
|
|
2046
2584
|
return successCount;
|
|
@@ -2065,14 +2603,14 @@ function validateRemoveRequest(name, options) {
|
|
|
2065
2603
|
async function runRemove(name, options, cwd = process.cwd()) {
|
|
2066
2604
|
const requestError = validateRemoveRequest(name, options);
|
|
2067
2605
|
if (requestError) {
|
|
2068
|
-
console.log(
|
|
2606
|
+
console.log(chalk11.red(requestError));
|
|
2069
2607
|
return 1;
|
|
2070
2608
|
}
|
|
2071
2609
|
const scope = options.scope ?? "project";
|
|
2072
2610
|
const type = options.type;
|
|
2073
2611
|
const handler = getHandler(type);
|
|
2074
2612
|
if (!handler) {
|
|
2075
|
-
console.log(
|
|
2613
|
+
console.log(chalk11.red(`No handler found for type "${type}"`));
|
|
2076
2614
|
return 1;
|
|
2077
2615
|
}
|
|
2078
2616
|
let agents;
|
|
@@ -2085,11 +2623,11 @@ async function runRemove(name, options, cwd = process.cwd()) {
|
|
|
2085
2623
|
}
|
|
2086
2624
|
if (agents.length === 0) {
|
|
2087
2625
|
if (type === "settings") {
|
|
2088
|
-
console.log(
|
|
2089
|
-
console.log(
|
|
2626
|
+
console.log(chalk11.yellow(`Cannot detect where "${name}" is installed: ${SETTINGS_NOT_DISCOVERABLE}`));
|
|
2627
|
+
console.log(chalk11.gray("Name the agents explicitly, e.g. --agents claude"));
|
|
2090
2628
|
return 1;
|
|
2091
2629
|
}
|
|
2092
|
-
console.log(
|
|
2630
|
+
console.log(chalk11.yellow(`${type} "${name}" is not installed in ${scope} scope`));
|
|
2093
2631
|
return 0;
|
|
2094
2632
|
}
|
|
2095
2633
|
if (!options.yes) {
|
|
@@ -2102,7 +2640,7 @@ Will remove ${type} "${name}" from:`));
|
|
|
2102
2640
|
const answer = await confirm("Proceed with removal?");
|
|
2103
2641
|
const confirmed = !p.isCancel(answer) && answer;
|
|
2104
2642
|
if (!confirmed) {
|
|
2105
|
-
console.log(
|
|
2643
|
+
console.log(chalk11.yellow("Removal cancelled"));
|
|
2106
2644
|
return 0;
|
|
2107
2645
|
}
|
|
2108
2646
|
}
|
|
@@ -2112,10 +2650,10 @@ Will remove ${type} "${name}" from:`));
|
|
|
2112
2650
|
console.log(brand(`Successfully removed from ${successCount} agent(s)`));
|
|
2113
2651
|
return 0;
|
|
2114
2652
|
}
|
|
2115
|
-
console.log(
|
|
2653
|
+
console.log(chalk11.yellow("Nothing to remove"));
|
|
2116
2654
|
return 0;
|
|
2117
2655
|
}
|
|
2118
|
-
var removeCommand = new Command3("remove").alias("rm").description("Remove an installed item (skill, plugin, agent, hook, mcp)").argument("<name>", "Name/slug of the item to remove").option("-t, --type <type>",
|
|
2656
|
+
var removeCommand = new Command3("remove").alias("rm").description("Remove an installed item (skill, plugin, agent, hook, mcp)").argument("<name>", "Name/slug of the item to remove").option("-t, --type <type>", `Content type: ${TYPE_LIST}`).option(
|
|
2119
2657
|
"-a, --agents <agents>",
|
|
2120
2658
|
"Comma-separated coding agents or 'all'"
|
|
2121
2659
|
).option(
|
|
@@ -2133,9 +2671,9 @@ var removeCommand = new Command3("remove").alias("rm").description("Remove an in
|
|
|
2133
2671
|
|
|
2134
2672
|
// src/commands/init.ts
|
|
2135
2673
|
import { Command as Command4 } from "commander";
|
|
2136
|
-
import
|
|
2137
|
-
import
|
|
2138
|
-
import { join as
|
|
2674
|
+
import chalk12 from "chalk";
|
|
2675
|
+
import ora9 from "ora";
|
|
2676
|
+
import { join as join7 } from "path";
|
|
2139
2677
|
function readmeFor(agent) {
|
|
2140
2678
|
return `# ${CODING_AGENTS[agent].name} Configuration
|
|
2141
2679
|
|
|
@@ -2153,7 +2691,7 @@ async function initializeAgent(agent, cwd) {
|
|
|
2153
2691
|
const path = getAgentPath(agent, "project", cwd);
|
|
2154
2692
|
if (await exists(path)) return false;
|
|
2155
2693
|
await ensureDir(path);
|
|
2156
|
-
await writeTextFile(
|
|
2694
|
+
await writeTextFile(join7(path, "README.md"), readmeFor(agent));
|
|
2157
2695
|
return true;
|
|
2158
2696
|
}
|
|
2159
2697
|
async function runInit(options, cwd = process.cwd()) {
|
|
@@ -2164,13 +2702,13 @@ async function runInit(options, cwd = process.cwd()) {
|
|
|
2164
2702
|
} else {
|
|
2165
2703
|
const parsed = parseAgentsArgStrict(agentsArg);
|
|
2166
2704
|
if (parsed.unknown.length > 0) {
|
|
2167
|
-
console.error(
|
|
2705
|
+
console.error(chalk12.red(`Unknown agent(s): ${parsed.unknown.join(", ")}. Valid agents: ${ALL_AGENTS.join(", ")} or "all"`));
|
|
2168
2706
|
return 1;
|
|
2169
2707
|
}
|
|
2170
2708
|
agents = parsed.agents;
|
|
2171
2709
|
}
|
|
2172
2710
|
if (agents.length === 0) {
|
|
2173
|
-
console.error(
|
|
2711
|
+
console.error(chalk12.red("No valid agents specified"));
|
|
2174
2712
|
return 1;
|
|
2175
2713
|
}
|
|
2176
2714
|
console.log(brand("\nWill initialize configuration for:"));
|
|
@@ -2182,16 +2720,16 @@ async function runInit(options, cwd = process.cwd()) {
|
|
|
2182
2720
|
const answer = await confirm("Proceed?");
|
|
2183
2721
|
const confirmed = !p.isCancel(answer) && answer;
|
|
2184
2722
|
if (!confirmed) {
|
|
2185
|
-
console.log(
|
|
2723
|
+
console.log(chalk12.yellow("Cancelled"));
|
|
2186
2724
|
return 0;
|
|
2187
2725
|
}
|
|
2188
2726
|
}
|
|
2189
2727
|
for (const agent of agents) {
|
|
2190
|
-
const spinner =
|
|
2728
|
+
const spinner = ora9(`Initializing ${CODING_AGENTS[agent].name}...`).start();
|
|
2191
2729
|
if (await initializeAgent(agent, cwd)) {
|
|
2192
2730
|
spinner.succeed(brand(`Initialized ${CODING_AGENTS[agent].name}`));
|
|
2193
2731
|
} else {
|
|
2194
|
-
spinner.info(
|
|
2732
|
+
spinner.info(chalk12.gray(`${CODING_AGENTS[agent].name} already initialized`));
|
|
2195
2733
|
}
|
|
2196
2734
|
}
|
|
2197
2735
|
console.log("");
|
|
@@ -2214,7 +2752,7 @@ var initCommand = new Command4("init").description("Initialize coding agent conf
|
|
|
2214
2752
|
// package.json
|
|
2215
2753
|
var package_default = {
|
|
2216
2754
|
name: "@danieldeusing/seedr",
|
|
2217
|
-
version: "0.1.
|
|
2755
|
+
version: "0.1.99",
|
|
2218
2756
|
description: "Seed your projects with AI configurations",
|
|
2219
2757
|
type: "module",
|
|
2220
2758
|
bin: {
|