claudekit-cli 3.9.2 → 3.10.0

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.
Files changed (2) hide show
  1. package/dist/index.js +372 -129
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -12363,6 +12363,231 @@ var init_install_error_handler = __esm(() => {
12363
12363
  init_logger();
12364
12364
  });
12365
12365
 
12366
+ // src/services/package-installer/gemini-mcp-linker.ts
12367
+ var exports_gemini_mcp_linker = {};
12368
+ __export(exports_gemini_mcp_linker, {
12369
+ processGeminiMcpLinking: () => processGeminiMcpLinking,
12370
+ linkGeminiMcpConfig: () => linkGeminiMcpConfig,
12371
+ findMcpConfigPath: () => findMcpConfigPath,
12372
+ checkExistingGeminiConfig: () => checkExistingGeminiConfig,
12373
+ addGeminiToGitignore: () => addGeminiToGitignore
12374
+ });
12375
+ import { existsSync as existsSync7, lstatSync, readlinkSync } from "node:fs";
12376
+ import { mkdir as mkdir9, readFile as readFile14, symlink as symlink2, writeFile as writeFile12 } from "node:fs/promises";
12377
+ import { homedir as homedir4 } from "node:os";
12378
+ import { dirname as dirname6, join as join25, resolve as resolve3 } from "node:path";
12379
+ function getGlobalMcpConfigPath() {
12380
+ return join25(homedir4(), ".claude", ".mcp.json");
12381
+ }
12382
+ function getLocalMcpConfigPath(projectDir) {
12383
+ return join25(projectDir, ".mcp.json");
12384
+ }
12385
+ function findMcpConfigPath(projectDir) {
12386
+ const localPath = getLocalMcpConfigPath(projectDir);
12387
+ if (existsSync7(localPath)) {
12388
+ logger.debug(`Found local MCP config: ${localPath}`);
12389
+ return localPath;
12390
+ }
12391
+ const globalPath = getGlobalMcpConfigPath();
12392
+ if (existsSync7(globalPath)) {
12393
+ logger.debug(`Found global MCP config: ${globalPath}`);
12394
+ return globalPath;
12395
+ }
12396
+ logger.debug("No MCP config found (local or global)");
12397
+ return null;
12398
+ }
12399
+ function checkExistingGeminiConfig(projectDir) {
12400
+ const geminiSettingsPath = join25(projectDir, ".gemini", "settings.json");
12401
+ if (!existsSync7(geminiSettingsPath)) {
12402
+ return { exists: false, isSymlink: false };
12403
+ }
12404
+ try {
12405
+ const stats = lstatSync(geminiSettingsPath);
12406
+ if (stats.isSymbolicLink()) {
12407
+ const target = readlinkSync(geminiSettingsPath);
12408
+ return { exists: true, isSymlink: true, currentTarget: target };
12409
+ }
12410
+ return { exists: true, isSymlink: false };
12411
+ } catch {
12412
+ return { exists: true, isSymlink: false };
12413
+ }
12414
+ }
12415
+ async function readJsonFile(filePath) {
12416
+ try {
12417
+ const content = await readFile14(filePath, "utf-8");
12418
+ return JSON.parse(content);
12419
+ } catch (error) {
12420
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
12421
+ logger.debug(`Failed to read/parse JSON file ${filePath}: ${errorMessage}`);
12422
+ return null;
12423
+ }
12424
+ }
12425
+ async function createSymlink(targetPath, linkPath, projectDir) {
12426
+ const isWindows5 = process.platform === "win32";
12427
+ const linkDir = dirname6(linkPath);
12428
+ if (!existsSync7(linkDir)) {
12429
+ await mkdir9(linkDir, { recursive: true });
12430
+ logger.debug(`Created directory: ${linkDir}`);
12431
+ }
12432
+ const localMcpPath = join25(projectDir, ".mcp.json");
12433
+ const isLocalConfig = targetPath === localMcpPath;
12434
+ const symlinkTarget = isLocalConfig ? "../.mcp.json" : targetPath;
12435
+ try {
12436
+ await symlink2(symlinkTarget, linkPath, isWindows5 ? "file" : undefined);
12437
+ logger.debug(`Created symlink: ${linkPath} → ${symlinkTarget}`);
12438
+ return { success: true, method: "symlink", targetPath };
12439
+ } catch (error) {
12440
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
12441
+ return {
12442
+ success: false,
12443
+ method: "symlink",
12444
+ error: `Failed to create symlink: ${errorMessage}`
12445
+ };
12446
+ }
12447
+ }
12448
+ async function createNewSettingsWithMerge(geminiSettingsPath, mcpConfigPath) {
12449
+ const linkDir = dirname6(geminiSettingsPath);
12450
+ if (!existsSync7(linkDir)) {
12451
+ await mkdir9(linkDir, { recursive: true });
12452
+ logger.debug(`Created directory: ${linkDir}`);
12453
+ }
12454
+ const mcpConfig = await readJsonFile(mcpConfigPath);
12455
+ if (!mcpConfig) {
12456
+ return { success: false, method: "merge", error: "Failed to read MCP config" };
12457
+ }
12458
+ const mcpServers = mcpConfig.mcpServers;
12459
+ if (!mcpServers || typeof mcpServers !== "object" || Array.isArray(mcpServers)) {
12460
+ return { success: false, method: "merge", error: "MCP config has no valid mcpServers object" };
12461
+ }
12462
+ const newSettings = { mcpServers };
12463
+ try {
12464
+ await writeFile12(geminiSettingsPath, JSON.stringify(newSettings, null, 2), "utf-8");
12465
+ logger.debug(`Created new Gemini settings with mcpServers: ${geminiSettingsPath}`);
12466
+ return { success: true, method: "merge", targetPath: mcpConfigPath };
12467
+ } catch (error) {
12468
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
12469
+ return {
12470
+ success: false,
12471
+ method: "merge",
12472
+ error: `Failed to write settings: ${errorMessage}`
12473
+ };
12474
+ }
12475
+ }
12476
+ async function mergeGeminiSettings(geminiSettingsPath, mcpConfigPath) {
12477
+ const geminiSettings = await readJsonFile(geminiSettingsPath);
12478
+ if (!geminiSettings) {
12479
+ return { success: false, method: "merge", error: "Failed to read existing Gemini settings" };
12480
+ }
12481
+ const mcpConfig = await readJsonFile(mcpConfigPath);
12482
+ if (!mcpConfig) {
12483
+ return { success: false, method: "merge", error: "Failed to read MCP config" };
12484
+ }
12485
+ const mcpServers = mcpConfig.mcpServers;
12486
+ if (!mcpServers || typeof mcpServers !== "object" || Array.isArray(mcpServers)) {
12487
+ return { success: false, method: "merge", error: "MCP config has no valid mcpServers object" };
12488
+ }
12489
+ const mergedSettings = {
12490
+ ...geminiSettings,
12491
+ mcpServers
12492
+ };
12493
+ try {
12494
+ await writeFile12(geminiSettingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
12495
+ logger.debug(`Merged mcpServers into: ${geminiSettingsPath}`);
12496
+ return { success: true, method: "merge", targetPath: mcpConfigPath };
12497
+ } catch (error) {
12498
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
12499
+ return {
12500
+ success: false,
12501
+ method: "merge",
12502
+ error: `Failed to write merged settings: ${errorMessage}`
12503
+ };
12504
+ }
12505
+ }
12506
+ async function addGeminiToGitignore(projectDir) {
12507
+ const gitignorePath = join25(projectDir, ".gitignore");
12508
+ const geminiPattern = ".gemini/";
12509
+ try {
12510
+ let content = "";
12511
+ if (existsSync7(gitignorePath)) {
12512
+ content = await readFile14(gitignorePath, "utf-8");
12513
+ const lines = content.split(`
12514
+ `).map((line) => line.trim()).filter((line) => !line.startsWith("#"));
12515
+ const geminiPatterns = [".gemini/", ".gemini", "/.gemini/", "/.gemini"];
12516
+ if (lines.some((line) => geminiPatterns.includes(line))) {
12517
+ logger.debug(".gemini/ already in .gitignore");
12518
+ return;
12519
+ }
12520
+ }
12521
+ const newLine = content.endsWith(`
12522
+ `) || content === "" ? "" : `
12523
+ `;
12524
+ const comment = "# Gemini CLI settings (contains user-specific config)";
12525
+ await writeFile12(gitignorePath, `${content}${newLine}${comment}
12526
+ ${geminiPattern}
12527
+ `, "utf-8");
12528
+ logger.debug(`Added ${geminiPattern} to .gitignore`);
12529
+ } catch (error) {
12530
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
12531
+ logger.warning(`Failed to update .gitignore: ${errorMessage}`);
12532
+ }
12533
+ }
12534
+ async function linkGeminiMcpConfig(projectDir, options = {}) {
12535
+ const { skipGitignore = false } = options;
12536
+ const resolvedProjectDir = resolve3(projectDir);
12537
+ const geminiSettingsPath = join25(resolvedProjectDir, ".gemini", "settings.json");
12538
+ const mcpConfigPath = findMcpConfigPath(resolvedProjectDir);
12539
+ if (!mcpConfigPath) {
12540
+ return {
12541
+ success: false,
12542
+ method: "symlink",
12543
+ error: "No MCP config found. Create .mcp.json or ~/.claude/.mcp.json first."
12544
+ };
12545
+ }
12546
+ const existing = checkExistingGeminiConfig(resolvedProjectDir);
12547
+ let result;
12548
+ if (!existing.exists) {
12549
+ result = await createSymlink(mcpConfigPath, geminiSettingsPath, resolvedProjectDir);
12550
+ if (!result.success && process.platform === "win32") {
12551
+ logger.debug("Symlink failed on Windows, falling back to creating new settings with mcpServers");
12552
+ result = await createNewSettingsWithMerge(geminiSettingsPath, mcpConfigPath);
12553
+ }
12554
+ } else if (existing.isSymlink) {
12555
+ logger.debug(`Gemini config already symlinked: ${existing.currentTarget}`);
12556
+ result = { success: true, method: "skipped", targetPath: existing.currentTarget };
12557
+ } else {
12558
+ result = await mergeGeminiSettings(geminiSettingsPath, mcpConfigPath);
12559
+ }
12560
+ if (result.success && !skipGitignore) {
12561
+ await addGeminiToGitignore(resolvedProjectDir);
12562
+ }
12563
+ return result;
12564
+ }
12565
+ async function processGeminiMcpLinking(projectDir) {
12566
+ logger.info("Setting up Gemini CLI MCP integration...");
12567
+ const result = await linkGeminiMcpConfig(projectDir);
12568
+ if (result.success) {
12569
+ switch (result.method) {
12570
+ case "symlink":
12571
+ logger.success(`Gemini MCP linked: .gemini/settings.json → ${result.targetPath}`);
12572
+ logger.info("MCP servers will auto-sync with your Claude config.");
12573
+ break;
12574
+ case "merge":
12575
+ logger.success("Gemini MCP config updated (merged mcpServers, preserved your settings)");
12576
+ logger.info("Note: Run 'ck init' again to sync MCP config changes.");
12577
+ break;
12578
+ case "skipped":
12579
+ logger.info("Gemini MCP config already configured.");
12580
+ break;
12581
+ }
12582
+ } else {
12583
+ logger.warning(`Gemini MCP setup incomplete: ${result.error}`);
12584
+ logger.info("Manual setup: mkdir -p .gemini && ln -sf .claude/.mcp.json .gemini/settings.json");
12585
+ }
12586
+ }
12587
+ var init_gemini_mcp_linker = __esm(() => {
12588
+ init_logger();
12589
+ });
12590
+
12366
12591
  // src/services/package-installer/package-installer.ts
12367
12592
  var exports_package_installer = {};
12368
12593
  __export(exports_package_installer, {
@@ -12379,10 +12604,10 @@ __export(exports_package_installer, {
12379
12604
  getPackageVersion: () => getPackageVersion
12380
12605
  });
12381
12606
  import { exec as exec5, execFile as execFile2, spawn } from "node:child_process";
12382
- import { join as join25, resolve as resolve3 } from "node:path";
12607
+ import { join as join26, resolve as resolve4 } from "node:path";
12383
12608
  import { promisify as promisify5 } from "node:util";
12384
12609
  function executeInteractiveScript(command, args, options) {
12385
- return new Promise((resolve4, reject) => {
12610
+ return new Promise((resolve5, reject) => {
12386
12611
  const child = spawn(command, args, {
12387
12612
  stdio: ["ignore", "inherit", "inherit"],
12388
12613
  cwd: options?.cwd,
@@ -12403,7 +12628,7 @@ function executeInteractiveScript(command, args, options) {
12403
12628
  } else if (code2 !== 0) {
12404
12629
  reject(new Error(`Command exited with code ${code2}`));
12405
12630
  } else {
12406
- resolve4();
12631
+ resolve5();
12407
12632
  }
12408
12633
  });
12409
12634
  child.on("error", (error) => {
@@ -12574,7 +12799,7 @@ async function installOpenCode() {
12574
12799
  logger.info(`Installing ${displayName}...`);
12575
12800
  const { unlink: unlink5 } = await import("node:fs/promises");
12576
12801
  const { tmpdir: tmpdir3 } = await import("node:os");
12577
- const tempScriptPath = join25(tmpdir3(), "opencode-install.sh");
12802
+ const tempScriptPath = join26(tmpdir3(), "opencode-install.sh");
12578
12803
  try {
12579
12804
  logger.info("Downloading OpenCode installation script...");
12580
12805
  await execFileAsync("curl", ["-fsSL", "https://opencode.ai/install", "-o", tempScriptPath], {
@@ -12618,7 +12843,7 @@ async function installOpenCode() {
12618
12843
  async function installGemini() {
12619
12844
  return installPackageGlobally("@google/gemini-cli", "Google Gemini CLI");
12620
12845
  }
12621
- async function processPackageInstallations(shouldInstallOpenCode, shouldInstallGemini) {
12846
+ async function processPackageInstallations(shouldInstallOpenCode, shouldInstallGemini, projectDir) {
12622
12847
  const results = {};
12623
12848
  if (shouldInstallOpenCode) {
12624
12849
  const alreadyInstalled = await isOpenCodeInstalled();
@@ -12643,12 +12868,17 @@ async function processPackageInstallations(shouldInstallOpenCode, shouldInstallG
12643
12868
  } else {
12644
12869
  results.gemini = await installGemini();
12645
12870
  }
12871
+ const geminiAvailable = alreadyInstalled || results.gemini?.success;
12872
+ if (projectDir && geminiAvailable) {
12873
+ const { processGeminiMcpLinking: processGeminiMcpLinking2 } = await Promise.resolve().then(() => (init_gemini_mcp_linker(), exports_gemini_mcp_linker));
12874
+ await processGeminiMcpLinking2(projectDir);
12875
+ }
12646
12876
  }
12647
12877
  return results;
12648
12878
  }
12649
12879
  function validateScriptPath(skillsDir, scriptPath) {
12650
- const skillsDirResolved = resolve3(skillsDir);
12651
- const scriptPathResolved = resolve3(scriptPath);
12880
+ const skillsDirResolved = resolve4(skillsDir);
12881
+ const scriptPathResolved = resolve4(scriptPath);
12652
12882
  const isWindows5 = process.platform === "win32";
12653
12883
  const skillsDirNormalized = isWindows5 ? skillsDirResolved.toLowerCase() : skillsDirResolved;
12654
12884
  const scriptPathNormalized = isWindows5 ? scriptPathResolved.toLowerCase() : scriptPathResolved;
@@ -12684,11 +12914,11 @@ async function installSkillsDependencies(skillsDir) {
12684
12914
  };
12685
12915
  }
12686
12916
  try {
12687
- const { existsSync: existsSync7 } = await import("node:fs");
12917
+ const { existsSync: existsSync8 } = await import("node:fs");
12688
12918
  const clack = await Promise.resolve().then(() => (init_dist2(), exports_dist));
12689
12919
  const platform9 = process.platform;
12690
12920
  const scriptName = platform9 === "win32" ? "install.ps1" : "install.sh";
12691
- const scriptPath = join25(skillsDir, scriptName);
12921
+ const scriptPath = join26(skillsDir, scriptName);
12692
12922
  try {
12693
12923
  validateScriptPath(skillsDir, scriptPath);
12694
12924
  } catch (error) {
@@ -12700,11 +12930,11 @@ async function installSkillsDependencies(skillsDir) {
12700
12930
  error: `Path validation failed: ${errorMessage}`
12701
12931
  };
12702
12932
  }
12703
- if (!existsSync7(scriptPath)) {
12933
+ if (!existsSync8(scriptPath)) {
12704
12934
  logger.warning(`Skills installation script not found: ${scriptPath}`);
12705
12935
  logger.info("");
12706
12936
  logger.info("\uD83D\uDCD6 Manual Installation Instructions:");
12707
- logger.info(` See: ${join25(skillsDir, "INSTALLATION.md")}`);
12937
+ logger.info(` See: ${join26(skillsDir, "INSTALLATION.md")}`);
12708
12938
  logger.info("");
12709
12939
  logger.info("Quick start:");
12710
12940
  logger.info(" cd .claude/skills/ai-multimodal/scripts");
@@ -12720,8 +12950,8 @@ async function installSkillsDependencies(skillsDir) {
12720
12950
  logger.info(` Platform: ${platform9 === "win32" ? "Windows (PowerShell)" : "Unix (bash)"}`);
12721
12951
  if (logger.isVerbose()) {
12722
12952
  try {
12723
- const { readFile: readFile14 } = await import("node:fs/promises");
12724
- const scriptContent = await readFile14(scriptPath, "utf-8");
12953
+ const { readFile: readFile15 } = await import("node:fs/promises");
12954
+ const scriptContent = await readFile15(scriptPath, "utf-8");
12725
12955
  const previewLines = scriptContent.split(`
12726
12956
  `).slice(0, 20);
12727
12957
  logger.verbose("Script preview (first 20 lines):");
@@ -12747,7 +12977,7 @@ async function installSkillsDependencies(skillsDir) {
12747
12977
  logger.info(` ${platform9 === "win32" ? `powershell -File "${scriptPath}"` : `bash ${scriptPath}`}`);
12748
12978
  logger.info("");
12749
12979
  logger.info("Or see complete guide:");
12750
- logger.info(` ${join25(skillsDir, "INSTALLATION.md")}`);
12980
+ logger.info(` ${join26(skillsDir, "INSTALLATION.md")}`);
12751
12981
  return {
12752
12982
  success: false,
12753
12983
  package: displayName,
@@ -12848,7 +13078,7 @@ async function installSkillsDependencies(skillsDir) {
12848
13078
  logger.info("\uD83D\uDCD6 Manual Installation Instructions:");
12849
13079
  logger.info("");
12850
13080
  logger.info("See complete guide:");
12851
- logger.info(` cat ${join25(skillsDir, "INSTALLATION.md")}`);
13081
+ logger.info(` cat ${join26(skillsDir, "INSTALLATION.md")}`);
12852
13082
  logger.info("");
12853
13083
  logger.info("Quick start:");
12854
13084
  logger.info(" cd .claude/skills/ai-multimodal/scripts");
@@ -13524,7 +13754,7 @@ function getPagerArgs(pagerCmd) {
13524
13754
  return [];
13525
13755
  }
13526
13756
  async function trySystemPager(content) {
13527
- return new Promise((resolve7) => {
13757
+ return new Promise((resolve8) => {
13528
13758
  const pagerCmd = process.env.PAGER || "less";
13529
13759
  const pagerArgs = getPagerArgs(pagerCmd);
13530
13760
  try {
@@ -13534,20 +13764,20 @@ async function trySystemPager(content) {
13534
13764
  });
13535
13765
  const timeout = setTimeout(() => {
13536
13766
  pager.kill();
13537
- resolve7(false);
13767
+ resolve8(false);
13538
13768
  }, 30000);
13539
13769
  pager.stdin.write(content);
13540
13770
  pager.stdin.end();
13541
13771
  pager.on("close", (code2) => {
13542
13772
  clearTimeout(timeout);
13543
- resolve7(code2 === 0);
13773
+ resolve8(code2 === 0);
13544
13774
  });
13545
13775
  pager.on("error", () => {
13546
13776
  clearTimeout(timeout);
13547
- resolve7(false);
13777
+ resolve8(false);
13548
13778
  });
13549
13779
  } catch {
13550
- resolve7(false);
13780
+ resolve8(false);
13551
13781
  }
13552
13782
  });
13553
13783
  }
@@ -13574,16 +13804,16 @@ async function basicPager(content) {
13574
13804
  break;
13575
13805
  }
13576
13806
  const remaining = lines.length - currentLine;
13577
- await new Promise((resolve7) => {
13807
+ await new Promise((resolve8) => {
13578
13808
  rl.question(`-- More (${remaining} lines) [Enter/q] --`, (answer) => {
13579
13809
  if (answer.toLowerCase() === "q") {
13580
13810
  rl.close();
13581
13811
  process.exitCode = 0;
13582
- resolve7();
13812
+ resolve8();
13583
13813
  return;
13584
13814
  }
13585
13815
  process.stdout.write("\x1B[1A\x1B[2K");
13586
- resolve7();
13816
+ resolve8();
13587
13817
  });
13588
13818
  });
13589
13819
  }
@@ -13841,8 +14071,8 @@ var init_help_interceptor = __esm(() => {
13841
14071
  });
13842
14072
 
13843
14073
  // src/index.ts
13844
- import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
13845
- import { join as join35 } from "path";
14074
+ import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
14075
+ import { join as join36 } from "path";
13846
14076
 
13847
14077
  // node_modules/cac/dist/index.mjs
13848
14078
  import { EventEmitter } from "events";
@@ -14447,7 +14677,7 @@ var cac = (name = "") => new CAC(name);
14447
14677
  // package.json
14448
14678
  var package_default = {
14449
14679
  name: "claudekit-cli",
14450
- version: "3.9.2",
14680
+ version: "3.10.0",
14451
14681
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
14452
14682
  type: "module",
14453
14683
  repository: {
@@ -18291,7 +18521,7 @@ async function doctorCommand(options = {}) {
18291
18521
  }
18292
18522
 
18293
18523
  // src/commands/init.ts
18294
- import { join as join30, resolve as resolve5 } from "node:path";
18524
+ import { join as join31, resolve as resolve6 } from "node:path";
18295
18525
 
18296
18526
  // src/domains/config/config-manager.ts
18297
18527
  init_logger();
@@ -31577,7 +31807,7 @@ class PromptsManager {
31577
31807
 
31578
31808
  // src/services/file-operations/file-scanner.ts
31579
31809
  init_logger();
31580
- import { join as join26, relative as relative7, resolve as resolve4 } from "node:path";
31810
+ import { join as join27, relative as relative7, resolve as resolve5 } from "node:path";
31581
31811
  var import_fs_extra15 = __toESM(require_lib(), 1);
31582
31812
 
31583
31813
  class FileScanner {
@@ -31594,7 +31824,7 @@ class FileScanner {
31594
31824
  logger.debug(`Skipping directory: ${entry}`);
31595
31825
  continue;
31596
31826
  }
31597
- const fullPath = join26(dirPath, entry);
31827
+ const fullPath = join27(dirPath, entry);
31598
31828
  if (!FileScanner.isSafePath(basePath, fullPath)) {
31599
31829
  logger.warning(`Skipping potentially unsafe path: ${entry}`);
31600
31830
  continue;
@@ -31629,8 +31859,8 @@ class FileScanner {
31629
31859
  return files;
31630
31860
  }
31631
31861
  static async findCustomFiles(destDir, sourceDir, subPath) {
31632
- const destSubDir = join26(destDir, subPath);
31633
- const sourceSubDir = join26(sourceDir, subPath);
31862
+ const destSubDir = join27(destDir, subPath);
31863
+ const sourceSubDir = join27(sourceDir, subPath);
31634
31864
  logger.debug(`findCustomFiles - destDir: ${destDir}`);
31635
31865
  logger.debug(`findCustomFiles - sourceDir: ${sourceDir}`);
31636
31866
  logger.debug(`findCustomFiles - subPath: "${subPath}"`);
@@ -31658,8 +31888,8 @@ class FileScanner {
31658
31888
  return customFiles;
31659
31889
  }
31660
31890
  static isSafePath(basePath, targetPath) {
31661
- const resolvedBase = resolve4(basePath);
31662
- const resolvedTarget = resolve4(targetPath);
31891
+ const resolvedBase = resolve5(basePath);
31892
+ const resolvedTarget = resolve5(targetPath);
31663
31893
  return resolvedTarget.startsWith(resolvedBase);
31664
31894
  }
31665
31895
  static toPosixPath(path9) {
@@ -31668,8 +31898,8 @@ class FileScanner {
31668
31898
  }
31669
31899
 
31670
31900
  // src/services/transformers/commands-prefix.ts
31671
- import { lstat as lstat3, mkdir as mkdir9, readdir as readdir11, stat as stat4 } from "node:fs/promises";
31672
- import { join as join27 } from "node:path";
31901
+ import { lstat as lstat3, mkdir as mkdir10, readdir as readdir11, stat as stat4 } from "node:fs/promises";
31902
+ import { join as join28 } from "node:path";
31673
31903
  init_logger();
31674
31904
  var import_fs_extra16 = __toESM(require_lib(), 1);
31675
31905
  function stripWindowsDrivePrefix(path9) {
@@ -31710,14 +31940,14 @@ function validatePath4(path9, paramName) {
31710
31940
  class CommandsPrefix {
31711
31941
  static async applyPrefix(extractDir) {
31712
31942
  validatePath4(extractDir, "extractDir");
31713
- const commandsDir = join27(extractDir, ".claude", "commands");
31943
+ const commandsDir = join28(extractDir, ".claude", "commands");
31714
31944
  if (!await import_fs_extra16.pathExists(commandsDir)) {
31715
31945
  logger.verbose("No commands directory found, skipping prefix application");
31716
31946
  return;
31717
31947
  }
31718
31948
  logger.info("Applying /ck: prefix to slash commands...");
31719
- const backupDir = join27(extractDir, ".commands-backup");
31720
- const tempDir = join27(extractDir, ".commands-prefix-temp");
31949
+ const backupDir = join28(extractDir, ".commands-backup");
31950
+ const tempDir = join28(extractDir, ".commands-prefix-temp");
31721
31951
  try {
31722
31952
  const entries = await readdir11(commandsDir);
31723
31953
  if (entries.length === 0) {
@@ -31725,7 +31955,7 @@ class CommandsPrefix {
31725
31955
  return;
31726
31956
  }
31727
31957
  if (entries.length === 1 && entries[0] === "ck") {
31728
- const ckDir2 = join27(commandsDir, "ck");
31958
+ const ckDir2 = join28(commandsDir, "ck");
31729
31959
  const ckStat = await stat4(ckDir2);
31730
31960
  if (ckStat.isDirectory()) {
31731
31961
  logger.verbose("Commands already have /ck: prefix, skipping");
@@ -31734,18 +31964,18 @@ class CommandsPrefix {
31734
31964
  }
31735
31965
  await import_fs_extra16.copy(commandsDir, backupDir);
31736
31966
  logger.verbose("Created backup of commands directory");
31737
- await mkdir9(tempDir, { recursive: true });
31738
- const ckDir = join27(tempDir, "ck");
31739
- await mkdir9(ckDir, { recursive: true });
31967
+ await mkdir10(tempDir, { recursive: true });
31968
+ const ckDir = join28(tempDir, "ck");
31969
+ await mkdir10(ckDir, { recursive: true });
31740
31970
  let processedCount = 0;
31741
31971
  for (const entry of entries) {
31742
- const sourcePath = join27(commandsDir, entry);
31972
+ const sourcePath = join28(commandsDir, entry);
31743
31973
  const stats = await lstat3(sourcePath);
31744
31974
  if (stats.isSymbolicLink()) {
31745
31975
  logger.warning(`Skipping symlink for security: ${entry}`);
31746
31976
  continue;
31747
31977
  }
31748
- const destPath = join27(ckDir, entry);
31978
+ const destPath = join28(ckDir, entry);
31749
31979
  await import_fs_extra16.copy(sourcePath, destPath, {
31750
31980
  overwrite: false,
31751
31981
  errorOnExist: true
@@ -31793,8 +32023,8 @@ class CommandsPrefix {
31793
32023
  static async cleanupCommandsDirectory(targetDir, isGlobal, options = {}) {
31794
32024
  const { dryRun = false, forceOverwrite = false } = options;
31795
32025
  validatePath4(targetDir, "targetDir");
31796
- const claudeDir = isGlobal ? targetDir : join27(targetDir, ".claude");
31797
- const commandsDir = join27(claudeDir, "commands");
32026
+ const claudeDir = isGlobal ? targetDir : join28(targetDir, ".claude");
32027
+ const commandsDir = join28(claudeDir, "commands");
31798
32028
  const result = {
31799
32029
  results: [],
31800
32030
  deletedCount: 0,
@@ -31822,7 +32052,7 @@ class CommandsPrefix {
31822
32052
  return result;
31823
32053
  }
31824
32054
  for (const entry of entries) {
31825
- const entryPath = join27(commandsDir, entry);
32055
+ const entryPath = join28(commandsDir, entry);
31826
32056
  const stats = await lstat3(entryPath);
31827
32057
  if (stats.isSymbolicLink()) {
31828
32058
  logger.warning(`Skipping symlink: ${entry}`);
@@ -31988,7 +32218,7 @@ class CommandsPrefix {
31988
32218
  const files = [];
31989
32219
  const entries = await readdir11(dir);
31990
32220
  for (const entry of entries) {
31991
- const fullPath = join27(dir, entry);
32221
+ const fullPath = join28(dir, entry);
31992
32222
  const stats = await lstat3(fullPath);
31993
32223
  if (stats.isSymbolicLink()) {
31994
32224
  continue;
@@ -32007,8 +32237,8 @@ class CommandsPrefix {
32007
32237
  init_logger();
32008
32238
  init_types2();
32009
32239
  var import_fs_extra17 = __toESM(require_lib(), 1);
32010
- import { readFile as readFile14, readdir as readdir12, rename as rename3, writeFile as writeFile12 } from "node:fs/promises";
32011
- import { join as join28, relative as relative8 } from "node:path";
32240
+ import { readFile as readFile15, readdir as readdir12, rename as rename3, writeFile as writeFile13 } from "node:fs/promises";
32241
+ import { join as join29, relative as relative8 } from "node:path";
32012
32242
  var TRANSFORMABLE_FILE_PATTERNS = [
32013
32243
  ".md",
32014
32244
  ".txt",
@@ -32054,34 +32284,34 @@ async function transformFolderPaths(extractDir, folders, options = {}) {
32054
32284
  }
32055
32285
  const dirsToRename = [];
32056
32286
  if (folders.docs !== DEFAULT_FOLDERS.docs) {
32057
- const docsPath = join28(extractDir, DEFAULT_FOLDERS.docs);
32287
+ const docsPath = join29(extractDir, DEFAULT_FOLDERS.docs);
32058
32288
  if (await import_fs_extra17.pathExists(docsPath)) {
32059
32289
  dirsToRename.push({
32060
32290
  from: docsPath,
32061
- to: join28(extractDir, folders.docs)
32291
+ to: join29(extractDir, folders.docs)
32062
32292
  });
32063
32293
  }
32064
- const claudeDocsPath = join28(extractDir, ".claude", DEFAULT_FOLDERS.docs);
32294
+ const claudeDocsPath = join29(extractDir, ".claude", DEFAULT_FOLDERS.docs);
32065
32295
  if (await import_fs_extra17.pathExists(claudeDocsPath)) {
32066
32296
  dirsToRename.push({
32067
32297
  from: claudeDocsPath,
32068
- to: join28(extractDir, ".claude", folders.docs)
32298
+ to: join29(extractDir, ".claude", folders.docs)
32069
32299
  });
32070
32300
  }
32071
32301
  }
32072
32302
  if (folders.plans !== DEFAULT_FOLDERS.plans) {
32073
- const plansPath = join28(extractDir, DEFAULT_FOLDERS.plans);
32303
+ const plansPath = join29(extractDir, DEFAULT_FOLDERS.plans);
32074
32304
  if (await import_fs_extra17.pathExists(plansPath)) {
32075
32305
  dirsToRename.push({
32076
32306
  from: plansPath,
32077
- to: join28(extractDir, folders.plans)
32307
+ to: join29(extractDir, folders.plans)
32078
32308
  });
32079
32309
  }
32080
- const claudePlansPath = join28(extractDir, ".claude", DEFAULT_FOLDERS.plans);
32310
+ const claudePlansPath = join29(extractDir, ".claude", DEFAULT_FOLDERS.plans);
32081
32311
  if (await import_fs_extra17.pathExists(claudePlansPath)) {
32082
32312
  dirsToRename.push({
32083
32313
  from: claudePlansPath,
32084
- to: join28(extractDir, ".claude", folders.plans)
32314
+ to: join29(extractDir, ".claude", folders.plans)
32085
32315
  });
32086
32316
  }
32087
32317
  }
@@ -32118,7 +32348,7 @@ async function transformFileContents(dir, compiledReplacements, options) {
32118
32348
  let replacementsCount = 0;
32119
32349
  const entries = await readdir12(dir, { withFileTypes: true });
32120
32350
  for (const entry of entries) {
32121
- const fullPath = join28(dir, entry.name);
32351
+ const fullPath = join29(dir, entry.name);
32122
32352
  if (entry.isDirectory()) {
32123
32353
  if (entry.name === "node_modules" || entry.name === ".git") {
32124
32354
  continue;
@@ -32131,7 +32361,7 @@ async function transformFileContents(dir, compiledReplacements, options) {
32131
32361
  if (!shouldTransform)
32132
32362
  continue;
32133
32363
  try {
32134
- const content = await readFile14(fullPath, "utf-8");
32364
+ const content = await readFile15(fullPath, "utf-8");
32135
32365
  let newContent = content;
32136
32366
  let changeCount = 0;
32137
32367
  for (const { regex: regex2, replacement } of compiledReplacements) {
@@ -32147,7 +32377,7 @@ async function transformFileContents(dir, compiledReplacements, options) {
32147
32377
  if (options.dryRun) {
32148
32378
  logger.debug(`[dry-run] Would update ${relative8(dir, fullPath)}: ${changeCount} replacement(s)`);
32149
32379
  } else {
32150
- await writeFile12(fullPath, newContent, "utf-8");
32380
+ await writeFile13(fullPath, newContent, "utf-8");
32151
32381
  logger.debug(`Updated ${relative8(dir, fullPath)}: ${changeCount} replacement(s)`);
32152
32382
  }
32153
32383
  filesChanged++;
@@ -32227,9 +32457,9 @@ function validateFolderName(name2) {
32227
32457
 
32228
32458
  // src/services/transformers/global-path-transformer.ts
32229
32459
  init_logger();
32230
- import { readFile as readFile15, readdir as readdir13, writeFile as writeFile13 } from "node:fs/promises";
32460
+ import { readFile as readFile16, readdir as readdir13, writeFile as writeFile14 } from "node:fs/promises";
32231
32461
  import { platform as platform9 } from "node:os";
32232
- import { extname, join as join29 } from "node:path";
32462
+ import { extname, join as join30 } from "node:path";
32233
32463
  var IS_WINDOWS2 = platform9() === "win32";
32234
32464
  var HOME_PREFIX = IS_WINDOWS2 ? "%USERPROFILE%" : "$HOME";
32235
32465
  function getHomeDirPrefix() {
@@ -32321,7 +32551,7 @@ async function transformPathsForGlobalInstall(directory, options = {}) {
32321
32551
  async function processDirectory(dir) {
32322
32552
  const entries = await readdir13(dir, { withFileTypes: true });
32323
32553
  for (const entry of entries) {
32324
- const fullPath = join29(dir, entry.name);
32554
+ const fullPath = join30(dir, entry.name);
32325
32555
  if (entry.isDirectory()) {
32326
32556
  if (entry.name === "node_modules" || entry.name.startsWith(".") && entry.name !== ".claude") {
32327
32557
  continue;
@@ -32329,10 +32559,10 @@ async function transformPathsForGlobalInstall(directory, options = {}) {
32329
32559
  await processDirectory(fullPath);
32330
32560
  } else if (entry.isFile() && shouldTransformFile(entry.name)) {
32331
32561
  try {
32332
- const content = await readFile15(fullPath, "utf-8");
32562
+ const content = await readFile16(fullPath, "utf-8");
32333
32563
  const { transformed, changes } = transformContent(content);
32334
32564
  if (changes > 0) {
32335
- await writeFile13(fullPath, transformed, "utf-8");
32565
+ await writeFile14(fullPath, transformed, "utf-8");
32336
32566
  filesTransformed++;
32337
32567
  totalChanges += changes;
32338
32568
  if (options.verbose) {
@@ -32380,9 +32610,9 @@ async function initCommand(options) {
32380
32610
  }
32381
32611
  if (validOptions.global) {
32382
32612
  const globalKitDir = PathResolver.getGlobalKitDir();
32383
- const cwdResolved = resolve5(process.cwd());
32384
- const isInGlobalDir = cwdResolved === globalKitDir || cwdResolved === resolve5(globalKitDir, "..");
32385
- const localSettingsPath = join30(process.cwd(), ".claude", "settings.json");
32613
+ const cwdResolved = resolve6(process.cwd());
32614
+ const isInGlobalDir = cwdResolved === globalKitDir || cwdResolved === resolve6(globalKitDir, "..");
32615
+ const localSettingsPath = join31(process.cwd(), ".claude", "settings.json");
32386
32616
  if (!isInGlobalDir && await import_fs_extra18.pathExists(localSettingsPath)) {
32387
32617
  if (isNonInteractive2) {
32388
32618
  logger.warning("Local .claude/settings.json detected. Local settings take precedence over global.");
@@ -32394,7 +32624,7 @@ async function initCommand(options) {
32394
32624
  return;
32395
32625
  }
32396
32626
  if (choice === "remove") {
32397
- const localClaudeDir = join30(process.cwd(), ".claude");
32627
+ const localClaudeDir = join31(process.cwd(), ".claude");
32398
32628
  try {
32399
32629
  await import_fs_extra18.remove(localClaudeDir);
32400
32630
  logger.success("Removed local .claude/ directory");
@@ -32438,12 +32668,12 @@ async function initCommand(options) {
32438
32668
  }
32439
32669
  }
32440
32670
  }
32441
- const resolvedDir = resolve5(targetDir);
32671
+ const resolvedDir = resolve6(targetDir);
32442
32672
  logger.info(`Target directory: ${resolvedDir}`);
32443
32673
  if (!await import_fs_extra18.pathExists(resolvedDir)) {
32444
32674
  if (validOptions.global) {
32445
- const { mkdir: mkdir10 } = await import("node:fs/promises");
32446
- await mkdir10(resolvedDir, { recursive: true });
32675
+ const { mkdir: mkdir11 } = await import("node:fs/promises");
32676
+ await mkdir11(resolvedDir, { recursive: true });
32447
32677
  logger.info(`Created global directory: ${resolvedDir}`);
32448
32678
  } else {
32449
32679
  logger.error(`Directory does not exist: ${resolvedDir}`);
@@ -32453,7 +32683,7 @@ async function initCommand(options) {
32453
32683
  }
32454
32684
  if (validOptions.fresh) {
32455
32685
  const prefix = PathResolver.getPathPrefix(validOptions.global);
32456
- const claudeDir2 = prefix ? join30(resolvedDir, prefix) : resolvedDir;
32686
+ const claudeDir2 = prefix ? join31(resolvedDir, prefix) : resolvedDir;
32457
32687
  const canProceed = await handleFreshInstallation(claudeDir2, prompts);
32458
32688
  if (!canProceed) {
32459
32689
  return;
@@ -32481,7 +32711,7 @@ async function initCommand(options) {
32481
32711
  logger.info("Fetching available versions...");
32482
32712
  let currentVersion = null;
32483
32713
  try {
32484
- const metadataPath = validOptions.global ? join30(PathResolver.getGlobalKitDir(), "metadata.json") : join30(resolvedDir, ".claude", "metadata.json");
32714
+ const metadataPath = validOptions.global ? join31(PathResolver.getGlobalKitDir(), "metadata.json") : join31(resolvedDir, ".claude", "metadata.json");
32485
32715
  const metadata = await readClaudeKitMetadata(metadataPath);
32486
32716
  currentVersion = metadata?.version || null;
32487
32717
  if (currentVersion) {
@@ -32606,7 +32836,7 @@ async function initCommand(options) {
32606
32836
  }
32607
32837
  }
32608
32838
  if (!validOptions.fresh) {
32609
- const newSkillsDir = join30(extractDir, ".claude", "skills");
32839
+ const newSkillsDir = join31(extractDir, ".claude", "skills");
32610
32840
  const currentSkillsDir = PathResolver.buildSkillsPath(resolvedDir, validOptions.global);
32611
32841
  if (await import_fs_extra18.pathExists(newSkillsDir) && await import_fs_extra18.pathExists(currentSkillsDir)) {
32612
32842
  logger.info("Checking for skills directory migration...");
@@ -32631,7 +32861,7 @@ async function initCommand(options) {
32631
32861
  let customClaudeFiles = [];
32632
32862
  if (!validOptions.fresh) {
32633
32863
  logger.info("Scanning for custom .claude files...");
32634
- const scanSourceDir = validOptions.global ? join30(extractDir, ".claude") : extractDir;
32864
+ const scanSourceDir = validOptions.global ? join31(extractDir, ".claude") : extractDir;
32635
32865
  const scanTargetSubdir = validOptions.global ? "" : ".claude";
32636
32866
  customClaudeFiles = await FileScanner.findCustomFiles(resolvedDir, scanSourceDir, scanTargetSubdir);
32637
32867
  } else {
@@ -32666,7 +32896,7 @@ async function initCommand(options) {
32666
32896
  }
32667
32897
  merger.setGlobalFlag(validOptions.global);
32668
32898
  merger.setForceOverwriteSettings(validOptions.forceOverwriteSettings);
32669
- const claudeDir = validOptions.global ? resolvedDir : join30(resolvedDir, ".claude");
32899
+ const claudeDir = validOptions.global ? resolvedDir : join31(resolvedDir, ".claude");
32670
32900
  const releaseManifest = await ReleaseManifestLoader.load(extractDir);
32671
32901
  if (!validOptions.fresh && await import_fs_extra18.pathExists(claudeDir)) {
32672
32902
  const legacyDetection = await LegacyMigration.detectLegacy(claudeDir);
@@ -32688,7 +32918,7 @@ async function initCommand(options) {
32688
32918
  return;
32689
32919
  }
32690
32920
  }
32691
- const sourceDir = validOptions.global ? join30(extractDir, ".claude") : extractDir;
32921
+ const sourceDir = validOptions.global ? join31(extractDir, ".claude") : extractDir;
32692
32922
  await merger.merge(sourceDir, resolvedDir, false);
32693
32923
  const manifestWriter = new ManifestWriter;
32694
32924
  const installedFiles = merger.getAllInstalledFiles();
@@ -32697,7 +32927,7 @@ async function initCommand(options) {
32697
32927
  if (!validOptions.global && !installedPath.startsWith(".claude/"))
32698
32928
  continue;
32699
32929
  const relativePath = validOptions.global ? installedPath : installedPath.replace(/^\.claude\//, "");
32700
- const filePath = join30(claudeDir, relativePath);
32930
+ const filePath = join31(claudeDir, relativePath);
32701
32931
  const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
32702
32932
  const ownership = manifestEntry ? "ck" : "user";
32703
32933
  filesToTrack.push({
@@ -32718,8 +32948,8 @@ async function initCommand(options) {
32718
32948
  trackingSpinner.succeed(`Tracked ${trackResult.success} files`);
32719
32949
  await manifestWriter.writeManifest(claudeDir, kitConfig.name, release.tag_name, validOptions.global ? "global" : "local");
32720
32950
  if (validOptions.global) {
32721
- const claudeMdSource = join30(extractDir, "CLAUDE.md");
32722
- const claudeMdDest = join30(resolvedDir, "CLAUDE.md");
32951
+ const claudeMdSource = join31(extractDir, "CLAUDE.md");
32952
+ const claudeMdDest = join31(resolvedDir, "CLAUDE.md");
32723
32953
  if (await import_fs_extra18.pathExists(claudeMdSource)) {
32724
32954
  if (!await import_fs_extra18.pathExists(claudeMdDest)) {
32725
32955
  await import_fs_extra18.copy(claudeMdSource, claudeMdDest);
@@ -32738,8 +32968,21 @@ async function initCommand(options) {
32738
32968
  const skillsDir = PathResolver.buildSkillsPath(resolvedDir, validOptions.global);
32739
32969
  await handleSkillsInstallation2(skillsDir);
32740
32970
  }
32971
+ if (!isNonInteractive2) {
32972
+ const { isGeminiInstalled: isGeminiInstalled2 } = await Promise.resolve().then(() => (init_package_installer(), exports_package_installer));
32973
+ const { checkExistingGeminiConfig: checkExistingGeminiConfig2, findMcpConfigPath: findMcpConfigPath2, processGeminiMcpLinking: processGeminiMcpLinking2 } = await Promise.resolve().then(() => (init_gemini_mcp_linker(), exports_gemini_mcp_linker));
32974
+ const geminiInstalled = await isGeminiInstalled2();
32975
+ const existingConfig = checkExistingGeminiConfig2(resolvedDir);
32976
+ const mcpConfigExists = findMcpConfigPath2(resolvedDir) !== null;
32977
+ if (geminiInstalled && !existingConfig.exists && mcpConfigExists) {
32978
+ const shouldSetupGemini = await prompts.confirm("Gemini CLI detected. Set up MCP integration? (creates .gemini/settings.json symlink)");
32979
+ if (shouldSetupGemini) {
32980
+ await processGeminiMcpLinking2(resolvedDir);
32981
+ }
32982
+ }
32983
+ }
32741
32984
  if (!validOptions.skipSetup && !isNonInteractive2) {
32742
- const envPath = join30(claudeDir, ".env");
32985
+ const envPath = join31(claudeDir, ".env");
32743
32986
  if (!await import_fs_extra18.pathExists(envPath)) {
32744
32987
  const shouldSetup = await prompts.confirm("Set up API keys now? (Gemini API key for ai-multimodal skill, optional webhooks)");
32745
32988
  if (shouldSetup) {
@@ -32770,7 +33013,7 @@ Protected files (.env, etc.) were not modified.`;
32770
33013
  }
32771
33014
 
32772
33015
  // src/commands/new.ts
32773
- import { join as join31, resolve as resolve6 } from "node:path";
33016
+ import { join as join32, resolve as resolve7 } from "node:path";
32774
33017
  init_package_installer();
32775
33018
  init_environment();
32776
33019
  init_logger();
@@ -32801,7 +33044,7 @@ async function newCommand(options) {
32801
33044
  targetDir = await prompts.getDirectory(targetDir);
32802
33045
  }
32803
33046
  }
32804
- const resolvedDir = resolve6(targetDir);
33047
+ const resolvedDir = resolve7(targetDir);
32805
33048
  logger.info(`Target directory: ${resolvedDir}`);
32806
33049
  if (await import_fs_extra19.pathExists(resolvedDir)) {
32807
33050
  const files = await import_fs_extra19.readdir(resolvedDir);
@@ -32951,7 +33194,7 @@ async function newCommand(options) {
32951
33194
  await CommandsPrefix.cleanupCommandsDirectory(resolvedDir, false);
32952
33195
  }
32953
33196
  await merger.merge(extractDir, resolvedDir, true);
32954
- const claudeDir = join31(resolvedDir, ".claude");
33197
+ const claudeDir = join32(resolvedDir, ".claude");
32955
33198
  const manifestWriter = new ManifestWriter;
32956
33199
  const releaseManifest = await ReleaseManifestLoader.load(extractDir);
32957
33200
  const installedFiles = merger.getAllInstalledFiles();
@@ -32960,7 +33203,7 @@ async function newCommand(options) {
32960
33203
  if (!installedPath.startsWith(".claude/"))
32961
33204
  continue;
32962
33205
  const relativePath = installedPath.replace(/^\.claude\//, "");
32963
- const filePath = join31(claudeDir, relativePath);
33206
+ const filePath = join32(claudeDir, relativePath);
32964
33207
  const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
32965
33208
  const ownership = manifestEntry ? "ck" : "user";
32966
33209
  filesToTrack.push({
@@ -32992,7 +33235,7 @@ async function newCommand(options) {
32992
33235
  if (installOpenCode2 || installGemini2) {
32993
33236
  logger.info("Installing optional packages...");
32994
33237
  try {
32995
- const installationResults = await processPackageInstallations(installOpenCode2, installGemini2);
33238
+ const installationResults = await processPackageInstallations(installOpenCode2, installGemini2, resolvedDir);
32996
33239
  prompts.showPackageInstallationResults(installationResults);
32997
33240
  } catch (error) {
32998
33241
  logger.warning(`Package installation failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -33013,7 +33256,7 @@ async function newCommand(options) {
33013
33256
 
33014
33257
  // src/commands/uninstall.ts
33015
33258
  import { readdirSync, rmSync } from "node:fs";
33016
- import { dirname as dirname6, join as join32 } from "node:path";
33259
+ import { dirname as dirname7, join as join33 } from "node:path";
33017
33260
  init_logger();
33018
33261
  init_types2();
33019
33262
  var import_fs_extra20 = __toESM(require_lib(), 1);
@@ -33075,7 +33318,7 @@ async function confirmUninstall(scope) {
33075
33318
  }
33076
33319
  async function cleanupEmptyDirectories(filePath, installationRoot) {
33077
33320
  let cleaned = 0;
33078
- let currentDir = dirname6(filePath);
33321
+ let currentDir = dirname7(filePath);
33079
33322
  while (currentDir !== installationRoot && currentDir.startsWith(installationRoot)) {
33080
33323
  try {
33081
33324
  const entries = readdirSync(currentDir);
@@ -33083,7 +33326,7 @@ async function cleanupEmptyDirectories(filePath, installationRoot) {
33083
33326
  rmSync(currentDir, { recursive: true });
33084
33327
  cleaned++;
33085
33328
  logger.debug(`Removed empty directory: ${currentDir}`);
33086
- currentDir = dirname6(currentDir);
33329
+ currentDir = dirname7(currentDir);
33087
33330
  } else {
33088
33331
  break;
33089
33332
  }
@@ -33106,7 +33349,7 @@ async function analyzeInstallation(installation, forceOverwrite) {
33106
33349
  return result;
33107
33350
  }
33108
33351
  for (const trackedFile of metadata.files) {
33109
- const filePath = join32(installation.path, trackedFile.path);
33352
+ const filePath = join33(installation.path, trackedFile.path);
33110
33353
  const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
33111
33354
  if (!ownershipResult.exists)
33112
33355
  continue;
@@ -33164,7 +33407,7 @@ async function removeInstallations(installations, options) {
33164
33407
  let removedCount = 0;
33165
33408
  let cleanedDirs = 0;
33166
33409
  for (const item of analysis.toDelete) {
33167
- const filePath = join32(installation.path, item.path);
33410
+ const filePath = join33(installation.path, item.path);
33168
33411
  if (await import_fs_extra20.pathExists(filePath)) {
33169
33412
  await import_fs_extra20.remove(filePath);
33170
33413
  removedCount++;
@@ -33405,7 +33648,7 @@ var import_compare_versions2 = __toESM(require_umd(), 1);
33405
33648
  // package.json
33406
33649
  var package_default2 = {
33407
33650
  name: "claudekit-cli",
33408
- version: "3.9.2",
33651
+ version: "3.10.0",
33409
33652
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
33410
33653
  type: "module",
33411
33654
  repository: {
@@ -33712,24 +33955,24 @@ var import_picocolors15 = __toESM(require_picocolors(), 1);
33712
33955
 
33713
33956
  // src/domains/versioning/version-cache.ts
33714
33957
  init_logger();
33715
- import { existsSync as existsSync7 } from "node:fs";
33716
- import { mkdir as mkdir10, readFile as readFile16, writeFile as writeFile14 } from "node:fs/promises";
33717
- import { join as join33 } from "node:path";
33958
+ import { existsSync as existsSync8 } from "node:fs";
33959
+ import { mkdir as mkdir11, readFile as readFile17, writeFile as writeFile15 } from "node:fs/promises";
33960
+ import { join as join34 } from "node:path";
33718
33961
  class VersionCacheManager {
33719
33962
  static CACHE_FILENAME = "version-check.json";
33720
33963
  static CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
33721
33964
  static getCacheFile() {
33722
33965
  const cacheDir = PathResolver.getCacheDir(false);
33723
- return join33(cacheDir, VersionCacheManager.CACHE_FILENAME);
33966
+ return join34(cacheDir, VersionCacheManager.CACHE_FILENAME);
33724
33967
  }
33725
33968
  static async load() {
33726
33969
  const cacheFile = VersionCacheManager.getCacheFile();
33727
33970
  try {
33728
- if (!existsSync7(cacheFile)) {
33971
+ if (!existsSync8(cacheFile)) {
33729
33972
  logger.debug("Version check cache not found");
33730
33973
  return null;
33731
33974
  }
33732
- const content = await readFile16(cacheFile, "utf-8");
33975
+ const content = await readFile17(cacheFile, "utf-8");
33733
33976
  const cache2 = JSON.parse(content);
33734
33977
  if (!cache2.lastCheck || !cache2.currentVersion || !cache2.latestVersion) {
33735
33978
  logger.debug("Invalid cache structure, ignoring");
@@ -33746,10 +33989,10 @@ class VersionCacheManager {
33746
33989
  const cacheFile = VersionCacheManager.getCacheFile();
33747
33990
  const cacheDir = PathResolver.getCacheDir(false);
33748
33991
  try {
33749
- if (!existsSync7(cacheDir)) {
33750
- await mkdir10(cacheDir, { recursive: true, mode: 448 });
33992
+ if (!existsSync8(cacheDir)) {
33993
+ await mkdir11(cacheDir, { recursive: true, mode: 448 });
33751
33994
  }
33752
- await writeFile14(cacheFile, JSON.stringify(cache2, null, 2), "utf-8");
33995
+ await writeFile15(cacheFile, JSON.stringify(cache2, null, 2), "utf-8");
33753
33996
  logger.debug(`Version check cache saved to ${cacheFile}`);
33754
33997
  } catch (error) {
33755
33998
  logger.debug(`Failed to save version check cache: ${error}`);
@@ -33768,7 +34011,7 @@ class VersionCacheManager {
33768
34011
  static async clear() {
33769
34012
  const cacheFile = VersionCacheManager.getCacheFile();
33770
34013
  try {
33771
- if (existsSync7(cacheFile)) {
34014
+ if (existsSync8(cacheFile)) {
33772
34015
  const fs12 = await import("node:fs/promises");
33773
34016
  await fs12.unlink(cacheFile);
33774
34017
  logger.debug("Version check cache cleared");
@@ -34231,8 +34474,8 @@ class OutputManager2 {
34231
34474
  var output2 = new OutputManager2;
34232
34475
 
34233
34476
  // src/shared/path-resolver.ts
34234
- import { homedir as homedir4, platform as platform10 } from "node:os";
34235
- import { join as join34, normalize as normalize6 } from "node:path";
34477
+ import { homedir as homedir5, platform as platform10 } from "node:os";
34478
+ import { join as join35, normalize as normalize6 } from "node:path";
34236
34479
 
34237
34480
  class PathResolver2 {
34238
34481
  static getTestHomeDir() {
@@ -34265,50 +34508,50 @@ class PathResolver2 {
34265
34508
  static getConfigDir(global3 = false) {
34266
34509
  const testHome = PathResolver2.getTestHomeDir();
34267
34510
  if (testHome) {
34268
- return global3 ? join34(testHome, ".config", "claude") : join34(testHome, ".claudekit");
34511
+ return global3 ? join35(testHome, ".config", "claude") : join35(testHome, ".claudekit");
34269
34512
  }
34270
34513
  if (!global3) {
34271
- return join34(homedir4(), ".claudekit");
34514
+ return join35(homedir5(), ".claudekit");
34272
34515
  }
34273
34516
  const os2 = platform10();
34274
34517
  if (os2 === "win32") {
34275
- const localAppData = process.env.LOCALAPPDATA || join34(homedir4(), "AppData", "Local");
34276
- return join34(localAppData, "claude");
34518
+ const localAppData = process.env.LOCALAPPDATA || join35(homedir5(), "AppData", "Local");
34519
+ return join35(localAppData, "claude");
34277
34520
  }
34278
34521
  const xdgConfigHome = process.env.XDG_CONFIG_HOME;
34279
34522
  if (xdgConfigHome) {
34280
- return join34(xdgConfigHome, "claude");
34523
+ return join35(xdgConfigHome, "claude");
34281
34524
  }
34282
- return join34(homedir4(), ".config", "claude");
34525
+ return join35(homedir5(), ".config", "claude");
34283
34526
  }
34284
34527
  static getConfigFile(global3 = false) {
34285
- return join34(PathResolver2.getConfigDir(global3), "config.json");
34528
+ return join35(PathResolver2.getConfigDir(global3), "config.json");
34286
34529
  }
34287
34530
  static getCacheDir(global3 = false) {
34288
34531
  const testHome = PathResolver2.getTestHomeDir();
34289
34532
  if (testHome) {
34290
- return global3 ? join34(testHome, ".cache", "claude") : join34(testHome, ".claudekit", "cache");
34533
+ return global3 ? join35(testHome, ".cache", "claude") : join35(testHome, ".claudekit", "cache");
34291
34534
  }
34292
34535
  if (!global3) {
34293
- return join34(homedir4(), ".claudekit", "cache");
34536
+ return join35(homedir5(), ".claudekit", "cache");
34294
34537
  }
34295
34538
  const os2 = platform10();
34296
34539
  if (os2 === "win32") {
34297
- const localAppData = process.env.LOCALAPPDATA || join34(homedir4(), "AppData", "Local");
34298
- return join34(localAppData, "claude", "cache");
34540
+ const localAppData = process.env.LOCALAPPDATA || join35(homedir5(), "AppData", "Local");
34541
+ return join35(localAppData, "claude", "cache");
34299
34542
  }
34300
34543
  const xdgCacheHome = process.env.XDG_CACHE_HOME;
34301
34544
  if (xdgCacheHome) {
34302
- return join34(xdgCacheHome, "claude");
34545
+ return join35(xdgCacheHome, "claude");
34303
34546
  }
34304
- return join34(homedir4(), ".cache", "claude");
34547
+ return join35(homedir5(), ".cache", "claude");
34305
34548
  }
34306
34549
  static getGlobalKitDir() {
34307
34550
  const testHome = PathResolver2.getTestHomeDir();
34308
34551
  if (testHome) {
34309
- return join34(testHome, ".claude");
34552
+ return join35(testHome, ".claude");
34310
34553
  }
34311
- return join34(homedir4(), ".claude");
34554
+ return join35(homedir5(), ".claude");
34312
34555
  }
34313
34556
  static getPathPrefix(global3) {
34314
34557
  return global3 ? "" : ".claude";
@@ -34316,9 +34559,9 @@ class PathResolver2 {
34316
34559
  static buildSkillsPath(baseDir, global3) {
34317
34560
  const prefix = PathResolver2.getPathPrefix(global3);
34318
34561
  if (prefix) {
34319
- return join34(baseDir, prefix, "skills");
34562
+ return join35(baseDir, prefix, "skills");
34320
34563
  }
34321
- return join34(baseDir, "skills");
34564
+ return join35(baseDir, "skills");
34322
34565
  }
34323
34566
  static buildComponentPath(baseDir, component, global3) {
34324
34567
  if (!PathResolver2.isPathSafe(component)) {
@@ -34326,9 +34569,9 @@ class PathResolver2 {
34326
34569
  }
34327
34570
  const prefix = PathResolver2.getPathPrefix(global3);
34328
34571
  if (prefix) {
34329
- return join34(baseDir, prefix, component);
34572
+ return join35(baseDir, prefix, component);
34330
34573
  }
34331
- return join34(baseDir, component);
34574
+ return join35(baseDir, component);
34332
34575
  }
34333
34576
  }
34334
34577
 
@@ -34360,11 +34603,11 @@ async function displayVersion() {
34360
34603
  let localKitVersion = null;
34361
34604
  let isGlobalOnlyKit = false;
34362
34605
  const globalKitDir = PathResolver2.getGlobalKitDir();
34363
- const globalMetadataPath = join35(globalKitDir, "metadata.json");
34606
+ const globalMetadataPath = join36(globalKitDir, "metadata.json");
34364
34607
  const prefix = PathResolver2.getPathPrefix(false);
34365
- const localMetadataPath = prefix ? join35(process.cwd(), prefix, "metadata.json") : join35(process.cwd(), "metadata.json");
34608
+ const localMetadataPath = prefix ? join36(process.cwd(), prefix, "metadata.json") : join36(process.cwd(), "metadata.json");
34366
34609
  const isLocalSameAsGlobal = localMetadataPath === globalMetadataPath;
34367
- if (!isLocalSameAsGlobal && existsSync8(localMetadataPath)) {
34610
+ if (!isLocalSameAsGlobal && existsSync9(localMetadataPath)) {
34368
34611
  try {
34369
34612
  const rawMetadata = JSON.parse(readFileSync5(localMetadataPath, "utf-8"));
34370
34613
  const metadata = MetadataSchema.parse(rawMetadata);
@@ -34378,7 +34621,7 @@ async function displayVersion() {
34378
34621
  logger2.verbose("Failed to parse local metadata.json", { error });
34379
34622
  }
34380
34623
  }
34381
- if (existsSync8(globalMetadataPath)) {
34624
+ if (existsSync9(globalMetadataPath)) {
34382
34625
  try {
34383
34626
  const rawMetadata = JSON.parse(readFileSync5(globalMetadataPath, "utf-8"));
34384
34627
  const metadata = MetadataSchema.parse(rawMetadata);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudekit-cli",
3
- "version": "3.9.2",
3
+ "version": "3.10.0",
4
4
  "description": "CLI tool for bootstrapping and updating ClaudeKit projects",
5
5
  "type": "module",
6
6
  "repository": {