@danieldeusing/seedr 0.1.96 → 0.1.98
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-NQOS5TI7.js} +159 -22
- package/dist/cli.js +840 -340
- 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,10 @@ import {
|
|
|
11
13
|
brand,
|
|
12
14
|
canonicalAgent,
|
|
13
15
|
canonicalAgents,
|
|
16
|
+
claudeUserRoot,
|
|
14
17
|
confirm,
|
|
15
18
|
copyDirectory,
|
|
19
|
+
deepMerge,
|
|
16
20
|
ensureDir,
|
|
17
21
|
error,
|
|
18
22
|
exists,
|
|
@@ -33,6 +37,7 @@ import {
|
|
|
33
37
|
intro,
|
|
34
38
|
isInteractive,
|
|
35
39
|
isLegacyAgent,
|
|
40
|
+
isTypeSupported,
|
|
36
41
|
isValidSlug,
|
|
37
42
|
listItems,
|
|
38
43
|
mainFileName,
|
|
@@ -42,6 +47,7 @@ import {
|
|
|
42
47
|
parseAgentsArgStrict,
|
|
43
48
|
parseGitHubRepo,
|
|
44
49
|
printLogo,
|
|
50
|
+
readJson,
|
|
45
51
|
removePathEntry,
|
|
46
52
|
resolveContained,
|
|
47
53
|
restoreFile,
|
|
@@ -56,15 +62,16 @@ import {
|
|
|
56
62
|
success,
|
|
57
63
|
warn,
|
|
58
64
|
writeFileAtomic,
|
|
65
|
+
writeJson,
|
|
59
66
|
writeTextFile
|
|
60
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-NQOS5TI7.js";
|
|
61
68
|
|
|
62
69
|
// src/cli.ts
|
|
63
70
|
import { Command as Command5 } from "commander";
|
|
64
71
|
|
|
65
72
|
// src/commands/add.ts
|
|
66
73
|
import { Command } from "commander";
|
|
67
|
-
import
|
|
74
|
+
import chalk9 from "chalk";
|
|
68
75
|
|
|
69
76
|
// src/handlers/registry.ts
|
|
70
77
|
var handlers = /* @__PURE__ */ new Map();
|
|
@@ -104,15 +111,8 @@ function handleCommandError(error2) {
|
|
|
104
111
|
// src/utils/validate-options.ts
|
|
105
112
|
var SCOPES = ["project", "user", "local"];
|
|
106
113
|
var METHODS = ["symlink", "copy"];
|
|
107
|
-
var TYPES =
|
|
108
|
-
|
|
109
|
-
"hook",
|
|
110
|
-
"agent",
|
|
111
|
-
"plugin",
|
|
112
|
-
"command",
|
|
113
|
-
"settings",
|
|
114
|
-
"mcp"
|
|
115
|
-
];
|
|
114
|
+
var TYPES = ALL_TYPES;
|
|
115
|
+
var TYPE_LIST = TYPES.join(", ");
|
|
116
116
|
function validateScope(value) {
|
|
117
117
|
if (value === void 0) return null;
|
|
118
118
|
if (!SCOPES.includes(value)) {
|
|
@@ -144,7 +144,7 @@ function isTelemetryDisabled(env = process.env) {
|
|
|
144
144
|
return env[TELEMETRY_OPT_OUT_VARIABLE] !== void 0;
|
|
145
145
|
}
|
|
146
146
|
function cliVersion() {
|
|
147
|
-
return true ? "0.1.
|
|
147
|
+
return true ? "0.1.98" : "dev";
|
|
148
148
|
}
|
|
149
149
|
async function sendEvent(event) {
|
|
150
150
|
try {
|
|
@@ -165,26 +165,12 @@ function trackInstalls(slug, type, results, scope) {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
// 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
168
|
function filterCompatibleAgents(type, agents) {
|
|
182
169
|
const compatible = AGENT_COMPATIBILITY[type];
|
|
183
170
|
return canonicalAgents(agents).filter((a) => compatible.includes(a));
|
|
184
171
|
}
|
|
185
172
|
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"
|
|
173
|
+
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
174
|
};
|
|
189
175
|
function describeIncompatibility(type, agent) {
|
|
190
176
|
const canonical = canonicalAgent(agent);
|
|
@@ -201,11 +187,14 @@ import { lstat, readdir, unlink } from "fs/promises";
|
|
|
201
187
|
import chalk2 from "chalk";
|
|
202
188
|
import ora from "ora";
|
|
203
189
|
var SLUG_LABEL = "agent slug";
|
|
190
|
+
function subagentFileName(agent, slug) {
|
|
191
|
+
return (canonicalAgent(agent) ?? agent) === "copilot" ? `${slug}.agent.md` : `${slug}.md`;
|
|
192
|
+
}
|
|
204
193
|
async function resolveAgentFilePath(agent, slug, scope, cwd) {
|
|
205
194
|
const destDir = getContentPath(agent, "agent", scope, cwd);
|
|
206
195
|
if (!destDir) return null;
|
|
207
196
|
const scopeRoot = scope === "user" ? homedir() : cwd;
|
|
208
|
-
return resolveContained(scopeRoot, relative(scopeRoot, destDir),
|
|
197
|
+
return resolveContained(scopeRoot, relative(scopeRoot, destDir), subagentFileName(agent, slug));
|
|
209
198
|
}
|
|
210
199
|
async function installAgentForCodingAgent(item, agent, scope, method, force, cwd) {
|
|
211
200
|
const spinner = ora(
|
|
@@ -413,60 +402,11 @@ var commandHandler = {
|
|
|
413
402
|
};
|
|
414
403
|
|
|
415
404
|
// src/handlers/hook.ts
|
|
416
|
-
import { join as join3, basename, dirname
|
|
405
|
+
import { join as join3, basename, dirname } from "path";
|
|
417
406
|
import { homedir as homedir3 } from "os";
|
|
418
|
-
import { lstat as lstat3, mkdir, mkdtemp, readFile
|
|
407
|
+
import { lstat as lstat3, mkdir, mkdtemp, readFile, realpath, rm, unlink as unlink3 } from "fs/promises";
|
|
419
408
|
import chalk4 from "chalk";
|
|
420
409
|
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
410
|
var CLAUDE_ONLY_ERROR = "Hooks are only supported for Claude Code";
|
|
471
411
|
var SCRIPT_NAME_LABEL = "hook script name";
|
|
472
412
|
var SCRIPT_MODE = 493;
|
|
@@ -520,13 +460,13 @@ async function assertRegularFileOrMissing(path) {
|
|
|
520
460
|
async function loadScriptBytes(item, scriptFile, hooksDir) {
|
|
521
461
|
const sourcePath = getItemSourcePath(item);
|
|
522
462
|
if (sourcePath && await exists(sourcePath)) {
|
|
523
|
-
return
|
|
463
|
+
return readFile(join3(sourcePath, scriptFile));
|
|
524
464
|
}
|
|
525
|
-
const fetchDir = await mkdtemp(join3(
|
|
465
|
+
const fetchDir = await mkdtemp(join3(dirname(hooksDir), ".seedr-hook-"));
|
|
526
466
|
try {
|
|
527
467
|
const contentDir = join3(fetchDir, "content");
|
|
528
468
|
await fetchItemToDestination(item, contentDir);
|
|
529
|
-
return await
|
|
469
|
+
return await readFile(join3(contentDir, scriptFile));
|
|
530
470
|
} finally {
|
|
531
471
|
await rm(fetchDir, { recursive: true, force: true });
|
|
532
472
|
}
|
|
@@ -703,6 +643,7 @@ async function planHook(item, agents, scope, _method, cwd) {
|
|
|
703
643
|
}
|
|
704
644
|
var hookHandler = {
|
|
705
645
|
type: "hook",
|
|
646
|
+
honoursMethod: false,
|
|
706
647
|
async install(item, agents, scope, method, force, cwd) {
|
|
707
648
|
return installHook(item, agents, scope, method, force, cwd);
|
|
708
649
|
},
|
|
@@ -716,8 +657,8 @@ var hookHandler = {
|
|
|
716
657
|
};
|
|
717
658
|
|
|
718
659
|
// src/handlers/mcp.ts
|
|
719
|
-
import { readFile as
|
|
720
|
-
import { dirname as
|
|
660
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
661
|
+
import { dirname as dirname2 } from "path";
|
|
721
662
|
import chalk5 from "chalk";
|
|
722
663
|
import ora4 from "ora";
|
|
723
664
|
|
|
@@ -1014,7 +955,7 @@ function jsonAdapter(agent, key, translate, defaults = {}) {
|
|
|
1014
955
|
}
|
|
1015
956
|
async function readTextOrEmpty(path) {
|
|
1016
957
|
try {
|
|
1017
|
-
return await
|
|
958
|
+
return await readFile2(path, "utf-8");
|
|
1018
959
|
} catch (error2) {
|
|
1019
960
|
if (error2.code === "ENOENT") return "";
|
|
1020
961
|
throw error2;
|
|
@@ -1028,7 +969,7 @@ var codexAdapter = {
|
|
|
1028
969
|
},
|
|
1029
970
|
async write(path, name, config) {
|
|
1030
971
|
const text = await readTextOrEmpty(path);
|
|
1031
|
-
await ensureDir(
|
|
972
|
+
await ensureDir(dirname2(path));
|
|
1032
973
|
await writeFileAtomic(path, upsertTomlTables(text, [CODEX_TABLE, name], toCodexTables(name, config)));
|
|
1033
974
|
},
|
|
1034
975
|
async remove(path, name) {
|
|
@@ -1044,7 +985,10 @@ var codexAdapter = {
|
|
|
1044
985
|
var ADAPTERS = {
|
|
1045
986
|
claude: jsonAdapter("claude", MCP_SERVERS_KEY, (config) => ({ ...config })),
|
|
1046
987
|
opencode: jsonAdapter("opencode", "mcp", toOpenCodeServer, { $schema: "https://opencode.ai/config.json" }),
|
|
1047
|
-
codex: codexAdapter
|
|
988
|
+
codex: codexAdapter,
|
|
989
|
+
// Copilot reads the same `mcpServers` map Claude does — verified from
|
|
990
|
+
// `copilot mcp --help` and from a populated `~/.copilot/mcp-config.json`.
|
|
991
|
+
copilot: jsonAdapter("copilot", MCP_SERVERS_KEY, (config) => ({ ...config }))
|
|
1048
992
|
};
|
|
1049
993
|
function adapterFor(agent) {
|
|
1050
994
|
const adapter = ADAPTERS[agent];
|
|
@@ -1129,6 +1073,7 @@ async function planMcp(item, agents, scope, _method, cwd) {
|
|
|
1129
1073
|
}
|
|
1130
1074
|
var mcpHandler = {
|
|
1131
1075
|
type: "mcp",
|
|
1076
|
+
honoursMethod: false,
|
|
1132
1077
|
async install(item, agents, scope, method, force, cwd) {
|
|
1133
1078
|
return installMcp(item, agents, scope, method, force, cwd);
|
|
1134
1079
|
},
|
|
@@ -1255,6 +1200,7 @@ async function planSettings(item, agents, scope, _method, cwd) {
|
|
|
1255
1200
|
}
|
|
1256
1201
|
var settingsHandler = {
|
|
1257
1202
|
type: "settings",
|
|
1203
|
+
honoursMethod: false,
|
|
1258
1204
|
async install(item, agents, scope, method, force, cwd) {
|
|
1259
1205
|
return installSettings(item, agents, scope, method, force, cwd);
|
|
1260
1206
|
},
|
|
@@ -1268,23 +1214,30 @@ var settingsHandler = {
|
|
|
1268
1214
|
};
|
|
1269
1215
|
|
|
1270
1216
|
// src/handlers/plugin.ts
|
|
1217
|
+
import { join as join5, relative as relative3 } from "path";
|
|
1218
|
+
import { mkdir as mkdir3, mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
|
|
1219
|
+
import chalk7 from "chalk";
|
|
1220
|
+
import ora6 from "ora";
|
|
1221
|
+
|
|
1222
|
+
// src/handlers/pluginStores.ts
|
|
1271
1223
|
import { homedir as homedir4 } from "os";
|
|
1272
|
-
import { join as join4
|
|
1273
|
-
import { mkdir as mkdir2,
|
|
1224
|
+
import { join as join4 } from "path";
|
|
1225
|
+
import { mkdir as mkdir2, readFile as readFile3, writeFile } from "fs/promises";
|
|
1274
1226
|
import { execFile } from "child_process";
|
|
1275
1227
|
import { promisify } from "util";
|
|
1276
|
-
import chalk7 from "chalk";
|
|
1277
|
-
import ora6 from "ora";
|
|
1278
1228
|
var home = homedir4();
|
|
1279
|
-
var
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1229
|
+
var CLAUDE_MANIFEST = ".claude-plugin/plugin.json";
|
|
1230
|
+
async function readTextOrEmpty2(path) {
|
|
1231
|
+
try {
|
|
1232
|
+
return await readFile3(path, "utf-8");
|
|
1233
|
+
} catch {
|
|
1234
|
+
return "";
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
async function writeText(path, text) {
|
|
1238
|
+
await mkdir2(join4(path, ".."), { recursive: true });
|
|
1239
|
+
await writeFile(path, text, "utf-8");
|
|
1240
|
+
}
|
|
1288
1241
|
function getPluginId(name, marketplace) {
|
|
1289
1242
|
return `${name}@${marketplace}`;
|
|
1290
1243
|
}
|
|
@@ -1293,6 +1246,399 @@ function splitPluginId(pluginId) {
|
|
|
1293
1246
|
if (at <= 0 || at === pluginId.length - 1) return null;
|
|
1294
1247
|
return { name: pluginId.slice(0, at), marketplace: pluginId.slice(at + 1) };
|
|
1295
1248
|
}
|
|
1249
|
+
function manifestsFor(ownManifest) {
|
|
1250
|
+
return ownManifest ? [ownManifest, CLAUDE_MANIFEST] : [CLAUDE_MANIFEST];
|
|
1251
|
+
}
|
|
1252
|
+
var CLAUDE_PLUGINS_DIR = join4(claudeUserRoot(), "plugins");
|
|
1253
|
+
var CLAUDE_CACHE_DIR = join4(CLAUDE_PLUGINS_DIR, "cache");
|
|
1254
|
+
var CLAUDE_INSTALLED_PATH = join4(CLAUDE_PLUGINS_DIR, "installed_plugins.json");
|
|
1255
|
+
var CLAUDE_KNOWN_MARKETPLACES_PATH = join4(CLAUDE_PLUGINS_DIR, "known_marketplaces.json");
|
|
1256
|
+
var CLAUDE_MARKETPLACES_DIR = join4(CLAUDE_PLUGINS_DIR, "marketplaces");
|
|
1257
|
+
function isScopeEntry(entry, scope, cwd) {
|
|
1258
|
+
return entry.scope === scope && (scope === "user" || entry.projectPath === cwd);
|
|
1259
|
+
}
|
|
1260
|
+
async function enableInJsonSettings(path, pluginId) {
|
|
1261
|
+
const settings = await readJson(path);
|
|
1262
|
+
settings.enabledPlugins = settings.enabledPlugins || {};
|
|
1263
|
+
settings.enabledPlugins[pluginId] = true;
|
|
1264
|
+
await writeJson(path, settings);
|
|
1265
|
+
}
|
|
1266
|
+
async function disableInJsonSettings(path, pluginId) {
|
|
1267
|
+
const settings = await readJson(path);
|
|
1268
|
+
if (!settings.enabledPlugins || !(pluginId in settings.enabledPlugins)) return false;
|
|
1269
|
+
delete settings.enabledPlugins[pluginId];
|
|
1270
|
+
await writeJson(path, settings);
|
|
1271
|
+
return true;
|
|
1272
|
+
}
|
|
1273
|
+
var claudeStore = {
|
|
1274
|
+
manifestPaths: manifestsFor(),
|
|
1275
|
+
userGlobal: false,
|
|
1276
|
+
cacheRoot: CLAUDE_CACHE_DIR,
|
|
1277
|
+
cachePath: (marketplace, name, version) => resolveContained(CLAUDE_CACHE_DIR, marketplace, name, version),
|
|
1278
|
+
/**
|
|
1279
|
+
* Claude Code reports a plugin as "orphaned" unless its marketplace is both
|
|
1280
|
+
* recorded and cloned, so this is the one store that fetches a second
|
|
1281
|
+
* repository. The other agents only record the source.
|
|
1282
|
+
*/
|
|
1283
|
+
async ensureMarketplace(marketplace, item) {
|
|
1284
|
+
const known = await readJson(CLAUDE_KNOWN_MARKETPLACES_PATH);
|
|
1285
|
+
if (known[marketplace]) return [];
|
|
1286
|
+
const repo = marketplaceRepo(item);
|
|
1287
|
+
if (!repo) return [];
|
|
1288
|
+
const installLocation = await resolveContained(CLAUDE_MARKETPLACES_DIR, marketplace);
|
|
1289
|
+
const alreadyCloned = await exists(installLocation);
|
|
1290
|
+
return [
|
|
1291
|
+
{
|
|
1292
|
+
path: CLAUDE_KNOWN_MARKETPLACES_PATH,
|
|
1293
|
+
detail: `marketplace "${marketplace}"`,
|
|
1294
|
+
...alreadyCloned ? {} : {
|
|
1295
|
+
creates: {
|
|
1296
|
+
path: installLocation,
|
|
1297
|
+
detail: `git clone https://github.com/${repo}.git`
|
|
1298
|
+
}
|
|
1299
|
+
},
|
|
1300
|
+
apply: async () => {
|
|
1301
|
+
await mkdir2(CLAUDE_MARKETPLACES_DIR, { recursive: true });
|
|
1302
|
+
if (!await exists(installLocation)) {
|
|
1303
|
+
await promisify(execFile)("git", [
|
|
1304
|
+
"clone",
|
|
1305
|
+
"--depth",
|
|
1306
|
+
"1",
|
|
1307
|
+
`https://github.com/${repo}.git`,
|
|
1308
|
+
installLocation
|
|
1309
|
+
]);
|
|
1310
|
+
}
|
|
1311
|
+
const current = await readJson(
|
|
1312
|
+
CLAUDE_KNOWN_MARKETPLACES_PATH
|
|
1313
|
+
);
|
|
1314
|
+
current[marketplace] = {
|
|
1315
|
+
source: { source: "github", repo },
|
|
1316
|
+
installLocation,
|
|
1317
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
1318
|
+
};
|
|
1319
|
+
await writeJson(CLAUDE_KNOWN_MARKETPLACES_PATH, current);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
];
|
|
1323
|
+
},
|
|
1324
|
+
mutations: (context) => [
|
|
1325
|
+
{
|
|
1326
|
+
path: CLAUDE_INSTALLED_PATH,
|
|
1327
|
+
detail: `plugins["${context.pluginId}"] entry for ${context.scope} scope`,
|
|
1328
|
+
apply: async () => {
|
|
1329
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1330
|
+
const registry = await readJson(CLAUDE_INSTALLED_PATH);
|
|
1331
|
+
registry.version = registry.version || 2;
|
|
1332
|
+
registry.plugins = registry.plugins || {};
|
|
1333
|
+
const entry = {
|
|
1334
|
+
scope: context.scope,
|
|
1335
|
+
...context.scope !== "user" ? { projectPath: context.cwd } : {},
|
|
1336
|
+
installPath: context.cachePath ?? "",
|
|
1337
|
+
version: context.version,
|
|
1338
|
+
installedAt: now,
|
|
1339
|
+
lastUpdated: now,
|
|
1340
|
+
gitCommitSha: context.gitCommitSha
|
|
1341
|
+
};
|
|
1342
|
+
const existing = registry.plugins[context.pluginId] || [];
|
|
1343
|
+
registry.plugins[context.pluginId] = [
|
|
1344
|
+
...existing.filter((candidate) => !isScopeEntry(candidate, context.scope, context.cwd)),
|
|
1345
|
+
entry
|
|
1346
|
+
];
|
|
1347
|
+
await writeJson(CLAUDE_INSTALLED_PATH, registry);
|
|
1348
|
+
}
|
|
1349
|
+
},
|
|
1350
|
+
{
|
|
1351
|
+
path: getSettingsPath(context.scope, context.cwd),
|
|
1352
|
+
detail: `enabledPlugins["${context.pluginId}"] = true`,
|
|
1353
|
+
apply: () => enableInJsonSettings(getSettingsPath(context.scope, context.cwd), context.pluginId)
|
|
1354
|
+
}
|
|
1355
|
+
],
|
|
1356
|
+
async listInstalled(scope, cwd) {
|
|
1357
|
+
const registry = await readJson(CLAUDE_INSTALLED_PATH);
|
|
1358
|
+
if (!registry.plugins) return [];
|
|
1359
|
+
return Object.entries(registry.plugins).filter(([, entries]) => entries.some((entry) => isScopeEntry(entry, scope, cwd))).map(([pluginId]) => pluginId);
|
|
1360
|
+
},
|
|
1361
|
+
async remove(pluginId, scope, cwd) {
|
|
1362
|
+
const registry = await readJson(CLAUDE_INSTALLED_PATH);
|
|
1363
|
+
if (!registry.plugins?.[pluginId]) return false;
|
|
1364
|
+
const entries = registry.plugins[pluginId];
|
|
1365
|
+
const removed = entries.filter((entry) => isScopeEntry(entry, scope, cwd));
|
|
1366
|
+
if (removed.length === 0) return false;
|
|
1367
|
+
const remaining = entries.filter((entry) => !isScopeEntry(entry, scope, cwd));
|
|
1368
|
+
if (remaining.length === 0) delete registry.plugins[pluginId];
|
|
1369
|
+
else registry.plugins[pluginId] = remaining;
|
|
1370
|
+
await writeJson(CLAUDE_INSTALLED_PATH, registry);
|
|
1371
|
+
await disableInJsonSettings(getSettingsPath(scope, cwd), pluginId);
|
|
1372
|
+
for (const installPath of new Set(removed.map((entry) => entry.installPath))) {
|
|
1373
|
+
const stillUsed = Object.values(registry.plugins).some(
|
|
1374
|
+
(list) => list.some((entry) => entry.installPath === installPath)
|
|
1375
|
+
);
|
|
1376
|
+
if (stillUsed || installPath === "") continue;
|
|
1377
|
+
try {
|
|
1378
|
+
await removePathEntry(await resolveContained(CLAUDE_CACHE_DIR, installPath));
|
|
1379
|
+
} catch {
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
return true;
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1385
|
+
var COPILOT_DIR = join4(home, ".copilot");
|
|
1386
|
+
var COPILOT_SETTINGS_PATH = join4(COPILOT_DIR, "settings.json");
|
|
1387
|
+
var COPILOT_PLUGINS_DIR = join4(COPILOT_DIR, "installed-plugins");
|
|
1388
|
+
var copilotStore = {
|
|
1389
|
+
manifestPaths: manifestsFor(),
|
|
1390
|
+
userGlobal: true,
|
|
1391
|
+
cacheRoot: COPILOT_PLUGINS_DIR,
|
|
1392
|
+
cachePath: (marketplace, name) => resolveContained(COPILOT_PLUGINS_DIR, marketplace, name),
|
|
1393
|
+
async ensureMarketplace(marketplace, item) {
|
|
1394
|
+
const repo = marketplaceRepo(item);
|
|
1395
|
+
if (!repo) return [];
|
|
1396
|
+
const settings = await readJson(COPILOT_SETTINGS_PATH);
|
|
1397
|
+
if (settings.extraKnownMarketplaces?.[marketplace]) return [];
|
|
1398
|
+
return [
|
|
1399
|
+
{
|
|
1400
|
+
path: COPILOT_SETTINGS_PATH,
|
|
1401
|
+
detail: `extraKnownMarketplaces["${marketplace}"] = ${repo}`,
|
|
1402
|
+
apply: async () => {
|
|
1403
|
+
const current = await readJson(COPILOT_SETTINGS_PATH);
|
|
1404
|
+
current.extraKnownMarketplaces = current.extraKnownMarketplaces || {};
|
|
1405
|
+
current.extraKnownMarketplaces[marketplace] = { source: { source: "github", repo } };
|
|
1406
|
+
await writeJson(COPILOT_SETTINGS_PATH, current);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
];
|
|
1410
|
+
},
|
|
1411
|
+
mutations: (context) => [
|
|
1412
|
+
{
|
|
1413
|
+
path: COPILOT_SETTINGS_PATH,
|
|
1414
|
+
detail: `enabledPlugins["${context.pluginId}"] = true`,
|
|
1415
|
+
apply: () => enableInJsonSettings(COPILOT_SETTINGS_PATH, context.pluginId)
|
|
1416
|
+
}
|
|
1417
|
+
],
|
|
1418
|
+
async listInstalled() {
|
|
1419
|
+
const settings = await readJson(COPILOT_SETTINGS_PATH);
|
|
1420
|
+
return Object.entries(settings.enabledPlugins || {}).filter(([, enabled]) => enabled).map(([pluginId]) => pluginId);
|
|
1421
|
+
},
|
|
1422
|
+
async remove(pluginId) {
|
|
1423
|
+
const disabled = await disableInJsonSettings(COPILOT_SETTINGS_PATH, pluginId);
|
|
1424
|
+
if (!disabled) return false;
|
|
1425
|
+
const split = splitPluginId(pluginId);
|
|
1426
|
+
if (split) {
|
|
1427
|
+
try {
|
|
1428
|
+
await removePathEntry(await resolveContained(COPILOT_PLUGINS_DIR, split.marketplace, split.name));
|
|
1429
|
+
} catch {
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
return true;
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
var CODEX_DIR = join4(home, ".codex");
|
|
1436
|
+
var CODEX_CONFIG_PATH = join4(CODEX_DIR, "config.toml");
|
|
1437
|
+
var CODEX_CACHE_DIR = join4(CODEX_DIR, "plugins", "cache");
|
|
1438
|
+
function toCodexMarketplaceTable(marketplace, url, revision) {
|
|
1439
|
+
return {
|
|
1440
|
+
keyPath: ["marketplaces", marketplace],
|
|
1441
|
+
entries: {
|
|
1442
|
+
source_type: "git",
|
|
1443
|
+
source: url,
|
|
1444
|
+
...revision ? { last_revision: revision } : {},
|
|
1445
|
+
last_updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
1446
|
+
}
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
function toCodexPluginTable(pluginId) {
|
|
1450
|
+
return { keyPath: ["plugins", pluginId], entries: { enabled: true } };
|
|
1451
|
+
}
|
|
1452
|
+
async function upsertCodex(table) {
|
|
1453
|
+
const text = await readTextOrEmpty2(CODEX_CONFIG_PATH);
|
|
1454
|
+
await writeText(CODEX_CONFIG_PATH, upsertTomlTables(text, table.keyPath, [table]));
|
|
1455
|
+
}
|
|
1456
|
+
var codexStore = {
|
|
1457
|
+
manifestPaths: manifestsFor(".codex-plugin/plugin.json"),
|
|
1458
|
+
userGlobal: true,
|
|
1459
|
+
cacheRoot: CODEX_CACHE_DIR,
|
|
1460
|
+
// Real cache entries on disk are <marketplace>/<name>/<version> — e.g.
|
|
1461
|
+
// claude-plugins-official/claude-code-setup/1.0.0. Dropping the version
|
|
1462
|
+
// segment writes a tree Codex does not look in.
|
|
1463
|
+
cachePath: (marketplace, name, version) => resolveContained(CODEX_CACHE_DIR, marketplace, name, version),
|
|
1464
|
+
async ensureMarketplace(marketplace, item) {
|
|
1465
|
+
const repo = marketplaceRepo(item);
|
|
1466
|
+
if (!repo) return [];
|
|
1467
|
+
const url = `https://github.com/${repo}.git`;
|
|
1468
|
+
return [
|
|
1469
|
+
{
|
|
1470
|
+
path: CODEX_CONFIG_PATH,
|
|
1471
|
+
detail: `[marketplaces.${marketplace}] source = "${url}"`,
|
|
1472
|
+
apply: () => upsertCodex(toCodexMarketplaceTable(marketplace, url, getEffectiveSourceRevision(item) ?? void 0))
|
|
1473
|
+
}
|
|
1474
|
+
];
|
|
1475
|
+
},
|
|
1476
|
+
mutations: (context) => [
|
|
1477
|
+
{
|
|
1478
|
+
path: CODEX_CONFIG_PATH,
|
|
1479
|
+
detail: `[plugins."${context.pluginId}"] enabled = true`,
|
|
1480
|
+
apply: () => upsertCodex(toCodexPluginTable(context.pluginId))
|
|
1481
|
+
}
|
|
1482
|
+
],
|
|
1483
|
+
async listInstalled() {
|
|
1484
|
+
const text = await readTextOrEmpty2(CODEX_CONFIG_PATH);
|
|
1485
|
+
const ids = [];
|
|
1486
|
+
for (const match of text.matchAll(/^\[plugins\."([^"]+)"\]/gm)) {
|
|
1487
|
+
if (match[1]) ids.push(match[1]);
|
|
1488
|
+
}
|
|
1489
|
+
return ids;
|
|
1490
|
+
},
|
|
1491
|
+
async remove(pluginId) {
|
|
1492
|
+
const text = await readTextOrEmpty2(CODEX_CONFIG_PATH);
|
|
1493
|
+
const { text: next, removed } = removeTomlTables(text, ["plugins", pluginId]);
|
|
1494
|
+
if (!removed) return false;
|
|
1495
|
+
await writeText(CODEX_CONFIG_PATH, next);
|
|
1496
|
+
const split = splitPluginId(pluginId);
|
|
1497
|
+
if (split) {
|
|
1498
|
+
try {
|
|
1499
|
+
await removePathEntry(await resolveContained(CODEX_CACHE_DIR, split.marketplace, split.name));
|
|
1500
|
+
} catch {
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
return true;
|
|
1504
|
+
}
|
|
1505
|
+
};
|
|
1506
|
+
function openCodeConfigPath(scope, cwd) {
|
|
1507
|
+
return scope === "user" ? join4(home, ".config", "opencode", "opencode.json") : join4(cwd, "opencode.json");
|
|
1508
|
+
}
|
|
1509
|
+
function toOpenCodePluginSpec(item, name) {
|
|
1510
|
+
const source = item.pluginSource;
|
|
1511
|
+
if (source?.path) {
|
|
1512
|
+
throw new Error(
|
|
1513
|
+
`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`
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
const repo = githubRepoOf(source?.url) ?? githubRepoOf(item.externalUrl);
|
|
1517
|
+
if (!repo) return name;
|
|
1518
|
+
const pinned = source?.sha ?? item.sourceRevision;
|
|
1519
|
+
return `${name}@git+https://github.com/${repo}.git${pinned ? `#${pinned}` : ""}`;
|
|
1520
|
+
}
|
|
1521
|
+
function specName(spec) {
|
|
1522
|
+
const at = spec.indexOf("@", 1);
|
|
1523
|
+
return at > 0 ? spec.slice(0, at) : spec;
|
|
1524
|
+
}
|
|
1525
|
+
var openCodeStore = {
|
|
1526
|
+
manifestPaths: manifestsFor(),
|
|
1527
|
+
userGlobal: false,
|
|
1528
|
+
cacheRoot: null,
|
|
1529
|
+
// OpenCode installs the module from the spec; seedr writes no tree.
|
|
1530
|
+
cachePath: async () => null,
|
|
1531
|
+
mutations: (context) => {
|
|
1532
|
+
const path = openCodeConfigPath(context.scope, context.cwd);
|
|
1533
|
+
const spec = toOpenCodePluginSpec(context.item, context.name);
|
|
1534
|
+
return [
|
|
1535
|
+
{
|
|
1536
|
+
path,
|
|
1537
|
+
detail: `plugin[] += "${spec}"`,
|
|
1538
|
+
apply: async () => {
|
|
1539
|
+
const config = await readJson(path);
|
|
1540
|
+
config.$schema = config.$schema || "https://opencode.ai/config.json";
|
|
1541
|
+
const plugins = config.plugin || [];
|
|
1542
|
+
config.plugin = [...plugins.filter((entry) => specName(entry) !== context.name), spec];
|
|
1543
|
+
await writeJson(path, config);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
];
|
|
1547
|
+
},
|
|
1548
|
+
async listInstalled(scope, cwd) {
|
|
1549
|
+
const config = await readJson(openCodeConfigPath(scope, cwd));
|
|
1550
|
+
return (config.plugin || []).map(specName);
|
|
1551
|
+
},
|
|
1552
|
+
async remove(pluginId, scope, cwd) {
|
|
1553
|
+
const path = openCodeConfigPath(scope, cwd);
|
|
1554
|
+
const config = await readJson(path);
|
|
1555
|
+
const name = splitPluginId(pluginId)?.name ?? pluginId;
|
|
1556
|
+
const plugins = config.plugin || [];
|
|
1557
|
+
const remaining = plugins.filter((entry) => specName(entry) !== name);
|
|
1558
|
+
if (remaining.length === plugins.length) return false;
|
|
1559
|
+
config.plugin = remaining;
|
|
1560
|
+
await writeJson(path, config);
|
|
1561
|
+
return true;
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1564
|
+
var GEMINI_CONFIG_DIR = join4(home, ".gemini", "config");
|
|
1565
|
+
var GEMINI_PLUGINS_DIR = join4(GEMINI_CONFIG_DIR, "plugins");
|
|
1566
|
+
var GEMINI_IMPORT_MANIFEST_PATH = join4(GEMINI_CONFIG_DIR, "import_manifest.json");
|
|
1567
|
+
var antigravityStore = {
|
|
1568
|
+
manifestPaths: manifestsFor(),
|
|
1569
|
+
userGlobal: true,
|
|
1570
|
+
cacheRoot: GEMINI_PLUGINS_DIR,
|
|
1571
|
+
// Antigravity files plugins by name only — it has no marketplace dimension.
|
|
1572
|
+
cachePath: (_marketplace, name) => resolveContained(GEMINI_PLUGINS_DIR, name),
|
|
1573
|
+
mutations: (context) => [
|
|
1574
|
+
{
|
|
1575
|
+
path: GEMINI_IMPORT_MANIFEST_PATH,
|
|
1576
|
+
detail: `imports[] += "${context.name}"`,
|
|
1577
|
+
apply: async () => {
|
|
1578
|
+
const manifest = await readJson(GEMINI_IMPORT_MANIFEST_PATH);
|
|
1579
|
+
const imports = manifest.imports || [];
|
|
1580
|
+
manifest.imports = [
|
|
1581
|
+
...imports.filter((entry) => entry.name !== context.name),
|
|
1582
|
+
{
|
|
1583
|
+
name: context.name,
|
|
1584
|
+
source: "seedr",
|
|
1585
|
+
importedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1586
|
+
components: ["skills", "hooks"]
|
|
1587
|
+
}
|
|
1588
|
+
];
|
|
1589
|
+
await writeJson(GEMINI_IMPORT_MANIFEST_PATH, manifest);
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
],
|
|
1593
|
+
async listInstalled() {
|
|
1594
|
+
const manifest = await readJson(GEMINI_IMPORT_MANIFEST_PATH);
|
|
1595
|
+
return (manifest.imports || []).map((entry) => entry.name);
|
|
1596
|
+
},
|
|
1597
|
+
async remove(pluginId) {
|
|
1598
|
+
const name = splitPluginId(pluginId)?.name ?? pluginId;
|
|
1599
|
+
const manifest = await readJson(GEMINI_IMPORT_MANIFEST_PATH);
|
|
1600
|
+
const imports = manifest.imports || [];
|
|
1601
|
+
const remaining = imports.filter((entry) => entry.name !== name);
|
|
1602
|
+
if (remaining.length === imports.length) return false;
|
|
1603
|
+
manifest.imports = remaining;
|
|
1604
|
+
await writeJson(GEMINI_IMPORT_MANIFEST_PATH, manifest);
|
|
1605
|
+
try {
|
|
1606
|
+
await removePathEntry(await resolveContained(GEMINI_PLUGINS_DIR, name));
|
|
1607
|
+
} catch {
|
|
1608
|
+
}
|
|
1609
|
+
return true;
|
|
1610
|
+
}
|
|
1611
|
+
};
|
|
1612
|
+
var GITHUB_REPO_SLUG_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
1613
|
+
function githubRepoOf(url) {
|
|
1614
|
+
if (!url) return null;
|
|
1615
|
+
const parsed = parseGitHubRepo(url);
|
|
1616
|
+
if (!parsed) return null;
|
|
1617
|
+
const repo = `${parsed.owner}/${parsed.repo}`;
|
|
1618
|
+
if (!GITHUB_REPO_SLUG_PATTERN.test(repo) || repo.includes("..")) return null;
|
|
1619
|
+
return repo;
|
|
1620
|
+
}
|
|
1621
|
+
function marketplaceRepo(item) {
|
|
1622
|
+
return githubRepoOf(item.marketplaceRef?.url ?? item.externalUrl);
|
|
1623
|
+
}
|
|
1624
|
+
var STORES = {
|
|
1625
|
+
claude: claudeStore,
|
|
1626
|
+
copilot: copilotStore,
|
|
1627
|
+
codex: codexStore,
|
|
1628
|
+
opencode: openCodeStore,
|
|
1629
|
+
antigravity: antigravityStore,
|
|
1630
|
+
gemini: antigravityStore
|
|
1631
|
+
};
|
|
1632
|
+
function pluginStoreFor(agent) {
|
|
1633
|
+
const store = STORES[canonicalAgent(agent) ?? agent];
|
|
1634
|
+
if (!store || !isTypeSupported("plugin", agent)) {
|
|
1635
|
+
throw new Error(`Plugins are not supported for ${CODING_AGENTS[agent]?.name ?? agent}`);
|
|
1636
|
+
}
|
|
1637
|
+
return store;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
// src/handlers/plugin.ts
|
|
1641
|
+
var SLUG_LABEL3 = "plugin slug";
|
|
1296
1642
|
function resolveMarketplaceName(item) {
|
|
1297
1643
|
const marketplace = item.marketplace || item.author?.name || "seedr";
|
|
1298
1644
|
assertSafePathSegment(marketplace, "plugin marketplace");
|
|
@@ -1305,48 +1651,29 @@ function resolvePluginIdentity(item, pluginJson) {
|
|
|
1305
1651
|
assertSafePathSegment(version, "plugin version");
|
|
1306
1652
|
return { name, version };
|
|
1307
1653
|
}
|
|
1308
|
-
function
|
|
1309
|
-
const
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
return
|
|
1314
|
-
}
|
|
1315
|
-
function marketplaceRepoUrl(item) {
|
|
1316
|
-
return item.marketplaceRef?.url ?? item.externalUrl;
|
|
1654
|
+
async function readManifestFromDir(dir, manifestPaths) {
|
|
1655
|
+
for (const manifest of manifestPaths) {
|
|
1656
|
+
const path = join5(dir, ...manifest.split("/"));
|
|
1657
|
+
if (await exists(path)) return readJson(path);
|
|
1658
|
+
}
|
|
1659
|
+
return {};
|
|
1317
1660
|
}
|
|
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);
|
|
1661
|
+
async function readManifestForPlan(item, manifestPaths) {
|
|
1662
|
+
const sourcePath = getItemSourcePath(item);
|
|
1663
|
+
if (sourcePath) return readManifestFromDir(sourcePath, manifestPaths);
|
|
1664
|
+
for (const manifest of manifestPaths) {
|
|
1665
|
+
try {
|
|
1666
|
+
return JSON.parse(await fetchItemFile(item, manifest));
|
|
1667
|
+
} catch {
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
throw new Error(`No plugin manifest for "${item.slug}" (looked for ${manifestPaths.join(", ")})`);
|
|
1344
1671
|
}
|
|
1345
|
-
async function createStagingDir(slug) {
|
|
1346
|
-
const tmpRoot = await resolveContained(
|
|
1347
|
-
await
|
|
1348
|
-
const staging = await mkdtemp2(
|
|
1349
|
-
return resolveContained(
|
|
1672
|
+
async function createStagingDir(cacheRoot, slug) {
|
|
1673
|
+
const tmpRoot = await resolveContained(cacheRoot, ".tmp");
|
|
1674
|
+
await mkdir3(tmpRoot, { recursive: true });
|
|
1675
|
+
const staging = await mkdtemp2(join5(tmpRoot, `${slug}-`));
|
|
1676
|
+
return resolveContained(cacheRoot, relative3(cacheRoot, staging));
|
|
1350
1677
|
}
|
|
1351
1678
|
async function stageContent(item, contentPath) {
|
|
1352
1679
|
const sourcePath = getItemSourcePath(item);
|
|
@@ -1365,37 +1692,6 @@ function resolveGitCommitSha(item, fetched) {
|
|
|
1365
1692
|
}
|
|
1366
1693
|
return sha;
|
|
1367
1694
|
}
|
|
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
1695
|
async function rollback(undo, error2) {
|
|
1400
1696
|
const failures = [];
|
|
1401
1697
|
for (const step2 of undo.reverse()) {
|
|
@@ -1411,70 +1707,75 @@ async function rollback(undo, error2) {
|
|
|
1411
1707
|
}
|
|
1412
1708
|
throw error2;
|
|
1413
1709
|
}
|
|
1414
|
-
async function commitInstall(
|
|
1415
|
-
const { contentPath, cachePath, pluginId, scope, cwd } = params;
|
|
1710
|
+
async function commitInstall(contentPath, cachePath, mutations) {
|
|
1416
1711
|
const undo = [];
|
|
1417
1712
|
try {
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1713
|
+
if (contentPath && cachePath) {
|
|
1714
|
+
await removePathEntry(cachePath);
|
|
1715
|
+
await moveDirectory(contentPath, cachePath);
|
|
1716
|
+
undo.push(() => removePathEntry(cachePath).then(() => void 0));
|
|
1717
|
+
}
|
|
1718
|
+
for (const mutation of mutations) {
|
|
1719
|
+
const snapshot = await snapshotFile(mutation.path);
|
|
1720
|
+
await mutation.apply();
|
|
1721
|
+
undo.push(() => restoreFile(mutation.path, snapshot));
|
|
1722
|
+
}
|
|
1428
1723
|
} catch (error2) {
|
|
1429
1724
|
await rollback(undo, error2);
|
|
1430
1725
|
}
|
|
1431
1726
|
}
|
|
1432
1727
|
async function installPluginForAgent(item, agent, scope, _method, force, cwd) {
|
|
1433
|
-
const spinner = ora6(
|
|
1434
|
-
`Installing ${item.name} for ${CODING_AGENTS[agent].name}...`
|
|
1435
|
-
).start();
|
|
1728
|
+
const spinner = ora6(`Installing ${item.name} for ${CODING_AGENTS[agent].name}...`).start();
|
|
1436
1729
|
try {
|
|
1437
|
-
|
|
1438
|
-
throw new Error(CLAUDE_ONLY_ERROR3);
|
|
1439
|
-
}
|
|
1730
|
+
const store = pluginStoreFor(agent);
|
|
1440
1731
|
assertValidSlug(item.slug, SLUG_LABEL3);
|
|
1441
1732
|
const marketplace = resolveMarketplaceName(item);
|
|
1442
|
-
const
|
|
1733
|
+
const cacheRoot = store.cacheRoot;
|
|
1734
|
+
const staging = cacheRoot ? await createStagingDir(cacheRoot, item.slug) : null;
|
|
1443
1735
|
try {
|
|
1444
|
-
const contentPath =
|
|
1445
|
-
const fetched = await stageContent(item, contentPath);
|
|
1446
|
-
const pluginJson = await
|
|
1736
|
+
const contentPath = staging ? join5(staging, "content") : null;
|
|
1737
|
+
const fetched = contentPath ? await stageContent(item, contentPath) : null;
|
|
1738
|
+
const pluginJson = contentPath ? await readManifestFromDir(contentPath, store.manifestPaths) : await readManifestForPlan(item, store.manifestPaths);
|
|
1447
1739
|
const { name, version } = resolvePluginIdentity(item, pluginJson);
|
|
1448
1740
|
const gitCommitSha = resolveGitCommitSha(item, fetched);
|
|
1449
1741
|
const pluginId = getPluginId(name, marketplace);
|
|
1450
|
-
await
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1742
|
+
const cachePath = await store.cachePath(marketplace, name, version);
|
|
1743
|
+
const marketplaceMutations = store.ensureMarketplace ? await store.ensureMarketplace(marketplace, item) : [];
|
|
1744
|
+
const context = {
|
|
1745
|
+
item,
|
|
1746
|
+
marketplace,
|
|
1747
|
+
name,
|
|
1748
|
+
version,
|
|
1749
|
+
pluginId,
|
|
1750
|
+
cachePath,
|
|
1751
|
+
gitCommitSha,
|
|
1752
|
+
scope,
|
|
1753
|
+
cwd
|
|
1754
|
+
};
|
|
1755
|
+
if (cachePath) await assertOverwritable(cachePath, force);
|
|
1756
|
+
await commitInstall(contentPath, cachePath, [
|
|
1757
|
+
...marketplaceMutations,
|
|
1758
|
+
...store.mutations(context)
|
|
1759
|
+
]);
|
|
1760
|
+
spinner.succeed(brand(`Installed ${item.name} for ${CODING_AGENTS[agent].name}`));
|
|
1761
|
+
return { agent, success: true, path: cachePath ?? "" };
|
|
1458
1762
|
} finally {
|
|
1459
|
-
await rm2(staging, { recursive: true, force: true });
|
|
1763
|
+
if (staging) await rm2(staging, { recursive: true, force: true });
|
|
1460
1764
|
}
|
|
1461
1765
|
} catch (error2) {
|
|
1462
1766
|
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
|
-
);
|
|
1767
|
+
spinner.fail(chalk7.red(`Failed to install for ${CODING_AGENTS[agent].name}: ${errorMsg}`));
|
|
1466
1768
|
return { agent, success: false, path: "", error: errorMsg };
|
|
1467
1769
|
}
|
|
1468
1770
|
}
|
|
1469
1771
|
async function installPlugin(item, agents, scope, method, force, cwd = process.cwd()) {
|
|
1470
1772
|
const results = [];
|
|
1471
1773
|
for (const agent of agents) {
|
|
1472
|
-
|
|
1473
|
-
results.push(result);
|
|
1774
|
+
results.push(await installPluginForAgent(item, agent, scope, method, force, cwd));
|
|
1474
1775
|
}
|
|
1475
1776
|
return results;
|
|
1476
1777
|
}
|
|
1477
|
-
async function findInstalledPluginId(
|
|
1778
|
+
async function findInstalledPluginId(installed, slug) {
|
|
1478
1779
|
let registryMarketplace = null;
|
|
1479
1780
|
try {
|
|
1480
1781
|
const item = await getItem(slug, "plugin");
|
|
@@ -1484,70 +1785,34 @@ async function findInstalledPluginId(registry, slug) {
|
|
|
1484
1785
|
}
|
|
1485
1786
|
if (registryMarketplace !== null) {
|
|
1486
1787
|
const exact = getPluginId(slug, registryMarketplace);
|
|
1487
|
-
|
|
1788
|
+
if (installed.includes(exact)) return exact;
|
|
1789
|
+
return installed.includes(slug) ? slug : null;
|
|
1488
1790
|
}
|
|
1489
|
-
const candidates =
|
|
1791
|
+
const candidates = installed.filter((id) => (splitPluginId(id)?.name ?? id) === slug);
|
|
1490
1792
|
return candidates.length === 1 ? candidates[0] : null;
|
|
1491
1793
|
}
|
|
1492
|
-
async function
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
);
|
|
1496
|
-
if (stillReferenced) return;
|
|
1497
|
-
let contained;
|
|
1794
|
+
async function uninstallPlugin(slug, agent, scope, cwd = process.cwd()) {
|
|
1795
|
+
assertValidSlug(slug, SLUG_LABEL3);
|
|
1796
|
+
let store;
|
|
1498
1797
|
try {
|
|
1499
|
-
|
|
1798
|
+
store = pluginStoreFor(agent);
|
|
1500
1799
|
} catch {
|
|
1501
|
-
return;
|
|
1800
|
+
return false;
|
|
1502
1801
|
}
|
|
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);
|
|
1802
|
+
const installed = await store.listInstalled(scope, cwd);
|
|
1803
|
+
const pluginId = await findInstalledPluginId(installed, slug);
|
|
1511
1804
|
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;
|
|
1805
|
+
return store.remove(pluginId, scope, cwd);
|
|
1532
1806
|
}
|
|
1533
1807
|
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("/")));
|
|
1808
|
+
let store;
|
|
1809
|
+
try {
|
|
1810
|
+
store = pluginStoreFor(agent);
|
|
1811
|
+
} catch {
|
|
1812
|
+
return [];
|
|
1549
1813
|
}
|
|
1550
|
-
|
|
1814
|
+
const installed = await store.listInstalled(scope, cwd);
|
|
1815
|
+
return installed.map((pluginId) => splitPluginId(pluginId)?.name ?? pluginId);
|
|
1551
1816
|
}
|
|
1552
1817
|
async function kindFor(path) {
|
|
1553
1818
|
return await exists(path) ? "modify" : "create";
|
|
@@ -1555,40 +1820,53 @@ async function kindFor(path) {
|
|
|
1555
1820
|
async function planPlugin(item, agents, scope, _method, cwd) {
|
|
1556
1821
|
const changes = [];
|
|
1557
1822
|
for (const agent of agents) {
|
|
1558
|
-
|
|
1823
|
+
const store = pluginStoreFor(agent);
|
|
1559
1824
|
assertValidSlug(item.slug, SLUG_LABEL3);
|
|
1560
1825
|
const marketplace = resolveMarketplaceName(item);
|
|
1561
|
-
const { name, version } = resolvePluginIdentity(
|
|
1826
|
+
const { name, version } = resolvePluginIdentity(
|
|
1827
|
+
item,
|
|
1828
|
+
await readManifestForPlan(item, store.manifestPaths)
|
|
1829
|
+
);
|
|
1562
1830
|
const pluginId = getPluginId(name, marketplace);
|
|
1563
|
-
const cachePath = await
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
{
|
|
1567
|
-
{
|
|
1831
|
+
const cachePath = await store.cachePath(marketplace, name, version);
|
|
1832
|
+
resolveGitCommitSha(item, null);
|
|
1833
|
+
if (cachePath) {
|
|
1834
|
+
changes.push({
|
|
1568
1835
|
agent,
|
|
1569
|
-
kind: await kindFor(
|
|
1570
|
-
path:
|
|
1571
|
-
detail:
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
const
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1836
|
+
kind: await kindFor(cachePath),
|
|
1837
|
+
path: cachePath,
|
|
1838
|
+
detail: "plugin files (digest-verified download)"
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
const marketplaceMutations = store.ensureMarketplace ? await store.ensureMarketplace(marketplace, item) : [];
|
|
1842
|
+
const context = {
|
|
1843
|
+
item,
|
|
1844
|
+
marketplace,
|
|
1845
|
+
name,
|
|
1846
|
+
version,
|
|
1847
|
+
pluginId,
|
|
1848
|
+
cachePath,
|
|
1849
|
+
gitCommitSha: resolveGitCommitSha(item, null),
|
|
1850
|
+
scope,
|
|
1851
|
+
cwd
|
|
1852
|
+
};
|
|
1853
|
+
for (const mutation of [...marketplaceMutations, ...store.mutations(context)]) {
|
|
1854
|
+
changes.push({
|
|
1855
|
+
agent,
|
|
1856
|
+
kind: await kindFor(mutation.path),
|
|
1857
|
+
path: mutation.path,
|
|
1858
|
+
detail: store.userGlobal ? `${mutation.detail} \u2014 user-global, ${CODING_AGENTS[agent].name} has no project-scoped plugin store` : mutation.detail
|
|
1859
|
+
});
|
|
1860
|
+
if (mutation.creates) {
|
|
1861
|
+
changes.push({ agent, kind: "create", ...mutation.creates });
|
|
1584
1862
|
}
|
|
1585
1863
|
}
|
|
1586
|
-
changes.push({ agent, kind: await kindFor(settingsPath), path: settingsPath, detail: `enabledPlugins["${pluginId}"] = true` });
|
|
1587
1864
|
}
|
|
1588
1865
|
return changes;
|
|
1589
1866
|
}
|
|
1590
1867
|
var pluginHandler = {
|
|
1591
1868
|
type: "plugin",
|
|
1869
|
+
honoursMethod: false,
|
|
1592
1870
|
async install(item, agents, scope, method, force, cwd) {
|
|
1593
1871
|
return installPlugin(item, agents, scope, method, force, cwd);
|
|
1594
1872
|
},
|
|
@@ -1601,6 +1879,216 @@ var pluginHandler = {
|
|
|
1601
1879
|
plan: planPlugin
|
|
1602
1880
|
};
|
|
1603
1881
|
|
|
1882
|
+
// src/handlers/rule.ts
|
|
1883
|
+
import { dirname as dirname3 } from "path";
|
|
1884
|
+
import { readdir as readdir3, readFile as readFile4, writeFile as writeFile2, mkdir as mkdir4 } from "fs/promises";
|
|
1885
|
+
import chalk8 from "chalk";
|
|
1886
|
+
import ora7 from "ora";
|
|
1887
|
+
|
|
1888
|
+
// src/handlers/ruleTargets.ts
|
|
1889
|
+
import { homedir as homedir5 } from "os";
|
|
1890
|
+
import { join as join6 } from "path";
|
|
1891
|
+
var home2 = homedir5();
|
|
1892
|
+
var markdownFile = (slug) => `${slug}.md`;
|
|
1893
|
+
var claudeTarget = {
|
|
1894
|
+
kind: "file",
|
|
1895
|
+
dir: (scope, cwd) => join6(scope === "user" ? claudeUserRoot() : join6(cwd, ".claude"), "rules"),
|
|
1896
|
+
fileName: markdownFile,
|
|
1897
|
+
keepsFrontmatter: true
|
|
1898
|
+
};
|
|
1899
|
+
var antigravityTarget = {
|
|
1900
|
+
kind: "file",
|
|
1901
|
+
dir: (scope, cwd) => join6(scope === "user" ? join6(home2, ".gemini", "config") : join6(cwd, ".agents"), "rules"),
|
|
1902
|
+
fileName: markdownFile,
|
|
1903
|
+
keepsFrontmatter: true
|
|
1904
|
+
};
|
|
1905
|
+
var copilotTarget = {
|
|
1906
|
+
kind: "file",
|
|
1907
|
+
dir: (scope, cwd) => join6(scope === "user" ? join6(home2, ".copilot") : join6(cwd, ".github"), "instructions"),
|
|
1908
|
+
fileName: (slug) => `${slug}.instructions.md`,
|
|
1909
|
+
keepsFrontmatter: true
|
|
1910
|
+
};
|
|
1911
|
+
var codexTarget = {
|
|
1912
|
+
kind: "section",
|
|
1913
|
+
file: (scope, cwd) => scope === "user" ? join6(home2, ".codex", "AGENTS.md") : join6(cwd, "AGENTS.md")
|
|
1914
|
+
};
|
|
1915
|
+
var openCodeTarget = {
|
|
1916
|
+
kind: "section",
|
|
1917
|
+
file: (scope, cwd) => scope === "user" ? join6(home2, ".config", "opencode", "AGENTS.md") : join6(cwd, "AGENTS.md")
|
|
1918
|
+
};
|
|
1919
|
+
var TARGETS = {
|
|
1920
|
+
claude: claudeTarget,
|
|
1921
|
+
antigravity: antigravityTarget,
|
|
1922
|
+
gemini: antigravityTarget,
|
|
1923
|
+
copilot: copilotTarget,
|
|
1924
|
+
codex: codexTarget,
|
|
1925
|
+
opencode: openCodeTarget
|
|
1926
|
+
};
|
|
1927
|
+
function ruleTargetFor(agent) {
|
|
1928
|
+
const target = TARGETS[canonicalAgent(agent) ?? agent];
|
|
1929
|
+
if (!target || !isTypeSupported("rule", agent)) {
|
|
1930
|
+
throw new Error(`Rules are not supported for ${CODING_AGENTS[agent]?.name ?? agent}`);
|
|
1931
|
+
}
|
|
1932
|
+
return target;
|
|
1933
|
+
}
|
|
1934
|
+
var sectionStart = (slug) => `<!-- seedr:rule:${slug} -->`;
|
|
1935
|
+
var sectionEnd = (slug) => `<!-- /seedr:rule:${slug} -->`;
|
|
1936
|
+
function sectionPattern(slug) {
|
|
1937
|
+
const escaped = slug.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1938
|
+
return new RegExp(`\\n*<!-- seedr:rule:${escaped} -->[\\s\\S]*?<!-- /seedr:rule:${escaped} -->\\n*`, "g");
|
|
1939
|
+
}
|
|
1940
|
+
function stripFrontmatter(content) {
|
|
1941
|
+
const match = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/.exec(content);
|
|
1942
|
+
return match ? content.slice(match[0].length) : content;
|
|
1943
|
+
}
|
|
1944
|
+
function upsertSection(document, slug, content) {
|
|
1945
|
+
const block = `${sectionStart(slug)}
|
|
1946
|
+
${stripFrontmatter(content).trim()}
|
|
1947
|
+
${sectionEnd(slug)}`;
|
|
1948
|
+
const existing = sectionPattern(slug);
|
|
1949
|
+
if (existing.test(document)) {
|
|
1950
|
+
return document.replace(sectionPattern(slug), `
|
|
1951
|
+
|
|
1952
|
+
${block}
|
|
1953
|
+
`);
|
|
1954
|
+
}
|
|
1955
|
+
const base = document.trimEnd();
|
|
1956
|
+
return base.length > 0 ? `${base}
|
|
1957
|
+
|
|
1958
|
+
${block}
|
|
1959
|
+
` : `${block}
|
|
1960
|
+
`;
|
|
1961
|
+
}
|
|
1962
|
+
function removeSection(document, slug) {
|
|
1963
|
+
if (!sectionPattern(slug).test(document)) return null;
|
|
1964
|
+
const stripped = document.replace(sectionPattern(slug), "\n\n").trimEnd();
|
|
1965
|
+
return stripped.length > 0 ? `${stripped}
|
|
1966
|
+
` : "";
|
|
1967
|
+
}
|
|
1968
|
+
function listSections(document) {
|
|
1969
|
+
return [...document.matchAll(/<!-- seedr:rule:([^\s>]+) -->/g)].map((match) => match[1]);
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
// src/handlers/rule.ts
|
|
1973
|
+
var SLUG_LABEL4 = "rule slug";
|
|
1974
|
+
var RULE_FILE = "rule.md";
|
|
1975
|
+
async function readTextOrEmpty3(path) {
|
|
1976
|
+
try {
|
|
1977
|
+
return await readFile4(path, "utf-8");
|
|
1978
|
+
} catch {
|
|
1979
|
+
return "";
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
async function readRuleContent(item) {
|
|
1983
|
+
const sourcePath = getItemSourcePath(item);
|
|
1984
|
+
if (sourcePath) {
|
|
1985
|
+
const local = await readTextOrEmpty3(`${sourcePath}/${RULE_FILE}`);
|
|
1986
|
+
if (local) return local;
|
|
1987
|
+
}
|
|
1988
|
+
return fetchItemFile(item, RULE_FILE);
|
|
1989
|
+
}
|
|
1990
|
+
async function destinationFor(target, slug, scope, cwd) {
|
|
1991
|
+
if (target.kind === "section") return target.file(scope, cwd);
|
|
1992
|
+
const dir = target.dir(scope, cwd);
|
|
1993
|
+
return resolveContained(dir, target.fileName(slug));
|
|
1994
|
+
}
|
|
1995
|
+
async function installRuleForAgent(item, agent, scope, force, cwd, content) {
|
|
1996
|
+
const spinner = ora7(`Installing ${item.name} for ${CODING_AGENTS[agent].name}...`).start();
|
|
1997
|
+
try {
|
|
1998
|
+
const target = ruleTargetFor(agent);
|
|
1999
|
+
const destination = await destinationFor(target, item.slug, scope, cwd);
|
|
2000
|
+
await mkdir4(dirname3(destination), { recursive: true });
|
|
2001
|
+
if (target.kind === "file") {
|
|
2002
|
+
await assertOverwritable(destination, force);
|
|
2003
|
+
await writeFile2(
|
|
2004
|
+
destination,
|
|
2005
|
+
target.keepsFrontmatter ? content : stripFrontmatter(content),
|
|
2006
|
+
"utf-8"
|
|
2007
|
+
);
|
|
2008
|
+
} else {
|
|
2009
|
+
const document = await readTextOrEmpty3(destination);
|
|
2010
|
+
await writeFile2(destination, upsertSection(document, item.slug, content), "utf-8");
|
|
2011
|
+
}
|
|
2012
|
+
spinner.succeed(brand(`Installed ${item.name} for ${CODING_AGENTS[agent].name}`));
|
|
2013
|
+
return { agent, success: true, path: destination };
|
|
2014
|
+
} catch (error2) {
|
|
2015
|
+
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
2016
|
+
spinner.fail(chalk8.red(`Failed to install for ${CODING_AGENTS[agent].name}: ${message}`));
|
|
2017
|
+
return { agent, success: false, path: "", error: message };
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
async function installRule(item, agents, scope, _method, force, cwd = process.cwd()) {
|
|
2021
|
+
assertValidSlug(item.slug, SLUG_LABEL4);
|
|
2022
|
+
const content = await readRuleContent(item);
|
|
2023
|
+
const results = [];
|
|
2024
|
+
for (const agent of agents) {
|
|
2025
|
+
results.push(await installRuleForAgent(item, agent, scope, force, cwd, content));
|
|
2026
|
+
}
|
|
2027
|
+
return results;
|
|
2028
|
+
}
|
|
2029
|
+
async function uninstallRule(slug, agent, scope, cwd = process.cwd()) {
|
|
2030
|
+
assertValidSlug(slug, SLUG_LABEL4);
|
|
2031
|
+
let target;
|
|
2032
|
+
try {
|
|
2033
|
+
target = ruleTargetFor(agent);
|
|
2034
|
+
} catch {
|
|
2035
|
+
return false;
|
|
2036
|
+
}
|
|
2037
|
+
const destination = await destinationFor(target, slug, scope, cwd);
|
|
2038
|
+
if (target.kind === "file") return removePathEntry(destination);
|
|
2039
|
+
const document = await readTextOrEmpty3(destination);
|
|
2040
|
+
const stripped = removeSection(document, slug);
|
|
2041
|
+
if (stripped === null) return false;
|
|
2042
|
+
await writeFile2(destination, stripped, "utf-8");
|
|
2043
|
+
return true;
|
|
2044
|
+
}
|
|
2045
|
+
async function getInstalledRules(agent, scope, cwd = process.cwd()) {
|
|
2046
|
+
let target;
|
|
2047
|
+
try {
|
|
2048
|
+
target = ruleTargetFor(agent);
|
|
2049
|
+
} catch {
|
|
2050
|
+
return [];
|
|
2051
|
+
}
|
|
2052
|
+
if (target.kind === "section") {
|
|
2053
|
+
return listSections(await readTextOrEmpty3(target.file(scope, cwd)));
|
|
2054
|
+
}
|
|
2055
|
+
const dir = target.dir(scope, cwd);
|
|
2056
|
+
if (!await exists(dir)) return [];
|
|
2057
|
+
const suffix = target.fileName("");
|
|
2058
|
+
const entries = await readdir3(dir, { withFileTypes: true });
|
|
2059
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(suffix) && !entry.name.startsWith(".")).map((entry) => entry.name.slice(0, -suffix.length));
|
|
2060
|
+
}
|
|
2061
|
+
async function planRule(item, agents, scope, _method, cwd) {
|
|
2062
|
+
assertValidSlug(item.slug, SLUG_LABEL4);
|
|
2063
|
+
const changes = [];
|
|
2064
|
+
for (const agent of agents) {
|
|
2065
|
+
const target = ruleTargetFor(agent);
|
|
2066
|
+
const destination = await destinationFor(target, item.slug, scope, cwd);
|
|
2067
|
+
const present = await exists(destination);
|
|
2068
|
+
changes.push({
|
|
2069
|
+
agent,
|
|
2070
|
+
kind: present ? "modify" : "create",
|
|
2071
|
+
path: destination,
|
|
2072
|
+
detail: target.kind === "file" ? "rule file" : `merged section <!-- seedr:rule:${item.slug} --> (the rest of the file is untouched)`
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
return changes;
|
|
2076
|
+
}
|
|
2077
|
+
var ruleHandler = {
|
|
2078
|
+
type: "rule",
|
|
2079
|
+
honoursMethod: false,
|
|
2080
|
+
async install(item, agents, scope, method, force, cwd) {
|
|
2081
|
+
return installRule(item, agents, scope, method, force, cwd);
|
|
2082
|
+
},
|
|
2083
|
+
async uninstall(slug, agent, scope, cwd) {
|
|
2084
|
+
return uninstallRule(slug, agent, scope, cwd);
|
|
2085
|
+
},
|
|
2086
|
+
async listInstalled(agent, scope, cwd) {
|
|
2087
|
+
return getInstalledRules(agent, scope, cwd);
|
|
2088
|
+
},
|
|
2089
|
+
plan: planRule
|
|
2090
|
+
};
|
|
2091
|
+
|
|
1604
2092
|
// src/handlers/index.ts
|
|
1605
2093
|
registerHandler(skillHandler);
|
|
1606
2094
|
registerHandler(agentHandler);
|
|
@@ -1609,6 +2097,7 @@ registerHandler(hookHandler);
|
|
|
1609
2097
|
registerHandler(mcpHandler);
|
|
1610
2098
|
registerHandler(settingsHandler);
|
|
1611
2099
|
registerHandler(pluginHandler);
|
|
2100
|
+
registerHandler(ruleHandler);
|
|
1612
2101
|
|
|
1613
2102
|
// src/commands/add.ts
|
|
1614
2103
|
var CANCELLED_MESSAGE = "Operation cancelled";
|
|
@@ -1689,8 +2178,8 @@ function formatPlan(changes) {
|
|
|
1689
2178
|
for (const [label, group] of groups) {
|
|
1690
2179
|
lines.push(` ${label}:`);
|
|
1691
2180
|
for (const change of group) {
|
|
1692
|
-
const detail = change.detail ? ` ${
|
|
1693
|
-
lines.push(` ${
|
|
2181
|
+
const detail = change.detail ? ` ${chalk9.gray(`\u2014 ${change.detail}`)}` : "";
|
|
2182
|
+
lines.push(` ${chalk9.gray(`[${KIND_LABEL[change.kind]}]`)} ${change.path}${detail}`);
|
|
1694
2183
|
}
|
|
1695
2184
|
}
|
|
1696
2185
|
return lines;
|
|
@@ -1800,13 +2289,13 @@ function printInstallSummary(results) {
|
|
|
1800
2289
|
if (successful.length > 0) {
|
|
1801
2290
|
success(`Installed for ${successful.length} agent(s)`);
|
|
1802
2291
|
for (const r of successful) {
|
|
1803
|
-
console.log(
|
|
2292
|
+
console.log(chalk9.gray(` \u2192 ${r.path}`));
|
|
1804
2293
|
}
|
|
1805
2294
|
}
|
|
1806
2295
|
if (failed.length > 0) {
|
|
1807
2296
|
error(`Failed for ${failed.length} agent(s)`);
|
|
1808
2297
|
for (const r of failed) {
|
|
1809
|
-
console.log(
|
|
2298
|
+
console.log(chalk9.red(` \xD7 ${r.agent}: ${r.error}`));
|
|
1810
2299
|
}
|
|
1811
2300
|
}
|
|
1812
2301
|
return exitCode;
|
|
@@ -1820,7 +2309,7 @@ async function runAdd(name, options, cwd = process.cwd()) {
|
|
|
1820
2309
|
const item = await resolveItem(name, options.type);
|
|
1821
2310
|
if (item === CANCELLED) return 0;
|
|
1822
2311
|
if (!item) return 1;
|
|
1823
|
-
step(`Selected: ${brand(item.name)} ${
|
|
2312
|
+
step(`Selected: ${brand(item.name)} ${chalk9.gray(`(${item.type})`)} ${chalk9.gray(`- ${item.description}`)}`);
|
|
1824
2313
|
const handler = getHandler(item.type);
|
|
1825
2314
|
if (!handler) {
|
|
1826
2315
|
error(`No handler found for type "${item.type}"`);
|
|
@@ -1835,7 +2324,11 @@ async function runAdd(name, options, cwd = process.cwd()) {
|
|
|
1835
2324
|
step(`Scope: ${brand(scope)}`);
|
|
1836
2325
|
const method = await chooseMethod(options, item, agents, scope, cwd);
|
|
1837
2326
|
if (method === CANCELLED) return 0;
|
|
1838
|
-
|
|
2327
|
+
if (method === "symlink" && handler.honoursMethod === false) {
|
|
2328
|
+
step(`Method: ${brand("copy")} (${item.type} content cannot be symlinked)`);
|
|
2329
|
+
} else {
|
|
2330
|
+
step(`Method: ${brand(method)}`);
|
|
2331
|
+
}
|
|
1839
2332
|
if (options.dryRun) {
|
|
1840
2333
|
info("Dry run - no files will be written");
|
|
1841
2334
|
printPlan("Would write:", await planInstall(handler, item, agents, scope, method, cwd));
|
|
@@ -1857,7 +2350,7 @@ async function runAdd(name, options, cwd = process.cwd()) {
|
|
|
1857
2350
|
if (exitCode === 0) outro("Installation complete");
|
|
1858
2351
|
return exitCode;
|
|
1859
2352
|
}
|
|
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>",
|
|
2353
|
+
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
2354
|
"-a, --agents <agents>",
|
|
1862
2355
|
"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
2356
|
).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 +2367,18 @@ Telemetry: ${TELEMETRY_HELP_TEXT}.`).action(async (name, options) => {
|
|
|
1874
2367
|
|
|
1875
2368
|
// src/commands/list.ts
|
|
1876
2369
|
import { Command as Command2 } from "commander";
|
|
1877
|
-
import
|
|
2370
|
+
import chalk10 from "chalk";
|
|
1878
2371
|
var SEPARATOR_WIDTH = 40;
|
|
1879
2372
|
var SLUG_COLUMN_WIDTH = 24;
|
|
1880
2373
|
var TYPE_COLORS = {
|
|
1881
|
-
skill:
|
|
1882
|
-
hook:
|
|
1883
|
-
agent:
|
|
1884
|
-
plugin:
|
|
1885
|
-
command:
|
|
1886
|
-
settings:
|
|
1887
|
-
mcp:
|
|
2374
|
+
skill: chalk10.magenta,
|
|
2375
|
+
hook: chalk10.hex("#a855f7"),
|
|
2376
|
+
agent: chalk10.blue,
|
|
2377
|
+
plugin: chalk10.hex("#6366f1"),
|
|
2378
|
+
command: chalk10.hex("#f59e0b"),
|
|
2379
|
+
settings: chalk10.hex("#f97316"),
|
|
2380
|
+
mcp: chalk10.hex("#2dd4bf"),
|
|
2381
|
+
rule: chalk10.hex("#84cc16")
|
|
1888
2382
|
};
|
|
1889
2383
|
async function collectInstalledItems(params) {
|
|
1890
2384
|
const types = params.types ?? getRegisteredTypes();
|
|
@@ -1913,7 +2407,7 @@ function resolveListAgents(agentsArg) {
|
|
|
1913
2407
|
async function runList(options, cwd = process.cwd()) {
|
|
1914
2408
|
const optionError = validateType(options.type) || validateScope(options.scope);
|
|
1915
2409
|
if (optionError) {
|
|
1916
|
-
console.log(
|
|
2410
|
+
console.log(chalk10.red(optionError));
|
|
1917
2411
|
return 1;
|
|
1918
2412
|
}
|
|
1919
2413
|
const type = options.type;
|
|
@@ -1922,14 +2416,14 @@ async function runList(options, cwd = process.cwd()) {
|
|
|
1922
2416
|
}
|
|
1923
2417
|
const agents = resolveListAgents(options.agents);
|
|
1924
2418
|
if (typeof agents === "string") {
|
|
1925
|
-
console.log(
|
|
2419
|
+
console.log(chalk10.red(agents));
|
|
1926
2420
|
return 1;
|
|
1927
2421
|
}
|
|
1928
2422
|
const scope = options.scope ?? "project";
|
|
1929
2423
|
await listInstalled({ types: type ? [type] : void 0, agents, scope, cwd });
|
|
1930
2424
|
return 0;
|
|
1931
2425
|
}
|
|
1932
|
-
var listCommand = new Command2("list").alias("ls").description("List available or installed items").option("-t, --type <type>",
|
|
2426
|
+
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
2427
|
try {
|
|
1934
2428
|
const exitCode = await runList(options);
|
|
1935
2429
|
if (exitCode !== 0) process.exit(exitCode);
|
|
@@ -1942,32 +2436,32 @@ async function listAvailable(type, label) {
|
|
|
1942
2436
|
const items = label ? all.filter((item) => item.label === label) : all;
|
|
1943
2437
|
if (items.length === 0 && label) {
|
|
1944
2438
|
const known = [...new Set(all.map((item) => item.label).filter(Boolean))].sort();
|
|
1945
|
-
console.log(
|
|
1946
|
-
if (known.length > 0) console.log(
|
|
2439
|
+
console.log(chalk10.red(`No item carries the label "${label}".`));
|
|
2440
|
+
if (known.length > 0) console.log(chalk10.gray(`Labels in use: ${known.join(", ")}`));
|
|
1947
2441
|
return 1;
|
|
1948
2442
|
}
|
|
1949
2443
|
if (items.length === 0) {
|
|
1950
|
-
console.log(
|
|
2444
|
+
console.log(chalk10.yellow("No items found in registry"));
|
|
1951
2445
|
return 0;
|
|
1952
2446
|
}
|
|
1953
2447
|
const grouped = groupByType(items);
|
|
1954
2448
|
for (const [itemType, typeItems] of Object.entries(grouped)) {
|
|
1955
|
-
const colorFn = TYPE_COLORS[itemType] ??
|
|
2449
|
+
const colorFn = TYPE_COLORS[itemType] ?? chalk10.white;
|
|
1956
2450
|
console.log(colorFn(`
|
|
1957
2451
|
${itemType.toUpperCase()}S`));
|
|
1958
|
-
console.log(
|
|
2452
|
+
console.log(chalk10.gray("\u2500".repeat(SEPARATOR_WIDTH)));
|
|
1959
2453
|
for (const item of typeItems) {
|
|
1960
2454
|
const compatIcons = [...new Set(item.compatibility.map((a) => CODING_AGENTS[a]?.shortName ?? a))].join(" ");
|
|
1961
|
-
const featured = item.featured ?
|
|
2455
|
+
const featured = item.featured ? chalk10.yellow("\u2605 ") : " ";
|
|
1962
2456
|
console.log(
|
|
1963
|
-
`${featured}${
|
|
2457
|
+
`${featured}${chalk10.white(item.slug.padEnd(SLUG_COLUMN_WIDTH))} ${chalk10.gray(compatIcons)}`
|
|
1964
2458
|
);
|
|
1965
|
-
console.log(` ${
|
|
2459
|
+
console.log(` ${chalk10.gray(item.description)}`);
|
|
1966
2460
|
}
|
|
1967
2461
|
}
|
|
1968
2462
|
console.log("");
|
|
1969
2463
|
console.log(
|
|
1970
|
-
|
|
2464
|
+
chalk10.gray(`Total: ${items.length} items. Use 'npx @danieldeusing/seedr add <name>' to install.`)
|
|
1971
2465
|
);
|
|
1972
2466
|
return 0;
|
|
1973
2467
|
}
|
|
@@ -1981,23 +2475,23 @@ Installed items (${params.scope} scope):
|
|
|
1981
2475
|
for (const group of groups) {
|
|
1982
2476
|
if (group.type !== currentType) {
|
|
1983
2477
|
currentType = group.type;
|
|
1984
|
-
const colorFn = TYPE_COLORS[group.type] ??
|
|
2478
|
+
const colorFn = TYPE_COLORS[group.type] ?? chalk10.white;
|
|
1985
2479
|
console.log(colorFn(`${group.type.toUpperCase()}S`));
|
|
1986
2480
|
}
|
|
1987
|
-
console.log(
|
|
2481
|
+
console.log(chalk10.blue(` ${CODING_AGENTS[group.agent].name}`));
|
|
1988
2482
|
for (const slug of group.slugs) {
|
|
1989
|
-
console.log(` ${
|
|
2483
|
+
console.log(` ${chalk10.white(slug)}`);
|
|
1990
2484
|
total++;
|
|
1991
2485
|
}
|
|
1992
2486
|
console.log("");
|
|
1993
2487
|
}
|
|
1994
2488
|
if (!params.types || params.types.includes("settings")) {
|
|
1995
|
-
console.log(
|
|
2489
|
+
console.log(chalk10.gray(`Note: ${SETTINGS_NOT_DISCOVERABLE}`));
|
|
1996
2490
|
}
|
|
1997
2491
|
if (total === 0) {
|
|
1998
|
-
console.log(
|
|
2492
|
+
console.log(chalk10.yellow("No items installed"));
|
|
1999
2493
|
} else {
|
|
2000
|
-
console.log(
|
|
2494
|
+
console.log(chalk10.gray(`Total: ${total} installed`));
|
|
2001
2495
|
}
|
|
2002
2496
|
}
|
|
2003
2497
|
function groupByType(items) {
|
|
@@ -2015,8 +2509,8 @@ function groupByType(items) {
|
|
|
2015
2509
|
|
|
2016
2510
|
// src/commands/remove.ts
|
|
2017
2511
|
import { Command as Command3 } from "commander";
|
|
2018
|
-
import
|
|
2019
|
-
import
|
|
2512
|
+
import chalk11 from "chalk";
|
|
2513
|
+
import ora8 from "ora";
|
|
2020
2514
|
async function findInstalledAgents(slug, type, scope, cwd) {
|
|
2021
2515
|
const handler = getHandler(type);
|
|
2022
2516
|
if (!handler) return [];
|
|
@@ -2034,13 +2528,19 @@ async function removeFromAgents(slug, type, agents, scope, cwd) {
|
|
|
2034
2528
|
if (!handler) return 0;
|
|
2035
2529
|
let successCount = 0;
|
|
2036
2530
|
for (const agent of agents) {
|
|
2037
|
-
|
|
2531
|
+
if (!isTypeSupported(type, agent)) {
|
|
2532
|
+
ora8().start().info(
|
|
2533
|
+
chalk11.gray(`Skipped ${CODING_AGENTS[agent].name}: ${describeIncompatibility(type, agent)}`)
|
|
2534
|
+
);
|
|
2535
|
+
continue;
|
|
2536
|
+
}
|
|
2537
|
+
const spinner = ora8(`Removing from ${CODING_AGENTS[agent].name}...`).start();
|
|
2038
2538
|
const removed = await handler.uninstall(slug, agent, scope, cwd);
|
|
2039
2539
|
if (removed) {
|
|
2040
2540
|
spinner.succeed(brand(`Removed from ${CODING_AGENTS[agent].name}`));
|
|
2041
2541
|
successCount++;
|
|
2042
2542
|
} else {
|
|
2043
|
-
spinner.info(
|
|
2543
|
+
spinner.info(chalk11.gray(`Not found in ${CODING_AGENTS[agent].name}`));
|
|
2044
2544
|
}
|
|
2045
2545
|
}
|
|
2046
2546
|
return successCount;
|
|
@@ -2065,14 +2565,14 @@ function validateRemoveRequest(name, options) {
|
|
|
2065
2565
|
async function runRemove(name, options, cwd = process.cwd()) {
|
|
2066
2566
|
const requestError = validateRemoveRequest(name, options);
|
|
2067
2567
|
if (requestError) {
|
|
2068
|
-
console.log(
|
|
2568
|
+
console.log(chalk11.red(requestError));
|
|
2069
2569
|
return 1;
|
|
2070
2570
|
}
|
|
2071
2571
|
const scope = options.scope ?? "project";
|
|
2072
2572
|
const type = options.type;
|
|
2073
2573
|
const handler = getHandler(type);
|
|
2074
2574
|
if (!handler) {
|
|
2075
|
-
console.log(
|
|
2575
|
+
console.log(chalk11.red(`No handler found for type "${type}"`));
|
|
2076
2576
|
return 1;
|
|
2077
2577
|
}
|
|
2078
2578
|
let agents;
|
|
@@ -2085,11 +2585,11 @@ async function runRemove(name, options, cwd = process.cwd()) {
|
|
|
2085
2585
|
}
|
|
2086
2586
|
if (agents.length === 0) {
|
|
2087
2587
|
if (type === "settings") {
|
|
2088
|
-
console.log(
|
|
2089
|
-
console.log(
|
|
2588
|
+
console.log(chalk11.yellow(`Cannot detect where "${name}" is installed: ${SETTINGS_NOT_DISCOVERABLE}`));
|
|
2589
|
+
console.log(chalk11.gray("Name the agents explicitly, e.g. --agents claude"));
|
|
2090
2590
|
return 1;
|
|
2091
2591
|
}
|
|
2092
|
-
console.log(
|
|
2592
|
+
console.log(chalk11.yellow(`${type} "${name}" is not installed in ${scope} scope`));
|
|
2093
2593
|
return 0;
|
|
2094
2594
|
}
|
|
2095
2595
|
if (!options.yes) {
|
|
@@ -2102,7 +2602,7 @@ Will remove ${type} "${name}" from:`));
|
|
|
2102
2602
|
const answer = await confirm("Proceed with removal?");
|
|
2103
2603
|
const confirmed = !p.isCancel(answer) && answer;
|
|
2104
2604
|
if (!confirmed) {
|
|
2105
|
-
console.log(
|
|
2605
|
+
console.log(chalk11.yellow("Removal cancelled"));
|
|
2106
2606
|
return 0;
|
|
2107
2607
|
}
|
|
2108
2608
|
}
|
|
@@ -2112,10 +2612,10 @@ Will remove ${type} "${name}" from:`));
|
|
|
2112
2612
|
console.log(brand(`Successfully removed from ${successCount} agent(s)`));
|
|
2113
2613
|
return 0;
|
|
2114
2614
|
}
|
|
2115
|
-
console.log(
|
|
2615
|
+
console.log(chalk11.yellow("Nothing to remove"));
|
|
2116
2616
|
return 0;
|
|
2117
2617
|
}
|
|
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>",
|
|
2618
|
+
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
2619
|
"-a, --agents <agents>",
|
|
2120
2620
|
"Comma-separated coding agents or 'all'"
|
|
2121
2621
|
).option(
|
|
@@ -2133,9 +2633,9 @@ var removeCommand = new Command3("remove").alias("rm").description("Remove an in
|
|
|
2133
2633
|
|
|
2134
2634
|
// src/commands/init.ts
|
|
2135
2635
|
import { Command as Command4 } from "commander";
|
|
2136
|
-
import
|
|
2137
|
-
import
|
|
2138
|
-
import { join as
|
|
2636
|
+
import chalk12 from "chalk";
|
|
2637
|
+
import ora9 from "ora";
|
|
2638
|
+
import { join as join7 } from "path";
|
|
2139
2639
|
function readmeFor(agent) {
|
|
2140
2640
|
return `# ${CODING_AGENTS[agent].name} Configuration
|
|
2141
2641
|
|
|
@@ -2153,7 +2653,7 @@ async function initializeAgent(agent, cwd) {
|
|
|
2153
2653
|
const path = getAgentPath(agent, "project", cwd);
|
|
2154
2654
|
if (await exists(path)) return false;
|
|
2155
2655
|
await ensureDir(path);
|
|
2156
|
-
await writeTextFile(
|
|
2656
|
+
await writeTextFile(join7(path, "README.md"), readmeFor(agent));
|
|
2157
2657
|
return true;
|
|
2158
2658
|
}
|
|
2159
2659
|
async function runInit(options, cwd = process.cwd()) {
|
|
@@ -2164,13 +2664,13 @@ async function runInit(options, cwd = process.cwd()) {
|
|
|
2164
2664
|
} else {
|
|
2165
2665
|
const parsed = parseAgentsArgStrict(agentsArg);
|
|
2166
2666
|
if (parsed.unknown.length > 0) {
|
|
2167
|
-
console.error(
|
|
2667
|
+
console.error(chalk12.red(`Unknown agent(s): ${parsed.unknown.join(", ")}. Valid agents: ${ALL_AGENTS.join(", ")} or "all"`));
|
|
2168
2668
|
return 1;
|
|
2169
2669
|
}
|
|
2170
2670
|
agents = parsed.agents;
|
|
2171
2671
|
}
|
|
2172
2672
|
if (agents.length === 0) {
|
|
2173
|
-
console.error(
|
|
2673
|
+
console.error(chalk12.red("No valid agents specified"));
|
|
2174
2674
|
return 1;
|
|
2175
2675
|
}
|
|
2176
2676
|
console.log(brand("\nWill initialize configuration for:"));
|
|
@@ -2182,16 +2682,16 @@ async function runInit(options, cwd = process.cwd()) {
|
|
|
2182
2682
|
const answer = await confirm("Proceed?");
|
|
2183
2683
|
const confirmed = !p.isCancel(answer) && answer;
|
|
2184
2684
|
if (!confirmed) {
|
|
2185
|
-
console.log(
|
|
2685
|
+
console.log(chalk12.yellow("Cancelled"));
|
|
2186
2686
|
return 0;
|
|
2187
2687
|
}
|
|
2188
2688
|
}
|
|
2189
2689
|
for (const agent of agents) {
|
|
2190
|
-
const spinner =
|
|
2690
|
+
const spinner = ora9(`Initializing ${CODING_AGENTS[agent].name}...`).start();
|
|
2191
2691
|
if (await initializeAgent(agent, cwd)) {
|
|
2192
2692
|
spinner.succeed(brand(`Initialized ${CODING_AGENTS[agent].name}`));
|
|
2193
2693
|
} else {
|
|
2194
|
-
spinner.info(
|
|
2694
|
+
spinner.info(chalk12.gray(`${CODING_AGENTS[agent].name} already initialized`));
|
|
2195
2695
|
}
|
|
2196
2696
|
}
|
|
2197
2697
|
console.log("");
|
|
@@ -2214,7 +2714,7 @@ var initCommand = new Command4("init").description("Initialize coding agent conf
|
|
|
2214
2714
|
// package.json
|
|
2215
2715
|
var package_default = {
|
|
2216
2716
|
name: "@danieldeusing/seedr",
|
|
2217
|
-
version: "0.1.
|
|
2717
|
+
version: "0.1.98",
|
|
2218
2718
|
description: "Seed your projects with AI configurations",
|
|
2219
2719
|
type: "module",
|
|
2220
2720
|
bin: {
|