harnessed 3.9.4 → 3.9.6
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 +176 -156
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -762,6 +762,43 @@ var init_check_mattpocock_skills = __esm({
|
|
|
762
762
|
INSTALL_COMMANDS2 = ["npx skills@latest add mattpocock/skills"];
|
|
763
763
|
}
|
|
764
764
|
});
|
|
765
|
+
function getUserClaudeJsonPath() {
|
|
766
|
+
return join(homedir(), ".claude.json");
|
|
767
|
+
}
|
|
768
|
+
async function readUserClaudeJson() {
|
|
769
|
+
const path = getUserClaudeJsonPath();
|
|
770
|
+
let raw;
|
|
771
|
+
try {
|
|
772
|
+
raw = await readFile(path, "utf8");
|
|
773
|
+
} catch (err2) {
|
|
774
|
+
if (err2.code === "ENOENT") return {};
|
|
775
|
+
throw err2;
|
|
776
|
+
}
|
|
777
|
+
try {
|
|
778
|
+
const parsed = JSON.parse(raw);
|
|
779
|
+
if (parsed === null || typeof parsed !== "object") return {};
|
|
780
|
+
return parsed;
|
|
781
|
+
} catch {
|
|
782
|
+
return {};
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
async function isMcpServerRegistered(name) {
|
|
786
|
+
const config = await readUserClaudeJson();
|
|
787
|
+
const servers = config.mcpServers;
|
|
788
|
+
if (!servers || typeof servers !== "object") return false;
|
|
789
|
+
return Object.hasOwn(servers, name);
|
|
790
|
+
}
|
|
791
|
+
async function isPluginRegistered(pluginName) {
|
|
792
|
+
const config = await readUserClaudeJson();
|
|
793
|
+
const plugins = config.enabledPlugins;
|
|
794
|
+
if (!plugins || typeof plugins !== "object") return false;
|
|
795
|
+
if (Object.hasOwn(plugins, pluginName)) return true;
|
|
796
|
+
return Object.keys(plugins).some((k) => k.split("@")[0] === pluginName);
|
|
797
|
+
}
|
|
798
|
+
var init_readClaudeConfig = __esm({
|
|
799
|
+
"src/installers/lib/readClaudeConfig.ts"() {
|
|
800
|
+
}
|
|
801
|
+
});
|
|
765
802
|
|
|
766
803
|
// src/cli/lib/check-mcp-availability.ts
|
|
767
804
|
var check_mcp_availability_exports = {};
|
|
@@ -769,17 +806,15 @@ __export(check_mcp_availability_exports, {
|
|
|
769
806
|
checkMcpAvailability: () => checkMcpAvailability
|
|
770
807
|
});
|
|
771
808
|
async function checkMcpAvailability() {
|
|
772
|
-
const
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
missing = TARGET_SERVERS.filter((s) => !installed.includes(s));
|
|
782
|
-
} catch {
|
|
809
|
+
const installed = [];
|
|
810
|
+
const missing = [];
|
|
811
|
+
for (const s of TARGET_SERVERS) {
|
|
812
|
+
const present = await isMcpServerRegistered(s);
|
|
813
|
+
if (present) {
|
|
814
|
+
installed.push(s);
|
|
815
|
+
} else {
|
|
816
|
+
missing.push(s);
|
|
817
|
+
}
|
|
783
818
|
}
|
|
784
819
|
if (missing.length === 0) {
|
|
785
820
|
return {
|
|
@@ -788,35 +823,26 @@ async function checkMcpAvailability() {
|
|
|
788
823
|
message: `all 3 installed: ${installed.join(", ")}`
|
|
789
824
|
};
|
|
790
825
|
}
|
|
791
|
-
const installCommands = missing.map((s) => SERVER_INSTALL_COMMANDS[s]);
|
|
792
826
|
if (installed.length === 0) {
|
|
793
827
|
return {
|
|
794
828
|
name: "MCP servers (tavily/exa/chrome-devtools)",
|
|
795
829
|
status: "warn",
|
|
796
|
-
message: "none of 3 target MCP servers
|
|
797
|
-
fix: "
|
|
798
|
-
install_commands: installCommands
|
|
830
|
+
message: "none of 3 target MCP servers registered in ~/.claude.json",
|
|
831
|
+
fix: "run `harnessed setup` to install via Step B (manifests/tools/{tavily,exa,chrome-devtools}-mcp.yaml)"
|
|
799
832
|
};
|
|
800
833
|
}
|
|
801
834
|
return {
|
|
802
835
|
name: "MCP servers (tavily/exa/chrome-devtools)",
|
|
803
836
|
status: "warn",
|
|
804
837
|
message: `${installed.length}/3 installed: ${installed.join(", ")}; missing: ${missing.join(", ")}`,
|
|
805
|
-
fix: `
|
|
806
|
-
install_commands: installCommands
|
|
838
|
+
fix: "run `harnessed setup` to install missing MCPs via Step B"
|
|
807
839
|
};
|
|
808
840
|
}
|
|
809
|
-
var TARGET_SERVERS
|
|
841
|
+
var TARGET_SERVERS;
|
|
810
842
|
var init_check_mcp_availability = __esm({
|
|
811
843
|
"src/cli/lib/check-mcp-availability.ts"() {
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
"tavily-remote-mcp": "claude mcp add tavily-remote-mcp --transport http https://mcp.tavily.com/mcp/",
|
|
815
|
-
exa: "claude mcp add --transport http exa https://mcp.exa.ai/mcp",
|
|
816
|
-
// chrome-devtools: official Claude marketplace direct install (v3.9.2 dogfood
|
|
817
|
-
// confirmed — was assumed npx in v3.9.1 SPEC, corrected to official marketplace).
|
|
818
|
-
"chrome-devtools": "claude plugin install chrome-devtools-mcp"
|
|
819
|
-
};
|
|
844
|
+
init_readClaudeConfig();
|
|
845
|
+
TARGET_SERVERS = ["tavily-mcp", "exa-mcp", "chrome-devtools-mcp"];
|
|
820
846
|
}
|
|
821
847
|
});
|
|
822
848
|
|
|
@@ -1174,7 +1200,7 @@ var init_auto_install = __esm({
|
|
|
1174
1200
|
|
|
1175
1201
|
// package.json
|
|
1176
1202
|
var package_default = {
|
|
1177
|
-
version: "3.9.
|
|
1203
|
+
version: "3.9.6"};
|
|
1178
1204
|
|
|
1179
1205
|
// src/manifest/errors.ts
|
|
1180
1206
|
function instancePathToKeyPath(instancePath) {
|
|
@@ -4331,39 +4357,98 @@ var installCcHookAdd = async (ctx) => {
|
|
|
4331
4357
|
await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, "", "");
|
|
4332
4358
|
return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath3] };
|
|
4333
4359
|
};
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4360
|
+
var DEFAULT_VERIFY_TIMEOUT_MS = 15e3;
|
|
4361
|
+
var DEFAULT_INSTALL_TIMEOUT_MS = 6e4;
|
|
4362
|
+
async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
4363
|
+
const violation = checkCmdString(cmd);
|
|
4364
|
+
if (violation) {
|
|
4365
|
+
return {
|
|
4366
|
+
ok: false,
|
|
4367
|
+
phase: "preflight",
|
|
4368
|
+
error: {
|
|
4369
|
+
file: ctx.manifest.metadata.name,
|
|
4370
|
+
path: "/spec/install/cmd",
|
|
4371
|
+
message: `shell escape detected at spawn boundary: '${violation.label}' (${violation.hint}) \u2014 refusing to execute. v0.1 forbids dynamic shell evaluation; this is a defense-in-depth gate after schema validation.`,
|
|
4372
|
+
line: null,
|
|
4373
|
+
column: null,
|
|
4374
|
+
keyword: "security-gate-bypass"
|
|
4375
|
+
}
|
|
4376
|
+
};
|
|
4345
4377
|
}
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4378
|
+
const installCfg = ctx.manifest.spec.install;
|
|
4379
|
+
const effectiveTimeoutMs = timeoutMs ?? DEFAULT_INSTALL_TIMEOUT_MS;
|
|
4380
|
+
const env = { ...process.env, ...installCfg.env ?? {} };
|
|
4381
|
+
const cwd = installCfg.cwd ?? ctx.cwd;
|
|
4382
|
+
let child;
|
|
4383
|
+
if (process.platform === "win32") {
|
|
4384
|
+
child = spawn("cmd.exe", ["/c", cmd, ...args], { cwd, env, windowsHide: true });
|
|
4385
|
+
} else {
|
|
4386
|
+
const joined = args.length > 0 ? `${cmd} ${args.join(" ")}` : cmd;
|
|
4387
|
+
child = spawn("/bin/sh", ["-c", joined], { cwd, env });
|
|
4352
4388
|
}
|
|
4389
|
+
let stdout2 = "";
|
|
4390
|
+
let stderr = "";
|
|
4391
|
+
child.stdout?.setEncoding("utf8").on("data", (chunk) => {
|
|
4392
|
+
stdout2 += chunk;
|
|
4393
|
+
});
|
|
4394
|
+
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
4395
|
+
stderr += chunk;
|
|
4396
|
+
});
|
|
4397
|
+
return await new Promise((resolve15) => {
|
|
4398
|
+
const timer = setTimeout(() => {
|
|
4399
|
+
child.kill("SIGKILL");
|
|
4400
|
+
resolve15({
|
|
4401
|
+
ok: false,
|
|
4402
|
+
phase: "spawn",
|
|
4403
|
+
error: {
|
|
4404
|
+
file: ctx.manifest.metadata.name,
|
|
4405
|
+
path: "/spec/install/cmd",
|
|
4406
|
+
message: `spawn timed out after ${effectiveTimeoutMs}ms (cmd: ${cmd}); partial stderr: ${stderr.slice(0, 200)}`,
|
|
4407
|
+
line: null,
|
|
4408
|
+
column: null,
|
|
4409
|
+
keyword: "spawn-timeout"
|
|
4410
|
+
}
|
|
4411
|
+
});
|
|
4412
|
+
}, effectiveTimeoutMs);
|
|
4413
|
+
child.on("error", (err2) => {
|
|
4414
|
+
clearTimeout(timer);
|
|
4415
|
+
resolve15({
|
|
4416
|
+
ok: false,
|
|
4417
|
+
phase: "spawn",
|
|
4418
|
+
error: {
|
|
4419
|
+
file: ctx.manifest.metadata.name,
|
|
4420
|
+
path: "/spec/install/cmd",
|
|
4421
|
+
message: `spawn failed: ${err2.message}`,
|
|
4422
|
+
line: null,
|
|
4423
|
+
column: null,
|
|
4424
|
+
keyword: "spawn-error"
|
|
4425
|
+
}
|
|
4426
|
+
});
|
|
4427
|
+
});
|
|
4428
|
+
child.on("close", (code) => {
|
|
4429
|
+
clearTimeout(timer);
|
|
4430
|
+
resolve15({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
|
|
4431
|
+
});
|
|
4432
|
+
});
|
|
4353
4433
|
}
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
if (
|
|
4365
|
-
|
|
4434
|
+
|
|
4435
|
+
// src/installers/lib/idempotent.ts
|
|
4436
|
+
var IDEMPOTENT_CHECK_TIMEOUT_MS = 1e4;
|
|
4437
|
+
async function isAlreadyInstalled(ctx) {
|
|
4438
|
+
if (ctx.opts.updateInstalled === true) return false;
|
|
4439
|
+
const idempotentCmd = ctx.manifest.spec.install.idempotent_check;
|
|
4440
|
+
if (typeof idempotentCmd !== "string" || idempotentCmd.length === 0) {
|
|
4441
|
+
return false;
|
|
4442
|
+
}
|
|
4443
|
+
const r = await spawnCmd(ctx, idempotentCmd, [], IDEMPOTENT_CHECK_TIMEOUT_MS);
|
|
4444
|
+
if (!("exitCode" in r)) {
|
|
4445
|
+
return false;
|
|
4446
|
+
}
|
|
4447
|
+
return r.exitCode === 0;
|
|
4366
4448
|
}
|
|
4449
|
+
|
|
4450
|
+
// src/installers/ccPluginMarketplace.ts
|
|
4451
|
+
init_readClaudeConfig();
|
|
4367
4452
|
function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
4368
4453
|
return new Promise((resolve15) => {
|
|
4369
4454
|
const isWin = process.platform === "win32";
|
|
@@ -4424,6 +4509,9 @@ var installCcPluginMarketplace = async (ctx) => {
|
|
|
4424
4509
|
const e = pre.errors[0] ?? err(ctx, "/", "preflight failed (no detail)", "preflight");
|
|
4425
4510
|
return { ok: false, phase: "preflight", error: e };
|
|
4426
4511
|
}
|
|
4512
|
+
if (await isAlreadyInstalled(ctx)) {
|
|
4513
|
+
return { ok: true, alreadyInstalled: true, backupId: "noop-idempotent" };
|
|
4514
|
+
}
|
|
4427
4515
|
const parsed = parseCmd(install.cmd);
|
|
4428
4516
|
if (!parsed) {
|
|
4429
4517
|
return {
|
|
@@ -4524,82 +4612,6 @@ ${newEntry}
|
|
|
4524
4612
|
await updateInstalled(ctx.cwd, ctx.manifest.metadata.name, install.git_ref, "");
|
|
4525
4613
|
return { ok: true, backupId: bk.backupId, appliedFiles: [settingsFile] };
|
|
4526
4614
|
};
|
|
4527
|
-
var DEFAULT_VERIFY_TIMEOUT_MS = 15e3;
|
|
4528
|
-
var DEFAULT_INSTALL_TIMEOUT_MS = 6e4;
|
|
4529
|
-
async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
4530
|
-
const violation = checkCmdString(cmd);
|
|
4531
|
-
if (violation) {
|
|
4532
|
-
return {
|
|
4533
|
-
ok: false,
|
|
4534
|
-
phase: "preflight",
|
|
4535
|
-
error: {
|
|
4536
|
-
file: ctx.manifest.metadata.name,
|
|
4537
|
-
path: "/spec/install/cmd",
|
|
4538
|
-
message: `shell escape detected at spawn boundary: '${violation.label}' (${violation.hint}) \u2014 refusing to execute. v0.1 forbids dynamic shell evaluation; this is a defense-in-depth gate after schema validation.`,
|
|
4539
|
-
line: null,
|
|
4540
|
-
column: null,
|
|
4541
|
-
keyword: "security-gate-bypass"
|
|
4542
|
-
}
|
|
4543
|
-
};
|
|
4544
|
-
}
|
|
4545
|
-
const installCfg = ctx.manifest.spec.install;
|
|
4546
|
-
const effectiveTimeoutMs = timeoutMs ?? DEFAULT_INSTALL_TIMEOUT_MS;
|
|
4547
|
-
const env = { ...process.env, ...installCfg.env ?? {} };
|
|
4548
|
-
const cwd = installCfg.cwd ?? ctx.cwd;
|
|
4549
|
-
let child;
|
|
4550
|
-
if (process.platform === "win32") {
|
|
4551
|
-
child = spawn("cmd.exe", ["/c", cmd, ...args], { cwd, env, windowsHide: true });
|
|
4552
|
-
} else {
|
|
4553
|
-
const joined = args.length > 0 ? `${cmd} ${args.join(" ")}` : cmd;
|
|
4554
|
-
child = spawn("/bin/sh", ["-c", joined], { cwd, env });
|
|
4555
|
-
}
|
|
4556
|
-
let stdout2 = "";
|
|
4557
|
-
let stderr = "";
|
|
4558
|
-
child.stdout?.setEncoding("utf8").on("data", (chunk) => {
|
|
4559
|
-
stdout2 += chunk;
|
|
4560
|
-
});
|
|
4561
|
-
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
4562
|
-
stderr += chunk;
|
|
4563
|
-
});
|
|
4564
|
-
return await new Promise((resolve15) => {
|
|
4565
|
-
const timer = setTimeout(() => {
|
|
4566
|
-
child.kill("SIGKILL");
|
|
4567
|
-
resolve15({
|
|
4568
|
-
ok: false,
|
|
4569
|
-
phase: "spawn",
|
|
4570
|
-
error: {
|
|
4571
|
-
file: ctx.manifest.metadata.name,
|
|
4572
|
-
path: "/spec/install/cmd",
|
|
4573
|
-
message: `spawn timed out after ${effectiveTimeoutMs}ms (cmd: ${cmd}); partial stderr: ${stderr.slice(0, 200)}`,
|
|
4574
|
-
line: null,
|
|
4575
|
-
column: null,
|
|
4576
|
-
keyword: "spawn-timeout"
|
|
4577
|
-
}
|
|
4578
|
-
});
|
|
4579
|
-
}, effectiveTimeoutMs);
|
|
4580
|
-
child.on("error", (err2) => {
|
|
4581
|
-
clearTimeout(timer);
|
|
4582
|
-
resolve15({
|
|
4583
|
-
ok: false,
|
|
4584
|
-
phase: "spawn",
|
|
4585
|
-
error: {
|
|
4586
|
-
file: ctx.manifest.metadata.name,
|
|
4587
|
-
path: "/spec/install/cmd",
|
|
4588
|
-
message: `spawn failed: ${err2.message}`,
|
|
4589
|
-
line: null,
|
|
4590
|
-
column: null,
|
|
4591
|
-
keyword: "spawn-error"
|
|
4592
|
-
}
|
|
4593
|
-
});
|
|
4594
|
-
});
|
|
4595
|
-
child.on("close", (code) => {
|
|
4596
|
-
clearTimeout(timer);
|
|
4597
|
-
resolve15({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
|
|
4598
|
-
});
|
|
4599
|
-
});
|
|
4600
|
-
}
|
|
4601
|
-
|
|
4602
|
-
// src/installers/gitCloneWithSetup.ts
|
|
4603
4615
|
function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
4604
4616
|
return new Promise((resolve15) => {
|
|
4605
4617
|
const isWin = process.platform === "win32";
|
|
@@ -4669,6 +4681,9 @@ var installGitCloneWithSetup = async (ctx) => {
|
|
|
4669
4681
|
const e = pre.errors[0] ?? err(ctx, "/", "preflight failed (no detail)", "preflight");
|
|
4670
4682
|
return { ok: false, phase: "preflight", error: e };
|
|
4671
4683
|
}
|
|
4684
|
+
if (await isAlreadyInstalled(ctx)) {
|
|
4685
|
+
return { ok: true, alreadyInstalled: true, backupId: "noop-idempotent" };
|
|
4686
|
+
}
|
|
4672
4687
|
if (!/^[a-f0-9]{7,40}$/.test(install.git_ref)) {
|
|
4673
4688
|
return {
|
|
4674
4689
|
ok: false,
|
|
@@ -4786,6 +4801,7 @@ var installGitCloneWithSetup = async (ctx) => {
|
|
|
4786
4801
|
};
|
|
4787
4802
|
|
|
4788
4803
|
// src/installers/mcpHttpAdd.ts
|
|
4804
|
+
init_readClaudeConfig();
|
|
4789
4805
|
function resolveEnvVars(value) {
|
|
4790
4806
|
const pattern = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
|
|
4791
4807
|
let resolved = value;
|
|
@@ -4954,6 +4970,7 @@ ${newEntry}
|
|
|
4954
4970
|
};
|
|
4955
4971
|
|
|
4956
4972
|
// src/installers/mcpStdioAdd.ts
|
|
4973
|
+
init_readClaudeConfig();
|
|
4957
4974
|
var installMcpStdioAdd = async (ctx) => {
|
|
4958
4975
|
const install = ctx.manifest.spec.install;
|
|
4959
4976
|
if (install.method !== "mcp-stdio-add") {
|
|
@@ -5094,6 +5111,9 @@ var installNpmCli = async (ctx) => {
|
|
|
5094
5111
|
const e = pre.errors[0] ?? err(ctx, "/", "preflight failed (no detail)", "preflight");
|
|
5095
5112
|
return { ok: false, phase: "preflight", error: e };
|
|
5096
5113
|
}
|
|
5114
|
+
if (await isAlreadyInstalled(ctx)) {
|
|
5115
|
+
return { ok: true, alreadyInstalled: true, backupId: "noop-idempotent" };
|
|
5116
|
+
}
|
|
5097
5117
|
let level = detectLevel(install.cmd);
|
|
5098
5118
|
let cmd = install.cmd;
|
|
5099
5119
|
const plan = { files: [] };
|
|
@@ -5189,6 +5209,9 @@ var installNpxSkillInstaller = async (ctx) => {
|
|
|
5189
5209
|
const e = pre.errors[0] ?? err(ctx, "/", "preflight failed (no detail)", "preflight");
|
|
5190
5210
|
return { ok: false, phase: "preflight", error: e };
|
|
5191
5211
|
}
|
|
5212
|
+
if (await isAlreadyInstalled(ctx)) {
|
|
5213
|
+
return { ok: true, alreadyInstalled: true, backupId: "noop-idempotent" };
|
|
5214
|
+
}
|
|
5192
5215
|
if (!/\bskills@(?!latest\b)\S+/.test(install.cmd)) {
|
|
5193
5216
|
return {
|
|
5194
5217
|
ok: false,
|
|
@@ -5403,12 +5426,6 @@ ${t("install.manifest_not_found.fix", { name: resolvedName })}`
|
|
|
5403
5426
|
process.exit(1);
|
|
5404
5427
|
});
|
|
5405
5428
|
}
|
|
5406
|
-
var PHASE_21 = /* @__PURE__ */ new Set([
|
|
5407
|
-
"cc-plugin-marketplace",
|
|
5408
|
-
"git-clone-with-setup",
|
|
5409
|
-
"npx-skill-installer",
|
|
5410
|
-
"mcp-http-add"
|
|
5411
|
-
]);
|
|
5412
5429
|
async function listBaseManifests(cwd) {
|
|
5413
5430
|
const out = [];
|
|
5414
5431
|
for (const d of ["manifests/tools", "manifests/skill-packs"]) {
|
|
@@ -5451,11 +5468,6 @@ function registerInstallBase(program2) {
|
|
|
5451
5468
|
continue;
|
|
5452
5469
|
}
|
|
5453
5470
|
const name = v.manifest.metadata.name;
|
|
5454
|
-
const method = v.manifest.spec.install.method;
|
|
5455
|
-
if (PHASE_21.has(method)) {
|
|
5456
|
-
skipped.push({ name, reason: `deferred phase 2.1 (${method})` });
|
|
5457
|
-
continue;
|
|
5458
|
-
}
|
|
5459
5471
|
const r = await runInstall(v.manifest, opts);
|
|
5460
5472
|
if ("aborted" in r) skipped.push({ name, reason: `aborted: ${r.reason}` });
|
|
5461
5473
|
else if (r.ok && "alreadyInstalled" in r && r.alreadyInstalled) alreadyInstalled.push(name);
|
|
@@ -5464,7 +5476,7 @@ function registerInstallBase(program2) {
|
|
|
5464
5476
|
}
|
|
5465
5477
|
console.log(
|
|
5466
5478
|
`
|
|
5467
|
-
installed: ${installed.length} / already-installed: ${alreadyInstalled.length} / skipped (
|
|
5479
|
+
installed: ${installed.length} / already-installed: ${alreadyInstalled.length} / skipped (user-aborted): ${skipped.length} / failed: ${failed.length}`
|
|
5468
5480
|
);
|
|
5469
5481
|
for (const i of installed) console.log(` installed ${i}`);
|
|
5470
5482
|
for (const a of alreadyInstalled)
|
|
@@ -6054,12 +6066,6 @@ async function scanWorkflowsNested(workflowsDir, entries) {
|
|
|
6054
6066
|
}
|
|
6055
6067
|
|
|
6056
6068
|
// src/cli/lib/setup-helpers.ts
|
|
6057
|
-
var PHASE_212 = /* @__PURE__ */ new Set([
|
|
6058
|
-
"cc-plugin-marketplace",
|
|
6059
|
-
"git-clone-with-setup",
|
|
6060
|
-
"npx-skill-installer",
|
|
6061
|
-
"mcp-http-add"
|
|
6062
|
-
]);
|
|
6063
6069
|
async function warnIfAgentTeamsMissing() {
|
|
6064
6070
|
const r = await checkAgentTeams();
|
|
6065
6071
|
if (r.status !== "missing") return;
|
|
@@ -6075,14 +6081,15 @@ async function warnIfAgentTeamsMissing() {
|
|
|
6075
6081
|
async function scanWorkflowsWithSkill(workflowsDir, entries) {
|
|
6076
6082
|
return scanWorkflowsNested(workflowsDir, entries);
|
|
6077
6083
|
}
|
|
6078
|
-
async function runStepBInstall(manifestPaths) {
|
|
6084
|
+
async function runStepBInstall(manifestPaths, runOpts = {}) {
|
|
6079
6085
|
const opts = {
|
|
6080
6086
|
apply: true,
|
|
6081
6087
|
dryRun: false,
|
|
6082
6088
|
system: false,
|
|
6083
6089
|
nonInteractive: true,
|
|
6084
6090
|
fullDiff: false,
|
|
6085
|
-
color: "auto"
|
|
6091
|
+
color: "auto",
|
|
6092
|
+
updateInstalled: runOpts.updateInstalled === true
|
|
6086
6093
|
};
|
|
6087
6094
|
const start = Date.now();
|
|
6088
6095
|
const settled = await Promise.allSettled(
|
|
@@ -6102,8 +6109,6 @@ async function runStepBInstall(manifestPaths) {
|
|
|
6102
6109
|
};
|
|
6103
6110
|
}
|
|
6104
6111
|
const name = v.manifest.metadata.name;
|
|
6105
|
-
const method = v.manifest.spec.install.method;
|
|
6106
|
-
if (PHASE_212.has(method)) return { status: "skipped", name };
|
|
6107
6112
|
const r = await runInstall(v.manifest, opts);
|
|
6108
6113
|
if ("aborted" in r) return { status: "skipped", name };
|
|
6109
6114
|
if (r.ok && "alreadyInstalled" in r && r.alreadyInstalled)
|
|
@@ -6149,7 +6154,10 @@ function registerSetup(program2) {
|
|
|
6149
6154
|
).option("--dry-run", "preview only \u2014 do not write to disk (opt-in for advanced users)").option(
|
|
6150
6155
|
"--user-lang <code>",
|
|
6151
6156
|
"override detected OS locale for env.HARNESSED_USER_LANG (en | zh-Hans / zh-CN / zh-TW)"
|
|
6152
|
-
).option("--non-interactive", "skip all confirm prompts (CI / scripted setup)").option("--no-auto-install", "do not prompt to auto-install missing plugins (advisory only)").
|
|
6157
|
+
).option("--non-interactive", "skip all confirm prompts (CI / scripted setup)").option("--no-auto-install", "do not prompt to auto-install missing plugins (advisory only)").option(
|
|
6158
|
+
"--update-installed",
|
|
6159
|
+
"force re-install already-installed plugins (excludes MCP servers); default: skip if installed"
|
|
6160
|
+
).action(async (raw) => {
|
|
6153
6161
|
const dryRun = raw.dryRun === true;
|
|
6154
6162
|
const pkgRoot = getPackageRoot();
|
|
6155
6163
|
const workflowsDir = resolve(pkgRoot, "workflows");
|
|
@@ -6277,8 +6285,20 @@ function registerSetup(program2) {
|
|
|
6277
6285
|
} else {
|
|
6278
6286
|
console.warn(t("setup.step_d.skipped", { message: dResult.message }));
|
|
6279
6287
|
}
|
|
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
|
+
}
|
|
6280
6300
|
const manifestPaths = await listBaseManifests2(pkgRoot);
|
|
6281
|
-
const b = await runStepBInstall(manifestPaths);
|
|
6301
|
+
const b = await runStepBInstall(manifestPaths, { updateInstalled: updateInstalled2 });
|
|
6282
6302
|
const stepBMs = (b.elapsedMs / 1e3).toFixed(1);
|
|
6283
6303
|
console.log(
|
|
6284
6304
|
t("setup.step_b_complete", {
|