claudekit-cli 3.35.0 → 3.36.0-dev.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.
Files changed (2) hide show
  1. package/dist/index.js +1643 -732
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -38479,7 +38479,10 @@ var init_metadata = __esm(() => {
38479
38479
  files: exports_external.array(TrackedFileSchema).optional(),
38480
38480
  lastUpdateCheck: exports_external.string().optional(),
38481
38481
  dismissedVersion: exports_external.string().optional(),
38482
- installedSettings: InstalledSettingsSchema.optional()
38482
+ installedSettings: InstalledSettingsSchema.optional(),
38483
+ pluginInstalled: exports_external.boolean().optional(),
38484
+ pluginInstalledAt: exports_external.string().optional(),
38485
+ pluginVersion: exports_external.string().optional()
38483
38486
  });
38484
38487
  MultiKitMetadataSchema = exports_external.object({
38485
38488
  kits: exports_external.record(KitType, KitMetadataSchema).optional(),
@@ -53920,6 +53923,17 @@ var init_package_manager_detector = __esm(() => {
53920
53923
  });
53921
53924
 
53922
53925
  // src/domains/migration/metadata-migration.ts
53926
+ var exports_metadata_migration = {};
53927
+ __export(exports_metadata_migration, {
53928
+ updateKitPluginState: () => updateKitPluginState,
53929
+ needsMigration: () => needsMigration,
53930
+ migrateToMultiKit: () => migrateToMultiKit,
53931
+ getTrackedFilesForKit: () => getTrackedFilesForKit,
53932
+ getKitMetadata: () => getKitMetadata,
53933
+ getInstalledKits: () => getInstalledKits,
53934
+ getAllTrackedFiles: () => getAllTrackedFiles,
53935
+ detectMetadataFormat: () => detectMetadataFormat
53936
+ });
53923
53937
  import { join as join35 } from "node:path";
53924
53938
  async function detectMetadataFormat(claudeDir2) {
53925
53939
  const metadataPath = join35(claudeDir2, "metadata.json");
@@ -53956,6 +53970,9 @@ async function detectMetadataFormat(claudeDir2) {
53956
53970
  return { format: "none", metadata: null, detectedKit: null };
53957
53971
  }
53958
53972
  }
53973
+ function needsMigration(detection) {
53974
+ return detection.format === "legacy";
53975
+ }
53959
53976
  async function migrateToMultiKit(claudeDir2) {
53960
53977
  const detection = await detectMetadataFormat(claudeDir2);
53961
53978
  if (detection.format === "multi-kit") {
@@ -54079,6 +54096,41 @@ function getInstalledKits(metadata) {
54079
54096
  }
54080
54097
  return [];
54081
54098
  }
54099
+ async function updateKitPluginState(claudeDir2, kit, state) {
54100
+ const metadataPath = join35(claudeDir2, "metadata.json");
54101
+ if (!await import_fs_extra3.pathExists(metadataPath)) {
54102
+ logger.debug("No metadata.json found — skipping plugin state update");
54103
+ return;
54104
+ }
54105
+ try {
54106
+ let metadata = {};
54107
+ try {
54108
+ const raw = await import_fs_extra3.readFile(metadataPath, "utf-8");
54109
+ const parsed = JSON.parse(raw);
54110
+ if (parsed && typeof parsed === "object") {
54111
+ metadata = parsed;
54112
+ } else {
54113
+ logger.warning("metadata.json is not an object; rebuilding minimal plugin metadata structure");
54114
+ }
54115
+ } catch (error) {
54116
+ logger.warning(`metadata.json unreadable during plugin state update; rebuilding: ${error}`);
54117
+ }
54118
+ if (!metadata.kits || typeof metadata.kits !== "object") {
54119
+ metadata.kits = {};
54120
+ }
54121
+ const existingKitState = metadata.kits[kit];
54122
+ metadata.kits[kit] = {
54123
+ ...existingKitState || {},
54124
+ version: existingKitState?.version ?? "",
54125
+ installedAt: existingKitState?.installedAt ?? state.pluginInstalledAt,
54126
+ ...state
54127
+ };
54128
+ await import_fs_extra3.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
54129
+ logger.debug(`Plugin state saved: ${state.pluginInstalled ? "installed" : "failed"}`);
54130
+ } catch (error) {
54131
+ logger.debug(`Failed to update plugin state: ${error}`);
54132
+ }
54133
+ }
54082
54134
  var import_fs_extra3;
54083
54135
  var init_metadata_migration = __esm(() => {
54084
54136
  init_logger();
@@ -54314,7 +54366,7 @@ var package_default;
54314
54366
  var init_package = __esm(() => {
54315
54367
  package_default = {
54316
54368
  name: "claudekit-cli",
54317
- version: "3.35.0",
54369
+ version: "3.36.0-dev.1",
54318
54370
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
54319
54371
  type: "module",
54320
54372
  repository: {
@@ -61503,7 +61555,7 @@ var require_picomatch2 = __commonJS((exports, module) => {
61503
61555
  import { exec as exec7, execFile as execFile7, spawn as spawn3 } from "node:child_process";
61504
61556
  import { promisify as promisify13 } from "node:util";
61505
61557
  function executeInteractiveScript(command, args, options2) {
61506
- return new Promise((resolve13, reject) => {
61558
+ return new Promise((resolve14, reject) => {
61507
61559
  const child = spawn3(command, args, {
61508
61560
  stdio: ["ignore", "inherit", "inherit"],
61509
61561
  cwd: options2?.cwd,
@@ -61524,7 +61576,7 @@ function executeInteractiveScript(command, args, options2) {
61524
61576
  } else if (code !== 0) {
61525
61577
  reject(new Error(`Command exited with code ${code}`));
61526
61578
  } else {
61527
- resolve13();
61579
+ resolve14();
61528
61580
  }
61529
61581
  });
61530
61582
  child.on("error", (error) => {
@@ -61545,7 +61597,7 @@ var init_process_executor = __esm(() => {
61545
61597
  });
61546
61598
 
61547
61599
  // src/services/package-installer/validators.ts
61548
- import { resolve as resolve13 } from "node:path";
61600
+ import { resolve as resolve14 } from "node:path";
61549
61601
  function validatePackageName(packageName) {
61550
61602
  if (!packageName || typeof packageName !== "string") {
61551
61603
  throw new Error("Package name must be a non-empty string");
@@ -61558,8 +61610,8 @@ function validatePackageName(packageName) {
61558
61610
  }
61559
61611
  }
61560
61612
  function validateScriptPath(skillsDir2, scriptPath) {
61561
- const skillsDirResolved = resolve13(skillsDir2);
61562
- const scriptPathResolved = resolve13(scriptPath);
61613
+ const skillsDirResolved = resolve14(skillsDir2);
61614
+ const scriptPathResolved = resolve14(scriptPath);
61563
61615
  const skillsDirNormalized = isWindows() ? skillsDirResolved.toLowerCase() : skillsDirResolved;
61564
61616
  const scriptPathNormalized = isWindows() ? scriptPathResolved.toLowerCase() : scriptPathResolved;
61565
61617
  if (!scriptPathNormalized.startsWith(skillsDirNormalized)) {
@@ -61746,7 +61798,7 @@ var init_gemini_installer = __esm(() => {
61746
61798
  });
61747
61799
 
61748
61800
  // src/services/package-installer/opencode-installer.ts
61749
- import { join as join59 } from "node:path";
61801
+ import { join as join58 } from "node:path";
61750
61802
  async function isOpenCodeInstalled() {
61751
61803
  try {
61752
61804
  await execAsync7("opencode --version", { timeout: 5000 });
@@ -61769,7 +61821,7 @@ async function installOpenCode() {
61769
61821
  logger.info(`Installing ${displayName}...`);
61770
61822
  const { unlink: unlink11 } = await import("node:fs/promises");
61771
61823
  const { tmpdir: tmpdir4 } = await import("node:os");
61772
- const tempScriptPath = join59(tmpdir4(), "opencode-install.sh");
61824
+ const tempScriptPath = join58(tmpdir4(), "opencode-install.sh");
61773
61825
  try {
61774
61826
  logger.info("Downloading OpenCode installation script...");
61775
61827
  await execFileAsync5("curl", ["-fsSL", "https://opencode.ai/install", "-o", tempScriptPath], {
@@ -61821,7 +61873,7 @@ var PARTIAL_INSTALL_VERSION = "partial", EXIT_CODE_CRITICAL_FAILURE = 1, EXIT_CO
61821
61873
 
61822
61874
  // src/services/package-installer/install-error-handler.ts
61823
61875
  import { existsSync as existsSync44, readFileSync as readFileSync9, unlinkSync as unlinkSync2 } from "node:fs";
61824
- import { join as join60 } from "node:path";
61876
+ import { join as join59 } from "node:path";
61825
61877
  function parseNameReason(str2) {
61826
61878
  const colonIndex = str2.indexOf(":");
61827
61879
  if (colonIndex === -1) {
@@ -61830,7 +61882,7 @@ function parseNameReason(str2) {
61830
61882
  return [str2.slice(0, colonIndex).trim(), str2.slice(colonIndex + 1).trim()];
61831
61883
  }
61832
61884
  function displayInstallErrors(skillsDir2) {
61833
- const summaryPath = join60(skillsDir2, ".install-error-summary.json");
61885
+ const summaryPath = join59(skillsDir2, ".install-error-summary.json");
61834
61886
  if (!existsSync44(summaryPath)) {
61835
61887
  logger.error("Skills installation failed. Run with --verbose for details.");
61836
61888
  return;
@@ -61921,7 +61973,7 @@ async function checkNeedsSudoPackages() {
61921
61973
  }
61922
61974
  }
61923
61975
  function hasInstallState(skillsDir2) {
61924
- const stateFilePath = join60(skillsDir2, ".install-state.json");
61976
+ const stateFilePath = join59(skillsDir2, ".install-state.json");
61925
61977
  return existsSync44(stateFilePath);
61926
61978
  }
61927
61979
  var WHICH_COMMAND_TIMEOUT_MS = 5000;
@@ -61930,7 +61982,7 @@ var init_install_error_handler = __esm(() => {
61930
61982
  });
61931
61983
 
61932
61984
  // src/services/package-installer/skills-installer.ts
61933
- import { join as join61 } from "node:path";
61985
+ import { join as join60 } from "node:path";
61934
61986
  async function installSkillsDependencies(skillsDir2, options2 = {}) {
61935
61987
  const { skipConfirm = false, withSudo = false } = options2;
61936
61988
  const displayName = "Skills Dependencies";
@@ -61956,7 +62008,7 @@ async function installSkillsDependencies(skillsDir2, options2 = {}) {
61956
62008
  const clack = await Promise.resolve().then(() => (init_dist2(), exports_dist));
61957
62009
  const platform7 = process.platform;
61958
62010
  const scriptName = platform7 === "win32" ? "install.ps1" : "install.sh";
61959
- const scriptPath = join61(skillsDir2, scriptName);
62011
+ const scriptPath = join60(skillsDir2, scriptName);
61960
62012
  try {
61961
62013
  validateScriptPath(skillsDir2, scriptPath);
61962
62014
  } catch (error) {
@@ -61972,7 +62024,7 @@ async function installSkillsDependencies(skillsDir2, options2 = {}) {
61972
62024
  logger.warning(`Skills installation script not found: ${scriptPath}`);
61973
62025
  logger.info("");
61974
62026
  logger.info("\uD83D\uDCD6 Manual Installation Instructions:");
61975
- logger.info(` See: ${join61(skillsDir2, "INSTALLATION.md")}`);
62027
+ logger.info(` See: ${join60(skillsDir2, "INSTALLATION.md")}`);
61976
62028
  logger.info("");
61977
62029
  logger.info("Quick start:");
61978
62030
  logger.info(" cd .claude/skills/ai-multimodal/scripts");
@@ -62019,7 +62071,7 @@ async function installSkillsDependencies(skillsDir2, options2 = {}) {
62019
62071
  logger.info(` ${platform7 === "win32" ? `powershell -File "${scriptPath}"` : `bash ${scriptPath}`}`);
62020
62072
  logger.info("");
62021
62073
  logger.info("Or see complete guide:");
62022
- logger.info(` ${join61(skillsDir2, "INSTALLATION.md")}`);
62074
+ logger.info(` ${join60(skillsDir2, "INSTALLATION.md")}`);
62023
62075
  return {
62024
62076
  success: false,
62025
62077
  package: displayName,
@@ -62140,7 +62192,7 @@ async function installSkillsDependencies(skillsDir2, options2 = {}) {
62140
62192
  logger.info("\uD83D\uDCD6 Manual Installation Instructions:");
62141
62193
  logger.info("");
62142
62194
  logger.info("See complete guide:");
62143
- logger.info(` cat ${join61(skillsDir2, "INSTALLATION.md")}`);
62195
+ logger.info(` cat ${join60(skillsDir2, "INSTALLATION.md")}`);
62144
62196
  logger.info("");
62145
62197
  logger.info("Quick start:");
62146
62198
  logger.info(" cd .claude/skills/ai-multimodal/scripts");
@@ -62186,7 +62238,7 @@ var init_skills_installer2 = __esm(() => {
62186
62238
  // src/services/package-installer/gemini-mcp/config-manager.ts
62187
62239
  import { existsSync as existsSync45 } from "node:fs";
62188
62240
  import { mkdir as mkdir17, readFile as readFile35, writeFile as writeFile20 } from "node:fs/promises";
62189
- import { dirname as dirname15, join as join62 } from "node:path";
62241
+ import { dirname as dirname16, join as join61 } from "node:path";
62190
62242
  async function readJsonFile(filePath) {
62191
62243
  try {
62192
62244
  const content = await readFile35(filePath, "utf-8");
@@ -62198,7 +62250,7 @@ async function readJsonFile(filePath) {
62198
62250
  }
62199
62251
  }
62200
62252
  async function addGeminiToGitignore(projectDir) {
62201
- const gitignorePath = join62(projectDir, ".gitignore");
62253
+ const gitignorePath = join61(projectDir, ".gitignore");
62202
62254
  const geminiPattern = ".gemini/";
62203
62255
  try {
62204
62256
  let content = "";
@@ -62226,7 +62278,7 @@ ${geminiPattern}
62226
62278
  }
62227
62279
  }
62228
62280
  async function createNewSettingsWithMerge(geminiSettingsPath, mcpConfigPath) {
62229
- const linkDir = dirname15(geminiSettingsPath);
62281
+ const linkDir = dirname16(geminiSettingsPath);
62230
62282
  if (!existsSync45(linkDir)) {
62231
62283
  await mkdir17(linkDir, { recursive: true });
62232
62284
  logger.debug(`Created directory: ${linkDir}`);
@@ -62290,12 +62342,12 @@ var init_config_manager2 = __esm(() => {
62290
62342
  // src/services/package-installer/gemini-mcp/validation.ts
62291
62343
  import { existsSync as existsSync46, lstatSync, readlinkSync } from "node:fs";
62292
62344
  import { homedir as homedir27 } from "node:os";
62293
- import { join as join63 } from "node:path";
62345
+ import { join as join62 } from "node:path";
62294
62346
  function getGlobalMcpConfigPath() {
62295
- return join63(homedir27(), ".claude", ".mcp.json");
62347
+ return join62(homedir27(), ".claude", ".mcp.json");
62296
62348
  }
62297
62349
  function getLocalMcpConfigPath(projectDir) {
62298
- return join63(projectDir, ".mcp.json");
62350
+ return join62(projectDir, ".mcp.json");
62299
62351
  }
62300
62352
  function findMcpConfigPath(projectDir) {
62301
62353
  const localPath = getLocalMcpConfigPath(projectDir);
@@ -62313,9 +62365,9 @@ function findMcpConfigPath(projectDir) {
62313
62365
  }
62314
62366
  function getGeminiSettingsPath(projectDir, isGlobal) {
62315
62367
  if (isGlobal) {
62316
- return join63(homedir27(), ".gemini", "settings.json");
62368
+ return join62(homedir27(), ".gemini", "settings.json");
62317
62369
  }
62318
- return join63(projectDir, ".gemini", "settings.json");
62370
+ return join62(projectDir, ".gemini", "settings.json");
62319
62371
  }
62320
62372
  function checkExistingGeminiConfig(projectDir, isGlobal = false) {
62321
62373
  const geminiSettingsPath = getGeminiSettingsPath(projectDir, isGlobal);
@@ -62345,9 +62397,9 @@ var init_validation = __esm(() => {
62345
62397
  // src/services/package-installer/gemini-mcp/linker-core.ts
62346
62398
  import { existsSync as existsSync47 } from "node:fs";
62347
62399
  import { mkdir as mkdir18, symlink as symlink2 } from "node:fs/promises";
62348
- import { dirname as dirname16, join as join64 } from "node:path";
62400
+ import { dirname as dirname17, join as join63 } from "node:path";
62349
62401
  async function createSymlink(targetPath, linkPath, projectDir, isGlobal) {
62350
- const linkDir = dirname16(linkPath);
62402
+ const linkDir = dirname17(linkPath);
62351
62403
  if (!existsSync47(linkDir)) {
62352
62404
  await mkdir18(linkDir, { recursive: true });
62353
62405
  logger.debug(`Created directory: ${linkDir}`);
@@ -62356,7 +62408,7 @@ async function createSymlink(targetPath, linkPath, projectDir, isGlobal) {
62356
62408
  if (isGlobal) {
62357
62409
  symlinkTarget = getGlobalMcpConfigPath();
62358
62410
  } else {
62359
- const localMcpPath = join64(projectDir, ".mcp.json");
62411
+ const localMcpPath = join63(projectDir, ".mcp.json");
62360
62412
  const isLocalConfig = targetPath === localMcpPath;
62361
62413
  symlinkTarget = isLocalConfig ? "../.mcp.json" : targetPath;
62362
62414
  }
@@ -62389,10 +62441,10 @@ __export(exports_gemini_mcp_linker, {
62389
62441
  checkExistingGeminiConfig: () => checkExistingGeminiConfig,
62390
62442
  addGeminiToGitignore: () => addGeminiToGitignore
62391
62443
  });
62392
- import { resolve as resolve14 } from "node:path";
62444
+ import { resolve as resolve15 } from "node:path";
62393
62445
  async function linkGeminiMcpConfig(projectDir, options2 = {}) {
62394
62446
  const { skipGitignore = false, isGlobal = false } = options2;
62395
- const resolvedProjectDir = resolve14(projectDir);
62447
+ const resolvedProjectDir = resolve15(projectDir);
62396
62448
  const geminiSettingsPath = getGeminiSettingsPath(resolvedProjectDir, isGlobal);
62397
62449
  const mcpConfigPath = findMcpConfigPath(resolvedProjectDir);
62398
62450
  if (!mcpConfigPath) {
@@ -63040,7 +63092,7 @@ var require_get_stream = __commonJS((exports, module) => {
63040
63092
  };
63041
63093
  const { maxBuffer } = options2;
63042
63094
  let stream;
63043
- await new Promise((resolve16, reject) => {
63095
+ await new Promise((resolve17, reject) => {
63044
63096
  const rejectPromise = (error) => {
63045
63097
  if (error && stream.getBufferedLength() <= BufferConstants.MAX_LENGTH) {
63046
63098
  error.bufferedData = stream.getBufferedValue();
@@ -63052,7 +63104,7 @@ var require_get_stream = __commonJS((exports, module) => {
63052
63104
  rejectPromise(error);
63053
63105
  return;
63054
63106
  }
63055
- resolve16();
63107
+ resolve17();
63056
63108
  });
63057
63109
  stream.on("data", () => {
63058
63110
  if (stream.getBufferedLength() > maxBuffer) {
@@ -64413,7 +64465,7 @@ var require_extract_zip = __commonJS((exports, module) => {
64413
64465
  debug("opening", this.zipPath, "with opts", this.opts);
64414
64466
  this.zipfile = await openZip(this.zipPath, { lazyEntries: true });
64415
64467
  this.canceled = false;
64416
- return new Promise((resolve16, reject) => {
64468
+ return new Promise((resolve17, reject) => {
64417
64469
  this.zipfile.on("error", (err) => {
64418
64470
  this.canceled = true;
64419
64471
  reject(err);
@@ -64422,7 +64474,7 @@ var require_extract_zip = __commonJS((exports, module) => {
64422
64474
  this.zipfile.on("close", () => {
64423
64475
  if (!this.canceled) {
64424
64476
  debug("zip extraction complete");
64425
- resolve16();
64477
+ resolve17();
64426
64478
  }
64427
64479
  });
64428
64480
  this.zipfile.on("entry", async (entry) => {
@@ -64529,6 +64581,412 @@ var require_extract_zip = __commonJS((exports, module) => {
64529
64581
  };
64530
64582
  });
64531
64583
 
64584
+ // src/services/file-operations/manifest/manifest-reader.ts
64585
+ import { join as join74 } from "node:path";
64586
+ async function readManifest(claudeDir2) {
64587
+ const metadataPath = join74(claudeDir2, "metadata.json");
64588
+ if (!await import_fs_extra8.pathExists(metadataPath)) {
64589
+ return null;
64590
+ }
64591
+ try {
64592
+ const content = await import_fs_extra8.readFile(metadataPath, "utf-8");
64593
+ const parsed = JSON.parse(content);
64594
+ return MetadataSchema.parse(parsed);
64595
+ } catch (error) {
64596
+ logger.debug(`Failed to read manifest: ${error}`);
64597
+ return null;
64598
+ }
64599
+ }
64600
+ async function readKitManifest(claudeDir2, kit) {
64601
+ const metadata = await readManifest(claudeDir2);
64602
+ if (!metadata)
64603
+ return null;
64604
+ return getKitMetadata(metadata, kit);
64605
+ }
64606
+ async function findFileInInstalledKits(claudeDir2, relativePath, excludeKit) {
64607
+ const metadata = await readManifest(claudeDir2);
64608
+ if (!metadata?.kits) {
64609
+ return {
64610
+ exists: false,
64611
+ ownerKit: null,
64612
+ checksum: null,
64613
+ version: null,
64614
+ sourceTimestamp: null,
64615
+ installedAt: null
64616
+ };
64617
+ }
64618
+ for (const [kitName, kitMeta] of Object.entries(metadata.kits)) {
64619
+ const kit = kitName;
64620
+ if (kit === excludeKit)
64621
+ continue;
64622
+ if (!kitMeta.files)
64623
+ continue;
64624
+ const file = kitMeta.files.find((f3) => f3.path === relativePath);
64625
+ if (file) {
64626
+ return {
64627
+ exists: true,
64628
+ ownerKit: kit,
64629
+ checksum: file.checksum,
64630
+ version: kitMeta.version,
64631
+ sourceTimestamp: file.sourceTimestamp ?? null,
64632
+ installedAt: file.installedAt ?? null
64633
+ };
64634
+ }
64635
+ }
64636
+ return {
64637
+ exists: false,
64638
+ ownerKit: null,
64639
+ checksum: null,
64640
+ version: null,
64641
+ sourceTimestamp: null,
64642
+ installedAt: null
64643
+ };
64644
+ }
64645
+ async function getUninstallManifest(claudeDir2, kit) {
64646
+ const detection = await detectMetadataFormat(claudeDir2);
64647
+ if (detection.format === "multi-kit" && detection.metadata?.kits) {
64648
+ const installedKits = Object.keys(detection.metadata.kits);
64649
+ if (kit) {
64650
+ const kitMeta = detection.metadata.kits[kit];
64651
+ if (!kitMeta?.files) {
64652
+ return {
64653
+ filesToRemove: [],
64654
+ filesToPreserve: USER_CONFIG_PATTERNS,
64655
+ hasManifest: true,
64656
+ isMultiKit: true,
64657
+ remainingKits: installedKits.filter((k2) => k2 !== kit)
64658
+ };
64659
+ }
64660
+ const kitFiles = kitMeta.files.map((f3) => f3.path);
64661
+ const sharedFiles = new Set;
64662
+ for (const otherKit of installedKits) {
64663
+ if (otherKit !== kit) {
64664
+ const otherMeta = detection.metadata.kits[otherKit];
64665
+ if (otherMeta?.files) {
64666
+ for (const f3 of otherMeta.files) {
64667
+ sharedFiles.add(f3.path);
64668
+ }
64669
+ }
64670
+ }
64671
+ }
64672
+ const filesToRemove = kitFiles.filter((f3) => !sharedFiles.has(f3));
64673
+ const filesToPreserve = [
64674
+ ...USER_CONFIG_PATTERNS,
64675
+ ...kitFiles.filter((f3) => sharedFiles.has(f3))
64676
+ ];
64677
+ return {
64678
+ filesToRemove,
64679
+ filesToPreserve,
64680
+ hasManifest: true,
64681
+ isMultiKit: true,
64682
+ remainingKits: installedKits.filter((k2) => k2 !== kit)
64683
+ };
64684
+ }
64685
+ const allFiles = getAllTrackedFiles(detection.metadata);
64686
+ return {
64687
+ filesToRemove: allFiles.map((f3) => f3.path),
64688
+ filesToPreserve: USER_CONFIG_PATTERNS,
64689
+ hasManifest: true,
64690
+ isMultiKit: true,
64691
+ remainingKits: []
64692
+ };
64693
+ }
64694
+ if (detection.format === "legacy" && detection.metadata) {
64695
+ const legacyFiles2 = detection.metadata.files?.map((f3) => f3.path) || [];
64696
+ const installedFiles = detection.metadata.installedFiles || [];
64697
+ const hasFiles = legacyFiles2.length > 0 || installedFiles.length > 0;
64698
+ if (!hasFiles) {
64699
+ const legacyDirs2 = ["commands", "agents", "skills", "rules", "workflows", "hooks", "scripts"];
64700
+ const legacyFileList = ["metadata.json"];
64701
+ return {
64702
+ filesToRemove: [...legacyDirs2, ...legacyFileList],
64703
+ filesToPreserve: USER_CONFIG_PATTERNS,
64704
+ hasManifest: false,
64705
+ isMultiKit: false,
64706
+ remainingKits: []
64707
+ };
64708
+ }
64709
+ return {
64710
+ filesToRemove: legacyFiles2.length > 0 ? legacyFiles2 : installedFiles,
64711
+ filesToPreserve: detection.metadata.userConfigFiles || USER_CONFIG_PATTERNS,
64712
+ hasManifest: true,
64713
+ isMultiKit: false,
64714
+ remainingKits: []
64715
+ };
64716
+ }
64717
+ const legacyDirs = ["commands", "agents", "skills", "rules", "workflows", "hooks", "scripts"];
64718
+ const legacyFiles = ["metadata.json"];
64719
+ return {
64720
+ filesToRemove: [...legacyDirs, ...legacyFiles],
64721
+ filesToPreserve: USER_CONFIG_PATTERNS,
64722
+ hasManifest: false,
64723
+ isMultiKit: false,
64724
+ remainingKits: []
64725
+ };
64726
+ }
64727
+ var import_fs_extra8;
64728
+ var init_manifest_reader = __esm(() => {
64729
+ init_metadata_migration();
64730
+ init_logger();
64731
+ init_types3();
64732
+ import_fs_extra8 = __toESM(require_lib3(), 1);
64733
+ });
64734
+
64735
+ // src/domains/installation/deletion-handler.ts
64736
+ var exports_deletion_handler = {};
64737
+ __export(exports_deletion_handler, {
64738
+ handleDeletions: () => handleDeletions,
64739
+ categorizeDeletions: () => categorizeDeletions
64740
+ });
64741
+ import {
64742
+ existsSync as existsSync48,
64743
+ lstatSync as lstatSync3,
64744
+ readdirSync as readdirSync3,
64745
+ realpathSync as realpathSync3,
64746
+ rmSync as rmSync2,
64747
+ rmdirSync,
64748
+ unlinkSync as unlinkSync3
64749
+ } from "node:fs";
64750
+ import { dirname as dirname19, join as join75, relative as relative10, resolve as resolve18, sep as sep3 } from "node:path";
64751
+ function findFileInMetadata(metadata, path13) {
64752
+ if (!metadata)
64753
+ return null;
64754
+ const normalizedPath = normalizeRelativePath(path13);
64755
+ if (metadata.kits) {
64756
+ for (const kitMeta of Object.values(metadata.kits)) {
64757
+ if (kitMeta?.files) {
64758
+ const found = kitMeta.files.find((f3) => normalizeRelativePath(f3.path) === normalizedPath);
64759
+ if (found)
64760
+ return found;
64761
+ }
64762
+ }
64763
+ }
64764
+ if (metadata.files) {
64765
+ const found = metadata.files.find((f3) => normalizeRelativePath(f3.path) === normalizedPath);
64766
+ if (found)
64767
+ return found;
64768
+ }
64769
+ return null;
64770
+ }
64771
+ function shouldDeletePath(path13, metadata) {
64772
+ const tracked = findFileInMetadata(metadata, path13);
64773
+ if (!tracked)
64774
+ return true;
64775
+ return tracked.ownership !== "user";
64776
+ }
64777
+ function collectFilesRecursively(dir, baseDir) {
64778
+ const results = [];
64779
+ if (!existsSync48(dir))
64780
+ return results;
64781
+ try {
64782
+ const entries = readdirSync3(dir, { withFileTypes: true });
64783
+ for (const entry of entries) {
64784
+ const fullPath = join75(dir, entry.name);
64785
+ const relativePath = relative10(baseDir, fullPath);
64786
+ if (entry.isDirectory()) {
64787
+ results.push(...collectFilesRecursively(fullPath, baseDir));
64788
+ } else {
64789
+ results.push(normalizeRelativePath(relativePath));
64790
+ }
64791
+ }
64792
+ } catch {}
64793
+ return results;
64794
+ }
64795
+ function expandGlobPatterns(patterns, claudeDir2) {
64796
+ const expanded = [];
64797
+ const allFiles = collectFilesRecursively(claudeDir2, claudeDir2);
64798
+ for (const pattern of patterns) {
64799
+ const normalizedPattern = normalizeRelativePath(pattern);
64800
+ if (PathResolver.isGlobPattern(normalizedPattern)) {
64801
+ const matcher = import_picomatch2.default(normalizedPattern);
64802
+ const matches = allFiles.filter((file) => matcher(file));
64803
+ expanded.push(...matches);
64804
+ if (matches.length > 0) {
64805
+ logger.debug(`Pattern "${normalizedPattern}" matched ${matches.length} files`);
64806
+ }
64807
+ } else {
64808
+ expanded.push(normalizedPattern);
64809
+ }
64810
+ }
64811
+ return [...new Set(expanded)];
64812
+ }
64813
+ function cleanupEmptyDirectories(filePath, claudeDir2) {
64814
+ const normalizedClaudeDir = resolve18(claudeDir2);
64815
+ let currentDir = resolve18(dirname19(filePath));
64816
+ let iterations = 0;
64817
+ while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir) && iterations < MAX_CLEANUP_ITERATIONS) {
64818
+ iterations++;
64819
+ try {
64820
+ const entries = readdirSync3(currentDir);
64821
+ if (entries.length === 0) {
64822
+ rmdirSync(currentDir);
64823
+ logger.debug(`Removed empty directory: ${currentDir}`);
64824
+ currentDir = resolve18(dirname19(currentDir));
64825
+ } else {
64826
+ break;
64827
+ }
64828
+ } catch {
64829
+ break;
64830
+ }
64831
+ }
64832
+ }
64833
+ function normalizeRelativePath(path13) {
64834
+ return path13.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
64835
+ }
64836
+ function isWithinBase(candidatePath, basePath) {
64837
+ return candidatePath === basePath || candidatePath.startsWith(`${basePath}${sep3}`);
64838
+ }
64839
+ function validateExistingDeletionTarget(fullPath, claudeDir2) {
64840
+ const normalizedPath = resolve18(fullPath);
64841
+ const normalizedClaudeDir = resolve18(claudeDir2);
64842
+ if (!isWithinBase(normalizedPath, normalizedClaudeDir)) {
64843
+ throw new Error(`Path traversal detected: ${fullPath}`);
64844
+ }
64845
+ let realBase = normalizedClaudeDir;
64846
+ try {
64847
+ realBase = realpathSync3(normalizedClaudeDir);
64848
+ } catch {}
64849
+ try {
64850
+ const realParent = realpathSync3(dirname19(fullPath));
64851
+ if (!isWithinBase(realParent, realBase)) {
64852
+ throw new Error(`Path escapes base via symlink parent: ${fullPath}`);
64853
+ }
64854
+ } catch (error) {
64855
+ throw new Error(`Failed to validate deletion parent for ${fullPath}: ${String(error)}`);
64856
+ }
64857
+ try {
64858
+ const stat13 = lstatSync3(fullPath);
64859
+ if (!stat13.isSymbolicLink()) {
64860
+ const realTarget = realpathSync3(fullPath);
64861
+ if (!isWithinBase(realTarget, realBase)) {
64862
+ throw new Error(`Path escapes base via symlink target: ${fullPath}`);
64863
+ }
64864
+ }
64865
+ } catch (error) {
64866
+ throw new Error(`Failed to validate deletion target ${fullPath}: ${String(error)}`);
64867
+ }
64868
+ }
64869
+ function deletePath(fullPath, claudeDir2) {
64870
+ validateExistingDeletionTarget(fullPath, claudeDir2);
64871
+ try {
64872
+ const stat13 = lstatSync3(fullPath);
64873
+ if (stat13.isDirectory()) {
64874
+ rmSync2(fullPath, { recursive: true, force: true });
64875
+ } else {
64876
+ unlinkSync3(fullPath);
64877
+ cleanupEmptyDirectories(fullPath, claudeDir2);
64878
+ }
64879
+ } catch (error) {
64880
+ throw new Error(`Failed to delete ${fullPath}: ${error instanceof Error ? error.message : String(error)}`);
64881
+ }
64882
+ }
64883
+ async function updateMetadataAfterDeletion(claudeDir2, deletedPaths) {
64884
+ const metadataPath = join75(claudeDir2, "metadata.json");
64885
+ if (!await import_fs_extra9.pathExists(metadataPath)) {
64886
+ return;
64887
+ }
64888
+ let content;
64889
+ try {
64890
+ content = await import_fs_extra9.readFile(metadataPath, "utf-8");
64891
+ } catch {
64892
+ logger.debug("Failed to read metadata.json for cleanup");
64893
+ return;
64894
+ }
64895
+ let metadata;
64896
+ try {
64897
+ metadata = JSON.parse(content);
64898
+ } catch {
64899
+ logger.debug("Failed to parse metadata.json for cleanup");
64900
+ return;
64901
+ }
64902
+ const deletedSet = new Set(deletedPaths);
64903
+ const isDeletedOrInDeletedDir = (path13) => {
64904
+ if (deletedSet.has(path13))
64905
+ return true;
64906
+ for (const deleted of deletedPaths) {
64907
+ if (path13.startsWith(`${deleted}/`))
64908
+ return true;
64909
+ }
64910
+ return false;
64911
+ };
64912
+ if (metadata.kits) {
64913
+ for (const kitName of Object.keys(metadata.kits)) {
64914
+ const kit = metadata.kits[kitName];
64915
+ if (kit?.files) {
64916
+ kit.files = kit.files.filter((f3) => !isDeletedOrInDeletedDir(f3.path));
64917
+ }
64918
+ }
64919
+ }
64920
+ if (metadata.files) {
64921
+ metadata.files = metadata.files.filter((f3) => !isDeletedOrInDeletedDir(f3.path));
64922
+ }
64923
+ try {
64924
+ await import_fs_extra9.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
64925
+ logger.debug(`Updated metadata.json, removed ${deletedPaths.length} entries`);
64926
+ } catch {
64927
+ logger.debug("Failed to write updated metadata.json");
64928
+ }
64929
+ }
64930
+ function categorizeDeletions(deletions) {
64931
+ const immediate = [];
64932
+ const deferred = [];
64933
+ for (const path13 of deletions) {
64934
+ if (path13.startsWith("skills/") || path13.startsWith("skills\\")) {
64935
+ deferred.push(path13);
64936
+ } else {
64937
+ immediate.push(path13);
64938
+ }
64939
+ }
64940
+ return { immediate, deferred };
64941
+ }
64942
+ async function handleDeletions(sourceMetadata, claudeDir2) {
64943
+ const deletionPatterns = sourceMetadata.deletions || [];
64944
+ if (deletionPatterns.length === 0) {
64945
+ return { deletedPaths: [], preservedPaths: [], errors: [] };
64946
+ }
64947
+ const deletions = expandGlobPatterns(deletionPatterns, claudeDir2);
64948
+ const userMetadata = await readManifest(claudeDir2);
64949
+ const result = { deletedPaths: [], preservedPaths: [], errors: [] };
64950
+ for (const path13 of deletions) {
64951
+ const normalizedRelativePath = normalizeRelativePath(path13);
64952
+ const fullPath = join75(claudeDir2, normalizedRelativePath);
64953
+ const normalizedResolvedPath = resolve18(fullPath);
64954
+ const normalizedClaudeDir = resolve18(claudeDir2);
64955
+ if (!isWithinBase(normalizedResolvedPath, normalizedClaudeDir)) {
64956
+ logger.warning(`Skipping invalid path: ${normalizedRelativePath}`);
64957
+ result.errors.push(normalizedRelativePath);
64958
+ continue;
64959
+ }
64960
+ if (!shouldDeletePath(normalizedRelativePath, userMetadata)) {
64961
+ result.preservedPaths.push(normalizedRelativePath);
64962
+ logger.verbose(`Preserved user file: ${normalizedRelativePath}`);
64963
+ continue;
64964
+ }
64965
+ if (existsSync48(fullPath)) {
64966
+ try {
64967
+ deletePath(fullPath, claudeDir2);
64968
+ result.deletedPaths.push(normalizedRelativePath);
64969
+ logger.verbose(`Deleted: ${normalizedRelativePath}`);
64970
+ } catch (error) {
64971
+ result.errors.push(normalizedRelativePath);
64972
+ logger.debug(`Failed to delete ${normalizedRelativePath}: ${error}`);
64973
+ }
64974
+ }
64975
+ }
64976
+ if (result.deletedPaths.length > 0) {
64977
+ await updateMetadataAfterDeletion(claudeDir2, result.deletedPaths);
64978
+ }
64979
+ return result;
64980
+ }
64981
+ var import_fs_extra9, import_picomatch2, MAX_CLEANUP_ITERATIONS = 50;
64982
+ var init_deletion_handler = __esm(() => {
64983
+ init_manifest_reader();
64984
+ init_logger();
64985
+ init_path_resolver();
64986
+ import_fs_extra9 = __toESM(require_lib3(), 1);
64987
+ import_picomatch2 = __toESM(require_picomatch2(), 1);
64988
+ });
64989
+
64532
64990
  // src/domains/ui/ownership-display.ts
64533
64991
  var exports_ownership_display = {};
64534
64992
  __export(exports_ownership_display, {
@@ -64665,6 +65123,481 @@ var init_ownership_display = __esm(() => {
64665
65123
  import_picocolors22 = __toESM(require_picocolors(), 1);
64666
65124
  });
64667
65125
 
65126
+ // src/shared/claude-exec-options.ts
65127
+ function buildExecOptions(timeout2) {
65128
+ const env2 = { ...process.env };
65129
+ for (const key of Object.keys(env2)) {
65130
+ if (key.startsWith("CLAUDE") && key !== "CLAUDE_CONFIG_DIR") {
65131
+ delete env2[key];
65132
+ }
65133
+ }
65134
+ return {
65135
+ timeout: timeout2,
65136
+ env: env2,
65137
+ shell: process.platform === "win32"
65138
+ };
65139
+ }
65140
+
65141
+ // src/services/cc-version-checker.ts
65142
+ var exports_cc_version_checker = {};
65143
+ __export(exports_cc_version_checker, {
65144
+ requireCCPluginSupport: () => requireCCPluginSupport,
65145
+ parseVersion: () => parseVersion,
65146
+ isCCPluginSupportError: () => isCCPluginSupportError,
65147
+ getCCVersion: () => getCCVersion,
65148
+ compareVersions: () => compareVersions9,
65149
+ MIN_PLUGIN_VERSION: () => MIN_PLUGIN_VERSION,
65150
+ CCPluginSupportError: () => CCPluginSupportError
65151
+ });
65152
+ import { execFile as execFile9 } from "node:child_process";
65153
+ import { promisify as promisify14 } from "node:util";
65154
+ function parseVersion(version) {
65155
+ const match2 = version.match(/^(\d+)\.(\d+)\.(\d+)/);
65156
+ if (!match2)
65157
+ return null;
65158
+ return [Number(match2[1]), Number(match2[2]), Number(match2[3])];
65159
+ }
65160
+ function compareVersions9(a3, b3) {
65161
+ const parsedA = parseVersion(a3);
65162
+ const parsedB = parseVersion(b3);
65163
+ if (!parsedA || !parsedB)
65164
+ return -1;
65165
+ for (let i = 0;i < 3; i++) {
65166
+ if (parsedA[i] !== parsedB[i])
65167
+ return parsedA[i] - parsedB[i];
65168
+ }
65169
+ return 0;
65170
+ }
65171
+ async function getCCVersionResult() {
65172
+ try {
65173
+ const { stdout: stdout2, stderr } = await execFileAsync6("claude", ["--version"], buildExecOptions(5000));
65174
+ const output2 = stdout2.trim();
65175
+ const match2 = output2.match(/(\d+\.\d+\.\d+)/);
65176
+ if (!match2) {
65177
+ return {
65178
+ version: null,
65179
+ error: new CCPluginSupportError("cc_version_unparseable", "Failed to parse Claude Code version output", output2 || stderr?.trim() || "empty output")
65180
+ };
65181
+ }
65182
+ return { version: match2[1] };
65183
+ } catch (error) {
65184
+ const err = error;
65185
+ if (err.code === "ENOENT") {
65186
+ return {
65187
+ version: null,
65188
+ error: new CCPluginSupportError("cc_not_found", "Claude Code CLI not found on PATH")
65189
+ };
65190
+ }
65191
+ return {
65192
+ version: null,
65193
+ error: new CCPluginSupportError("cc_exec_failed", "Failed to run 'claude --version'", (err.stderr ?? err.message ?? "Unknown error").trim())
65194
+ };
65195
+ }
65196
+ }
65197
+ async function getCCVersion() {
65198
+ const result = await getCCVersionResult();
65199
+ return result.version;
65200
+ }
65201
+ async function requireCCPluginSupport() {
65202
+ const result = await getCCVersionResult();
65203
+ const version = result.version;
65204
+ if (!version) {
65205
+ throw result.error ?? new CCPluginSupportError("cc_exec_failed", "Failed to determine Claude Code version");
65206
+ }
65207
+ if (compareVersions9(version, MIN_PLUGIN_VERSION) < 0) {
65208
+ throw new CCPluginSupportError("cc_version_too_old", `Claude Code ${version} does not support plugins (requires >= ${MIN_PLUGIN_VERSION})`);
65209
+ }
65210
+ logger.debug(`CC version ${version} supports plugins (>= ${MIN_PLUGIN_VERSION})`);
65211
+ }
65212
+ function isCCPluginSupportError(error) {
65213
+ return error instanceof CCPluginSupportError;
65214
+ }
65215
+ var execFileAsync6, MIN_PLUGIN_VERSION = "1.0.33", CCPluginSupportError;
65216
+ var init_cc_version_checker = __esm(() => {
65217
+ init_logger();
65218
+ execFileAsync6 = promisify14(execFile9);
65219
+ CCPluginSupportError = class CCPluginSupportError extends Error {
65220
+ code;
65221
+ details;
65222
+ constructor(code2, message, details) {
65223
+ super(message);
65224
+ this.code = code2;
65225
+ this.details = details;
65226
+ this.name = "CCPluginSupportError";
65227
+ }
65228
+ };
65229
+ });
65230
+
65231
+ // src/services/plugin-installer.ts
65232
+ var exports_plugin_installer = {};
65233
+ __export(exports_plugin_installer, {
65234
+ handlePluginUninstall: () => handlePluginUninstall,
65235
+ handlePluginInstall: () => handlePluginInstall
65236
+ });
65237
+ import { execFile as execFile10 } from "node:child_process";
65238
+ import { rename as rename6 } from "node:fs/promises";
65239
+ import { join as join99 } from "node:path";
65240
+ import { promisify as promisify15 } from "node:util";
65241
+ function getMarketplacePath() {
65242
+ const dataDir = PathResolver.getClaudeKitDir();
65243
+ return join99(dataDir, MARKETPLACE_DIR_NAME);
65244
+ }
65245
+ function escapeRegex2(value) {
65246
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
65247
+ }
65248
+ function outputContainsToken(output2, token) {
65249
+ const tokenPattern = new RegExp(`(^|\\s)${escapeRegex2(token)}(?=\\s|$)`, "i");
65250
+ return output2.split(/\r?\n/).map((line) => line.trim()).some((line) => tokenPattern.test(line));
65251
+ }
65252
+ async function runClaudePlugin(args) {
65253
+ try {
65254
+ const { stdout: stdout2, stderr } = await execFileAsync7("claude", ["plugin", ...args], buildExecOptions(30000));
65255
+ return { success: true, stdout: stdout2.trim(), stderr: stderr.trim() };
65256
+ } catch (error) {
65257
+ const err = error;
65258
+ return {
65259
+ success: false,
65260
+ stdout: (err.stdout ?? "").trim(),
65261
+ stderr: (err.stderr ?? err.message ?? "Unknown error").trim()
65262
+ };
65263
+ }
65264
+ }
65265
+ async function isClaudeAvailable() {
65266
+ try {
65267
+ await execFileAsync7("claude", ["--version"], buildExecOptions(5000));
65268
+ return true;
65269
+ } catch {
65270
+ return false;
65271
+ }
65272
+ }
65273
+ async function copyPluginToMarketplace(extractDir) {
65274
+ const marketplacePath = getMarketplacePath();
65275
+ const sourceMarketplaceJson = join99(extractDir, ".claude-plugin", "marketplace.json");
65276
+ const sourcePluginDir = join99(extractDir, "plugins", PLUGIN_NAME);
65277
+ if (!await import_fs_extra30.pathExists(sourceMarketplaceJson) || !await import_fs_extra30.pathExists(sourcePluginDir)) {
65278
+ logger.debug("Kit release does not contain plugin structure — skipping plugin install");
65279
+ return false;
65280
+ }
65281
+ await import_fs_extra30.ensureDir(join99(marketplacePath, ".claude-plugin"));
65282
+ await import_fs_extra30.ensureDir(join99(marketplacePath, "plugins"));
65283
+ const suffix = `${process.pid}-${Date.now()}`;
65284
+ const destMarketplaceJson = join99(marketplacePath, ".claude-plugin", "marketplace.json");
65285
+ const destPluginDir = join99(marketplacePath, "plugins", PLUGIN_NAME);
65286
+ const tempMarketplaceJson = `${destMarketplaceJson}.tmp-${suffix}`;
65287
+ const tempPluginDir = `${destPluginDir}.tmp-${suffix}`;
65288
+ const backupMarketplaceJson = `${destMarketplaceJson}.bak-${suffix}`;
65289
+ const backupPluginDir = `${destPluginDir}.bak-${suffix}`;
65290
+ await import_fs_extra30.remove(tempMarketplaceJson).catch(() => {});
65291
+ await import_fs_extra30.remove(tempPluginDir).catch(() => {});
65292
+ await import_fs_extra30.remove(backupMarketplaceJson).catch(() => {});
65293
+ await import_fs_extra30.remove(backupPluginDir).catch(() => {});
65294
+ await import_fs_extra30.copy(sourceMarketplaceJson, tempMarketplaceJson, { overwrite: true });
65295
+ await import_fs_extra30.copy(sourcePluginDir, tempPluginDir, { overwrite: true });
65296
+ let backupMarketplaceCreated = false;
65297
+ let backupPluginCreated = false;
65298
+ let marketplaceReplaced = false;
65299
+ let pluginReplaced = false;
65300
+ try {
65301
+ if (await import_fs_extra30.pathExists(destMarketplaceJson)) {
65302
+ await rename6(destMarketplaceJson, backupMarketplaceJson);
65303
+ backupMarketplaceCreated = true;
65304
+ }
65305
+ await rename6(tempMarketplaceJson, destMarketplaceJson);
65306
+ marketplaceReplaced = true;
65307
+ if (await import_fs_extra30.pathExists(destPluginDir)) {
65308
+ await rename6(destPluginDir, backupPluginDir);
65309
+ backupPluginCreated = true;
65310
+ }
65311
+ await rename6(tempPluginDir, destPluginDir);
65312
+ pluginReplaced = true;
65313
+ } catch (error) {
65314
+ if (marketplaceReplaced) {
65315
+ await import_fs_extra30.remove(destMarketplaceJson).catch(() => {});
65316
+ }
65317
+ if (pluginReplaced) {
65318
+ await import_fs_extra30.remove(destPluginDir).catch(() => {});
65319
+ }
65320
+ if (backupMarketplaceCreated) {
65321
+ await rename6(backupMarketplaceJson, destMarketplaceJson).catch(() => {});
65322
+ }
65323
+ if (backupPluginCreated) {
65324
+ await rename6(backupPluginDir, destPluginDir).catch(() => {});
65325
+ }
65326
+ await import_fs_extra30.remove(tempMarketplaceJson).catch(() => {});
65327
+ await import_fs_extra30.remove(tempPluginDir).catch(() => {});
65328
+ throw error;
65329
+ }
65330
+ await import_fs_extra30.remove(backupMarketplaceJson).catch(() => {});
65331
+ await import_fs_extra30.remove(backupPluginDir).catch(() => {});
65332
+ await import_fs_extra30.remove(tempMarketplaceJson).catch(() => {});
65333
+ await import_fs_extra30.remove(tempPluginDir).catch(() => {});
65334
+ logger.debug("Plugin copied to marketplace");
65335
+ return true;
65336
+ }
65337
+ async function registerMarketplace() {
65338
+ const marketplacePath = getMarketplacePath();
65339
+ const listResult = await runClaudePlugin(["marketplace", "list"]);
65340
+ const alreadyRegistered = listResult.success && outputContainsToken(listResult.stdout, MARKETPLACE_NAME);
65341
+ if (alreadyRegistered) {
65342
+ const addResult = await runClaudePlugin(["marketplace", "add", marketplacePath]);
65343
+ if (addResult.success) {
65344
+ logger.debug("Marketplace re-registered successfully");
65345
+ return true;
65346
+ }
65347
+ logger.debug("Marketplace add failed while registered; removing stale entry and retrying");
65348
+ const removeResult = await runClaudePlugin(["marketplace", "remove", MARKETPLACE_NAME]);
65349
+ if (!removeResult.success) {
65350
+ logger.debug(`Marketplace remove failed: ${removeResult.stderr}`);
65351
+ return false;
65352
+ }
65353
+ const retryResult = await runClaudePlugin(["marketplace", "add", marketplacePath]);
65354
+ if (!retryResult.success) {
65355
+ logger.warning(`Marketplace remove succeeded but retry-add failed: ${retryResult.stderr}`);
65356
+ return false;
65357
+ }
65358
+ logger.debug("Marketplace re-registered after remove+retry");
65359
+ return true;
65360
+ }
65361
+ const result = await runClaudePlugin(["marketplace", "add", marketplacePath]);
65362
+ if (!result.success) {
65363
+ logger.debug(`Marketplace registration failed: ${result.stderr}`);
65364
+ return false;
65365
+ }
65366
+ logger.debug("Marketplace registered successfully");
65367
+ return true;
65368
+ }
65369
+ async function installOrUpdatePlugin() {
65370
+ const pluginRef = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
65371
+ const listResult = await runClaudePlugin(["list"]);
65372
+ const isInstalled = listResult.success && outputContainsToken(listResult.stdout, pluginRef);
65373
+ if (isInstalled) {
65374
+ const result2 = await runClaudePlugin(["update", pluginRef]);
65375
+ if (result2.success) {
65376
+ logger.debug("Plugin updated successfully");
65377
+ return true;
65378
+ }
65379
+ logger.debug(`Plugin update failed (${result2.stderr}); re-verifying install state`);
65380
+ const stillInstalled = await verifyPluginInstalled();
65381
+ if (stillInstalled) {
65382
+ logger.debug("Plugin update failed but plugin is still installed — treating as success");
65383
+ return true;
65384
+ }
65385
+ logger.debug("Plugin update failed and plugin is no longer listed");
65386
+ return false;
65387
+ }
65388
+ const result = await runClaudePlugin(["install", pluginRef]);
65389
+ if (!result.success) {
65390
+ logger.debug(`Plugin install failed: ${result.stderr}`);
65391
+ return false;
65392
+ }
65393
+ logger.debug("Plugin installed successfully");
65394
+ return true;
65395
+ }
65396
+ async function verifyPluginInstalled() {
65397
+ const pluginRef = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
65398
+ const result = await runClaudePlugin(["list"]);
65399
+ if (!result.success)
65400
+ return false;
65401
+ return outputContainsToken(result.stdout, pluginRef);
65402
+ }
65403
+ async function handlePluginInstall(extractDir) {
65404
+ if (!await isClaudeAvailable()) {
65405
+ return {
65406
+ installed: false,
65407
+ marketplaceRegistered: false,
65408
+ verified: false,
65409
+ error: "Claude Code CLI not found on PATH"
65410
+ };
65411
+ }
65412
+ logger.debug("Registering CK plugin with Claude Code...");
65413
+ let copied = false;
65414
+ try {
65415
+ copied = await copyPluginToMarketplace(extractDir);
65416
+ } catch (error) {
65417
+ return {
65418
+ installed: false,
65419
+ marketplaceRegistered: false,
65420
+ verified: false,
65421
+ error: `Plugin copy failed: ${error instanceof Error ? error.message : "Unknown error"}`
65422
+ };
65423
+ }
65424
+ if (!copied) {
65425
+ return {
65426
+ installed: false,
65427
+ marketplaceRegistered: false,
65428
+ verified: false,
65429
+ error: "No plugin found in kit release"
65430
+ };
65431
+ }
65432
+ const registered = await registerMarketplace();
65433
+ if (!registered) {
65434
+ return {
65435
+ installed: false,
65436
+ marketplaceRegistered: false,
65437
+ verified: false,
65438
+ error: "Marketplace registration failed"
65439
+ };
65440
+ }
65441
+ const installOk = await installOrUpdatePlugin();
65442
+ if (!installOk) {
65443
+ return {
65444
+ installed: false,
65445
+ marketplaceRegistered: true,
65446
+ verified: false,
65447
+ error: "Plugin install/update failed"
65448
+ };
65449
+ }
65450
+ const verified = await verifyPluginInstalled();
65451
+ if (verified) {
65452
+ logger.success("CK plugin installed — skills available as /ck:* commands");
65453
+ } else {
65454
+ logger.warning("Plugin installed but verification failed — skills may not be available");
65455
+ }
65456
+ return {
65457
+ installed: true,
65458
+ marketplaceRegistered: true,
65459
+ verified,
65460
+ error: verified ? undefined : "Post-install verification failed"
65461
+ };
65462
+ }
65463
+ async function handlePluginUninstall() {
65464
+ if (!await isClaudeAvailable()) {
65465
+ logger.debug("Claude Code CLI not found — skipping plugin cleanup");
65466
+ return;
65467
+ }
65468
+ const isInstalled = await verifyPluginInstalled();
65469
+ if (isInstalled) {
65470
+ const pluginRef = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
65471
+ const uninstallResult = await runClaudePlugin(["uninstall", pluginRef]);
65472
+ if (uninstallResult.success) {
65473
+ logger.debug("CK plugin uninstalled from Claude Code");
65474
+ } else {
65475
+ logger.debug(`Plugin uninstall failed: ${uninstallResult.stderr}`);
65476
+ }
65477
+ }
65478
+ const marketplaceRemoveResult = await runClaudePlugin([
65479
+ "marketplace",
65480
+ "remove",
65481
+ MARKETPLACE_NAME
65482
+ ]);
65483
+ if (!marketplaceRemoveResult.success) {
65484
+ logger.debug(`Marketplace remove failed: ${marketplaceRemoveResult.stderr}`);
65485
+ }
65486
+ const marketplacePath = getMarketplacePath();
65487
+ if (await import_fs_extra30.pathExists(marketplacePath)) {
65488
+ try {
65489
+ await import_fs_extra30.remove(marketplacePath);
65490
+ logger.debug("Marketplace directory cleaned up");
65491
+ } catch (error) {
65492
+ logger.warning(`Failed to clean marketplace directory: ${error instanceof Error ? error.message : String(error)}`);
65493
+ }
65494
+ }
65495
+ }
65496
+ var import_fs_extra30, execFileAsync7, MARKETPLACE_DIR_NAME = "marketplace", PLUGIN_NAME = "ck", MARKETPLACE_NAME = "claudekit";
65497
+ var init_plugin_installer = __esm(() => {
65498
+ init_logger();
65499
+ init_path_resolver();
65500
+ import_fs_extra30 = __toESM(require_lib3(), 1);
65501
+ execFileAsync7 = promisify15(execFile10);
65502
+ });
65503
+
65504
+ // src/services/skill-migration-merger.ts
65505
+ var exports_skill_migration_merger = {};
65506
+ __export(exports_skill_migration_merger, {
65507
+ migrateUserSkills: () => migrateUserSkills
65508
+ });
65509
+ import { readFile as readFile47 } from "node:fs/promises";
65510
+ import { join as join100 } from "node:path";
65511
+ async function migrateUserSkills(claudeDir2, pluginVerified) {
65512
+ const result = {
65513
+ preserved: [],
65514
+ deleted: [],
65515
+ userOwned: [],
65516
+ canDelete: false
65517
+ };
65518
+ if (!pluginVerified) {
65519
+ return result;
65520
+ }
65521
+ const metadataPath = join100(claudeDir2, "metadata.json");
65522
+ if (!await import_fs_extra31.pathExists(metadataPath)) {
65523
+ return result;
65524
+ }
65525
+ let trackedFiles;
65526
+ try {
65527
+ const content = await readFile47(metadataPath, "utf-8");
65528
+ const metadata = JSON.parse(content);
65529
+ trackedFiles = extractTrackedSkillFiles(metadata);
65530
+ } catch {
65531
+ logger.debug("Could not read metadata for skill migration");
65532
+ return result;
65533
+ }
65534
+ if (trackedFiles.length === 0) {
65535
+ return result;
65536
+ }
65537
+ result.canDelete = true;
65538
+ const preservedSet = new Set;
65539
+ const deletedSet = new Set;
65540
+ const userOwnedSet = new Set;
65541
+ for (const file of trackedFiles) {
65542
+ const skillDir = extractSkillDir(file.path);
65543
+ if (!skillDir)
65544
+ continue;
65545
+ switch (file.ownership) {
65546
+ case "user":
65547
+ userOwnedSet.add(skillDir);
65548
+ break;
65549
+ case "ck-modified":
65550
+ preservedSet.add(skillDir);
65551
+ break;
65552
+ case "ck":
65553
+ deletedSet.add(skillDir);
65554
+ break;
65555
+ }
65556
+ }
65557
+ for (const dir of preservedSet) {
65558
+ deletedSet.delete(dir);
65559
+ }
65560
+ result.preserved = [...preservedSet];
65561
+ result.deleted = [...deletedSet];
65562
+ result.userOwned = [...userOwnedSet];
65563
+ for (const dir of result.preserved) {
65564
+ const skillName = dir.split("/")[1];
65565
+ logger.info(`Preserved customizations: /${skillName} (standalone alongside /ck:${skillName})`);
65566
+ }
65567
+ return result;
65568
+ }
65569
+ function extractTrackedSkillFiles(metadata) {
65570
+ const files = [];
65571
+ if (metadata.kits && typeof metadata.kits === "object") {
65572
+ for (const kit of Object.values(metadata.kits)) {
65573
+ if (kit.files) {
65574
+ files.push(...kit.files.filter((f3) => isSkillPath(f3.path)));
65575
+ }
65576
+ }
65577
+ }
65578
+ if (Array.isArray(metadata.files)) {
65579
+ files.push(...metadata.files.filter((f3) => isSkillPath(f3.path)));
65580
+ }
65581
+ return files;
65582
+ }
65583
+ function isSkillPath(path14) {
65584
+ const normalized = path14.replace(/\\/g, "/");
65585
+ return normalized.startsWith("skills/");
65586
+ }
65587
+ function extractSkillDir(path14) {
65588
+ const normalized = path14.replace(/\\/g, "/");
65589
+ const parts = normalized.split("/").filter(Boolean);
65590
+ if (parts.length < 2 || parts[0] !== "skills") {
65591
+ return null;
65592
+ }
65593
+ return `skills/${parts[1]}`;
65594
+ }
65595
+ var import_fs_extra31;
65596
+ var init_skill_migration_merger = __esm(() => {
65597
+ init_logger();
65598
+ import_fs_extra31 = __toESM(require_lib3(), 1);
65599
+ });
65600
+
64668
65601
  // src/domains/help/commands/common-options.ts
64669
65602
  var filterOptionsGroup, folderOptionsGroup;
64670
65603
  var init_common_options = __esm(() => {
@@ -65782,7 +66715,7 @@ function getPagerArgs(pagerCmd) {
65782
66715
  return [];
65783
66716
  }
65784
66717
  async function trySystemPager(content) {
65785
- return new Promise((resolve26) => {
66718
+ return new Promise((resolve27) => {
65786
66719
  const pagerCmd = process.env.PAGER || "less";
65787
66720
  const pagerArgs = getPagerArgs(pagerCmd);
65788
66721
  try {
@@ -65792,20 +66725,20 @@ async function trySystemPager(content) {
65792
66725
  });
65793
66726
  const timeout2 = setTimeout(() => {
65794
66727
  pager.kill();
65795
- resolve26(false);
66728
+ resolve27(false);
65796
66729
  }, 30000);
65797
66730
  pager.stdin.write(content);
65798
66731
  pager.stdin.end();
65799
66732
  pager.on("close", (code2) => {
65800
66733
  clearTimeout(timeout2);
65801
- resolve26(code2 === 0);
66734
+ resolve27(code2 === 0);
65802
66735
  });
65803
66736
  pager.on("error", () => {
65804
66737
  clearTimeout(timeout2);
65805
- resolve26(false);
66738
+ resolve27(false);
65806
66739
  });
65807
66740
  } catch {
65808
- resolve26(false);
66741
+ resolve27(false);
65809
66742
  }
65810
66743
  });
65811
66744
  }
@@ -65832,16 +66765,16 @@ async function basicPager(content) {
65832
66765
  break;
65833
66766
  }
65834
66767
  const remaining = lines.length - currentLine;
65835
- await new Promise((resolve26) => {
66768
+ await new Promise((resolve27) => {
65836
66769
  rl.question(`-- More (${remaining} lines) [Enter/q] --`, (answer) => {
65837
66770
  if (answer.toLowerCase() === "q") {
65838
66771
  rl.close();
65839
66772
  process.exitCode = 0;
65840
- resolve26();
66773
+ resolve27();
65841
66774
  return;
65842
66775
  }
65843
66776
  process.stdout.write("\x1B[1A\x1B[2K");
65844
- resolve26();
66777
+ resolve27();
65845
66778
  });
65846
66779
  });
65847
66780
  }
@@ -72514,7 +73447,7 @@ class ConfigVersionChecker {
72514
73447
  }
72515
73448
  // src/domains/sync/sync-engine.ts
72516
73449
  import { lstat as lstat3, readFile as readFile34, readlink, realpath as realpath3, stat as stat9 } from "node:fs/promises";
72517
- import { isAbsolute as isAbsolute3, join as join58, normalize as normalize7, relative as relative7 } from "node:path";
73450
+ import { dirname as dirname15, isAbsolute as isAbsolute3, normalize as normalize7, relative as relative7, resolve as resolve13 } from "node:path";
72518
73451
 
72519
73452
  // src/services/file-operations/ownership-checker.ts
72520
73453
  init_metadata_migration();
@@ -73875,7 +74808,7 @@ async function validateSymlinkChain(path5, basePath, maxDepth = MAX_SYMLINK_DEPT
73875
74808
  if (!stats.isSymbolicLink())
73876
74809
  break;
73877
74810
  const target = await readlink(current);
73878
- const resolvedTarget = isAbsolute3(target) ? target : join58(current, "..", target);
74811
+ const resolvedTarget = isAbsolute3(target) ? target : resolve13(dirname15(current), target);
73879
74812
  const normalizedTarget = normalize7(resolvedTarget);
73880
74813
  const rel = relative7(basePath, normalizedTarget);
73881
74814
  if (rel.startsWith("..") || isAbsolute3(rel)) {
@@ -73894,6 +74827,10 @@ async function validateSymlinkChain(path5, basePath, maxDepth = MAX_SYMLINK_DEPT
73894
74827
  throw new Error(`Symlink chain too deep (>${maxDepth}): ${path5}`);
73895
74828
  }
73896
74829
  }
74830
+ function isOutsideBase(candidatePath, basePath) {
74831
+ const rel = relative7(basePath, candidatePath);
74832
+ return rel.startsWith("..") || isAbsolute3(rel);
74833
+ }
73897
74834
  async function validateSyncPath(basePath, filePath) {
73898
74835
  if (!filePath || filePath.trim() === "") {
73899
74836
  throw new Error("Empty file path not allowed");
@@ -73908,37 +74845,44 @@ async function validateSyncPath(basePath, filePath) {
73908
74845
  if (isAbsolute3(normalized)) {
73909
74846
  throw new Error(`Absolute paths not allowed: ${filePath}`);
73910
74847
  }
73911
- if (normalized.startsWith("..") || normalized.includes("/../")) {
74848
+ const pathParts = normalized.split(/[\\/]+/).filter(Boolean);
74849
+ if (pathParts.includes("..")) {
73912
74850
  throw new Error(`Path traversal not allowed: ${filePath}`);
73913
74851
  }
73914
- const fullPath = join58(basePath, normalized);
73915
- const rel = relative7(basePath, fullPath);
73916
- if (rel.startsWith("..") || isAbsolute3(rel)) {
74852
+ const lexicalBase = resolve13(basePath);
74853
+ const fullPath = resolve13(basePath, normalized);
74854
+ const resolvedBase = await realpath3(basePath).catch(() => lexicalBase);
74855
+ if (isOutsideBase(fullPath, lexicalBase)) {
73917
74856
  throw new Error(`Path escapes base directory: ${filePath}`);
73918
74857
  }
73919
- await validateSymlinkChain(fullPath, basePath);
74858
+ await validateSymlinkChain(fullPath, resolvedBase);
73920
74859
  try {
73921
- const resolvedBase = await realpath3(basePath);
73922
74860
  const resolvedFull = await realpath3(fullPath);
73923
- const resolvedRel = relative7(resolvedBase, resolvedFull);
73924
- if (resolvedRel.startsWith("..") || isAbsolute3(resolvedRel)) {
74861
+ if (isOutsideBase(resolvedFull, resolvedBase)) {
73925
74862
  throw new Error(`Symlink escapes base directory: ${filePath}`);
73926
74863
  }
73927
74864
  } catch (error) {
73928
74865
  if (error.code === "ENOENT") {
73929
- const parentPath = join58(fullPath, "..");
73930
- try {
73931
- const resolvedBase = await realpath3(basePath);
73932
- const resolvedParent = await realpath3(parentPath);
73933
- const resolvedRel = relative7(resolvedBase, resolvedParent);
73934
- if (resolvedRel.startsWith("..") || isAbsolute3(resolvedRel)) {
73935
- throw new Error(`Parent symlink escapes base directory: ${filePath}`);
73936
- }
73937
- } catch (parentError) {
73938
- if (parentError.code !== "ENOENT") {
73939
- throw parentError;
74866
+ let ancestor = dirname15(fullPath);
74867
+ while (ancestor !== lexicalBase) {
74868
+ try {
74869
+ await lstat3(ancestor);
74870
+ break;
74871
+ } catch (ancestorError) {
74872
+ if (ancestorError.code !== "ENOENT") {
74873
+ throw ancestorError;
74874
+ }
74875
+ const nextAncestor = dirname15(ancestor);
74876
+ if (nextAncestor === ancestor) {
74877
+ break;
74878
+ }
74879
+ ancestor = nextAncestor;
73940
74880
  }
73941
74881
  }
74882
+ const resolvedAncestor = await realpath3(ancestor).catch(() => null);
74883
+ if (!resolvedAncestor || isOutsideBase(resolvedAncestor, resolvedBase)) {
74884
+ throw new Error(`Parent symlink escapes base directory: ${filePath}`);
74885
+ }
73942
74886
  } else {
73943
74887
  throw error;
73944
74888
  }
@@ -74090,14 +75034,24 @@ class SyncEngine {
74090
75034
  }
74091
75035
  static async loadFileContent(filePath) {
74092
75036
  try {
74093
- const lstats = await lstat3(filePath);
74094
- if (lstats.isSymbolicLink()) {
75037
+ const beforeStats = await lstat3(filePath);
75038
+ if (beforeStats.isSymbolicLink()) {
74095
75039
  throw new Error(`Symlink not allowed for sync: ${filePath}`);
74096
75040
  }
74097
- if (lstats.size > MAX_SYNC_FILE_SIZE) {
74098
- throw new Error(`File too large for sync (${Math.round(lstats.size / 1024 / 1024)}MB > ${MAX_SYNC_FILE_SIZE / 1024 / 1024}MB limit)`);
75041
+ if (beforeStats.size > MAX_SYNC_FILE_SIZE) {
75042
+ throw new Error(`File too large for sync (${Math.round(beforeStats.size / 1024 / 1024)}MB > ${MAX_SYNC_FILE_SIZE / 1024 / 1024}MB limit)`);
74099
75043
  }
74100
75044
  const buffer = await readFile34(filePath);
75045
+ const afterStats = await lstat3(filePath);
75046
+ if (afterStats.isSymbolicLink()) {
75047
+ throw new Error(`File became symlink during read: ${filePath}`);
75048
+ }
75049
+ if (beforeStats.dev !== afterStats.dev || beforeStats.ino !== afterStats.ino) {
75050
+ throw new Error(`File changed identity during read: ${filePath}`);
75051
+ }
75052
+ if (beforeStats.mtimeMs !== afterStats.mtimeMs || beforeStats.size !== afterStats.size) {
75053
+ throw new Error(`File changed during read: ${filePath}`);
75054
+ }
74101
75055
  if (buffer.includes(0)) {
74102
75056
  return { content: "", isBinary: true };
74103
75057
  }
@@ -75108,7 +76062,7 @@ init_logger();
75108
76062
  var import_proper_lockfile4 = __toESM(require_proper_lockfile(), 1);
75109
76063
  import { mkdir as mkdir19 } from "node:fs/promises";
75110
76064
  import os5 from "node:os";
75111
- import { join as join65 } from "node:path";
76065
+ import { join as join64 } from "node:path";
75112
76066
  var LOCK_CONFIG = {
75113
76067
  stale: 60000,
75114
76068
  retries: 0
@@ -75116,12 +76070,12 @@ var LOCK_CONFIG = {
75116
76070
  var activeLocks = new Set;
75117
76071
  var cleanupRegistered = false;
75118
76072
  function getLocksDir() {
75119
- return join65(os5.homedir(), ".claudekit", "locks");
76073
+ return join64(os5.homedir(), ".claudekit", "locks");
75120
76074
  }
75121
76075
  function cleanupLocks() {
75122
76076
  for (const name of activeLocks) {
75123
76077
  try {
75124
- const lockPath = join65(getLocksDir(), `${name}.lock`);
76078
+ const lockPath = join64(getLocksDir(), `${name}.lock`);
75125
76079
  import_proper_lockfile4.default.unlockSync(lockPath, { realpath: false });
75126
76080
  } catch {
75127
76081
  try {
@@ -75144,7 +76098,7 @@ async function ensureLocksDir() {
75144
76098
  async function withProcessLock(lockName, fn) {
75145
76099
  registerCleanupHandlers();
75146
76100
  await ensureLocksDir();
75147
- const lockPath = join65(getLocksDir(), `${lockName}.lock`);
76101
+ const lockPath = join64(getLocksDir(), `${lockName}.lock`);
75148
76102
  let release;
75149
76103
  try {
75150
76104
  release = await import_proper_lockfile4.default.lock(lockPath, { ...LOCK_CONFIG, realpath: false });
@@ -75175,7 +76129,7 @@ init_logger();
75175
76129
  init_logger();
75176
76130
  init_path_resolver();
75177
76131
  var import_fs_extra7 = __toESM(require_lib3(), 1);
75178
- import { join as join66 } from "node:path";
76132
+ import { join as join65 } from "node:path";
75179
76133
  async function handleConflicts(ctx) {
75180
76134
  if (ctx.cancelled)
75181
76135
  return ctx;
@@ -75184,7 +76138,7 @@ async function handleConflicts(ctx) {
75184
76138
  if (PathResolver.isLocalSameAsGlobal()) {
75185
76139
  return ctx;
75186
76140
  }
75187
- const localSettingsPath = join66(process.cwd(), ".claude", "settings.json");
76141
+ const localSettingsPath = join65(process.cwd(), ".claude", "settings.json");
75188
76142
  if (!await import_fs_extra7.pathExists(localSettingsPath)) {
75189
76143
  return ctx;
75190
76144
  }
@@ -75199,7 +76153,7 @@ async function handleConflicts(ctx) {
75199
76153
  return { ...ctx, cancelled: true };
75200
76154
  }
75201
76155
  if (choice === "remove") {
75202
- const localClaudeDir = join66(process.cwd(), ".claude");
76156
+ const localClaudeDir = join65(process.cwd(), ".claude");
75203
76157
  try {
75204
76158
  await import_fs_extra7.remove(localClaudeDir);
75205
76159
  logger.success("Removed local .claude/ directory");
@@ -75296,7 +76250,7 @@ init_logger();
75296
76250
  init_safe_spinner();
75297
76251
  import { mkdir as mkdir25, stat as stat12 } from "node:fs/promises";
75298
76252
  import { tmpdir as tmpdir4 } from "node:os";
75299
- import { join as join73 } from "node:path";
76253
+ import { join as join72 } from "node:path";
75300
76254
 
75301
76255
  // src/shared/temp-cleanup.ts
75302
76256
  init_logger();
@@ -75315,7 +76269,7 @@ init_logger();
75315
76269
  init_output_manager();
75316
76270
  import { createWriteStream as createWriteStream2, rmSync } from "node:fs";
75317
76271
  import { mkdir as mkdir20 } from "node:fs/promises";
75318
- import { join as join67 } from "node:path";
76272
+ import { join as join66 } from "node:path";
75319
76273
 
75320
76274
  // src/shared/progress-bar.ts
75321
76275
  init_output_manager();
@@ -75480,10 +76434,10 @@ init_types3();
75480
76434
  // src/domains/installation/utils/path-security.ts
75481
76435
  init_types3();
75482
76436
  import { lstatSync as lstatSync2, realpathSync as realpathSync2 } from "node:fs";
75483
- import { relative as relative8, resolve as resolve15 } from "node:path";
76437
+ import { relative as relative8, resolve as resolve16 } from "node:path";
75484
76438
  var MAX_EXTRACTION_SIZE = 500 * 1024 * 1024;
75485
76439
  function isPathSafe(basePath, targetPath) {
75486
- const resolvedBase = resolve15(basePath);
76440
+ const resolvedBase = resolve16(basePath);
75487
76441
  try {
75488
76442
  const stat10 = lstatSync2(targetPath);
75489
76443
  if (stat10.isSymbolicLink()) {
@@ -75493,7 +76447,7 @@ function isPathSafe(basePath, targetPath) {
75493
76447
  }
75494
76448
  }
75495
76449
  } catch {}
75496
- const resolvedTarget = resolve15(targetPath);
76450
+ const resolvedTarget = resolve16(targetPath);
75497
76451
  const relativePath = relative8(resolvedBase, resolvedTarget);
75498
76452
  return !relativePath.startsWith("..") && !relativePath.startsWith("/") && resolvedTarget.startsWith(resolvedBase);
75499
76453
  }
@@ -75525,7 +76479,7 @@ var MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024;
75525
76479
  class FileDownloader {
75526
76480
  async downloadAsset(asset, destDir) {
75527
76481
  try {
75528
- const destPath = join67(destDir, asset.name);
76482
+ const destPath = join66(destDir, asset.name);
75529
76483
  await mkdir20(destDir, { recursive: true });
75530
76484
  output.info(`Downloading ${asset.name} (${formatBytes(asset.size)})...`);
75531
76485
  logger.verbose("Download details", {
@@ -75581,7 +76535,7 @@ class FileDownloader {
75581
76535
  }
75582
76536
  if (downloadedSize !== totalSize) {
75583
76537
  fileStream.end();
75584
- await new Promise((resolve16) => fileStream.once("close", resolve16));
76538
+ await new Promise((resolve17) => fileStream.once("close", resolve17));
75585
76539
  try {
75586
76540
  rmSync(destPath, { force: true });
75587
76541
  } catch (cleanupError) {
@@ -75595,7 +76549,7 @@ class FileDownloader {
75595
76549
  return destPath;
75596
76550
  } catch (error) {
75597
76551
  fileStream.end();
75598
- await new Promise((resolve16) => fileStream.once("close", resolve16));
76552
+ await new Promise((resolve17) => fileStream.once("close", resolve17));
75599
76553
  try {
75600
76554
  rmSync(destPath, { force: true });
75601
76555
  } catch (cleanupError) {
@@ -75610,7 +76564,7 @@ class FileDownloader {
75610
76564
  }
75611
76565
  async downloadFile(params) {
75612
76566
  const { url, name, size, destDir, token } = params;
75613
- const destPath = join67(destDir, name);
76567
+ const destPath = join66(destDir, name);
75614
76568
  await mkdir20(destDir, { recursive: true });
75615
76569
  output.info(`Downloading ${name}${size ? ` (${formatBytes(size)})` : ""}...`);
75616
76570
  const headers = {};
@@ -75661,7 +76615,7 @@ class FileDownloader {
75661
76615
  const expectedSize = Number(response.headers.get("content-length"));
75662
76616
  if (expectedSize > 0 && downloadedSize !== expectedSize) {
75663
76617
  fileStream.end();
75664
- await new Promise((resolve16) => fileStream.once("close", resolve16));
76618
+ await new Promise((resolve17) => fileStream.once("close", resolve17));
75665
76619
  try {
75666
76620
  rmSync(destPath, { force: true });
75667
76621
  } catch (cleanupError) {
@@ -75679,7 +76633,7 @@ class FileDownloader {
75679
76633
  return destPath;
75680
76634
  } catch (error) {
75681
76635
  fileStream.end();
75682
- await new Promise((resolve16) => fileStream.once("close", resolve16));
76636
+ await new Promise((resolve17) => fileStream.once("close", resolve17));
75683
76637
  try {
75684
76638
  rmSync(destPath, { force: true });
75685
76639
  } catch (cleanupError) {
@@ -75696,7 +76650,7 @@ init_logger();
75696
76650
  init_types3();
75697
76651
  import { constants as constants4 } from "node:fs";
75698
76652
  import { access as access4, readdir as readdir13 } from "node:fs/promises";
75699
- import { join as join68 } from "node:path";
76653
+ import { join as join67 } from "node:path";
75700
76654
  async function validateExtraction(extractDir) {
75701
76655
  try {
75702
76656
  const entries = await readdir13(extractDir, { encoding: "utf8" });
@@ -75708,7 +76662,7 @@ async function validateExtraction(extractDir) {
75708
76662
  const missingPaths = [];
75709
76663
  for (const path5 of criticalPaths) {
75710
76664
  try {
75711
- await access4(join68(extractDir, path5), constants4.F_OK);
76665
+ await access4(join67(extractDir, path5), constants4.F_OK);
75712
76666
  logger.debug(`Found: ${path5}`);
75713
76667
  } catch {
75714
76668
  logger.warning(`Expected path not found: ${path5}`);
@@ -75730,7 +76684,7 @@ async function validateExtraction(extractDir) {
75730
76684
  // src/domains/installation/extraction/tar-extractor.ts
75731
76685
  init_logger();
75732
76686
  import { copyFile as copyFile4, mkdir as mkdir23, readdir as readdir15, rm as rm8, stat as stat10 } from "node:fs/promises";
75733
- import { join as join71 } from "node:path";
76687
+ import { join as join70 } from "node:path";
75734
76688
 
75735
76689
  // node_modules/@isaacs/fs-minipass/dist/esm/index.js
75736
76690
  import EE from "events";
@@ -76278,10 +77232,10 @@ class Minipass extends EventEmitter3 {
76278
77232
  return this[ENCODING] ? buf.join("") : Buffer.concat(buf, buf.dataLength);
76279
77233
  }
76280
77234
  async promise() {
76281
- return new Promise((resolve16, reject) => {
77235
+ return new Promise((resolve17, reject) => {
76282
77236
  this.on(DESTROYED, () => reject(new Error("stream destroyed")));
76283
77237
  this.on("error", (er) => reject(er));
76284
- this.on("end", () => resolve16());
77238
+ this.on("end", () => resolve17());
76285
77239
  });
76286
77240
  }
76287
77241
  [Symbol.asyncIterator]() {
@@ -76300,7 +77254,7 @@ class Minipass extends EventEmitter3 {
76300
77254
  return Promise.resolve({ done: false, value: res });
76301
77255
  if (this[EOF])
76302
77256
  return stop();
76303
- let resolve16;
77257
+ let resolve17;
76304
77258
  let reject;
76305
77259
  const onerr = (er) => {
76306
77260
  this.off("data", ondata);
@@ -76314,19 +77268,19 @@ class Minipass extends EventEmitter3 {
76314
77268
  this.off("end", onend);
76315
77269
  this.off(DESTROYED, ondestroy);
76316
77270
  this.pause();
76317
- resolve16({ value, done: !!this[EOF] });
77271
+ resolve17({ value, done: !!this[EOF] });
76318
77272
  };
76319
77273
  const onend = () => {
76320
77274
  this.off("error", onerr);
76321
77275
  this.off("data", ondata);
76322
77276
  this.off(DESTROYED, ondestroy);
76323
77277
  stop();
76324
- resolve16({ done: true, value: undefined });
77278
+ resolve17({ done: true, value: undefined });
76325
77279
  };
76326
77280
  const ondestroy = () => onerr(new Error("stream destroyed"));
76327
77281
  return new Promise((res2, rej) => {
76328
77282
  reject = rej;
76329
- resolve16 = res2;
77283
+ resolve17 = res2;
76330
77284
  this.once(DESTROYED, ondestroy);
76331
77285
  this.once("error", onerr);
76332
77286
  this.once("end", onend);
@@ -76783,7 +77737,7 @@ import path7 from "node:path";
76783
77737
 
76784
77738
  // node_modules/tar/dist/esm/list.js
76785
77739
  import fs9 from "node:fs";
76786
- import { dirname as dirname17, parse as parse3 } from "path";
77740
+ import { dirname as dirname18, parse as parse3 } from "path";
76787
77741
 
76788
77742
  // node_modules/tar/dist/esm/options.js
76789
77743
  var argmap = new Map([
@@ -77432,10 +78386,10 @@ class Minipass2 extends EventEmitter4 {
77432
78386
  return this[ENCODING2] ? buf.join("") : Buffer.concat(buf, buf.dataLength);
77433
78387
  }
77434
78388
  async promise() {
77435
- return new Promise((resolve16, reject) => {
78389
+ return new Promise((resolve17, reject) => {
77436
78390
  this.on(DESTROYED2, () => reject(new Error("stream destroyed")));
77437
78391
  this.on("error", (er) => reject(er));
77438
- this.on("end", () => resolve16());
78392
+ this.on("end", () => resolve17());
77439
78393
  });
77440
78394
  }
77441
78395
  [Symbol.asyncIterator]() {
@@ -77454,7 +78408,7 @@ class Minipass2 extends EventEmitter4 {
77454
78408
  return Promise.resolve({ done: false, value: res });
77455
78409
  if (this[EOF2])
77456
78410
  return stop();
77457
- let resolve16;
78411
+ let resolve17;
77458
78412
  let reject;
77459
78413
  const onerr = (er) => {
77460
78414
  this.off("data", ondata);
@@ -77468,19 +78422,19 @@ class Minipass2 extends EventEmitter4 {
77468
78422
  this.off("end", onend);
77469
78423
  this.off(DESTROYED2, ondestroy);
77470
78424
  this.pause();
77471
- resolve16({ value, done: !!this[EOF2] });
78425
+ resolve17({ value, done: !!this[EOF2] });
77472
78426
  };
77473
78427
  const onend = () => {
77474
78428
  this.off("error", onerr);
77475
78429
  this.off("data", ondata);
77476
78430
  this.off(DESTROYED2, ondestroy);
77477
78431
  stop();
77478
- resolve16({ done: true, value: undefined });
78432
+ resolve17({ done: true, value: undefined });
77479
78433
  };
77480
78434
  const ondestroy = () => onerr(new Error("stream destroyed"));
77481
78435
  return new Promise((res2, rej) => {
77482
78436
  reject = rej;
77483
- resolve16 = res2;
78437
+ resolve17 = res2;
77484
78438
  this.once(DESTROYED2, ondestroy);
77485
78439
  this.once("error", onerr);
77486
78440
  this.once("end", onend);
@@ -78908,10 +79862,10 @@ class Minipass3 extends EventEmitter5 {
78908
79862
  return this[ENCODING3] ? buf.join("") : Buffer.concat(buf, buf.dataLength);
78909
79863
  }
78910
79864
  async promise() {
78911
- return new Promise((resolve16, reject) => {
79865
+ return new Promise((resolve17, reject) => {
78912
79866
  this.on(DESTROYED3, () => reject(new Error("stream destroyed")));
78913
79867
  this.on("error", (er) => reject(er));
78914
- this.on("end", () => resolve16());
79868
+ this.on("end", () => resolve17());
78915
79869
  });
78916
79870
  }
78917
79871
  [Symbol.asyncIterator]() {
@@ -78930,7 +79884,7 @@ class Minipass3 extends EventEmitter5 {
78930
79884
  return Promise.resolve({ done: false, value: res });
78931
79885
  if (this[EOF3])
78932
79886
  return stop();
78933
- let resolve16;
79887
+ let resolve17;
78934
79888
  let reject;
78935
79889
  const onerr = (er) => {
78936
79890
  this.off("data", ondata);
@@ -78944,19 +79898,19 @@ class Minipass3 extends EventEmitter5 {
78944
79898
  this.off("end", onend);
78945
79899
  this.off(DESTROYED3, ondestroy);
78946
79900
  this.pause();
78947
- resolve16({ value, done: !!this[EOF3] });
79901
+ resolve17({ value, done: !!this[EOF3] });
78948
79902
  };
78949
79903
  const onend = () => {
78950
79904
  this.off("error", onerr);
78951
79905
  this.off("data", ondata);
78952
79906
  this.off(DESTROYED3, ondestroy);
78953
79907
  stop();
78954
- resolve16({ done: true, value: undefined });
79908
+ resolve17({ done: true, value: undefined });
78955
79909
  };
78956
79910
  const ondestroy = () => onerr(new Error("stream destroyed"));
78957
79911
  return new Promise((res2, rej) => {
78958
79912
  reject = rej;
78959
- resolve16 = res2;
79913
+ resolve17 = res2;
78960
79914
  this.once(DESTROYED3, ondestroy);
78961
79915
  this.once("error", onerr);
78962
79916
  this.once("end", onend);
@@ -79683,7 +80637,7 @@ var filesFilter = (opt, files) => {
79683
80637
  if (m2 !== undefined) {
79684
80638
  ret = m2;
79685
80639
  } else {
79686
- ret = mapHas(dirname17(file), root);
80640
+ ret = mapHas(dirname18(file), root);
79687
80641
  }
79688
80642
  }
79689
80643
  map.set(file, ret);
@@ -79727,9 +80681,9 @@ var listFile = (opt, _files) => {
79727
80681
  const parse4 = new Parser(opt);
79728
80682
  const readSize = opt.maxReadSize || 16 * 1024 * 1024;
79729
80683
  const file = opt.file;
79730
- const p = new Promise((resolve16, reject) => {
80684
+ const p = new Promise((resolve17, reject) => {
79731
80685
  parse4.on("error", reject);
79732
- parse4.on("end", resolve16);
80686
+ parse4.on("end", resolve17);
79733
80687
  fs9.stat(file, (er, stat10) => {
79734
80688
  if (er) {
79735
80689
  reject(er);
@@ -81492,7 +82446,7 @@ var mkdirSync = (dir, opt) => {
81492
82446
  };
81493
82447
 
81494
82448
  // node_modules/tar/dist/esm/path-reservations.js
81495
- import { join as join69 } from "node:path";
82449
+ import { join as join68 } from "node:path";
81496
82450
 
81497
82451
  // node_modules/tar/dist/esm/normalize-unicode.js
81498
82452
  var normalizeCache = Object.create(null);
@@ -81525,7 +82479,7 @@ var getDirs = (path10) => {
81525
82479
  const dirs = path10.split("/").slice(0, -1).reduce((set, path11) => {
81526
82480
  const s = set[set.length - 1];
81527
82481
  if (s !== undefined) {
81528
- path11 = join69(s, path11);
82482
+ path11 = join68(s, path11);
81529
82483
  }
81530
82484
  set.push(path11 || "/");
81531
82485
  return set;
@@ -81539,7 +82493,7 @@ class PathReservations {
81539
82493
  #running = new Set;
81540
82494
  reserve(paths, fn) {
81541
82495
  paths = isWindows4 ? ["win32 parallelization disabled"] : paths.map((p) => {
81542
- return stripTrailingSlashes(join69(normalizeUnicode(p))).toLowerCase();
82496
+ return stripTrailingSlashes(join68(normalizeUnicode(p))).toLowerCase();
81543
82497
  });
81544
82498
  const dirs = new Set(paths.map((path10) => getDirs(path10)).reduce((a3, b3) => a3.concat(b3)));
81545
82499
  this.#reservations.set(fn, { dirs, paths });
@@ -82309,9 +83263,9 @@ var extractFile = (opt, _3) => {
82309
83263
  const u = new Unpack(opt);
82310
83264
  const readSize = opt.maxReadSize || 16 * 1024 * 1024;
82311
83265
  const file = opt.file;
82312
- const p = new Promise((resolve16, reject) => {
83266
+ const p = new Promise((resolve17, reject) => {
82313
83267
  u.on("error", reject);
82314
- u.on("close", resolve16);
83268
+ u.on("close", resolve17);
82315
83269
  fs16.stat(file, (er, stat10) => {
82316
83270
  if (er) {
82317
83271
  reject(er);
@@ -82444,7 +83398,7 @@ var replaceAsync = (opt, files) => {
82444
83398
  };
82445
83399
  fs17.read(fd, headBuf, 0, 512, position, onread);
82446
83400
  };
82447
- const promise = new Promise((resolve16, reject) => {
83401
+ const promise = new Promise((resolve17, reject) => {
82448
83402
  p.on("error", reject);
82449
83403
  let flag = "r+";
82450
83404
  const onopen = (er, fd) => {
@@ -82469,7 +83423,7 @@ var replaceAsync = (opt, files) => {
82469
83423
  });
82470
83424
  p.pipe(stream);
82471
83425
  stream.on("error", reject);
82472
- stream.on("close", resolve16);
83426
+ stream.on("close", resolve17);
82473
83427
  addFilesAsync2(p, files);
82474
83428
  });
82475
83429
  });
@@ -82599,7 +83553,7 @@ function decodeFilePath(path12) {
82599
83553
  init_logger();
82600
83554
  init_types3();
82601
83555
  import { copyFile as copyFile3, lstat as lstat4, mkdir as mkdir22, readdir as readdir14 } from "node:fs/promises";
82602
- import { join as join70, relative as relative9 } from "node:path";
83556
+ import { join as join69, relative as relative9 } from "node:path";
82603
83557
  async function withRetry(fn, retries = 3) {
82604
83558
  for (let i = 0;i < retries; i++) {
82605
83559
  try {
@@ -82621,8 +83575,8 @@ async function moveDirectoryContents(sourceDir, destDir, shouldExclude, sizeTrac
82621
83575
  await mkdir22(destDir, { recursive: true });
82622
83576
  const entries = await readdir14(sourceDir, { encoding: "utf8" });
82623
83577
  for (const entry of entries) {
82624
- const sourcePath = join70(sourceDir, entry);
82625
- const destPath = join70(destDir, entry);
83578
+ const sourcePath = join69(sourceDir, entry);
83579
+ const destPath = join69(destDir, entry);
82626
83580
  const relativePath = relative9(sourceDir, sourcePath);
82627
83581
  if (!isPathSafe(destDir, destPath)) {
82628
83582
  logger.warning(`Skipping unsafe path: ${relativePath}`);
@@ -82649,8 +83603,8 @@ async function copyDirectory(sourceDir, destDir, shouldExclude, sizeTracker) {
82649
83603
  await mkdir22(destDir, { recursive: true });
82650
83604
  const entries = await readdir14(sourceDir, { encoding: "utf8" });
82651
83605
  for (const entry of entries) {
82652
- const sourcePath = join70(sourceDir, entry);
82653
- const destPath = join70(destDir, entry);
83606
+ const sourcePath = join69(sourceDir, entry);
83607
+ const destPath = join69(destDir, entry);
82654
83608
  const relativePath = relative9(sourceDir, sourcePath);
82655
83609
  if (!isPathSafe(destDir, destPath)) {
82656
83610
  logger.warning(`Skipping unsafe path: ${relativePath}`);
@@ -82698,7 +83652,7 @@ class TarExtractor {
82698
83652
  logger.debug(`Root entries: ${entries.join(", ")}`);
82699
83653
  if (entries.length === 1) {
82700
83654
  const rootEntry = entries[0];
82701
- const rootPath = join71(tempExtractDir, rootEntry);
83655
+ const rootPath = join70(tempExtractDir, rootEntry);
82702
83656
  const rootStat = await stat10(rootPath);
82703
83657
  if (rootStat.isDirectory()) {
82704
83658
  const rootContents = await readdir15(rootPath, { encoding: "utf8" });
@@ -82714,7 +83668,7 @@ class TarExtractor {
82714
83668
  }
82715
83669
  } else {
82716
83670
  await mkdir23(destDir, { recursive: true });
82717
- await copyFile4(rootPath, join71(destDir, rootEntry));
83671
+ await copyFile4(rootPath, join70(destDir, rootEntry));
82718
83672
  }
82719
83673
  } else {
82720
83674
  logger.debug("Multiple root entries - moving all");
@@ -82737,26 +83691,26 @@ init_logger();
82737
83691
  var import_extract_zip = __toESM(require_extract_zip(), 1);
82738
83692
  import { execFile as execFile8 } from "node:child_process";
82739
83693
  import { copyFile as copyFile5, mkdir as mkdir24, readdir as readdir16, rm as rm9, stat as stat11 } from "node:fs/promises";
82740
- import { join as join72 } from "node:path";
83694
+ import { join as join71 } from "node:path";
82741
83695
  class ZipExtractor {
82742
83696
  async tryNativeUnzip(archivePath, destDir) {
82743
83697
  if (!isMacOS()) {
82744
83698
  return false;
82745
83699
  }
82746
- return new Promise((resolve16) => {
83700
+ return new Promise((resolve17) => {
82747
83701
  mkdir24(destDir, { recursive: true }).then(() => {
82748
83702
  execFile8("unzip", ["-o", "-q", archivePath, "-d", destDir], (error, _stdout, stderr) => {
82749
83703
  if (error) {
82750
83704
  logger.debug(`Native unzip failed: ${stderr || error.message}`);
82751
- resolve16(false);
83705
+ resolve17(false);
82752
83706
  return;
82753
83707
  }
82754
83708
  logger.debug("Native unzip succeeded");
82755
- resolve16(true);
83709
+ resolve17(true);
82756
83710
  });
82757
83711
  }).catch((err) => {
82758
83712
  logger.debug(`Failed to create directory for native unzip: ${err.message}`);
82759
- resolve16(false);
83713
+ resolve17(false);
82760
83714
  });
82761
83715
  });
82762
83716
  }
@@ -82785,7 +83739,7 @@ class ZipExtractor {
82785
83739
  logger.debug(`Root entries: ${entries.join(", ")}`);
82786
83740
  if (entries.length === 1) {
82787
83741
  const rootEntry = entries[0];
82788
- const rootPath = join72(tempExtractDir, rootEntry);
83742
+ const rootPath = join71(tempExtractDir, rootEntry);
82789
83743
  const rootStat = await stat11(rootPath);
82790
83744
  if (rootStat.isDirectory()) {
82791
83745
  const rootContents = await readdir16(rootPath, { encoding: "utf8" });
@@ -82801,7 +83755,7 @@ class ZipExtractor {
82801
83755
  }
82802
83756
  } else {
82803
83757
  await mkdir24(destDir, { recursive: true });
82804
- await copyFile5(rootPath, join72(destDir, rootEntry));
83758
+ await copyFile5(rootPath, join71(destDir, rootEntry));
82805
83759
  }
82806
83760
  } else {
82807
83761
  logger.debug("Multiple root entries - moving all");
@@ -82900,7 +83854,7 @@ class DownloadManager {
82900
83854
  async createTempDir() {
82901
83855
  const timestamp = Date.now();
82902
83856
  const counter = DownloadManager.tempDirCounter++;
82903
- const primaryTempDir = join73(tmpdir4(), `claudekit-${timestamp}-${counter}`);
83857
+ const primaryTempDir = join72(tmpdir4(), `claudekit-${timestamp}-${counter}`);
82904
83858
  try {
82905
83859
  await mkdir25(primaryTempDir, { recursive: true });
82906
83860
  logger.debug(`Created temp directory: ${primaryTempDir}`);
@@ -82917,7 +83871,7 @@ Solutions:
82917
83871
  2. Set HOME environment variable
82918
83872
  3. Try running from a different directory`);
82919
83873
  }
82920
- const fallbackTempDir = join73(homeDir, ".claudekit", "tmp", `claudekit-${timestamp}-${counter}`);
83874
+ const fallbackTempDir = join72(homeDir, ".claudekit", "tmp", `claudekit-${timestamp}-${counter}`);
82921
83875
  try {
82922
83876
  await mkdir25(fallbackTempDir, { recursive: true });
82923
83877
  logger.debug(`Created temp directory (fallback): ${fallbackTempDir}`);
@@ -83266,349 +84220,8 @@ async function handleDownload(ctx) {
83266
84220
  };
83267
84221
  }
83268
84222
  // src/commands/init/phases/merge-handler.ts
83269
- import { join as join89 } from "node:path";
83270
-
83271
- // src/domains/installation/deletion-handler.ts
83272
- import { existsSync as existsSync48, lstatSync as lstatSync3, readdirSync as readdirSync3, rmSync as rmSync2, rmdirSync, unlinkSync as unlinkSync3 } from "node:fs";
83273
- import { dirname as dirname18, join as join76, relative as relative10, resolve as resolve17, sep as sep3 } from "node:path";
83274
-
83275
- // src/services/file-operations/manifest/manifest-reader.ts
83276
- init_metadata_migration();
83277
- init_logger();
83278
- init_types3();
83279
- var import_fs_extra8 = __toESM(require_lib3(), 1);
83280
- import { join as join75 } from "node:path";
83281
- async function readManifest(claudeDir2) {
83282
- const metadataPath = join75(claudeDir2, "metadata.json");
83283
- if (!await import_fs_extra8.pathExists(metadataPath)) {
83284
- return null;
83285
- }
83286
- try {
83287
- const content = await import_fs_extra8.readFile(metadataPath, "utf-8");
83288
- const parsed = JSON.parse(content);
83289
- return MetadataSchema.parse(parsed);
83290
- } catch (error) {
83291
- logger.debug(`Failed to read manifest: ${error}`);
83292
- return null;
83293
- }
83294
- }
83295
- async function readKitManifest(claudeDir2, kit) {
83296
- const metadata = await readManifest(claudeDir2);
83297
- if (!metadata)
83298
- return null;
83299
- return getKitMetadata(metadata, kit);
83300
- }
83301
- async function findFileInInstalledKits(claudeDir2, relativePath, excludeKit) {
83302
- const metadata = await readManifest(claudeDir2);
83303
- if (!metadata?.kits) {
83304
- return {
83305
- exists: false,
83306
- ownerKit: null,
83307
- checksum: null,
83308
- version: null,
83309
- sourceTimestamp: null,
83310
- installedAt: null
83311
- };
83312
- }
83313
- for (const [kitName, kitMeta] of Object.entries(metadata.kits)) {
83314
- const kit = kitName;
83315
- if (kit === excludeKit)
83316
- continue;
83317
- if (!kitMeta.files)
83318
- continue;
83319
- const file = kitMeta.files.find((f3) => f3.path === relativePath);
83320
- if (file) {
83321
- return {
83322
- exists: true,
83323
- ownerKit: kit,
83324
- checksum: file.checksum,
83325
- version: kitMeta.version,
83326
- sourceTimestamp: file.sourceTimestamp ?? null,
83327
- installedAt: file.installedAt ?? null
83328
- };
83329
- }
83330
- }
83331
- return {
83332
- exists: false,
83333
- ownerKit: null,
83334
- checksum: null,
83335
- version: null,
83336
- sourceTimestamp: null,
83337
- installedAt: null
83338
- };
83339
- }
83340
- async function getUninstallManifest(claudeDir2, kit) {
83341
- const detection = await detectMetadataFormat(claudeDir2);
83342
- if (detection.format === "multi-kit" && detection.metadata?.kits) {
83343
- const installedKits = Object.keys(detection.metadata.kits);
83344
- if (kit) {
83345
- const kitMeta = detection.metadata.kits[kit];
83346
- if (!kitMeta?.files) {
83347
- return {
83348
- filesToRemove: [],
83349
- filesToPreserve: USER_CONFIG_PATTERNS,
83350
- hasManifest: true,
83351
- isMultiKit: true,
83352
- remainingKits: installedKits.filter((k2) => k2 !== kit)
83353
- };
83354
- }
83355
- const kitFiles = kitMeta.files.map((f3) => f3.path);
83356
- const sharedFiles = new Set;
83357
- for (const otherKit of installedKits) {
83358
- if (otherKit !== kit) {
83359
- const otherMeta = detection.metadata.kits[otherKit];
83360
- if (otherMeta?.files) {
83361
- for (const f3 of otherMeta.files) {
83362
- sharedFiles.add(f3.path);
83363
- }
83364
- }
83365
- }
83366
- }
83367
- const filesToRemove = kitFiles.filter((f3) => !sharedFiles.has(f3));
83368
- const filesToPreserve = [
83369
- ...USER_CONFIG_PATTERNS,
83370
- ...kitFiles.filter((f3) => sharedFiles.has(f3))
83371
- ];
83372
- return {
83373
- filesToRemove,
83374
- filesToPreserve,
83375
- hasManifest: true,
83376
- isMultiKit: true,
83377
- remainingKits: installedKits.filter((k2) => k2 !== kit)
83378
- };
83379
- }
83380
- const allFiles = getAllTrackedFiles(detection.metadata);
83381
- return {
83382
- filesToRemove: allFiles.map((f3) => f3.path),
83383
- filesToPreserve: USER_CONFIG_PATTERNS,
83384
- hasManifest: true,
83385
- isMultiKit: true,
83386
- remainingKits: []
83387
- };
83388
- }
83389
- if (detection.format === "legacy" && detection.metadata) {
83390
- const legacyFiles2 = detection.metadata.files?.map((f3) => f3.path) || [];
83391
- const installedFiles = detection.metadata.installedFiles || [];
83392
- const hasFiles = legacyFiles2.length > 0 || installedFiles.length > 0;
83393
- if (!hasFiles) {
83394
- const legacyDirs2 = ["commands", "agents", "skills", "rules", "workflows", "hooks", "scripts"];
83395
- const legacyFileList = ["metadata.json"];
83396
- return {
83397
- filesToRemove: [...legacyDirs2, ...legacyFileList],
83398
- filesToPreserve: USER_CONFIG_PATTERNS,
83399
- hasManifest: false,
83400
- isMultiKit: false,
83401
- remainingKits: []
83402
- };
83403
- }
83404
- return {
83405
- filesToRemove: legacyFiles2.length > 0 ? legacyFiles2 : installedFiles,
83406
- filesToPreserve: detection.metadata.userConfigFiles || USER_CONFIG_PATTERNS,
83407
- hasManifest: true,
83408
- isMultiKit: false,
83409
- remainingKits: []
83410
- };
83411
- }
83412
- const legacyDirs = ["commands", "agents", "skills", "rules", "workflows", "hooks", "scripts"];
83413
- const legacyFiles = ["metadata.json"];
83414
- return {
83415
- filesToRemove: [...legacyDirs, ...legacyFiles],
83416
- filesToPreserve: USER_CONFIG_PATTERNS,
83417
- hasManifest: false,
83418
- isMultiKit: false,
83419
- remainingKits: []
83420
- };
83421
- }
83422
-
83423
- // src/domains/installation/deletion-handler.ts
83424
- init_logger();
83425
- init_path_resolver();
83426
- var import_fs_extra9 = __toESM(require_lib3(), 1);
83427
- var import_picomatch2 = __toESM(require_picomatch2(), 1);
83428
- function findFileInMetadata(metadata, path13) {
83429
- if (!metadata)
83430
- return null;
83431
- if (metadata.kits) {
83432
- for (const kitMeta of Object.values(metadata.kits)) {
83433
- if (kitMeta?.files) {
83434
- const found = kitMeta.files.find((f3) => f3.path === path13);
83435
- if (found)
83436
- return found;
83437
- }
83438
- }
83439
- }
83440
- if (metadata.files) {
83441
- const found = metadata.files.find((f3) => f3.path === path13);
83442
- if (found)
83443
- return found;
83444
- }
83445
- return null;
83446
- }
83447
- function shouldDeletePath(path13, metadata) {
83448
- const tracked = findFileInMetadata(metadata, path13);
83449
- if (!tracked)
83450
- return true;
83451
- return tracked.ownership !== "user";
83452
- }
83453
- function collectFilesRecursively(dir, baseDir) {
83454
- const results = [];
83455
- if (!existsSync48(dir))
83456
- return results;
83457
- try {
83458
- const entries = readdirSync3(dir, { withFileTypes: true });
83459
- for (const entry of entries) {
83460
- const fullPath = join76(dir, entry.name);
83461
- const relativePath = relative10(baseDir, fullPath);
83462
- if (entry.isDirectory()) {
83463
- results.push(...collectFilesRecursively(fullPath, baseDir));
83464
- } else {
83465
- results.push(relativePath);
83466
- }
83467
- }
83468
- } catch {}
83469
- return results;
83470
- }
83471
- function expandGlobPatterns(patterns, claudeDir2) {
83472
- const expanded = [];
83473
- const allFiles = collectFilesRecursively(claudeDir2, claudeDir2);
83474
- for (const pattern of patterns) {
83475
- if (PathResolver.isGlobPattern(pattern)) {
83476
- const matcher = import_picomatch2.default(pattern);
83477
- const matches = allFiles.filter((file) => matcher(file));
83478
- expanded.push(...matches);
83479
- if (matches.length > 0) {
83480
- logger.debug(`Pattern "${pattern}" matched ${matches.length} files`);
83481
- }
83482
- } else {
83483
- expanded.push(pattern);
83484
- }
83485
- }
83486
- return [...new Set(expanded)];
83487
- }
83488
- var MAX_CLEANUP_ITERATIONS = 50;
83489
- function cleanupEmptyDirectories(filePath, claudeDir2) {
83490
- const normalizedClaudeDir = resolve17(claudeDir2);
83491
- let currentDir = resolve17(dirname18(filePath));
83492
- let iterations = 0;
83493
- while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir) && iterations < MAX_CLEANUP_ITERATIONS) {
83494
- iterations++;
83495
- try {
83496
- const entries = readdirSync3(currentDir);
83497
- if (entries.length === 0) {
83498
- rmdirSync(currentDir);
83499
- logger.debug(`Removed empty directory: ${currentDir}`);
83500
- currentDir = resolve17(dirname18(currentDir));
83501
- } else {
83502
- break;
83503
- }
83504
- } catch {
83505
- break;
83506
- }
83507
- }
83508
- }
83509
- function deletePath(fullPath, claudeDir2) {
83510
- const normalizedPath = resolve17(fullPath);
83511
- const normalizedClaudeDir = resolve17(claudeDir2);
83512
- if (!normalizedPath.startsWith(`${normalizedClaudeDir}${sep3}`) && normalizedPath !== normalizedClaudeDir) {
83513
- throw new Error(`Path traversal detected: ${fullPath}`);
83514
- }
83515
- try {
83516
- const stat13 = lstatSync3(fullPath);
83517
- if (stat13.isDirectory()) {
83518
- rmSync2(fullPath, { recursive: true, force: true });
83519
- } else {
83520
- unlinkSync3(fullPath);
83521
- cleanupEmptyDirectories(fullPath, claudeDir2);
83522
- }
83523
- } catch (error) {
83524
- throw new Error(`Failed to delete ${fullPath}: ${error instanceof Error ? error.message : String(error)}`);
83525
- }
83526
- }
83527
- async function updateMetadataAfterDeletion(claudeDir2, deletedPaths) {
83528
- const metadataPath = join76(claudeDir2, "metadata.json");
83529
- if (!await import_fs_extra9.pathExists(metadataPath)) {
83530
- return;
83531
- }
83532
- let content;
83533
- try {
83534
- content = await import_fs_extra9.readFile(metadataPath, "utf-8");
83535
- } catch {
83536
- logger.debug("Failed to read metadata.json for cleanup");
83537
- return;
83538
- }
83539
- let metadata;
83540
- try {
83541
- metadata = JSON.parse(content);
83542
- } catch {
83543
- logger.debug("Failed to parse metadata.json for cleanup");
83544
- return;
83545
- }
83546
- const deletedSet = new Set(deletedPaths);
83547
- const isDeletedOrInDeletedDir = (path13) => {
83548
- if (deletedSet.has(path13))
83549
- return true;
83550
- for (const deleted of deletedPaths) {
83551
- if (path13.startsWith(`${deleted}/`))
83552
- return true;
83553
- }
83554
- return false;
83555
- };
83556
- if (metadata.kits) {
83557
- for (const kitName of Object.keys(metadata.kits)) {
83558
- const kit = metadata.kits[kitName];
83559
- if (kit?.files) {
83560
- kit.files = kit.files.filter((f3) => !isDeletedOrInDeletedDir(f3.path));
83561
- }
83562
- }
83563
- }
83564
- if (metadata.files) {
83565
- metadata.files = metadata.files.filter((f3) => !isDeletedOrInDeletedDir(f3.path));
83566
- }
83567
- try {
83568
- await import_fs_extra9.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
83569
- logger.debug(`Updated metadata.json, removed ${deletedPaths.length} entries`);
83570
- } catch {
83571
- logger.debug("Failed to write updated metadata.json");
83572
- }
83573
- }
83574
- async function handleDeletions(sourceMetadata, claudeDir2) {
83575
- const deletionPatterns = sourceMetadata.deletions || [];
83576
- if (deletionPatterns.length === 0) {
83577
- return { deletedPaths: [], preservedPaths: [], errors: [] };
83578
- }
83579
- const deletions = expandGlobPatterns(deletionPatterns, claudeDir2);
83580
- const userMetadata = await readManifest(claudeDir2);
83581
- const result = { deletedPaths: [], preservedPaths: [], errors: [] };
83582
- for (const path13 of deletions) {
83583
- const fullPath = join76(claudeDir2, path13);
83584
- const normalizedPath = resolve17(fullPath);
83585
- const normalizedClaudeDir = resolve17(claudeDir2);
83586
- if (!normalizedPath.startsWith(`${normalizedClaudeDir}${sep3}`)) {
83587
- logger.warning(`Skipping invalid path: ${path13}`);
83588
- result.errors.push(path13);
83589
- continue;
83590
- }
83591
- if (!shouldDeletePath(path13, userMetadata)) {
83592
- result.preservedPaths.push(path13);
83593
- logger.verbose(`Preserved user file: ${path13}`);
83594
- continue;
83595
- }
83596
- if (existsSync48(fullPath)) {
83597
- try {
83598
- deletePath(fullPath, claudeDir2);
83599
- result.deletedPaths.push(path13);
83600
- logger.verbose(`Deleted: ${path13}`);
83601
- } catch (error) {
83602
- result.errors.push(path13);
83603
- logger.debug(`Failed to delete ${path13}: ${error}`);
83604
- }
83605
- }
83606
- }
83607
- if (result.deletedPaths.length > 0) {
83608
- await updateMetadataAfterDeletion(claudeDir2, result.deletedPaths);
83609
- }
83610
- return result;
83611
- }
84223
+ init_deletion_handler();
84224
+ import { join as join88 } from "node:path";
83612
84225
 
83613
84226
  // src/domains/installation/file-merger.ts
83614
84227
  init_logger();
@@ -83620,9 +84233,10 @@ init_logger();
83620
84233
  init_types3();
83621
84234
  var import_fs_extra12 = __toESM(require_lib3(), 1);
83622
84235
  var import_ignore3 = __toESM(require_ignore(), 1);
83623
- import { dirname as dirname20, join as join79, relative as relative12 } from "node:path";
84236
+ import { dirname as dirname21, join as join78, relative as relative12 } from "node:path";
83624
84237
 
83625
84238
  // src/domains/installation/selective-merger.ts
84239
+ init_manifest_reader();
83626
84240
  import { stat as stat13 } from "node:fs/promises";
83627
84241
  init_logger();
83628
84242
  var import_semver2 = __toESM(require_semver2(), 1);
@@ -83799,7 +84413,7 @@ init_logger();
83799
84413
  var import_fs_extra10 = __toESM(require_lib3(), 1);
83800
84414
  var import_ignore2 = __toESM(require_ignore(), 1);
83801
84415
  import { relative as relative11 } from "node:path";
83802
- import { join as join77 } from "node:path";
84416
+ import { join as join76 } from "node:path";
83803
84417
 
83804
84418
  // node_modules/@isaacs/balanced-match/dist/esm/index.js
83805
84419
  var balanced = (a3, b3, str2) => {
@@ -85255,7 +85869,7 @@ class FileScanner {
85255
85869
  const files = [];
85256
85870
  const entries = await import_fs_extra10.readdir(dir, { encoding: "utf8" });
85257
85871
  for (const entry of entries) {
85258
- const fullPath = join77(dir, entry);
85872
+ const fullPath = join76(dir, entry);
85259
85873
  const relativePath = relative11(baseDir, fullPath);
85260
85874
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
85261
85875
  const stats = await import_fs_extra10.lstat(fullPath);
@@ -85295,7 +85909,7 @@ import { execSync as execSync4 } from "node:child_process";
85295
85909
  init_shared();
85296
85910
  import { existsSync as existsSync49 } from "node:fs";
85297
85911
  import { mkdir as mkdir26, readFile as readFile38, writeFile as writeFile22 } from "node:fs/promises";
85298
- import { dirname as dirname19, join as join78 } from "node:path";
85912
+ import { dirname as dirname20, join as join77 } from "node:path";
85299
85913
  var CK_JSON_FILE = ".ck.json";
85300
85914
 
85301
85915
  class InstalledSettingsTracker {
@@ -85309,9 +85923,9 @@ class InstalledSettingsTracker {
85309
85923
  }
85310
85924
  getCkJsonPath() {
85311
85925
  if (this.isGlobal) {
85312
- return join78(this.projectDir, CK_JSON_FILE);
85926
+ return join77(this.projectDir, CK_JSON_FILE);
85313
85927
  }
85314
- return join78(this.projectDir, ".claude", CK_JSON_FILE);
85928
+ return join77(this.projectDir, ".claude", CK_JSON_FILE);
85315
85929
  }
85316
85930
  async loadInstalledSettings() {
85317
85931
  const ckJsonPath = this.getCkJsonPath();
@@ -85346,7 +85960,7 @@ class InstalledSettingsTracker {
85346
85960
  data.kits[this.kitName] = {};
85347
85961
  }
85348
85962
  data.kits[this.kitName].installedSettings = settings;
85349
- await mkdir26(dirname19(ckJsonPath), { recursive: true });
85963
+ await mkdir26(dirname20(ckJsonPath), { recursive: true });
85350
85964
  await writeFile22(ckJsonPath, JSON.stringify(data, null, 2), "utf-8");
85351
85965
  logger.debug(`Saved installed settings to ${ckJsonPath}`);
85352
85966
  } catch (error) {
@@ -85864,7 +86478,7 @@ class CopyExecutor {
85864
86478
  for (const file of files) {
85865
86479
  const relativePath = relative12(sourceDir, file);
85866
86480
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
85867
- const destPath = join79(destDir, relativePath);
86481
+ const destPath = join78(destDir, relativePath);
85868
86482
  if (await import_fs_extra12.pathExists(destPath)) {
85869
86483
  if (this.fileScanner.shouldNeverCopy(normalizedRelativePath)) {
85870
86484
  logger.debug(`Security-sensitive file exists but won't be overwritten: ${normalizedRelativePath}`);
@@ -85886,7 +86500,7 @@ class CopyExecutor {
85886
86500
  for (const file of files) {
85887
86501
  const relativePath = relative12(sourceDir, file);
85888
86502
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
85889
- const destPath = join79(destDir, relativePath);
86503
+ const destPath = join78(destDir, relativePath);
85890
86504
  if (this.fileScanner.shouldNeverCopy(normalizedRelativePath)) {
85891
86505
  logger.debug(`Skipping security-sensitive file: ${normalizedRelativePath}`);
85892
86506
  skippedCount++;
@@ -85963,10 +86577,10 @@ class CopyExecutor {
85963
86577
  }
85964
86578
  trackInstalledFile(relativePath) {
85965
86579
  this.installedFiles.add(relativePath);
85966
- let dir = dirname20(relativePath);
86580
+ let dir = dirname21(relativePath);
85967
86581
  while (dir && dir !== "." && dir !== "/") {
85968
86582
  this.installedDirectories.add(`${dir}/`);
85969
- dir = dirname20(dir);
86583
+ dir = dirname21(dir);
85970
86584
  }
85971
86585
  }
85972
86586
  }
@@ -86056,15 +86670,19 @@ class FileMerger {
86056
86670
 
86057
86671
  // src/domains/migration/legacy-migration.ts
86058
86672
  import { readdir as readdir18, stat as stat14 } from "node:fs/promises";
86059
- import { join as join83, relative as relative13 } from "node:path";
86673
+ import { join as join82, relative as relative13 } from "node:path";
86674
+
86675
+ // src/services/file-operations/manifest/index.ts
86676
+ init_manifest_reader();
86677
+
86060
86678
  // src/services/file-operations/manifest/manifest-tracker.ts
86061
- import { join as join82 } from "node:path";
86679
+ import { join as join81 } from "node:path";
86062
86680
 
86063
86681
  // src/domains/migration/release-manifest.ts
86064
86682
  init_logger();
86065
86683
  init_zod();
86066
86684
  var import_fs_extra13 = __toESM(require_lib3(), 1);
86067
- import { join as join80 } from "node:path";
86685
+ import { join as join79 } from "node:path";
86068
86686
  var ReleaseManifestFileSchema = exports_external.object({
86069
86687
  path: exports_external.string(),
86070
86688
  checksum: exports_external.string().regex(/^[a-f0-9]{64}$/),
@@ -86079,7 +86697,7 @@ var ReleaseManifestSchema = exports_external.object({
86079
86697
 
86080
86698
  class ReleaseManifestLoader {
86081
86699
  static async load(extractDir) {
86082
- const manifestPath = join80(extractDir, "release-manifest.json");
86700
+ const manifestPath = join79(extractDir, "release-manifest.json");
86083
86701
  try {
86084
86702
  const content = await import_fs_extra13.readFile(manifestPath, "utf-8");
86085
86703
  const parsed = JSON.parse(content);
@@ -86103,11 +86721,12 @@ init_safe_spinner();
86103
86721
  init_metadata_migration();
86104
86722
  init_logger();
86105
86723
  init_types3();
86724
+ init_manifest_reader();
86106
86725
  var import_fs_extra14 = __toESM(require_lib3(), 1);
86107
86726
  var import_proper_lockfile5 = __toESM(require_proper_lockfile(), 1);
86108
- import { join as join81 } from "node:path";
86727
+ import { join as join80 } from "node:path";
86109
86728
  async function writeManifest(claudeDir2, kitName, version, scope, kitType, trackedFiles, userConfigFiles) {
86110
- const metadataPath = join81(claudeDir2, "metadata.json");
86729
+ const metadataPath = join80(claudeDir2, "metadata.json");
86111
86730
  const kit = kitType || (/\bmarketing\b/i.test(kitName) ? "marketing" : "engineer");
86112
86731
  await import_fs_extra14.ensureFile(metadataPath);
86113
86732
  let release = null;
@@ -86127,20 +86746,54 @@ async function writeManifest(claudeDir2, kitName, version, scope, kitType, track
86127
86746
  const content = await import_fs_extra14.readFile(metadataPath, "utf-8");
86128
86747
  const parsed = JSON.parse(content);
86129
86748
  if (parsed && typeof parsed === "object" && Object.keys(parsed).length > 0) {
86130
- existingMetadata = parsed;
86749
+ const validatedExisting = MetadataSchema.safeParse(parsed);
86750
+ if (validatedExisting.success) {
86751
+ existingMetadata = validatedExisting.data;
86752
+ } else {
86753
+ logger.warning("Existing metadata.json is invalid; preserving recoverable fields and rebuilding the rest");
86754
+ const raw2 = parsed;
86755
+ const recoveredKits = {};
86756
+ if (raw2.kits && typeof raw2.kits === "object") {
86757
+ for (const [rawKitName, rawKitValue] of Object.entries(raw2.kits)) {
86758
+ if ((rawKitName === "engineer" || rawKitName === "marketing") && rawKitValue && typeof rawKitValue === "object") {
86759
+ const recoveredKit = rawKitValue;
86760
+ if (typeof recoveredKit.version === "string" && typeof recoveredKit.installedAt === "string") {
86761
+ recoveredKits[rawKitName] = recoveredKit;
86762
+ }
86763
+ }
86764
+ }
86765
+ }
86766
+ existingMetadata = {
86767
+ kits: recoveredKits,
86768
+ scope: raw2.scope === "local" || raw2.scope === "global" ? raw2.scope : undefined,
86769
+ name: typeof raw2.name === "string" ? raw2.name : undefined,
86770
+ version: typeof raw2.version === "string" ? raw2.version : undefined,
86771
+ installedAt: typeof raw2.installedAt === "string" ? raw2.installedAt : undefined,
86772
+ userConfigFiles: Array.isArray(raw2.userConfigFiles) ? raw2.userConfigFiles.filter((entry) => typeof entry === "string") : undefined
86773
+ };
86774
+ }
86131
86775
  }
86132
86776
  } catch (error) {
86133
86777
  logger.debug(`Could not read existing metadata: ${error}`);
86134
86778
  }
86135
86779
  }
86136
86780
  const installedAt = new Date().toISOString();
86781
+ const existingKits = existingMetadata.kits || {};
86782
+ const existingKitMetadata = existingKits[kit];
86137
86783
  const kitMetadata = {
86784
+ ...existingKitMetadata,
86138
86785
  version,
86139
86786
  installedAt,
86140
86787
  files: trackedFiles.length > 0 ? trackedFiles : undefined
86141
86788
  };
86142
- const existingKits = existingMetadata.kits || {};
86143
86789
  const otherKitsExist = Object.keys(existingKits).some((k2) => k2 !== kit);
86790
+ const mergedUserConfigFiles = [
86791
+ ...new Set([
86792
+ ...existingMetadata.userConfigFiles || [],
86793
+ ...USER_CONFIG_PATTERNS,
86794
+ ...userConfigFiles
86795
+ ])
86796
+ ];
86144
86797
  const metadata = {
86145
86798
  kits: {
86146
86799
  ...existingKits,
@@ -86150,7 +86803,7 @@ async function writeManifest(claudeDir2, kitName, version, scope, kitType, track
86150
86803
  name: otherKitsExist ? existingMetadata.name ?? kitName : kitName,
86151
86804
  version: otherKitsExist ? existingMetadata.version ?? version : version,
86152
86805
  installedAt: otherKitsExist ? existingMetadata.installedAt ?? installedAt : installedAt,
86153
- userConfigFiles: [...USER_CONFIG_PATTERNS, ...userConfigFiles]
86806
+ userConfigFiles: mergedUserConfigFiles
86154
86807
  };
86155
86808
  const validated = MetadataSchema.parse(metadata);
86156
86809
  await import_fs_extra14.writeFile(metadataPath, JSON.stringify(validated, null, 2), "utf-8");
@@ -86163,7 +86816,7 @@ async function writeManifest(claudeDir2, kitName, version, scope, kitType, track
86163
86816
  }
86164
86817
  }
86165
86818
  async function removeKitFromManifest(claudeDir2, kit) {
86166
- const metadataPath = join81(claudeDir2, "metadata.json");
86819
+ const metadataPath = join80(claudeDir2, "metadata.json");
86167
86820
  if (!await import_fs_extra14.pathExists(metadataPath))
86168
86821
  return false;
86169
86822
  let release = null;
@@ -86293,7 +86946,7 @@ function buildFileTrackingList(options2) {
86293
86946
  if (!isGlobal && !installedPath.startsWith(".claude/"))
86294
86947
  continue;
86295
86948
  const relativePath = isGlobal ? installedPath : installedPath.replace(/^\.claude\//, "");
86296
- const filePath = join82(claudeDir2, relativePath);
86949
+ const filePath = join81(claudeDir2, relativePath);
86297
86950
  const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
86298
86951
  const ownership = manifestEntry ? "ck" : "user";
86299
86952
  filesToTrack.push({
@@ -86400,7 +87053,7 @@ class LegacyMigration {
86400
87053
  continue;
86401
87054
  if (SKIP_DIRS_ALL.includes(entry))
86402
87055
  continue;
86403
- const fullPath = join83(dir, entry);
87056
+ const fullPath = join82(dir, entry);
86404
87057
  let stats;
86405
87058
  try {
86406
87059
  stats = await stat14(fullPath);
@@ -86502,7 +87155,7 @@ User-created files (sample):`);
86502
87155
  ];
86503
87156
  if (filesToChecksum.length > 0) {
86504
87157
  const checksumResults = await mapWithLimit(filesToChecksum, async ({ relativePath, ownership }) => {
86505
- const fullPath = join83(claudeDir2, relativePath);
87158
+ const fullPath = join82(claudeDir2, relativePath);
86506
87159
  const checksum = await OwnershipChecker.calculateChecksum(fullPath);
86507
87160
  return { relativePath, checksum, ownership };
86508
87161
  });
@@ -86523,7 +87176,7 @@ User-created files (sample):`);
86523
87176
  installedAt: new Date().toISOString(),
86524
87177
  files: trackedFiles
86525
87178
  };
86526
- const metadataPath = join83(claudeDir2, "metadata.json");
87179
+ const metadataPath = join82(claudeDir2, "metadata.json");
86527
87180
  await import_fs_extra15.writeFile(metadataPath, JSON.stringify(updatedMetadata, null, 2));
86528
87181
  logger.success(`Migration complete: tracked ${trackedFiles.length} files`);
86529
87182
  return true;
@@ -86629,7 +87282,7 @@ function buildConflictSummary(fileConflicts, hookConflicts, mcpConflicts) {
86629
87282
  init_logger();
86630
87283
  init_skip_directories();
86631
87284
  var import_fs_extra16 = __toESM(require_lib3(), 1);
86632
- import { join as join84, relative as relative14, resolve as resolve18 } from "node:path";
87285
+ import { join as join83, relative as relative14, resolve as resolve19 } from "node:path";
86633
87286
 
86634
87287
  class FileScanner2 {
86635
87288
  static async getFiles(dirPath, relativeTo) {
@@ -86645,7 +87298,7 @@ class FileScanner2 {
86645
87298
  logger.debug(`Skipping directory: ${entry}`);
86646
87299
  continue;
86647
87300
  }
86648
- const fullPath = join84(dirPath, entry);
87301
+ const fullPath = join83(dirPath, entry);
86649
87302
  if (!FileScanner2.isSafePath(basePath, fullPath)) {
86650
87303
  logger.warning(`Skipping potentially unsafe path: ${entry}`);
86651
87304
  continue;
@@ -86680,8 +87333,8 @@ class FileScanner2 {
86680
87333
  return files;
86681
87334
  }
86682
87335
  static async findCustomFiles(destDir, sourceDir, subPath) {
86683
- const destSubDir = join84(destDir, subPath);
86684
- const sourceSubDir = join84(sourceDir, subPath);
87336
+ const destSubDir = join83(destDir, subPath);
87337
+ const sourceSubDir = join83(sourceDir, subPath);
86685
87338
  logger.debug(`findCustomFiles - destDir: ${destDir}`);
86686
87339
  logger.debug(`findCustomFiles - sourceDir: ${sourceDir}`);
86687
87340
  logger.debug(`findCustomFiles - subPath: "${subPath}"`);
@@ -86709,8 +87362,8 @@ class FileScanner2 {
86709
87362
  return customFiles;
86710
87363
  }
86711
87364
  static isSafePath(basePath, targetPath) {
86712
- const resolvedBase = resolve18(basePath);
86713
- const resolvedTarget = resolve18(targetPath);
87365
+ const resolvedBase = resolve19(basePath);
87366
+ const resolvedTarget = resolve19(targetPath);
86714
87367
  return resolvedTarget.startsWith(resolvedBase);
86715
87368
  }
86716
87369
  static toPosixPath(path14) {
@@ -86722,12 +87375,12 @@ class FileScanner2 {
86722
87375
  init_logger();
86723
87376
  var import_fs_extra17 = __toESM(require_lib3(), 1);
86724
87377
  import { lstat as lstat7, mkdir as mkdir27, readdir as readdir21, stat as stat15 } from "node:fs/promises";
86725
- import { join as join86 } from "node:path";
87378
+ import { join as join85 } from "node:path";
86726
87379
 
86727
87380
  // src/services/transformers/commands-prefix/content-transformer.ts
86728
87381
  init_logger();
86729
87382
  import { readFile as readFile42, readdir as readdir20, writeFile as writeFile26 } from "node:fs/promises";
86730
- import { join as join85 } from "node:path";
87383
+ import { join as join84 } from "node:path";
86731
87384
  var TRANSFORMABLE_EXTENSIONS = new Set([
86732
87385
  ".md",
86733
87386
  ".txt",
@@ -86788,7 +87441,7 @@ async function transformCommandReferences(directory, options2 = {}) {
86788
87441
  async function processDirectory(dir) {
86789
87442
  const entries = await readdir20(dir, { withFileTypes: true });
86790
87443
  for (const entry of entries) {
86791
- const fullPath = join85(dir, entry.name);
87444
+ const fullPath = join84(dir, entry.name);
86792
87445
  if (entry.isDirectory()) {
86793
87446
  if (entry.name === "node_modules" || entry.name.startsWith(".") && entry.name !== ".claude") {
86794
87447
  continue;
@@ -86863,14 +87516,14 @@ function shouldApplyPrefix(options2) {
86863
87516
  // src/services/transformers/commands-prefix/prefix-applier.ts
86864
87517
  async function applyPrefix(extractDir) {
86865
87518
  validatePath(extractDir, "extractDir");
86866
- const commandsDir = join86(extractDir, ".claude", "commands");
87519
+ const commandsDir = join85(extractDir, ".claude", "commands");
86867
87520
  if (!await import_fs_extra17.pathExists(commandsDir)) {
86868
87521
  logger.verbose("No commands directory found, skipping prefix application");
86869
87522
  return;
86870
87523
  }
86871
87524
  logger.info("Applying /ck: prefix to slash commands...");
86872
- const backupDir = join86(extractDir, ".commands-backup");
86873
- const tempDir = join86(extractDir, ".commands-prefix-temp");
87525
+ const backupDir = join85(extractDir, ".commands-backup");
87526
+ const tempDir = join85(extractDir, ".commands-prefix-temp");
86874
87527
  try {
86875
87528
  const entries = await readdir21(commandsDir);
86876
87529
  if (entries.length === 0) {
@@ -86878,7 +87531,7 @@ async function applyPrefix(extractDir) {
86878
87531
  return;
86879
87532
  }
86880
87533
  if (entries.length === 1 && entries[0] === "ck") {
86881
- const ckDir2 = join86(commandsDir, "ck");
87534
+ const ckDir2 = join85(commandsDir, "ck");
86882
87535
  const ckStat = await stat15(ckDir2);
86883
87536
  if (ckStat.isDirectory()) {
86884
87537
  logger.verbose("Commands already have /ck: prefix, skipping");
@@ -86888,17 +87541,17 @@ async function applyPrefix(extractDir) {
86888
87541
  await import_fs_extra17.copy(commandsDir, backupDir);
86889
87542
  logger.verbose("Created backup of commands directory");
86890
87543
  await mkdir27(tempDir, { recursive: true });
86891
- const ckDir = join86(tempDir, "ck");
87544
+ const ckDir = join85(tempDir, "ck");
86892
87545
  await mkdir27(ckDir, { recursive: true });
86893
87546
  let processedCount = 0;
86894
87547
  for (const entry of entries) {
86895
- const sourcePath = join86(commandsDir, entry);
87548
+ const sourcePath = join85(commandsDir, entry);
86896
87549
  const stats = await lstat7(sourcePath);
86897
87550
  if (stats.isSymbolicLink()) {
86898
87551
  logger.warning(`Skipping symlink for security: ${entry}`);
86899
87552
  continue;
86900
87553
  }
86901
- const destPath = join86(ckDir, entry);
87554
+ const destPath = join85(ckDir, entry);
86902
87555
  await import_fs_extra17.copy(sourcePath, destPath, {
86903
87556
  overwrite: false,
86904
87557
  errorOnExist: true
@@ -86916,7 +87569,7 @@ async function applyPrefix(extractDir) {
86916
87569
  await import_fs_extra17.move(tempDir, commandsDir);
86917
87570
  await import_fs_extra17.remove(backupDir);
86918
87571
  logger.success("Successfully reorganized commands to /ck: prefix");
86919
- const claudeDir2 = join86(extractDir, ".claude");
87572
+ const claudeDir2 = join85(extractDir, ".claude");
86920
87573
  logger.info("Transforming command references in file contents...");
86921
87574
  const transformResult = await transformCommandReferences(claudeDir2, {
86922
87575
  verbose: logger.isVerbose()
@@ -86954,20 +87607,20 @@ async function applyPrefix(extractDir) {
86954
87607
  // src/services/transformers/commands-prefix/prefix-cleaner.ts
86955
87608
  init_metadata_migration();
86956
87609
  import { lstat as lstat9, readdir as readdir23 } from "node:fs/promises";
86957
- import { join as join88 } from "node:path";
87610
+ import { join as join87 } from "node:path";
86958
87611
  init_logger();
86959
87612
  var import_fs_extra19 = __toESM(require_lib3(), 1);
86960
87613
 
86961
87614
  // src/services/transformers/commands-prefix/file-processor.ts
86962
87615
  import { lstat as lstat8, readdir as readdir22 } from "node:fs/promises";
86963
- import { join as join87 } from "node:path";
87616
+ import { join as join86 } from "node:path";
86964
87617
  init_logger();
86965
87618
  var import_fs_extra18 = __toESM(require_lib3(), 1);
86966
87619
  async function scanDirectoryFiles(dir) {
86967
87620
  const files = [];
86968
87621
  const entries = await readdir22(dir);
86969
87622
  for (const entry of entries) {
86970
- const fullPath = join87(dir, entry);
87623
+ const fullPath = join86(dir, entry);
86971
87624
  const stats = await lstat8(fullPath);
86972
87625
  if (stats.isSymbolicLink()) {
86973
87626
  continue;
@@ -87095,8 +87748,8 @@ function isDifferentKitDirectory(dirName, currentKit) {
87095
87748
  async function cleanupCommandsDirectory(targetDir, isGlobal, options2 = {}) {
87096
87749
  const { dryRun = false } = options2;
87097
87750
  validatePath(targetDir, "targetDir");
87098
- const claudeDir2 = isGlobal ? targetDir : join88(targetDir, ".claude");
87099
- const commandsDir = join88(claudeDir2, "commands");
87751
+ const claudeDir2 = isGlobal ? targetDir : join87(targetDir, ".claude");
87752
+ const commandsDir = join87(claudeDir2, "commands");
87100
87753
  const accumulator = {
87101
87754
  results: [],
87102
87755
  deletedCount: 0,
@@ -87138,7 +87791,7 @@ async function cleanupCommandsDirectory(targetDir, isGlobal, options2 = {}) {
87138
87791
  }
87139
87792
  const metadataForChecks = options2.kitType ? createKitSpecificMetadata(metadata, options2.kitType) : metadata;
87140
87793
  for (const entry of entries) {
87141
- const entryPath = join88(commandsDir, entry);
87794
+ const entryPath = join87(commandsDir, entry);
87142
87795
  const stats = await lstat9(entryPath);
87143
87796
  if (stats.isSymbolicLink()) {
87144
87797
  addSymlinkSkip(entry, accumulator);
@@ -87195,7 +87848,7 @@ async function handleMerge(ctx) {
87195
87848
  let customClaudeFiles = [];
87196
87849
  if (!ctx.options.fresh) {
87197
87850
  logger.info("Scanning for custom .claude files...");
87198
- const scanSourceDir = ctx.options.global ? join89(ctx.extractDir, ".claude") : ctx.extractDir;
87851
+ const scanSourceDir = ctx.options.global ? join88(ctx.extractDir, ".claude") : ctx.extractDir;
87199
87852
  const scanTargetSubdir = ctx.options.global ? "" : ".claude";
87200
87853
  customClaudeFiles = await FileScanner2.findCustomFiles(ctx.resolvedDir, scanSourceDir, scanTargetSubdir);
87201
87854
  } else {
@@ -87260,28 +87913,38 @@ async function handleMerge(ctx) {
87260
87913
  return { ...ctx, cancelled: true };
87261
87914
  }
87262
87915
  }
87263
- const sourceDir = ctx.options.global ? join89(ctx.extractDir, ".claude") : ctx.extractDir;
87916
+ const sourceDir = ctx.options.global ? join88(ctx.extractDir, ".claude") : ctx.extractDir;
87264
87917
  await merger.merge(sourceDir, ctx.resolvedDir, ctx.isNonInteractive);
87265
87918
  const fileConflicts = merger.getFileConflicts();
87266
87919
  if (fileConflicts.length > 0 && !ctx.isNonInteractive) {
87267
87920
  const summary = buildConflictSummary(fileConflicts, [], []);
87268
87921
  displayConflictSummary(summary);
87269
87922
  }
87923
+ let deferredDeletions = [];
87270
87924
  try {
87271
- const sourceMetadataPath = ctx.options.global ? join89(sourceDir, "metadata.json") : join89(sourceDir, ".claude", "metadata.json");
87925
+ const sourceMetadataPath = ctx.options.global ? join88(sourceDir, "metadata.json") : join88(sourceDir, ".claude", "metadata.json");
87272
87926
  if (await import_fs_extra20.pathExists(sourceMetadataPath)) {
87273
87927
  const metadataContent = await import_fs_extra20.readFile(sourceMetadataPath, "utf-8");
87274
87928
  const sourceMetadata = JSON.parse(metadataContent);
87275
87929
  if (sourceMetadata.deletions && sourceMetadata.deletions.length > 0) {
87276
- const deletionResult = await handleDeletions(sourceMetadata, ctx.claudeDir);
87277
- if (deletionResult.deletedPaths.length > 0) {
87278
- logger.info(`Removed ${deletionResult.deletedPaths.length} deprecated file(s)`);
87279
- for (const path14 of deletionResult.deletedPaths) {
87280
- logger.verbose(` - ${path14}`);
87930
+ const { categorizeDeletions: categorizeDeletions2 } = await Promise.resolve().then(() => (init_deletion_handler(), exports_deletion_handler));
87931
+ const categorized = categorizeDeletions2(sourceMetadata.deletions);
87932
+ if (categorized.immediate.length > 0) {
87933
+ const immediateMetadata = { ...sourceMetadata, deletions: categorized.immediate };
87934
+ const deletionResult = await handleDeletions(immediateMetadata, ctx.claudeDir);
87935
+ if (deletionResult.deletedPaths.length > 0) {
87936
+ logger.info(`Removed ${deletionResult.deletedPaths.length} deprecated file(s)`);
87937
+ for (const path14 of deletionResult.deletedPaths) {
87938
+ logger.verbose(` - ${path14}`);
87939
+ }
87940
+ }
87941
+ if (deletionResult.preservedPaths.length > 0) {
87942
+ logger.verbose(`Preserved ${deletionResult.preservedPaths.length} user-owned file(s)`);
87281
87943
  }
87282
87944
  }
87283
- if (deletionResult.preservedPaths.length > 0) {
87284
- logger.verbose(`Preserved ${deletionResult.preservedPaths.length} user-owned file(s)`);
87945
+ if (categorized.deferred.length > 0) {
87946
+ deferredDeletions = categorized.deferred;
87947
+ logger.debug(`Deferred ${categorized.deferred.length} skill deletion(s) to post-install phase`);
87285
87948
  }
87286
87949
  }
87287
87950
  } else {
@@ -87308,11 +87971,12 @@ async function handleMerge(ctx) {
87308
87971
  return {
87309
87972
  ...ctx,
87310
87973
  customClaudeFiles,
87311
- includePatterns
87974
+ includePatterns,
87975
+ deferredDeletions
87312
87976
  };
87313
87977
  }
87314
87978
  // src/commands/init/phases/migration-handler.ts
87315
- import { join as join97 } from "node:path";
87979
+ import { join as join96 } from "node:path";
87316
87980
 
87317
87981
  // src/domains/skills/skills-detector.ts
87318
87982
  init_logger();
@@ -87328,7 +87992,7 @@ init_types3();
87328
87992
  var import_fs_extra21 = __toESM(require_lib3(), 1);
87329
87993
  import { createHash as createHash4 } from "node:crypto";
87330
87994
  import { readFile as readFile44, readdir as readdir24, writeFile as writeFile27 } from "node:fs/promises";
87331
- import { join as join90, relative as relative15 } from "node:path";
87995
+ import { join as join89, relative as relative15 } from "node:path";
87332
87996
 
87333
87997
  class SkillsManifestManager {
87334
87998
  static MANIFEST_FILENAME = ".skills-manifest.json";
@@ -87350,12 +88014,12 @@ class SkillsManifestManager {
87350
88014
  return manifest;
87351
88015
  }
87352
88016
  static async writeManifest(skillsDir2, manifest) {
87353
- const manifestPath = join90(skillsDir2, SkillsManifestManager.MANIFEST_FILENAME);
88017
+ const manifestPath = join89(skillsDir2, SkillsManifestManager.MANIFEST_FILENAME);
87354
88018
  await writeFile27(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
87355
88019
  logger.debug(`Wrote manifest to: ${manifestPath}`);
87356
88020
  }
87357
88021
  static async readManifest(skillsDir2) {
87358
- const manifestPath = join90(skillsDir2, SkillsManifestManager.MANIFEST_FILENAME);
88022
+ const manifestPath = join89(skillsDir2, SkillsManifestManager.MANIFEST_FILENAME);
87359
88023
  if (!await import_fs_extra21.pathExists(manifestPath)) {
87360
88024
  logger.debug(`No manifest found at: ${manifestPath}`);
87361
88025
  return null;
@@ -87378,7 +88042,7 @@ class SkillsManifestManager {
87378
88042
  return "flat";
87379
88043
  }
87380
88044
  for (const dir of dirs.slice(0, 3)) {
87381
- const dirPath = join90(skillsDir2, dir.name);
88045
+ const dirPath = join89(skillsDir2, dir.name);
87382
88046
  const subEntries = await readdir24(dirPath, { withFileTypes: true });
87383
88047
  const hasSubdirs = subEntries.some((entry) => entry.isDirectory());
87384
88048
  if (hasSubdirs) {
@@ -87397,7 +88061,7 @@ class SkillsManifestManager {
87397
88061
  const entries = await readdir24(skillsDir2, { withFileTypes: true });
87398
88062
  for (const entry of entries) {
87399
88063
  if (entry.isDirectory() && !BUILD_ARTIFACT_DIRS.includes(entry.name) && !entry.name.startsWith(".")) {
87400
- const skillPath = join90(skillsDir2, entry.name);
88064
+ const skillPath = join89(skillsDir2, entry.name);
87401
88065
  const hash = await SkillsManifestManager.hashDirectory(skillPath);
87402
88066
  skills.push({
87403
88067
  name: entry.name,
@@ -87409,11 +88073,11 @@ class SkillsManifestManager {
87409
88073
  const categories = await readdir24(skillsDir2, { withFileTypes: true });
87410
88074
  for (const category of categories) {
87411
88075
  if (category.isDirectory() && !BUILD_ARTIFACT_DIRS.includes(category.name) && !category.name.startsWith(".")) {
87412
- const categoryPath = join90(skillsDir2, category.name);
88076
+ const categoryPath = join89(skillsDir2, category.name);
87413
88077
  const skillEntries = await readdir24(categoryPath, { withFileTypes: true });
87414
88078
  for (const skillEntry of skillEntries) {
87415
88079
  if (skillEntry.isDirectory() && !skillEntry.name.startsWith(".")) {
87416
- const skillPath = join90(categoryPath, skillEntry.name);
88080
+ const skillPath = join89(categoryPath, skillEntry.name);
87417
88081
  const hash = await SkillsManifestManager.hashDirectory(skillPath);
87418
88082
  skills.push({
87419
88083
  name: skillEntry.name,
@@ -87443,7 +88107,7 @@ class SkillsManifestManager {
87443
88107
  const files = [];
87444
88108
  const entries = await readdir24(dirPath, { withFileTypes: true });
87445
88109
  for (const entry of entries) {
87446
- const fullPath = join90(dirPath, entry.name);
88110
+ const fullPath = join89(dirPath, entry.name);
87447
88111
  if (entry.name.startsWith(".") || BUILD_ARTIFACT_DIRS.includes(entry.name)) {
87448
88112
  continue;
87449
88113
  }
@@ -87565,7 +88229,7 @@ function getPathMapping(skillName, oldBasePath, newBasePath) {
87565
88229
  // src/domains/skills/detection/script-detector.ts
87566
88230
  var import_fs_extra22 = __toESM(require_lib3(), 1);
87567
88231
  import { readdir as readdir25 } from "node:fs/promises";
87568
- import { join as join91 } from "node:path";
88232
+ import { join as join90 } from "node:path";
87569
88233
  async function scanDirectory(skillsDir2) {
87570
88234
  if (!await import_fs_extra22.pathExists(skillsDir2)) {
87571
88235
  return ["flat", []];
@@ -87578,12 +88242,12 @@ async function scanDirectory(skillsDir2) {
87578
88242
  let totalSkillLikeCount = 0;
87579
88243
  const allSkills = [];
87580
88244
  for (const dir of dirs) {
87581
- const dirPath = join91(skillsDir2, dir.name);
88245
+ const dirPath = join90(skillsDir2, dir.name);
87582
88246
  const subEntries = await readdir25(dirPath, { withFileTypes: true });
87583
88247
  const subdirs = subEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
87584
88248
  if (subdirs.length > 0) {
87585
88249
  for (const subdir of subdirs.slice(0, 3)) {
87586
- const subdirPath = join91(dirPath, subdir.name);
88250
+ const subdirPath = join90(dirPath, subdir.name);
87587
88251
  const subdirFiles = await readdir25(subdirPath, { withFileTypes: true });
87588
88252
  const hasSkillMarker = subdirFiles.some((file) => file.isFile() && (file.name === "skill.md" || file.name === "README.md" || file.name === "readme.md" || file.name === "config.json" || file.name === "package.json"));
87589
88253
  if (hasSkillMarker) {
@@ -87740,12 +88404,12 @@ class SkillsMigrationDetector {
87740
88404
  // src/domains/skills/skills-migrator.ts
87741
88405
  init_logger();
87742
88406
  init_types3();
87743
- import { join as join96 } from "node:path";
88407
+ import { join as join95 } from "node:path";
87744
88408
 
87745
88409
  // src/domains/skills/migrator/migration-executor.ts
87746
88410
  init_logger();
87747
88411
  import { copyFile as copyFile6, mkdir as mkdir28, readdir as readdir26, rm as rm10 } from "node:fs/promises";
87748
- import { join as join92 } from "node:path";
88412
+ import { join as join91 } from "node:path";
87749
88413
  var import_fs_extra24 = __toESM(require_lib3(), 1);
87750
88414
 
87751
88415
  // src/domains/skills/skills-migration-prompts.ts
@@ -87910,8 +88574,8 @@ async function copySkillDirectory(sourceDir, destDir) {
87910
88574
  await mkdir28(destDir, { recursive: true });
87911
88575
  const entries = await readdir26(sourceDir, { withFileTypes: true });
87912
88576
  for (const entry of entries) {
87913
- const sourcePath = join92(sourceDir, entry.name);
87914
- const destPath = join92(destDir, entry.name);
88577
+ const sourcePath = join91(sourceDir, entry.name);
88578
+ const destPath = join91(destDir, entry.name);
87915
88579
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
87916
88580
  continue;
87917
88581
  }
@@ -87926,7 +88590,7 @@ async function executeInternal(mappings, customizations, currentSkillsDir, inter
87926
88590
  const migrated = [];
87927
88591
  const preserved = [];
87928
88592
  const errors2 = [];
87929
- const tempDir = join92(currentSkillsDir, "..", ".skills-migration-temp");
88593
+ const tempDir = join91(currentSkillsDir, "..", ".skills-migration-temp");
87930
88594
  await mkdir28(tempDir, { recursive: true });
87931
88595
  try {
87932
88596
  for (const mapping of mappings) {
@@ -87947,9 +88611,9 @@ async function executeInternal(mappings, customizations, currentSkillsDir, inter
87947
88611
  }
87948
88612
  }
87949
88613
  const category = mapping.category;
87950
- const targetPath = category ? join92(tempDir, category, skillName) : join92(tempDir, skillName);
88614
+ const targetPath = category ? join91(tempDir, category, skillName) : join91(tempDir, skillName);
87951
88615
  if (category) {
87952
- await mkdir28(join92(tempDir, category), { recursive: true });
88616
+ await mkdir28(join91(tempDir, category), { recursive: true });
87953
88617
  }
87954
88618
  await copySkillDirectory(currentSkillPath, targetPath);
87955
88619
  migrated.push(skillName);
@@ -88016,7 +88680,7 @@ init_logger();
88016
88680
  init_types3();
88017
88681
  var import_fs_extra25 = __toESM(require_lib3(), 1);
88018
88682
  import { copyFile as copyFile7, mkdir as mkdir29, readdir as readdir27, rm as rm11, stat as stat16 } from "node:fs/promises";
88019
- import { basename as basename10, join as join93, normalize as normalize8 } from "node:path";
88683
+ import { basename as basename10, join as join92, normalize as normalize8 } from "node:path";
88020
88684
  function validatePath2(path14, paramName) {
88021
88685
  if (!path14 || typeof path14 !== "string") {
88022
88686
  throw new SkillsMigrationError(`${paramName} must be a non-empty string`);
@@ -88042,7 +88706,7 @@ class SkillsBackupManager {
88042
88706
  const timestamp = Date.now();
88043
88707
  const randomSuffix = Math.random().toString(36).substring(2, 8);
88044
88708
  const backupDirName = `${SkillsBackupManager.BACKUP_PREFIX}${timestamp}-${randomSuffix}`;
88045
- const backupDir = parentDir ? join93(parentDir, backupDirName) : join93(skillsDir2, "..", backupDirName);
88709
+ const backupDir = parentDir ? join92(parentDir, backupDirName) : join92(skillsDir2, "..", backupDirName);
88046
88710
  logger.info(`Creating backup at: ${backupDir}`);
88047
88711
  try {
88048
88712
  await mkdir29(backupDir, { recursive: true });
@@ -88093,7 +88757,7 @@ class SkillsBackupManager {
88093
88757
  }
88094
88758
  try {
88095
88759
  const entries = await readdir27(parentDir, { withFileTypes: true });
88096
- const backups = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(SkillsBackupManager.BACKUP_PREFIX)).map((entry) => join93(parentDir, entry.name));
88760
+ const backups = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(SkillsBackupManager.BACKUP_PREFIX)).map((entry) => join92(parentDir, entry.name));
88097
88761
  backups.sort().reverse();
88098
88762
  return backups;
88099
88763
  } catch (error) {
@@ -88121,8 +88785,8 @@ class SkillsBackupManager {
88121
88785
  static async copyDirectory(sourceDir, destDir) {
88122
88786
  const entries = await readdir27(sourceDir, { withFileTypes: true });
88123
88787
  for (const entry of entries) {
88124
- const sourcePath = join93(sourceDir, entry.name);
88125
- const destPath = join93(destDir, entry.name);
88788
+ const sourcePath = join92(sourceDir, entry.name);
88789
+ const destPath = join92(destDir, entry.name);
88126
88790
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
88127
88791
  continue;
88128
88792
  }
@@ -88138,7 +88802,7 @@ class SkillsBackupManager {
88138
88802
  let size = 0;
88139
88803
  const entries = await readdir27(dirPath, { withFileTypes: true });
88140
88804
  for (const entry of entries) {
88141
- const fullPath = join93(dirPath, entry.name);
88805
+ const fullPath = join92(dirPath, entry.name);
88142
88806
  if (entry.isSymbolicLink()) {
88143
88807
  continue;
88144
88808
  }
@@ -88174,12 +88838,12 @@ init_skip_directories();
88174
88838
  import { createHash as createHash5 } from "node:crypto";
88175
88839
  import { createReadStream as createReadStream3 } from "node:fs";
88176
88840
  import { readFile as readFile45, readdir as readdir28 } from "node:fs/promises";
88177
- import { join as join94, relative as relative16 } from "node:path";
88841
+ import { join as join93, relative as relative16 } from "node:path";
88178
88842
  async function getAllFiles(dirPath) {
88179
88843
  const files = [];
88180
88844
  const entries = await readdir28(dirPath, { withFileTypes: true });
88181
88845
  for (const entry of entries) {
88182
- const fullPath = join94(dirPath, entry.name);
88846
+ const fullPath = join93(dirPath, entry.name);
88183
88847
  if (entry.name.startsWith(".") || BUILD_ARTIFACT_DIRS.includes(entry.name) || entry.isSymbolicLink()) {
88184
88848
  continue;
88185
88849
  }
@@ -88193,12 +88857,12 @@ async function getAllFiles(dirPath) {
88193
88857
  return files;
88194
88858
  }
88195
88859
  async function hashFile(filePath) {
88196
- return new Promise((resolve19, reject) => {
88860
+ return new Promise((resolve20, reject) => {
88197
88861
  const hash = createHash5("sha256");
88198
88862
  const stream = createReadStream3(filePath);
88199
88863
  stream.on("data", (chunk) => hash.update(chunk));
88200
88864
  stream.on("end", () => {
88201
- resolve19(hash.digest("hex"));
88865
+ resolve20(hash.digest("hex"));
88202
88866
  });
88203
88867
  stream.on("error", (error) => {
88204
88868
  stream.destroy();
@@ -88306,7 +88970,7 @@ async function detectFileChanges(currentSkillPath, baselineSkillPath) {
88306
88970
  init_types3();
88307
88971
  var import_fs_extra27 = __toESM(require_lib3(), 1);
88308
88972
  import { readdir as readdir29 } from "node:fs/promises";
88309
- import { join as join95, normalize as normalize9 } from "node:path";
88973
+ import { join as join94, normalize as normalize9 } from "node:path";
88310
88974
  function validatePath3(path14, paramName) {
88311
88975
  if (!path14 || typeof path14 !== "string") {
88312
88976
  throw new SkillsMigrationError(`${paramName} must be a non-empty string`);
@@ -88327,13 +88991,13 @@ async function scanSkillsDirectory(skillsDir2) {
88327
88991
  if (dirs.length === 0) {
88328
88992
  return ["flat", []];
88329
88993
  }
88330
- const firstDirPath = join95(skillsDir2, dirs[0].name);
88994
+ const firstDirPath = join94(skillsDir2, dirs[0].name);
88331
88995
  const subEntries = await readdir29(firstDirPath, { withFileTypes: true });
88332
88996
  const subdirs = subEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
88333
88997
  if (subdirs.length > 0) {
88334
88998
  let skillLikeCount = 0;
88335
88999
  for (const subdir of subdirs.slice(0, 3)) {
88336
- const subdirPath = join95(firstDirPath, subdir.name);
89000
+ const subdirPath = join94(firstDirPath, subdir.name);
88337
89001
  const subdirFiles = await readdir29(subdirPath, { withFileTypes: true });
88338
89002
  const hasSkillMarker = subdirFiles.some((file) => file.isFile() && (file.name === "skill.md" || file.name === "README.md" || file.name === "readme.md" || file.name === "config.json" || file.name === "package.json"));
88339
89003
  if (hasSkillMarker) {
@@ -88343,7 +89007,7 @@ async function scanSkillsDirectory(skillsDir2) {
88343
89007
  if (skillLikeCount > 0) {
88344
89008
  const skills = [];
88345
89009
  for (const dir of dirs) {
88346
- const categoryPath = join95(skillsDir2, dir.name);
89010
+ const categoryPath = join94(skillsDir2, dir.name);
88347
89011
  const skillDirs = await readdir29(categoryPath, { withFileTypes: true });
88348
89012
  skills.push(...skillDirs.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name));
88349
89013
  }
@@ -88353,7 +89017,7 @@ async function scanSkillsDirectory(skillsDir2) {
88353
89017
  return ["flat", dirs.map((dir) => dir.name)];
88354
89018
  }
88355
89019
  async function findSkillPath(skillsDir2, skillName) {
88356
- const flatPath = join95(skillsDir2, skillName);
89020
+ const flatPath = join94(skillsDir2, skillName);
88357
89021
  if (await import_fs_extra27.pathExists(flatPath)) {
88358
89022
  return { path: flatPath, category: undefined };
88359
89023
  }
@@ -88362,8 +89026,8 @@ async function findSkillPath(skillsDir2, skillName) {
88362
89026
  if (!entry.isDirectory() || entry.name.startsWith(".") || entry.name === "node_modules") {
88363
89027
  continue;
88364
89028
  }
88365
- const categoryPath = join95(skillsDir2, entry.name);
88366
- const skillPath = join95(categoryPath, skillName);
89029
+ const categoryPath = join94(skillsDir2, entry.name);
89030
+ const skillPath = join94(categoryPath, skillName);
88367
89031
  if (await import_fs_extra27.pathExists(skillPath)) {
88368
89032
  return { path: skillPath, category: entry.name };
88369
89033
  }
@@ -88457,7 +89121,7 @@ class SkillsMigrator {
88457
89121
  }
88458
89122
  }
88459
89123
  if (options2.backup && !options2.dryRun) {
88460
- const claudeDir2 = join96(currentSkillsDir, "..");
89124
+ const claudeDir2 = join95(currentSkillsDir, "..");
88461
89125
  result.backupPath = await SkillsBackupManager.createBackup(currentSkillsDir, claudeDir2);
88462
89126
  logger.success(`Backup created at: ${result.backupPath}`);
88463
89127
  }
@@ -88518,7 +89182,7 @@ async function handleMigration(ctx) {
88518
89182
  logger.debug("Skipping skills migration (fresh installation)");
88519
89183
  return ctx;
88520
89184
  }
88521
- const newSkillsDir = join97(ctx.extractDir, ".claude", "skills");
89185
+ const newSkillsDir = join96(ctx.extractDir, ".claude", "skills");
88522
89186
  const currentSkillsDir = PathResolver.buildSkillsPath(ctx.resolvedDir, ctx.options.global);
88523
89187
  if (!await import_fs_extra28.pathExists(newSkillsDir) || !await import_fs_extra28.pathExists(currentSkillsDir)) {
88524
89188
  return ctx;
@@ -88542,13 +89206,13 @@ async function handleMigration(ctx) {
88542
89206
  }
88543
89207
  // src/commands/init/phases/opencode-handler.ts
88544
89208
  import { cp as cp3, readdir as readdir31, rm as rm12 } from "node:fs/promises";
88545
- import { join as join99 } from "node:path";
89209
+ import { join as join98 } from "node:path";
88546
89210
 
88547
89211
  // src/services/transformers/opencode-path-transformer.ts
88548
89212
  init_logger();
88549
89213
  import { readFile as readFile46, readdir as readdir30, writeFile as writeFile28 } from "node:fs/promises";
88550
89214
  import { platform as platform12 } from "node:os";
88551
- import { extname as extname5, join as join98 } from "node:path";
89215
+ import { extname as extname5, join as join97 } from "node:path";
88552
89216
  var IS_WINDOWS2 = platform12() === "win32";
88553
89217
  function getOpenCodeGlobalPath() {
88554
89218
  return "$HOME/.config/opencode/";
@@ -88609,7 +89273,7 @@ async function transformPathsForGlobalOpenCode(directory, options2 = {}) {
88609
89273
  async function processDirectory2(dir) {
88610
89274
  const entries = await readdir30(dir, { withFileTypes: true });
88611
89275
  for (const entry of entries) {
88612
- const fullPath = join98(dir, entry.name);
89276
+ const fullPath = join97(dir, entry.name);
88613
89277
  if (entry.isDirectory()) {
88614
89278
  if (entry.name === "node_modules" || entry.name.startsWith(".")) {
88615
89279
  continue;
@@ -88648,7 +89312,7 @@ async function handleOpenCode(ctx) {
88648
89312
  if (ctx.cancelled || !ctx.extractDir || !ctx.resolvedDir) {
88649
89313
  return ctx;
88650
89314
  }
88651
- const openCodeSource = join99(ctx.extractDir, ".opencode");
89315
+ const openCodeSource = join98(ctx.extractDir, ".opencode");
88652
89316
  if (!await import_fs_extra29.pathExists(openCodeSource)) {
88653
89317
  logger.debug("No .opencode directory in archive, skipping");
88654
89318
  return ctx;
@@ -88666,8 +89330,8 @@ async function handleOpenCode(ctx) {
88666
89330
  await import_fs_extra29.ensureDir(targetDir);
88667
89331
  const entries = await readdir31(openCodeSource, { withFileTypes: true });
88668
89332
  for (const entry of entries) {
88669
- const sourcePath = join99(openCodeSource, entry.name);
88670
- const targetPath = join99(targetDir, entry.name);
89333
+ const sourcePath = join98(openCodeSource, entry.name);
89334
+ const targetPath = join98(targetDir, entry.name);
88671
89335
  if (await import_fs_extra29.pathExists(targetPath)) {
88672
89336
  if (!ctx.options.forceOverwrite) {
88673
89337
  logger.verbose(`Skipping existing: ${entry.name}`);
@@ -88764,20 +89428,21 @@ Please use only one download method.`);
88764
89428
  }
88765
89429
  // src/commands/init/phases/post-install-handler.ts
88766
89430
  init_projects_registry();
88767
- import { join as join100 } from "node:path";
89431
+ import { join as join101 } from "node:path";
89432
+ init_cc_version_checker();
88768
89433
  init_logger();
88769
89434
  init_path_resolver();
88770
- var import_fs_extra30 = __toESM(require_lib3(), 1);
89435
+ var import_fs_extra32 = __toESM(require_lib3(), 1);
88771
89436
  async function handlePostInstall(ctx) {
88772
89437
  if (ctx.cancelled || !ctx.extractDir || !ctx.resolvedDir || !ctx.claudeDir) {
88773
89438
  return ctx;
88774
89439
  }
88775
89440
  if (ctx.options.global) {
88776
- const claudeMdSource = join100(ctx.extractDir, "CLAUDE.md");
88777
- const claudeMdDest = join100(ctx.resolvedDir, "CLAUDE.md");
88778
- if (await import_fs_extra30.pathExists(claudeMdSource)) {
88779
- if (ctx.options.fresh || !await import_fs_extra30.pathExists(claudeMdDest)) {
88780
- await import_fs_extra30.copy(claudeMdSource, claudeMdDest);
89441
+ const claudeMdSource = join101(ctx.extractDir, "CLAUDE.md");
89442
+ const claudeMdDest = join101(ctx.resolvedDir, "CLAUDE.md");
89443
+ if (await import_fs_extra32.pathExists(claudeMdSource)) {
89444
+ if (ctx.options.fresh || !await import_fs_extra32.pathExists(claudeMdDest)) {
89445
+ await import_fs_extra32.copy(claudeMdSource, claudeMdDest);
88781
89446
  logger.success(ctx.options.fresh ? "Replaced CLAUDE.md in global directory (fresh install)" : "Copied CLAUDE.md to global directory");
88782
89447
  } else {
88783
89448
  logger.debug("CLAUDE.md already exists in global directory (preserved)");
@@ -88796,6 +89461,77 @@ async function handlePostInstall(ctx) {
88796
89461
  withSudo: ctx.options.withSudo
88797
89462
  });
88798
89463
  }
89464
+ let pluginSupported = false;
89465
+ try {
89466
+ const { requireCCPluginSupport: requireCCPluginSupport2 } = await Promise.resolve().then(() => (init_cc_version_checker(), exports_cc_version_checker));
89467
+ await requireCCPluginSupport2();
89468
+ pluginSupported = true;
89469
+ } catch (error) {
89470
+ if (error instanceof CCPluginSupportError) {
89471
+ logger.info(`Plugin install skipped: ${error.message}`);
89472
+ if (error.code === "cc_version_too_old") {
89473
+ logger.info("Upgrade: brew upgrade claude-code (or npm i -g @anthropic-ai/claude-code)");
89474
+ }
89475
+ if (error.code === "cc_not_found") {
89476
+ logger.info("Install Claude Code CLI, then re-run ck init to enable /ck:* plugin skills");
89477
+ }
89478
+ } else {
89479
+ logger.info(`Plugin install skipped: ${error instanceof Error ? error.message : "Unknown error"}`);
89480
+ }
89481
+ }
89482
+ let pluginVerified = false;
89483
+ if (pluginSupported) {
89484
+ try {
89485
+ const { handlePluginInstall: handlePluginInstall2 } = await Promise.resolve().then(() => (init_plugin_installer(), exports_plugin_installer));
89486
+ const pluginResult = await handlePluginInstall2(ctx.extractDir);
89487
+ pluginVerified = pluginResult.verified;
89488
+ if (pluginResult.error) {
89489
+ logger.info(`Plugin install issue: ${pluginResult.error}`);
89490
+ }
89491
+ } catch (error) {
89492
+ logger.debug(`Plugin install skipped: ${error instanceof Error ? error.message : "Unknown error"}`);
89493
+ }
89494
+ }
89495
+ if (ctx.claudeDir && ctx.kitType) {
89496
+ try {
89497
+ const { updateKitPluginState: updateKitPluginState2 } = await Promise.resolve().then(() => (init_metadata_migration(), exports_metadata_migration));
89498
+ await updateKitPluginState2(ctx.claudeDir, ctx.kitType, {
89499
+ pluginInstalled: pluginVerified,
89500
+ pluginInstalledAt: new Date().toISOString(),
89501
+ pluginVersion: ctx.selectedVersion || "unknown"
89502
+ });
89503
+ } catch {}
89504
+ }
89505
+ if (ctx.deferredDeletions?.length && ctx.claudeDir) {
89506
+ try {
89507
+ const { migrateUserSkills: migrateUserSkills2 } = await Promise.resolve().then(() => (init_skill_migration_merger(), exports_skill_migration_merger));
89508
+ const migration = await migrateUserSkills2(ctx.claudeDir, pluginVerified);
89509
+ if (pluginVerified) {
89510
+ if (!migration.canDelete) {
89511
+ logger.warning("Skill migration metadata unavailable — preserving existing skills (fail-safe)");
89512
+ return { ...ctx, installSkills, pluginSupported };
89513
+ }
89514
+ const preservedDirs = new Set(migration.preserved.map(normalizeSkillDir));
89515
+ const safeDeletions = ctx.deferredDeletions.filter((d3) => {
89516
+ const dirPath = extractSkillDirFromDeletionPath(d3);
89517
+ if (!dirPath)
89518
+ return true;
89519
+ return !preservedDirs.has(normalizeSkillDir(dirPath));
89520
+ });
89521
+ if (safeDeletions.length > 0) {
89522
+ const { handleDeletions: handleDeletions2 } = await Promise.resolve().then(() => (init_deletion_handler(), exports_deletion_handler));
89523
+ const deferredResult = await handleDeletions2({ deletions: safeDeletions }, ctx.claudeDir);
89524
+ if (deferredResult.deletedPaths.length > 0) {
89525
+ logger.info(`Removed ${deferredResult.deletedPaths.length} old skill file(s) (replaced by plugin)`);
89526
+ }
89527
+ }
89528
+ } else {
89529
+ logger.info("Plugin not verified — keeping existing skills as fallback");
89530
+ }
89531
+ } catch (error) {
89532
+ logger.debug(`Deferred skill deletion failed: ${error}`);
89533
+ }
89534
+ }
88799
89535
  if (!ctx.isNonInteractive) {
88800
89536
  const { isGeminiInstalled: isGeminiInstalled2 } = await Promise.resolve().then(() => (init_package_installer(), exports_package_installer));
88801
89537
  const { checkExistingGeminiConfig: checkExistingGeminiConfig2, findMcpConfigPath: findMcpConfigPath2, processGeminiMcpLinking: processGeminiMcpLinking2 } = await Promise.resolve().then(() => (init_gemini_mcp_linker(), exports_gemini_mcp_linker));
@@ -88822,7 +89558,7 @@ async function handlePostInstall(ctx) {
88822
89558
  }
88823
89559
  if (!ctx.options.skipSetup) {
88824
89560
  await promptSetupWizardIfNeeded({
88825
- envPath: join100(ctx.claudeDir, ".env"),
89561
+ envPath: join101(ctx.claudeDir, ".env"),
88826
89562
  claudeDir: ctx.claudeDir,
88827
89563
  isGlobal: ctx.options.global,
88828
89564
  isNonInteractive: ctx.isNonInteractive,
@@ -88839,14 +89575,26 @@ async function handlePostInstall(ctx) {
88839
89575
  }
88840
89576
  return {
88841
89577
  ...ctx,
88842
- installSkills
89578
+ installSkills,
89579
+ pluginSupported
88843
89580
  };
88844
89581
  }
89582
+ function normalizeSkillDir(path14) {
89583
+ return path14.replace(/\\/g, "/").replace(/\/+/g, "/");
89584
+ }
89585
+ function extractSkillDirFromDeletionPath(path14) {
89586
+ const normalized = normalizeSkillDir(path14);
89587
+ const parts = normalized.split("/").filter(Boolean);
89588
+ if (parts.length < 2 || parts[0] !== "skills") {
89589
+ return null;
89590
+ }
89591
+ return `skills/${parts[1]}`;
89592
+ }
88845
89593
  // src/commands/init/phases/selection-handler.ts
88846
89594
  init_config_manager();
88847
89595
  init_github_client();
88848
89596
  import { mkdir as mkdir30 } from "node:fs/promises";
88849
- import { join as join102, resolve as resolve20 } from "node:path";
89597
+ import { join as join103, resolve as resolve21 } from "node:path";
88850
89598
 
88851
89599
  // src/domains/github/kit-access-checker.ts
88852
89600
  init_logger();
@@ -88878,8 +89626,8 @@ async function detectAccessibleKits() {
88878
89626
  // src/domains/github/preflight-checker.ts
88879
89627
  init_logger();
88880
89628
  import { exec as exec8 } from "node:child_process";
88881
- import { promisify as promisify14 } from "node:util";
88882
- var execAsync8 = promisify14(exec8);
89629
+ import { promisify as promisify16 } from "node:util";
89630
+ var execAsync8 = promisify16(exec8);
88883
89631
  function createSuccessfulPreflightResult() {
88884
89632
  return {
88885
89633
  success: true,
@@ -88976,11 +89724,12 @@ async function runPreflightChecks() {
88976
89724
 
88977
89725
  // src/domains/installation/fresh-installer.ts
88978
89726
  init_metadata_migration();
88979
- import { existsSync as existsSync50, readdirSync as readdirSync4, rmSync as rmSync3, rmdirSync as rmdirSync2, unlinkSync as unlinkSync4 } from "node:fs";
88980
- import { dirname as dirname21, join as join101, resolve as resolve19 } from "node:path";
89727
+ init_manifest_reader();
88981
89728
  init_logger();
88982
89729
  init_safe_spinner();
88983
- var import_fs_extra31 = __toESM(require_lib3(), 1);
89730
+ var import_fs_extra33 = __toESM(require_lib3(), 1);
89731
+ import { existsSync as existsSync50, readdirSync as readdirSync4, rmSync as rmSync3, rmdirSync as rmdirSync2, unlinkSync as unlinkSync4 } from "node:fs";
89732
+ import { dirname as dirname22, join as join102, resolve as resolve20 } from "node:path";
88984
89733
  var CLAUDEKIT_SUBDIRECTORIES = ["commands", "agents", "skills", "rules", "hooks"];
88985
89734
  async function analyzeFreshInstallation(claudeDir2) {
88986
89735
  const metadata = await readManifest(claudeDir2);
@@ -89025,15 +89774,15 @@ async function analyzeFreshInstallation(claudeDir2) {
89025
89774
  };
89026
89775
  }
89027
89776
  function cleanupEmptyDirectories2(filePath, claudeDir2) {
89028
- const normalizedClaudeDir = resolve19(claudeDir2);
89029
- let currentDir = resolve19(dirname21(filePath));
89777
+ const normalizedClaudeDir = resolve20(claudeDir2);
89778
+ let currentDir = resolve20(dirname22(filePath));
89030
89779
  while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir)) {
89031
89780
  try {
89032
89781
  const entries = readdirSync4(currentDir);
89033
89782
  if (entries.length === 0) {
89034
89783
  rmdirSync2(currentDir);
89035
89784
  logger.debug(`Removed empty directory: ${currentDir}`);
89036
- currentDir = resolve19(dirname21(currentDir));
89785
+ currentDir = resolve20(dirname22(currentDir));
89037
89786
  } else {
89038
89787
  break;
89039
89788
  }
@@ -89050,7 +89799,7 @@ async function removeFilesByOwnership(claudeDir2, analysis, includeModified) {
89050
89799
  const filesToRemove = includeModified ? [...analysis.ckFiles, ...analysis.ckModifiedFiles] : analysis.ckFiles;
89051
89800
  const filesToPreserve = includeModified ? analysis.userFiles : [...analysis.ckModifiedFiles, ...analysis.userFiles];
89052
89801
  for (const file of filesToRemove) {
89053
- const fullPath = join101(claudeDir2, file.path);
89802
+ const fullPath = join102(claudeDir2, file.path);
89054
89803
  try {
89055
89804
  if (existsSync50(fullPath)) {
89056
89805
  unlinkSync4(fullPath);
@@ -89075,13 +89824,13 @@ async function removeFilesByOwnership(claudeDir2, analysis, includeModified) {
89075
89824
  };
89076
89825
  }
89077
89826
  async function updateMetadataAfterFresh(claudeDir2, removedFiles) {
89078
- const metadataPath = join101(claudeDir2, "metadata.json");
89079
- if (!await import_fs_extra31.pathExists(metadataPath)) {
89827
+ const metadataPath = join102(claudeDir2, "metadata.json");
89828
+ if (!await import_fs_extra33.pathExists(metadataPath)) {
89080
89829
  return;
89081
89830
  }
89082
89831
  let content;
89083
89832
  try {
89084
- content = await import_fs_extra31.readFile(metadataPath, "utf-8");
89833
+ content = await import_fs_extra33.readFile(metadataPath, "utf-8");
89085
89834
  } catch (readError) {
89086
89835
  logger.warning(`Failed to read metadata.json: ${readError instanceof Error ? readError.message : String(readError)}`);
89087
89836
  return;
@@ -89107,7 +89856,7 @@ async function updateMetadataAfterFresh(claudeDir2, removedFiles) {
89107
89856
  metadata.files = metadata.files.filter((f3) => !removedSet.has(f3.path));
89108
89857
  }
89109
89858
  try {
89110
- await import_fs_extra31.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
89859
+ await import_fs_extra33.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
89111
89860
  logger.debug(`Updated metadata.json, removed ${removedFiles.length} file entries`);
89112
89861
  } catch (writeError) {
89113
89862
  logger.warning(`Failed to write metadata.json: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
@@ -89118,16 +89867,16 @@ async function removeSubdirectoriesFallback(claudeDir2) {
89118
89867
  const removedFiles = [];
89119
89868
  let removedDirCount = 0;
89120
89869
  for (const subdir of CLAUDEKIT_SUBDIRECTORIES) {
89121
- const subdirPath = join101(claudeDir2, subdir);
89122
- if (await import_fs_extra31.pathExists(subdirPath)) {
89870
+ const subdirPath = join102(claudeDir2, subdir);
89871
+ if (await import_fs_extra33.pathExists(subdirPath)) {
89123
89872
  rmSync3(subdirPath, { recursive: true, force: true });
89124
89873
  removedDirCount++;
89125
89874
  removedFiles.push(`${subdir}/ (entire directory)`);
89126
89875
  logger.debug(`Removed subdirectory: ${subdir}/`);
89127
89876
  }
89128
89877
  }
89129
- const metadataPath = join101(claudeDir2, "metadata.json");
89130
- if (await import_fs_extra31.pathExists(metadataPath)) {
89878
+ const metadataPath = join102(claudeDir2, "metadata.json");
89879
+ if (await import_fs_extra33.pathExists(metadataPath)) {
89131
89880
  unlinkSync4(metadataPath);
89132
89881
  removedFiles.push("metadata.json");
89133
89882
  }
@@ -89140,7 +89889,7 @@ async function removeSubdirectoriesFallback(claudeDir2) {
89140
89889
  };
89141
89890
  }
89142
89891
  async function handleFreshInstallation(claudeDir2, prompts) {
89143
- if (!await import_fs_extra31.pathExists(claudeDir2)) {
89892
+ if (!await import_fs_extra33.pathExists(claudeDir2)) {
89144
89893
  logger.info(".claude directory does not exist, proceeding with fresh installation");
89145
89894
  return true;
89146
89895
  }
@@ -89172,10 +89921,11 @@ async function handleFreshInstallation(claudeDir2, prompts) {
89172
89921
 
89173
89922
  // src/commands/init/phases/selection-handler.ts
89174
89923
  init_claudekit_scanner();
89924
+ init_manifest_reader();
89175
89925
  init_logger();
89176
89926
  init_path_resolver();
89177
89927
  init_types3();
89178
- var import_fs_extra32 = __toESM(require_lib3(), 1);
89928
+ var import_fs_extra34 = __toESM(require_lib3(), 1);
89179
89929
 
89180
89930
  // src/commands/init/types.ts
89181
89931
  function isSyncContext(ctx) {
@@ -89344,7 +90094,7 @@ async function handleSelection(ctx) {
89344
90094
  }
89345
90095
  }
89346
90096
  }
89347
- const resolvedDir = resolve20(targetDir);
90097
+ const resolvedDir = resolve21(targetDir);
89348
90098
  logger.info(`Target directory: ${resolvedDir}`);
89349
90099
  if (!ctx.options.global && PathResolver.isLocalSameAsGlobal(resolvedDir)) {
89350
90100
  logger.warning("You're at HOME directory. Installing here modifies your GLOBAL ClaudeKit.");
@@ -89366,7 +90116,7 @@ async function handleSelection(ctx) {
89366
90116
  return { ...ctx, cancelled: true };
89367
90117
  }
89368
90118
  }
89369
- if (!await import_fs_extra32.pathExists(resolvedDir)) {
90119
+ if (!await import_fs_extra34.pathExists(resolvedDir)) {
89370
90120
  if (ctx.options.global) {
89371
90121
  await mkdir30(resolvedDir, { recursive: true });
89372
90122
  logger.info(`Created global directory: ${resolvedDir}`);
@@ -89378,7 +90128,7 @@ async function handleSelection(ctx) {
89378
90128
  }
89379
90129
  if (!ctx.options.fresh) {
89380
90130
  const prefix = PathResolver.getPathPrefix(ctx.options.global);
89381
- const claudeDir2 = prefix ? join102(resolvedDir, prefix) : resolvedDir;
90131
+ const claudeDir2 = prefix ? join103(resolvedDir, prefix) : resolvedDir;
89382
90132
  try {
89383
90133
  const existingMetadata = await readManifest(claudeDir2);
89384
90134
  if (existingMetadata?.kits) {
@@ -89410,7 +90160,7 @@ async function handleSelection(ctx) {
89410
90160
  }
89411
90161
  if (ctx.options.fresh) {
89412
90162
  const prefix = PathResolver.getPathPrefix(ctx.options.global);
89413
- const claudeDir2 = prefix ? join102(resolvedDir, prefix) : resolvedDir;
90163
+ const claudeDir2 = prefix ? join103(resolvedDir, prefix) : resolvedDir;
89414
90164
  const canProceed = await handleFreshInstallation(claudeDir2, ctx.prompts);
89415
90165
  if (!canProceed) {
89416
90166
  return { ...ctx, cancelled: true };
@@ -89429,7 +90179,7 @@ async function handleSelection(ctx) {
89429
90179
  logger.info("Fetching available versions...");
89430
90180
  let currentVersion = null;
89431
90181
  try {
89432
- const metadataPath = ctx.options.global ? join102(PathResolver.getGlobalKitDir(), "metadata.json") : join102(resolvedDir, ".claude", "metadata.json");
90182
+ const metadataPath = ctx.options.global ? join103(PathResolver.getGlobalKitDir(), "metadata.json") : join103(resolvedDir, ".claude", "metadata.json");
89433
90183
  const metadata = await readClaudeKitMetadata(metadataPath);
89434
90184
  currentVersion = metadata?.version || null;
89435
90185
  if (currentVersion) {
@@ -89503,25 +90253,27 @@ async function handleSelection(ctx) {
89503
90253
  };
89504
90254
  }
89505
90255
  // src/commands/init/phases/sync-handler.ts
89506
- import { copyFile as copyFile8, mkdir as mkdir31, open as open4, readFile as readFile48, rename as rename6, stat as stat17, unlink as unlink11, writeFile as writeFile30 } from "node:fs/promises";
89507
- import { dirname as dirname22, join as join103, resolve as resolve21 } from "node:path";
90256
+ import { copyFile as copyFile8, mkdir as mkdir31, open as open4, readFile as readFile49, rename as rename7, stat as stat17, unlink as unlink11, writeFile as writeFile30 } from "node:fs/promises";
90257
+ import { dirname as dirname23, join as join104, resolve as resolve22 } from "node:path";
90258
+ init_cc_version_checker();
90259
+ init_manifest_reader();
89508
90260
  init_logger();
89509
90261
  init_path_resolver();
89510
- var import_fs_extra33 = __toESM(require_lib3(), 1);
90262
+ var import_fs_extra35 = __toESM(require_lib3(), 1);
89511
90263
  var import_picocolors23 = __toESM(require_picocolors(), 1);
89512
90264
  async function handleSync(ctx) {
89513
90265
  if (!ctx.options.sync) {
89514
90266
  return ctx;
89515
90267
  }
89516
- const resolvedDir = ctx.options.global ? PathResolver.getGlobalKitDir() : resolve21(ctx.options.dir || ".");
89517
- const claudeDir2 = ctx.options.global ? resolvedDir : join103(resolvedDir, ".claude");
89518
- if (!await import_fs_extra33.pathExists(claudeDir2)) {
90268
+ const resolvedDir = ctx.options.global ? PathResolver.getGlobalKitDir() : resolve22(ctx.options.dir || ".");
90269
+ const claudeDir2 = ctx.options.global ? resolvedDir : join104(resolvedDir, ".claude");
90270
+ if (!await import_fs_extra35.pathExists(claudeDir2)) {
89519
90271
  logger.error("Cannot sync: no .claude directory found");
89520
90272
  ctx.prompts.note("Run 'ck init' without --sync to install first.", "No Installation Found");
89521
90273
  return { ...ctx, cancelled: true };
89522
90274
  }
89523
- const metadataPath = join103(claudeDir2, "metadata.json");
89524
- if (!await import_fs_extra33.pathExists(metadataPath)) {
90275
+ const metadataPath = join104(claudeDir2, "metadata.json");
90276
+ if (!await import_fs_extra35.pathExists(metadataPath)) {
89525
90277
  logger.error("Cannot sync: no metadata.json found");
89526
90278
  ctx.prompts.note(`Your installation may be from an older version.
89527
90279
  Run 'ck init' to update.`, "Legacy Installation");
@@ -89618,17 +90370,42 @@ function getLockTimeout() {
89618
90370
  return timeoutMs;
89619
90371
  }
89620
90372
  var STALE_LOCK_THRESHOLD_MS = 5 * 60 * 1000;
90373
+ function isProcessAlive(pid) {
90374
+ try {
90375
+ process.kill(pid, 0);
90376
+ return true;
90377
+ } catch (error) {
90378
+ const code2 = error.code;
90379
+ return code2 === "EPERM";
90380
+ }
90381
+ }
90382
+ async function readLockPayload(lockPath) {
90383
+ try {
90384
+ const raw2 = await readFile49(lockPath, "utf-8");
90385
+ const parsed = JSON.parse(raw2);
90386
+ if (typeof parsed.pid === "number" && Number.isInteger(parsed.pid) && parsed.pid > 0) {
90387
+ return {
90388
+ pid: parsed.pid,
90389
+ startedAt: typeof parsed.startedAt === "string" && parsed.startedAt.length > 0 ? parsed.startedAt : "unknown"
90390
+ };
90391
+ }
90392
+ } catch {}
90393
+ return null;
90394
+ }
89621
90395
  async function acquireSyncLock(global3) {
89622
90396
  const cacheDir = PathResolver.getCacheDir(global3);
89623
- const lockPath = join103(cacheDir, ".sync-lock");
90397
+ const lockPath = join104(cacheDir, ".sync-lock");
89624
90398
  const startTime = Date.now();
89625
90399
  const lockTimeout = getLockTimeout();
89626
- await mkdir31(dirname22(lockPath), { recursive: true });
90400
+ await mkdir31(dirname23(lockPath), { recursive: true });
89627
90401
  while (Date.now() - startTime < lockTimeout) {
89628
90402
  try {
89629
90403
  const handle = await open4(lockPath, "wx");
90404
+ await handle.writeFile(JSON.stringify({ pid: process.pid, startedAt: new Date().toISOString() }), "utf-8");
89630
90405
  return async () => {
89631
- await handle.close();
90406
+ try {
90407
+ await handle.close();
90408
+ } catch {}
89632
90409
  await unlink11(lockPath).catch(() => {});
89633
90410
  };
89634
90411
  } catch (err) {
@@ -89636,18 +90413,29 @@ async function acquireSyncLock(global3) {
89636
90413
  try {
89637
90414
  const lockStat = await stat17(lockPath);
89638
90415
  const lockAge = Math.abs(Date.now() - lockStat.mtimeMs);
89639
- if (lockAge > STALE_LOCK_THRESHOLD_MS) {
89640
- logger.warning(`Removing stale sync lock (age: ${Math.round(lockAge / 1000)}s)`);
90416
+ const lockOwner = await readLockPayload(lockPath);
90417
+ if (lockOwner?.pid === process.pid) {
90418
+ throw new Error("Sync lock is already held by current process");
90419
+ }
90420
+ const ownerAlive = lockOwner?.pid ? isProcessAlive(lockOwner.pid) : null;
90421
+ if (lockAge > STALE_LOCK_THRESHOLD_MS && ownerAlive !== true) {
90422
+ logger.warning(`Removing stale sync lock (age: ${Math.round(lockAge / 1000)}s${lockOwner?.pid ? `, pid=${lockOwner.pid}` : ""})`);
89641
90423
  await unlink11(lockPath).catch(() => {});
89642
90424
  continue;
89643
90425
  }
90426
+ if (lockAge > STALE_LOCK_THRESHOLD_MS && ownerAlive === true) {
90427
+ logger.debug(`Sync lock older than threshold but owner pid=${lockOwner?.pid} still alive; waiting`);
90428
+ }
89644
90429
  } catch (statError) {
90430
+ if (statError instanceof Error && statError.message.includes("already held by current process")) {
90431
+ throw statError;
90432
+ }
89645
90433
  if (statError.code === "ENOENT") {
89646
90434
  continue;
89647
90435
  }
89648
90436
  logger.debug(`Lock stat failed: ${statError}`);
89649
90437
  }
89650
- await new Promise((resolve22) => setTimeout(resolve22, 100));
90438
+ await new Promise((resolve23) => setTimeout(resolve23, 100));
89651
90439
  continue;
89652
90440
  }
89653
90441
  throw err;
@@ -89666,18 +90454,20 @@ async function executeSyncMerge(ctx) {
89666
90454
  const releaseLock = await acquireSyncLock(ctx.options.global);
89667
90455
  try {
89668
90456
  const trackedFiles = ctx.syncTrackedFiles;
89669
- const upstreamDir = ctx.options.global ? join103(ctx.extractDir, ".claude") : ctx.extractDir;
90457
+ const upstreamDir = ctx.options.global ? join104(ctx.extractDir, ".claude") : ctx.extractDir;
89670
90458
  let deletions = [];
89671
90459
  try {
89672
- const sourceMetadataPath = join103(upstreamDir, "metadata.json");
89673
- if (await import_fs_extra33.pathExists(sourceMetadataPath)) {
89674
- const content = await readFile48(sourceMetadataPath, "utf-8");
90460
+ const sourceMetadataPath = join104(upstreamDir, "metadata.json");
90461
+ if (await import_fs_extra35.pathExists(sourceMetadataPath)) {
90462
+ const content = await readFile49(sourceMetadataPath, "utf-8");
89675
90463
  const sourceMetadata = JSON.parse(content);
89676
90464
  deletions = sourceMetadata.deletions || [];
89677
90465
  }
89678
90466
  } catch (error) {
89679
90467
  logger.debug(`Failed to load source metadata for deletion filtering: ${error}`);
89680
90468
  }
90469
+ const { categorizeDeletions: categorizeDeletions2, handleDeletions: handleDeletions2 } = await Promise.resolve().then(() => (init_deletion_handler(), exports_deletion_handler));
90470
+ const categorizedDeletions = categorizeDeletions2(deletions);
89681
90471
  const filteredTrackedFiles = filterDeletionPaths(trackedFiles, deletions);
89682
90472
  if (deletions.length > 0) {
89683
90473
  const filtered = trackedFiles.length - filteredTrackedFiles.length;
@@ -89685,23 +90475,44 @@ async function executeSyncMerge(ctx) {
89685
90475
  }
89686
90476
  logger.info("Analyzing file changes...");
89687
90477
  const plan = await SyncEngine.createSyncPlan(filteredTrackedFiles, ctx.claudeDir, upstreamDir);
90478
+ const forceOverwriteNonInteractive = ctx.isNonInteractive && ctx.options.forceOverwrite;
90479
+ const autoUpdateQueue = forceOverwriteNonInteractive ? dedupeTrackedFiles([...plan.autoUpdate, ...plan.needsReview]) : plan.autoUpdate;
90480
+ const reviewQueue = forceOverwriteNonInteractive ? [] : plan.needsReview;
89688
90481
  displaySyncPlan(plan);
89689
- if (plan.autoUpdate.length === 0 && plan.needsReview.length === 0) {
90482
+ if (autoUpdateQueue.length === 0 && reviewQueue.length === 0 && categorizedDeletions.immediate.length === 0 && categorizedDeletions.deferred.length === 0) {
89690
90483
  ctx.prompts.note("All files are up to date or user-owned.", "No Changes Needed");
90484
+ }
90485
+ if (reviewQueue.length > 0 && ctx.isNonInteractive) {
90486
+ logger.error(`Cannot complete sync: ${reviewQueue.length} file(s) require interactive review`);
90487
+ ctx.prompts.note(`The following files have local modifications:
90488
+ ${reviewQueue.slice(0, 5).map((f3) => ` • ${f3.path}`).join(`
90489
+ `)}${reviewQueue.length > 5 ? `
90490
+ ... and ${reviewQueue.length - 5} more` : ""}
90491
+
90492
+ Options:
90493
+ 1. Run 'ck init --sync' without --yes for interactive merge
90494
+ 2. Use --force-overwrite to accept all upstream changes
90495
+ 3. Manually resolve conflicts before syncing`, "Sync Blocked");
89691
90496
  return { ...ctx, cancelled: true };
89692
90497
  }
89693
- const backupDir = PathResolver.getBackupDir();
89694
- await createBackup(ctx.claudeDir, trackedFiles, backupDir);
89695
- logger.success(`Backup created at ${import_picocolors23.default.dim(backupDir)}`);
89696
- if (plan.autoUpdate.length > 0) {
89697
- logger.info(`Auto-updating ${plan.autoUpdate.length} file(s)...`);
90498
+ if (forceOverwriteNonInteractive && plan.needsReview.length > 0) {
90499
+ logger.info(`--force-overwrite enabled: auto-updating ${plan.needsReview.length} locally modified file(s)`);
90500
+ }
90501
+ const willModifyFiles = autoUpdateQueue.length > 0 || reviewQueue.length > 0 || categorizedDeletions.immediate.length > 0 || categorizedDeletions.deferred.length > 0;
90502
+ if (willModifyFiles) {
90503
+ const backupDir = PathResolver.getBackupDir();
90504
+ await createBackup(ctx.claudeDir, trackedFiles, backupDir);
90505
+ logger.success(`Backup created at ${import_picocolors23.default.dim(backupDir)}`);
90506
+ }
90507
+ if (autoUpdateQueue.length > 0) {
90508
+ logger.info(`Auto-updating ${autoUpdateQueue.length} file(s)...`);
89698
90509
  let updateSuccess = 0;
89699
90510
  let updateFailed = 0;
89700
- for (const file of plan.autoUpdate) {
90511
+ for (const file of autoUpdateQueue) {
89701
90512
  try {
89702
90513
  const sourcePath = await validateSyncPath(upstreamDir, file.path);
89703
90514
  const targetPath = await validateSyncPath(ctx.claudeDir, file.path);
89704
- const targetDir = join103(targetPath, "..");
90515
+ const targetDir = dirname23(targetPath);
89705
90516
  try {
89706
90517
  await mkdir31(targetDir, { recursive: true });
89707
90518
  } catch (mkdirError) {
@@ -89711,7 +90522,7 @@ async function executeSyncMerge(ctx) {
89711
90522
  ctx.prompts.note("Your disk is full. Free up space and try again.", "Sync Failed");
89712
90523
  return { ...ctx, cancelled: true };
89713
90524
  }
89714
- if (errCode === "EROFS" || errCode === "EACCES") {
90525
+ if (errCode === "EROFS" || errCode === "EACCES" || errCode === "EPERM") {
89715
90526
  logger.warning(`Cannot create directory ${file.path}: ${errCode}`);
89716
90527
  updateFailed++;
89717
90528
  continue;
@@ -89729,7 +90540,7 @@ async function executeSyncMerge(ctx) {
89729
90540
  ctx.prompts.note("Your disk is full. Free up space and try again.", "Sync Failed");
89730
90541
  return { ...ctx, cancelled: true };
89731
90542
  }
89732
- if (errCode === "EACCES" || errCode === "EPERM") {
90543
+ if (errCode === "EACCES" || errCode === "EPERM" || errCode === "EROFS") {
89733
90544
  logger.warning(`Permission denied: ${file.path} - check file permissions`);
89734
90545
  updateFailed++;
89735
90546
  } else if (errMsg.includes("Symlink") || errMsg.includes("Path")) {
@@ -89744,19 +90555,22 @@ async function executeSyncMerge(ctx) {
89744
90555
  if (updateSuccess > 0) {
89745
90556
  logger.success(`Auto-updated ${updateSuccess} file(s)${updateFailed > 0 ? ` (${updateFailed} failed)` : ""}`);
89746
90557
  }
90558
+ if (updateSuccess === 0 && updateFailed > 0) {
90559
+ logger.warning("No files were updated due to write errors");
90560
+ }
89747
90561
  }
89748
- if (plan.needsReview.length > 0 && !ctx.isNonInteractive) {
89749
- logger.info(`${plan.needsReview.length} file(s) need interactive review...`);
90562
+ if (reviewQueue.length > 0 && !ctx.isNonInteractive) {
90563
+ logger.info(`${reviewQueue.length} file(s) need interactive review...`);
89750
90564
  let totalApplied = 0;
89751
90565
  let totalRejected = 0;
89752
90566
  let skippedFiles = 0;
89753
- for (const file of plan.needsReview) {
90567
+ for (const file of reviewQueue) {
89754
90568
  let currentPath;
89755
90569
  let upstreamPath;
89756
90570
  try {
89757
90571
  currentPath = await validateSyncPath(ctx.claudeDir, file.path);
89758
90572
  upstreamPath = await validateSyncPath(upstreamDir, file.path);
89759
- } catch (error) {
90573
+ } catch {
89760
90574
  logger.warning(`Skipping invalid path during review: ${file.path}`);
89761
90575
  skippedFiles++;
89762
90576
  continue;
@@ -89783,7 +90597,7 @@ async function executeSyncMerge(ctx) {
89783
90597
  const tempPath = `${currentPath}.tmp.${Date.now()}`;
89784
90598
  try {
89785
90599
  await writeFile30(tempPath, result.result, "utf-8");
89786
- await rename6(tempPath, currentPath);
90600
+ await rename7(tempPath, currentPath);
89787
90601
  } catch (atomicError) {
89788
90602
  await unlink11(tempPath).catch(() => {});
89789
90603
  throw atomicError;
@@ -89804,8 +90618,8 @@ async function executeSyncMerge(ctx) {
89804
90618
  console.log("");
89805
90619
  console.log(import_picocolors23.default.bold("Sync Summary:"));
89806
90620
  console.log(import_picocolors23.default.dim("─".repeat(40)));
89807
- if (plan.autoUpdate.length > 0) {
89808
- console.log(import_picocolors23.default.green(` ✓ ${plan.autoUpdate.length} file(s) auto-updated`));
90621
+ if (autoUpdateQueue.length > 0) {
90622
+ console.log(import_picocolors23.default.green(` ✓ ${autoUpdateQueue.length} file(s) auto-updated`));
89809
90623
  }
89810
90624
  if (totalApplied > 0) {
89811
90625
  console.log(import_picocolors23.default.green(` ✓ ${totalApplied} hunk(s) applied`));
@@ -89819,23 +90633,96 @@ async function executeSyncMerge(ctx) {
89819
90633
  if (plan.skipped.length > 0) {
89820
90634
  console.log(import_picocolors23.default.dim(` ─ ${plan.skipped.length} user-owned file(s) unchanged`));
89821
90635
  }
89822
- } else if (plan.needsReview.length > 0 && ctx.isNonInteractive) {
89823
- logger.error(`Cannot complete sync: ${plan.needsReview.length} file(s) require interactive review`);
89824
- ctx.prompts.note(`The following files have local modifications:
89825
- ${plan.needsReview.slice(0, 5).map((f3) => ` • ${f3.path}`).join(`
89826
- `)}${plan.needsReview.length > 5 ? `
89827
- ... and ${plan.needsReview.length - 5} more` : ""}
89828
-
89829
- Options:
89830
- 1. Run 'ck init --sync' without --yes for interactive merge
89831
- 2. Use --force-overwrite to accept all upstream changes
89832
- 3. Manually resolve conflicts before syncing`, "Sync Blocked");
89833
- return { ...ctx, cancelled: true };
90636
+ }
90637
+ if (categorizedDeletions.immediate.length > 0) {
90638
+ try {
90639
+ const deletionResult = await handleDeletions2({ deletions: categorizedDeletions.immediate }, ctx.claudeDir);
90640
+ if (deletionResult.deletedPaths.length > 0) {
90641
+ logger.info(`Removed ${deletionResult.deletedPaths.length} deprecated file(s)`);
90642
+ }
90643
+ if (deletionResult.preservedPaths.length > 0) {
90644
+ logger.verbose(`Preserved ${deletionResult.preservedPaths.length} user-owned file(s)`);
90645
+ }
90646
+ } catch (error) {
90647
+ logger.debug(`Immediate deletion cleanup failed during sync: ${error}`);
90648
+ }
90649
+ }
90650
+ let pluginSupported = false;
90651
+ let pluginVerified = false;
90652
+ try {
90653
+ const { requireCCPluginSupport: requireCCPluginSupport2 } = await Promise.resolve().then(() => (init_cc_version_checker(), exports_cc_version_checker));
90654
+ await requireCCPluginSupport2();
90655
+ pluginSupported = true;
90656
+ } catch (error) {
90657
+ if (error instanceof CCPluginSupportError) {
90658
+ logger.info(`Plugin install skipped during sync: ${error.message}`);
90659
+ if (error.code === "cc_version_too_old") {
90660
+ logger.info("Upgrade Claude Code, then re-run: ck init --sync (plugin migration requires >= 1.0.33)");
90661
+ }
90662
+ if (error.code === "cc_not_found") {
90663
+ logger.info("Install Claude Code CLI to enable /ck:* plugin skills");
90664
+ }
90665
+ } else {
90666
+ logger.debug(`Plugin version check failed during sync: ${error instanceof Error ? error.message : "Unknown error"}`);
90667
+ }
90668
+ }
90669
+ if (pluginSupported && ctx.extractDir) {
90670
+ try {
90671
+ const { handlePluginInstall: handlePluginInstall2 } = await Promise.resolve().then(() => (init_plugin_installer(), exports_plugin_installer));
90672
+ const pluginResult = await handlePluginInstall2(ctx.extractDir);
90673
+ pluginVerified = pluginResult.verified;
90674
+ if (pluginResult.error) {
90675
+ logger.debug(`Plugin install issue: ${pluginResult.error}`);
90676
+ }
90677
+ } catch (error) {
90678
+ logger.debug(`Plugin install skipped: ${error instanceof Error ? error.message : "Unknown error"}`);
90679
+ }
90680
+ }
90681
+ if (ctx.claudeDir && ctx.kitType) {
90682
+ try {
90683
+ const { updateKitPluginState: updateKitPluginState2 } = await Promise.resolve().then(() => (init_metadata_migration(), exports_metadata_migration));
90684
+ await updateKitPluginState2(ctx.claudeDir, ctx.kitType, {
90685
+ pluginInstalled: pluginVerified,
90686
+ pluginInstalledAt: new Date().toISOString(),
90687
+ pluginVersion: ctx.selectedVersion || ctx.syncLatestVersion || "unknown"
90688
+ });
90689
+ } catch {}
90690
+ }
90691
+ if (categorizedDeletions.deferred.length > 0) {
90692
+ if (pluginVerified) {
90693
+ try {
90694
+ const { migrateUserSkills: migrateUserSkills2 } = await Promise.resolve().then(() => (init_skill_migration_merger(), exports_skill_migration_merger));
90695
+ const migration = await migrateUserSkills2(ctx.claudeDir, pluginVerified);
90696
+ if (!migration.canDelete) {
90697
+ logger.warning("Skill migration metadata unavailable during sync — preserving existing skills");
90698
+ } else {
90699
+ const preservedDirs = new Set(migration.preserved.map(normalizeSkillDir2));
90700
+ const safeDeletions = categorizedDeletions.deferred.filter((path14) => {
90701
+ const skillDir = extractSkillDirFromDeletionPath2(path14);
90702
+ return !skillDir || !preservedDirs.has(normalizeSkillDir2(skillDir));
90703
+ });
90704
+ if (safeDeletions.length > 0) {
90705
+ const deferredResult = await handleDeletions2({ deletions: safeDeletions }, ctx.claudeDir);
90706
+ if (deferredResult.deletedPaths.length > 0) {
90707
+ logger.info(`Removed ${deferredResult.deletedPaths.length} old skill file(s) during sync`);
90708
+ }
90709
+ }
90710
+ }
90711
+ } catch (error) {
90712
+ logger.debug(`Deferred skill cleanup failed during sync: ${error}`);
90713
+ }
90714
+ } else {
90715
+ logger.info("Plugin not verified during sync — keeping existing skills as fallback");
90716
+ }
89834
90717
  }
89835
90718
  ctx.prompts.outro("Config sync completed successfully");
89836
90719
  return { ...ctx, cancelled: true };
89837
90720
  } finally {
89838
- await releaseLock();
90721
+ try {
90722
+ await releaseLock();
90723
+ } catch (error) {
90724
+ logger.debug(`Failed to release sync lock: ${error}`);
90725
+ }
89839
90726
  }
89840
90727
  }
89841
90728
  function displaySyncPlan(plan) {
@@ -89870,9 +90757,9 @@ async function createBackup(claudeDir2, files, backupDir) {
89870
90757
  for (const file of files) {
89871
90758
  try {
89872
90759
  const sourcePath = await validateSyncPath(claudeDir2, file.path);
89873
- if (await import_fs_extra33.pathExists(sourcePath)) {
90760
+ if (await import_fs_extra35.pathExists(sourcePath)) {
89874
90761
  const targetPath = await validateSyncPath(backupDir, file.path);
89875
- const targetDir = join103(targetPath, "..");
90762
+ const targetDir = dirname23(targetPath);
89876
90763
  await mkdir31(targetDir, { recursive: true });
89877
90764
  await copyFile8(sourcePath, targetPath);
89878
90765
  }
@@ -89885,9 +90772,27 @@ async function createBackup(claudeDir2, files, backupDir) {
89885
90772
  }
89886
90773
  }
89887
90774
  }
90775
+ function dedupeTrackedFiles(files) {
90776
+ const deduped = new Map;
90777
+ for (const file of files) {
90778
+ deduped.set(file.path, file);
90779
+ }
90780
+ return [...deduped.values()];
90781
+ }
90782
+ function normalizeSkillDir2(path14) {
90783
+ return path14.replace(/\\/g, "/").replace(/\/+/g, "/");
90784
+ }
90785
+ function extractSkillDirFromDeletionPath2(path14) {
90786
+ const normalized = normalizeSkillDir2(path14);
90787
+ const parts = normalized.split("/").filter(Boolean);
90788
+ if (parts.length < 2 || parts[0] !== "skills") {
90789
+ return null;
90790
+ }
90791
+ return `skills/${parts[1]}`;
90792
+ }
89888
90793
  // src/commands/init/phases/transform-handler.ts
89889
90794
  init_config_manager();
89890
- import { join as join107 } from "node:path";
90795
+ import { join as join108 } from "node:path";
89891
90796
 
89892
90797
  // src/services/transformers/folder-path-transformer.ts
89893
90798
  init_logger();
@@ -89896,40 +90801,40 @@ init_types3();
89896
90801
  // src/services/transformers/folder-transform/folder-renamer.ts
89897
90802
  init_logger();
89898
90803
  init_types3();
89899
- var import_fs_extra34 = __toESM(require_lib3(), 1);
89900
- import { rename as rename7, rm as rm13 } from "node:fs/promises";
89901
- import { join as join104, relative as relative18 } from "node:path";
90804
+ var import_fs_extra36 = __toESM(require_lib3(), 1);
90805
+ import { rename as rename8, rm as rm13 } from "node:fs/promises";
90806
+ import { join as join105, relative as relative18 } from "node:path";
89902
90807
  async function collectDirsToRename(extractDir, folders) {
89903
90808
  const dirsToRename = [];
89904
90809
  if (folders.docs !== DEFAULT_FOLDERS.docs) {
89905
- const docsPath = join104(extractDir, DEFAULT_FOLDERS.docs);
89906
- if (await import_fs_extra34.pathExists(docsPath)) {
90810
+ const docsPath = join105(extractDir, DEFAULT_FOLDERS.docs);
90811
+ if (await import_fs_extra36.pathExists(docsPath)) {
89907
90812
  dirsToRename.push({
89908
90813
  from: docsPath,
89909
- to: join104(extractDir, folders.docs)
90814
+ to: join105(extractDir, folders.docs)
89910
90815
  });
89911
90816
  }
89912
- const claudeDocsPath = join104(extractDir, ".claude", DEFAULT_FOLDERS.docs);
89913
- if (await import_fs_extra34.pathExists(claudeDocsPath)) {
90817
+ const claudeDocsPath = join105(extractDir, ".claude", DEFAULT_FOLDERS.docs);
90818
+ if (await import_fs_extra36.pathExists(claudeDocsPath)) {
89914
90819
  dirsToRename.push({
89915
90820
  from: claudeDocsPath,
89916
- to: join104(extractDir, ".claude", folders.docs)
90821
+ to: join105(extractDir, ".claude", folders.docs)
89917
90822
  });
89918
90823
  }
89919
90824
  }
89920
90825
  if (folders.plans !== DEFAULT_FOLDERS.plans) {
89921
- const plansPath = join104(extractDir, DEFAULT_FOLDERS.plans);
89922
- if (await import_fs_extra34.pathExists(plansPath)) {
90826
+ const plansPath = join105(extractDir, DEFAULT_FOLDERS.plans);
90827
+ if (await import_fs_extra36.pathExists(plansPath)) {
89923
90828
  dirsToRename.push({
89924
90829
  from: plansPath,
89925
- to: join104(extractDir, folders.plans)
90830
+ to: join105(extractDir, folders.plans)
89926
90831
  });
89927
90832
  }
89928
- const claudePlansPath = join104(extractDir, ".claude", DEFAULT_FOLDERS.plans);
89929
- if (await import_fs_extra34.pathExists(claudePlansPath)) {
90833
+ const claudePlansPath = join105(extractDir, ".claude", DEFAULT_FOLDERS.plans);
90834
+ if (await import_fs_extra36.pathExists(claudePlansPath)) {
89930
90835
  dirsToRename.push({
89931
90836
  from: claudePlansPath,
89932
- to: join104(extractDir, ".claude", folders.plans)
90837
+ to: join105(extractDir, ".claude", folders.plans)
89933
90838
  });
89934
90839
  }
89935
90840
  }
@@ -89937,11 +90842,11 @@ async function collectDirsToRename(extractDir, folders) {
89937
90842
  }
89938
90843
  async function moveAcrossDevices(src, dest) {
89939
90844
  try {
89940
- await rename7(src, dest);
90845
+ await rename8(src, dest);
89941
90846
  } catch (e2) {
89942
90847
  if (e2.code === "EXDEV") {
89943
90848
  logger.debug(`Cross-device move detected, using copy+delete: ${src} -> ${dest}`);
89944
- await import_fs_extra34.copy(src, dest, { overwrite: true });
90849
+ await import_fs_extra36.copy(src, dest, { overwrite: true });
89945
90850
  await rm13(src, { recursive: true, force: true });
89946
90851
  } else {
89947
90852
  throw e2;
@@ -89969,8 +90874,8 @@ async function renameFolders(dirsToRename, extractDir, options2) {
89969
90874
  // src/services/transformers/folder-transform/path-replacer.ts
89970
90875
  init_logger();
89971
90876
  init_types3();
89972
- import { readFile as readFile49, readdir as readdir32, writeFile as writeFile31 } from "node:fs/promises";
89973
- import { join as join105, relative as relative19 } from "node:path";
90877
+ import { readFile as readFile50, readdir as readdir32, writeFile as writeFile31 } from "node:fs/promises";
90878
+ import { join as join106, relative as relative19 } from "node:path";
89974
90879
  var TRANSFORMABLE_FILE_PATTERNS = [
89975
90880
  ".md",
89976
90881
  ".txt",
@@ -90023,7 +90928,7 @@ async function transformFileContents(dir, compiledReplacements, options2) {
90023
90928
  let replacementsCount = 0;
90024
90929
  const entries = await readdir32(dir, { withFileTypes: true });
90025
90930
  for (const entry of entries) {
90026
- const fullPath = join105(dir, entry.name);
90931
+ const fullPath = join106(dir, entry.name);
90027
90932
  if (entry.isDirectory()) {
90028
90933
  if (entry.name === "node_modules" || entry.name === ".git") {
90029
90934
  continue;
@@ -90036,7 +90941,7 @@ async function transformFileContents(dir, compiledReplacements, options2) {
90036
90941
  if (!shouldTransform)
90037
90942
  continue;
90038
90943
  try {
90039
- const content = await readFile49(fullPath, "utf-8");
90944
+ const content = await readFile50(fullPath, "utf-8");
90040
90945
  let newContent = content;
90041
90946
  let changeCount = 0;
90042
90947
  for (const { regex: regex2, replacement } of compiledReplacements) {
@@ -90158,9 +91063,9 @@ async function transformFolderPaths(extractDir, folders, options2 = {}) {
90158
91063
 
90159
91064
  // src/services/transformers/global-path-transformer.ts
90160
91065
  init_logger();
90161
- import { readFile as readFile50, readdir as readdir33, writeFile as writeFile32 } from "node:fs/promises";
91066
+ import { readFile as readFile51, readdir as readdir33, writeFile as writeFile32 } from "node:fs/promises";
90162
91067
  import { platform as platform13 } from "node:os";
90163
- import { extname as extname6, join as join106 } from "node:path";
91068
+ import { extname as extname6, join as join107 } from "node:path";
90164
91069
  var IS_WINDOWS3 = platform13() === "win32";
90165
91070
  var HOME_PREFIX = IS_WINDOWS3 ? "%USERPROFILE%" : "$HOME";
90166
91071
  function getHomeDirPrefix() {
@@ -90270,7 +91175,7 @@ async function transformPathsForGlobalInstall(directory, options2 = {}) {
90270
91175
  async function processDirectory2(dir) {
90271
91176
  const entries = await readdir33(dir, { withFileTypes: true });
90272
91177
  for (const entry of entries) {
90273
- const fullPath = join106(dir, entry.name);
91178
+ const fullPath = join107(dir, entry.name);
90274
91179
  if (entry.isDirectory()) {
90275
91180
  if (entry.name === "node_modules" || entry.name.startsWith(".") && entry.name !== ".claude") {
90276
91181
  continue;
@@ -90278,7 +91183,7 @@ async function transformPathsForGlobalInstall(directory, options2 = {}) {
90278
91183
  await processDirectory2(fullPath);
90279
91184
  } else if (entry.isFile() && shouldTransformFile3(entry.name)) {
90280
91185
  try {
90281
- const content = await readFile50(fullPath, "utf-8");
91186
+ const content = await readFile51(fullPath, "utf-8");
90282
91187
  const { transformed, changes } = transformContent(content);
90283
91188
  if (changes > 0) {
90284
91189
  await writeFile32(fullPath, transformed, "utf-8");
@@ -90346,7 +91251,7 @@ async function handleTransforms(ctx) {
90346
91251
  logger.debug(ctx.options.global ? "Saved folder configuration to ~/.claude/.ck.json" : "Saved folder configuration to .claude/.ck.json");
90347
91252
  }
90348
91253
  }
90349
- const claudeDir2 = ctx.options.global ? ctx.resolvedDir : join107(ctx.resolvedDir, ".claude");
91254
+ const claudeDir2 = ctx.options.global ? ctx.resolvedDir : join108(ctx.resolvedDir, ".claude");
90350
91255
  return {
90351
91256
  ...ctx,
90352
91257
  foldersConfig,
@@ -90540,8 +91445,8 @@ init_checksum_utils();
90540
91445
  init_config_discovery();
90541
91446
  var import_picocolors25 = __toESM(require_picocolors(), 1);
90542
91447
  import { existsSync as existsSync51 } from "node:fs";
90543
- import { readFile as readFile51, rm as rm14, unlink as unlink12 } from "node:fs/promises";
90544
- import { resolve as resolve22 } from "node:path";
91448
+ import { readFile as readFile52, rm as rm14, unlink as unlink12 } from "node:fs/promises";
91449
+ import { resolve as resolve23 } from "node:path";
90545
91450
 
90546
91451
  // src/commands/portable/conflict-resolver.ts
90547
91452
  init_dist2();
@@ -90888,7 +91793,7 @@ function shouldExecuteAction2(action) {
90888
91793
  }
90889
91794
  async function executeDeleteAction(action, options2) {
90890
91795
  const preservePaths = options2?.preservePaths ?? new Set;
90891
- const shouldPreserveTarget = action.targetPath.length > 0 && preservePaths.has(resolve22(action.targetPath));
91796
+ const shouldPreserveTarget = action.targetPath.length > 0 && preservePaths.has(resolve23(action.targetPath));
90892
91797
  try {
90893
91798
  if (!shouldPreserveTarget && action.targetPath && existsSync51(action.targetPath)) {
90894
91799
  await rm14(action.targetPath, { recursive: true, force: true });
@@ -91113,7 +92018,7 @@ async function migrateCommand(options2) {
91113
92018
  for (const action of conflictActions) {
91114
92019
  if (!action.diff && action.targetPath && existsSync51(action.targetPath)) {
91115
92020
  try {
91116
- const targetContent = await readFile51(action.targetPath, "utf-8");
92021
+ const targetContent = await readFile52(action.targetPath, "utf-8");
91117
92022
  const sourceItem = agents2.find((a3) => a3.name === action.item) || commands.find((c2) => c2.name === action.item) || (configItem?.name === action.item ? configItem : null) || ruleItems.find((r2) => r2.name === action.item);
91118
92023
  if (sourceItem) {
91119
92024
  const providerConfig = providers[action.provider];
@@ -91204,7 +92109,7 @@ async function migrateCommand(options2) {
91204
92109
  allResults.push(...await installSkillDirectories(skills, skillProviders, installOpts));
91205
92110
  }
91206
92111
  }
91207
- const writtenPaths = new Set(allResults.filter((result) => result.success && !result.skipped && result.path.length > 0).map((result) => resolve22(result.path)));
92112
+ const writtenPaths = new Set(allResults.filter((result) => result.success && !result.skipped && result.path.length > 0).map((result) => resolve23(result.path)));
91208
92113
  for (const deleteAction of plannedDeleteActions) {
91209
92114
  allResults.push(await executeDeleteAction(deleteAction, {
91210
92115
  preservePaths: writtenPaths
@@ -91320,7 +92225,7 @@ async function computeTargetStates(selectedProviders, global3) {
91320
92225
  exists: true
91321
92226
  };
91322
92227
  try {
91323
- const content = await readFile51(entry.path, "utf-8");
92228
+ const content = await readFile52(entry.path, "utf-8");
91324
92229
  state.currentChecksum = computeContentChecksum(content);
91325
92230
  } catch (error) {
91326
92231
  logger.debug(`[migrate] Failed to read target for checksum: ${entry.path} (${String(error)})`);
@@ -91379,11 +92284,11 @@ var import_picocolors26 = __toESM(require_picocolors(), 1);
91379
92284
 
91380
92285
  // src/commands/new/phases/directory-setup.ts
91381
92286
  init_config_manager();
91382
- import { resolve as resolve23 } from "node:path";
92287
+ import { resolve as resolve24 } from "node:path";
91383
92288
  init_logger();
91384
92289
  init_path_resolver();
91385
92290
  init_types3();
91386
- var import_fs_extra35 = __toESM(require_lib3(), 1);
92291
+ var import_fs_extra37 = __toESM(require_lib3(), 1);
91387
92292
  async function directorySetup(validOptions, prompts) {
91388
92293
  const isNonInteractive2 = !process.stdin.isTTY || process.env.CI === "true" || process.env.NON_INTERACTIVE === "true";
91389
92294
  const config = await ConfigManager.get();
@@ -91464,7 +92369,7 @@ async function directorySetup(validOptions, prompts) {
91464
92369
  targetDir = await prompts.getDirectory(targetDir);
91465
92370
  }
91466
92371
  }
91467
- const resolvedDir = resolve23(targetDir);
92372
+ const resolvedDir = resolve24(targetDir);
91468
92373
  logger.info(`Target directory: ${resolvedDir}`);
91469
92374
  if (PathResolver.isLocalSameAsGlobal(resolvedDir)) {
91470
92375
  logger.warning("You're creating a project at HOME directory.");
@@ -91482,8 +92387,8 @@ async function directorySetup(validOptions, prompts) {
91482
92387
  return null;
91483
92388
  }
91484
92389
  }
91485
- if (await import_fs_extra35.pathExists(resolvedDir)) {
91486
- const files = await import_fs_extra35.readdir(resolvedDir);
92390
+ if (await import_fs_extra37.pathExists(resolvedDir)) {
92391
+ const files = await import_fs_extra37.readdir(resolvedDir);
91487
92392
  const isEmpty = files.length === 0;
91488
92393
  if (!isEmpty) {
91489
92394
  if (isNonInteractive2) {
@@ -91521,7 +92426,7 @@ async function handleDirectorySetup(ctx) {
91521
92426
  // src/commands/new/phases/project-creation.ts
91522
92427
  init_config_manager();
91523
92428
  init_github_client();
91524
- import { join as join108 } from "node:path";
92429
+ import { join as join109 } from "node:path";
91525
92430
  init_logger();
91526
92431
  init_output_manager();
91527
92432
  init_types3();
@@ -91647,7 +92552,7 @@ async function projectCreation(kit, resolvedDir, validOptions, isNonInteractive2
91647
92552
  output.section("Installing");
91648
92553
  logger.verbose("Installation target", { directory: resolvedDir });
91649
92554
  const merger = new FileMerger;
91650
- const claudeDir2 = join108(resolvedDir, ".claude");
92555
+ const claudeDir2 = join109(resolvedDir, ".claude");
91651
92556
  merger.setMultiKitContext(claudeDir2, kit);
91652
92557
  if (validOptions.exclude && validOptions.exclude.length > 0) {
91653
92558
  merger.addIgnorePatterns(validOptions.exclude);
@@ -91694,7 +92599,7 @@ async function handleProjectCreation(ctx) {
91694
92599
  }
91695
92600
  // src/commands/new/phases/post-setup.ts
91696
92601
  init_projects_registry();
91697
- import { join as join109 } from "node:path";
92602
+ import { join as join110 } from "node:path";
91698
92603
  init_package_installer();
91699
92604
  init_logger();
91700
92605
  init_path_resolver();
@@ -91726,9 +92631,9 @@ async function postSetup(resolvedDir, validOptions, isNonInteractive2, prompts)
91726
92631
  withSudo: validOptions.withSudo
91727
92632
  });
91728
92633
  }
91729
- const claudeDir2 = join109(resolvedDir, ".claude");
92634
+ const claudeDir2 = join110(resolvedDir, ".claude");
91730
92635
  await promptSetupWizardIfNeeded({
91731
- envPath: join109(claudeDir2, ".env"),
92636
+ envPath: join110(claudeDir2, ".env"),
91732
92637
  claudeDir: claudeDir2,
91733
92638
  isGlobal: false,
91734
92639
  isNonInteractive: isNonInteractive2,
@@ -91801,11 +92706,11 @@ init_logger();
91801
92706
  init_safe_prompts();
91802
92707
  var import_picocolors27 = __toESM(require_picocolors(), 1);
91803
92708
  import { existsSync as existsSync52 } from "node:fs";
91804
- import { resolve as resolve24 } from "node:path";
92709
+ import { resolve as resolve25 } from "node:path";
91805
92710
  async function handleAdd(projectPath, options2) {
91806
92711
  logger.debug(`Adding project: ${projectPath}, options: ${JSON.stringify(options2)}`);
91807
92712
  intro("Add Project");
91808
- const absolutePath = resolve24(projectPath);
92713
+ const absolutePath = resolve25(projectPath);
91809
92714
  if (!existsSync52(absolutePath)) {
91810
92715
  log.error(`Path does not exist: ${absolutePath}`);
91811
92716
  process.exitCode = 1;
@@ -92695,7 +93600,7 @@ var import_picocolors32 = __toESM(require_picocolors(), 1);
92695
93600
  // src/commands/uninstall/installation-detector.ts
92696
93601
  init_claudekit_scanner();
92697
93602
  init_path_resolver();
92698
- var import_fs_extra36 = __toESM(require_lib3(), 1);
93603
+ var import_fs_extra38 = __toESM(require_lib3(), 1);
92699
93604
  function hasClaudeKitComponents(components) {
92700
93605
  return components.agents > 0 || components.commands > 0 || components.rules > 0 || components.skills > 0;
92701
93606
  }
@@ -92710,7 +93615,7 @@ async function detectInstallations() {
92710
93615
  installations.push({
92711
93616
  type: "local",
92712
93617
  path: setup.project.path,
92713
- exists: await import_fs_extra36.pathExists(setup.project.path),
93618
+ exists: await import_fs_extra38.pathExists(setup.project.path),
92714
93619
  hasMetadata,
92715
93620
  components: setup.project.components
92716
93621
  });
@@ -92723,7 +93628,7 @@ async function detectInstallations() {
92723
93628
  installations.push({
92724
93629
  type: "global",
92725
93630
  path: setup.global.path,
92726
- exists: await import_fs_extra36.pathExists(setup.global.path),
93631
+ exists: await import_fs_extra38.pathExists(setup.global.path),
92727
93632
  hasMetadata,
92728
93633
  components: setup.global.components
92729
93634
  });
@@ -92734,16 +93639,16 @@ async function detectInstallations() {
92734
93639
 
92735
93640
  // src/commands/uninstall/removal-handler.ts
92736
93641
  import { readdirSync as readdirSync6, rmSync as rmSync5 } from "node:fs";
92737
- import { join as join111, resolve as resolve25, sep as sep5 } from "node:path";
93642
+ import { join as join112, resolve as resolve26, sep as sep5 } from "node:path";
92738
93643
  init_logger();
92739
93644
  init_safe_prompts();
92740
93645
  init_safe_spinner();
92741
- var import_fs_extra37 = __toESM(require_lib3(), 1);
93646
+ var import_fs_extra39 = __toESM(require_lib3(), 1);
92742
93647
 
92743
93648
  // src/commands/uninstall/analysis-handler.ts
92744
93649
  init_metadata_migration();
92745
93650
  import { readdirSync as readdirSync5, rmSync as rmSync4 } from "node:fs";
92746
- import { dirname as dirname23, join as join110 } from "node:path";
93651
+ import { dirname as dirname24, join as join111 } from "node:path";
92747
93652
  init_logger();
92748
93653
  init_safe_prompts();
92749
93654
  var import_picocolors31 = __toESM(require_picocolors(), 1);
@@ -92761,7 +93666,7 @@ function classifyFileByOwnership(ownership, forceOverwrite, deleteReason) {
92761
93666
  }
92762
93667
  async function cleanupEmptyDirectories3(filePath, installationRoot) {
92763
93668
  let cleaned = 0;
92764
- let currentDir = dirname23(filePath);
93669
+ let currentDir = dirname24(filePath);
92765
93670
  while (currentDir !== installationRoot && currentDir.startsWith(installationRoot)) {
92766
93671
  try {
92767
93672
  const entries = readdirSync5(currentDir);
@@ -92769,7 +93674,7 @@ async function cleanupEmptyDirectories3(filePath, installationRoot) {
92769
93674
  rmSync4(currentDir, { recursive: true });
92770
93675
  cleaned++;
92771
93676
  logger.debug(`Removed empty directory: ${currentDir}`);
92772
- currentDir = dirname23(currentDir);
93677
+ currentDir = dirname24(currentDir);
92773
93678
  } else {
92774
93679
  break;
92775
93680
  }
@@ -92791,7 +93696,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
92791
93696
  if (uninstallManifest.isMultiKit && kit && metadata?.kits?.[kit]) {
92792
93697
  const kitFiles = metadata.kits[kit].files || [];
92793
93698
  for (const trackedFile of kitFiles) {
92794
- const filePath = join110(installation.path, trackedFile.path);
93699
+ const filePath = join111(installation.path, trackedFile.path);
92795
93700
  if (uninstallManifest.filesToPreserve.includes(trackedFile.path)) {
92796
93701
  result.toPreserve.push({ path: trackedFile.path, reason: "shared with other kit" });
92797
93702
  continue;
@@ -92821,7 +93726,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
92821
93726
  return result;
92822
93727
  }
92823
93728
  for (const trackedFile of allTrackedFiles) {
92824
- const filePath = join110(installation.path, trackedFile.path);
93729
+ const filePath = join111(installation.path, trackedFile.path);
92825
93730
  const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
92826
93731
  if (!ownershipResult.exists)
92827
93732
  continue;
@@ -92866,7 +93771,7 @@ function displayDryRunPreview(analysis, installationType) {
92866
93771
  // src/commands/uninstall/removal-handler.ts
92867
93772
  async function isDirectory(filePath) {
92868
93773
  try {
92869
- const stats = await import_fs_extra37.lstat(filePath);
93774
+ const stats = await import_fs_extra39.lstat(filePath);
92870
93775
  return stats.isDirectory();
92871
93776
  } catch {
92872
93777
  logger.debug(`Failed to check if path is directory: ${filePath}`);
@@ -92875,16 +93780,16 @@ async function isDirectory(filePath) {
92875
93780
  }
92876
93781
  async function isPathSafeToRemove(filePath, baseDir) {
92877
93782
  try {
92878
- const resolvedPath = resolve25(filePath);
92879
- const resolvedBase = resolve25(baseDir);
93783
+ const resolvedPath = resolve26(filePath);
93784
+ const resolvedBase = resolve26(baseDir);
92880
93785
  if (!resolvedPath.startsWith(resolvedBase + sep5) && resolvedPath !== resolvedBase) {
92881
93786
  logger.debug(`Path outside installation directory: ${filePath}`);
92882
93787
  return false;
92883
93788
  }
92884
- const stats = await import_fs_extra37.lstat(filePath);
93789
+ const stats = await import_fs_extra39.lstat(filePath);
92885
93790
  if (stats.isSymbolicLink()) {
92886
- const realPath = await import_fs_extra37.realpath(filePath);
92887
- const resolvedReal = resolve25(realPath);
93791
+ const realPath = await import_fs_extra39.realpath(filePath);
93792
+ const resolvedReal = resolve26(realPath);
92888
93793
  if (!resolvedReal.startsWith(resolvedBase + sep5) && resolvedReal !== resolvedBase) {
92889
93794
  logger.debug(`Symlink points outside installation directory: ${filePath} -> ${realPath}`);
92890
93795
  return false;
@@ -92918,15 +93823,15 @@ async function removeInstallations(installations, options2) {
92918
93823
  let removedCount = 0;
92919
93824
  let cleanedDirs = 0;
92920
93825
  for (const item of analysis.toDelete) {
92921
- const filePath = join111(installation.path, item.path);
92922
- if (!await import_fs_extra37.pathExists(filePath))
93826
+ const filePath = join112(installation.path, item.path);
93827
+ if (!await import_fs_extra39.pathExists(filePath))
92923
93828
  continue;
92924
93829
  if (!await isPathSafeToRemove(filePath, installation.path)) {
92925
93830
  logger.debug(`Skipping unsafe path: ${item.path}`);
92926
93831
  continue;
92927
93832
  }
92928
93833
  const isDir = await isDirectory(filePath);
92929
- await import_fs_extra37.remove(filePath);
93834
+ await import_fs_extra39.remove(filePath);
92930
93835
  removedCount++;
92931
93836
  logger.debug(`Removed ${isDir ? "directory" : "file"}: ${item.path}`);
92932
93837
  if (!isDir) {
@@ -93110,6 +94015,12 @@ ${import_picocolors32.default.yellow("User modifications will be permanently del
93110
94015
  forceOverwrite: validOptions.forceOverwrite,
93111
94016
  kit: validOptions.kit
93112
94017
  });
94018
+ if (!validOptions.kit || validOptions.kit === "engineer") {
94019
+ try {
94020
+ const { handlePluginUninstall: handlePluginUninstall2 } = await Promise.resolve().then(() => (init_plugin_installer(), exports_plugin_installer));
94021
+ await handlePluginUninstall2();
94022
+ } catch {}
94023
+ }
93113
94024
  const kitMsg = validOptions.kit ? ` (${validOptions.kit} kit)` : "";
93114
94025
  prompts.outro(`ClaudeKit${kitMsg} uninstalled successfully!`);
93115
94026
  } catch (error) {
@@ -93311,7 +94222,7 @@ init_logger();
93311
94222
  init_path_resolver();
93312
94223
  init_types3();
93313
94224
  import { existsSync as existsSync53, readFileSync as readFileSync10 } from "node:fs";
93314
- import { join as join112 } from "node:path";
94225
+ import { join as join113 } from "node:path";
93315
94226
  var packageVersion = package_default.version;
93316
94227
  function formatInstalledKits(metadata) {
93317
94228
  if (!metadata.kits || Object.keys(metadata.kits).length === 0) {
@@ -93343,9 +94254,9 @@ async function displayVersion() {
93343
94254
  let localKitVersion = null;
93344
94255
  let isGlobalOnlyKit = false;
93345
94256
  const globalKitDir = PathResolver.getGlobalKitDir();
93346
- const globalMetadataPath = join112(globalKitDir, "metadata.json");
94257
+ const globalMetadataPath = join113(globalKitDir, "metadata.json");
93347
94258
  const prefix = PathResolver.getPathPrefix(false);
93348
- const localMetadataPath = prefix ? join112(process.cwd(), prefix, "metadata.json") : join112(process.cwd(), "metadata.json");
94259
+ const localMetadataPath = prefix ? join113(process.cwd(), prefix, "metadata.json") : join113(process.cwd(), "metadata.json");
93349
94260
  const isLocalSameAsGlobal = localMetadataPath === globalMetadataPath;
93350
94261
  if (!isLocalSameAsGlobal && existsSync53(localMetadataPath)) {
93351
94262
  try {
@@ -93738,7 +94649,7 @@ var output2 = new OutputManager2;
93738
94649
 
93739
94650
  // src/shared/temp-cleanup.ts
93740
94651
  init_logger();
93741
- var import_fs_extra38 = __toESM(require_lib3(), 1);
94652
+ var import_fs_extra40 = __toESM(require_lib3(), 1);
93742
94653
  import { rmSync as rmSync6 } from "node:fs";
93743
94654
  var tempDirs2 = new Set;
93744
94655
  async function cleanup() {
@@ -93747,7 +94658,7 @@ async function cleanup() {
93747
94658
  logger.debug(`Cleaning up ${tempDirs2.size} temporary director(ies)...`);
93748
94659
  for (const dir of tempDirs2) {
93749
94660
  try {
93750
- await import_fs_extra38.remove(dir);
94661
+ await import_fs_extra40.remove(dir);
93751
94662
  logger.debug(`Cleaned up temp directory: ${dir}`);
93752
94663
  } catch (error) {
93753
94664
  logger.debug(`Failed to clean temp directory ${dir}: ${error}`);