claude-multi 0.3.0 → 0.4.1
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/CHANGELOG.md +39 -0
- package/README.md +7 -85
- package/development.md +602 -0
- package/dist/cli.js +748 -138
- package/package.json +3 -3
- package/bun.lock +0 -77
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
3
|
var __create = Object.create;
|
|
4
4
|
var __getProtoOf = Object.getPrototypeOf;
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
@@ -17,7 +17,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
17
17
|
return to;
|
|
18
18
|
};
|
|
19
19
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
20
|
-
var __require =
|
|
20
|
+
var __require = import.meta.require;
|
|
21
21
|
|
|
22
22
|
// node_modules/commander/lib/error.js
|
|
23
23
|
var require_error = __commonJS((exports) => {
|
|
@@ -342,7 +342,7 @@ var require_help = __commonJS((exports) => {
|
|
|
342
342
|
return Math.max(helper.longestOptionTermLength(cmd, helper), helper.longestGlobalOptionTermLength(cmd, helper), helper.longestSubcommandTermLength(cmd, helper), helper.longestArgumentTermLength(cmd, helper));
|
|
343
343
|
}
|
|
344
344
|
wrap(str, width, indent, minColumnWidth = 40) {
|
|
345
|
-
const indents = " \\f\\t\\v
|
|
345
|
+
const indents = " \\f\\t\\v\xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF";
|
|
346
346
|
const manualIndent = new RegExp(`[\\n][${indents}]+`);
|
|
347
347
|
if (str.match(manualIndent))
|
|
348
348
|
return str;
|
|
@@ -354,7 +354,7 @@ var require_help = __commonJS((exports) => {
|
|
|
354
354
|
`, `
|
|
355
355
|
`);
|
|
356
356
|
const indentString = " ".repeat(indent);
|
|
357
|
-
const zeroWidthSpace = "
|
|
357
|
+
const zeroWidthSpace = "\u200B";
|
|
358
358
|
const breaks = `\\s${zeroWidthSpace}`;
|
|
359
359
|
const regex = new RegExp(`
|
|
360
360
|
|.{1,${columnWidth - 1}}([${breaks}]|$)|[^${breaks}]+?([${breaks}]|$)`, "g");
|
|
@@ -597,11 +597,11 @@ var require_suggestSimilar = __commonJS((exports) => {
|
|
|
597
597
|
|
|
598
598
|
// node_modules/commander/lib/command.js
|
|
599
599
|
var require_command = __commonJS((exports) => {
|
|
600
|
-
var EventEmitter = __require("
|
|
601
|
-
var childProcess = __require("
|
|
602
|
-
var path = __require("
|
|
603
|
-
var fs = __require("
|
|
604
|
-
var process2 = __require("
|
|
600
|
+
var EventEmitter = __require("events").EventEmitter;
|
|
601
|
+
var childProcess = __require("child_process");
|
|
602
|
+
var path = __require("path");
|
|
603
|
+
var fs = __require("fs");
|
|
604
|
+
var process2 = __require("process");
|
|
605
605
|
var { Argument, humanReadableArgName } = require_argument();
|
|
606
606
|
var { CommanderError } = require_error();
|
|
607
607
|
var { Help } = require_help();
|
|
@@ -2162,18 +2162,18 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
|
|
|
2162
2162
|
// node_modules/prompts/dist/util/figures.js
|
|
2163
2163
|
var require_figures = __commonJS((exports, module) => {
|
|
2164
2164
|
var main = {
|
|
2165
|
-
arrowUp: "
|
|
2166
|
-
arrowDown: "
|
|
2167
|
-
arrowLeft: "
|
|
2168
|
-
arrowRight: "
|
|
2169
|
-
radioOn: "
|
|
2170
|
-
radioOff: "
|
|
2171
|
-
tick: "
|
|
2172
|
-
cross: "
|
|
2173
|
-
ellipsis: "
|
|
2174
|
-
pointerSmall: "
|
|
2175
|
-
line: "
|
|
2176
|
-
pointer: "
|
|
2165
|
+
arrowUp: "\u2191",
|
|
2166
|
+
arrowDown: "\u2193",
|
|
2167
|
+
arrowLeft: "\u2190",
|
|
2168
|
+
arrowRight: "\u2192",
|
|
2169
|
+
radioOn: "\u25C9",
|
|
2170
|
+
radioOff: "\u25EF",
|
|
2171
|
+
tick: "\u2714",
|
|
2172
|
+
cross: "\u2716",
|
|
2173
|
+
ellipsis: "\u2026",
|
|
2174
|
+
pointerSmall: "\u203A",
|
|
2175
|
+
line: "\u2500",
|
|
2176
|
+
pointer: "\u276F"
|
|
2177
2177
|
};
|
|
2178
2178
|
var win = {
|
|
2179
2179
|
arrowUp: main.arrowUp,
|
|
@@ -2182,11 +2182,11 @@ var require_figures = __commonJS((exports, module) => {
|
|
|
2182
2182
|
arrowRight: main.arrowRight,
|
|
2183
2183
|
radioOn: "(*)",
|
|
2184
2184
|
radioOff: "( )",
|
|
2185
|
-
tick: "
|
|
2186
|
-
cross: "
|
|
2185
|
+
tick: "\u221A",
|
|
2186
|
+
cross: "\xD7",
|
|
2187
2187
|
ellipsis: "...",
|
|
2188
|
-
pointerSmall: "
|
|
2189
|
-
line: "
|
|
2188
|
+
pointerSmall: "\xBB",
|
|
2189
|
+
line: "\u2500",
|
|
2190
2190
|
pointer: ">"
|
|
2191
2191
|
};
|
|
2192
2192
|
var figures = process.platform === "win32" ? win : main;
|
|
@@ -4686,18 +4686,18 @@ var require_clear2 = __commonJS((exports, module) => {
|
|
|
4686
4686
|
// node_modules/prompts/lib/util/figures.js
|
|
4687
4687
|
var require_figures2 = __commonJS((exports, module) => {
|
|
4688
4688
|
var main = {
|
|
4689
|
-
arrowUp: "
|
|
4690
|
-
arrowDown: "
|
|
4691
|
-
arrowLeft: "
|
|
4692
|
-
arrowRight: "
|
|
4693
|
-
radioOn: "
|
|
4694
|
-
radioOff: "
|
|
4695
|
-
tick: "
|
|
4696
|
-
cross: "
|
|
4697
|
-
ellipsis: "
|
|
4698
|
-
pointerSmall: "
|
|
4699
|
-
line: "
|
|
4700
|
-
pointer: "
|
|
4689
|
+
arrowUp: "\u2191",
|
|
4690
|
+
arrowDown: "\u2193",
|
|
4691
|
+
arrowLeft: "\u2190",
|
|
4692
|
+
arrowRight: "\u2192",
|
|
4693
|
+
radioOn: "\u25C9",
|
|
4694
|
+
radioOff: "\u25EF",
|
|
4695
|
+
tick: "\u2714",
|
|
4696
|
+
cross: "\u2716",
|
|
4697
|
+
ellipsis: "\u2026",
|
|
4698
|
+
pointerSmall: "\u203A",
|
|
4699
|
+
line: "\u2500",
|
|
4700
|
+
pointer: "\u276F"
|
|
4701
4701
|
};
|
|
4702
4702
|
var win = {
|
|
4703
4703
|
arrowUp: main.arrowUp,
|
|
@@ -4706,11 +4706,11 @@ var require_figures2 = __commonJS((exports, module) => {
|
|
|
4706
4706
|
arrowRight: main.arrowRight,
|
|
4707
4707
|
radioOn: "(*)",
|
|
4708
4708
|
radioOff: "( )",
|
|
4709
|
-
tick: "
|
|
4710
|
-
cross: "
|
|
4709
|
+
tick: "\u221A",
|
|
4710
|
+
cross: "\xD7",
|
|
4711
4711
|
ellipsis: "...",
|
|
4712
|
-
pointerSmall: "
|
|
4713
|
-
line: "
|
|
4712
|
+
pointerSmall: "\xBB",
|
|
4713
|
+
line: "\u2500",
|
|
4714
4714
|
pointer: ">"
|
|
4715
4715
|
};
|
|
4716
4716
|
var figures = process.platform === "win32" ? win : main;
|
|
@@ -6969,9 +6969,9 @@ var ansiStyles = assembleStyles();
|
|
|
6969
6969
|
var ansi_styles_default = ansiStyles;
|
|
6970
6970
|
|
|
6971
6971
|
// node_modules/chalk/source/vendor/supports-color/index.js
|
|
6972
|
-
import process2 from "
|
|
6973
|
-
import os from "
|
|
6974
|
-
import tty from "
|
|
6972
|
+
import process2 from "process";
|
|
6973
|
+
import os from "os";
|
|
6974
|
+
import tty from "tty";
|
|
6975
6975
|
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
|
|
6976
6976
|
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
6977
6977
|
const position = argv.indexOf(prefix + flag);
|
|
@@ -7282,31 +7282,31 @@ var source_default = chalk;
|
|
|
7282
7282
|
|
|
7283
7283
|
// src/cli.ts
|
|
7284
7284
|
var import_prompts = __toESM(require_prompts3(), 1);
|
|
7285
|
-
import { homedir as homedir2 } from "
|
|
7286
|
-
import { join as
|
|
7285
|
+
import { homedir as homedir2 } from "os";
|
|
7286
|
+
import { join as join3 } from "path";
|
|
7287
7287
|
|
|
7288
7288
|
// src/config.ts
|
|
7289
|
-
import { existsSync, mkdirSync, readdirSync, statSync } from "
|
|
7290
|
-
import { readFile, writeFile, copyFile, mkdir } from "
|
|
7291
|
-
import { homedir } from "
|
|
7292
|
-
import { join } from "
|
|
7289
|
+
import { existsSync, mkdirSync, readdirSync, statSync, unlinkSync, readlinkSync, rmSync } from "fs";
|
|
7290
|
+
import { readFile, writeFile, copyFile, mkdir, symlink } from "fs/promises";
|
|
7291
|
+
import { homedir } from "os";
|
|
7292
|
+
import { join } from "path";
|
|
7293
7293
|
|
|
7294
7294
|
// src/templates.ts
|
|
7295
7295
|
var PROVIDER_TEMPLATES = {
|
|
7296
7296
|
glm: {
|
|
7297
7297
|
name: "glm",
|
|
7298
|
-
displayName: "GLM (
|
|
7299
|
-
description: "GLM-4.5 and GLM-4.
|
|
7298
|
+
displayName: "GLM (\u667A\u8C31AI)",
|
|
7299
|
+
description: "GLM-4.5 and GLM-4.7 models via z.ai",
|
|
7300
7300
|
settings: {
|
|
7301
7301
|
env: {
|
|
7302
7302
|
ANTHROPIC_AUTH_TOKEN: "",
|
|
7303
7303
|
ANTHROPIC_BASE_URL: "https://api.z.ai/api/anthropic",
|
|
7304
7304
|
API_TIMEOUT_MS: "3000000",
|
|
7305
7305
|
ANTHROPIC_DEFAULT_HAIKU_MODEL: "glm-4.5-air",
|
|
7306
|
-
ANTHROPIC_DEFAULT_SONNET_MODEL: "glm-4.
|
|
7307
|
-
ANTHROPIC_DEFAULT_OPUS_MODEL: "glm-4.
|
|
7308
|
-
ANTHROPIC_MODEL: "glm-4.
|
|
7309
|
-
ANTHROPIC_SMALL_FAST_MODEL: "glm-4.
|
|
7306
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "glm-4.7",
|
|
7307
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "glm-4.7",
|
|
7308
|
+
ANTHROPIC_MODEL: "glm-4.7",
|
|
7309
|
+
ANTHROPIC_SMALL_FAST_MODEL: "glm-4.5-air",
|
|
7310
7310
|
ENABLE_THINKING: "true",
|
|
7311
7311
|
REASONING_EFFORT: "high",
|
|
7312
7312
|
MAX_THINKING_TOKENS: "8000",
|
|
@@ -7403,7 +7403,21 @@ async function listInstances() {
|
|
|
7403
7403
|
const config = await loadConfig();
|
|
7404
7404
|
return config.instances;
|
|
7405
7405
|
}
|
|
7406
|
+
async function updateInstanceAutoSync(name, autoSync) {
|
|
7407
|
+
const config = await loadConfig();
|
|
7408
|
+
const index = config.instances.findIndex((i) => i.name === name);
|
|
7409
|
+
if (index === -1) {
|
|
7410
|
+
return null;
|
|
7411
|
+
}
|
|
7412
|
+
config.instances[index].autoSync = autoSync;
|
|
7413
|
+
await saveConfig(config);
|
|
7414
|
+
return config.instances[index];
|
|
7415
|
+
}
|
|
7416
|
+
var _testDefaultClaudeDir;
|
|
7406
7417
|
function getDefaultClaudeDir() {
|
|
7418
|
+
if (_testDefaultClaudeDir) {
|
|
7419
|
+
return _testDefaultClaudeDir;
|
|
7420
|
+
}
|
|
7407
7421
|
return join(homedir(), ".claude");
|
|
7408
7422
|
}
|
|
7409
7423
|
function hasDefaultClaudeConfig() {
|
|
@@ -7414,16 +7428,29 @@ function hasDefaultClaudeConfig() {
|
|
|
7414
7428
|
async function copySettingsFromDefault(targetConfigDir) {
|
|
7415
7429
|
const defaultDir = getDefaultClaudeDir();
|
|
7416
7430
|
const sourceSettings = join(defaultDir, "settings.json");
|
|
7417
|
-
const targetSettings = join(targetConfigDir, "settings.json");
|
|
7418
7431
|
if (!existsSync(sourceSettings)) {
|
|
7419
7432
|
throw new Error("Default Claude settings.json not found");
|
|
7420
7433
|
}
|
|
7434
|
+
const content = await readFile(sourceSettings, "utf-8");
|
|
7435
|
+
const settings = JSON.parse(content);
|
|
7436
|
+
const safeSettings = {};
|
|
7437
|
+
const SAFE_SETTINGS = [
|
|
7438
|
+
"includeCoAuthoredBy",
|
|
7439
|
+
"alwaysThinkingEnabled",
|
|
7440
|
+
"enabledPlugins"
|
|
7441
|
+
];
|
|
7442
|
+
for (const key of SAFE_SETTINGS) {
|
|
7443
|
+
if (settings[key] !== undefined) {
|
|
7444
|
+
safeSettings[key] = settings[key];
|
|
7445
|
+
}
|
|
7446
|
+
}
|
|
7421
7447
|
if (!existsSync(targetConfigDir)) {
|
|
7422
7448
|
await mkdir(targetConfigDir, { recursive: true });
|
|
7423
7449
|
}
|
|
7424
|
-
|
|
7450
|
+
const targetSettings = join(targetConfigDir, "settings.json");
|
|
7451
|
+
await writeFile(targetSettings, JSON.stringify(safeSettings, null, 2), "utf-8");
|
|
7425
7452
|
}
|
|
7426
|
-
async function copyAllFromDefault(targetConfigDir) {
|
|
7453
|
+
async function copyAllFromDefault(targetConfigDir, autoSync = true) {
|
|
7427
7454
|
const defaultDir = getDefaultClaudeDir();
|
|
7428
7455
|
if (!existsSync(defaultDir)) {
|
|
7429
7456
|
throw new Error("Default Claude directory not found");
|
|
@@ -7431,6 +7458,7 @@ async function copyAllFromDefault(targetConfigDir) {
|
|
|
7431
7458
|
if (!existsSync(targetConfigDir)) {
|
|
7432
7459
|
await mkdir(targetConfigDir, { recursive: true });
|
|
7433
7460
|
}
|
|
7461
|
+
const symlinkDirs = ["plugins", "skills"];
|
|
7434
7462
|
const excludeFiles = [
|
|
7435
7463
|
"config.json",
|
|
7436
7464
|
".config.json",
|
|
@@ -7455,10 +7483,24 @@ async function copyAllFromDefault(targetConfigDir) {
|
|
|
7455
7483
|
continue;
|
|
7456
7484
|
}
|
|
7457
7485
|
if (stat.isDirectory()) {
|
|
7458
|
-
if (
|
|
7459
|
-
|
|
7486
|
+
if (autoSync && symlinkDirs.includes(entry)) {
|
|
7487
|
+
if (existsSync(targetPath)) {
|
|
7488
|
+
try {
|
|
7489
|
+
readlinkSync(targetPath);
|
|
7490
|
+
unlinkSync(targetPath);
|
|
7491
|
+
} catch {
|
|
7492
|
+
rmSync(targetPath, { force: true, recursive: true });
|
|
7493
|
+
}
|
|
7494
|
+
}
|
|
7495
|
+
const relativePath = join("..", "..", ".claude", entry);
|
|
7496
|
+
await symlink(relativePath, targetPath, "dir");
|
|
7497
|
+
console.log(` Symlinked: ${entry} -> ~/.claude/${entry}`);
|
|
7498
|
+
} else {
|
|
7499
|
+
if (!existsSync(targetPath)) {
|
|
7500
|
+
await mkdir(targetPath, { recursive: true });
|
|
7501
|
+
}
|
|
7502
|
+
await copyRecursive(sourcePath, targetPath);
|
|
7460
7503
|
}
|
|
7461
|
-
await copyRecursive(sourcePath, targetPath);
|
|
7462
7504
|
} else {
|
|
7463
7505
|
await copyFile(sourcePath, targetPath);
|
|
7464
7506
|
}
|
|
@@ -7567,12 +7609,136 @@ async function createSettingsFromTemplate(targetConfigDir, template, apiKey) {
|
|
|
7567
7609
|
const settingsFile = join(targetConfigDir, "settings.json");
|
|
7568
7610
|
await writeFile(settingsFile, JSON.stringify(settings, null, 2), "utf-8");
|
|
7569
7611
|
}
|
|
7612
|
+
async function syncPluginsAndSkills(configDir) {
|
|
7613
|
+
const defaultDir = getDefaultClaudeDir();
|
|
7614
|
+
const symlinkDirs = ["plugins", "skills"];
|
|
7615
|
+
if (!existsSync(configDir)) {
|
|
7616
|
+
throw new Error("Instance config directory does not exist");
|
|
7617
|
+
}
|
|
7618
|
+
for (const dir of symlinkDirs) {
|
|
7619
|
+
const targetPath = join(configDir, dir);
|
|
7620
|
+
const sourcePath = join(defaultDir, dir);
|
|
7621
|
+
if (!existsSync(sourcePath)) {
|
|
7622
|
+
console.log(source_default.yellow(` \u26A0 Source ${dir} not found in ~/.claude, skipping`));
|
|
7623
|
+
continue;
|
|
7624
|
+
}
|
|
7625
|
+
if (existsSync(targetPath)) {
|
|
7626
|
+
try {
|
|
7627
|
+
const linkTarget = readlinkSync(targetPath);
|
|
7628
|
+
const expectedTarget = join("..", "..", ".claude", dir);
|
|
7629
|
+
if (linkTarget === expectedTarget || linkTarget === sourcePath) {
|
|
7630
|
+
console.log(source_default.gray(` \u2713 ${dir} already synced`));
|
|
7631
|
+
continue;
|
|
7632
|
+
}
|
|
7633
|
+
rmSync(targetPath, { force: true });
|
|
7634
|
+
} catch {
|
|
7635
|
+
rmSync(targetPath, { force: true, recursive: true });
|
|
7636
|
+
}
|
|
7637
|
+
}
|
|
7638
|
+
const relativePath = join("..", "..", ".claude", dir);
|
|
7639
|
+
await symlink(relativePath, targetPath, "dir");
|
|
7640
|
+
console.log(source_default.green(` \u2713 Symlinked: ${dir} -> ~/.claude/${dir}`));
|
|
7641
|
+
}
|
|
7642
|
+
}
|
|
7643
|
+
async function unsyncPluginsAndSkills(configDir) {
|
|
7644
|
+
const defaultDir = getDefaultClaudeDir();
|
|
7645
|
+
const symlinkDirs = ["plugins", "skills"];
|
|
7646
|
+
if (!existsSync(configDir)) {
|
|
7647
|
+
throw new Error("Instance config directory does not exist");
|
|
7648
|
+
}
|
|
7649
|
+
for (const dir of symlinkDirs) {
|
|
7650
|
+
const targetPath = join(configDir, dir);
|
|
7651
|
+
const sourcePath = join(defaultDir, dir);
|
|
7652
|
+
if (!existsSync(sourcePath)) {
|
|
7653
|
+
console.log(source_default.yellow(` \u26A0 Source ${dir} not found in ~/.claude, skipping`));
|
|
7654
|
+
continue;
|
|
7655
|
+
}
|
|
7656
|
+
let isSymlink = false;
|
|
7657
|
+
try {
|
|
7658
|
+
readlinkSync(targetPath);
|
|
7659
|
+
isSymlink = true;
|
|
7660
|
+
} catch {}
|
|
7661
|
+
if (isSymlink) {
|
|
7662
|
+
rmSync(targetPath, { force: true });
|
|
7663
|
+
console.log(source_default.gray(` \u2713 Removed symlink for ${dir}`));
|
|
7664
|
+
} else if (!existsSync(targetPath)) {} else {
|
|
7665
|
+
console.log(source_default.yellow(` \u26A0 ${dir} is already a regular directory, skipping`));
|
|
7666
|
+
continue;
|
|
7667
|
+
}
|
|
7668
|
+
await mkdir(targetPath, { recursive: true });
|
|
7669
|
+
const copyRecursive = async (source, target) => {
|
|
7670
|
+
const entries = readdirSync(source);
|
|
7671
|
+
for (const entry of entries) {
|
|
7672
|
+
const sourceEntry = join(source, entry);
|
|
7673
|
+
const targetEntry = join(target, entry);
|
|
7674
|
+
const stat = statSync(sourceEntry);
|
|
7675
|
+
if (stat.isDirectory()) {
|
|
7676
|
+
await mkdir(targetEntry, { recursive: true });
|
|
7677
|
+
await copyRecursive(sourceEntry, targetEntry);
|
|
7678
|
+
} else {
|
|
7679
|
+
await copyFile(sourceEntry, targetEntry);
|
|
7680
|
+
}
|
|
7681
|
+
}
|
|
7682
|
+
};
|
|
7683
|
+
await copyRecursive(sourcePath, targetPath);
|
|
7684
|
+
console.log(source_default.green(` \u2713 Copied files for ${dir}`));
|
|
7685
|
+
}
|
|
7686
|
+
}
|
|
7687
|
+
async function readClaudeSettings(configDir) {
|
|
7688
|
+
const settingsFile = join(configDir, "settings.json");
|
|
7689
|
+
if (!existsSync(settingsFile)) {
|
|
7690
|
+
return null;
|
|
7691
|
+
}
|
|
7692
|
+
try {
|
|
7693
|
+
const content = await readFile(settingsFile, "utf-8");
|
|
7694
|
+
return JSON.parse(content);
|
|
7695
|
+
} catch (error) {
|
|
7696
|
+
console.error(source_default.yellow(`Warning: Failed to parse settings.json: ${error}`));
|
|
7697
|
+
return null;
|
|
7698
|
+
}
|
|
7699
|
+
}
|
|
7700
|
+
async function writeClaudeSettings(configDir, settings) {
|
|
7701
|
+
const settingsFile = join(configDir, "settings.json");
|
|
7702
|
+
if (!existsSync(configDir)) {
|
|
7703
|
+
await mkdir(configDir, { recursive: true });
|
|
7704
|
+
}
|
|
7705
|
+
await writeFile(settingsFile, JSON.stringify(settings, null, 2), "utf-8");
|
|
7706
|
+
}
|
|
7707
|
+
async function getEnabledPlugins(configDir) {
|
|
7708
|
+
const settings = await readClaudeSettings(configDir);
|
|
7709
|
+
return settings?.enabledPlugins || null;
|
|
7710
|
+
}
|
|
7711
|
+
async function setEnabledPlugins(configDir, enabledPlugins) {
|
|
7712
|
+
const settings = await readClaudeSettings(configDir) || {};
|
|
7713
|
+
settings.enabledPlugins = enabledPlugins;
|
|
7714
|
+
await writeClaudeSettings(configDir, settings);
|
|
7715
|
+
}
|
|
7716
|
+
async function enablePlugin(configDir, pluginId) {
|
|
7717
|
+
const settings = await readClaudeSettings(configDir) || {};
|
|
7718
|
+
if (!settings.enabledPlugins) {
|
|
7719
|
+
settings.enabledPlugins = {};
|
|
7720
|
+
}
|
|
7721
|
+
settings.enabledPlugins[pluginId] = true;
|
|
7722
|
+
await writeClaudeSettings(configDir, settings);
|
|
7723
|
+
}
|
|
7724
|
+
async function disablePlugin(configDir, pluginId) {
|
|
7725
|
+
const settings = await readClaudeSettings(configDir) || {};
|
|
7726
|
+
if (!settings.enabledPlugins) {
|
|
7727
|
+
settings.enabledPlugins = {};
|
|
7728
|
+
}
|
|
7729
|
+
settings.enabledPlugins[pluginId] = false;
|
|
7730
|
+
await writeClaudeSettings(configDir, settings);
|
|
7731
|
+
}
|
|
7732
|
+
async function listAvailablePlugins() {
|
|
7733
|
+
const defaultSettings = await readClaudeSettings(getDefaultClaudeDir());
|
|
7734
|
+
return defaultSettings?.enabledPlugins || null;
|
|
7735
|
+
}
|
|
7570
7736
|
|
|
7571
7737
|
// src/wrapper.ts
|
|
7572
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync, unlinkSync } from "
|
|
7573
|
-
import { writeFile as writeFile2 } from "
|
|
7574
|
-
import { resolve, dirname } from "
|
|
7575
|
-
import { execSync } from "
|
|
7738
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync, unlinkSync as unlinkSync2 } from "fs";
|
|
7739
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
7740
|
+
import { resolve, dirname } from "path";
|
|
7741
|
+
import { execSync } from "child_process";
|
|
7576
7742
|
function getClaudePath() {
|
|
7577
7743
|
try {
|
|
7578
7744
|
const command = process.platform === "win32" ? "where claude" : "which claude";
|
|
@@ -7589,7 +7755,7 @@ function getClaudePath() {
|
|
|
7589
7755
|
}
|
|
7590
7756
|
function generateWrapperScript(options) {
|
|
7591
7757
|
const claudePath = getClaudePath();
|
|
7592
|
-
return `#!/usr/bin/env
|
|
7758
|
+
return `#!/usr/bin/env bun
|
|
7593
7759
|
|
|
7594
7760
|
// Claude Multi - Wrapper for ${options.name}
|
|
7595
7761
|
// Generated by claude-multi
|
|
@@ -7639,20 +7805,48 @@ async function createWrapper(options) {
|
|
|
7639
7805
|
}
|
|
7640
7806
|
function removeWrapper(binaryPath) {
|
|
7641
7807
|
if (existsSync2(binaryPath)) {
|
|
7642
|
-
|
|
7808
|
+
unlinkSync2(binaryPath);
|
|
7643
7809
|
}
|
|
7644
7810
|
}
|
|
7645
7811
|
function getDefaultBinaryPath(name) {
|
|
7646
|
-
const binDir = process.platform === "win32" ? resolve(process.env.
|
|
7812
|
+
const binDir = process.platform === "win32" ? resolve(process.env.LOCALAPPDATA || "", "bun", "bin") : resolve(process.env.HOME || "", ".local", "bin");
|
|
7647
7813
|
const binaryName = process.platform === "win32" ? `claude-${name}.cmd` : `claude-${name}`;
|
|
7648
7814
|
return resolve(binDir, binaryName);
|
|
7649
7815
|
}
|
|
7650
7816
|
|
|
7651
7817
|
// src/version.ts
|
|
7652
|
-
import { execSync as execSync2 } from "
|
|
7818
|
+
import { execSync as execSync2 } from "child_process";
|
|
7819
|
+
import { readFileSync } from "fs";
|
|
7820
|
+
import { join as join2 } from "path";
|
|
7821
|
+
function getClaudeMultiVersion() {
|
|
7822
|
+
const pkgPath = join2(process.cwd(), "package.json");
|
|
7823
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
7824
|
+
return pkg.version;
|
|
7825
|
+
}
|
|
7826
|
+
async function getLatestClaudeMultiVersion() {
|
|
7827
|
+
const response = await fetch("https://registry.npmjs.org/claude-multi/latest");
|
|
7828
|
+
const data = await response.json();
|
|
7829
|
+
return data.version;
|
|
7830
|
+
}
|
|
7831
|
+
async function checkForClaudeMultiUpdates() {
|
|
7832
|
+
try {
|
|
7833
|
+
const current = getClaudeMultiVersion();
|
|
7834
|
+
const latest = await getLatestClaudeMultiVersion();
|
|
7835
|
+
return {
|
|
7836
|
+
current,
|
|
7837
|
+
latest,
|
|
7838
|
+
updateAvailable: current !== latest
|
|
7839
|
+
};
|
|
7840
|
+
} catch {
|
|
7841
|
+
return { current: "", latest: "", updateAvailable: false };
|
|
7842
|
+
}
|
|
7843
|
+
}
|
|
7844
|
+
function upgradeClaudeMulti() {
|
|
7845
|
+
execSync2("bun upgrade -g claude-multi", { stdio: "inherit" });
|
|
7846
|
+
}
|
|
7653
7847
|
function getCurrentVersion() {
|
|
7654
7848
|
try {
|
|
7655
|
-
const output = execSync2("
|
|
7849
|
+
const output = execSync2("bun pm ls -g @anthropic-ai/claude-code --json", {
|
|
7656
7850
|
encoding: "utf-8",
|
|
7657
7851
|
stdio: ["pipe", "pipe", "ignore"]
|
|
7658
7852
|
});
|
|
@@ -7665,7 +7859,7 @@ function getCurrentVersion() {
|
|
|
7665
7859
|
}
|
|
7666
7860
|
async function getLatestVersion() {
|
|
7667
7861
|
try {
|
|
7668
|
-
const output = execSync2("npm view @anthropic-ai/claude-code version", {
|
|
7862
|
+
const output = execSync2("bun pm npm view @anthropic-ai/claude-code version", {
|
|
7669
7863
|
encoding: "utf-8",
|
|
7670
7864
|
stdio: ["pipe", "pipe", "ignore"]
|
|
7671
7865
|
});
|
|
@@ -7686,7 +7880,7 @@ async function checkForUpdates() {
|
|
|
7686
7880
|
async function updateClaudeCode() {
|
|
7687
7881
|
try {
|
|
7688
7882
|
console.log("Updating @anthropic-ai/claude-code...");
|
|
7689
|
-
execSync2("
|
|
7883
|
+
execSync2("bun install -g @anthropic-ai/claude-code@latest", {
|
|
7690
7884
|
stdio: "inherit"
|
|
7691
7885
|
});
|
|
7692
7886
|
console.log("Update completed successfully!");
|
|
@@ -7697,10 +7891,10 @@ async function updateClaudeCode() {
|
|
|
7697
7891
|
|
|
7698
7892
|
// src/cli.ts
|
|
7699
7893
|
var program2 = new Command;
|
|
7700
|
-
program2.name("claude-multi").description("Manage multiple Claude Code instances with different aliases").version("0.
|
|
7701
|
-
program2.command("add <name>").description("Add a new Claude Code instance").option("-c, --config <dir>", "Config directory path", (name) =>
|
|
7894
|
+
program2.name("claude-multi").description("Manage multiple Claude Code instances with different aliases").version("0.4.0");
|
|
7895
|
+
program2.command("add <name>").description("Add a new Claude Code instance").option("-c, --config <dir>", "Config directory path", (name) => join3(homedir2(), `.claude-${name}`)).option("-b, --binary <path>", "Binary path (default: ~/.local/bin/claude-<name>)").option("--copy-settings", "Copy settings.json from default Claude").option("--copy-all", "Copy all files from default Claude").option("--copy-mcp", "Copy MCP server configurations from default Claude").option("--skip-prompts", "Skip interactive prompts (start fresh)").option("--provider <name>", "Use a provider template (glm, minimax)").option("--api-key <key>", "API key for the provider").option("--auto-sync", "Auto-sync plugins/skills via symlinks (default)").option("--manual", "Manually manage plugins/skills (copy files)").action(async (name, options) => {
|
|
7702
7896
|
try {
|
|
7703
|
-
const configDir = options.config ||
|
|
7897
|
+
const configDir = options.config || join3(homedir2(), `.claude-${name}`);
|
|
7704
7898
|
const binaryPath = options.binary || getDefaultBinaryPath(name);
|
|
7705
7899
|
const hasDefaultConfig = hasDefaultClaudeConfig();
|
|
7706
7900
|
const hasDefaultMcp = await hasDefaultMcpConfig();
|
|
@@ -7710,14 +7904,15 @@ program2.command("add <name>").description("Add a new Claude Code instance").opt
|
|
|
7710
7904
|
let useProviderTemplate = false;
|
|
7711
7905
|
let providerTemplate = null;
|
|
7712
7906
|
let apiKey = "";
|
|
7907
|
+
let autoSync = !options.manual;
|
|
7713
7908
|
if (options.provider) {
|
|
7714
7909
|
providerTemplate = getProviderTemplate(options.provider);
|
|
7715
7910
|
if (!providerTemplate) {
|
|
7716
|
-
console.error(source_default.red(
|
|
7911
|
+
console.error(source_default.red(`\u2717 Unknown provider '${options.provider}'. Available: glm, minimax`));
|
|
7717
7912
|
process.exit(1);
|
|
7718
7913
|
}
|
|
7719
7914
|
if (!options.apiKey) {
|
|
7720
|
-
console.error(source_default.red("
|
|
7915
|
+
console.error(source_default.red("\u2717 --api-key is required when using --provider"));
|
|
7721
7916
|
process.exit(1);
|
|
7722
7917
|
}
|
|
7723
7918
|
apiKey = options.apiKey;
|
|
@@ -7765,7 +7960,7 @@ Found existing Claude Code configuration at ~/.claude`));
|
|
|
7765
7960
|
]);
|
|
7766
7961
|
if (response.copyOption === undefined) {
|
|
7767
7962
|
console.log(source_default.yellow(`
|
|
7768
|
-
|
|
7963
|
+
\u2717 Cancelled`));
|
|
7769
7964
|
process.exit(0);
|
|
7770
7965
|
}
|
|
7771
7966
|
copySettings = response.copyOption === "settings" || response.copyOption === "settings+mcp" || response.copyOption === "all";
|
|
@@ -7776,34 +7971,39 @@ Found existing Claude Code configuration at ~/.claude`));
|
|
|
7776
7971
|
name,
|
|
7777
7972
|
configDir,
|
|
7778
7973
|
binaryPath,
|
|
7779
|
-
createdAt: new Date().toISOString()
|
|
7974
|
+
createdAt: new Date().toISOString(),
|
|
7975
|
+
autoSync
|
|
7780
7976
|
};
|
|
7781
7977
|
await addInstance(instance);
|
|
7782
7978
|
await createWrapper(instance);
|
|
7783
7979
|
if (copySettings && !copyAllFiles) {
|
|
7784
7980
|
await copySettingsFromDefault(configDir);
|
|
7785
|
-
console.log(source_default.green("
|
|
7981
|
+
console.log(source_default.green("\u2713 Copied settings.json"));
|
|
7786
7982
|
}
|
|
7787
7983
|
if (copyMcpServers && !copyAllFiles) {
|
|
7788
7984
|
try {
|
|
7789
7985
|
await copyMcpServersFromDefault(configDir);
|
|
7790
|
-
console.log(source_default.green("
|
|
7986
|
+
console.log(source_default.green("\u2713 Copied MCP server configurations"));
|
|
7791
7987
|
} catch (error) {
|
|
7792
|
-
console.log(source_default.yellow(
|
|
7988
|
+
console.log(source_default.yellow(`\u26A0 Warning: ${error.message}`));
|
|
7793
7989
|
}
|
|
7794
7990
|
}
|
|
7795
7991
|
if (copyAllFiles) {
|
|
7796
|
-
await copyAllFromDefault(configDir);
|
|
7797
|
-
|
|
7992
|
+
await copyAllFromDefault(configDir, autoSync);
|
|
7993
|
+
if (autoSync) {
|
|
7994
|
+
console.log(source_default.green("\u2713 Copied all files with auto-sync (plugins/skills symlinked)"));
|
|
7995
|
+
} else {
|
|
7996
|
+
console.log(source_default.green("\u2713 Copied all files from default Claude (manual mode)"));
|
|
7997
|
+
}
|
|
7798
7998
|
}
|
|
7799
7999
|
if (useProviderTemplate && providerTemplate && !copySettings && !copyAllFiles) {
|
|
7800
8000
|
await createSettingsFromTemplate(configDir, providerTemplate, apiKey);
|
|
7801
|
-
console.log(source_default.green(
|
|
8001
|
+
console.log(source_default.green(`\u2713 Applied ${providerTemplate.displayName} template`));
|
|
7802
8002
|
} else if (useProviderTemplate && (copySettings || copyAllFiles)) {
|
|
7803
|
-
console.log(source_default.yellow("
|
|
8003
|
+
console.log(source_default.yellow("\u26A0 Provider template skipped (copied settings from default Claude)"));
|
|
7804
8004
|
}
|
|
7805
8005
|
console.log(source_default.green(`
|
|
7806
|
-
|
|
8006
|
+
\u2713 Instance '${name}' created successfully!`));
|
|
7807
8007
|
console.log(source_default.gray(` Binary: ${binaryPath}`));
|
|
7808
8008
|
console.log(source_default.gray(` Config: ${configDir}`));
|
|
7809
8009
|
console.log();
|
|
@@ -7811,7 +8011,7 @@ Found existing Claude Code configuration at ~/.claude`));
|
|
|
7811
8011
|
const pathEnv = process.env.PATH || "";
|
|
7812
8012
|
const isInPath = pathEnv.split(":").some((p) => p === binDir);
|
|
7813
8013
|
if (!isInPath) {
|
|
7814
|
-
console.log(source_default.yellow(
|
|
8014
|
+
console.log(source_default.yellow(`\u26A0 Warning: ${binDir} is not in your PATH`));
|
|
7815
8015
|
console.log(source_default.gray(`Add to PATH by running:`));
|
|
7816
8016
|
console.log(source_default.cyan(` echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc`));
|
|
7817
8017
|
console.log(source_default.cyan(` source ~/.zshrc`));
|
|
@@ -7821,7 +8021,7 @@ Found existing Claude Code configuration at ~/.claude`));
|
|
|
7821
8021
|
console.log(source_default.cyan(`Run: claude-${name} --help`));
|
|
7822
8022
|
}
|
|
7823
8023
|
} catch (error) {
|
|
7824
|
-
console.error(source_default.red(
|
|
8024
|
+
console.error(source_default.red(`\u2717 Error: ${error.message}`));
|
|
7825
8025
|
process.exit(1);
|
|
7826
8026
|
}
|
|
7827
8027
|
});
|
|
@@ -7829,7 +8029,7 @@ program2.command("remove <name>").alias("rm").description("Remove a Claude Code
|
|
|
7829
8029
|
try {
|
|
7830
8030
|
const instance = await getInstance(name);
|
|
7831
8031
|
if (!instance) {
|
|
7832
|
-
console.error(source_default.red(
|
|
8032
|
+
console.error(source_default.red(`\u2717 Instance '${name}' not found`));
|
|
7833
8033
|
process.exit(1);
|
|
7834
8034
|
}
|
|
7835
8035
|
if (!options.force) {
|
|
@@ -7843,11 +8043,11 @@ program2.command("remove <name>").alias("rm").description("Remove a Claude Code
|
|
|
7843
8043
|
}
|
|
7844
8044
|
await removeInstance(name);
|
|
7845
8045
|
removeWrapper(instance.binaryPath);
|
|
7846
|
-
console.log(source_default.green(
|
|
8046
|
+
console.log(source_default.green(`\u2713 Instance '${name}' removed successfully!`));
|
|
7847
8047
|
console.log();
|
|
7848
8048
|
console.log(source_default.gray(`To remove config files, run: rm -rf ${instance.configDir}`));
|
|
7849
8049
|
} catch (error) {
|
|
7850
|
-
console.error(source_default.red(
|
|
8050
|
+
console.error(source_default.red(`\u2717 Error: ${error.message}`));
|
|
7851
8051
|
process.exit(1);
|
|
7852
8052
|
}
|
|
7853
8053
|
});
|
|
@@ -7863,14 +8063,16 @@ program2.command("list").alias("ls").description("List all Claude Code instances
|
|
|
7863
8063
|
console.log(source_default.bold(`Found ${instances.length} instance(s):
|
|
7864
8064
|
`));
|
|
7865
8065
|
for (const instance of instances) {
|
|
7866
|
-
console.log(source_default.cyan(
|
|
8066
|
+
console.log(source_default.cyan(`\u25CF ${instance.name}`));
|
|
7867
8067
|
console.log(source_default.gray(` Binary: ${instance.binaryPath}`));
|
|
7868
8068
|
console.log(source_default.gray(` Config: ${instance.configDir}`));
|
|
7869
8069
|
console.log(source_default.gray(` Created: ${new Date(instance.createdAt).toLocaleString()}`));
|
|
8070
|
+
const autoSyncStatus = instance.autoSync !== false ? source_default.green("on") : source_default.yellow("off");
|
|
8071
|
+
console.log(source_default.gray(` Auto-sync: ${autoSyncStatus}`));
|
|
7870
8072
|
console.log();
|
|
7871
8073
|
}
|
|
7872
8074
|
} catch (error) {
|
|
7873
|
-
console.error(source_default.red(
|
|
8075
|
+
console.error(source_default.red(`\u2717 Error: ${error.message}`));
|
|
7874
8076
|
process.exit(1);
|
|
7875
8077
|
}
|
|
7876
8078
|
});
|
|
@@ -7878,7 +8080,7 @@ program2.command("info <name>").description("Show details about a specific insta
|
|
|
7878
8080
|
try {
|
|
7879
8081
|
const instance = await getInstance(name);
|
|
7880
8082
|
if (!instance) {
|
|
7881
|
-
console.error(source_default.red(
|
|
8083
|
+
console.error(source_default.red(`\u2717 Instance '${name}' not found`));
|
|
7882
8084
|
process.exit(1);
|
|
7883
8085
|
}
|
|
7884
8086
|
console.log(source_default.bold(`Instance: ${source_default.cyan(instance.name)}
|
|
@@ -7886,8 +8088,10 @@ program2.command("info <name>").description("Show details about a specific insta
|
|
|
7886
8088
|
console.log(`${source_default.gray("Binary:")} ${instance.binaryPath}`);
|
|
7887
8089
|
console.log(`${source_default.gray("Config:")} ${instance.configDir}`);
|
|
7888
8090
|
console.log(`${source_default.gray("Created:")} ${new Date(instance.createdAt).toLocaleString()}`);
|
|
8091
|
+
const autoSyncStatus = instance.autoSync !== false ? source_default.green("\u2713 Enabled") : source_default.yellow("\u2717 Disabled");
|
|
8092
|
+
console.log(`${source_default.gray("Auto-sync:")} ${autoSyncStatus}`);
|
|
7889
8093
|
} catch (error) {
|
|
7890
|
-
console.error(source_default.red(
|
|
8094
|
+
console.error(source_default.red(`\u2717 Error: ${error.message}`));
|
|
7891
8095
|
process.exit(1);
|
|
7892
8096
|
}
|
|
7893
8097
|
});
|
|
@@ -7904,13 +8108,13 @@ program2.command("version").description("Check Claude Code version and updates")
|
|
|
7904
8108
|
console.log(`${source_default.gray("Latest:")} ${source_default.cyan(versionInfo.latest)}`);
|
|
7905
8109
|
console.log();
|
|
7906
8110
|
if (versionInfo.updateAvailable) {
|
|
7907
|
-
console.log(source_default.yellow(
|
|
8111
|
+
console.log(source_default.yellow(`\u26A0 Update available: ${versionInfo.latest}`));
|
|
7908
8112
|
console.log(source_default.gray("Run 'claude-multi update' to update"));
|
|
7909
8113
|
} else if (versionInfo.current) {
|
|
7910
|
-
console.log(source_default.green("
|
|
8114
|
+
console.log(source_default.green("\u2713 You're up to date!"));
|
|
7911
8115
|
}
|
|
7912
8116
|
} catch (error) {
|
|
7913
|
-
console.error(source_default.red(
|
|
8117
|
+
console.error(source_default.red(`\u2717 Error: ${error.message}`));
|
|
7914
8118
|
process.exit(1);
|
|
7915
8119
|
}
|
|
7916
8120
|
});
|
|
@@ -7918,20 +8122,206 @@ program2.command("update").description("Update @anthropic-ai/claude-code to the
|
|
|
7918
8122
|
try {
|
|
7919
8123
|
const versionInfo = await checkForUpdates();
|
|
7920
8124
|
if (!versionInfo.updateAvailable && versionInfo.current) {
|
|
7921
|
-
console.log(source_default.green(
|
|
8125
|
+
console.log(source_default.green(`\u2713 Already up to date (${versionInfo.current})`));
|
|
7922
8126
|
return;
|
|
7923
8127
|
}
|
|
7924
8128
|
await updateClaudeCode();
|
|
7925
8129
|
} catch (error) {
|
|
7926
|
-
console.error(source_default.red(
|
|
8130
|
+
console.error(source_default.red(`\u2717 Error: ${error.message}`));
|
|
8131
|
+
process.exit(1);
|
|
8132
|
+
}
|
|
8133
|
+
});
|
|
8134
|
+
program2.command("auto-sync <name> <status>").description("Toggle auto-sync for plugins/skills (on/off)").action(async (name, status) => {
|
|
8135
|
+
try {
|
|
8136
|
+
const instance = await getInstance(name);
|
|
8137
|
+
if (!instance) {
|
|
8138
|
+
console.error(source_default.red(`\u2717 Instance '${name}' not found`));
|
|
8139
|
+
process.exit(1);
|
|
8140
|
+
}
|
|
8141
|
+
const newStatus = status.toLowerCase() === "on" || status.toLowerCase() === "true" || status === "1";
|
|
8142
|
+
const currentStatus = instance.autoSync !== false;
|
|
8143
|
+
if (currentStatus === newStatus) {
|
|
8144
|
+
console.log(source_default.yellow(`Auto-sync is already ${newStatus ? "enabled" : "disabled"} for '${name}'`));
|
|
8145
|
+
return;
|
|
8146
|
+
}
|
|
8147
|
+
await updateInstanceAutoSync(name, newStatus);
|
|
8148
|
+
console.log(source_default.bold(`
|
|
8149
|
+
\uD83D\uDD04 ${newStatus ? "Enabling" : "Disabling"} auto-sync for '${name}'...
|
|
8150
|
+
`));
|
|
8151
|
+
if (newStatus) {
|
|
8152
|
+
await syncPluginsAndSkills(instance.configDir);
|
|
8153
|
+
} else {
|
|
8154
|
+
await unsyncPluginsAndSkills(instance.configDir);
|
|
8155
|
+
}
|
|
8156
|
+
console.log(source_default.green(`
|
|
8157
|
+
\u2713 Auto-sync ${newStatus ? "enabled" : "disabled"} for '${name}'`));
|
|
8158
|
+
} catch (error) {
|
|
8159
|
+
console.error(source_default.red(`\u2717 Error: ${error.message}`));
|
|
8160
|
+
process.exit(1);
|
|
8161
|
+
}
|
|
8162
|
+
});
|
|
8163
|
+
program2.command("plugins").description("Manage enabled plugins for instances").argument("[action]", "Action to perform (list, enable, disable, copy)", "list").argument("[instance]", "Instance name (for list/enable/disable)", "").argument("[plugins...]", "Plugin IDs to enable/disable", []).action(async (action = "list", instanceName = "", plugins = []) => {
|
|
8164
|
+
try {
|
|
8165
|
+
switch (action) {
|
|
8166
|
+
case "list":
|
|
8167
|
+
await handlePluginsList(instanceName);
|
|
8168
|
+
break;
|
|
8169
|
+
case "enable":
|
|
8170
|
+
await handlePluginsEnable(instanceName, plugins);
|
|
8171
|
+
break;
|
|
8172
|
+
case "disable":
|
|
8173
|
+
await handlePluginsDisable(instanceName, plugins);
|
|
8174
|
+
break;
|
|
8175
|
+
case "copy":
|
|
8176
|
+
await handlePluginsCopy(instanceName);
|
|
8177
|
+
break;
|
|
8178
|
+
default:
|
|
8179
|
+
console.error(source_default.red(`\u2717 Unknown action: ${action}`));
|
|
8180
|
+
console.log(source_default.gray("Available actions: list, enable, disable, copy"));
|
|
8181
|
+
process.exit(1);
|
|
8182
|
+
}
|
|
8183
|
+
} catch (error) {
|
|
8184
|
+
console.error(source_default.red(`\u2717 Error: ${error.message}`));
|
|
7927
8185
|
process.exit(1);
|
|
7928
8186
|
}
|
|
7929
8187
|
});
|
|
8188
|
+
async function handlePluginsList(instanceName) {
|
|
8189
|
+
const instances = await listInstances();
|
|
8190
|
+
if (!instanceName) {
|
|
8191
|
+
console.log(source_default.bold(`
|
|
8192
|
+
\uD83D\uDCCB Enabled Plugins by Instance
|
|
8193
|
+
`));
|
|
8194
|
+
const defaultPlugins = await listAvailablePlugins();
|
|
8195
|
+
if (defaultPlugins) {
|
|
8196
|
+
console.log(source_default.gray(`Default Claude (~/.claude):`));
|
|
8197
|
+
const enabledCount = Object.values(defaultPlugins).filter((v) => v === true).length;
|
|
8198
|
+
console.log(source_default.gray(` Plugins: ${enabledCount} enabled
|
|
8199
|
+
`));
|
|
8200
|
+
for (const [pluginId, enabled] of Object.entries(defaultPlugins)) {
|
|
8201
|
+
const status = enabled ? source_default.green("\u2713") : source_default.gray("\u2717");
|
|
8202
|
+
console.log(source_default.gray(` ${status} ${pluginId}`));
|
|
8203
|
+
}
|
|
8204
|
+
console.log();
|
|
8205
|
+
}
|
|
8206
|
+
if (instances.length === 0) {
|
|
8207
|
+
console.log(source_default.yellow("No instances found."));
|
|
8208
|
+
return;
|
|
8209
|
+
}
|
|
8210
|
+
for (const instance of instances) {
|
|
8211
|
+
const plugins = await getEnabledPlugins(instance.configDir);
|
|
8212
|
+
console.log(source_default.cyan(`${instance.name}:`));
|
|
8213
|
+
if (plugins) {
|
|
8214
|
+
const enabledCount = Object.values(plugins).filter((v) => v === true).length;
|
|
8215
|
+
console.log(source_default.gray(` Plugins: ${enabledCount} enabled
|
|
8216
|
+
`));
|
|
8217
|
+
for (const [pluginId, enabled] of Object.entries(plugins)) {
|
|
8218
|
+
const status = enabled ? source_default.green("\u2713") : source_default.gray("\u2717");
|
|
8219
|
+
console.log(source_default.gray(` ${status} ${pluginId}`));
|
|
8220
|
+
}
|
|
8221
|
+
} else {
|
|
8222
|
+
console.log(source_default.yellow(" No plugins configured"));
|
|
8223
|
+
}
|
|
8224
|
+
console.log();
|
|
8225
|
+
}
|
|
8226
|
+
} else {
|
|
8227
|
+
const instance = await getInstance(instanceName);
|
|
8228
|
+
if (!instance) {
|
|
8229
|
+
console.error(source_default.red(`\u2717 Instance '${instanceName}' not found`));
|
|
8230
|
+
process.exit(1);
|
|
8231
|
+
}
|
|
8232
|
+
const plugins = await getEnabledPlugins(instance.configDir);
|
|
8233
|
+
if (!plugins) {
|
|
8234
|
+
console.log(source_default.yellow(`No plugins configured for '${instanceName}'`));
|
|
8235
|
+
return;
|
|
8236
|
+
}
|
|
8237
|
+
console.log(source_default.bold(`
|
|
8238
|
+
\uD83D\uDCCB Enabled Plugins for '${instanceName}'
|
|
8239
|
+
`));
|
|
8240
|
+
for (const [pluginId, enabled] of Object.entries(plugins)) {
|
|
8241
|
+
const status = enabled ? source_default.green("\u2713") : source_default.gray("\u2717");
|
|
8242
|
+
console.log(`${status} ${source_default.cyan(pluginId)}`);
|
|
8243
|
+
}
|
|
8244
|
+
}
|
|
8245
|
+
}
|
|
8246
|
+
async function handlePluginsEnable(instanceName, plugins) {
|
|
8247
|
+
const instance = await getInstance(instanceName);
|
|
8248
|
+
if (!instance) {
|
|
8249
|
+
console.error(source_default.red(`\u2717 Instance '${instanceName}' not found`));
|
|
8250
|
+
process.exit(1);
|
|
8251
|
+
}
|
|
8252
|
+
if (plugins.length === 0) {
|
|
8253
|
+
console.error(source_default.red("\u2717 No plugins specified"));
|
|
8254
|
+
console.log(source_default.gray("Usage: claude-multi plugins enable <instance> <plugin-id>..."));
|
|
8255
|
+
process.exit(1);
|
|
8256
|
+
}
|
|
8257
|
+
const currentPlugins = await getEnabledPlugins(instance.configDir) || {};
|
|
8258
|
+
let updated = false;
|
|
8259
|
+
for (const pluginId of plugins) {
|
|
8260
|
+
if (currentPlugins[pluginId] === true) {
|
|
8261
|
+
console.log(source_default.yellow(`\u26A0 Plugin '${pluginId}' is already enabled`));
|
|
8262
|
+
} else {
|
|
8263
|
+
currentPlugins[pluginId] = true;
|
|
8264
|
+
await enablePlugin(instance.configDir, pluginId);
|
|
8265
|
+
console.log(source_default.green(`\u2713 Enabled plugin '${pluginId}'`));
|
|
8266
|
+
updated = true;
|
|
8267
|
+
}
|
|
8268
|
+
}
|
|
8269
|
+
if (updated) {
|
|
8270
|
+
console.log(source_default.green(`
|
|
8271
|
+
\u2713 Updated plugins for '${instanceName}'`));
|
|
8272
|
+
}
|
|
8273
|
+
}
|
|
8274
|
+
async function handlePluginsDisable(instanceName, plugins) {
|
|
8275
|
+
const instance = await getInstance(instanceName);
|
|
8276
|
+
if (!instance) {
|
|
8277
|
+
console.error(source_default.red(`\u2717 Instance '${instanceName}' not found`));
|
|
8278
|
+
process.exit(1);
|
|
8279
|
+
}
|
|
8280
|
+
if (plugins.length === 0) {
|
|
8281
|
+
console.error(source_default.red("\u2717 No plugins specified"));
|
|
8282
|
+
console.log(source_default.gray("Usage: claude-multi plugins disable <instance> <plugin-id>..."));
|
|
8283
|
+
process.exit(1);
|
|
8284
|
+
}
|
|
8285
|
+
const currentPlugins = await getEnabledPlugins(instance.configDir) || {};
|
|
8286
|
+
let updated = false;
|
|
8287
|
+
for (const pluginId of plugins) {
|
|
8288
|
+
if (currentPlugins[pluginId] === false) {
|
|
8289
|
+
console.log(source_default.yellow(`\u26A0 Plugin '${pluginId}' is already disabled`));
|
|
8290
|
+
} else {
|
|
8291
|
+
currentPlugins[pluginId] = false;
|
|
8292
|
+
await disablePlugin(instance.configDir, pluginId);
|
|
8293
|
+
console.log(source_default.green(`\u2713 Disabled plugin '${pluginId}'`));
|
|
8294
|
+
updated = true;
|
|
8295
|
+
}
|
|
8296
|
+
}
|
|
8297
|
+
if (updated) {
|
|
8298
|
+
console.log(source_default.green(`
|
|
8299
|
+
\u2713 Updated plugins for '${instanceName}'`));
|
|
8300
|
+
}
|
|
8301
|
+
}
|
|
8302
|
+
async function handlePluginsCopy(instanceName) {
|
|
8303
|
+
const instance = await getInstance(instanceName);
|
|
8304
|
+
if (!instance) {
|
|
8305
|
+
console.error(source_default.red(`\u2717 Instance '${instanceName}' not found`));
|
|
8306
|
+
process.exit(1);
|
|
8307
|
+
}
|
|
8308
|
+
const defaultPlugins = await listAvailablePlugins();
|
|
8309
|
+
if (!defaultPlugins) {
|
|
8310
|
+
console.log(source_default.yellow("No plugins found in default Claude settings"));
|
|
8311
|
+
return;
|
|
8312
|
+
}
|
|
8313
|
+
console.log(source_default.bold(`
|
|
8314
|
+
\uD83D\uDCCB Copying plugins from default Claude to '${instanceName}'
|
|
8315
|
+
`));
|
|
8316
|
+
await setEnabledPlugins(instance.configDir, defaultPlugins);
|
|
8317
|
+
const enabledCount = Object.values(defaultPlugins).filter((v) => v === true).length;
|
|
8318
|
+
console.log(source_default.green(`\u2713 Copied ${enabledCount} enabled plugins to '${instanceName}'`));
|
|
8319
|
+
}
|
|
7930
8320
|
program2.command("interactive").alias("i").description("Launch interactive mode for managing Claude Code instances").action(async () => {
|
|
7931
8321
|
try {
|
|
7932
8322
|
await runInteractiveMode();
|
|
7933
8323
|
} catch (error) {
|
|
7934
|
-
console.error(source_default.red(
|
|
8324
|
+
console.error(source_default.red(`\u2717 Error: ${error.message}`));
|
|
7935
8325
|
process.exit(1);
|
|
7936
8326
|
}
|
|
7937
8327
|
});
|
|
@@ -7952,11 +8342,13 @@ async function runInteractiveMode() {
|
|
|
7952
8342
|
name: "action",
|
|
7953
8343
|
message: "What would you like to do?",
|
|
7954
8344
|
choices: [
|
|
7955
|
-
{ title: "
|
|
8345
|
+
{ title: "\u2795 Add new instance", value: "add" },
|
|
7956
8346
|
{ title: "\uD83D\uDCCB List all instances", value: "list" },
|
|
7957
8347
|
...instances.length > 0 ? [
|
|
7958
|
-
{ title: "
|
|
7959
|
-
{ title: "\uD83D\
|
|
8348
|
+
{ title: "\u2139\uFE0F Show instance details", value: "info" },
|
|
8349
|
+
{ title: "\uD83D\uDD0C Manage plugins", value: "plugins" },
|
|
8350
|
+
{ title: "\uD83D\uDD04 Toggle auto-sync", value: "autosync" },
|
|
8351
|
+
{ title: "\uD83D\uDDD1\uFE0F Remove instance", value: "remove" }
|
|
7960
8352
|
] : [],
|
|
7961
8353
|
{ title: "\uD83D\uDEAA Exit", value: "exit" }
|
|
7962
8354
|
],
|
|
@@ -7977,6 +8369,12 @@ async function runInteractiveMode() {
|
|
|
7977
8369
|
case "info":
|
|
7978
8370
|
await handleShowInstanceInfo(instances);
|
|
7979
8371
|
break;
|
|
8372
|
+
case "plugins":
|
|
8373
|
+
await handleManagePlugins(instances);
|
|
8374
|
+
break;
|
|
8375
|
+
case "autosync":
|
|
8376
|
+
await handleToggleAutoSync(instances);
|
|
8377
|
+
break;
|
|
7980
8378
|
case "remove":
|
|
7981
8379
|
await handleRemoveInstance(instances);
|
|
7982
8380
|
break;
|
|
@@ -8008,7 +8406,7 @@ async function runInteractiveMode() {
|
|
|
8008
8406
|
}
|
|
8009
8407
|
async function handleAddInstance() {
|
|
8010
8408
|
console.log(source_default.bold(`
|
|
8011
|
-
|
|
8409
|
+
\u2795 Add New Instance
|
|
8012
8410
|
`));
|
|
8013
8411
|
const { name } = await import_prompts.default({
|
|
8014
8412
|
type: "text",
|
|
@@ -8027,7 +8425,7 @@ async function handleAddInstance() {
|
|
|
8027
8425
|
return;
|
|
8028
8426
|
const existing = await getInstance(name);
|
|
8029
8427
|
if (existing) {
|
|
8030
|
-
console.log(source_default.red(
|
|
8428
|
+
console.log(source_default.red(`\u2717 Instance '${name}' already exists`));
|
|
8031
8429
|
return;
|
|
8032
8430
|
}
|
|
8033
8431
|
const { useProvider } = await import_prompts.default({
|
|
@@ -8082,7 +8480,7 @@ async function handleAddInstance() {
|
|
|
8082
8480
|
let configDir;
|
|
8083
8481
|
let binaryPath;
|
|
8084
8482
|
if (useDefaults) {
|
|
8085
|
-
configDir =
|
|
8483
|
+
configDir = join3(homedir2(), `.claude-${name}`);
|
|
8086
8484
|
binaryPath = getDefaultBinaryPath(name);
|
|
8087
8485
|
} else {
|
|
8088
8486
|
const configResponse = await import_prompts.default([
|
|
@@ -8090,7 +8488,7 @@ async function handleAddInstance() {
|
|
|
8090
8488
|
type: "text",
|
|
8091
8489
|
name: "configDir",
|
|
8092
8490
|
message: "Config directory path:",
|
|
8093
|
-
initial:
|
|
8491
|
+
initial: join3(homedir2(), `.claude-${name}`)
|
|
8094
8492
|
},
|
|
8095
8493
|
{
|
|
8096
8494
|
type: "text",
|
|
@@ -8139,45 +8537,63 @@ async function handleAddInstance() {
|
|
|
8139
8537
|
copyMcpServers = copyOption === "mcp" || copyOption === "settings+mcp" || copyOption === "all";
|
|
8140
8538
|
copyAllFiles = copyOption === "all";
|
|
8141
8539
|
}
|
|
8540
|
+
let autoSync = true;
|
|
8541
|
+
if (copyAllFiles) {
|
|
8542
|
+
const { autoSync: autoSyncResponse } = await import_prompts.default({
|
|
8543
|
+
type: "confirm",
|
|
8544
|
+
name: "autoSync",
|
|
8545
|
+
message: "Auto-sync plugins and skills via symlinks?",
|
|
8546
|
+
initial: true,
|
|
8547
|
+
hint: "When enabled, plugins/skills are symlinked from ~/.claude (shared across instances)"
|
|
8548
|
+
});
|
|
8549
|
+
if (autoSyncResponse === undefined)
|
|
8550
|
+
return;
|
|
8551
|
+
autoSync = autoSyncResponse;
|
|
8552
|
+
}
|
|
8142
8553
|
const instance = {
|
|
8143
8554
|
name,
|
|
8144
8555
|
configDir,
|
|
8145
8556
|
binaryPath,
|
|
8146
|
-
createdAt: new Date().toISOString()
|
|
8557
|
+
createdAt: new Date().toISOString(),
|
|
8558
|
+
autoSync
|
|
8147
8559
|
};
|
|
8148
8560
|
await addInstance(instance);
|
|
8149
8561
|
await createWrapper(instance);
|
|
8150
8562
|
if (copySettings && !copyAllFiles) {
|
|
8151
8563
|
await copySettingsFromDefault(configDir);
|
|
8152
|
-
console.log(source_default.green("
|
|
8564
|
+
console.log(source_default.green("\u2713 Copied settings.json"));
|
|
8153
8565
|
}
|
|
8154
8566
|
if (copyMcpServers && !copyAllFiles) {
|
|
8155
8567
|
try {
|
|
8156
8568
|
await copyMcpServersFromDefault(configDir);
|
|
8157
|
-
console.log(source_default.green("
|
|
8569
|
+
console.log(source_default.green("\u2713 Copied MCP server configurations"));
|
|
8158
8570
|
} catch (error) {
|
|
8159
|
-
console.log(source_default.yellow(
|
|
8571
|
+
console.log(source_default.yellow(`\u26A0 Warning: ${error.message}`));
|
|
8160
8572
|
}
|
|
8161
8573
|
}
|
|
8162
8574
|
if (copyAllFiles) {
|
|
8163
|
-
await copyAllFromDefault(configDir);
|
|
8164
|
-
|
|
8575
|
+
await copyAllFromDefault(configDir, autoSync);
|
|
8576
|
+
if (autoSync) {
|
|
8577
|
+
console.log(source_default.green("\u2713 Copied all files with auto-sync (plugins/skills symlinked)"));
|
|
8578
|
+
} else {
|
|
8579
|
+
console.log(source_default.green("\u2713 Copied all files from default Claude (manual mode)"));
|
|
8580
|
+
}
|
|
8165
8581
|
}
|
|
8166
8582
|
if (providerTemplate && !copySettings && !copyAllFiles) {
|
|
8167
8583
|
await createSettingsFromTemplate(configDir, providerTemplate, apiKey);
|
|
8168
|
-
console.log(source_default.green(
|
|
8584
|
+
console.log(source_default.green(`\u2713 Applied ${providerTemplate.displayName} template`));
|
|
8169
8585
|
} else if (providerTemplate && (copySettings || copyAllFiles)) {
|
|
8170
|
-
console.log(source_default.yellow("
|
|
8586
|
+
console.log(source_default.yellow("\u26A0 Provider template skipped (copied settings from default Claude)"));
|
|
8171
8587
|
}
|
|
8172
8588
|
console.log(source_default.green(`
|
|
8173
|
-
|
|
8589
|
+
\u2713 Instance '${name}' created successfully!`));
|
|
8174
8590
|
console.log(source_default.gray(` Binary: ${binaryPath}`));
|
|
8175
8591
|
console.log(source_default.gray(` Config: ${configDir}`));
|
|
8176
8592
|
const binDir = binaryPath.substring(0, binaryPath.lastIndexOf("/"));
|
|
8177
8593
|
const pathEnv = process.env.PATH || "";
|
|
8178
8594
|
const isInPath = pathEnv.split(":").some((p) => p === binDir);
|
|
8179
8595
|
if (!isInPath) {
|
|
8180
|
-
console.log(source_default.yellow(
|
|
8596
|
+
console.log(source_default.yellow(`\u26A0 Warning: ${binDir} is not in your PATH`));
|
|
8181
8597
|
console.log(source_default.gray(`Add to PATH by running:`));
|
|
8182
8598
|
console.log(source_default.cyan(` echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc`));
|
|
8183
8599
|
console.log(source_default.cyan(` source ~/.zshrc`));
|
|
@@ -8195,10 +8611,12 @@ async function handleListInstances(instances) {
|
|
|
8195
8611
|
return;
|
|
8196
8612
|
}
|
|
8197
8613
|
for (const instance of instances) {
|
|
8198
|
-
console.log(source_default.cyan(
|
|
8614
|
+
console.log(source_default.cyan(`\u25CF ${instance.name}`));
|
|
8199
8615
|
console.log(source_default.gray(` Binary: ${instance.binaryPath}`));
|
|
8200
8616
|
console.log(source_default.gray(` Config: ${instance.configDir}`));
|
|
8201
8617
|
console.log(source_default.gray(` Created: ${new Date(instance.createdAt).toLocaleString()}`));
|
|
8618
|
+
const autoSyncStatus = instance.autoSync !== false ? source_default.green("on") : source_default.yellow("off");
|
|
8619
|
+
console.log(source_default.gray(` Auto-sync: ${autoSyncStatus}`));
|
|
8202
8620
|
console.log();
|
|
8203
8621
|
}
|
|
8204
8622
|
}
|
|
@@ -8208,7 +8626,7 @@ async function handleShowInstanceInfo(instances) {
|
|
|
8208
8626
|
return;
|
|
8209
8627
|
}
|
|
8210
8628
|
console.log(source_default.bold(`
|
|
8211
|
-
|
|
8629
|
+
\u2139\uFE0F Instance Details
|
|
8212
8630
|
`));
|
|
8213
8631
|
const { instanceName } = await import_prompts.default({
|
|
8214
8632
|
type: "select",
|
|
@@ -8229,6 +8647,8 @@ Instance: ${source_default.cyan(instance.name)}`));
|
|
|
8229
8647
|
console.log(`${source_default.gray("Binary:")} ${instance.binaryPath}`);
|
|
8230
8648
|
console.log(`${source_default.gray("Config:")} ${instance.configDir}`);
|
|
8231
8649
|
console.log(`${source_default.gray("Created:")} ${new Date(instance.createdAt).toLocaleString()}`);
|
|
8650
|
+
const autoSyncStatus = instance.autoSync !== false ? source_default.green("\u2713 Enabled") : source_default.yellow("\u2717 Disabled");
|
|
8651
|
+
console.log(`${source_default.gray("Auto-sync:")} ${autoSyncStatus}`);
|
|
8232
8652
|
}
|
|
8233
8653
|
async function handleRemoveInstance(instances) {
|
|
8234
8654
|
if (instances.length === 0) {
|
|
@@ -8236,7 +8656,7 @@ async function handleRemoveInstance(instances) {
|
|
|
8236
8656
|
return;
|
|
8237
8657
|
}
|
|
8238
8658
|
console.log(source_default.bold(`
|
|
8239
|
-
\uD83D\uDDD1
|
|
8659
|
+
\uD83D\uDDD1\uFE0F Remove Instance
|
|
8240
8660
|
`));
|
|
8241
8661
|
const { instanceName } = await import_prompts.default({
|
|
8242
8662
|
type: "select",
|
|
@@ -8259,14 +8679,176 @@ async function handleRemoveInstance(instances) {
|
|
|
8259
8679
|
initial: false
|
|
8260
8680
|
});
|
|
8261
8681
|
if (!confirm) {
|
|
8262
|
-
console.log(source_default.yellow("
|
|
8682
|
+
console.log(source_default.yellow("\u2717 Cancelled"));
|
|
8263
8683
|
return;
|
|
8264
8684
|
}
|
|
8265
8685
|
await removeInstance(instanceName);
|
|
8266
8686
|
removeWrapper(instance.binaryPath);
|
|
8267
|
-
console.log(source_default.green(
|
|
8687
|
+
console.log(source_default.green(`\u2713 Instance '${instanceName}' removed successfully!`));
|
|
8268
8688
|
console.log(source_default.gray(`To remove config files, run: rm -rf ${instance.configDir}`));
|
|
8269
8689
|
}
|
|
8690
|
+
async function handleToggleAutoSync(instances) {
|
|
8691
|
+
if (instances.length === 0) {
|
|
8692
|
+
console.log(source_default.yellow("No instances found."));
|
|
8693
|
+
return;
|
|
8694
|
+
}
|
|
8695
|
+
console.log(source_default.bold(`
|
|
8696
|
+
\uD83D\uDD04 Toggle Auto-Sync
|
|
8697
|
+
`));
|
|
8698
|
+
const { instanceName } = await import_prompts.default({
|
|
8699
|
+
type: "select",
|
|
8700
|
+
name: "instanceName",
|
|
8701
|
+
message: "Select an instance:",
|
|
8702
|
+
choices: instances.map((instance2) => {
|
|
8703
|
+
const currentStatus2 = instance2.autoSync !== false ? "on" : "off";
|
|
8704
|
+
return {
|
|
8705
|
+
title: `${instance2.name} (auto-sync: ${currentStatus2})`,
|
|
8706
|
+
value: instance2.name
|
|
8707
|
+
};
|
|
8708
|
+
})
|
|
8709
|
+
});
|
|
8710
|
+
if (!instanceName)
|
|
8711
|
+
return;
|
|
8712
|
+
const instance = instances.find((i) => i.name === instanceName);
|
|
8713
|
+
if (!instance)
|
|
8714
|
+
return;
|
|
8715
|
+
const currentStatus = instance.autoSync !== false;
|
|
8716
|
+
const { action } = await import_prompts.default({
|
|
8717
|
+
type: "select",
|
|
8718
|
+
name: "action",
|
|
8719
|
+
message: `Auto-sync is currently ${source_default.cyan(currentStatus ? "enabled" : "disabled")}. What would you like to do?`,
|
|
8720
|
+
choices: [
|
|
8721
|
+
{ title: currentStatus ? "Turn off (copy files locally)" : "Turn on (use symlinks)", value: "toggle" },
|
|
8722
|
+
{ title: "Cancel", value: "cancel" }
|
|
8723
|
+
],
|
|
8724
|
+
initial: 0
|
|
8725
|
+
});
|
|
8726
|
+
if (!action || action === "cancel") {
|
|
8727
|
+
console.log(source_default.yellow("\u2717 Cancelled"));
|
|
8728
|
+
return;
|
|
8729
|
+
}
|
|
8730
|
+
const newStatus = !currentStatus;
|
|
8731
|
+
console.log(source_default.bold(`
|
|
8732
|
+
\uD83D\uDD04 ${newStatus ? "Enabling" : "Disabling"} auto-sync for '${instanceName}'...
|
|
8733
|
+
`));
|
|
8734
|
+
try {
|
|
8735
|
+
await updateInstanceAutoSync(instanceName, newStatus);
|
|
8736
|
+
if (newStatus) {
|
|
8737
|
+
await syncPluginsAndSkills(instance.configDir);
|
|
8738
|
+
} else {
|
|
8739
|
+
await unsyncPluginsAndSkills(instance.configDir);
|
|
8740
|
+
}
|
|
8741
|
+
console.log(source_default.green(`
|
|
8742
|
+
\u2713 Auto-sync ${newStatus ? "enabled" : "disabled"} for '${instanceName}'`));
|
|
8743
|
+
} catch (error) {
|
|
8744
|
+
console.error(source_default.red(`\u2717 Error: ${error.message}`));
|
|
8745
|
+
}
|
|
8746
|
+
}
|
|
8747
|
+
async function handleManagePlugins(instances) {
|
|
8748
|
+
if (instances.length === 0) {
|
|
8749
|
+
console.log(source_default.yellow("No instances found."));
|
|
8750
|
+
return;
|
|
8751
|
+
}
|
|
8752
|
+
console.log(source_default.bold(`
|
|
8753
|
+
\uD83D\uDD0C Manage Plugins
|
|
8754
|
+
`));
|
|
8755
|
+
const { action } = await import_prompts.default({
|
|
8756
|
+
type: "select",
|
|
8757
|
+
name: "action",
|
|
8758
|
+
message: "What would you like to do?",
|
|
8759
|
+
choices: [
|
|
8760
|
+
{ title: "\uD83D\uDCCB List all plugins", value: "list" },
|
|
8761
|
+
{ title: "\uD83D\uDCCB Plugins for instance", value: "instance" },
|
|
8762
|
+
{ title: "\u2705 Enable plugins", value: "enable" },
|
|
8763
|
+
{ title: "\u274C Disable plugins", value: "disable" },
|
|
8764
|
+
{ title: "\uD83D\uDCCB Copy from default", value: "copy" },
|
|
8765
|
+
{ title: "Cancel", value: "cancel" }
|
|
8766
|
+
],
|
|
8767
|
+
initial: 0
|
|
8768
|
+
});
|
|
8769
|
+
if (!action || action === "cancel") {
|
|
8770
|
+
console.log(source_default.yellow("\u2717 Cancelled"));
|
|
8771
|
+
return;
|
|
8772
|
+
}
|
|
8773
|
+
switch (action) {
|
|
8774
|
+
case "list":
|
|
8775
|
+
await handlePluginsList("");
|
|
8776
|
+
break;
|
|
8777
|
+
case "instance":
|
|
8778
|
+
const { listInstance } = await import_prompts.default({
|
|
8779
|
+
type: "select",
|
|
8780
|
+
name: "listInstance",
|
|
8781
|
+
message: "Select an instance:",
|
|
8782
|
+
choices: instances.map((i) => ({ title: i.name, value: i.name }))
|
|
8783
|
+
});
|
|
8784
|
+
if (listInstance) {
|
|
8785
|
+
await handlePluginsList(listInstance);
|
|
8786
|
+
}
|
|
8787
|
+
break;
|
|
8788
|
+
case "enable": {
|
|
8789
|
+
const { enableInstance } = await import_prompts.default({
|
|
8790
|
+
type: "select",
|
|
8791
|
+
name: "enableInstance",
|
|
8792
|
+
message: "Select an instance:",
|
|
8793
|
+
choices: instances.map((i) => ({ title: i.name, value: i.name }))
|
|
8794
|
+
});
|
|
8795
|
+
if (!enableInstance)
|
|
8796
|
+
return;
|
|
8797
|
+
const plugins = await getEnabledPlugins(instances.find((i) => i.name === enableInstance).configDir);
|
|
8798
|
+
if (!plugins) {
|
|
8799
|
+
console.log(source_default.yellow("No plugins configured for this instance"));
|
|
8800
|
+
return;
|
|
8801
|
+
}
|
|
8802
|
+
const { pluginsToEnable } = await import_prompts.default({
|
|
8803
|
+
type: "multiselect",
|
|
8804
|
+
name: "pluginsToEnable",
|
|
8805
|
+
message: "Select plugins to enable:",
|
|
8806
|
+
choices: Object.entries(plugins).filter(([_, enabled]) => !enabled).map(([pluginId, _]) => ({ title: pluginId, value: pluginId }))
|
|
8807
|
+
});
|
|
8808
|
+
if (pluginsToEnable && pluginsToEnable.length > 0) {
|
|
8809
|
+
await handlePluginsEnable(enableInstance, pluginsToEnable);
|
|
8810
|
+
}
|
|
8811
|
+
break;
|
|
8812
|
+
}
|
|
8813
|
+
case "disable": {
|
|
8814
|
+
const { disableInstance } = await import_prompts.default({
|
|
8815
|
+
type: "select",
|
|
8816
|
+
name: "disableInstance",
|
|
8817
|
+
message: "Select an instance:",
|
|
8818
|
+
choices: instances.map((i) => ({ title: i.name, value: i.name }))
|
|
8819
|
+
});
|
|
8820
|
+
if (!disableInstance)
|
|
8821
|
+
return;
|
|
8822
|
+
const plugins = await getEnabledPlugins(instances.find((i) => i.name === disableInstance).configDir);
|
|
8823
|
+
if (!plugins) {
|
|
8824
|
+
console.log(source_default.yellow("No plugins configured for this instance"));
|
|
8825
|
+
return;
|
|
8826
|
+
}
|
|
8827
|
+
const { pluginsToDisable } = await import_prompts.default({
|
|
8828
|
+
type: "multiselect",
|
|
8829
|
+
name: "pluginsToDisable",
|
|
8830
|
+
message: "Select plugins to disable:",
|
|
8831
|
+
choices: Object.entries(plugins).filter(([_, enabled]) => enabled).map(([pluginId, _]) => ({ title: pluginId, value: pluginId }))
|
|
8832
|
+
});
|
|
8833
|
+
if (pluginsToDisable && pluginsToDisable.length > 0) {
|
|
8834
|
+
await handlePluginsDisable(disableInstance, pluginsToDisable);
|
|
8835
|
+
}
|
|
8836
|
+
break;
|
|
8837
|
+
}
|
|
8838
|
+
case "copy": {
|
|
8839
|
+
const { copyInstance } = await import_prompts.default({
|
|
8840
|
+
type: "select",
|
|
8841
|
+
name: "copyInstance",
|
|
8842
|
+
message: "Select an instance:",
|
|
8843
|
+
choices: instances.map((i) => ({ title: i.name, value: i.name }))
|
|
8844
|
+
});
|
|
8845
|
+
if (copyInstance) {
|
|
8846
|
+
await handlePluginsCopy(copyInstance);
|
|
8847
|
+
}
|
|
8848
|
+
break;
|
|
8849
|
+
}
|
|
8850
|
+
}
|
|
8851
|
+
}
|
|
8270
8852
|
program2.command("mcp").description("Manage MCP server configurations").argument("[action]", "Action to perform (list, copy, verify)", "list").argument("[instance]", "Instance name (for list/verify)", "").argument("[source]", "Source instance name (for copy)", "").argument("[target]", "Target instance name (for copy)", "").action(async (action = "list", instance = "", source = "", target = "") => {
|
|
8271
8853
|
try {
|
|
8272
8854
|
switch (action) {
|
|
@@ -8280,12 +8862,12 @@ program2.command("mcp").description("Manage MCP server configurations").argument
|
|
|
8280
8862
|
await handleMcpVerify(instance);
|
|
8281
8863
|
break;
|
|
8282
8864
|
default:
|
|
8283
|
-
console.error(source_default.red(
|
|
8865
|
+
console.error(source_default.red(`\u2717 Unknown action: ${action}`));
|
|
8284
8866
|
console.log(source_default.gray("Available actions: list, copy, verify"));
|
|
8285
8867
|
process.exit(1);
|
|
8286
8868
|
}
|
|
8287
8869
|
} catch (error) {
|
|
8288
|
-
console.error(source_default.red(
|
|
8870
|
+
console.error(source_default.red(`\u2717 Error: ${error.message}`));
|
|
8289
8871
|
process.exit(1);
|
|
8290
8872
|
}
|
|
8291
8873
|
});
|
|
@@ -8303,11 +8885,11 @@ async function handleMcpList(instanceName) {
|
|
|
8303
8885
|
for (const instance of instances) {
|
|
8304
8886
|
const mcpServers = await listMcpServers(instance.name);
|
|
8305
8887
|
const hasMcp = mcpServers && Object.keys(mcpServers).length > 0;
|
|
8306
|
-
console.log(source_default.cyan(
|
|
8888
|
+
console.log(source_default.cyan(`\u25CF ${instance.name}`));
|
|
8307
8889
|
console.log(source_default.gray(` MCP Servers: ${hasMcp ? Object.keys(mcpServers).length : "None"}`));
|
|
8308
8890
|
if (hasMcp) {
|
|
8309
8891
|
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
8310
|
-
console.log(source_default.gray(`
|
|
8892
|
+
console.log(source_default.gray(` \u2022 ${serverName} (${serverConfig.type})`));
|
|
8311
8893
|
}
|
|
8312
8894
|
}
|
|
8313
8895
|
console.log();
|
|
@@ -8322,7 +8904,7 @@ async function handleMcpList(instanceName) {
|
|
|
8322
8904
|
\uD83D\uDCCB MCP Servers in '${instanceName}'
|
|
8323
8905
|
`));
|
|
8324
8906
|
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
8325
|
-
console.log(source_default.cyan(
|
|
8907
|
+
console.log(source_default.cyan(`\u25CF ${serverName}`));
|
|
8326
8908
|
console.log(source_default.gray(` Type: ${serverConfig.type}`));
|
|
8327
8909
|
if (serverConfig.command) {
|
|
8328
8910
|
console.log(source_default.gray(` Command: ${serverConfig.command}`));
|
|
@@ -8385,7 +8967,7 @@ async function handleMcpCopy(sourceInstanceName, targetInstanceName) {
|
|
|
8385
8967
|
\uD83D\uDD04 Copying MCP servers from '${source}' to '${target}'
|
|
8386
8968
|
`));
|
|
8387
8969
|
await copyMcpServersBetweenInstances(source, target);
|
|
8388
|
-
console.log(source_default.green(
|
|
8970
|
+
console.log(source_default.green(`\u2713 MCP servers copied successfully!`));
|
|
8389
8971
|
}
|
|
8390
8972
|
async function handleMcpVerify(instanceName) {
|
|
8391
8973
|
const instances = await listInstances();
|
|
@@ -8413,20 +8995,48 @@ async function handleMcpVerify(instanceName) {
|
|
|
8413
8995
|
`));
|
|
8414
8996
|
const mcpServers = await listMcpServers(instance);
|
|
8415
8997
|
if (!mcpServers) {
|
|
8416
|
-
console.log(source_default.yellow("
|
|
8998
|
+
console.log(source_default.yellow("\u26A0 No MCP configuration found"));
|
|
8417
8999
|
return;
|
|
8418
9000
|
}
|
|
8419
9001
|
const serverCount = Object.keys(mcpServers).length;
|
|
8420
|
-
console.log(source_default.green(
|
|
9002
|
+
console.log(source_default.green(`\u2713 Found ${serverCount} MCP server(s)`));
|
|
8421
9003
|
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
8422
|
-
console.log(source_default.gray(`
|
|
9004
|
+
console.log(source_default.gray(` \u2022 ${serverName}: ${serverConfig.type}`));
|
|
8423
9005
|
if (serverConfig.type === "stdio" && !serverConfig.command) {
|
|
8424
|
-
console.log(source_default.yellow(`
|
|
9006
|
+
console.log(source_default.yellow(` \u26A0 Missing command for stdio server`));
|
|
8425
9007
|
} else if ((serverConfig.type === "http" || serverConfig.type === "sse") && !serverConfig.url) {
|
|
8426
|
-
console.log(source_default.yellow(`
|
|
9008
|
+
console.log(source_default.yellow(` \u26A0 Missing URL for ${serverConfig.type} server`));
|
|
8427
9009
|
} else {
|
|
8428
|
-
console.log(source_default.green(`
|
|
9010
|
+
console.log(source_default.green(` \u2713 Configuration looks valid`));
|
|
8429
9011
|
}
|
|
8430
9012
|
}
|
|
8431
9013
|
}
|
|
8432
9014
|
program2.parse();
|
|
9015
|
+
async function runUpdateCheck() {
|
|
9016
|
+
try {
|
|
9017
|
+
const updateInfo = await checkForClaudeMultiUpdates();
|
|
9018
|
+
if (updateInfo.updateAvailable) {
|
|
9019
|
+
console.log();
|
|
9020
|
+
console.log(source_default.yellow(`New version available: claude-multi ${updateInfo.latest}`));
|
|
9021
|
+
console.log(source_default.gray(`Current version: ${updateInfo.current}`));
|
|
9022
|
+
console.log();
|
|
9023
|
+
const { shouldUpdate } = await import_prompts.default({
|
|
9024
|
+
type: "confirm",
|
|
9025
|
+
name: "shouldUpdate",
|
|
9026
|
+
message: "Would you like to update now?",
|
|
9027
|
+
initial: true
|
|
9028
|
+
});
|
|
9029
|
+
if (shouldUpdate) {
|
|
9030
|
+
console.log(source_default.cyan(`
|
|
9031
|
+
Updating claude-multi...
|
|
9032
|
+
`));
|
|
9033
|
+
upgradeClaudeMulti();
|
|
9034
|
+
console.log(source_default.green(`
|
|
9035
|
+
Update complete! Please run your command again.
|
|
9036
|
+
`));
|
|
9037
|
+
process.exit(0);
|
|
9038
|
+
}
|
|
9039
|
+
}
|
|
9040
|
+
} catch {}
|
|
9041
|
+
}
|
|
9042
|
+
runUpdateCheck().catch(() => {});
|