claudekit-cli 3.34.3 → 3.34.5

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 +226 -70
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -33805,14 +33805,11 @@ function normalizeCommand(cmd) {
33805
33805
  if (!cmd)
33806
33806
  return "";
33807
33807
  let normalized = cmd;
33808
- normalized = normalized.replace(/"\$HOME"/g, "$HOME");
33809
- normalized = normalized.replace(/"\$CLAUDE_PROJECT_DIR"/g, "$HOME");
33810
- normalized = normalized.replace(/"\$\{HOME\}"/g, "$HOME");
33808
+ normalized = normalized.replace(/"/g, "");
33809
+ normalized = normalized.replace(/~\//g, "$HOME/");
33811
33810
  normalized = normalized.replace(/\$CLAUDE_PROJECT_DIR/g, "$HOME");
33812
33811
  normalized = normalized.replace(/\$\{HOME\}/g, "$HOME");
33813
- normalized = normalized.replace(/"%USERPROFILE%"/g, "$HOME");
33814
33812
  normalized = normalized.replace(/%USERPROFILE%/g, "$HOME");
33815
- normalized = normalized.replace(/"%CLAUDE_PROJECT_DIR%"/g, "$HOME");
33816
33813
  normalized = normalized.replace(/%CLAUDE_PROJECT_DIR%/g, "$HOME");
33817
33814
  normalized = normalized.replace(/\\/g, "/");
33818
33815
  normalized = normalized.replace(/\s+/g, " ").trim();
@@ -44439,6 +44436,27 @@ var init_skill_routes = __esm(() => {
44439
44436
  });
44440
44437
 
44441
44438
  // src/domains/github/npm-registry.ts
44439
+ function redactRegistryUrlForLog(url) {
44440
+ if (!url)
44441
+ return url;
44442
+ try {
44443
+ const parsed = new URL(url);
44444
+ if (parsed.username) {
44445
+ parsed.username = REDACTED_VALUE;
44446
+ }
44447
+ if (parsed.password) {
44448
+ parsed.password = REDACTED_VALUE;
44449
+ }
44450
+ for (const key of parsed.searchParams.keys()) {
44451
+ if (/(token|auth|password|secret|key)/i.test(key)) {
44452
+ parsed.searchParams.set(key, REDACTED_VALUE);
44453
+ }
44454
+ }
44455
+ return parsed.toString();
44456
+ } catch {
44457
+ return url.replace(/\/\/([^/@\s]+)@/, `//${REDACTED_VALUE}@`);
44458
+ }
44459
+ }
44442
44460
  async function fetchWithTimeout(url, options2 = {}, timeout2 = REQUEST_TIMEOUT) {
44443
44461
  const controller = new AbortController;
44444
44462
  const timeoutId = setTimeout(() => controller.abort(), timeout2);
@@ -44460,7 +44478,7 @@ class NpmRegistryClient {
44460
44478
  }
44461
44479
  const registry = registryUrl || DEFAULT_REGISTRY_URL;
44462
44480
  const url = `${registry}/${encodeURIComponent(packageName)}`;
44463
- logger.debug(`Fetching package info from: ${url}`);
44481
+ logger.debug(`Fetching package info from: ${redactRegistryUrlForLog(url)}`);
44464
44482
  try {
44465
44483
  const response = await fetchWithTimeout(url, {
44466
44484
  headers: {
@@ -44523,18 +44541,12 @@ class NpmRegistryClient {
44523
44541
  }
44524
44542
  }
44525
44543
  static async versionExists(packageName, version, registryUrl) {
44526
- try {
44527
- const info = await NpmRegistryClient.getPackageInfo(packageName, registryUrl);
44528
- if (!info)
44529
- return false;
44530
- const exists = version in (info.versions || {});
44531
- logger.debug(`Version ${version} exists for ${packageName}: ${exists}`);
44532
- return exists;
44533
- } catch (error) {
44534
- const message = error instanceof Error ? error.message : "Unknown error";
44535
- logger.debug(`Failed to check version ${version} for ${packageName}: ${message}`);
44544
+ const info = await NpmRegistryClient.getPackageInfo(packageName, registryUrl);
44545
+ if (!info)
44536
44546
  return false;
44537
- }
44547
+ const exists = version in (info.versions || {});
44548
+ logger.debug(`Version ${version} exists for ${packageName}: ${exists}`);
44549
+ return exists;
44538
44550
  }
44539
44551
  static async getVersionInfo(packageName, version, registryUrl) {
44540
44552
  try {
@@ -44572,7 +44584,7 @@ class NpmRegistryClient {
44572
44584
  }
44573
44585
  }
44574
44586
  }
44575
- var DEFAULT_REGISTRY_URL = "https://registry.npmjs.org", REQUEST_TIMEOUT = 5000;
44587
+ var DEFAULT_REGISTRY_URL = "https://registry.npmjs.org", REQUEST_TIMEOUT = 5000, REDACTED_VALUE = "***";
44576
44588
  var init_npm_registry = __esm(() => {
44577
44589
  init_logger();
44578
44590
  });
@@ -44599,9 +44611,9 @@ function getNpmQuery() {
44599
44611
  checkFn: (stdout) => {
44600
44612
  try {
44601
44613
  const data = JSON.parse(stdout);
44602
- return !!(data.dependencies?.["claudekit-cli"] || stdout.includes("claudekit-cli"));
44614
+ return !!data.dependencies?.["claudekit-cli"];
44603
44615
  } catch {
44604
- return stdout.includes("claudekit-cli");
44616
+ return /"claudekit-cli"\s*:/.test(stdout) || /(?:^|[^a-z0-9-])claudekit-cli@/m.test(stdout);
44605
44617
  }
44606
44618
  }
44607
44619
  };
@@ -44617,7 +44629,36 @@ async function getNpmVersion() {
44617
44629
  return null;
44618
44630
  }
44619
44631
  }
44620
- function getNpmUpdateCommand(packageName, version) {
44632
+ function normalizeNpmRegistryUrl(rawValue) {
44633
+ const value = rawValue.trim();
44634
+ if (!value) {
44635
+ return null;
44636
+ }
44637
+ if (!/^https?:\/\//i.test(value)) {
44638
+ return null;
44639
+ }
44640
+ try {
44641
+ const parsed = new URL(value);
44642
+ const protocol = parsed.protocol.toLowerCase();
44643
+ if (protocol !== "http:" && protocol !== "https:") {
44644
+ return null;
44645
+ }
44646
+ const normalizedPath = parsed.pathname.replace(/\/+$/, "");
44647
+ return `${parsed.protocol}//${parsed.host}${normalizedPath}${parsed.search}${parsed.hash}`;
44648
+ } catch {
44649
+ return null;
44650
+ }
44651
+ }
44652
+ async function getNpmRegistryUrl() {
44653
+ try {
44654
+ const cmd = isWindows() ? "npm.cmd config get registry" : "npm config get registry";
44655
+ const { stdout } = await execAsync(cmd, { timeout: 3000 });
44656
+ return normalizeNpmRegistryUrl(stdout);
44657
+ } catch {
44658
+ return null;
44659
+ }
44660
+ }
44661
+ function getNpmUpdateCommand(packageName, version, registryUrl) {
44621
44662
  if (!isValidPackageName(packageName)) {
44622
44663
  throw new Error(`Invalid package name: ${packageName}`);
44623
44664
  }
@@ -44625,7 +44666,8 @@ function getNpmUpdateCommand(packageName, version) {
44625
44666
  throw new Error(`Invalid version: ${version}`);
44626
44667
  }
44627
44668
  const versionSuffix = version ? `@${version}` : "@latest";
44628
- return isWindows() ? `npm.cmd install -g ${packageName}${versionSuffix}` : `npm install -g ${packageName}${versionSuffix}`;
44669
+ const registryFlag = registryUrl ? ` --registry ${registryUrl}` : "";
44670
+ return isWindows() ? `npm.cmd install -g ${packageName}${versionSuffix}${registryFlag}` : `npm install -g ${packageName}${versionSuffix}${registryFlag}`;
44629
44671
  }
44630
44672
  var init_npm_detector = __esm(() => {
44631
44673
  init_environment();
@@ -44637,7 +44679,7 @@ function getBunQuery() {
44637
44679
  return {
44638
44680
  pm: "bun",
44639
44681
  cmd: "bun pm ls -g",
44640
- checkFn: (stdout) => stdout.includes("claudekit-cli")
44682
+ checkFn: (stdout) => /(?:^|[^a-z0-9-])claudekit-cli@/m.test(stdout)
44641
44683
  };
44642
44684
  }
44643
44685
  function getBunVersionCommand() {
@@ -44651,7 +44693,7 @@ async function getBunVersion() {
44651
44693
  return null;
44652
44694
  }
44653
44695
  }
44654
- function getBunUpdateCommand(packageName, version) {
44696
+ function getBunUpdateCommand(packageName, version, registryUrl) {
44655
44697
  if (!isValidPackageName(packageName)) {
44656
44698
  throw new Error(`Invalid package name: ${packageName}`);
44657
44699
  }
@@ -44659,7 +44701,8 @@ function getBunUpdateCommand(packageName, version) {
44659
44701
  throw new Error(`Invalid version: ${version}`);
44660
44702
  }
44661
44703
  const versionSuffix = version ? `@${version}` : "@latest";
44662
- return `bun add -g ${packageName}${versionSuffix}`;
44704
+ const registryFlag = registryUrl ? ` --registry ${registryUrl}` : "";
44705
+ return `bun add -g ${packageName}${versionSuffix}${registryFlag}`;
44663
44706
  }
44664
44707
  var init_bun_detector = __esm(() => {
44665
44708
  init_detector_base();
@@ -44670,7 +44713,7 @@ function getYarnQuery() {
44670
44713
  return {
44671
44714
  pm: "yarn",
44672
44715
  cmd: isWindows() ? "yarn.cmd global list --pattern claudekit-cli" : "yarn global list --pattern claudekit-cli",
44673
- checkFn: (stdout) => stdout.includes("claudekit-cli")
44716
+ checkFn: (stdout) => /(?:^|[^a-z0-9-])claudekit-cli@/m.test(stdout)
44674
44717
  };
44675
44718
  }
44676
44719
  function getYarnVersionCommand() {
@@ -44684,7 +44727,7 @@ async function getYarnVersion() {
44684
44727
  return null;
44685
44728
  }
44686
44729
  }
44687
- function getYarnUpdateCommand(packageName, version) {
44730
+ function getYarnUpdateCommand(packageName, version, registryUrl) {
44688
44731
  if (!isValidPackageName(packageName)) {
44689
44732
  throw new Error(`Invalid package name: ${packageName}`);
44690
44733
  }
@@ -44692,7 +44735,8 @@ function getYarnUpdateCommand(packageName, version) {
44692
44735
  throw new Error(`Invalid version: ${version}`);
44693
44736
  }
44694
44737
  const versionSuffix = version ? `@${version}` : "@latest";
44695
- return isWindows() ? `yarn.cmd global add ${packageName}${versionSuffix}` : `yarn global add ${packageName}${versionSuffix}`;
44738
+ const registryFlag = registryUrl ? ` --registry ${registryUrl}` : "";
44739
+ return isWindows() ? `yarn.cmd global add ${packageName}${versionSuffix}${registryFlag}` : `yarn global add ${packageName}${versionSuffix}${registryFlag}`;
44696
44740
  }
44697
44741
  var init_yarn_detector = __esm(() => {
44698
44742
  init_environment();
@@ -44704,7 +44748,7 @@ function getPnpmQuery() {
44704
44748
  return {
44705
44749
  pm: "pnpm",
44706
44750
  cmd: isWindows() ? "pnpm.cmd ls -g claudekit-cli" : "pnpm ls -g claudekit-cli",
44707
- checkFn: (stdout) => stdout.includes("claudekit-cli")
44751
+ checkFn: (stdout) => /(?:^|[^a-z0-9-])claudekit-cli(?:@|\s+\d)/m.test(stdout)
44708
44752
  };
44709
44753
  }
44710
44754
  function getPnpmVersionCommand() {
@@ -44718,7 +44762,7 @@ async function getPnpmVersion() {
44718
44762
  return null;
44719
44763
  }
44720
44764
  }
44721
- function getPnpmUpdateCommand(packageName, version) {
44765
+ function getPnpmUpdateCommand(packageName, version, registryUrl) {
44722
44766
  if (!isValidPackageName(packageName)) {
44723
44767
  throw new Error(`Invalid package name: ${packageName}`);
44724
44768
  }
@@ -44726,7 +44770,8 @@ function getPnpmUpdateCommand(packageName, version) {
44726
44770
  throw new Error(`Invalid version: ${version}`);
44727
44771
  }
44728
44772
  const versionSuffix = version ? `@${version}` : "@latest";
44729
- return isWindows() ? `pnpm.cmd add -g ${packageName}${versionSuffix}` : `pnpm add -g ${packageName}${versionSuffix}`;
44773
+ const registryFlag = registryUrl ? ` --registry ${registryUrl}` : "";
44774
+ return isWindows() ? `pnpm.cmd add -g ${packageName}${versionSuffix}${registryFlag}` : `pnpm add -g ${packageName}${versionSuffix}${registryFlag}`;
44730
44775
  }
44731
44776
  var init_pnpm_detector = __esm(() => {
44732
44777
  init_environment();
@@ -44939,24 +44984,25 @@ var init_package_manager_detector = __esm(() => {
44939
44984
  return "echo unknown";
44940
44985
  }
44941
44986
  }
44942
- static getUpdateCommand(pm, packageName, version) {
44987
+ static getNpmRegistryUrl = getNpmRegistryUrl;
44988
+ static getUpdateCommand(pm, packageName, version, registryUrl) {
44943
44989
  if (!isValidPackageName(packageName))
44944
44990
  throw new Error(`Invalid package name: ${packageName}`);
44945
44991
  if (version && !isValidVersion(version))
44946
44992
  throw new Error(`Invalid version: ${version}`);
44947
44993
  switch (pm) {
44948
44994
  case "bun":
44949
- return getBunUpdateCommand(packageName, version);
44995
+ return getBunUpdateCommand(packageName, version, registryUrl);
44950
44996
  case "yarn":
44951
- return getYarnUpdateCommand(packageName, version);
44997
+ return getYarnUpdateCommand(packageName, version, registryUrl);
44952
44998
  case "pnpm":
44953
- return getPnpmUpdateCommand(packageName, version);
44999
+ return getPnpmUpdateCommand(packageName, version, registryUrl);
44954
45000
  default:
44955
- return getNpmUpdateCommand(packageName, version);
45001
+ return getNpmUpdateCommand(packageName, version, registryUrl);
44956
45002
  }
44957
45003
  }
44958
- static getInstallCommand(pm, packageName, version) {
44959
- return PackageManagerDetector.getUpdateCommand(pm, packageName, version);
45004
+ static getInstallCommand(pm, packageName, version, registryUrl) {
45005
+ return PackageManagerDetector.getUpdateCommand(pm, packageName, version, registryUrl);
44960
45006
  }
44961
45007
  static getDisplayName(pm) {
44962
45008
  switch (pm) {
@@ -45385,7 +45431,7 @@ var package_default;
45385
45431
  var init_package = __esm(() => {
45386
45432
  package_default = {
45387
45433
  name: "claudekit-cli",
45388
- version: "3.34.3",
45434
+ version: "3.34.5",
45389
45435
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
45390
45436
  type: "module",
45391
45437
  repository: {
@@ -45495,6 +45541,12 @@ var init_package = __esm(() => {
45495
45541
  import { exec as exec2 } from "node:child_process";
45496
45542
  import { join as join26 } from "node:path";
45497
45543
  import { promisify as promisify8 } from "node:util";
45544
+ function redactCommandForLog(command) {
45545
+ if (!command)
45546
+ return command;
45547
+ const redactedRegistryFlags = command.replace(/(--registry(?:=|\s+))(['"]?)(\S+?)(\2)(?=\s|$)/g, (_match, prefix, quote, url) => `${prefix}${quote}${redactRegistryUrlForLog(url)}${quote}`);
45548
+ return redactedRegistryFlags.replace(/https?:\/\/[^\s"']+/g, (url) => redactRegistryUrlForLog(url));
45549
+ }
45498
45550
  function buildInitCommand(isGlobal, kit, beta) {
45499
45551
  const parts = ["ck init"];
45500
45552
  if (isGlobal)
@@ -45624,31 +45676,50 @@ async function updateCliCommand(options2) {
45624
45676
  const pmVersion = await PackageManagerDetector.getVersion(pm);
45625
45677
  s.stop(`Using ${PackageManagerDetector.getDisplayName(pm)}${pmVersion ? ` v${pmVersion}` : ""}`);
45626
45678
  logger.verbose(`Detected package manager: ${pm}`);
45679
+ let registryUrl = opts.registry;
45680
+ if (!registryUrl && pm === "npm") {
45681
+ const userRegistry = await PackageManagerDetector.getNpmRegistryUrl();
45682
+ if (userRegistry) {
45683
+ registryUrl = userRegistry;
45684
+ logger.verbose(`Using npm configured registry: ${redactRegistryUrlForLog(registryUrl)}`);
45685
+ }
45686
+ }
45627
45687
  s.start("Checking for updates...");
45628
45688
  let targetVersion = null;
45629
45689
  if (opts.release && opts.release !== "latest") {
45630
- const exists = await NpmRegistryClient.versionExists(PACKAGE_NAME, opts.release, opts.registry);
45631
- if (!exists) {
45632
- s.stop("Version not found");
45633
- throw new CliUpdateError(`Version ${opts.release} does not exist on npm registry. Run 'ck versions' to see available versions.`);
45690
+ try {
45691
+ const exists = await NpmRegistryClient.versionExists(PACKAGE_NAME, opts.release, registryUrl);
45692
+ if (!exists) {
45693
+ s.stop("Version not found");
45694
+ throw new CliUpdateError(`Version ${opts.release} does not exist on npm registry. Run 'ck versions' to see available versions.`);
45695
+ }
45696
+ } catch (error) {
45697
+ if (error instanceof CliUpdateError) {
45698
+ throw error;
45699
+ }
45700
+ s.stop("Version check failed");
45701
+ const message = error instanceof Error ? error.message : "Unknown error";
45702
+ logger.verbose(`Release check failed for ${opts.release}: ${message}`);
45703
+ const registryHint = registryUrl ? ` (${redactRegistryUrlForLog(registryUrl)})` : " (default registry)";
45704
+ throw new CliUpdateError(`Failed to verify version ${opts.release} on npm registry${registryHint}. Check registry settings/network connectivity and try again.`);
45634
45705
  }
45635
45706
  targetVersion = opts.release;
45636
45707
  s.stop(`Target version: ${targetVersion}`);
45637
45708
  } else if (opts.dev || opts.beta) {
45638
- targetVersion = await NpmRegistryClient.getDevVersion(PACKAGE_NAME, opts.registry);
45709
+ targetVersion = await NpmRegistryClient.getDevVersion(PACKAGE_NAME, registryUrl);
45639
45710
  if (!targetVersion) {
45640
45711
  s.stop("No dev version available");
45641
45712
  logger.warning("No dev version found. Using latest stable version instead.");
45642
- targetVersion = await NpmRegistryClient.getLatestVersion(PACKAGE_NAME, opts.registry);
45713
+ targetVersion = await NpmRegistryClient.getLatestVersion(PACKAGE_NAME, registryUrl);
45643
45714
  } else {
45644
45715
  s.stop(`Latest dev version: ${targetVersion}`);
45645
45716
  }
45646
45717
  } else {
45647
- targetVersion = await NpmRegistryClient.getLatestVersion(PACKAGE_NAME, opts.registry);
45718
+ targetVersion = await NpmRegistryClient.getLatestVersion(PACKAGE_NAME, registryUrl);
45648
45719
  s.stop(`Latest version: ${targetVersion || "unknown"}`);
45649
45720
  }
45650
45721
  if (!targetVersion) {
45651
- throw new CliUpdateError(`Failed to fetch version information from npm registry. Check your internet connection and try again. Manual update: ${PackageManagerDetector.getUpdateCommand(pm, PACKAGE_NAME)}`);
45722
+ throw new CliUpdateError(`Failed to fetch version information from npm registry. Check your internet connection and try again. Manual update: ${PackageManagerDetector.getUpdateCommand(pm, PACKAGE_NAME, undefined, registryUrl)}`);
45652
45723
  }
45653
45724
  const comparison = import_compare_versions.compareVersions(currentVersion, targetVersion);
45654
45725
  if (comparison === 0) {
@@ -45681,8 +45752,8 @@ Run 'ck update' to install`, "Update Check");
45681
45752
  return;
45682
45753
  }
45683
45754
  }
45684
- const updateCmd = PackageManagerDetector.getUpdateCommand(pm, PACKAGE_NAME, targetVersion);
45685
- logger.info(`Running: ${updateCmd}`);
45755
+ const updateCmd = PackageManagerDetector.getUpdateCommand(pm, PACKAGE_NAME, targetVersion, registryUrl);
45756
+ logger.info(`Running: ${redactCommandForLog(updateCmd)}`);
45686
45757
  s.start("Updating CLI...");
45687
45758
  try {
45688
45759
  await execAsync2(updateCmd, {
@@ -45706,7 +45777,7 @@ Run 'ck update' to install`, "Update Check");
45706
45777
  Or fix npm permissions: https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally`);
45707
45778
  }
45708
45779
  logger.error(`Update failed: ${errorMessage}`);
45709
- logger.info("Try running: npm install -g claudekit-cli@latest");
45780
+ logger.info(`Try running: ${redactCommandForLog(updateCmd)}`);
45710
45781
  throw new CliUpdateError(`Update failed: ${errorMessage}
45711
45782
 
45712
45783
  Manual update: ${updateCmd}`);
@@ -45726,7 +45797,6 @@ Manual update: ${updateCmd}`);
45726
45797
  }
45727
45798
  } catch (error) {
45728
45799
  if (error instanceof CliUpdateError) {
45729
- logger.error(error.message);
45730
45800
  throw error;
45731
45801
  }
45732
45802
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
@@ -58215,7 +58285,7 @@ var init_init_command_help = __esm(() => {
58215
58285
  },
58216
58286
  {
58217
58287
  flags: "--fresh",
58218
- description: "Remove ClaudeKit directories (commands/, agents/, skills/, rules/, hooks/) and reinstall"
58288
+ description: "Full reset: remove CK files, replace settings.json and CLAUDE.md, reinstall from scratch"
58219
58289
  }
58220
58290
  ]
58221
58291
  },
@@ -76097,7 +76167,6 @@ class InstalledSettingsTracker {
76097
76167
 
76098
76168
  // src/domains/installation/merger/settings-processor.ts
76099
76169
  init_settings_merger();
76100
- init_environment();
76101
76170
  init_logger();
76102
76171
  var import_fs_extra11 = __toESM(require_lib3(), 1);
76103
76172
 
@@ -76135,13 +76204,13 @@ class SettingsProcessor {
76135
76204
  const sourceContent = await import_fs_extra11.readFile(sourceFile, "utf-8");
76136
76205
  let transformedSource = sourceContent;
76137
76206
  if (this.isGlobal) {
76138
- const homeVar = isWindows() ? '"%USERPROFILE%"' : '"$HOME"';
76207
+ const homeVar = '"$HOME"';
76139
76208
  transformedSource = this.transformClaudePaths(sourceContent, homeVar);
76140
76209
  if (transformedSource !== sourceContent) {
76141
76210
  logger.debug(`Transformed .claude/ paths to ${homeVar}/.claude/ in settings.json for global installation`);
76142
76211
  }
76143
76212
  } else {
76144
- const projectDirVar = isWindows() ? '"%CLAUDE_PROJECT_DIR%"' : '"$CLAUDE_PROJECT_DIR"';
76213
+ const projectDirVar = '"$CLAUDE_PROJECT_DIR"';
76145
76214
  transformedSource = this.transformClaudePaths(sourceContent, projectDirVar);
76146
76215
  if (transformedSource !== sourceContent) {
76147
76216
  logger.debug(`Transformed .claude/ paths to ${projectDirVar}/.claude/ in settings.json for local installation`);
@@ -76151,18 +76220,25 @@ class SettingsProcessor {
76151
76220
  if (destExists && !this.forceOverwriteSettings) {
76152
76221
  await this.selectiveMergeSettings(transformedSource, destFile);
76153
76222
  } else {
76154
- const formattedContent = this.formatJsonContent(transformedSource);
76155
- await import_fs_extra11.writeFile(destFile, formattedContent, "utf-8");
76156
76223
  try {
76157
- const parsedSettings = JSON.parse(formattedContent);
76158
- if (this.forceOverwriteSettings && destExists) {
76159
- logger.debug("Force overwrite enabled, replaced settings.json completely");
76160
- if (this.tracker) {
76161
- await this.tracker.clearTracking();
76224
+ const parsedSettings = JSON.parse(transformedSource);
76225
+ this.fixHookCommandPaths(parsedSettings);
76226
+ await SettingsMerger.writeSettingsFile(destFile, parsedSettings);
76227
+ try {
76228
+ if (this.forceOverwriteSettings && destExists) {
76229
+ logger.debug("Force overwrite enabled, replaced settings.json completely");
76230
+ if (this.tracker) {
76231
+ await this.tracker.clearTracking();
76232
+ }
76162
76233
  }
76234
+ await this.trackInstalledSettings(parsedSettings);
76235
+ } catch {
76236
+ logger.debug("Settings tracking failed (non-fatal)");
76163
76237
  }
76164
- await this.trackInstalledSettings(parsedSettings);
76165
- } catch {}
76238
+ } catch {
76239
+ const formattedContent = this.formatJsonContent(transformedSource);
76240
+ await import_fs_extra11.writeFile(destFile, formattedContent, "utf-8");
76241
+ }
76166
76242
  }
76167
76243
  } catch (error) {
76168
76244
  logger.error(`Failed to process settings.json: ${error}`);
@@ -76222,6 +76298,10 @@ class SettingsProcessor {
76222
76298
  }
76223
76299
  await this.tracker.saveInstalledSettings(installedSettings);
76224
76300
  }
76301
+ const pathsFixed = this.fixHookCommandPaths(mergeResult.merged);
76302
+ if (pathsFixed) {
76303
+ logger.info("Fixed hook command paths to canonical quoted format");
76304
+ }
76225
76305
  await SettingsMerger.writeSettingsFile(destFile, mergeResult.merged);
76226
76306
  logger.success("Merged settings.json (user customizations preserved)");
76227
76307
  }
@@ -76306,12 +76386,14 @@ class SettingsProcessor {
76306
76386
  const content = await import_fs_extra11.readFile(destFile, "utf-8");
76307
76387
  if (!content.trim())
76308
76388
  return null;
76309
- const homeVar = isWindows() ? "%USERPROFILE%" : "$HOME";
76389
+ const homeVar = "$HOME";
76310
76390
  let normalized = content;
76311
76391
  normalized = normalized.replace(/"\$CLAUDE_PROJECT_DIR"/g, `"${homeVar}"`);
76312
76392
  normalized = normalized.replace(/\$CLAUDE_PROJECT_DIR/g, homeVar);
76313
76393
  normalized = normalized.replace(/"%CLAUDE_PROJECT_DIR%"/g, `"${homeVar}"`);
76314
76394
  normalized = normalized.replace(/%CLAUDE_PROJECT_DIR%/g, homeVar);
76395
+ normalized = normalized.replace(/"%USERPROFILE%"/g, `"${homeVar}"`);
76396
+ normalized = normalized.replace(/%USERPROFILE%/g, homeVar);
76315
76397
  if (normalized !== content) {
76316
76398
  logger.debug("Normalized $CLAUDE_PROJECT_DIR paths to $HOME in existing global settings");
76317
76399
  }
@@ -76326,15 +76408,85 @@ class SettingsProcessor {
76326
76408
  throw new Error("Settings file contains potentially unsafe path characters");
76327
76409
  }
76328
76410
  let transformed = content;
76329
- const jsonSafePrefix = prefix.includes('"') ? prefix.replace(/"/g, "\\\"") : prefix;
76330
76411
  const rawPrefix = prefix.replace(/"/g, "");
76331
- transformed = transformed.replace(/(node\s+)(?:\.\/)?\.claude\//g, `$1${jsonSafePrefix}/.claude/`);
76412
+ transformed = transformed.replace(/(node\s+)(?:\.\/)?(\.claude\/[^\s"\\]+)/g, `$1\\"${rawPrefix}/$2\\"`);
76332
76413
  if (rawPrefix.includes("HOME") || rawPrefix.includes("USERPROFILE")) {
76333
76414
  transformed = transformed.replace(/\$CLAUDE_PROJECT_DIR/g, rawPrefix);
76334
76415
  transformed = transformed.replace(/%CLAUDE_PROJECT_DIR%/g, rawPrefix);
76335
76416
  }
76336
76417
  return transformed;
76337
76418
  }
76419
+ fixHookCommandPaths(settings) {
76420
+ let fixed = false;
76421
+ if (settings.hooks) {
76422
+ for (const entries of Object.values(settings.hooks)) {
76423
+ for (const entry of entries) {
76424
+ if ("command" in entry && entry.command) {
76425
+ const result = this.fixSingleCommandPath(entry.command);
76426
+ if (result !== entry.command) {
76427
+ entry.command = result;
76428
+ fixed = true;
76429
+ }
76430
+ }
76431
+ if ("hooks" in entry && entry.hooks) {
76432
+ for (const hook of entry.hooks) {
76433
+ if (hook.command) {
76434
+ const result = this.fixSingleCommandPath(hook.command);
76435
+ if (result !== hook.command) {
76436
+ hook.command = result;
76437
+ fixed = true;
76438
+ }
76439
+ }
76440
+ }
76441
+ }
76442
+ }
76443
+ }
76444
+ }
76445
+ const statusLine = settings.statusLine;
76446
+ if (statusLine?.command) {
76447
+ const result = this.fixSingleCommandPath(statusLine.command);
76448
+ if (result !== statusLine.command) {
76449
+ statusLine.command = result;
76450
+ fixed = true;
76451
+ }
76452
+ }
76453
+ return fixed;
76454
+ }
76455
+ fixSingleCommandPath(cmd) {
76456
+ if (!cmd.includes(".claude/") && !cmd.includes(".claude\\"))
76457
+ return cmd;
76458
+ const varOnlyQuotingRe = /^(node\s+)"(\$HOME|\$CLAUDE_PROJECT_DIR|%USERPROFILE%|%CLAUDE_PROJECT_DIR%)"[/\\](.+)$/;
76459
+ const varOnlyMatch = cmd.match(varOnlyQuotingRe);
76460
+ if (varOnlyMatch) {
76461
+ const [, nodePrefix, capturedVar, restPath] = varOnlyMatch;
76462
+ const canonicalVar = this.canonicalizePathVar(capturedVar);
76463
+ return `${nodePrefix}"${canonicalVar}/${restPath.replace(/\\/g, "/")}"`;
76464
+ }
76465
+ const tildeRe = /^(node\s+)~[/\\](.+)$/;
76466
+ const tildeMatch = cmd.match(tildeRe);
76467
+ if (tildeMatch) {
76468
+ const [, nodePrefix, restPath] = tildeMatch;
76469
+ return `${nodePrefix}"$HOME/${restPath.replace(/\\/g, "/")}"`;
76470
+ }
76471
+ const unquotedRe = /^(node\s+)(\$HOME|\$CLAUDE_PROJECT_DIR|%USERPROFILE%|%CLAUDE_PROJECT_DIR%)[/\\](.+)$/;
76472
+ const unquotedMatch = cmd.match(unquotedRe);
76473
+ if (unquotedMatch) {
76474
+ const [, nodePrefix, capturedVar, restPath] = unquotedMatch;
76475
+ const canonicalVar = this.canonicalizePathVar(capturedVar);
76476
+ return `${nodePrefix}"${canonicalVar}/${restPath.replace(/\\/g, "/")}"`;
76477
+ }
76478
+ return cmd;
76479
+ }
76480
+ canonicalizePathVar(capturedVar) {
76481
+ switch (capturedVar) {
76482
+ case "%USERPROFILE%":
76483
+ return "$HOME";
76484
+ case "%CLAUDE_PROJECT_DIR%":
76485
+ return "$CLAUDE_PROJECT_DIR";
76486
+ default:
76487
+ return capturedVar;
76488
+ }
76489
+ }
76338
76490
  }
76339
76491
 
76340
76492
  // src/domains/installation/merger/copy-executor.ts
@@ -79289,6 +79441,10 @@ Please use only one download method.`);
79289
79441
  if (validOptions.useGit && validOptions.beta) {
79290
79442
  logger.warning("--beta flag is ignored when using --use-git (version already specified via --release)");
79291
79443
  }
79444
+ if (validOptions.fresh && !validOptions.forceOverwriteSettings) {
79445
+ validOptions.forceOverwriteSettings = true;
79446
+ logger.debug("--fresh: auto-enabling settings.json full replace");
79447
+ }
79292
79448
  if (validOptions.fresh && validOptions.sync) {
79293
79449
  throw new Error(`--fresh and --sync are mutually exclusive.
79294
79450
 
@@ -79322,9 +79478,9 @@ async function handlePostInstall(ctx) {
79322
79478
  const claudeMdSource = join88(ctx.extractDir, "CLAUDE.md");
79323
79479
  const claudeMdDest = join88(ctx.resolvedDir, "CLAUDE.md");
79324
79480
  if (await import_fs_extra30.pathExists(claudeMdSource)) {
79325
- if (!await import_fs_extra30.pathExists(claudeMdDest)) {
79481
+ if (ctx.options.fresh || !await import_fs_extra30.pathExists(claudeMdDest)) {
79326
79482
  await import_fs_extra30.copy(claudeMdSource, claudeMdDest);
79327
- logger.success("Copied CLAUDE.md to global directory");
79483
+ logger.success(ctx.options.fresh ? "Replaced CLAUDE.md in global directory (fresh install)" : "Copied CLAUDE.md to global directory");
79328
79484
  } else {
79329
79485
  logger.debug("CLAUDE.md already exists in global directory (preserved)");
79330
79486
  }
@@ -82932,7 +83088,7 @@ function registerCommands(cli) {
82932
83088
  }
82933
83089
  await newCommand(options2);
82934
83090
  });
82935
- cli.command("init", "Initialize or update ClaudeKit project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to use: engineer, marketing, all, or comma-separated").option("-r, --release <version>", "Skip version selection, use specific version (e.g., latest, v1.0.0)").option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)").option("--only <pattern>", "Include only files matching glob pattern (can be used multiple times)").option("-g, --global", "Use platform-specific user configuration directory").option("--fresh", "Completely remove .claude directory before downloading (requires confirmation)").option("--install-skills", "Install skills dependencies (non-interactive mode)").option("--with-sudo", "Include system packages requiring sudo (Linux: ffmpeg, imagemagick)").option("--prefix", "Add /ck: prefix to all slash commands by moving them to commands/ck/ subdirectory").option("--beta", "Show beta versions in selection prompt").option("--refresh", "Bypass release cache to fetch latest versions from GitHub").option("--dry-run", "Preview changes without applying them (requires --prefix)").option("--force-overwrite", "Override ownership protections and delete user-modified files (requires --prefix)").option("--force-overwrite-settings", "Fully replace settings.json instead of selective merge (destroys user customizations)").option("--skip-setup", "Skip interactive configuration wizard").option("--docs-dir <name>", "Custom docs folder name (default: docs)").option("--plans-dir <name>", "Custom plans folder name (default: plans)").option("-y, --yes", "Non-interactive mode with sensible defaults (skip all prompts)").option("--sync", "Sync config files from upstream with interactive hunk-by-hunk merge").option("--use-git", "Use git clone instead of GitHub API (uses SSH/HTTPS credentials)").option("--archive <path>", "Use local archive file instead of downloading (zip/tar.gz)").option("--kit-path <path>", "Use local kit directory instead of downloading").action(async (options2) => {
83091
+ cli.command("init", "Initialize or update ClaudeKit project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to use: engineer, marketing, all, or comma-separated").option("-r, --release <version>", "Skip version selection, use specific version (e.g., latest, v1.0.0)").option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)").option("--only <pattern>", "Include only files matching glob pattern (can be used multiple times)").option("-g, --global", "Use platform-specific user configuration directory").option("--fresh", "Full reset: remove CK files, replace settings.json and CLAUDE.md, reinstall from scratch").option("--install-skills", "Install skills dependencies (non-interactive mode)").option("--with-sudo", "Include system packages requiring sudo (Linux: ffmpeg, imagemagick)").option("--prefix", "Add /ck: prefix to all slash commands by moving them to commands/ck/ subdirectory").option("--beta", "Show beta versions in selection prompt").option("--refresh", "Bypass release cache to fetch latest versions from GitHub").option("--dry-run", "Preview changes without applying them (requires --prefix)").option("--force-overwrite", "Override ownership protections and delete user-modified files (requires --prefix)").option("--force-overwrite-settings", "Fully replace settings.json instead of selective merge (destroys user customizations)").option("--skip-setup", "Skip interactive configuration wizard").option("--docs-dir <name>", "Custom docs folder name (default: docs)").option("--plans-dir <name>", "Custom plans folder name (default: plans)").option("-y, --yes", "Non-interactive mode with sensible defaults (skip all prompts)").option("--sync", "Sync config files from upstream with interactive hunk-by-hunk merge").option("--use-git", "Use git clone instead of GitHub API (uses SSH/HTTPS credentials)").option("--archive <path>", "Use local archive file instead of downloading (zip/tar.gz)").option("--kit-path <path>", "Use local kit directory instead of downloading").action(async (options2) => {
82936
83092
  if (options2.exclude && !Array.isArray(options2.exclude)) {
82937
83093
  options2.exclude = [options2.exclude];
82938
83094
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudekit-cli",
3
- "version": "3.34.3",
3
+ "version": "3.34.5",
4
4
  "description": "CLI tool for bootstrapping and updating ClaudeKit projects",
5
5
  "type": "module",
6
6
  "repository": {