claudekit-cli 3.5.1 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +353 -138
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7411,7 +7411,7 @@ var require_umd = __commonJS((exports, module) => {
7411
7411
  }
7412
7412
  return 0;
7413
7413
  };
7414
- const compareVersions2 = (v1, v2) => {
7414
+ const compareVersions3 = (v1, v2) => {
7415
7415
  const n1 = validateAndParse(v1);
7416
7416
  const n2 = validateAndParse(v2);
7417
7417
  const p1 = n1.pop();
@@ -7428,7 +7428,7 @@ var require_umd = __commonJS((exports, module) => {
7428
7428
  };
7429
7429
  const compare = (v1, v2, operator) => {
7430
7430
  assertValidOperator(operator);
7431
- const res = compareVersions2(v1, v2);
7431
+ const res = compareVersions3(v1, v2);
7432
7432
  return operatorResMap[operator].includes(res);
7433
7433
  };
7434
7434
  const operatorResMap = {
@@ -7485,7 +7485,7 @@ var require_umd = __commonJS((exports, module) => {
7485
7485
  const validate = (version) => typeof version === "string" && /^[v\d]/.test(version) && semver.test(version);
7486
7486
  const validateStrict = (version) => typeof version === "string" && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/.test(version);
7487
7487
  exports2.compare = compare;
7488
- exports2.compareVersions = compareVersions2;
7488
+ exports2.compareVersions = compareVersions3;
7489
7489
  exports2.satisfies = satisfies;
7490
7490
  exports2.validate = validate;
7491
7491
  exports2.validateStrict = validateStrict;
@@ -12725,6 +12725,116 @@ var require_emoji_regex2 = __commonJS((exports, module) => {
12725
12725
  };
12726
12726
  });
12727
12727
 
12728
+ // src/utils/install-error-handler.ts
12729
+ import { existsSync as existsSync5, readFileSync as readFileSync4, unlinkSync as unlinkSync2 } from "node:fs";
12730
+ import { join as join17 } from "node:path";
12731
+ function parseNameReason(str) {
12732
+ const colonIndex = str.indexOf(":");
12733
+ if (colonIndex === -1) {
12734
+ return [str.trim(), undefined];
12735
+ }
12736
+ return [str.slice(0, colonIndex).trim(), str.slice(colonIndex + 1).trim()];
12737
+ }
12738
+ function displayInstallErrors(skillsDir) {
12739
+ const summaryPath = join17(skillsDir, ".install-error-summary.json");
12740
+ if (!existsSync5(summaryPath)) {
12741
+ logger.error("Skills installation failed. Run with --verbose for details.");
12742
+ return;
12743
+ }
12744
+ let summary;
12745
+ try {
12746
+ summary = JSON.parse(readFileSync4(summaryPath, "utf-8"));
12747
+ } catch (parseError) {
12748
+ logger.error("Failed to parse error summary. File may be corrupted.");
12749
+ logger.debug(`Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
12750
+ return;
12751
+ }
12752
+ try {
12753
+ if (summary.critical_failures.length > 0) {
12754
+ logger.error("");
12755
+ logger.error("━━━ Critical Failures ━━━");
12756
+ for (const failure of summary.critical_failures) {
12757
+ const [name2, reason] = parseNameReason(failure);
12758
+ logger.error(` ✗ ${name2}`);
12759
+ if (reason)
12760
+ logger.error(` Reason: ${reason}`);
12761
+ }
12762
+ logger.error("");
12763
+ logger.error("These must be fixed before skills can work.");
12764
+ }
12765
+ if (summary.optional_failures.length > 0) {
12766
+ logger.warning("");
12767
+ logger.warning("━━━ Optional Package Failures ━━━");
12768
+ for (const failure of summary.optional_failures) {
12769
+ const [name2, reason] = parseNameReason(failure);
12770
+ logger.warning(` ! ${name2}`);
12771
+ if (reason)
12772
+ logger.info(` Reason: ${reason}`);
12773
+ }
12774
+ }
12775
+ if (summary.skipped.length > 0) {
12776
+ logger.info("");
12777
+ logger.info("━━━ Skipped (No sudo) ━━━");
12778
+ for (const skipped of summary.skipped) {
12779
+ const [name2] = parseNameReason(skipped);
12780
+ logger.info(` ~ ${name2}`);
12781
+ }
12782
+ }
12783
+ logger.info("");
12784
+ logger.info("━━━ How to Fix ━━━");
12785
+ logger.info("");
12786
+ if (summary.optional_failures.some((f3) => f3.includes("no wheel") || f3.includes("build tools") || f3.includes("build failed")) && summary.remediation.build_tools) {
12787
+ logger.info("Install build tools (one-time):");
12788
+ logger.info(` ${summary.remediation.build_tools}`);
12789
+ logger.info("");
12790
+ }
12791
+ if (summary.skipped.length > 0 && summary.remediation.sudo_packages) {
12792
+ logger.info("Install system packages:");
12793
+ logger.info(` ${summary.remediation.sudo_packages}`);
12794
+ logger.info("");
12795
+ }
12796
+ if (summary.optional_failures.length > 0 && summary.remediation.pip_retry) {
12797
+ logger.info("Then retry failed packages manually:");
12798
+ logger.info(` ${summary.remediation.pip_retry}`);
12799
+ }
12800
+ try {
12801
+ unlinkSync2(summaryPath);
12802
+ } catch (cleanupError) {
12803
+ if (cleanupError.code !== "ENOENT") {
12804
+ logger.debug(`Failed to cleanup summary file: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`);
12805
+ }
12806
+ }
12807
+ } catch (displayError) {
12808
+ logger.error("Failed to display error summary.");
12809
+ logger.debug(`Display error: ${displayError instanceof Error ? displayError.message : String(displayError)}`);
12810
+ }
12811
+ }
12812
+ async function checkNeedsSudoPackages() {
12813
+ if (process.platform !== "linux") {
12814
+ return false;
12815
+ }
12816
+ const { exec: exec5 } = await import("node:child_process");
12817
+ const { promisify: promisify5 } = await import("node:util");
12818
+ const execAsync5 = promisify5(exec5);
12819
+ try {
12820
+ await Promise.all([
12821
+ execAsync5("which ffmpeg", { timeout: WHICH_COMMAND_TIMEOUT_MS }),
12822
+ execAsync5("which convert", { timeout: WHICH_COMMAND_TIMEOUT_MS })
12823
+ ]);
12824
+ return false;
12825
+ } catch {
12826
+ return true;
12827
+ }
12828
+ }
12829
+ function hasInstallState(skillsDir) {
12830
+ const stateFilePath = join17(skillsDir, ".install-state.json");
12831
+ return existsSync5(stateFilePath);
12832
+ }
12833
+ var WHICH_COMMAND_TIMEOUT_MS = 5000;
12834
+ var init_install_error_handler = __esm(() => {
12835
+ init_logger();
12836
+ });
12837
+
12728
12838
  // src/utils/package-installer.ts
12729
12839
  var exports_package_installer = {};
12730
12840
  __export(exports_package_installer, {
@@ -12741,7 +12851,7 @@ __export(exports_package_installer, {
12741
12851
  getPackageVersion: () => getPackageVersion
12742
12852
  });
12743
12853
  import { exec as exec5, execFile as execFile2, spawn } from "node:child_process";
12744
- import { resolve as resolve2 } from "node:path";
12854
+ import { join as join18, resolve as resolve2 } from "node:path";
12745
12855
  import { promisify as promisify5 } from "node:util";
12746
12856
  function executeInteractiveScript(command, args, options) {
12747
12857
  return new Promise((resolve3, reject) => {
@@ -12935,9 +13045,8 @@ async function installOpenCode() {
12935
13045
  try {
12936
13046
  logger.info(`Installing ${displayName}...`);
12937
13047
  const { unlink: unlink2 } = await import("node:fs/promises");
12938
- const { join: join17 } = await import("node:path");
12939
13048
  const { tmpdir: tmpdir3 } = await import("node:os");
12940
- const tempScriptPath = join17(tmpdir3(), "opencode-install.sh");
13049
+ const tempScriptPath = join18(tmpdir3(), "opencode-install.sh");
12941
13050
  try {
12942
13051
  logger.info("Downloading OpenCode installation script...");
12943
13052
  await execFileAsync("curl", ["-fsSL", "https://opencode.ai/install", "-o", tempScriptPath], {
@@ -13047,13 +13156,12 @@ async function installSkillsDependencies(skillsDir) {
13047
13156
  };
13048
13157
  }
13049
13158
  try {
13050
- const { existsSync: existsSync5 } = await import("node:fs");
13159
+ const { existsSync: existsSync6 } = await import("node:fs");
13051
13160
  const { readFile: readFile9 } = await import("node:fs/promises");
13052
- const { join: join17 } = await import("node:path");
13053
13161
  const clack = await Promise.resolve().then(() => (init_dist2(), exports_dist));
13054
13162
  const platform8 = process.platform;
13055
13163
  const scriptName = platform8 === "win32" ? "install.ps1" : "install.sh";
13056
- const scriptPath = join17(skillsDir, scriptName);
13164
+ const scriptPath = join18(skillsDir, scriptName);
13057
13165
  try {
13058
13166
  validateScriptPath(skillsDir, scriptPath);
13059
13167
  } catch (error) {
@@ -13065,11 +13173,11 @@ async function installSkillsDependencies(skillsDir) {
13065
13173
  error: `Path validation failed: ${errorMessage}`
13066
13174
  };
13067
13175
  }
13068
- if (!existsSync5(scriptPath)) {
13176
+ if (!existsSync6(scriptPath)) {
13069
13177
  logger.warning(`Skills installation script not found: ${scriptPath}`);
13070
13178
  logger.info("");
13071
13179
  logger.info("\uD83D\uDCD6 Manual Installation Instructions:");
13072
- logger.info(` See: ${join17(skillsDir, "INSTALLATION.md")}`);
13180
+ logger.info(` See: ${join18(skillsDir, "INSTALLATION.md")}`);
13073
13181
  logger.info("");
13074
13182
  logger.info("Quick start:");
13075
13183
  logger.info(" cd .claude/skills/ai-multimodal/scripts");
@@ -13113,7 +13221,7 @@ async function installSkillsDependencies(skillsDir) {
13113
13221
  logger.info(` ${platform8 === "win32" ? `powershell -File "${scriptPath}"` : `bash ${scriptPath}`}`);
13114
13222
  logger.info("");
13115
13223
  logger.info("Or see complete guide:");
13116
- logger.info(` ${join17(skillsDir, "INSTALLATION.md")}`);
13224
+ logger.info(` ${join18(skillsDir, "INSTALLATION.md")}`);
13117
13225
  return {
13118
13226
  success: false,
13119
13227
  package: displayName,
@@ -13122,6 +13230,47 @@ async function installSkillsDependencies(skillsDir) {
13122
13230
  }
13123
13231
  logger.info(`Installing ${displayName}...`);
13124
13232
  logger.info(`Running: ${scriptPath}`);
13233
+ const scriptArgs = ["--yes"];
13234
+ if (hasInstallState(skillsDir)) {
13235
+ if (isNonInteractive()) {
13236
+ logger.info("Resuming previous installation (non-interactive mode)...");
13237
+ scriptArgs.push("--resume");
13238
+ } else {
13239
+ const shouldResume = await clack.confirm({
13240
+ message: "Previous installation was interrupted. Resume?",
13241
+ initialValue: true
13242
+ });
13243
+ if (!clack.isCancel(shouldResume) && shouldResume) {
13244
+ scriptArgs.push("--resume");
13245
+ logger.info("Resuming previous installation...");
13246
+ }
13247
+ }
13248
+ }
13249
+ if (platform8 === "linux") {
13250
+ const needsSudo = await checkNeedsSudoPackages();
13251
+ if (needsSudo) {
13252
+ if (isNonInteractive()) {
13253
+ logger.info("Skipping system packages in non-interactive mode.");
13254
+ logger.info("Install manually: sudo apt-get install -y ffmpeg imagemagick");
13255
+ } else {
13256
+ logger.info("");
13257
+ logger.info("System packages (requires sudo):");
13258
+ logger.info(" • ffmpeg - Video/audio processing");
13259
+ logger.info(" • imagemagick - Image editing & conversion");
13260
+ logger.info("");
13261
+ const shouldInstallSudo = await clack.confirm({
13262
+ message: "Install these packages? (requires sudo password)",
13263
+ initialValue: true
13264
+ });
13265
+ if (!clack.isCancel(shouldInstallSudo) && shouldInstallSudo) {
13266
+ scriptArgs.push("--with-sudo");
13267
+ } else {
13268
+ logger.info("Skipping system packages. Install manually later:");
13269
+ logger.info(" sudo apt-get install -y ffmpeg imagemagick");
13270
+ }
13271
+ }
13272
+ }
13273
+ }
13125
13274
  const scriptEnv = {
13126
13275
  ...process.env,
13127
13276
  NON_INTERACTIVE: "1"
@@ -13137,7 +13286,7 @@ async function installSkillsDependencies(skillsDir) {
13137
13286
  env: scriptEnv
13138
13287
  });
13139
13288
  } else {
13140
- await executeInteractiveScript("bash", [scriptPath, "--yes"], {
13289
+ await executeInteractiveScript("bash", [scriptPath, ...scriptArgs], {
13141
13290
  timeout: 600000,
13142
13291
  cwd: skillsDir,
13143
13292
  env: scriptEnv
@@ -13150,13 +13299,34 @@ async function installSkillsDependencies(skillsDir) {
13150
13299
  };
13151
13300
  } catch (error) {
13152
13301
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
13153
- logger.error(`Failed to install ${displayName}: ${errorMessage}`);
13302
+ const exitCodeMatch = errorMessage.match(/exited with code (\d+)/);
13303
+ const exitCode = exitCodeMatch ? Number.parseInt(exitCodeMatch[1], 10) : 1;
13304
+ if (exitCode === EXIT_CODE_PARTIAL_SUCCESS) {
13305
+ displayInstallErrors(skillsDir);
13306
+ logger.info("");
13307
+ logger.success("Core functionality is available despite some package failures.");
13308
+ return {
13309
+ success: true,
13310
+ package: displayName,
13311
+ version: PARTIAL_INSTALL_VERSION
13312
+ };
13313
+ }
13314
+ if (exitCode === EXIT_CODE_CRITICAL_FAILURE) {
13315
+ displayInstallErrors(skillsDir);
13316
+ logger.error("");
13317
+ logger.error("Skills installation failed. See above for details.");
13318
+ return {
13319
+ success: false,
13320
+ package: displayName,
13321
+ error: "Critical dependencies missing"
13322
+ };
13323
+ }
13324
+ logger.error(`Unexpected error: ${errorMessage}`);
13154
13325
  logger.info("");
13155
13326
  logger.info("\uD83D\uDCD6 Manual Installation Instructions:");
13156
13327
  logger.info("");
13157
13328
  logger.info("See complete guide:");
13158
- const { join: join17 } = await import("node:path");
13159
- logger.info(` cat ${join17(skillsDir, "INSTALLATION.md")}`);
13329
+ logger.info(` cat ${join18(skillsDir, "INSTALLATION.md")}`);
13160
13330
  logger.info("");
13161
13331
  logger.info("Quick start:");
13162
13332
  logger.info(" cd .claude/skills/ai-multimodal/scripts");
@@ -13177,19 +13347,24 @@ async function handleSkillsInstallation(skillsDir) {
13177
13347
  try {
13178
13348
  const skillsResult = await installSkillsDependencies(skillsDir);
13179
13349
  if (skillsResult.success) {
13180
- logger.success("Skills dependencies installed successfully");
13350
+ if (skillsResult.version === PARTIAL_INSTALL_VERSION) {
13351
+ logger.success("Skills core dependencies installed (some optional packages skipped)");
13352
+ } else {
13353
+ logger.success("Skills dependencies installed successfully");
13354
+ }
13181
13355
  } else {
13182
- logger.warning(`Skills installation failed: ${skillsResult.error || "Unknown error"}`);
13183
- logger.info(`You can install skills dependencies manually later by running the installation script in ${skillsDir}`);
13356
+ logger.warning(`Skills installation incomplete: ${skillsResult.error || "Unknown error"}`);
13357
+ logger.info("You can install skills dependencies manually. See INSTALLATION.md");
13184
13358
  }
13185
- } catch (error) {
13186
- logger.warning(`Skills installation failed: ${error instanceof Error ? error.message : String(error)}`);
13359
+ } catch {
13360
+ logger.warning("Skills installation failed");
13187
13361
  logger.info("You can install skills dependencies manually later");
13188
13362
  }
13189
13363
  }
13190
- var execAsync5, execFileAsync, NPM_PACKAGE_REGEX;
13364
+ var execAsync5, execFileAsync, PARTIAL_INSTALL_VERSION = "partial", EXIT_CODE_CRITICAL_FAILURE = 1, EXIT_CODE_PARTIAL_SUCCESS = 2, NPM_PACKAGE_REGEX;
13191
13365
  var init_package_installer = __esm(() => {
13192
13366
  init_environment();
13367
+ init_install_error_handler();
13193
13368
  init_logger();
13194
13369
  execAsync5 = promisify5(exec5);
13195
13370
  execFileAsync = promisify5(execFile2);
@@ -14144,8 +14319,8 @@ var init_help_interceptor = __esm(() => {
14144
14319
  });
14145
14320
 
14146
14321
  // src/index.ts
14147
- import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
14148
- import { join as join31 } from "path";
14322
+ import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
14323
+ import { join as join33 } from "path";
14149
14324
 
14150
14325
  // node_modules/cac/dist/index.mjs
14151
14326
  import { EventEmitter } from "events";
@@ -14750,7 +14925,7 @@ var cac = (name = "") => new CAC(name);
14750
14925
  // package.json
14751
14926
  var package_default = {
14752
14927
  name: "claudekit-cli",
14753
- version: "3.5.1",
14928
+ version: "3.6.0",
14754
14929
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
14755
14930
  type: "module",
14756
14931
  repository: {
@@ -15408,6 +15583,21 @@ async function installDependency(dependency, method) {
15408
15583
  // src/lib/health-checks/system-checker.ts
15409
15584
  init_logger();
15410
15585
  var execAsync3 = promisify3(exec3);
15586
+ var MIN_GH_CLI_VERSION = "2.20.0";
15587
+ function compareVersions2(a3, b3) {
15588
+ const partsA = a3.split(".").map(Number);
15589
+ const partsB = b3.split(".").map(Number);
15590
+ const maxLen = Math.max(partsA.length, partsB.length);
15591
+ for (let i = 0;i < maxLen; i++) {
15592
+ const numA = partsA[i] ?? 0;
15593
+ const numB = partsB[i] ?? 0;
15594
+ if (numA < numB)
15595
+ return -1;
15596
+ if (numA > numB)
15597
+ return 1;
15598
+ }
15599
+ return 0;
15600
+ }
15411
15601
 
15412
15602
  class SystemChecker {
15413
15603
  group = "system";
@@ -15527,12 +15717,25 @@ class SystemChecker {
15527
15717
  try {
15528
15718
  const { stdout } = await execAsync3("gh --version");
15529
15719
  const match = stdout.match(/(\d+\.\d+\.\d+)/);
15720
+ const version = match?.[1];
15721
+ if (version && compareVersions2(version, MIN_GH_CLI_VERSION) < 0) {
15722
+ return {
15723
+ id: "gh-cli-version",
15724
+ name: "GitHub CLI",
15725
+ group: "system",
15726
+ status: "warn",
15727
+ message: `v${version} (outdated)`,
15728
+ details: `Minimum required: v${MIN_GH_CLI_VERSION}`,
15729
+ suggestion: this.getGhUpgradeInstructions(),
15730
+ autoFixable: false
15731
+ };
15732
+ }
15530
15733
  return {
15531
15734
  id: "gh-cli-version",
15532
15735
  name: "GitHub CLI",
15533
15736
  group: "system",
15534
15737
  status: "pass",
15535
- message: match ? `v${match[1]}` : "Installed",
15738
+ message: version ? `v${version}` : "Installed",
15536
15739
  autoFixable: true,
15537
15740
  fix: undefined
15538
15741
  };
@@ -15549,6 +15752,13 @@ class SystemChecker {
15549
15752
  };
15550
15753
  }
15551
15754
  }
15755
+ getGhUpgradeInstructions() {
15756
+ return `Upgrade GitHub CLI to v${MIN_GH_CLI_VERSION}+:
15757
+ macOS: brew upgrade gh
15758
+ Windows: winget upgrade GitHub.cli
15759
+ Linux: sudo apt update && sudo apt upgrade gh
15760
+ Or visit: https://cli.github.com`;
15761
+ }
15552
15762
  createGhCliFix() {
15553
15763
  return {
15554
15764
  id: "install-gh-cli",
@@ -17722,7 +17932,7 @@ async function doctorCommand(options = {}) {
17722
17932
 
17723
17933
  // src/commands/init.ts
17724
17934
  var import_fs_extra17 = __toESM(require_lib(), 1);
17725
- import { join as join26, resolve as resolve4 } from "node:path";
17935
+ import { join as join28, resolve as resolve4 } from "node:path";
17726
17936
 
17727
17937
  // src/lib/commands-prefix.ts
17728
17938
  init_logger();
@@ -28977,7 +29187,7 @@ class FileMerger {
28977
29187
  const isWindows5 = process.platform === "win32";
28978
29188
  let processedContent = content;
28979
29189
  if (this.isGlobal) {
28980
- const homeVar = isWindows5 ? "%USERPROFILE%" : "$HOME";
29190
+ const homeVar = isWindows5 ? '"%USERPROFILE%"' : '"$HOME"';
28981
29191
  processedContent = this.transformClaudePaths(content, homeVar);
28982
29192
  if (processedContent !== content) {
28983
29193
  logger.debug(`Transformed .claude/ paths to ${homeVar}/.claude/ in settings.json for global installation`);
@@ -28996,12 +29206,17 @@ class FileMerger {
28996
29206
  }
28997
29207
  }
28998
29208
  transformClaudePaths(content, prefix) {
29209
+ if (/\.claude\/[^\s"']*[;`$&|><]/.test(content)) {
29210
+ logger.warning("Potentially unsafe characters detected in .claude/ paths");
29211
+ throw new Error("Settings file contains potentially unsafe path characters");
29212
+ }
28999
29213
  let transformed = content;
29000
29214
  const jsonSafePrefix = prefix.includes('"') ? prefix.replace(/"/g, "\\\"") : prefix;
29215
+ const rawPrefix = prefix.replace(/"/g, "");
29001
29216
  transformed = transformed.replace(/(node\s+)(?:\.\/)?\.claude\//g, `$1${jsonSafePrefix}/.claude/`);
29002
- if (prefix.includes("HOME") || prefix.includes("USERPROFILE")) {
29003
- transformed = transformed.replace(/\$CLAUDE_PROJECT_DIR/g, prefix);
29004
- transformed = transformed.replace(/%CLAUDE_PROJECT_DIR%/g, prefix);
29217
+ if (rawPrefix.includes("HOME") || rawPrefix.includes("USERPROFILE")) {
29218
+ transformed = transformed.replace(/\$CLAUDE_PROJECT_DIR/g, rawPrefix);
29219
+ transformed = transformed.replace(/%CLAUDE_PROJECT_DIR%/g, rawPrefix);
29005
29220
  }
29006
29221
  return transformed;
29007
29222
  }
@@ -29870,11 +30085,11 @@ class PromptsManager {
29870
30085
  init_dist2();
29871
30086
  init_logger();
29872
30087
  var import_fs_extra10 = __toESM(require_lib(), 1);
29873
- import { join as join18 } from "node:path";
30088
+ import { join as join20 } from "node:path";
29874
30089
 
29875
30090
  // src/lib/config-generator.ts
29876
30091
  var import_fs_extra9 = __toESM(require_lib(), 1);
29877
- import { join as join17 } from "node:path";
30092
+ import { join as join19 } from "node:path";
29878
30093
  async function generateEnvFile(targetDir, values) {
29879
30094
  const lines = [
29880
30095
  "# Generated by ClaudeKit CLI setup wizard",
@@ -29886,7 +30101,7 @@ async function generateEnvFile(targetDir, values) {
29886
30101
  lines.push(`${key}=${value}`);
29887
30102
  }
29888
30103
  }
29889
- const envPath = join17(targetDir, ".env");
30104
+ const envPath = join19(targetDir, ".env");
29890
30105
  await import_fs_extra9.writeFile(envPath, `${lines.join(`
29891
30106
  `)}
29892
30107
  `, { mode: 384 });
@@ -29957,7 +30172,7 @@ async function parseEnvFile(path9) {
29957
30172
  }
29958
30173
  }
29959
30174
  async function checkGlobalConfig() {
29960
- const globalEnvPath = join18(PathResolver.getGlobalKitDir(), ".env");
30175
+ const globalEnvPath = join20(PathResolver.getGlobalKitDir(), ".env");
29961
30176
  if (!await import_fs_extra10.pathExists(globalEnvPath))
29962
30177
  return false;
29963
30178
  const env2 = await parseEnvFile(globalEnvPath);
@@ -29973,7 +30188,7 @@ async function runSetupWizard(options) {
29973
30188
  let globalEnv = {};
29974
30189
  const hasGlobalConfig = !isGlobal && await checkGlobalConfig();
29975
30190
  if (!isGlobal) {
29976
- const globalEnvPath = join18(PathResolver.getGlobalKitDir(), ".env");
30191
+ const globalEnvPath = join20(PathResolver.getGlobalKitDir(), ".env");
29977
30192
  if (await import_fs_extra10.pathExists(globalEnvPath)) {
29978
30193
  globalEnv = await parseEnvFile(globalEnvPath);
29979
30194
  }
@@ -30026,7 +30241,7 @@ async function runSetupWizard(options) {
30026
30241
  }
30027
30242
  }
30028
30243
  await generateEnvFile(targetDir, values);
30029
- f2.success(`Configuration saved to ${join18(targetDir, ".env")}`);
30244
+ f2.success(`Configuration saved to ${join20(targetDir, ".env")}`);
30030
30245
  return true;
30031
30246
  }
30032
30247
 
@@ -30034,7 +30249,7 @@ async function runSetupWizard(options) {
30034
30249
  init_logger();
30035
30250
  var import_fs_extra12 = __toESM(require_lib(), 1);
30036
30251
  import { readdir as readdir8 } from "node:fs/promises";
30037
- import { join as join20 } from "node:path";
30252
+ import { join as join22 } from "node:path";
30038
30253
 
30039
30254
  // src/lib/skills-manifest.ts
30040
30255
  init_types2();
@@ -30042,7 +30257,7 @@ init_logger();
30042
30257
  var import_fs_extra11 = __toESM(require_lib(), 1);
30043
30258
  import { createHash as createHash2 } from "node:crypto";
30044
30259
  import { readFile as readFile10, readdir as readdir7, writeFile as writeFile9 } from "node:fs/promises";
30045
- import { join as join19, relative as relative6 } from "node:path";
30260
+ import { join as join21, relative as relative6 } from "node:path";
30046
30261
 
30047
30262
  class SkillsManifestManager {
30048
30263
  static MANIFEST_FILENAME = ".skills-manifest.json";
@@ -30064,12 +30279,12 @@ class SkillsManifestManager {
30064
30279
  return manifest;
30065
30280
  }
30066
30281
  static async writeManifest(skillsDir, manifest) {
30067
- const manifestPath = join19(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
30282
+ const manifestPath = join21(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
30068
30283
  await writeFile9(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
30069
30284
  logger.debug(`Wrote manifest to: ${manifestPath}`);
30070
30285
  }
30071
30286
  static async readManifest(skillsDir) {
30072
- const manifestPath = join19(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
30287
+ const manifestPath = join21(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
30073
30288
  if (!await import_fs_extra11.pathExists(manifestPath)) {
30074
30289
  logger.debug(`No manifest found at: ${manifestPath}`);
30075
30290
  return null;
@@ -30092,7 +30307,7 @@ class SkillsManifestManager {
30092
30307
  return "flat";
30093
30308
  }
30094
30309
  for (const dir of dirs.slice(0, 3)) {
30095
- const dirPath = join19(skillsDir, dir.name);
30310
+ const dirPath = join21(skillsDir, dir.name);
30096
30311
  const subEntries = await readdir7(dirPath, { withFileTypes: true });
30097
30312
  const hasSubdirs = subEntries.some((entry) => entry.isDirectory());
30098
30313
  if (hasSubdirs) {
@@ -30111,7 +30326,7 @@ class SkillsManifestManager {
30111
30326
  const entries = await readdir7(skillsDir, { withFileTypes: true });
30112
30327
  for (const entry of entries) {
30113
30328
  if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) {
30114
- const skillPath = join19(skillsDir, entry.name);
30329
+ const skillPath = join21(skillsDir, entry.name);
30115
30330
  const hash = await SkillsManifestManager.hashDirectory(skillPath);
30116
30331
  skills.push({
30117
30332
  name: entry.name,
@@ -30123,11 +30338,11 @@ class SkillsManifestManager {
30123
30338
  const categories = await readdir7(skillsDir, { withFileTypes: true });
30124
30339
  for (const category of categories) {
30125
30340
  if (category.isDirectory() && category.name !== "node_modules" && !category.name.startsWith(".")) {
30126
- const categoryPath = join19(skillsDir, category.name);
30341
+ const categoryPath = join21(skillsDir, category.name);
30127
30342
  const skillEntries = await readdir7(categoryPath, { withFileTypes: true });
30128
30343
  for (const skillEntry of skillEntries) {
30129
30344
  if (skillEntry.isDirectory() && !skillEntry.name.startsWith(".")) {
30130
- const skillPath = join19(categoryPath, skillEntry.name);
30345
+ const skillPath = join21(categoryPath, skillEntry.name);
30131
30346
  const hash = await SkillsManifestManager.hashDirectory(skillPath);
30132
30347
  skills.push({
30133
30348
  name: skillEntry.name,
@@ -30157,7 +30372,7 @@ class SkillsManifestManager {
30157
30372
  const files = [];
30158
30373
  const entries = await readdir7(dirPath, { withFileTypes: true });
30159
30374
  for (const entry of entries) {
30160
- const fullPath = join19(dirPath, entry.name);
30375
+ const fullPath = join21(dirPath, entry.name);
30161
30376
  if (entry.name.startsWith(".") || entry.name === "node_modules") {
30162
30377
  continue;
30163
30378
  }
@@ -30400,12 +30615,12 @@ class SkillsMigrationDetector {
30400
30615
  let totalSkillLikeCount = 0;
30401
30616
  const allSkills = [];
30402
30617
  for (const dir of dirs) {
30403
- const dirPath = join20(skillsDir, dir.name);
30618
+ const dirPath = join22(skillsDir, dir.name);
30404
30619
  const subEntries = await readdir8(dirPath, { withFileTypes: true });
30405
30620
  const subdirs = subEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
30406
30621
  if (subdirs.length > 0) {
30407
30622
  for (const subdir of subdirs.slice(0, 3)) {
30408
- const subdirPath = join20(dirPath, subdir.name);
30623
+ const subdirPath = join22(dirPath, subdir.name);
30409
30624
  const subdirFiles = await readdir8(subdirPath, { withFileTypes: true });
30410
30625
  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"));
30411
30626
  if (hasSkillMarker) {
@@ -30444,14 +30659,14 @@ init_types2();
30444
30659
  init_logger();
30445
30660
  var import_fs_extra15 = __toESM(require_lib(), 1);
30446
30661
  import { copyFile as copyFile2, mkdir as mkdir7, readdir as readdir11, rm as rm2 } from "node:fs/promises";
30447
- import { join as join23 } from "node:path";
30662
+ import { join as join25 } from "node:path";
30448
30663
 
30449
30664
  // src/lib/skills-backup-manager.ts
30450
30665
  init_types2();
30451
30666
  init_logger();
30452
30667
  var import_fs_extra13 = __toESM(require_lib(), 1);
30453
30668
  import { copyFile, mkdir as mkdir6, readdir as readdir9, rm, stat as stat4 } from "node:fs/promises";
30454
- import { basename as basename2, join as join21, normalize as normalize2 } from "node:path";
30669
+ import { basename as basename2, join as join23, normalize as normalize2 } from "node:path";
30455
30670
  function validatePath2(path9, paramName) {
30456
30671
  if (!path9 || typeof path9 !== "string") {
30457
30672
  throw new SkillsMigrationError(`${paramName} must be a non-empty string`);
@@ -30477,7 +30692,7 @@ class SkillsBackupManager {
30477
30692
  const timestamp = Date.now();
30478
30693
  const randomSuffix = Math.random().toString(36).substring(2, 8);
30479
30694
  const backupDirName = `${SkillsBackupManager.BACKUP_PREFIX}${timestamp}-${randomSuffix}`;
30480
- const backupDir = parentDir ? join21(parentDir, backupDirName) : join21(skillsDir, "..", backupDirName);
30695
+ const backupDir = parentDir ? join23(parentDir, backupDirName) : join23(skillsDir, "..", backupDirName);
30481
30696
  logger.info(`Creating backup at: ${backupDir}`);
30482
30697
  try {
30483
30698
  await mkdir6(backupDir, { recursive: true });
@@ -30528,7 +30743,7 @@ class SkillsBackupManager {
30528
30743
  }
30529
30744
  try {
30530
30745
  const entries = await readdir9(parentDir, { withFileTypes: true });
30531
- const backups = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(SkillsBackupManager.BACKUP_PREFIX)).map((entry) => join21(parentDir, entry.name));
30746
+ const backups = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(SkillsBackupManager.BACKUP_PREFIX)).map((entry) => join23(parentDir, entry.name));
30532
30747
  backups.sort().reverse();
30533
30748
  return backups;
30534
30749
  } catch (error) {
@@ -30556,8 +30771,8 @@ class SkillsBackupManager {
30556
30771
  static async copyDirectory(sourceDir, destDir) {
30557
30772
  const entries = await readdir9(sourceDir, { withFileTypes: true });
30558
30773
  for (const entry of entries) {
30559
- const sourcePath = join21(sourceDir, entry.name);
30560
- const destPath = join21(destDir, entry.name);
30774
+ const sourcePath = join23(sourceDir, entry.name);
30775
+ const destPath = join23(destDir, entry.name);
30561
30776
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
30562
30777
  continue;
30563
30778
  }
@@ -30573,7 +30788,7 @@ class SkillsBackupManager {
30573
30788
  let size = 0;
30574
30789
  const entries = await readdir9(dirPath, { withFileTypes: true });
30575
30790
  for (const entry of entries) {
30576
- const fullPath = join21(dirPath, entry.name);
30791
+ const fullPath = join23(dirPath, entry.name);
30577
30792
  if (entry.isSymbolicLink()) {
30578
30793
  continue;
30579
30794
  }
@@ -30604,7 +30819,7 @@ var import_fs_extra14 = __toESM(require_lib(), 1);
30604
30819
  import { createHash as createHash3 } from "node:crypto";
30605
30820
  import { createReadStream as createReadStream2 } from "node:fs";
30606
30821
  import { readFile as readFile11, readdir as readdir10 } from "node:fs/promises";
30607
- import { join as join22, normalize as normalize3, relative as relative7 } from "node:path";
30822
+ import { join as join24, normalize as normalize3, relative as relative7 } from "node:path";
30608
30823
  function validatePath3(path9, paramName) {
30609
30824
  if (!path9 || typeof path9 !== "string") {
30610
30825
  throw new SkillsMigrationError(`${paramName} must be a non-empty string`);
@@ -30751,13 +30966,13 @@ class SkillsCustomizationScanner {
30751
30966
  if (dirs.length === 0) {
30752
30967
  return ["flat", []];
30753
30968
  }
30754
- const firstDirPath = join22(skillsDir, dirs[0].name);
30969
+ const firstDirPath = join24(skillsDir, dirs[0].name);
30755
30970
  const subEntries = await readdir10(firstDirPath, { withFileTypes: true });
30756
30971
  const subdirs = subEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
30757
30972
  if (subdirs.length > 0) {
30758
30973
  let skillLikeCount = 0;
30759
30974
  for (const subdir of subdirs.slice(0, 3)) {
30760
- const subdirPath = join22(firstDirPath, subdir.name);
30975
+ const subdirPath = join24(firstDirPath, subdir.name);
30761
30976
  const subdirFiles = await readdir10(subdirPath, { withFileTypes: true });
30762
30977
  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"));
30763
30978
  if (hasSkillMarker) {
@@ -30767,7 +30982,7 @@ class SkillsCustomizationScanner {
30767
30982
  if (skillLikeCount > 0) {
30768
30983
  const skills = [];
30769
30984
  for (const dir of dirs) {
30770
- const categoryPath = join22(skillsDir, dir.name);
30985
+ const categoryPath = join24(skillsDir, dir.name);
30771
30986
  const skillDirs = await readdir10(categoryPath, { withFileTypes: true });
30772
30987
  skills.push(...skillDirs.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name));
30773
30988
  }
@@ -30777,7 +30992,7 @@ class SkillsCustomizationScanner {
30777
30992
  return ["flat", dirs.map((dir) => dir.name)];
30778
30993
  }
30779
30994
  static async findSkillPath(skillsDir, skillName) {
30780
- const flatPath = join22(skillsDir, skillName);
30995
+ const flatPath = join24(skillsDir, skillName);
30781
30996
  if (await import_fs_extra14.pathExists(flatPath)) {
30782
30997
  return { path: flatPath, category: undefined };
30783
30998
  }
@@ -30786,8 +31001,8 @@ class SkillsCustomizationScanner {
30786
31001
  if (!entry.isDirectory() || entry.name.startsWith(".") || entry.name === "node_modules") {
30787
31002
  continue;
30788
31003
  }
30789
- const categoryPath = join22(skillsDir, entry.name);
30790
- const skillPath = join22(categoryPath, skillName);
31004
+ const categoryPath = join24(skillsDir, entry.name);
31005
+ const skillPath = join24(categoryPath, skillName);
30791
31006
  if (await import_fs_extra14.pathExists(skillPath)) {
30792
31007
  return { path: skillPath, category: entry.name };
30793
31008
  }
@@ -30798,7 +31013,7 @@ class SkillsCustomizationScanner {
30798
31013
  const files = [];
30799
31014
  const entries = await readdir10(dirPath, { withFileTypes: true });
30800
31015
  for (const entry of entries) {
30801
- const fullPath = join22(dirPath, entry.name);
31016
+ const fullPath = join24(dirPath, entry.name);
30802
31017
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
30803
31018
  continue;
30804
31019
  }
@@ -31041,7 +31256,7 @@ class SkillsMigrator {
31041
31256
  }
31042
31257
  }
31043
31258
  if (options.backup && !options.dryRun) {
31044
- const claudeDir = join23(currentSkillsDir, "..");
31259
+ const claudeDir = join25(currentSkillsDir, "..");
31045
31260
  result.backupPath = await SkillsBackupManager.createBackup(currentSkillsDir, claudeDir);
31046
31261
  logger.success(`Backup created at: ${result.backupPath}`);
31047
31262
  }
@@ -31093,7 +31308,7 @@ class SkillsMigrator {
31093
31308
  const migrated = [];
31094
31309
  const preserved = [];
31095
31310
  const errors2 = [];
31096
- const tempDir = join23(currentSkillsDir, "..", ".skills-migration-temp");
31311
+ const tempDir = join25(currentSkillsDir, "..", ".skills-migration-temp");
31097
31312
  await mkdir7(tempDir, { recursive: true });
31098
31313
  try {
31099
31314
  for (const mapping of mappings) {
@@ -31114,9 +31329,9 @@ class SkillsMigrator {
31114
31329
  }
31115
31330
  }
31116
31331
  const category = mapping.category;
31117
- const targetPath = category ? join23(tempDir, category, skillName) : join23(tempDir, skillName);
31332
+ const targetPath = category ? join25(tempDir, category, skillName) : join25(tempDir, skillName);
31118
31333
  if (category) {
31119
- await mkdir7(join23(tempDir, category), { recursive: true });
31334
+ await mkdir7(join25(tempDir, category), { recursive: true });
31120
31335
  }
31121
31336
  await SkillsMigrator.copySkillDirectory(currentSkillPath, targetPath);
31122
31337
  migrated.push(skillName);
@@ -31150,8 +31365,8 @@ class SkillsMigrator {
31150
31365
  await mkdir7(destDir, { recursive: true });
31151
31366
  const entries = await readdir11(sourceDir, { withFileTypes: true });
31152
31367
  for (const entry of entries) {
31153
- const sourcePath = join23(sourceDir, entry.name);
31154
- const destPath = join23(destDir, entry.name);
31368
+ const sourcePath = join25(sourceDir, entry.name);
31369
+ const destPath = join25(destDir, entry.name);
31155
31370
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
31156
31371
  continue;
31157
31372
  }
@@ -31170,18 +31385,18 @@ init_types2();
31170
31385
  // src/utils/config.ts
31171
31386
  init_types2();
31172
31387
  init_logger();
31173
- import { existsSync as existsSync5 } from "node:fs";
31388
+ import { existsSync as existsSync6 } from "node:fs";
31174
31389
  import { mkdir as mkdir8, readFile as readFile12, rename as rename2, rm as rm3, writeFile as writeFile10 } from "node:fs/promises";
31175
31390
  import { chmod as chmod2 } from "node:fs/promises";
31176
31391
  import { platform as platform8 } from "node:os";
31177
- import { join as join24 } from "node:path";
31392
+ import { join as join26 } from "node:path";
31178
31393
  var PROJECT_CONFIG_FILE = ".ck.json";
31179
31394
 
31180
31395
  class ConfigManager {
31181
31396
  static config = null;
31182
31397
  static globalFlag = false;
31183
31398
  static getProjectConfigDir(projectDir, global3) {
31184
- return global3 ? projectDir : join24(projectDir, ".claude");
31399
+ return global3 ? projectDir : join26(projectDir, ".claude");
31185
31400
  }
31186
31401
  static setGlobalFlag(global3) {
31187
31402
  ConfigManager.globalFlag = global3;
@@ -31196,7 +31411,7 @@ class ConfigManager {
31196
31411
  }
31197
31412
  const configFile = PathResolver.getConfigFile(ConfigManager.globalFlag);
31198
31413
  try {
31199
- if (existsSync5(configFile)) {
31414
+ if (existsSync6(configFile)) {
31200
31415
  const content = await readFile12(configFile, "utf-8");
31201
31416
  const data = JSON.parse(content);
31202
31417
  ConfigManager.config = ConfigSchema.parse(data);
@@ -31214,7 +31429,7 @@ class ConfigManager {
31214
31429
  const validConfig = ConfigSchema.parse(config);
31215
31430
  const configDir = PathResolver.getConfigDir(ConfigManager.globalFlag);
31216
31431
  const configFile = PathResolver.getConfigFile(ConfigManager.globalFlag);
31217
- if (!existsSync5(configDir)) {
31432
+ if (!existsSync6(configDir)) {
31218
31433
  await mkdir8(configDir, { recursive: true });
31219
31434
  if (platform8() !== "win32") {
31220
31435
  await chmod2(configDir, 448);
@@ -31248,9 +31463,9 @@ class ConfigManager {
31248
31463
  }
31249
31464
  static async loadProjectConfig(projectDir, global3 = false) {
31250
31465
  const configDir = ConfigManager.getProjectConfigDir(projectDir, global3);
31251
- const configPath = join24(configDir, PROJECT_CONFIG_FILE);
31466
+ const configPath = join26(configDir, PROJECT_CONFIG_FILE);
31252
31467
  try {
31253
- if (existsSync5(configPath)) {
31468
+ if (existsSync6(configPath)) {
31254
31469
  const content = await readFile12(configPath, "utf-8");
31255
31470
  const data = JSON.parse(content);
31256
31471
  const folders = FoldersConfigSchema.parse(data.paths || data);
@@ -31264,9 +31479,9 @@ class ConfigManager {
31264
31479
  }
31265
31480
  static async saveProjectConfig(projectDir, folders, global3 = false) {
31266
31481
  const configDir = ConfigManager.getProjectConfigDir(projectDir, global3);
31267
- const configPath = join24(configDir, PROJECT_CONFIG_FILE);
31482
+ const configPath = join26(configDir, PROJECT_CONFIG_FILE);
31268
31483
  try {
31269
- if (!existsSync5(configDir)) {
31484
+ if (!existsSync6(configDir)) {
31270
31485
  await mkdir8(configDir, { recursive: true });
31271
31486
  }
31272
31487
  const validFolders = FoldersConfigSchema.parse(folders);
@@ -31296,21 +31511,21 @@ class ConfigManager {
31296
31511
  }
31297
31512
  static projectConfigExists(projectDir, global3 = false) {
31298
31513
  const configDir = ConfigManager.getProjectConfigDir(projectDir, global3);
31299
- return existsSync5(join24(configDir, PROJECT_CONFIG_FILE));
31514
+ return existsSync6(join26(configDir, PROJECT_CONFIG_FILE));
31300
31515
  }
31301
31516
  static async migrateNestedConfig(globalDir) {
31302
- const correctPath = join24(globalDir, PROJECT_CONFIG_FILE);
31303
- const incorrectPath = join24(globalDir, ".claude", PROJECT_CONFIG_FILE);
31304
- if (existsSync5(correctPath)) {
31517
+ const correctPath = join26(globalDir, PROJECT_CONFIG_FILE);
31518
+ const incorrectPath = join26(globalDir, ".claude", PROJECT_CONFIG_FILE);
31519
+ if (existsSync6(correctPath)) {
31305
31520
  logger.debug("Config already exists at correct location, skipping migration");
31306
31521
  return false;
31307
31522
  }
31308
- if (existsSync5(incorrectPath)) {
31523
+ if (existsSync6(incorrectPath)) {
31309
31524
  try {
31310
31525
  logger.info("Migrating .ck.json from nested location to correct location...");
31311
31526
  await rename2(incorrectPath, correctPath);
31312
31527
  logger.success(`Migrated ${PROJECT_CONFIG_FILE} to ${correctPath}`);
31313
- const nestedClaudeDir = join24(globalDir, ".claude");
31528
+ const nestedClaudeDir = join26(globalDir, ".claude");
31314
31529
  try {
31315
31530
  await rm3(nestedClaudeDir, { recursive: false });
31316
31531
  logger.debug("Removed empty nested .claude directory");
@@ -31333,7 +31548,7 @@ init_environment();
31333
31548
  // src/utils/file-scanner.ts
31334
31549
  init_logger();
31335
31550
  var import_fs_extra16 = __toESM(require_lib(), 1);
31336
- import { join as join25, relative as relative8, resolve as resolve3 } from "node:path";
31551
+ import { join as join27, relative as relative8, resolve as resolve3 } from "node:path";
31337
31552
  var SKIP_DIRS = [
31338
31553
  "node_modules",
31339
31554
  ".venv",
@@ -31360,7 +31575,7 @@ class FileScanner {
31360
31575
  logger.debug(`Skipping directory: ${entry}`);
31361
31576
  continue;
31362
31577
  }
31363
- const fullPath = join25(dirPath, entry);
31578
+ const fullPath = join27(dirPath, entry);
31364
31579
  if (!FileScanner.isSafePath(basePath, fullPath)) {
31365
31580
  logger.warning(`Skipping potentially unsafe path: ${entry}`);
31366
31581
  continue;
@@ -31395,8 +31610,8 @@ class FileScanner {
31395
31610
  return files;
31396
31611
  }
31397
31612
  static async findCustomFiles(destDir, sourceDir, subPath) {
31398
- const destSubDir = join25(destDir, subPath);
31399
- const sourceSubDir = join25(sourceDir, subPath);
31613
+ const destSubDir = join27(destDir, subPath);
31614
+ const sourceSubDir = join27(sourceDir, subPath);
31400
31615
  const destFiles = await FileScanner.getFiles(destSubDir, destDir);
31401
31616
  const sourceFiles = await FileScanner.getFiles(sourceSubDir, sourceDir);
31402
31617
  const sourceFileSet = new Set(sourceFiles);
@@ -31437,7 +31652,7 @@ async function initCommand(options) {
31437
31652
  logger.info("Running in non-interactive mode (--yes flag)");
31438
31653
  }
31439
31654
  if (validOptions.global) {
31440
- const localSettingsPath = join26(process.cwd(), ".claude", "settings.json");
31655
+ const localSettingsPath = join28(process.cwd(), ".claude", "settings.json");
31441
31656
  if (await import_fs_extra17.pathExists(localSettingsPath)) {
31442
31657
  if (isNonInteractive2) {
31443
31658
  logger.warning("Local .claude/settings.json detected. Local settings take precedence over global.");
@@ -31449,7 +31664,7 @@ async function initCommand(options) {
31449
31664
  return;
31450
31665
  }
31451
31666
  if (choice === "remove") {
31452
- const localClaudeDir = join26(process.cwd(), ".claude");
31667
+ const localClaudeDir = join28(process.cwd(), ".claude");
31453
31668
  try {
31454
31669
  await import_fs_extra17.remove(localClaudeDir);
31455
31670
  logger.success("Removed local .claude/ directory");
@@ -31508,7 +31723,7 @@ async function initCommand(options) {
31508
31723
  }
31509
31724
  if (validOptions.fresh) {
31510
31725
  const prefix = PathResolver.getPathPrefix(validOptions.global);
31511
- const claudeDir2 = prefix ? join26(resolvedDir, prefix) : resolvedDir;
31726
+ const claudeDir2 = prefix ? join28(resolvedDir, prefix) : resolvedDir;
31512
31727
  const canProceed = await handleFreshInstallation(claudeDir2, prompts);
31513
31728
  if (!canProceed) {
31514
31729
  return;
@@ -31645,7 +31860,7 @@ async function initCommand(options) {
31645
31860
  }
31646
31861
  }
31647
31862
  if (!validOptions.fresh) {
31648
- const newSkillsDir = join26(extractDir, ".claude", "skills");
31863
+ const newSkillsDir = join28(extractDir, ".claude", "skills");
31649
31864
  const currentSkillsDir = PathResolver.buildSkillsPath(resolvedDir, validOptions.global);
31650
31865
  if (await import_fs_extra17.pathExists(newSkillsDir) && await import_fs_extra17.pathExists(currentSkillsDir)) {
31651
31866
  logger.info("Checking for skills directory migration...");
@@ -31670,7 +31885,7 @@ async function initCommand(options) {
31670
31885
  let customClaudeFiles = [];
31671
31886
  if (!validOptions.fresh) {
31672
31887
  logger.info("Scanning for custom .claude files...");
31673
- const scanSourceDir = validOptions.global ? join26(extractDir, ".claude") : extractDir;
31888
+ const scanSourceDir = validOptions.global ? join28(extractDir, ".claude") : extractDir;
31674
31889
  const scanTargetSubdir = validOptions.global ? "" : ".claude";
31675
31890
  customClaudeFiles = await FileScanner.findCustomFiles(resolvedDir, scanSourceDir, scanTargetSubdir);
31676
31891
  } else {
@@ -31699,7 +31914,7 @@ async function initCommand(options) {
31699
31914
  merger.addIgnorePatterns(validOptions.exclude);
31700
31915
  }
31701
31916
  merger.setGlobalFlag(validOptions.global);
31702
- const claudeDir = validOptions.global ? resolvedDir : join26(resolvedDir, ".claude");
31917
+ const claudeDir = validOptions.global ? resolvedDir : join28(resolvedDir, ".claude");
31703
31918
  const releaseManifest = await ReleaseManifestLoader.load(extractDir);
31704
31919
  if (!validOptions.fresh && await import_fs_extra17.pathExists(claudeDir)) {
31705
31920
  const legacyDetection = await LegacyMigration.detectLegacy(claudeDir);
@@ -31721,7 +31936,7 @@ async function initCommand(options) {
31721
31936
  return;
31722
31937
  }
31723
31938
  }
31724
- const sourceDir = validOptions.global ? join26(extractDir, ".claude") : extractDir;
31939
+ const sourceDir = validOptions.global ? join28(extractDir, ".claude") : extractDir;
31725
31940
  await merger.merge(sourceDir, resolvedDir, false);
31726
31941
  const manifestWriter = new ManifestWriter;
31727
31942
  const installedFiles = merger.getAllInstalledFiles();
@@ -31730,7 +31945,7 @@ async function initCommand(options) {
31730
31945
  if (!validOptions.global && !installedPath.startsWith(".claude/"))
31731
31946
  continue;
31732
31947
  const relativePath = validOptions.global ? installedPath : installedPath.replace(/^\.claude\//, "");
31733
- const filePath = join26(claudeDir, relativePath);
31948
+ const filePath = join28(claudeDir, relativePath);
31734
31949
  const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
31735
31950
  const ownership = manifestEntry ? "ck" : "user";
31736
31951
  filesToTrack.push({
@@ -31751,8 +31966,8 @@ async function initCommand(options) {
31751
31966
  trackingSpinner.succeed(`Tracked ${trackResult.success} files`);
31752
31967
  await manifestWriter.writeManifest(claudeDir, kitConfig.name, release.tag_name, validOptions.global ? "global" : "local");
31753
31968
  if (validOptions.global) {
31754
- const claudeMdSource = join26(extractDir, "CLAUDE.md");
31755
- const claudeMdDest = join26(resolvedDir, "CLAUDE.md");
31969
+ const claudeMdSource = join28(extractDir, "CLAUDE.md");
31970
+ const claudeMdDest = join28(resolvedDir, "CLAUDE.md");
31756
31971
  if (await import_fs_extra17.pathExists(claudeMdSource)) {
31757
31972
  if (!await import_fs_extra17.pathExists(claudeMdDest)) {
31758
31973
  await import_fs_extra17.copy(claudeMdSource, claudeMdDest);
@@ -31772,7 +31987,7 @@ async function initCommand(options) {
31772
31987
  await handleSkillsInstallation2(skillsDir);
31773
31988
  }
31774
31989
  if (!validOptions.skipSetup && !isNonInteractive2) {
31775
- const envPath = join26(claudeDir, ".env");
31990
+ const envPath = join28(claudeDir, ".env");
31776
31991
  if (!await import_fs_extra17.pathExists(envPath)) {
31777
31992
  const shouldSetup = await prompts.confirm("Set up API keys now? (Gemini API key for ai-multimodal skill, optional webhooks)");
31778
31993
  if (shouldSetup) {
@@ -31804,7 +32019,7 @@ Protected files (.env, etc.) were not modified.`;
31804
32019
 
31805
32020
  // src/commands/new.ts
31806
32021
  var import_fs_extra18 = __toESM(require_lib(), 1);
31807
- import { join as join27, resolve as resolve5 } from "node:path";
32022
+ import { join as join29, resolve as resolve5 } from "node:path";
31808
32023
  init_types2();
31809
32024
  init_environment();
31810
32025
  init_logger();
@@ -31975,7 +32190,7 @@ async function newCommand(options) {
31975
32190
  await CommandsPrefix.cleanupCommandsDirectory(resolvedDir, false);
31976
32191
  }
31977
32192
  await merger.merge(extractDir, resolvedDir, true);
31978
- const claudeDir = join27(resolvedDir, ".claude");
32193
+ const claudeDir = join29(resolvedDir, ".claude");
31979
32194
  const manifestWriter = new ManifestWriter;
31980
32195
  const releaseManifest = await ReleaseManifestLoader.load(extractDir);
31981
32196
  const installedFiles = merger.getAllInstalledFiles();
@@ -31984,7 +32199,7 @@ async function newCommand(options) {
31984
32199
  if (!installedPath.startsWith(".claude/"))
31985
32200
  continue;
31986
32201
  const relativePath = installedPath.replace(/^\.claude\//, "");
31987
- const filePath = join27(claudeDir, relativePath);
32202
+ const filePath = join29(claudeDir, relativePath);
31988
32203
  const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
31989
32204
  const ownership = manifestEntry ? "ck" : "user";
31990
32205
  filesToTrack.push({
@@ -32040,7 +32255,7 @@ init_dist2();
32040
32255
  var import_fs_extra19 = __toESM(require_lib(), 1);
32041
32256
  var import_picocolors10 = __toESM(require_picocolors(), 1);
32042
32257
  import { readdirSync, rmSync } from "node:fs";
32043
- import { dirname as dirname4, join as join28 } from "node:path";
32258
+ import { dirname as dirname4, join as join30 } from "node:path";
32044
32259
  init_types2();
32045
32260
  init_logger();
32046
32261
  async function detectInstallations() {
@@ -32131,7 +32346,7 @@ async function analyzeInstallation(installation, forceOverwrite) {
32131
32346
  return result;
32132
32347
  }
32133
32348
  for (const trackedFile of metadata.files) {
32134
- const filePath = join28(installation.path, trackedFile.path);
32349
+ const filePath = join30(installation.path, trackedFile.path);
32135
32350
  const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
32136
32351
  if (!ownershipResult.exists)
32137
32352
  continue;
@@ -32189,7 +32404,7 @@ async function removeInstallations(installations, options) {
32189
32404
  let removedCount = 0;
32190
32405
  let cleanedDirs = 0;
32191
32406
  for (const item of analysis.toDelete) {
32192
- const filePath = join28(installation.path, item.path);
32407
+ const filePath = join30(installation.path, item.path);
32193
32408
  if (await import_fs_extra19.pathExists(filePath)) {
32194
32409
  await import_fs_extra19.remove(filePath);
32195
32410
  removedCount++;
@@ -32291,7 +32506,7 @@ import { promisify as promisify6 } from "node:util";
32291
32506
  // package.json
32292
32507
  var package_default2 = {
32293
32508
  name: "claudekit-cli",
32294
- version: "3.5.1",
32509
+ version: "3.6.0",
32295
32510
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
32296
32511
  type: "module",
32297
32512
  repository: {
@@ -32736,20 +32951,20 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
32736
32951
 
32737
32952
  // src/lib/version-cache.ts
32738
32953
  init_logger();
32739
- import { existsSync as existsSync6 } from "node:fs";
32954
+ import { existsSync as existsSync7 } from "node:fs";
32740
32955
  import { mkdir as mkdir9, readFile as readFile13, writeFile as writeFile11 } from "node:fs/promises";
32741
- import { join as join29 } from "node:path";
32956
+ import { join as join31 } from "node:path";
32742
32957
  class VersionCacheManager {
32743
32958
  static CACHE_FILENAME = "version-check.json";
32744
32959
  static CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
32745
32960
  static getCacheFile() {
32746
32961
  const cacheDir = PathResolver.getCacheDir(false);
32747
- return join29(cacheDir, VersionCacheManager.CACHE_FILENAME);
32962
+ return join31(cacheDir, VersionCacheManager.CACHE_FILENAME);
32748
32963
  }
32749
32964
  static async load() {
32750
32965
  const cacheFile = VersionCacheManager.getCacheFile();
32751
32966
  try {
32752
- if (!existsSync6(cacheFile)) {
32967
+ if (!existsSync7(cacheFile)) {
32753
32968
  logger.debug("Version check cache not found");
32754
32969
  return null;
32755
32970
  }
@@ -32770,7 +32985,7 @@ class VersionCacheManager {
32770
32985
  const cacheFile = VersionCacheManager.getCacheFile();
32771
32986
  const cacheDir = PathResolver.getCacheDir(false);
32772
32987
  try {
32773
- if (!existsSync6(cacheDir)) {
32988
+ if (!existsSync7(cacheDir)) {
32774
32989
  await mkdir9(cacheDir, { recursive: true, mode: 448 });
32775
32990
  }
32776
32991
  await writeFile11(cacheFile, JSON.stringify(cache2, null, 2), "utf-8");
@@ -32792,7 +33007,7 @@ class VersionCacheManager {
32792
33007
  static async clear() {
32793
33008
  const cacheFile = VersionCacheManager.getCacheFile();
32794
33009
  try {
32795
- if (existsSync6(cacheFile)) {
33010
+ if (existsSync7(cacheFile)) {
32796
33011
  const fs12 = await import("node:fs/promises");
32797
33012
  await fs12.unlink(cacheFile);
32798
33013
  logger.debug("Version check cache cleared");
@@ -33220,7 +33435,7 @@ var logger2 = new Logger2;
33220
33435
 
33221
33436
  // src/utils/path-resolver.ts
33222
33437
  import { homedir as homedir2, platform as platform9 } from "node:os";
33223
- import { join as join30, normalize as normalize4 } from "node:path";
33438
+ import { join as join32, normalize as normalize4 } from "node:path";
33224
33439
 
33225
33440
  class PathResolver2 {
33226
33441
  static getTestHomeDir() {
@@ -33253,50 +33468,50 @@ class PathResolver2 {
33253
33468
  static getConfigDir(global3 = false) {
33254
33469
  const testHome = PathResolver2.getTestHomeDir();
33255
33470
  if (testHome) {
33256
- return global3 ? join30(testHome, ".config", "claude") : join30(testHome, ".claudekit");
33471
+ return global3 ? join32(testHome, ".config", "claude") : join32(testHome, ".claudekit");
33257
33472
  }
33258
33473
  if (!global3) {
33259
- return join30(homedir2(), ".claudekit");
33474
+ return join32(homedir2(), ".claudekit");
33260
33475
  }
33261
33476
  const os2 = platform9();
33262
33477
  if (os2 === "win32") {
33263
- const localAppData = process.env.LOCALAPPDATA || join30(homedir2(), "AppData", "Local");
33264
- return join30(localAppData, "claude");
33478
+ const localAppData = process.env.LOCALAPPDATA || join32(homedir2(), "AppData", "Local");
33479
+ return join32(localAppData, "claude");
33265
33480
  }
33266
33481
  const xdgConfigHome = process.env.XDG_CONFIG_HOME;
33267
33482
  if (xdgConfigHome) {
33268
- return join30(xdgConfigHome, "claude");
33483
+ return join32(xdgConfigHome, "claude");
33269
33484
  }
33270
- return join30(homedir2(), ".config", "claude");
33485
+ return join32(homedir2(), ".config", "claude");
33271
33486
  }
33272
33487
  static getConfigFile(global3 = false) {
33273
- return join30(PathResolver2.getConfigDir(global3), "config.json");
33488
+ return join32(PathResolver2.getConfigDir(global3), "config.json");
33274
33489
  }
33275
33490
  static getCacheDir(global3 = false) {
33276
33491
  const testHome = PathResolver2.getTestHomeDir();
33277
33492
  if (testHome) {
33278
- return global3 ? join30(testHome, ".cache", "claude") : join30(testHome, ".claudekit", "cache");
33493
+ return global3 ? join32(testHome, ".cache", "claude") : join32(testHome, ".claudekit", "cache");
33279
33494
  }
33280
33495
  if (!global3) {
33281
- return join30(homedir2(), ".claudekit", "cache");
33496
+ return join32(homedir2(), ".claudekit", "cache");
33282
33497
  }
33283
33498
  const os2 = platform9();
33284
33499
  if (os2 === "win32") {
33285
- const localAppData = process.env.LOCALAPPDATA || join30(homedir2(), "AppData", "Local");
33286
- return join30(localAppData, "claude", "cache");
33500
+ const localAppData = process.env.LOCALAPPDATA || join32(homedir2(), "AppData", "Local");
33501
+ return join32(localAppData, "claude", "cache");
33287
33502
  }
33288
33503
  const xdgCacheHome = process.env.XDG_CACHE_HOME;
33289
33504
  if (xdgCacheHome) {
33290
- return join30(xdgCacheHome, "claude");
33505
+ return join32(xdgCacheHome, "claude");
33291
33506
  }
33292
- return join30(homedir2(), ".cache", "claude");
33507
+ return join32(homedir2(), ".cache", "claude");
33293
33508
  }
33294
33509
  static getGlobalKitDir() {
33295
33510
  const testHome = PathResolver2.getTestHomeDir();
33296
33511
  if (testHome) {
33297
- return join30(testHome, ".claude");
33512
+ return join32(testHome, ".claude");
33298
33513
  }
33299
- return join30(homedir2(), ".claude");
33514
+ return join32(homedir2(), ".claude");
33300
33515
  }
33301
33516
  static getPathPrefix(global3) {
33302
33517
  return global3 ? "" : ".claude";
@@ -33304,9 +33519,9 @@ class PathResolver2 {
33304
33519
  static buildSkillsPath(baseDir, global3) {
33305
33520
  const prefix = PathResolver2.getPathPrefix(global3);
33306
33521
  if (prefix) {
33307
- return join30(baseDir, prefix, "skills");
33522
+ return join32(baseDir, prefix, "skills");
33308
33523
  }
33309
- return join30(baseDir, "skills");
33524
+ return join32(baseDir, "skills");
33310
33525
  }
33311
33526
  static buildComponentPath(baseDir, component, global3) {
33312
33527
  if (!PathResolver2.isPathSafe(component)) {
@@ -33314,9 +33529,9 @@ class PathResolver2 {
33314
33529
  }
33315
33530
  const prefix = PathResolver2.getPathPrefix(global3);
33316
33531
  if (prefix) {
33317
- return join30(baseDir, prefix, component);
33532
+ return join32(baseDir, prefix, component);
33318
33533
  }
33319
- return join30(baseDir, component);
33534
+ return join32(baseDir, component);
33320
33535
  }
33321
33536
  }
33322
33537
 
@@ -33334,13 +33549,13 @@ async function displayVersion() {
33334
33549
  let localKitVersion = null;
33335
33550
  let isGlobalOnlyKit = false;
33336
33551
  const globalKitDir = PathResolver2.getGlobalKitDir();
33337
- const globalMetadataPath = join31(globalKitDir, "metadata.json");
33552
+ const globalMetadataPath = join33(globalKitDir, "metadata.json");
33338
33553
  const prefix = PathResolver2.getPathPrefix(false);
33339
- const localMetadataPath = prefix ? join31(process.cwd(), prefix, "metadata.json") : join31(process.cwd(), "metadata.json");
33554
+ const localMetadataPath = prefix ? join33(process.cwd(), prefix, "metadata.json") : join33(process.cwd(), "metadata.json");
33340
33555
  const isLocalSameAsGlobal = localMetadataPath === globalMetadataPath;
33341
- if (!isLocalSameAsGlobal && existsSync7(localMetadataPath)) {
33556
+ if (!isLocalSameAsGlobal && existsSync8(localMetadataPath)) {
33342
33557
  try {
33343
- const rawMetadata = JSON.parse(readFileSync4(localMetadataPath, "utf-8"));
33558
+ const rawMetadata = JSON.parse(readFileSync5(localMetadataPath, "utf-8"));
33344
33559
  const metadata = MetadataSchema2.parse(rawMetadata);
33345
33560
  if (metadata.version) {
33346
33561
  const kitName = metadata.name || "ClaudeKit";
@@ -33352,9 +33567,9 @@ async function displayVersion() {
33352
33567
  logger2.verbose("Failed to parse local metadata.json", { error });
33353
33568
  }
33354
33569
  }
33355
- if (existsSync7(globalMetadataPath)) {
33570
+ if (existsSync8(globalMetadataPath)) {
33356
33571
  try {
33357
- const rawMetadata = JSON.parse(readFileSync4(globalMetadataPath, "utf-8"));
33572
+ const rawMetadata = JSON.parse(readFileSync5(globalMetadataPath, "utf-8"));
33358
33573
  const metadata = MetadataSchema2.parse(rawMetadata);
33359
33574
  if (metadata.version) {
33360
33575
  const kitName = metadata.name || "ClaudeKit";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudekit-cli",
3
- "version": "3.5.1",
3
+ "version": "3.6.0",
4
4
  "description": "CLI tool for bootstrapping and updating ClaudeKit projects",
5
5
  "type": "module",
6
6
  "repository": {