harnessed 3.9.6 → 3.9.8
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/cli.mjs
CHANGED
|
@@ -789,11 +789,33 @@ async function isMcpServerRegistered(name) {
|
|
|
789
789
|
return Object.hasOwn(servers, name);
|
|
790
790
|
}
|
|
791
791
|
async function isPluginRegistered(pluginName) {
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
792
|
+
try {
|
|
793
|
+
const path = join(homedir(), ".claude", "plugins", "installed_plugins.json");
|
|
794
|
+
const raw = await readFile(path, "utf8");
|
|
795
|
+
const parsed = JSON.parse(raw);
|
|
796
|
+
const plugins = parsed.plugins;
|
|
797
|
+
if (plugins && typeof plugins === "object") {
|
|
798
|
+
if (Object.hasOwn(plugins, pluginName)) return true;
|
|
799
|
+
if (Object.keys(plugins).some((k) => k.split("@")[0] === pluginName)) return true;
|
|
800
|
+
}
|
|
801
|
+
} catch {
|
|
802
|
+
}
|
|
803
|
+
for (const path of [
|
|
804
|
+
join(homedir(), ".claude", "settings.json"),
|
|
805
|
+
join(homedir(), ".claude.json")
|
|
806
|
+
]) {
|
|
807
|
+
try {
|
|
808
|
+
const raw = await readFile(path, "utf8");
|
|
809
|
+
const parsed = JSON.parse(raw);
|
|
810
|
+
const plugins = parsed.enabledPlugins;
|
|
811
|
+
if (plugins && typeof plugins === "object") {
|
|
812
|
+
if (Object.hasOwn(plugins, pluginName)) return true;
|
|
813
|
+
if (Object.keys(plugins).some((k) => k.split("@")[0] === pluginName)) return true;
|
|
814
|
+
}
|
|
815
|
+
} catch {
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
return false;
|
|
797
819
|
}
|
|
798
820
|
var init_readClaudeConfig = __esm({
|
|
799
821
|
"src/installers/lib/readClaudeConfig.ts"() {
|
|
@@ -1200,7 +1222,7 @@ var init_auto_install = __esm({
|
|
|
1200
1222
|
|
|
1201
1223
|
// package.json
|
|
1202
1224
|
var package_default = {
|
|
1203
|
-
version: "3.9.
|
|
1225
|
+
version: "3.9.8"};
|
|
1204
1226
|
|
|
1205
1227
|
// src/manifest/errors.ts
|
|
1206
1228
|
function instancePathToKeyPath(instancePath) {
|
|
@@ -4357,6 +4379,10 @@ var installCcHookAdd = async (ctx) => {
|
|
|
4357
4379
|
await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, "", "");
|
|
4358
4380
|
return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath3] };
|
|
4359
4381
|
};
|
|
4382
|
+
function expandTildeForWindows(cmd) {
|
|
4383
|
+
const home = homedir().replace(/\\/g, "/");
|
|
4384
|
+
return cmd.replace(/(^|[\s"'`(])~\//g, `$1${home}/`);
|
|
4385
|
+
}
|
|
4360
4386
|
var DEFAULT_VERIFY_TIMEOUT_MS = 15e3;
|
|
4361
4387
|
var DEFAULT_INSTALL_TIMEOUT_MS = 6e4;
|
|
4362
4388
|
async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
@@ -4381,7 +4407,13 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
|
4381
4407
|
const cwd = installCfg.cwd ?? ctx.cwd;
|
|
4382
4408
|
let child;
|
|
4383
4409
|
if (process.platform === "win32") {
|
|
4384
|
-
|
|
4410
|
+
const expandedCmd = expandTildeForWindows(cmd);
|
|
4411
|
+
const expandedArgs = args.map(expandTildeForWindows);
|
|
4412
|
+
child = spawn("cmd.exe", ["/c", expandedCmd, ...expandedArgs], {
|
|
4413
|
+
cwd,
|
|
4414
|
+
env,
|
|
4415
|
+
windowsHide: true
|
|
4416
|
+
});
|
|
4385
4417
|
} else {
|
|
4386
4418
|
const joined = args.length > 0 ? `${cmd} ${args.join(" ")}` : cmd;
|
|
4387
4419
|
child = spawn("/bin/sh", ["-c", joined], { cwd, env });
|
|
@@ -4434,8 +4466,10 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
|
4434
4466
|
|
|
4435
4467
|
// src/installers/lib/idempotent.ts
|
|
4436
4468
|
var IDEMPOTENT_CHECK_TIMEOUT_MS = 1e4;
|
|
4437
|
-
async function isAlreadyInstalled(ctx) {
|
|
4438
|
-
|
|
4469
|
+
async function isAlreadyInstalled(ctx, opts = {}) {
|
|
4470
|
+
const honorUpdateFlag = opts.honorUpdateFlag !== false;
|
|
4471
|
+
if (honorUpdateFlag && ctx.opts.updateInstalled === true) return false;
|
|
4472
|
+
if (ctx.opts.dryRun) return false;
|
|
4439
4473
|
const idempotentCmd = ctx.manifest.spec.install.idempotent_check;
|
|
4440
4474
|
if (typeof idempotentCmd !== "string" || idempotentCmd.length === 0) {
|
|
4441
4475
|
return false;
|
|
@@ -4856,6 +4890,9 @@ var installMcpHttpAdd = async (ctx) => {
|
|
|
4856
4890
|
const e = pre.errors[0] ?? err(ctx, "/", "preflight failed (no detail)", "preflight");
|
|
4857
4891
|
return { ok: false, phase: "preflight", error: e };
|
|
4858
4892
|
}
|
|
4893
|
+
if (await isAlreadyInstalled(ctx, { honorUpdateFlag: false })) {
|
|
4894
|
+
return { ok: true, alreadyInstalled: true, backupId: "noop-idempotent" };
|
|
4895
|
+
}
|
|
4859
4896
|
const name = ctx.manifest.metadata.name;
|
|
4860
4897
|
const url = extractUrl(install.cmd);
|
|
4861
4898
|
if (!url) {
|
|
@@ -4992,6 +5029,9 @@ var installMcpStdioAdd = async (ctx) => {
|
|
|
4992
5029
|
const e = pre.errors[0] ?? err(ctx, "/", "preflight failed (no detail)", "preflight");
|
|
4993
5030
|
return { ok: false, phase: "preflight", error: e };
|
|
4994
5031
|
}
|
|
5032
|
+
if (await isAlreadyInstalled(ctx, { honorUpdateFlag: false })) {
|
|
5033
|
+
return { ok: true, alreadyInstalled: true, backupId: "noop-idempotent" };
|
|
5034
|
+
}
|
|
4995
5035
|
const name = ctx.manifest.metadata.name;
|
|
4996
5036
|
const pkg = ctx.manifest.metadata.upstream.source;
|
|
4997
5037
|
const ver = install.npm_version;
|
|
@@ -6110,7 +6150,7 @@ async function runStepBInstall(manifestPaths, runOpts = {}) {
|
|
|
6110
6150
|
}
|
|
6111
6151
|
const name = v.manifest.metadata.name;
|
|
6112
6152
|
const r = await runInstall(v.manifest, opts);
|
|
6113
|
-
if ("aborted" in r) return { status: "skipped", name };
|
|
6153
|
+
if ("aborted" in r) return { status: "skipped", name, reason: r.reason };
|
|
6114
6154
|
if (r.ok && "alreadyInstalled" in r && r.alreadyInstalled)
|
|
6115
6155
|
return { status: "already-installed", name };
|
|
6116
6156
|
if (r.ok) return { status: "installed", name };
|
|
@@ -6129,8 +6169,10 @@ async function runStepBInstall(manifestPaths, runOpts = {}) {
|
|
|
6129
6169
|
};
|
|
6130
6170
|
if (v.status === "installed") installed.push(v.name);
|
|
6131
6171
|
else if (v.status === "already-installed") alreadyInstalled.push(v.name);
|
|
6132
|
-
else if (v.status === "skipped")
|
|
6133
|
-
|
|
6172
|
+
else if (v.status === "skipped") {
|
|
6173
|
+
const skipReason = v.reason ?? "unknown";
|
|
6174
|
+
skipped.push({ name: v.name, reason: skipReason });
|
|
6175
|
+
} else
|
|
6134
6176
|
failed.push(`${v.name}: ${v.reason}`);
|
|
6135
6177
|
}
|
|
6136
6178
|
return { installed, alreadyInstalled, skipped, failed, elapsedMs: Date.now() - start };
|
|
@@ -6285,20 +6327,9 @@ function registerSetup(program2) {
|
|
|
6285
6327
|
} else {
|
|
6286
6328
|
console.warn(t("setup.step_d.skipped", { message: dResult.message }));
|
|
6287
6329
|
}
|
|
6288
|
-
let updateInstalled2 = raw.updateInstalled === true;
|
|
6289
|
-
if (!updateInstalled2 && !dryRun && raw.nonInteractive !== true) {
|
|
6290
|
-
const isTty = process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
6291
|
-
if (isTty) {
|
|
6292
|
-
const { confirm: confirm4, isCancel: isCancel5 } = await import('@clack/prompts');
|
|
6293
|
-
const ans = await confirm4({
|
|
6294
|
-
message: "Update already-installed third-party plugins? (excludes MCP servers)",
|
|
6295
|
-
initialValue: false
|
|
6296
|
-
});
|
|
6297
|
-
if (!isCancel5(ans) && ans === true) updateInstalled2 = true;
|
|
6298
|
-
}
|
|
6299
|
-
}
|
|
6300
6330
|
const manifestPaths = await listBaseManifests2(pkgRoot);
|
|
6301
|
-
const
|
|
6331
|
+
const forceFirstPass = raw.updateInstalled === true;
|
|
6332
|
+
const b = await runStepBInstall(manifestPaths, { updateInstalled: forceFirstPass });
|
|
6302
6333
|
const stepBMs = (b.elapsedMs / 1e3).toFixed(1);
|
|
6303
6334
|
console.log(
|
|
6304
6335
|
t("setup.step_b_complete", {
|
|
@@ -6314,8 +6345,32 @@ function registerSetup(program2) {
|
|
|
6314
6345
|
console.log(
|
|
6315
6346
|
` [B] already-installed ${n} \u2014 run \`/mcp\` in Claude Code to verify connection`
|
|
6316
6347
|
);
|
|
6317
|
-
for (const
|
|
6348
|
+
for (const s of b.skipped) console.log(` [B] skipped ${s.name} \u2014 ${s.reason}`);
|
|
6318
6349
|
for (const n of b.failed) console.error(` [B] failed ${n}`);
|
|
6350
|
+
if (!forceFirstPass && !dryRun && raw.nonInteractive !== true && b.alreadyInstalled.length > 0) {
|
|
6351
|
+
const isTty = process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
6352
|
+
if (isTty) {
|
|
6353
|
+
const { confirm: confirm4, isCancel: isCancel5 } = await import('@clack/prompts');
|
|
6354
|
+
const ans = await confirm4({
|
|
6355
|
+
message: `Update ${b.alreadyInstalled.length} already-installed plugin(s) listed above? (MCP servers excluded \u2014 they ignore force-update)`,
|
|
6356
|
+
initialValue: false
|
|
6357
|
+
});
|
|
6358
|
+
if (!isCancel5(ans) && ans === true) {
|
|
6359
|
+
const b2 = await runStepBInstall(manifestPaths, { updateInstalled: true });
|
|
6360
|
+
const stepB2Ms = (b2.elapsedMs / 1e3).toFixed(1);
|
|
6361
|
+
console.log(
|
|
6362
|
+
`
|
|
6363
|
+
Force-update pass complete: ${b2.installed.length} installed / ${b2.alreadyInstalled.length} still-already-installed (MCP) / ${b2.skipped.length} skipped / ${b2.failed.length} failed [parallel ${stepB2Ms}s]`
|
|
6364
|
+
);
|
|
6365
|
+
for (const n of b2.installed) console.log(` [B*] installed ${n}`);
|
|
6366
|
+
for (const n of b2.alreadyInstalled)
|
|
6367
|
+
console.log(` [B*] already-installed ${n} (MCP / no force-update)`);
|
|
6368
|
+
for (const s of b2.skipped)
|
|
6369
|
+
console.log(` [B*] skipped ${s.name} \u2014 ${s.reason}`);
|
|
6370
|
+
for (const n of b2.failed) console.error(` [B*] failed ${n}`);
|
|
6371
|
+
}
|
|
6372
|
+
}
|
|
6373
|
+
}
|
|
6319
6374
|
console.log(
|
|
6320
6375
|
t("setup.complete", {
|
|
6321
6376
|
skills: skillsInstalled,
|