claudekit-cli 3.12.1 → 3.13.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 +803 -546
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4094,24 +4094,10 @@ function supportsUnicode() {
4094
4094
  function isTTY() {
4095
4095
  return process.stdout.isTTY === true;
4096
4096
  }
4097
- function getStatusSymbols() {
4098
- return supportsUnicode() ? UNICODE_SYMBOLS : ASCII_SYMBOLS;
4099
- }
4100
- var import_picocolors, UNICODE_SYMBOLS, ASCII_SYMBOLS, COLOR_PALETTE;
4097
+ var import_picocolors, COLOR_PALETTE;
4101
4098
  var init_terminal_utils = __esm(() => {
4099
+ init_output_manager();
4102
4100
  import_picocolors = __toESM(require_picocolors(), 1);
4103
- UNICODE_SYMBOLS = {
4104
- pass: "✓",
4105
- warn: "⚠",
4106
- fail: "✗",
4107
- info: "ℹ"
4108
- };
4109
- ASCII_SYMBOLS = {
4110
- pass: "[PASS]",
4111
- warn: "[WARN]",
4112
- fail: "[FAIL]",
4113
- info: "[INFO]"
4114
- };
4115
4101
  COLOR_PALETTE = {
4116
4102
  pass: import_picocolors.default.green,
4117
4103
  warn: import_picocolors.default.yellow,
@@ -4123,6 +4109,16 @@ var init_terminal_utils = __esm(() => {
4123
4109
  });
4124
4110
 
4125
4111
  // src/shared/output-manager.ts
4112
+ function getStatusSymbols() {
4113
+ const symbolSet = supportsUnicode() ? SYMBOLS.unicode : SYMBOLS.ascii;
4114
+ return {
4115
+ pass: symbolSet.pass,
4116
+ warn: symbolSet.warn,
4117
+ fail: symbolSet.fail,
4118
+ info: symbolSet.infoStatus
4119
+ };
4120
+ }
4121
+
4126
4122
  class OutputManager {
4127
4123
  config = {
4128
4124
  verbose: false,
@@ -4132,6 +4128,7 @@ class OutputManager {
4132
4128
  jsonBuffer = [];
4133
4129
  unicodeSupported;
4134
4130
  flushPromise = null;
4131
+ flushQueued = false;
4135
4132
  constructor() {
4136
4133
  this.unicodeSupported = supportsUnicode();
4137
4134
  }
@@ -4237,8 +4234,12 @@ class OutputManager {
4237
4234
  ...entry,
4238
4235
  timestamp: new Date().toISOString()
4239
4236
  });
4240
- if (this.jsonBuffer.length >= 1000 && !this.flushPromise) {
4241
- queueMicrotask(() => this.flushJson());
4237
+ if (this.jsonBuffer.length >= 1000 && !this.flushPromise && !this.flushQueued) {
4238
+ this.flushQueued = true;
4239
+ queueMicrotask(() => {
4240
+ this.flushQueued = false;
4241
+ this.flushJson();
4242
+ });
4242
4243
  }
4243
4244
  }
4244
4245
  addJsonResult(data) {
@@ -4268,6 +4269,7 @@ class OutputManager {
4268
4269
  this.config = { verbose: false, json: false, quiet: false };
4269
4270
  this.jsonBuffer = [];
4270
4271
  this.flushPromise = null;
4272
+ this.flushQueued = false;
4271
4273
  this.unicodeSupported = supportsUnicode();
4272
4274
  }
4273
4275
  }
@@ -4285,7 +4287,11 @@ var init_output_manager = __esm(() => {
4285
4287
  line: "│",
4286
4288
  selected: "●",
4287
4289
  unselected: "○",
4288
- pointer: ">"
4290
+ pointer: ">",
4291
+ pass: "✓",
4292
+ warn: "⚠",
4293
+ fail: "✗",
4294
+ infoStatus: "ℹ"
4289
4295
  },
4290
4296
  ascii: {
4291
4297
  prompt: "?",
@@ -4296,7 +4302,11 @@ var init_output_manager = __esm(() => {
4296
4302
  line: "|",
4297
4303
  selected: ">",
4298
4304
  unselected: " ",
4299
- pointer: ">"
4305
+ pointer: ">",
4306
+ pass: "[PASS]",
4307
+ warn: "[WARN]",
4308
+ fail: "[FAIL]",
4309
+ infoStatus: "[INFO]"
4300
4310
  }
4301
4311
  };
4302
4312
  output = new OutputManager;
@@ -4308,6 +4318,7 @@ import { createWriteStream } from "node:fs";
4308
4318
  class Logger {
4309
4319
  verboseEnabled = false;
4310
4320
  logFileStream;
4321
+ exitHandlerRegistered = false;
4311
4322
  info(message) {
4312
4323
  const symbols = output.getSymbols();
4313
4324
  console.log(import_picocolors3.default.blue(symbols.info), message);
@@ -4362,9 +4373,49 @@ class Logger {
4362
4373
  flags: "a",
4363
4374
  mode: 384
4364
4375
  });
4376
+ this.registerExitHandler();
4365
4377
  this.verbose(`Logging to file: ${path}`);
4366
4378
  }
4367
4379
  }
4380
+ close() {
4381
+ if (this.logFileStream) {
4382
+ this.logFileStream.end();
4383
+ this.logFileStream = undefined;
4384
+ }
4385
+ }
4386
+ registerExitHandler() {
4387
+ if (this.exitHandlerRegistered)
4388
+ return;
4389
+ this.exitHandlerRegistered = true;
4390
+ const cleanup = () => {
4391
+ if (this.logFileStream) {
4392
+ try {
4393
+ this.logFileStream.end();
4394
+ } catch {}
4395
+ this.logFileStream = undefined;
4396
+ }
4397
+ };
4398
+ process.on("exit", cleanup);
4399
+ process.on("SIGINT", () => {
4400
+ cleanup();
4401
+ process.exit(130);
4402
+ });
4403
+ process.on("SIGTERM", () => {
4404
+ cleanup();
4405
+ process.exit(143);
4406
+ });
4407
+ process.on("uncaughtException", (error) => {
4408
+ if (this.logFileStream) {
4409
+ const timestamp = new Date().toISOString();
4410
+ this.logFileStream.write(`${timestamp} [FATAL] Uncaught exception: ${error.message}
4411
+ `);
4412
+ this.logFileStream.write(`${error.stack}
4413
+ `);
4414
+ }
4415
+ cleanup();
4416
+ process.exit(1);
4417
+ });
4418
+ }
4368
4419
  sanitize(text) {
4369
4420
  return text.replace(/ghp_[a-zA-Z0-9]{36}/g, "ghp_***").replace(/github_pat_[a-zA-Z0-9_]{82}/g, "github_pat_***").replace(/gho_[a-zA-Z0-9]{36}/g, "gho_***").replace(/ghu_[a-zA-Z0-9]{36}/g, "ghu_***").replace(/ghs_[a-zA-Z0-9]{36}/g, "ghs_***").replace(/ghr_[a-zA-Z0-9]{36}/g, "ghr_***").replace(/Bearer [a-zA-Z0-9_-]+/g, "Bearer ***").replace(/token=[a-zA-Z0-9_-]+/g, "token=***");
4370
4421
  }
@@ -6487,6 +6538,35 @@ var require_lib = __commonJS((exports, module) => {
6487
6538
  };
6488
6539
  });
6489
6540
 
6541
+ // src/shared/environment.ts
6542
+ function isCIEnvironment() {
6543
+ return process.env.CI === "true" || process.env.CI_SAFE_MODE === "true";
6544
+ }
6545
+ function isNonInteractive() {
6546
+ return !process.stdin.isTTY || process.env.CI === "true" || process.env.NON_INTERACTIVE === "true";
6547
+ }
6548
+ function isMacOS() {
6549
+ return process.platform === "darwin";
6550
+ }
6551
+ function isWindows() {
6552
+ return process.platform === "win32";
6553
+ }
6554
+ function getOptimalConcurrency() {
6555
+ if (isMacOS())
6556
+ return PLATFORM_CONCURRENCY.MACOS;
6557
+ if (isWindows())
6558
+ return PLATFORM_CONCURRENCY.WINDOWS;
6559
+ return PLATFORM_CONCURRENCY.LINUX;
6560
+ }
6561
+ var PLATFORM_CONCURRENCY;
6562
+ var init_environment = __esm(() => {
6563
+ PLATFORM_CONCURRENCY = {
6564
+ MACOS: 10,
6565
+ WINDOWS: 15,
6566
+ LINUX: 20
6567
+ };
6568
+ });
6569
+
6490
6570
  // src/types/kit.ts
6491
6571
  var KitType, KitConfigSchema, AVAILABLE_KITS, NEVER_COPY_PATTERNS, USER_CONFIG_PATTERNS, PROTECTED_PATTERNS;
6492
6572
  var init_kit = __esm(() => {
@@ -6561,6 +6641,7 @@ var init_commands = __esm(() => {
6561
6641
  opencode: exports_external.boolean().default(false),
6562
6642
  gemini: exports_external.boolean().default(false),
6563
6643
  installSkills: exports_external.boolean().default(false),
6644
+ withSudo: exports_external.boolean().default(false),
6564
6645
  prefix: exports_external.boolean().default(false),
6565
6646
  beta: exports_external.boolean().default(false),
6566
6647
  dryRun: exports_external.boolean().default(false),
@@ -6578,6 +6659,7 @@ var init_commands = __esm(() => {
6578
6659
  global: exports_external.boolean().default(false),
6579
6660
  fresh: exports_external.boolean().default(false),
6580
6661
  installSkills: exports_external.boolean().default(false),
6662
+ withSudo: exports_external.boolean().default(false),
6581
6663
  prefix: exports_external.boolean().default(false),
6582
6664
  beta: exports_external.boolean().default(false),
6583
6665
  dryRun: exports_external.boolean().default(false),
@@ -6914,35 +6996,6 @@ var require_umd = __commonJS((exports, module) => {
6914
6996
  });
6915
6997
  });
6916
6998
 
6917
- // src/shared/environment.ts
6918
- function isCIEnvironment() {
6919
- return process.env.CI === "true" || process.env.CI_SAFE_MODE === "true";
6920
- }
6921
- function isNonInteractive() {
6922
- return !process.stdin.isTTY || process.env.CI === "true" || process.env.NON_INTERACTIVE === "true";
6923
- }
6924
- function isMacOS() {
6925
- return process.platform === "darwin";
6926
- }
6927
- function isWindows4() {
6928
- return process.platform === "win32";
6929
- }
6930
- function getOptimalConcurrency() {
6931
- if (isMacOS())
6932
- return PLATFORM_CONCURRENCY.MACOS;
6933
- if (isWindows4())
6934
- return PLATFORM_CONCURRENCY.WINDOWS;
6935
- return PLATFORM_CONCURRENCY.LINUX;
6936
- }
6937
- var PLATFORM_CONCURRENCY;
6938
- var init_environment = __esm(() => {
6939
- PLATFORM_CONCURRENCY = {
6940
- MACOS: 10,
6941
- WINDOWS: 15,
6942
- LINUX: 20
6943
- };
6944
- });
6945
-
6946
6999
  // node_modules/sisteransi/src/index.js
6947
7000
  var require_src = __commonJS((exports, module) => {
6948
7001
  var ESC = "\x1B";
@@ -7891,9 +7944,8 @@ function validatePackageName(packageName) {
7891
7944
  function validateScriptPath(skillsDir, scriptPath) {
7892
7945
  const skillsDirResolved = resolve2(skillsDir);
7893
7946
  const scriptPathResolved = resolve2(scriptPath);
7894
- const isWindows5 = process.platform === "win32";
7895
- const skillsDirNormalized = isWindows5 ? skillsDirResolved.toLowerCase() : skillsDirResolved;
7896
- const scriptPathNormalized = isWindows5 ? scriptPathResolved.toLowerCase() : scriptPathResolved;
7947
+ const skillsDirNormalized = isWindows() ? skillsDirResolved.toLowerCase() : skillsDirResolved;
7948
+ const scriptPathNormalized = isWindows() ? scriptPathResolved.toLowerCase() : scriptPathResolved;
7897
7949
  if (!scriptPathNormalized.startsWith(skillsDirNormalized)) {
7898
7950
  throw new Error(`Script path outside skills directory: ${scriptPath}`);
7899
7951
  }
@@ -7908,6 +7960,7 @@ function validateScriptPath(skillsDir, scriptPath) {
7908
7960
  }
7909
7961
  var NPM_PACKAGE_REGEX;
7910
7962
  var init_validators = __esm(() => {
7963
+ init_environment();
7911
7964
  init_logger();
7912
7965
  NPM_PACKAGE_REGEX = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
7913
7966
  });
@@ -8239,7 +8292,8 @@ var init_install_error_handler = __esm(() => {
8239
8292
 
8240
8293
  // src/services/package-installer/skills-installer.ts
8241
8294
  import { join as join18 } from "node:path";
8242
- async function installSkillsDependencies(skillsDir) {
8295
+ async function installSkillsDependencies(skillsDir, options = {}) {
8296
+ const { skipConfirm = false, withSudo = false } = options;
8243
8297
  const displayName = "Skills Dependencies";
8244
8298
  if (isCIEnvironment()) {
8245
8299
  logger.info("CI environment detected: skipping skills installation");
@@ -8249,7 +8303,7 @@ async function installSkillsDependencies(skillsDir) {
8249
8303
  error: "Installation skipped in CI environment"
8250
8304
  };
8251
8305
  }
8252
- if (isNonInteractive()) {
8306
+ if (isNonInteractive() && !skipConfirm) {
8253
8307
  logger.info("Running in non-interactive mode. Skipping skills installation.");
8254
8308
  logger.info("See INSTALLATION.md for manual installation instructions.");
8255
8309
  return {
@@ -8311,11 +8365,15 @@ async function installSkillsDependencies(skillsDir) {
8311
8365
  logger.verbose("Could not preview script contents");
8312
8366
  }
8313
8367
  }
8314
- const shouldProceed = await clack.confirm({
8315
- message: "Execute this installation script?",
8316
- initialValue: false
8317
- });
8318
- if (clack.isCancel(shouldProceed) || !shouldProceed) {
8368
+ let shouldProceed = skipConfirm;
8369
+ if (!skipConfirm) {
8370
+ const userChoice = await clack.confirm({
8371
+ message: "Execute this installation script?",
8372
+ initialValue: false
8373
+ });
8374
+ shouldProceed = !clack.isCancel(userChoice) && userChoice;
8375
+ }
8376
+ if (!shouldProceed) {
8319
8377
  logger.info("Installation cancelled by user");
8320
8378
  logger.info("");
8321
8379
  logger.info("\uD83D\uDCD6 Manual Installation Instructions:");
@@ -8333,8 +8391,8 @@ async function installSkillsDependencies(skillsDir) {
8333
8391
  logger.info(`Running: ${scriptPath}`);
8334
8392
  const scriptArgs = ["--yes"];
8335
8393
  if (hasInstallState(skillsDir)) {
8336
- if (isNonInteractive()) {
8337
- logger.info("Resuming previous installation (non-interactive mode)...");
8394
+ if (skipConfirm || isNonInteractive()) {
8395
+ logger.info("Resuming previous installation...");
8338
8396
  scriptArgs.push("--resume");
8339
8397
  } else {
8340
8398
  const shouldResume = await clack.confirm({
@@ -8350,9 +8408,27 @@ async function installSkillsDependencies(skillsDir) {
8350
8408
  if (platform5 === "linux") {
8351
8409
  const needsSudo = await checkNeedsSudoPackages();
8352
8410
  if (needsSudo) {
8353
- if (isNonInteractive()) {
8354
- logger.info("Skipping system packages in non-interactive mode.");
8355
- logger.info("Install manually: sudo apt-get install -y ffmpeg imagemagick");
8411
+ if (withSudo) {
8412
+ logger.info("");
8413
+ logger.warning("Installing system packages with sudo:");
8414
+ logger.info(" • ffmpeg - Video/audio processing");
8415
+ logger.info(" • imagemagick - Image editing & conversion");
8416
+ logger.info("");
8417
+ logger.info("sudo will run: apt-get install -y ffmpeg imagemagick");
8418
+ logger.info("");
8419
+ scriptArgs.push("--with-sudo");
8420
+ } else if (skipConfirm || isNonInteractive()) {
8421
+ logger.info("");
8422
+ logger.warning("System packages skipped (not included without --with-sudo):");
8423
+ logger.info(" • ffmpeg - Video/audio processing");
8424
+ logger.info(" • imagemagick - Image editing & conversion");
8425
+ logger.info("");
8426
+ logger.info("To include system packages, run with --with-sudo flag:");
8427
+ logger.info(" ck init -g -y --install-skills --with-sudo");
8428
+ logger.info("");
8429
+ logger.info("Or install manually:");
8430
+ logger.info(" sudo apt-get install -y ffmpeg imagemagick");
8431
+ logger.info("");
8356
8432
  } else {
8357
8433
  logger.info("");
8358
8434
  logger.info("System packages (requires sudo):");
@@ -8364,6 +8440,8 @@ async function installSkillsDependencies(skillsDir) {
8364
8440
  initialValue: true
8365
8441
  });
8366
8442
  if (!clack.isCancel(shouldInstallSudo) && shouldInstallSudo) {
8443
+ logger.info("");
8444
+ logger.info("sudo will run: apt-get install -y ffmpeg imagemagick");
8367
8445
  scriptArgs.push("--with-sudo");
8368
8446
  } else {
8369
8447
  logger.info("Skipping system packages. Install manually later:");
@@ -8440,9 +8518,9 @@ async function installSkillsDependencies(skillsDir) {
8440
8518
  };
8441
8519
  }
8442
8520
  }
8443
- async function handleSkillsInstallation(skillsDir) {
8521
+ async function handleSkillsInstallation(skillsDir, options = {}) {
8444
8522
  try {
8445
- const skillsResult = await installSkillsDependencies(skillsDir);
8523
+ const skillsResult = await installSkillsDependencies(skillsDir, options);
8446
8524
  if (skillsResult.success) {
8447
8525
  if (skillsResult.version === PARTIAL_INSTALL_VERSION) {
8448
8526
  logger.success("Skills core dependencies installed (some optional packages skipped)");
@@ -8630,7 +8708,6 @@ import { existsSync as existsSync14 } from "node:fs";
8630
8708
  import { mkdir as mkdir6, symlink as symlink2 } from "node:fs/promises";
8631
8709
  import { dirname as dirname4, join as join21 } from "node:path";
8632
8710
  async function createSymlink(targetPath, linkPath, projectDir, isGlobal) {
8633
- const isWindows5 = process.platform === "win32";
8634
8711
  const linkDir = dirname4(linkPath);
8635
8712
  if (!existsSync14(linkDir)) {
8636
8713
  await mkdir6(linkDir, { recursive: true });
@@ -8645,7 +8722,7 @@ async function createSymlink(targetPath, linkPath, projectDir, isGlobal) {
8645
8722
  symlinkTarget = isLocalConfig ? "../.mcp.json" : targetPath;
8646
8723
  }
8647
8724
  try {
8648
- await symlink2(symlinkTarget, linkPath, isWindows5 ? "file" : undefined);
8725
+ await symlink2(symlinkTarget, linkPath, isWindows() ? "file" : undefined);
8649
8726
  logger.debug(`Created symlink: ${linkPath} → ${symlinkTarget}`);
8650
8727
  return { success: true, method: "symlink", targetPath, geminiSettingsPath: linkPath };
8651
8728
  } catch (error) {
@@ -8658,6 +8735,7 @@ async function createSymlink(targetPath, linkPath, projectDir, isGlobal) {
8658
8735
  }
8659
8736
  }
8660
8737
  var init_linker_core = __esm(() => {
8738
+ init_environment();
8661
8739
  init_logger();
8662
8740
  init_validation();
8663
8741
  });
@@ -15745,9 +15823,19 @@ import { exec as exec4 } from "node:child_process";
15745
15823
  import { promisify as promisify4 } from "node:util";
15746
15824
 
15747
15825
  // src/services/package-installer/dependency-checker.ts
15748
- init_logger();
15749
15826
  import { exec } from "node:child_process";
15750
15827
  import { promisify } from "node:util";
15828
+
15829
+ // src/shared/error-utils.ts
15830
+ function operationError(operation, subject, details) {
15831
+ return `${operation} failed for '${subject}': ${details}`;
15832
+ }
15833
+ function notFoundError(type, name, hint) {
15834
+ return hint ? `${type} not found: ${name} (${hint})` : `${type} not found: ${name}`;
15835
+ }
15836
+
15837
+ // src/services/package-installer/dependency-checker.ts
15838
+ init_logger();
15751
15839
  var execAsync = promisify(exec);
15752
15840
  function shouldSkipExpensiveOperations() {
15753
15841
  if (process.env.CK_TEST_HOME) {
@@ -15780,13 +15868,30 @@ function getCICommandPath(command) {
15780
15868
  const osInfo = getOSInfo();
15781
15869
  switch (command) {
15782
15870
  case "node":
15783
- return osInfo.isWindows ? "C:\\Program Files\\nodejs\\node.exe" : "/usr/bin/node";
15871
+ if (osInfo.isWindows) {
15872
+ return process.env.NODE_PATH ? `${process.env.NODE_PATH}\\node.exe` : "C:\\Program Files\\nodejs\\node.exe";
15873
+ }
15874
+ return "/usr/bin/node";
15784
15875
  case "python3":
15785
15876
  case "python":
15786
- return osInfo.isWindows ? "C:\\Python39\\python.exe" : "/usr/bin/python3";
15877
+ if (osInfo.isWindows) {
15878
+ const pythonDir = process.env.PYTHON;
15879
+ if (pythonDir) {
15880
+ return `${pythonDir}\\python.exe`;
15881
+ }
15882
+ return "C:\\Windows\\py.exe";
15883
+ }
15884
+ return "/usr/bin/python3";
15787
15885
  case "pip3":
15788
15886
  case "pip":
15789
- return osInfo.isWindows ? "C:\\Python39\\Scripts\\pip.exe" : "/usr/bin/pip3";
15887
+ if (osInfo.isWindows) {
15888
+ const pythonDir = process.env.PYTHON;
15889
+ if (pythonDir) {
15890
+ return `${pythonDir}\\Scripts\\pip.exe`;
15891
+ }
15892
+ return "C:\\Windows\\py.exe -m pip";
15893
+ }
15894
+ return "/usr/bin/pip3";
15790
15895
  default:
15791
15896
  return null;
15792
15897
  }
@@ -15932,7 +16037,7 @@ async function checkDependency(config) {
15932
16037
  installed: false,
15933
16038
  meetsRequirements: false,
15934
16039
  minVersion: config.minVersion,
15935
- message: `${config.name} not found in PATH`
16040
+ message: notFoundError(config.name, "PATH", "check installation")
15936
16041
  };
15937
16042
  }
15938
16043
  async function checkAllDependencies() {
@@ -16412,8 +16517,8 @@ class PathResolver {
16412
16517
  static getTestHomeDir() {
16413
16518
  return process.env.CK_TEST_HOME;
16414
16519
  }
16415
- static isPathSafe(path) {
16416
- if (!path || typeof path !== "string") {
16520
+ static isValidComponentName(name) {
16521
+ if (!name || typeof name !== "string") {
16417
16522
  return false;
16418
16523
  }
16419
16524
  const dangerousPatterns = [
@@ -16421,17 +16526,17 @@ class PathResolver {
16421
16526
  "~"
16422
16527
  ];
16423
16528
  for (const pattern of dangerousPatterns) {
16424
- if (path.includes(pattern)) {
16529
+ if (name.includes(pattern)) {
16425
16530
  return false;
16426
16531
  }
16427
16532
  }
16428
- const normalized = normalize(path);
16533
+ const normalized = normalize(name);
16429
16534
  for (const pattern of dangerousPatterns) {
16430
16535
  if (normalized.includes(pattern)) {
16431
16536
  return false;
16432
16537
  }
16433
16538
  }
16434
- if (path.startsWith("/") || normalized.startsWith("/") || /^[a-zA-Z]:/.test(path)) {
16539
+ if (name.startsWith("/") || normalized.startsWith("/") || /^[a-zA-Z]:/.test(name)) {
16435
16540
  return false;
16436
16541
  }
16437
16542
  return true;
@@ -16495,7 +16600,7 @@ class PathResolver {
16495
16600
  return join(baseDir, "skills");
16496
16601
  }
16497
16602
  static buildComponentPath(baseDir, component, global2) {
16498
- if (!PathResolver.isPathSafe(component)) {
16603
+ if (!PathResolver.isValidComponentName(component)) {
16499
16604
  throw new Error(`Invalid component name: "${component}" contains path traversal patterns. Valid names are simple directory names like "agents", "commands", "workflows", "skills", or "hooks".`);
16500
16605
  }
16501
16606
  const prefix = PathResolver.getPathPrefix(global2);
@@ -16685,11 +16790,11 @@ function isValidVersion(version) {
16685
16790
  return /^[a-zA-Z0-9._-]+$/.test(version);
16686
16791
  }
16687
16792
  // src/domains/installation/package-managers/npm-detector.ts
16688
- var isWindows = process.platform === "win32";
16793
+ init_environment();
16689
16794
  function getNpmQuery() {
16690
16795
  return {
16691
16796
  pm: "npm",
16692
- cmd: isWindows ? "npm.cmd ls -g claudekit-cli --depth=0 --json" : "npm ls -g claudekit-cli --depth=0 --json",
16797
+ cmd: isWindows() ? "npm.cmd ls -g claudekit-cli --depth=0 --json" : "npm ls -g claudekit-cli --depth=0 --json",
16693
16798
  checkFn: (stdout) => {
16694
16799
  try {
16695
16800
  const data = JSON.parse(stdout);
@@ -16701,7 +16806,7 @@ function getNpmQuery() {
16701
16806
  };
16702
16807
  }
16703
16808
  function getNpmVersionCommand() {
16704
- return isWindows ? "npm.cmd --version" : "npm --version";
16809
+ return isWindows() ? "npm.cmd --version" : "npm --version";
16705
16810
  }
16706
16811
  async function getNpmVersion() {
16707
16812
  try {
@@ -16719,7 +16824,7 @@ function getNpmUpdateCommand(packageName, version) {
16719
16824
  throw new Error(`Invalid version: ${version}`);
16720
16825
  }
16721
16826
  const versionSuffix = version ? `@${version}` : "@latest";
16722
- return isWindows ? `npm.cmd install -g ${packageName}${versionSuffix}` : `npm install -g ${packageName}${versionSuffix}`;
16827
+ return isWindows() ? `npm.cmd install -g ${packageName}${versionSuffix}` : `npm install -g ${packageName}${versionSuffix}`;
16723
16828
  }
16724
16829
  // src/domains/installation/package-managers/bun-detector.ts
16725
16830
  function getBunQuery() {
@@ -16751,16 +16856,16 @@ function getBunUpdateCommand(packageName, version) {
16751
16856
  return `bun add -g ${packageName}${versionSuffix}`;
16752
16857
  }
16753
16858
  // src/domains/installation/package-managers/yarn-detector.ts
16754
- var isWindows2 = process.platform === "win32";
16859
+ init_environment();
16755
16860
  function getYarnQuery() {
16756
16861
  return {
16757
16862
  pm: "yarn",
16758
- cmd: isWindows2 ? "yarn.cmd global list --pattern claudekit-cli" : "yarn global list --pattern claudekit-cli",
16863
+ cmd: isWindows() ? "yarn.cmd global list --pattern claudekit-cli" : "yarn global list --pattern claudekit-cli",
16759
16864
  checkFn: (stdout) => stdout.includes("claudekit-cli")
16760
16865
  };
16761
16866
  }
16762
16867
  function getYarnVersionCommand() {
16763
- return isWindows2 ? "yarn.cmd --version" : "yarn --version";
16868
+ return isWindows() ? "yarn.cmd --version" : "yarn --version";
16764
16869
  }
16765
16870
  async function getYarnVersion() {
16766
16871
  try {
@@ -16778,19 +16883,19 @@ function getYarnUpdateCommand(packageName, version) {
16778
16883
  throw new Error(`Invalid version: ${version}`);
16779
16884
  }
16780
16885
  const versionSuffix = version ? `@${version}` : "@latest";
16781
- return isWindows2 ? `yarn.cmd global add ${packageName}${versionSuffix}` : `yarn global add ${packageName}${versionSuffix}`;
16886
+ return isWindows() ? `yarn.cmd global add ${packageName}${versionSuffix}` : `yarn global add ${packageName}${versionSuffix}`;
16782
16887
  }
16783
16888
  // src/domains/installation/package-managers/pnpm-detector.ts
16784
- var isWindows3 = process.platform === "win32";
16889
+ init_environment();
16785
16890
  function getPnpmQuery() {
16786
16891
  return {
16787
16892
  pm: "pnpm",
16788
- cmd: isWindows3 ? "pnpm.cmd ls -g claudekit-cli" : "pnpm ls -g claudekit-cli",
16893
+ cmd: isWindows() ? "pnpm.cmd ls -g claudekit-cli" : "pnpm ls -g claudekit-cli",
16789
16894
  checkFn: (stdout) => stdout.includes("claudekit-cli")
16790
16895
  };
16791
16896
  }
16792
16897
  function getPnpmVersionCommand() {
16793
- return isWindows3 ? "pnpm.cmd --version" : "pnpm --version";
16898
+ return isWindows() ? "pnpm.cmd --version" : "pnpm --version";
16794
16899
  }
16795
16900
  async function getPnpmVersion() {
16796
16901
  try {
@@ -16808,7 +16913,7 @@ function getPnpmUpdateCommand(packageName, version) {
16808
16913
  throw new Error(`Invalid version: ${version}`);
16809
16914
  }
16810
16915
  const versionSuffix = version ? `@${version}` : "@latest";
16811
- return isWindows3 ? `pnpm.cmd add -g ${packageName}${versionSuffix}` : `pnpm add -g ${packageName}${versionSuffix}`;
16916
+ return isWindows() ? `pnpm.cmd add -g ${packageName}${versionSuffix}` : `pnpm add -g ${packageName}${versionSuffix}`;
16812
16917
  }
16813
16918
  // src/domains/installation/package-managers/detection-core.ts
16814
16919
  init_logger();
@@ -19808,6 +19913,7 @@ function normalizeVersionTag(version) {
19808
19913
  return trimmed.startsWith("v") ? trimmed : `v${trimmed}`;
19809
19914
  }
19810
19915
  // src/domains/versioning/selection/selection-ui.ts
19916
+ init_environment();
19811
19917
  init_logger();
19812
19918
  init_dist2();
19813
19919
  var import_picocolors10 = __toESM(require_picocolors(), 1);
@@ -19965,6 +20071,11 @@ This could be due to:
19965
20071
  if (!allowManualEntry) {
19966
20072
  throw new Error(`No releases available for ${kit.name}`);
19967
20073
  }
20074
+ if (isNonInteractive()) {
20075
+ logger.warning(`Non-interactive mode: no releases found for ${kit.name}`);
20076
+ logger.info("Provide a specific version with --version flag or run in interactive mode");
20077
+ return null;
20078
+ }
19968
20079
  const tryManual = await se({
19969
20080
  message: "Would you like to enter a version manually?"
19970
20081
  });
@@ -19974,6 +20085,11 @@ This could be due to:
19974
20085
  return await getManualVersion(kit);
19975
20086
  }
19976
20087
  async function getManualVersion(kit) {
20088
+ if (isNonInteractive()) {
20089
+ logger.warning("Non-interactive mode: cannot prompt for manual version entry");
20090
+ logger.info("Provide a specific version with --version flag");
20091
+ return null;
20092
+ }
19977
20093
  const version = await te({
19978
20094
  message: `Enter version tag for ${kit.name}:`,
19979
20095
  placeholder: "v1.0.0",
@@ -19993,6 +20109,19 @@ async function getManualVersion(kit) {
19993
20109
  return normalizeVersionTag(version);
19994
20110
  }
19995
20111
  async function createVersionPrompt(kit, choices, _defaultIndex, allowManualEntry, releases, currentVersion = null) {
20112
+ if (isNonInteractive()) {
20113
+ const latestStable2 = releases.find((r2) => r2.isLatestStable && !r2.prerelease);
20114
+ if (latestStable2) {
20115
+ logger.info(`Non-interactive mode: selecting latest stable version ${latestStable2.tag_name}`);
20116
+ return latestStable2.tag_name;
20117
+ }
20118
+ if (releases.length > 0) {
20119
+ logger.info(`Non-interactive mode: selecting version ${releases[0].tag_name}`);
20120
+ return releases[0].tag_name;
20121
+ }
20122
+ logger.warning("Non-interactive mode: no versions available");
20123
+ return null;
20124
+ }
19996
20125
  const clackChoices = [];
19997
20126
  const latestStable = releases.find((r2) => r2.isLatestStable && !r2.prerelease);
19998
20127
  if (latestStable) {
@@ -20060,6 +20189,10 @@ async function handleSelectionError(error, kit, allowManualEntry, retryCallback)
20060
20189
  } else {
20061
20190
  le(VersionDisplayFormatter.formatError(error.message || "Unknown error occurred", "Please try again or contact support"), import_picocolors10.default.red("Error"));
20062
20191
  }
20192
+ if (isNonInteractive()) {
20193
+ logger.warning("Non-interactive mode: version selection failed, cannot retry");
20194
+ return null;
20195
+ }
20063
20196
  if (allowManualEntry) {
20064
20197
  const retry2 = await se({
20065
20198
  message: "Would you like to try entering a version manually?"
@@ -27629,10 +27762,10 @@ import path6 from "node:path";
27629
27762
  // node_modules/tar/dist/esm/get-write-flag.js
27630
27763
  import fs6 from "fs";
27631
27764
  var platform6 = process.env.__FAKE_PLATFORM__ || process.platform;
27632
- var isWindows5 = platform6 === "win32";
27765
+ var isWindows2 = platform6 === "win32";
27633
27766
  var { O_CREAT, O_TRUNC, O_WRONLY } = fs6.constants;
27634
27767
  var UV_FS_O_FILEMAP = Number(process.env.__FAKE_FS_O_FILENAME__) || fs6.constants.UV_FS_O_FILEMAP || 0;
27635
- var fMapEnabled = isWindows5 && !!UV_FS_O_FILEMAP;
27768
+ var fMapEnabled = isWindows2 && !!UV_FS_O_FILEMAP;
27636
27769
  var fMapLimit = 512 * 1024;
27637
27770
  var fMapFlag = UV_FS_O_FILEMAP | O_TRUNC | O_CREAT | O_WRONLY;
27638
27771
  var getWriteFlag = !fMapEnabled ? () => "w" : (size) => size < fMapLimit ? fMapFlag : "w";
@@ -27921,7 +28054,7 @@ var normalizeUnicode = (s) => {
27921
28054
 
27922
28055
  // node_modules/tar/dist/esm/path-reservations.js
27923
28056
  var platform7 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
27924
- var isWindows6 = platform7 === "win32";
28057
+ var isWindows3 = platform7 === "win32";
27925
28058
  var getDirs = (path6) => {
27926
28059
  const dirs = path6.split("/").slice(0, -1).reduce((set, path7) => {
27927
28060
  const s = set[set.length - 1];
@@ -27939,7 +28072,7 @@ class PathReservations {
27939
28072
  #reservations = new Map;
27940
28073
  #running = new Set;
27941
28074
  reserve(paths, fn) {
27942
- paths = isWindows6 ? ["win32 parallelization disabled"] : paths.map((p) => {
28075
+ paths = isWindows3 ? ["win32 parallelization disabled"] : paths.map((p) => {
27943
28076
  return stripTrailingSlashes(join25(normalizeUnicode(p))).toLowerCase();
27944
28077
  });
27945
28078
  const dirs = new Set(paths.map((path6) => getDirs(path6)).reduce((a3, b3) => a3.concat(b3)));
@@ -28068,10 +28201,10 @@ var UID = Symbol("uid");
28068
28201
  var GID = Symbol("gid");
28069
28202
  var CHECKED_CWD = Symbol("checkedCwd");
28070
28203
  var platform8 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
28071
- var isWindows7 = platform8 === "win32";
28204
+ var isWindows4 = platform8 === "win32";
28072
28205
  var DEFAULT_MAX_DEPTH = 1024;
28073
28206
  var unlinkFile = (path7, cb) => {
28074
- if (!isWindows7) {
28207
+ if (!isWindows4) {
28075
28208
  return fs9.unlink(path7, cb);
28076
28209
  }
28077
28210
  const name2 = path7 + ".DELETE." + randomBytes(16).toString("hex");
@@ -28083,7 +28216,7 @@ var unlinkFile = (path7, cb) => {
28083
28216
  });
28084
28217
  };
28085
28218
  var unlinkFileSync = (path7) => {
28086
- if (!isWindows7) {
28219
+ if (!isWindows4) {
28087
28220
  return fs9.unlinkSync(path7);
28088
28221
  }
28089
28222
  const name2 = path7 + ".DELETE." + randomBytes(16).toString("hex");
@@ -28153,7 +28286,7 @@ class Unpack extends Parser {
28153
28286
  this.processGid = (this.preserveOwner || this.setOwner) && process.getgid ? process.getgid() : undefined;
28154
28287
  this.maxDepth = typeof opt.maxDepth === "number" ? opt.maxDepth : DEFAULT_MAX_DEPTH;
28155
28288
  this.forceChown = opt.forceChown === true;
28156
- this.win32 = !!opt.win32 || isWindows7;
28289
+ this.win32 = !!opt.win32 || isWindows4;
28157
28290
  this.newer = !!opt.newer;
28158
28291
  this.keep = !!opt.keep;
28159
28292
  this.noMtime = !!opt.noMtime;
@@ -28208,7 +28341,7 @@ class Unpack extends Parser {
28208
28341
  return false;
28209
28342
  }
28210
28343
  if (!this.preservePaths) {
28211
- if (parts.includes("..") || isWindows7 && /^[a-z]:\.\.$/i.test(parts[0] ?? "")) {
28344
+ if (parts.includes("..") || isWindows4 && /^[a-z]:\.\.$/i.test(parts[0] ?? "")) {
28212
28345
  this.warn("TAR_ENTRY_ERROR", `path contains '..'`, {
28213
28346
  entry,
28214
28347
  path: p
@@ -28422,7 +28555,7 @@ class Unpack extends Parser {
28422
28555
  entry.resume();
28423
28556
  }
28424
28557
  [ISREUSABLE](entry, st) {
28425
- return entry.type === "File" && !this.unlink && st.isFile() && st.nlink <= 1 && !isWindows7;
28558
+ return entry.type === "File" && !this.unlink && st.isFile() && st.nlink <= 1 && !isWindows4;
28426
28559
  }
28427
28560
  [CHECKFS](entry) {
28428
28561
  this[PEND]();
@@ -29316,23 +29449,22 @@ Solutions:
29316
29449
  }
29317
29450
  }
29318
29451
 
29319
- // src/commands/init/phases/download-handler.ts
29452
+ // src/domains/installation/download-extractor.ts
29320
29453
  init_logger();
29321
29454
  init_output_manager();
29322
- async function handleDownload(ctx) {
29323
- if (ctx.cancelled || !ctx.release || !ctx.kit)
29324
- return ctx;
29325
- const downloadInfo = GitHubClient.getDownloadableAsset(ctx.release);
29455
+ async function downloadAndExtract(options) {
29456
+ const { release, kit, exclude } = options;
29457
+ const downloadInfo = GitHubClient.getDownloadableAsset(release);
29326
29458
  logger.verbose("Release info", {
29327
- tag: ctx.release.tag_name,
29328
- prerelease: ctx.release.prerelease,
29459
+ tag: release.tag_name,
29460
+ prerelease: release.prerelease,
29329
29461
  downloadType: downloadInfo.type,
29330
29462
  assetSize: downloadInfo.size
29331
29463
  });
29332
29464
  output.section("Downloading");
29333
29465
  const downloadManager = new DownloadManager;
29334
- if (ctx.options.exclude && ctx.options.exclude.length > 0) {
29335
- downloadManager.setExcludePatterns(ctx.options.exclude);
29466
+ if (exclude && exclude.length > 0) {
29467
+ downloadManager.setExcludePatterns(exclude);
29336
29468
  }
29337
29469
  const tempDir = await downloadManager.createTempDir();
29338
29470
  const { token } = await AuthManager.getToken();
@@ -29350,8 +29482,8 @@ async function handleDownload(ctx) {
29350
29482
  logger.warning("Asset download failed, falling back to GitHub tarball...");
29351
29483
  const tarballInfo = {
29352
29484
  type: "github-tarball",
29353
- url: ctx.release.tarball_url,
29354
- name: `${ctx.kit.repo}-${ctx.release.tag_name}.tar.gz`,
29485
+ url: release.tarball_url,
29486
+ name: `${kit.repo}-${release.tag_name}.tar.gz`,
29355
29487
  size: 0
29356
29488
  };
29357
29489
  archivePath = await downloadManager.downloadFile({
@@ -29370,14 +29502,30 @@ async function handleDownload(ctx) {
29370
29502
  await downloadManager.extractArchive(archivePath, extractDir);
29371
29503
  await downloadManager.validateExtraction(extractDir);
29372
29504
  return {
29373
- ...ctx,
29374
29505
  tempDir,
29375
29506
  archivePath,
29376
29507
  extractDir
29377
29508
  };
29378
29509
  }
29510
+
29511
+ // src/commands/init/phases/download-handler.ts
29512
+ async function handleDownload(ctx) {
29513
+ if (ctx.cancelled || !ctx.release || !ctx.kit)
29514
+ return ctx;
29515
+ const result = await downloadAndExtract({
29516
+ release: ctx.release,
29517
+ kit: ctx.kit,
29518
+ exclude: ctx.options.exclude
29519
+ });
29520
+ return {
29521
+ ...ctx,
29522
+ tempDir: result.tempDir,
29523
+ archivePath: result.archivePath,
29524
+ extractDir: result.extractDir
29525
+ };
29526
+ }
29379
29527
  // src/commands/init/phases/merge-handler.ts
29380
- import { join as join42 } from "node:path";
29528
+ import { join as join43 } from "node:path";
29381
29529
 
29382
29530
  // src/domains/installation/file-merger.ts
29383
29531
  init_logger();
@@ -29561,7 +29709,7 @@ class OwnershipChecker {
29561
29709
  });
29562
29710
  stream.on("error", (err) => {
29563
29711
  stream.destroy();
29564
- reject(new Error(`Failed to calculate checksum for "${filePath}": ${err.message}`));
29712
+ reject(new Error(operationError("Checksum calculation", filePath, err.message)));
29565
29713
  });
29566
29714
  });
29567
29715
  }
@@ -31383,6 +31531,7 @@ class SettingsMerger {
31383
31531
  }
31384
31532
 
31385
31533
  // src/domains/installation/merger/settings-processor.ts
31534
+ init_environment();
31386
31535
  init_logger();
31387
31536
  var import_fs_extra6 = __toESM(require_lib(), 1);
31388
31537
 
@@ -31398,16 +31547,15 @@ class SettingsProcessor {
31398
31547
  async processSettingsJson(sourceFile, destFile) {
31399
31548
  try {
31400
31549
  const sourceContent = await import_fs_extra6.readFile(sourceFile, "utf-8");
31401
- const isWindows8 = process.platform === "win32";
31402
31550
  let transformedSource = sourceContent;
31403
31551
  if (this.isGlobal) {
31404
- const homeVar = isWindows8 ? '"%USERPROFILE%"' : '"$HOME"';
31552
+ const homeVar = isWindows() ? '"%USERPROFILE%"' : '"$HOME"';
31405
31553
  transformedSource = this.transformClaudePaths(sourceContent, homeVar);
31406
31554
  if (transformedSource !== sourceContent) {
31407
31555
  logger.debug(`Transformed .claude/ paths to ${homeVar}/.claude/ in settings.json for global installation`);
31408
31556
  }
31409
31557
  } else {
31410
- const projectDirVar = isWindows8 ? '"%CLAUDE_PROJECT_DIR%"' : '"$CLAUDE_PROJECT_DIR"';
31558
+ const projectDirVar = isWindows() ? '"%CLAUDE_PROJECT_DIR%"' : '"$CLAUDE_PROJECT_DIR"';
31411
31559
  transformedSource = this.transformClaudePaths(sourceContent, projectDirVar);
31412
31560
  if (transformedSource !== sourceContent) {
31413
31561
  logger.debug(`Transformed .claude/ paths to ${projectDirVar}/.claude/ in settings.json for local installation`);
@@ -31657,7 +31805,7 @@ class FileMerger {
31657
31805
 
31658
31806
  // src/domains/migration/legacy-migration.ts
31659
31807
  import { readdir as readdir9, stat as stat6 } from "node:fs/promises";
31660
- import { join as join37, relative as relative6 } from "node:path";
31808
+ import { join as join38, relative as relative6 } from "node:path";
31661
31809
 
31662
31810
  // src/services/file-operations/manifest/manifest-reader.ts
31663
31811
  import { join as join34 } from "node:path";
@@ -31767,6 +31915,43 @@ async function getUninstallManifest(claudeDir, kit) {
31767
31915
  };
31768
31916
  }
31769
31917
  // src/services/file-operations/manifest/manifest-tracker.ts
31918
+ import { join as join37 } from "node:path";
31919
+
31920
+ // src/domains/migration/release-manifest.ts
31921
+ init_logger();
31922
+ init_zod();
31923
+ var import_fs_extra9 = __toESM(require_lib(), 1);
31924
+ import { join as join35 } from "node:path";
31925
+ var ReleaseManifestFileSchema = exports_external.object({
31926
+ path: exports_external.string(),
31927
+ checksum: exports_external.string().regex(/^[a-f0-9]{64}$/),
31928
+ size: exports_external.number()
31929
+ });
31930
+ var ReleaseManifestSchema = exports_external.object({
31931
+ version: exports_external.string(),
31932
+ generatedAt: exports_external.string(),
31933
+ files: exports_external.array(ReleaseManifestFileSchema)
31934
+ });
31935
+
31936
+ class ReleaseManifestLoader {
31937
+ static async load(extractDir) {
31938
+ const manifestPath = join35(extractDir, "release-manifest.json");
31939
+ try {
31940
+ const content = await import_fs_extra9.readFile(manifestPath, "utf-8");
31941
+ const parsed = JSON.parse(content);
31942
+ return ReleaseManifestSchema.parse(parsed);
31943
+ } catch (error) {
31944
+ logger.debug(`Release manifest not found or invalid: ${error}`);
31945
+ return null;
31946
+ }
31947
+ }
31948
+ static findFile(manifest, relativePath) {
31949
+ return manifest.files.find((f3) => f3.path === relativePath);
31950
+ }
31951
+ }
31952
+
31953
+ // src/services/file-operations/manifest/manifest-tracker.ts
31954
+ init_environment();
31770
31955
  init_logger();
31771
31956
 
31772
31957
  // node_modules/yocto-queue/index.js
@@ -31909,111 +32094,31 @@ function validateConcurrency(concurrency) {
31909
32094
  }
31910
32095
  }
31911
32096
 
31912
- // src/services/file-operations/manifest/manifest-tracker.ts
31913
- class ManifestTracker {
31914
- installedFiles = new Set;
31915
- userConfigFiles = new Set;
31916
- trackedFiles = new Map;
31917
- addInstalledFile(relativePath) {
31918
- const normalized = relativePath.replace(/\\/g, "/");
31919
- this.installedFiles.add(normalized);
31920
- }
31921
- addInstalledFiles(relativePaths) {
31922
- for (const path9 of relativePaths) {
31923
- this.addInstalledFile(path9);
31924
- }
31925
- }
31926
- addUserConfigFile(relativePath) {
31927
- const normalized = relativePath.replace(/\\/g, "/");
31928
- this.userConfigFiles.add(normalized);
31929
- }
31930
- getInstalledFiles() {
31931
- return Array.from(this.installedFiles).sort();
31932
- }
31933
- getUserConfigFiles() {
31934
- return Array.from(this.userConfigFiles).sort();
31935
- }
31936
- async addTrackedFile(filePath, relativePath, ownership, installedVersion) {
31937
- const checksum = await OwnershipChecker.calculateChecksum(filePath);
31938
- const normalized = relativePath.replace(/\\/g, "/");
31939
- this.trackedFiles.set(normalized, {
31940
- path: normalized,
31941
- checksum,
31942
- ownership,
31943
- installedVersion
32097
+ // src/services/file-operations/manifest/manifest-updater.ts
32098
+ import { join as join36 } from "node:path";
32099
+ init_logger();
32100
+ init_types2();
32101
+ var import_fs_extra10 = __toESM(require_lib(), 1);
32102
+ var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
32103
+ async function writeManifest(claudeDir, kitName, version, scope, kitType, trackedFiles, userConfigFiles) {
32104
+ const metadataPath = join36(claudeDir, "metadata.json");
32105
+ const kit = kitType || (/\bmarketing\b/i.test(kitName) ? "marketing" : "engineer");
32106
+ await import_fs_extra10.ensureFile(metadataPath);
32107
+ let release = null;
32108
+ try {
32109
+ release = await import_proper_lockfile.lock(metadataPath, {
32110
+ retries: { retries: 5, minTimeout: 100, maxTimeout: 1000 },
32111
+ stale: 60000
31944
32112
  });
31945
- this.installedFiles.add(normalized);
31946
- }
31947
- async addTrackedFilesBatch(files, options = {}) {
31948
- const { concurrency = 20, onProgress } = options;
31949
- const limit = pLimit(concurrency);
31950
- const total = files.length;
31951
- const tasks = files.map((file) => limit(async () => {
32113
+ logger.debug(`Acquired lock on ${metadataPath}`);
32114
+ const migrationResult = await migrateToMultiKit(claudeDir);
32115
+ if (!migrationResult.success) {
32116
+ logger.warning(`Metadata migration warning: ${migrationResult.error}`);
32117
+ }
32118
+ let existingMetadata = { kits: {} };
32119
+ if (await import_fs_extra10.pathExists(metadataPath)) {
31952
32120
  try {
31953
- const checksum = await OwnershipChecker.calculateChecksum(file.filePath);
31954
- const normalized = file.relativePath.replace(/\\/g, "/");
31955
- this.trackedFiles.set(normalized, {
31956
- path: normalized,
31957
- checksum,
31958
- ownership: file.ownership,
31959
- installedVersion: file.installedVersion
31960
- });
31961
- this.installedFiles.add(normalized);
31962
- return true;
31963
- } catch (error) {
31964
- logger.debug(`Failed to track file ${file.relativePath}: ${error}`);
31965
- return false;
31966
- }
31967
- }));
31968
- const progressInterval = Math.max(1, Math.floor(total / 20));
31969
- let reportedProgress = 0;
31970
- const results = await Promise.all(tasks.map(async (task, index) => {
31971
- const result = await task;
31972
- const completed = index + 1;
31973
- if (completed % progressInterval === 0 || completed === total) {
31974
- if (completed > reportedProgress) {
31975
- reportedProgress = completed;
31976
- onProgress?.(completed, total);
31977
- }
31978
- }
31979
- return result;
31980
- }));
31981
- const success = results.filter(Boolean).length;
31982
- const failed = total - success;
31983
- if (failed > 0) {
31984
- logger.warning(`Failed to track ${failed} of ${total} files (check debug logs for details)`);
31985
- }
31986
- return { success, failed, total };
31987
- }
31988
- getTrackedFiles() {
31989
- return Array.from(this.trackedFiles.values()).sort((a3, b3) => a3.path.localeCompare(b3.path));
31990
- }
31991
- }
31992
- // src/services/file-operations/manifest/manifest-updater.ts
31993
- import { join as join35 } from "node:path";
31994
- init_logger();
31995
- init_types2();
31996
- var import_fs_extra9 = __toESM(require_lib(), 1);
31997
- var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
31998
- async function writeManifest(claudeDir, kitName, version, scope, kitType, trackedFiles, userConfigFiles) {
31999
- const metadataPath = join35(claudeDir, "metadata.json");
32000
- const kit = kitType || (/\bmarketing\b/i.test(kitName) ? "marketing" : "engineer");
32001
- await import_fs_extra9.ensureFile(metadataPath);
32002
- let release = null;
32003
- try {
32004
- release = await import_proper_lockfile.lock(metadataPath, {
32005
- retries: { retries: 5, minTimeout: 100, maxTimeout: 1000 },
32006
- stale: 60000
32007
- });
32008
- logger.debug(`Acquired lock on ${metadataPath}`);
32009
- const migrationResult = await migrateToMultiKit(claudeDir);
32010
- if (!migrationResult.success) {
32011
- logger.warning(`Metadata migration warning: ${migrationResult.error}`);
32012
- }
32013
- let existingMetadata = { kits: {} };
32014
- if (await import_fs_extra9.pathExists(metadataPath)) {
32015
- try {
32016
- const content = await import_fs_extra9.readFile(metadataPath, "utf-8");
32121
+ const content = await import_fs_extra10.readFile(metadataPath, "utf-8");
32017
32122
  const parsed = JSON.parse(content);
32018
32123
  if (parsed && typeof parsed === "object" && Object.keys(parsed).length > 0) {
32019
32124
  existingMetadata = parsed;
@@ -32040,7 +32145,7 @@ async function writeManifest(claudeDir, kitName, version, scope, kitType, tracke
32040
32145
  userConfigFiles: [...USER_CONFIG_PATTERNS, ...userConfigFiles]
32041
32146
  };
32042
32147
  const validated = MetadataSchema.parse(metadata);
32043
- await import_fs_extra9.writeFile(metadataPath, JSON.stringify(validated, null, 2), "utf-8");
32148
+ await import_fs_extra10.writeFile(metadataPath, JSON.stringify(validated, null, 2), "utf-8");
32044
32149
  logger.debug(`Wrote manifest for kit "${kit}" with ${trackedFiles.length} tracked files`);
32045
32150
  } finally {
32046
32151
  if (release) {
@@ -32050,8 +32155,8 @@ async function writeManifest(claudeDir, kitName, version, scope, kitType, tracke
32050
32155
  }
32051
32156
  }
32052
32157
  async function removeKitFromManifest(claudeDir, kit) {
32053
- const metadataPath = join35(claudeDir, "metadata.json");
32054
- if (!await import_fs_extra9.pathExists(metadataPath))
32158
+ const metadataPath = join36(claudeDir, "metadata.json");
32159
+ if (!await import_fs_extra10.pathExists(metadataPath))
32055
32160
  return false;
32056
32161
  let release = null;
32057
32162
  try {
@@ -32072,7 +32177,7 @@ async function removeKitFromManifest(claudeDir, kit) {
32072
32177
  ...metadata,
32073
32178
  kits: remainingKits
32074
32179
  };
32075
- await import_fs_extra9.writeFile(metadataPath, JSON.stringify(updated, null, 2), "utf-8");
32180
+ await import_fs_extra10.writeFile(metadataPath, JSON.stringify(updated, null, 2), "utf-8");
32076
32181
  logger.debug(`Removed kit "${kit}" from metadata, ${Object.keys(remainingKits).length} kit(s) remaining`);
32077
32182
  return true;
32078
32183
  } finally {
@@ -32082,6 +32187,126 @@ async function removeKitFromManifest(claudeDir, kit) {
32082
32187
  }
32083
32188
  }
32084
32189
  }
32190
+
32191
+ // src/services/file-operations/manifest/manifest-tracker.ts
32192
+ class ManifestTracker {
32193
+ installedFiles = new Set;
32194
+ userConfigFiles = new Set;
32195
+ trackedFiles = new Map;
32196
+ addInstalledFile(relativePath) {
32197
+ const normalized = relativePath.replace(/\\/g, "/");
32198
+ this.installedFiles.add(normalized);
32199
+ }
32200
+ addInstalledFiles(relativePaths) {
32201
+ for (const path9 of relativePaths) {
32202
+ this.addInstalledFile(path9);
32203
+ }
32204
+ }
32205
+ addUserConfigFile(relativePath) {
32206
+ const normalized = relativePath.replace(/\\/g, "/");
32207
+ this.userConfigFiles.add(normalized);
32208
+ }
32209
+ getInstalledFiles() {
32210
+ return Array.from(this.installedFiles).sort();
32211
+ }
32212
+ getUserConfigFiles() {
32213
+ return Array.from(this.userConfigFiles).sort();
32214
+ }
32215
+ async addTrackedFile(filePath, relativePath, ownership, installedVersion) {
32216
+ const checksum = await OwnershipChecker.calculateChecksum(filePath);
32217
+ const normalized = relativePath.replace(/\\/g, "/");
32218
+ this.trackedFiles.set(normalized, {
32219
+ path: normalized,
32220
+ checksum,
32221
+ ownership,
32222
+ installedVersion
32223
+ });
32224
+ this.installedFiles.add(normalized);
32225
+ }
32226
+ async addTrackedFilesBatch(files, options = {}) {
32227
+ const { concurrency = 20, onProgress } = options;
32228
+ const limit = pLimit(concurrency);
32229
+ const total = files.length;
32230
+ const tasks = files.map((file) => limit(async () => {
32231
+ try {
32232
+ const checksum = await OwnershipChecker.calculateChecksum(file.filePath);
32233
+ const normalized = file.relativePath.replace(/\\/g, "/");
32234
+ this.trackedFiles.set(normalized, {
32235
+ path: normalized,
32236
+ checksum,
32237
+ ownership: file.ownership,
32238
+ installedVersion: file.installedVersion
32239
+ });
32240
+ this.installedFiles.add(normalized);
32241
+ return true;
32242
+ } catch (error) {
32243
+ logger.debug(`Failed to track file ${file.relativePath}: ${error}`);
32244
+ return false;
32245
+ }
32246
+ }));
32247
+ const progressInterval = Math.max(1, Math.floor(total / 20));
32248
+ let reportedProgress = 0;
32249
+ const results = await Promise.all(tasks.map(async (task, index) => {
32250
+ const result = await task;
32251
+ const completed = index + 1;
32252
+ if (completed % progressInterval === 0 || completed === total) {
32253
+ if (completed > reportedProgress) {
32254
+ reportedProgress = completed;
32255
+ onProgress?.(completed, total);
32256
+ }
32257
+ }
32258
+ return result;
32259
+ }));
32260
+ const success = results.filter(Boolean).length;
32261
+ const failed = total - success;
32262
+ if (failed > 0) {
32263
+ logger.warning(`Failed to track ${failed} of ${total} files (check debug logs for details)`);
32264
+ }
32265
+ return { success, failed, total };
32266
+ }
32267
+ getTrackedFiles() {
32268
+ return Array.from(this.trackedFiles.values()).sort((a3, b3) => a3.path.localeCompare(b3.path));
32269
+ }
32270
+ }
32271
+ function buildFileTrackingList(options) {
32272
+ const {
32273
+ installedFiles,
32274
+ claudeDir,
32275
+ releaseManifest,
32276
+ installedVersion,
32277
+ isGlobal = false
32278
+ } = options;
32279
+ const filesToTrack = [];
32280
+ for (const installedPath of installedFiles) {
32281
+ if (!isGlobal && !installedPath.startsWith(".claude/"))
32282
+ continue;
32283
+ const relativePath = isGlobal ? installedPath : installedPath.replace(/^\.claude\//, "");
32284
+ const filePath = join37(claudeDir, relativePath);
32285
+ const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
32286
+ const ownership = manifestEntry ? "ck" : "user";
32287
+ filesToTrack.push({
32288
+ filePath,
32289
+ relativePath,
32290
+ ownership,
32291
+ installedVersion
32292
+ });
32293
+ }
32294
+ return filesToTrack;
32295
+ }
32296
+ async function trackFilesWithProgress(filesToTrack, manifestOptions) {
32297
+ const tracker = new ManifestTracker;
32298
+ const trackingSpinner = createSpinner(`Tracking ${filesToTrack.length} installed files...`);
32299
+ trackingSpinner.start();
32300
+ const trackResult = await tracker.addTrackedFilesBatch(filesToTrack, {
32301
+ concurrency: getOptimalConcurrency(),
32302
+ onProgress: (processed, total) => {
32303
+ trackingSpinner.text = `Tracking files... (${processed}/${total})`;
32304
+ }
32305
+ });
32306
+ trackingSpinner.succeed(`Tracked ${trackResult.success} files`);
32307
+ await writeManifest(manifestOptions.claudeDir, manifestOptions.kitName, manifestOptions.releaseTag, manifestOptions.mode, manifestOptions.kitType, tracker.getTrackedFiles(), tracker.getUserConfigFiles());
32308
+ return trackResult;
32309
+ }
32085
32310
  // src/services/file-operations/manifest-writer.ts
32086
32311
  class ManifestWriter {
32087
32312
  tracker = new ManifestTracker;
@@ -32129,41 +32354,6 @@ class ManifestWriter {
32129
32354
  // src/domains/migration/legacy-migration.ts
32130
32355
  init_logger();
32131
32356
  var import_fs_extra11 = __toESM(require_lib(), 1);
32132
-
32133
- // src/domains/migration/release-manifest.ts
32134
- init_logger();
32135
- init_zod();
32136
- var import_fs_extra10 = __toESM(require_lib(), 1);
32137
- import { join as join36 } from "node:path";
32138
- var ReleaseManifestFileSchema = exports_external.object({
32139
- path: exports_external.string(),
32140
- checksum: exports_external.string().regex(/^[a-f0-9]{64}$/),
32141
- size: exports_external.number()
32142
- });
32143
- var ReleaseManifestSchema = exports_external.object({
32144
- version: exports_external.string(),
32145
- generatedAt: exports_external.string(),
32146
- files: exports_external.array(ReleaseManifestFileSchema)
32147
- });
32148
-
32149
- class ReleaseManifestLoader {
32150
- static async load(extractDir) {
32151
- const manifestPath = join36(extractDir, "release-manifest.json");
32152
- try {
32153
- const content = await import_fs_extra10.readFile(manifestPath, "utf-8");
32154
- const parsed = JSON.parse(content);
32155
- return ReleaseManifestSchema.parse(parsed);
32156
- } catch (error) {
32157
- logger.debug(`Release manifest not found or invalid: ${error}`);
32158
- return null;
32159
- }
32160
- }
32161
- static findFile(manifest, relativePath) {
32162
- return manifest.files.find((f3) => f3.path === relativePath);
32163
- }
32164
- }
32165
-
32166
- // src/domains/migration/legacy-migration.ts
32167
32357
  class LegacyMigration {
32168
32358
  static async detectLegacy(claudeDir) {
32169
32359
  const metadata = await ManifestWriter.readManifest(claudeDir);
@@ -32194,7 +32384,7 @@ class LegacyMigration {
32194
32384
  for (const entry of entries) {
32195
32385
  if (entry === "metadata.json")
32196
32386
  continue;
32197
- const fullPath = join37(dir, entry);
32387
+ const fullPath = join38(dir, entry);
32198
32388
  let stats;
32199
32389
  try {
32200
32390
  stats = await stat6(fullPath);
@@ -32296,7 +32486,7 @@ User-created files (sample):`);
32296
32486
  ];
32297
32487
  if (filesToChecksum.length > 0) {
32298
32488
  const checksumResults = await Promise.all(filesToChecksum.map(async ({ relativePath, ownership }) => {
32299
- const fullPath = join37(claudeDir, relativePath);
32489
+ const fullPath = join38(claudeDir, relativePath);
32300
32490
  const checksum = await OwnershipChecker.calculateChecksum(fullPath);
32301
32491
  return { relativePath, checksum, ownership };
32302
32492
  }));
@@ -32317,7 +32507,7 @@ User-created files (sample):`);
32317
32507
  installedAt: new Date().toISOString(),
32318
32508
  files: trackedFiles
32319
32509
  };
32320
- const metadataPath = join37(claudeDir, "metadata.json");
32510
+ const metadataPath = join38(claudeDir, "metadata.json");
32321
32511
  await import_fs_extra11.writeFile(metadataPath, JSON.stringify(updatedMetadata, null, 2));
32322
32512
  logger.success(`Migration complete: tracked ${trackedFiles.length} files`);
32323
32513
  return true;
@@ -32325,8 +32515,8 @@ User-created files (sample):`);
32325
32515
  }
32326
32516
 
32327
32517
  // src/services/file-operations/file-scanner.ts
32518
+ import { join as join39, relative as relative7, resolve as resolve6 } from "node:path";
32328
32519
  init_logger();
32329
- import { join as join38, relative as relative7, resolve as resolve6 } from "node:path";
32330
32520
  var import_fs_extra12 = __toESM(require_lib(), 1);
32331
32521
 
32332
32522
  class FileScanner2 {
@@ -32343,7 +32533,7 @@ class FileScanner2 {
32343
32533
  logger.debug(`Skipping directory: ${entry}`);
32344
32534
  continue;
32345
32535
  }
32346
- const fullPath = join38(dirPath, entry);
32536
+ const fullPath = join39(dirPath, entry);
32347
32537
  if (!FileScanner2.isSafePath(basePath, fullPath)) {
32348
32538
  logger.warning(`Skipping potentially unsafe path: ${entry}`);
32349
32539
  continue;
@@ -32371,15 +32561,15 @@ class FileScanner2 {
32371
32561
  }
32372
32562
  }
32373
32563
  } catch (error) {
32374
- const errorMessage = error instanceof Error ? `Failed to scan directory: ${dirPath} - ${error.message}` : `Failed to scan directory: ${dirPath}`;
32564
+ const errorMessage = error instanceof Error ? operationError("Directory scan", dirPath, error.message) : operationError("Directory scan", dirPath, "unknown error");
32375
32565
  logger.error(errorMessage);
32376
32566
  throw error;
32377
32567
  }
32378
32568
  return files;
32379
32569
  }
32380
32570
  static async findCustomFiles(destDir, sourceDir, subPath) {
32381
- const destSubDir = join38(destDir, subPath);
32382
- const sourceSubDir = join38(sourceDir, subPath);
32571
+ const destSubDir = join39(destDir, subPath);
32572
+ const sourceSubDir = join39(sourceDir, subPath);
32383
32573
  logger.debug(`findCustomFiles - destDir: ${destDir}`);
32384
32574
  logger.debug(`findCustomFiles - sourceDir: ${sourceDir}`);
32385
32575
  logger.debug(`findCustomFiles - subPath: "${subPath}"`);
@@ -32420,7 +32610,7 @@ class FileScanner2 {
32420
32610
  init_logger();
32421
32611
  var import_fs_extra13 = __toESM(require_lib(), 1);
32422
32612
  import { lstat as lstat3, mkdir as mkdir13, readdir as readdir11, stat as stat7 } from "node:fs/promises";
32423
- import { join as join39 } from "node:path";
32613
+ import { join as join40 } from "node:path";
32424
32614
 
32425
32615
  // src/services/transformers/commands-prefix/prefix-utils.ts
32426
32616
  init_logger();
@@ -32465,14 +32655,14 @@ function shouldApplyPrefix(options) {
32465
32655
  // src/services/transformers/commands-prefix/prefix-applier.ts
32466
32656
  async function applyPrefix(extractDir) {
32467
32657
  validatePath(extractDir, "extractDir");
32468
- const commandsDir = join39(extractDir, ".claude", "commands");
32658
+ const commandsDir = join40(extractDir, ".claude", "commands");
32469
32659
  if (!await import_fs_extra13.pathExists(commandsDir)) {
32470
32660
  logger.verbose("No commands directory found, skipping prefix application");
32471
32661
  return;
32472
32662
  }
32473
32663
  logger.info("Applying /ck: prefix to slash commands...");
32474
- const backupDir = join39(extractDir, ".commands-backup");
32475
- const tempDir = join39(extractDir, ".commands-prefix-temp");
32664
+ const backupDir = join40(extractDir, ".commands-backup");
32665
+ const tempDir = join40(extractDir, ".commands-prefix-temp");
32476
32666
  try {
32477
32667
  const entries = await readdir11(commandsDir);
32478
32668
  if (entries.length === 0) {
@@ -32480,7 +32670,7 @@ async function applyPrefix(extractDir) {
32480
32670
  return;
32481
32671
  }
32482
32672
  if (entries.length === 1 && entries[0] === "ck") {
32483
- const ckDir2 = join39(commandsDir, "ck");
32673
+ const ckDir2 = join40(commandsDir, "ck");
32484
32674
  const ckStat = await stat7(ckDir2);
32485
32675
  if (ckStat.isDirectory()) {
32486
32676
  logger.verbose("Commands already have /ck: prefix, skipping");
@@ -32490,17 +32680,17 @@ async function applyPrefix(extractDir) {
32490
32680
  await import_fs_extra13.copy(commandsDir, backupDir);
32491
32681
  logger.verbose("Created backup of commands directory");
32492
32682
  await mkdir13(tempDir, { recursive: true });
32493
- const ckDir = join39(tempDir, "ck");
32683
+ const ckDir = join40(tempDir, "ck");
32494
32684
  await mkdir13(ckDir, { recursive: true });
32495
32685
  let processedCount = 0;
32496
32686
  for (const entry of entries) {
32497
- const sourcePath = join39(commandsDir, entry);
32687
+ const sourcePath = join40(commandsDir, entry);
32498
32688
  const stats = await lstat3(sourcePath);
32499
32689
  if (stats.isSymbolicLink()) {
32500
32690
  logger.warning(`Skipping symlink for security: ${entry}`);
32501
32691
  continue;
32502
32692
  }
32503
- const destPath = join39(ckDir, entry);
32693
+ const destPath = join40(ckDir, entry);
32504
32694
  await import_fs_extra13.copy(sourcePath, destPath, {
32505
32695
  overwrite: false,
32506
32696
  errorOnExist: true
@@ -32545,20 +32735,20 @@ async function applyPrefix(extractDir) {
32545
32735
 
32546
32736
  // src/services/transformers/commands-prefix/prefix-cleaner.ts
32547
32737
  import { lstat as lstat5, readdir as readdir13 } from "node:fs/promises";
32548
- import { join as join41 } from "node:path";
32738
+ import { join as join42 } from "node:path";
32549
32739
  init_logger();
32550
32740
  var import_fs_extra15 = __toESM(require_lib(), 1);
32551
32741
 
32552
32742
  // src/services/transformers/commands-prefix/file-processor.ts
32553
32743
  import { lstat as lstat4, readdir as readdir12 } from "node:fs/promises";
32554
- import { join as join40 } from "node:path";
32744
+ import { join as join41 } from "node:path";
32555
32745
  init_logger();
32556
32746
  var import_fs_extra14 = __toESM(require_lib(), 1);
32557
32747
  async function scanDirectoryFiles(dir) {
32558
32748
  const files = [];
32559
32749
  const entries = await readdir12(dir);
32560
32750
  for (const entry of entries) {
32561
- const fullPath = join40(dir, entry);
32751
+ const fullPath = join41(dir, entry);
32562
32752
  const stats = await lstat4(fullPath);
32563
32753
  if (stats.isSymbolicLink()) {
32564
32754
  continue;
@@ -32666,8 +32856,8 @@ function logCleanupSummary(deletedCount, preservedCount, dryRun, results) {
32666
32856
  async function cleanupCommandsDirectory(targetDir, isGlobal, options = {}) {
32667
32857
  const { dryRun = false } = options;
32668
32858
  validatePath(targetDir, "targetDir");
32669
- const claudeDir = isGlobal ? targetDir : join41(targetDir, ".claude");
32670
- const commandsDir = join41(claudeDir, "commands");
32859
+ const claudeDir = isGlobal ? targetDir : join42(targetDir, ".claude");
32860
+ const commandsDir = join42(claudeDir, "commands");
32671
32861
  const accumulator = {
32672
32862
  results: [],
32673
32863
  deletedCount: 0,
@@ -32701,7 +32891,7 @@ async function cleanupCommandsDirectory(targetDir, isGlobal, options = {}) {
32701
32891
  return result;
32702
32892
  }
32703
32893
  for (const entry of entries) {
32704
- const entryPath = join41(commandsDir, entry);
32894
+ const entryPath = join42(commandsDir, entry);
32705
32895
  const stats = await lstat5(entryPath);
32706
32896
  if (stats.isSymbolicLink()) {
32707
32897
  addSymlinkSkip(entry, accumulator);
@@ -32743,7 +32933,6 @@ class CommandsPrefix {
32743
32933
  }
32744
32934
 
32745
32935
  // src/commands/init/phases/merge-handler.ts
32746
- init_environment();
32747
32936
  init_logger();
32748
32937
  init_output_manager();
32749
32938
  var import_fs_extra16 = __toESM(require_lib(), 1);
@@ -32754,7 +32943,7 @@ async function handleMerge(ctx) {
32754
32943
  let customClaudeFiles = [];
32755
32944
  if (!ctx.options.fresh) {
32756
32945
  logger.info("Scanning for custom .claude files...");
32757
- const scanSourceDir = ctx.options.global ? join42(ctx.extractDir, ".claude") : ctx.extractDir;
32946
+ const scanSourceDir = ctx.options.global ? join43(ctx.extractDir, ".claude") : ctx.extractDir;
32758
32947
  const scanTargetSubdir = ctx.options.global ? "" : ".claude";
32759
32948
  customClaudeFiles = await FileScanner2.findCustomFiles(ctx.resolvedDir, scanSourceDir, scanTargetSubdir);
32760
32949
  } else {
@@ -32813,35 +33002,23 @@ async function handleMerge(ctx) {
32813
33002
  return { ...ctx, cancelled: true };
32814
33003
  }
32815
33004
  }
32816
- const sourceDir = ctx.options.global ? join42(ctx.extractDir, ".claude") : ctx.extractDir;
33005
+ const sourceDir = ctx.options.global ? join43(ctx.extractDir, ".claude") : ctx.extractDir;
32817
33006
  await merger.merge(sourceDir, ctx.resolvedDir, false);
32818
- const manifestWriter = new ManifestWriter;
32819
33007
  const installedFiles = merger.getAllInstalledFiles();
32820
- const filesToTrack = [];
32821
- for (const installedPath of installedFiles) {
32822
- if (!ctx.options.global && !installedPath.startsWith(".claude/"))
32823
- continue;
32824
- const relativePath = ctx.options.global ? installedPath : installedPath.replace(/^\.claude\//, "");
32825
- const filePath = join42(ctx.claudeDir, relativePath);
32826
- const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
32827
- const ownership = manifestEntry ? "ck" : "user";
32828
- filesToTrack.push({
32829
- filePath,
32830
- relativePath,
32831
- ownership,
32832
- installedVersion: ctx.release.tag_name
32833
- });
32834
- }
32835
- const trackingSpinner = createSpinner(`Tracking ${filesToTrack.length} installed files...`);
32836
- trackingSpinner.start();
32837
- const trackResult = await manifestWriter.addTrackedFilesBatch(filesToTrack, {
32838
- concurrency: getOptimalConcurrency(),
32839
- onProgress: (processed, total) => {
32840
- trackingSpinner.text = `Tracking files... (${processed}/${total})`;
32841
- }
33008
+ const filesToTrack = buildFileTrackingList({
33009
+ installedFiles,
33010
+ claudeDir: ctx.claudeDir,
33011
+ releaseManifest,
33012
+ installedVersion: ctx.release.tag_name,
33013
+ isGlobal: ctx.options.global
33014
+ });
33015
+ await trackFilesWithProgress(filesToTrack, {
33016
+ claudeDir: ctx.claudeDir,
33017
+ kitName: ctx.kit.name,
33018
+ releaseTag: ctx.release.tag_name,
33019
+ mode: ctx.options.global ? "global" : "local",
33020
+ kitType: ctx.kitType
32842
33021
  });
32843
- trackingSpinner.succeed(`Tracked ${trackResult.success} files`);
32844
- await manifestWriter.writeManifest(ctx.claudeDir, ctx.kit.name, ctx.release.tag_name, ctx.options.global ? "global" : "local", ctx.kitType);
32845
33022
  return {
32846
33023
  ...ctx,
32847
33024
  customClaudeFiles,
@@ -32849,7 +33026,7 @@ async function handleMerge(ctx) {
32849
33026
  };
32850
33027
  }
32851
33028
  // src/commands/init/phases/migration-handler.ts
32852
- import { join as join50 } from "node:path";
33029
+ import { join as join51 } from "node:path";
32853
33030
 
32854
33031
  // src/domains/skills/skills-detector.ts
32855
33032
  init_logger();
@@ -32864,7 +33041,7 @@ init_types2();
32864
33041
  var import_fs_extra17 = __toESM(require_lib(), 1);
32865
33042
  import { createHash as createHash2 } from "node:crypto";
32866
33043
  import { readFile as readFile14, readdir as readdir14, writeFile as writeFile12 } from "node:fs/promises";
32867
- import { join as join43, relative as relative8 } from "node:path";
33044
+ import { join as join44, relative as relative8 } from "node:path";
32868
33045
 
32869
33046
  class SkillsManifestManager {
32870
33047
  static MANIFEST_FILENAME = ".skills-manifest.json";
@@ -32886,12 +33063,12 @@ class SkillsManifestManager {
32886
33063
  return manifest;
32887
33064
  }
32888
33065
  static async writeManifest(skillsDir, manifest) {
32889
- const manifestPath = join43(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
33066
+ const manifestPath = join44(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
32890
33067
  await writeFile12(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
32891
33068
  logger.debug(`Wrote manifest to: ${manifestPath}`);
32892
33069
  }
32893
33070
  static async readManifest(skillsDir) {
32894
- const manifestPath = join43(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
33071
+ const manifestPath = join44(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
32895
33072
  if (!await import_fs_extra17.pathExists(manifestPath)) {
32896
33073
  logger.debug(`No manifest found at: ${manifestPath}`);
32897
33074
  return null;
@@ -32914,7 +33091,7 @@ class SkillsManifestManager {
32914
33091
  return "flat";
32915
33092
  }
32916
33093
  for (const dir of dirs.slice(0, 3)) {
32917
- const dirPath = join43(skillsDir, dir.name);
33094
+ const dirPath = join44(skillsDir, dir.name);
32918
33095
  const subEntries = await readdir14(dirPath, { withFileTypes: true });
32919
33096
  const hasSubdirs = subEntries.some((entry) => entry.isDirectory());
32920
33097
  if (hasSubdirs) {
@@ -32933,7 +33110,7 @@ class SkillsManifestManager {
32933
33110
  const entries = await readdir14(skillsDir, { withFileTypes: true });
32934
33111
  for (const entry of entries) {
32935
33112
  if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) {
32936
- const skillPath = join43(skillsDir, entry.name);
33113
+ const skillPath = join44(skillsDir, entry.name);
32937
33114
  const hash = await SkillsManifestManager.hashDirectory(skillPath);
32938
33115
  skills.push({
32939
33116
  name: entry.name,
@@ -32945,11 +33122,11 @@ class SkillsManifestManager {
32945
33122
  const categories = await readdir14(skillsDir, { withFileTypes: true });
32946
33123
  for (const category of categories) {
32947
33124
  if (category.isDirectory() && category.name !== "node_modules" && !category.name.startsWith(".")) {
32948
- const categoryPath = join43(skillsDir, category.name);
33125
+ const categoryPath = join44(skillsDir, category.name);
32949
33126
  const skillEntries = await readdir14(categoryPath, { withFileTypes: true });
32950
33127
  for (const skillEntry of skillEntries) {
32951
33128
  if (skillEntry.isDirectory() && !skillEntry.name.startsWith(".")) {
32952
- const skillPath = join43(categoryPath, skillEntry.name);
33129
+ const skillPath = join44(categoryPath, skillEntry.name);
32953
33130
  const hash = await SkillsManifestManager.hashDirectory(skillPath);
32954
33131
  skills.push({
32955
33132
  name: skillEntry.name,
@@ -32979,7 +33156,7 @@ class SkillsManifestManager {
32979
33156
  const files = [];
32980
33157
  const entries = await readdir14(dirPath, { withFileTypes: true });
32981
33158
  for (const entry of entries) {
32982
- const fullPath = join43(dirPath, entry.name);
33159
+ const fullPath = join44(dirPath, entry.name);
32983
33160
  if (entry.name.startsWith(".") || entry.name === "node_modules") {
32984
33161
  continue;
32985
33162
  }
@@ -33101,7 +33278,7 @@ function getPathMapping(skillName, oldBasePath, newBasePath) {
33101
33278
  // src/domains/skills/detection/script-detector.ts
33102
33279
  var import_fs_extra18 = __toESM(require_lib(), 1);
33103
33280
  import { readdir as readdir15 } from "node:fs/promises";
33104
- import { join as join44 } from "node:path";
33281
+ import { join as join45 } from "node:path";
33105
33282
  async function scanDirectory(skillsDir) {
33106
33283
  if (!await import_fs_extra18.pathExists(skillsDir)) {
33107
33284
  return ["flat", []];
@@ -33114,12 +33291,12 @@ async function scanDirectory(skillsDir) {
33114
33291
  let totalSkillLikeCount = 0;
33115
33292
  const allSkills = [];
33116
33293
  for (const dir of dirs) {
33117
- const dirPath = join44(skillsDir, dir.name);
33294
+ const dirPath = join45(skillsDir, dir.name);
33118
33295
  const subEntries = await readdir15(dirPath, { withFileTypes: true });
33119
33296
  const subdirs = subEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
33120
33297
  if (subdirs.length > 0) {
33121
33298
  for (const subdir of subdirs.slice(0, 3)) {
33122
- const subdirPath = join44(dirPath, subdir.name);
33299
+ const subdirPath = join45(dirPath, subdir.name);
33123
33300
  const subdirFiles = await readdir15(subdirPath, { withFileTypes: true });
33124
33301
  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"));
33125
33302
  if (hasSkillMarker) {
@@ -33276,19 +33453,25 @@ class SkillsMigrationDetector {
33276
33453
  // src/domains/skills/skills-migrator.ts
33277
33454
  init_logger();
33278
33455
  init_types2();
33279
- import { join as join49 } from "node:path";
33456
+ import { join as join50 } from "node:path";
33280
33457
 
33281
33458
  // src/domains/skills/migrator/migration-executor.ts
33282
33459
  init_logger();
33283
33460
  var import_fs_extra20 = __toESM(require_lib(), 1);
33284
33461
  import { copyFile as copyFile4, mkdir as mkdir14, readdir as readdir16, rm as rm3 } from "node:fs/promises";
33285
- import { join as join45 } from "node:path";
33462
+ import { join as join46 } from "node:path";
33286
33463
 
33287
33464
  // src/domains/skills/skills-migration-prompts.ts
33465
+ init_environment();
33466
+ init_logger();
33288
33467
  init_dist2();
33289
33468
 
33290
33469
  class SkillsMigrationPrompts {
33291
33470
  static async promptMigrationDecision(detection) {
33471
+ if (isNonInteractive()) {
33472
+ logger.info("Non-interactive mode: proceeding with skills migration");
33473
+ return true;
33474
+ }
33292
33475
  const customizedCount = detection.customizations.filter((c2) => c2.isCustomized).length;
33293
33476
  const totalSkills = detection.skillMappings.length;
33294
33477
  let message = `Skills directory structure migration available.
@@ -33334,6 +33517,10 @@ Would you like to migrate now?`;
33334
33517
  if (customizations.length === 0) {
33335
33518
  return "preserve";
33336
33519
  }
33520
+ if (isNonInteractive()) {
33521
+ logger.info("Non-interactive mode: preserving all customizations");
33522
+ return "preserve";
33523
+ }
33337
33524
  const result = await ie({
33338
33525
  message: "How should customized skills be handled?",
33339
33526
  options: [
@@ -33358,6 +33545,10 @@ Would you like to migrate now?`;
33358
33545
  return result;
33359
33546
  }
33360
33547
  static async promptSkillMigration(skillName, customization) {
33548
+ if (isNonInteractive()) {
33549
+ logger.info(`Non-interactive mode: migrating skill ${skillName}`);
33550
+ return true;
33551
+ }
33361
33552
  let message = `Migrate skill: ${skillName}?`;
33362
33553
  if (customization.isCustomized && customization.changes) {
33363
33554
  const changes = customization.changes;
@@ -33403,6 +33594,10 @@ Detected changes:`;
33403
33594
  f2.message("");
33404
33595
  }
33405
33596
  static async promptBackup() {
33597
+ if (isNonInteractive()) {
33598
+ logger.info("Non-interactive mode: creating backup before migration");
33599
+ return true;
33600
+ }
33406
33601
  const result = await se({
33407
33602
  message: "Create backup of current skills directory before migration?",
33408
33603
  initialValue: true
@@ -33428,8 +33623,8 @@ async function copySkillDirectory(sourceDir, destDir) {
33428
33623
  await mkdir14(destDir, { recursive: true });
33429
33624
  const entries = await readdir16(sourceDir, { withFileTypes: true });
33430
33625
  for (const entry of entries) {
33431
- const sourcePath = join45(sourceDir, entry.name);
33432
- const destPath = join45(destDir, entry.name);
33626
+ const sourcePath = join46(sourceDir, entry.name);
33627
+ const destPath = join46(destDir, entry.name);
33433
33628
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
33434
33629
  continue;
33435
33630
  }
@@ -33444,7 +33639,7 @@ async function executeMigration(mappings, customizations, currentSkillsDir, inte
33444
33639
  const migrated = [];
33445
33640
  const preserved = [];
33446
33641
  const errors2 = [];
33447
- const tempDir = join45(currentSkillsDir, "..", ".skills-migration-temp");
33642
+ const tempDir = join46(currentSkillsDir, "..", ".skills-migration-temp");
33448
33643
  await mkdir14(tempDir, { recursive: true });
33449
33644
  try {
33450
33645
  for (const mapping of mappings) {
@@ -33465,9 +33660,9 @@ async function executeMigration(mappings, customizations, currentSkillsDir, inte
33465
33660
  }
33466
33661
  }
33467
33662
  const category = mapping.category;
33468
- const targetPath = category ? join45(tempDir, category, skillName) : join45(tempDir, skillName);
33663
+ const targetPath = category ? join46(tempDir, category, skillName) : join46(tempDir, skillName);
33469
33664
  if (category) {
33470
- await mkdir14(join45(tempDir, category), { recursive: true });
33665
+ await mkdir14(join46(tempDir, category), { recursive: true });
33471
33666
  }
33472
33667
  await copySkillDirectory(currentSkillPath, targetPath);
33473
33668
  migrated.push(skillName);
@@ -33529,7 +33724,7 @@ init_logger();
33529
33724
  init_types2();
33530
33725
  var import_fs_extra21 = __toESM(require_lib(), 1);
33531
33726
  import { copyFile as copyFile5, mkdir as mkdir15, readdir as readdir17, rm as rm4, stat as stat8 } from "node:fs/promises";
33532
- import { basename as basename2, join as join46, normalize as normalize5 } from "node:path";
33727
+ import { basename as basename2, join as join47, normalize as normalize5 } from "node:path";
33533
33728
  function validatePath2(path9, paramName) {
33534
33729
  if (!path9 || typeof path9 !== "string") {
33535
33730
  throw new SkillsMigrationError(`${paramName} must be a non-empty string`);
@@ -33555,7 +33750,7 @@ class SkillsBackupManager {
33555
33750
  const timestamp = Date.now();
33556
33751
  const randomSuffix = Math.random().toString(36).substring(2, 8);
33557
33752
  const backupDirName = `${SkillsBackupManager.BACKUP_PREFIX}${timestamp}-${randomSuffix}`;
33558
- const backupDir = parentDir ? join46(parentDir, backupDirName) : join46(skillsDir, "..", backupDirName);
33753
+ const backupDir = parentDir ? join47(parentDir, backupDirName) : join47(skillsDir, "..", backupDirName);
33559
33754
  logger.info(`Creating backup at: ${backupDir}`);
33560
33755
  try {
33561
33756
  await mkdir15(backupDir, { recursive: true });
@@ -33606,7 +33801,7 @@ class SkillsBackupManager {
33606
33801
  }
33607
33802
  try {
33608
33803
  const entries = await readdir17(parentDir, { withFileTypes: true });
33609
- const backups = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(SkillsBackupManager.BACKUP_PREFIX)).map((entry) => join46(parentDir, entry.name));
33804
+ const backups = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(SkillsBackupManager.BACKUP_PREFIX)).map((entry) => join47(parentDir, entry.name));
33610
33805
  backups.sort().reverse();
33611
33806
  return backups;
33612
33807
  } catch (error) {
@@ -33634,8 +33829,8 @@ class SkillsBackupManager {
33634
33829
  static async copyDirectory(sourceDir, destDir) {
33635
33830
  const entries = await readdir17(sourceDir, { withFileTypes: true });
33636
33831
  for (const entry of entries) {
33637
- const sourcePath = join46(sourceDir, entry.name);
33638
- const destPath = join46(destDir, entry.name);
33832
+ const sourcePath = join47(sourceDir, entry.name);
33833
+ const destPath = join47(destDir, entry.name);
33639
33834
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
33640
33835
  continue;
33641
33836
  }
@@ -33651,7 +33846,7 @@ class SkillsBackupManager {
33651
33846
  let size = 0;
33652
33847
  const entries = await readdir17(dirPath, { withFileTypes: true });
33653
33848
  for (const entry of entries) {
33654
- const fullPath = join46(dirPath, entry.name);
33849
+ const fullPath = join47(dirPath, entry.name);
33655
33850
  if (entry.isSymbolicLink()) {
33656
33851
  continue;
33657
33852
  }
@@ -33686,12 +33881,12 @@ import { relative as relative10 } from "node:path";
33686
33881
  import { createHash as createHash3 } from "node:crypto";
33687
33882
  import { createReadStream as createReadStream2 } from "node:fs";
33688
33883
  import { readFile as readFile15, readdir as readdir18 } from "node:fs/promises";
33689
- import { join as join47, relative as relative9 } from "node:path";
33884
+ import { join as join48, relative as relative9 } from "node:path";
33690
33885
  async function getAllFiles(dirPath) {
33691
33886
  const files = [];
33692
33887
  const entries = await readdir18(dirPath, { withFileTypes: true });
33693
33888
  for (const entry of entries) {
33694
- const fullPath = join47(dirPath, entry.name);
33889
+ const fullPath = join48(dirPath, entry.name);
33695
33890
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
33696
33891
  continue;
33697
33892
  }
@@ -33818,7 +34013,7 @@ async function detectFileChanges(currentSkillPath, baselineSkillPath) {
33818
34013
  init_types2();
33819
34014
  var import_fs_extra23 = __toESM(require_lib(), 1);
33820
34015
  import { readdir as readdir19 } from "node:fs/promises";
33821
- import { join as join48, normalize as normalize6 } from "node:path";
34016
+ import { join as join49, normalize as normalize6 } from "node:path";
33822
34017
  function validatePath3(path9, paramName) {
33823
34018
  if (!path9 || typeof path9 !== "string") {
33824
34019
  throw new SkillsMigrationError(`${paramName} must be a non-empty string`);
@@ -33839,13 +34034,13 @@ async function scanSkillsDirectory(skillsDir) {
33839
34034
  if (dirs.length === 0) {
33840
34035
  return ["flat", []];
33841
34036
  }
33842
- const firstDirPath = join48(skillsDir, dirs[0].name);
34037
+ const firstDirPath = join49(skillsDir, dirs[0].name);
33843
34038
  const subEntries = await readdir19(firstDirPath, { withFileTypes: true });
33844
34039
  const subdirs = subEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
33845
34040
  if (subdirs.length > 0) {
33846
34041
  let skillLikeCount = 0;
33847
34042
  for (const subdir of subdirs.slice(0, 3)) {
33848
- const subdirPath = join48(firstDirPath, subdir.name);
34043
+ const subdirPath = join49(firstDirPath, subdir.name);
33849
34044
  const subdirFiles = await readdir19(subdirPath, { withFileTypes: true });
33850
34045
  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"));
33851
34046
  if (hasSkillMarker) {
@@ -33855,7 +34050,7 @@ async function scanSkillsDirectory(skillsDir) {
33855
34050
  if (skillLikeCount > 0) {
33856
34051
  const skills = [];
33857
34052
  for (const dir of dirs) {
33858
- const categoryPath = join48(skillsDir, dir.name);
34053
+ const categoryPath = join49(skillsDir, dir.name);
33859
34054
  const skillDirs = await readdir19(categoryPath, { withFileTypes: true });
33860
34055
  skills.push(...skillDirs.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name));
33861
34056
  }
@@ -33865,7 +34060,7 @@ async function scanSkillsDirectory(skillsDir) {
33865
34060
  return ["flat", dirs.map((dir) => dir.name)];
33866
34061
  }
33867
34062
  async function findSkillPath(skillsDir, skillName) {
33868
- const flatPath = join48(skillsDir, skillName);
34063
+ const flatPath = join49(skillsDir, skillName);
33869
34064
  if (await import_fs_extra23.pathExists(flatPath)) {
33870
34065
  return { path: flatPath, category: undefined };
33871
34066
  }
@@ -33874,8 +34069,8 @@ async function findSkillPath(skillsDir, skillName) {
33874
34069
  if (!entry.isDirectory() || entry.name.startsWith(".") || entry.name === "node_modules") {
33875
34070
  continue;
33876
34071
  }
33877
- const categoryPath = join48(skillsDir, entry.name);
33878
- const skillPath = join48(categoryPath, skillName);
34072
+ const categoryPath = join49(skillsDir, entry.name);
34073
+ const skillPath = join49(categoryPath, skillName);
33879
34074
  if (await import_fs_extra23.pathExists(skillPath)) {
33880
34075
  return { path: skillPath, category: entry.name };
33881
34076
  }
@@ -33969,7 +34164,7 @@ class SkillsMigrator {
33969
34164
  }
33970
34165
  }
33971
34166
  if (options.backup && !options.dryRun) {
33972
- const claudeDir = join49(currentSkillsDir, "..");
34167
+ const claudeDir = join50(currentSkillsDir, "..");
33973
34168
  result.backupPath = await SkillsBackupManager.createBackup(currentSkillsDir, claudeDir);
33974
34169
  logger.success(`Backup created at: ${result.backupPath}`);
33975
34170
  }
@@ -34029,7 +34224,7 @@ async function handleMigration(ctx) {
34029
34224
  logger.debug("Skipping skills migration (fresh installation)");
34030
34225
  return ctx;
34031
34226
  }
34032
- const newSkillsDir = join50(ctx.extractDir, ".claude", "skills");
34227
+ const newSkillsDir = join51(ctx.extractDir, ".claude", "skills");
34033
34228
  const currentSkillsDir = PathResolver.buildSkillsPath(ctx.resolvedDir, ctx.options.global);
34034
34229
  if (!await import_fs_extra24.pathExists(newSkillsDir) || !await import_fs_extra24.pathExists(currentSkillsDir)) {
34035
34230
  return ctx;
@@ -34057,7 +34252,7 @@ import { existsSync as existsSync15 } from "node:fs";
34057
34252
  import { mkdir as mkdir16, readFile as readFile16, rename as rename2, rm as rm5, writeFile as writeFile13 } from "node:fs/promises";
34058
34253
  import { chmod as chmod2 } from "node:fs/promises";
34059
34254
  import { platform as platform9 } from "node:os";
34060
- import { join as join51 } from "node:path";
34255
+ import { join as join52 } from "node:path";
34061
34256
  init_types2();
34062
34257
  var PROJECT_CONFIG_FILE = ".ck.json";
34063
34258
 
@@ -34065,7 +34260,7 @@ class ConfigManager {
34065
34260
  static config = null;
34066
34261
  static globalFlag = false;
34067
34262
  static getProjectConfigDir(projectDir, global3) {
34068
- return global3 ? projectDir : join51(projectDir, ".claude");
34263
+ return global3 ? projectDir : join52(projectDir, ".claude");
34069
34264
  }
34070
34265
  static setGlobalFlag(global3) {
34071
34266
  ConfigManager.globalFlag = global3;
@@ -34132,7 +34327,7 @@ class ConfigManager {
34132
34327
  }
34133
34328
  static async loadProjectConfig(projectDir, global3 = false) {
34134
34329
  const configDir = ConfigManager.getProjectConfigDir(projectDir, global3);
34135
- const configPath = join51(configDir, PROJECT_CONFIG_FILE);
34330
+ const configPath = join52(configDir, PROJECT_CONFIG_FILE);
34136
34331
  try {
34137
34332
  if (existsSync15(configPath)) {
34138
34333
  const content = await readFile16(configPath, "utf-8");
@@ -34148,7 +34343,7 @@ class ConfigManager {
34148
34343
  }
34149
34344
  static async saveProjectConfig(projectDir, folders, global3 = false) {
34150
34345
  const configDir = ConfigManager.getProjectConfigDir(projectDir, global3);
34151
- const configPath = join51(configDir, PROJECT_CONFIG_FILE);
34346
+ const configPath = join52(configDir, PROJECT_CONFIG_FILE);
34152
34347
  try {
34153
34348
  if (!existsSync15(configDir)) {
34154
34349
  await mkdir16(configDir, { recursive: true });
@@ -34180,11 +34375,11 @@ class ConfigManager {
34180
34375
  }
34181
34376
  static projectConfigExists(projectDir, global3 = false) {
34182
34377
  const configDir = ConfigManager.getProjectConfigDir(projectDir, global3);
34183
- return existsSync15(join51(configDir, PROJECT_CONFIG_FILE));
34378
+ return existsSync15(join52(configDir, PROJECT_CONFIG_FILE));
34184
34379
  }
34185
34380
  static async migrateNestedConfig(globalDir) {
34186
- const correctPath = join51(globalDir, PROJECT_CONFIG_FILE);
34187
- const incorrectPath = join51(globalDir, ".claude", PROJECT_CONFIG_FILE);
34381
+ const correctPath = join52(globalDir, PROJECT_CONFIG_FILE);
34382
+ const incorrectPath = join52(globalDir, ".claude", PROJECT_CONFIG_FILE);
34188
34383
  if (existsSync15(correctPath)) {
34189
34384
  logger.debug("Config already exists at correct location, skipping migration");
34190
34385
  return false;
@@ -34194,7 +34389,7 @@ class ConfigManager {
34194
34389
  logger.info("Migrating .ck.json from nested location to correct location...");
34195
34390
  await rename2(incorrectPath, correctPath);
34196
34391
  logger.success(`Migrated ${PROJECT_CONFIG_FILE} to ${correctPath}`);
34197
- const nestedClaudeDir = join51(globalDir, ".claude");
34392
+ const nestedClaudeDir = join52(globalDir, ".claude");
34198
34393
  try {
34199
34394
  await rm5(nestedClaudeDir, { recursive: false });
34200
34395
  logger.debug("Removed empty nested .claude directory");
@@ -34231,6 +34426,7 @@ async function resolveOptions(ctx) {
34231
34426
  docsDir: parsed.docsDir,
34232
34427
  plansDir: parsed.plansDir,
34233
34428
  installSkills: parsed.installSkills ?? false,
34429
+ withSudo: parsed.withSudo ?? false,
34234
34430
  skipSetup: parsed.skipSetup ?? false,
34235
34431
  forceOverwrite: parsed.forceOverwrite ?? false,
34236
34432
  forceOverwriteSettings: parsed.forceOverwriteSettings ?? false,
@@ -34253,14 +34449,14 @@ async function resolveOptions(ctx) {
34253
34449
  };
34254
34450
  }
34255
34451
  // src/commands/init/phases/post-install-handler.ts
34256
- import { join as join54 } from "node:path";
34452
+ import { join as join55 } from "node:path";
34257
34453
 
34258
34454
  // src/domains/installation/setup-wizard.ts
34259
- import { join as join53 } from "node:path";
34455
+ import { join as join54 } from "node:path";
34260
34456
 
34261
34457
  // src/domains/config/config-generator.ts
34262
34458
  var import_fs_extra25 = __toESM(require_lib(), 1);
34263
- import { join as join52 } from "node:path";
34459
+ import { join as join53 } from "node:path";
34264
34460
  async function generateEnvFile(targetDir, values) {
34265
34461
  const lines = [
34266
34462
  "# Generated by ClaudeKit CLI setup wizard",
@@ -34272,7 +34468,7 @@ async function generateEnvFile(targetDir, values) {
34272
34468
  lines.push(`${key}=${value}`);
34273
34469
  }
34274
34470
  }
34275
- const envPath = join52(targetDir, ".env");
34471
+ const envPath = join53(targetDir, ".env");
34276
34472
  await import_fs_extra25.writeFile(envPath, `${lines.join(`
34277
34473
  `)}
34278
34474
  `, { mode: 384 });
@@ -34346,7 +34542,7 @@ async function parseEnvFile(path9) {
34346
34542
  }
34347
34543
  }
34348
34544
  async function checkGlobalConfig() {
34349
- const globalEnvPath = join53(PathResolver.getGlobalKitDir(), ".env");
34545
+ const globalEnvPath = join54(PathResolver.getGlobalKitDir(), ".env");
34350
34546
  if (!await import_fs_extra26.pathExists(globalEnvPath))
34351
34547
  return false;
34352
34548
  const env2 = await parseEnvFile(globalEnvPath);
@@ -34362,7 +34558,7 @@ async function runSetupWizard(options) {
34362
34558
  let globalEnv = {};
34363
34559
  const hasGlobalConfig = !isGlobal && await checkGlobalConfig();
34364
34560
  if (!isGlobal) {
34365
- const globalEnvPath = join53(PathResolver.getGlobalKitDir(), ".env");
34561
+ const globalEnvPath = join54(PathResolver.getGlobalKitDir(), ".env");
34366
34562
  if (await import_fs_extra26.pathExists(globalEnvPath)) {
34367
34563
  globalEnv = await parseEnvFile(globalEnvPath);
34368
34564
  }
@@ -34415,7 +34611,7 @@ async function runSetupWizard(options) {
34415
34611
  }
34416
34612
  }
34417
34613
  await generateEnvFile(targetDir, values);
34418
- f2.success(`Configuration saved to ${join53(targetDir, ".env")}`);
34614
+ f2.success(`Configuration saved to ${join54(targetDir, ".env")}`);
34419
34615
  return true;
34420
34616
  }
34421
34617
 
@@ -34427,8 +34623,8 @@ async function handlePostInstall(ctx) {
34427
34623
  return ctx;
34428
34624
  }
34429
34625
  if (ctx.options.global) {
34430
- const claudeMdSource = join54(ctx.extractDir, "CLAUDE.md");
34431
- const claudeMdDest = join54(ctx.resolvedDir, "CLAUDE.md");
34626
+ const claudeMdSource = join55(ctx.extractDir, "CLAUDE.md");
34627
+ const claudeMdDest = join55(ctx.resolvedDir, "CLAUDE.md");
34432
34628
  if (await import_fs_extra27.pathExists(claudeMdSource)) {
34433
34629
  if (!await import_fs_extra27.pathExists(claudeMdDest)) {
34434
34630
  await import_fs_extra27.copy(claudeMdSource, claudeMdDest);
@@ -34445,7 +34641,10 @@ async function handlePostInstall(ctx) {
34445
34641
  if (installSkills) {
34446
34642
  const { handleSkillsInstallation: handleSkillsInstallation2 } = await Promise.resolve().then(() => (init_package_installer(), exports_package_installer));
34447
34643
  const skillsDir = PathResolver.buildSkillsPath(ctx.resolvedDir, ctx.options.global);
34448
- await handleSkillsInstallation2(skillsDir);
34644
+ await handleSkillsInstallation2(skillsDir, {
34645
+ skipConfirm: ctx.isNonInteractive,
34646
+ withSudo: ctx.options.withSudo
34647
+ });
34449
34648
  }
34450
34649
  if (!ctx.isNonInteractive) {
34451
34650
  const { isGeminiInstalled: isGeminiInstalled2 } = await Promise.resolve().then(() => (init_package_installer(), exports_package_installer));
@@ -34472,7 +34671,7 @@ async function handlePostInstall(ctx) {
34472
34671
  }
34473
34672
  }
34474
34673
  if (!ctx.options.skipSetup && !ctx.isNonInteractive) {
34475
- const envPath = join54(ctx.claudeDir, ".env");
34674
+ const envPath = join55(ctx.claudeDir, ".env");
34476
34675
  if (!await import_fs_extra27.pathExists(envPath)) {
34477
34676
  const shouldSetup = await ctx.prompts.confirm("Set up API keys now? (Gemini API key for ai-multimodal skill, optional webhooks)");
34478
34677
  if (shouldSetup) {
@@ -34494,11 +34693,11 @@ Optional: DISCORD_WEBHOOK_URL, TELEGRAM_BOT_TOKEN`, "Configuration skipped");
34494
34693
  }
34495
34694
  // src/commands/init/phases/selection-handler.ts
34496
34695
  import { mkdir as mkdir17 } from "node:fs/promises";
34497
- import { join as join56, resolve as resolve7 } from "node:path";
34696
+ import { join as join57, resolve as resolve7 } from "node:path";
34498
34697
 
34499
34698
  // src/domains/installation/fresh-installer.ts
34500
34699
  init_logger();
34501
- import { join as join55 } from "node:path";
34700
+ import { join as join56 } from "node:path";
34502
34701
  var import_fs_extra28 = __toESM(require_lib(), 1);
34503
34702
  var CLAUDEKIT_SUBDIRECTORIES = ["commands", "agents", "skills", "workflows", "hooks"];
34504
34703
  async function handleFreshInstallation(claudeDir, prompts) {
@@ -34517,7 +34716,7 @@ async function handleFreshInstallation(claudeDir, prompts) {
34517
34716
  const { rmSync } = await import("node:fs");
34518
34717
  let removedCount = 0;
34519
34718
  for (const subdir of CLAUDEKIT_SUBDIRECTORIES) {
34520
- const subdirPath = join55(claudeDir, subdir);
34719
+ const subdirPath = join56(claudeDir, subdir);
34521
34720
  if (await import_fs_extra28.pathExists(subdirPath)) {
34522
34721
  rmSync(subdirPath, { recursive: true, force: true });
34523
34722
  removedCount++;
@@ -34582,7 +34781,7 @@ async function handleSelection(ctx) {
34582
34781
  }
34583
34782
  if (ctx.options.fresh) {
34584
34783
  const prefix = PathResolver.getPathPrefix(ctx.options.global);
34585
- const claudeDir = prefix ? join56(resolvedDir, prefix) : resolvedDir;
34784
+ const claudeDir = prefix ? join57(resolvedDir, prefix) : resolvedDir;
34586
34785
  const canProceed = await handleFreshInstallation(claudeDir, ctx.prompts);
34587
34786
  if (!canProceed) {
34588
34787
  return { ...ctx, cancelled: true };
@@ -34610,7 +34809,7 @@ async function handleSelection(ctx) {
34610
34809
  logger.info("Fetching available versions...");
34611
34810
  let currentVersion = null;
34612
34811
  try {
34613
- const metadataPath = ctx.options.global ? join56(PathResolver.getGlobalKitDir(), "metadata.json") : join56(resolvedDir, ".claude", "metadata.json");
34812
+ const metadataPath = ctx.options.global ? join57(PathResolver.getGlobalKitDir(), "metadata.json") : join57(resolvedDir, ".claude", "metadata.json");
34614
34813
  const metadata = await readClaudeKitMetadata(metadataPath);
34615
34814
  currentVersion = metadata?.version || null;
34616
34815
  if (currentVersion) {
@@ -34664,7 +34863,7 @@ async function handleSelection(ctx) {
34664
34863
  };
34665
34864
  }
34666
34865
  // src/commands/init/phases/transform-handler.ts
34667
- import { join as join60 } from "node:path";
34866
+ import { join as join61 } from "node:path";
34668
34867
 
34669
34868
  // src/services/transformers/folder-path-transformer.ts
34670
34869
  init_logger();
@@ -34675,38 +34874,38 @@ init_logger();
34675
34874
  init_types2();
34676
34875
  var import_fs_extra30 = __toESM(require_lib(), 1);
34677
34876
  import { rename as rename3 } from "node:fs/promises";
34678
- import { join as join57, relative as relative11 } from "node:path";
34877
+ import { join as join58, relative as relative11 } from "node:path";
34679
34878
  async function collectDirsToRename(extractDir, folders) {
34680
34879
  const dirsToRename = [];
34681
34880
  if (folders.docs !== DEFAULT_FOLDERS.docs) {
34682
- const docsPath = join57(extractDir, DEFAULT_FOLDERS.docs);
34881
+ const docsPath = join58(extractDir, DEFAULT_FOLDERS.docs);
34683
34882
  if (await import_fs_extra30.pathExists(docsPath)) {
34684
34883
  dirsToRename.push({
34685
34884
  from: docsPath,
34686
- to: join57(extractDir, folders.docs)
34885
+ to: join58(extractDir, folders.docs)
34687
34886
  });
34688
34887
  }
34689
- const claudeDocsPath = join57(extractDir, ".claude", DEFAULT_FOLDERS.docs);
34888
+ const claudeDocsPath = join58(extractDir, ".claude", DEFAULT_FOLDERS.docs);
34690
34889
  if (await import_fs_extra30.pathExists(claudeDocsPath)) {
34691
34890
  dirsToRename.push({
34692
34891
  from: claudeDocsPath,
34693
- to: join57(extractDir, ".claude", folders.docs)
34892
+ to: join58(extractDir, ".claude", folders.docs)
34694
34893
  });
34695
34894
  }
34696
34895
  }
34697
34896
  if (folders.plans !== DEFAULT_FOLDERS.plans) {
34698
- const plansPath = join57(extractDir, DEFAULT_FOLDERS.plans);
34897
+ const plansPath = join58(extractDir, DEFAULT_FOLDERS.plans);
34699
34898
  if (await import_fs_extra30.pathExists(plansPath)) {
34700
34899
  dirsToRename.push({
34701
34900
  from: plansPath,
34702
- to: join57(extractDir, folders.plans)
34901
+ to: join58(extractDir, folders.plans)
34703
34902
  });
34704
34903
  }
34705
- const claudePlansPath = join57(extractDir, ".claude", DEFAULT_FOLDERS.plans);
34904
+ const claudePlansPath = join58(extractDir, ".claude", DEFAULT_FOLDERS.plans);
34706
34905
  if (await import_fs_extra30.pathExists(claudePlansPath)) {
34707
34906
  dirsToRename.push({
34708
34907
  from: claudePlansPath,
34709
- to: join57(extractDir, ".claude", folders.plans)
34908
+ to: join58(extractDir, ".claude", folders.plans)
34710
34909
  });
34711
34910
  }
34712
34911
  }
@@ -34734,7 +34933,7 @@ async function renameFolders(dirsToRename, extractDir, options) {
34734
34933
  init_logger();
34735
34934
  init_types2();
34736
34935
  import { readFile as readFile18, readdir as readdir20, writeFile as writeFile15 } from "node:fs/promises";
34737
- import { join as join58, relative as relative12 } from "node:path";
34936
+ import { join as join59, relative as relative12 } from "node:path";
34738
34937
  var TRANSFORMABLE_FILE_PATTERNS = [
34739
34938
  ".md",
34740
34939
  ".txt",
@@ -34787,7 +34986,7 @@ async function transformFileContents(dir, compiledReplacements, options) {
34787
34986
  let replacementsCount = 0;
34788
34987
  const entries = await readdir20(dir, { withFileTypes: true });
34789
34988
  for (const entry of entries) {
34790
- const fullPath = join58(dir, entry.name);
34989
+ const fullPath = join59(dir, entry.name);
34791
34990
  if (entry.isDirectory()) {
34792
34991
  if (entry.name === "node_modules" || entry.name === ".git") {
34793
34992
  continue;
@@ -34924,7 +35123,7 @@ async function transformFolderPaths(extractDir, folders, options = {}) {
34924
35123
  init_logger();
34925
35124
  import { readFile as readFile19, readdir as readdir21, writeFile as writeFile16 } from "node:fs/promises";
34926
35125
  import { platform as platform10 } from "node:os";
34927
- import { extname, join as join59 } from "node:path";
35126
+ import { extname, join as join60 } from "node:path";
34928
35127
  var IS_WINDOWS3 = platform10() === "win32";
34929
35128
  var HOME_PREFIX = IS_WINDOWS3 ? "%USERPROFILE%" : "$HOME";
34930
35129
  function getHomeDirPrefix() {
@@ -35016,7 +35215,7 @@ async function transformPathsForGlobalInstall(directory, options = {}) {
35016
35215
  async function processDirectory2(dir) {
35017
35216
  const entries = await readdir21(dir, { withFileTypes: true });
35018
35217
  for (const entry of entries) {
35019
- const fullPath = join59(dir, entry.name);
35218
+ const fullPath = join60(dir, entry.name);
35020
35219
  if (entry.isDirectory()) {
35021
35220
  if (entry.name === "node_modules" || entry.name.startsWith(".") && entry.name !== ".claude") {
35022
35221
  continue;
@@ -35092,7 +35291,7 @@ async function handleTransforms(ctx) {
35092
35291
  logger.debug(ctx.options.global ? "Saved folder configuration to ~/.claude/.ck.json" : "Saved folder configuration to .claude/.ck.json");
35093
35292
  }
35094
35293
  }
35095
- const claudeDir = ctx.options.global ? ctx.resolvedDir : join60(ctx.resolvedDir, ".claude");
35294
+ const claudeDir = ctx.options.global ? ctx.resolvedDir : join61(ctx.resolvedDir, ".claude");
35096
35295
  return {
35097
35296
  ...ctx,
35098
35297
  foldersConfig,
@@ -35111,6 +35310,7 @@ function createInitContext(rawOptions, prompts) {
35111
35310
  exclude: [],
35112
35311
  only: [],
35113
35312
  installSkills: false,
35313
+ withSudo: false,
35114
35314
  skipSetup: false,
35115
35315
  forceOverwrite: false,
35116
35316
  forceOverwriteSettings: false,
@@ -35227,57 +35427,28 @@ async function directorySetup(validOptions, prompts) {
35227
35427
  isNonInteractive: isNonInteractive2
35228
35428
  };
35229
35429
  }
35230
-
35231
- // src/commands/new/phases/post-setup.ts
35232
- init_package_installer();
35233
- init_logger();
35234
- async function postSetup(resolvedDir, validOptions, isNonInteractive2, prompts) {
35235
- let installOpenCode2 = validOptions.opencode;
35236
- let installGemini2 = validOptions.gemini;
35237
- let installSkills = validOptions.installSkills;
35238
- if (!isNonInteractive2 && !installOpenCode2 && !installGemini2 && !installSkills) {
35239
- const packageChoices = await prompts.promptPackageInstallations();
35240
- installOpenCode2 = packageChoices.installOpenCode;
35241
- installGemini2 = packageChoices.installGemini;
35242
- installSkills = await prompts.promptSkillsInstallation();
35243
- }
35244
- if (installOpenCode2 || installGemini2) {
35245
- logger.info("Installing optional packages...");
35246
- try {
35247
- const installationResults = await processPackageInstallations(installOpenCode2, installGemini2, resolvedDir);
35248
- prompts.showPackageInstallationResults(installationResults);
35249
- } catch (error) {
35250
- logger.warning(`Package installation failed: ${error instanceof Error ? error.message : String(error)}`);
35251
- logger.info("You can install these packages manually later using npm install -g <package>");
35252
- }
35253
- }
35254
- if (installSkills) {
35255
- const { handleSkillsInstallation: handleSkillsInstallation2 } = await Promise.resolve().then(() => (init_package_installer(), exports_package_installer));
35256
- const skillsDir = PathResolver.buildSkillsPath(resolvedDir, false);
35257
- await handleSkillsInstallation2(skillsDir);
35430
+ async function handleDirectorySetup(ctx) {
35431
+ const result = await directorySetup(ctx.options, ctx.prompts);
35432
+ if (!result) {
35433
+ return { ...ctx, cancelled: true };
35258
35434
  }
35435
+ return {
35436
+ ...ctx,
35437
+ kit: result.kit,
35438
+ resolvedDir: result.resolvedDir,
35439
+ isNonInteractive: result.isNonInteractive
35440
+ };
35259
35441
  }
35260
-
35261
35442
  // src/commands/new/phases/project-creation.ts
35262
- import { join as join61 } from "node:path";
35263
- init_environment();
35443
+ import { join as join62 } from "node:path";
35264
35444
  init_logger();
35265
35445
  init_output_manager();
35266
35446
  init_types2();
35267
- async function projectCreation(kit, resolvedDir, validOptions, isNonInteractive2, prompts) {
35268
- const kitConfig = AVAILABLE_KITS[kit];
35269
- const github = new GitHubClient;
35270
- const spinner = createSpinner("Checking repository access...").start();
35271
- logger.verbose("GitHub API check", { repo: kitConfig.repo, owner: kitConfig.owner });
35272
- try {
35273
- await github.checkAccess(kitConfig);
35274
- spinner.succeed("Repository access verified");
35275
- } catch (error) {
35276
- spinner.fail("Access denied to repository");
35277
- logger.error(error.message || `Cannot access ${kitConfig.name}`);
35278
- return null;
35279
- }
35280
- let selectedVersion = validOptions.release;
35447
+
35448
+ // src/commands/new/phases/version-selection.ts
35449
+ init_logger();
35450
+ async function selectVersion2(kit, options, isNonInteractive2, prompts, github) {
35451
+ let selectedVersion = options.release;
35281
35452
  if (!selectedVersion && isNonInteractive2) {
35282
35453
  throw new Error("Interactive version selection unavailable in non-interactive mode. " + "Either: (1) use --release <tag> flag, or (2) set CI=false to enable interactive mode");
35283
35454
  }
@@ -35285,11 +35456,11 @@ async function projectCreation(kit, resolvedDir, validOptions, isNonInteractive2
35285
35456
  logger.info("Fetching available versions...");
35286
35457
  try {
35287
35458
  const versionResult = await prompts.selectVersionEnhanced({
35288
- kit: kitConfig,
35289
- includePrereleases: validOptions.beta,
35459
+ kit,
35460
+ includePrereleases: options.beta,
35290
35461
  limit: 10,
35291
35462
  allowManualEntry: true,
35292
- forceRefresh: validOptions.refresh
35463
+ forceRefresh: options.refresh
35293
35464
  });
35294
35465
  if (!versionResult) {
35295
35466
  logger.warning("Version selection cancelled by user");
@@ -35299,73 +35470,57 @@ async function projectCreation(kit, resolvedDir, validOptions, isNonInteractive2
35299
35470
  logger.success(`Selected version: ${selectedVersion}`);
35300
35471
  } catch (error) {
35301
35472
  logger.error("Failed to fetch versions, using latest release");
35302
- logger.debug(`Version selection error: ${error.message}`);
35473
+ const message = error instanceof Error ? error.message : String(error);
35474
+ logger.debug(`Version selection error: ${message}`);
35303
35475
  selectedVersion = undefined;
35304
35476
  }
35305
35477
  }
35306
35478
  let release;
35307
35479
  if (selectedVersion) {
35308
- release = await github.getReleaseByTag(kitConfig, selectedVersion);
35480
+ release = await github.getReleaseByTag(kit, selectedVersion);
35309
35481
  } else {
35310
- if (validOptions.beta) {
35482
+ if (options.beta) {
35311
35483
  logger.info("Fetching latest beta release...");
35312
35484
  } else {
35313
35485
  logger.info("Fetching latest release...");
35314
35486
  }
35315
- release = await github.getLatestRelease(kitConfig, validOptions.beta);
35487
+ release = await github.getLatestRelease(kit, options.beta);
35316
35488
  if (release.prerelease) {
35317
35489
  logger.success(`Found beta: ${release.tag_name}`);
35318
35490
  } else {
35319
35491
  logger.success(`Found: ${release.tag_name}`);
35320
35492
  }
35321
35493
  }
35322
- const downloadInfo = GitHubClient.getDownloadableAsset(release);
35323
- logger.verbose("Release info", {
35324
- tag: release.tag_name,
35325
- prerelease: release.prerelease,
35326
- downloadType: downloadInfo.type,
35327
- assetSize: downloadInfo.size
35328
- });
35329
- output.section("Downloading");
35330
- const downloadManager = new DownloadManager;
35331
- if (validOptions.exclude && validOptions.exclude.length > 0) {
35332
- downloadManager.setExcludePatterns(validOptions.exclude);
35333
- }
35334
- const tempDir = await downloadManager.createTempDir();
35335
- const { token } = await AuthManager.getToken();
35336
- let archivePath;
35494
+ return {
35495
+ release,
35496
+ selectedVersion: release.tag_name
35497
+ };
35498
+ }
35499
+
35500
+ // src/commands/new/phases/project-creation.ts
35501
+ async function projectCreation(kit, resolvedDir, validOptions, isNonInteractive2, prompts) {
35502
+ const kitConfig = AVAILABLE_KITS[kit];
35503
+ const github = new GitHubClient;
35504
+ const spinner = createSpinner("Checking repository access...").start();
35505
+ logger.verbose("GitHub API check", { repo: kitConfig.repo, owner: kitConfig.owner });
35337
35506
  try {
35338
- archivePath = await downloadManager.downloadFile({
35339
- url: downloadInfo.url,
35340
- name: downloadInfo.name,
35341
- size: downloadInfo.size,
35342
- destDir: tempDir,
35343
- token
35344
- });
35507
+ await github.checkAccess(kitConfig);
35508
+ spinner.succeed("Repository access verified");
35345
35509
  } catch (error) {
35346
- if (downloadInfo.type === "asset") {
35347
- logger.warning("Asset download failed, falling back to GitHub tarball...");
35348
- const tarballInfo = {
35349
- type: "github-tarball",
35350
- url: release.tarball_url,
35351
- name: `${kitConfig.repo}-${release.tag_name}.tar.gz`,
35352
- size: 0
35353
- };
35354
- archivePath = await downloadManager.downloadFile({
35355
- url: tarballInfo.url,
35356
- name: tarballInfo.name,
35357
- size: tarballInfo.size,
35358
- destDir: tempDir,
35359
- token
35360
- });
35361
- } else {
35362
- throw error;
35363
- }
35510
+ spinner.fail("Access denied to repository");
35511
+ logger.error(error.message || `Cannot access ${kitConfig.name}`);
35512
+ return null;
35364
35513
  }
35365
- const extractDir = `${tempDir}/extracted`;
35366
- logger.verbose("Extraction", { archivePath, extractDir });
35367
- await downloadManager.extractArchive(archivePath, extractDir);
35368
- await downloadManager.validateExtraction(extractDir);
35514
+ const versionResult = await selectVersion2(kitConfig, validOptions, isNonInteractive2, prompts, github);
35515
+ if (!versionResult) {
35516
+ return null;
35517
+ }
35518
+ const { release } = versionResult;
35519
+ const { extractDir } = await downloadAndExtract({
35520
+ release,
35521
+ kit: kitConfig,
35522
+ exclude: validOptions.exclude
35523
+ });
35369
35524
  if (CommandsPrefix.shouldApplyPrefix(validOptions)) {
35370
35525
  await CommandsPrefix.applyPrefix(extractDir);
35371
35526
  }
@@ -35396,59 +35551,106 @@ async function projectCreation(kit, resolvedDir, validOptions, isNonInteractive2
35396
35551
  await CommandsPrefix.cleanupCommandsDirectory(resolvedDir, false);
35397
35552
  }
35398
35553
  await merger.merge(extractDir, resolvedDir, true);
35399
- const claudeDir = join61(resolvedDir, ".claude");
35400
- const manifestWriter = new ManifestWriter;
35554
+ const claudeDir = join62(resolvedDir, ".claude");
35401
35555
  const releaseManifest = await ReleaseManifestLoader.load(extractDir);
35402
35556
  const installedFiles = merger.getAllInstalledFiles();
35403
- const filesToTrack = [];
35404
- for (const installedPath of installedFiles) {
35405
- if (!installedPath.startsWith(".claude/"))
35406
- continue;
35407
- const relativePath = installedPath.replace(/^\.claude\//, "");
35408
- const filePath = join61(claudeDir, relativePath);
35409
- const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
35410
- const ownership = manifestEntry ? "ck" : "user";
35411
- filesToTrack.push({
35412
- filePath,
35413
- relativePath,
35414
- ownership,
35415
- installedVersion: release.tag_name
35416
- });
35417
- }
35418
- const trackingSpinner = createSpinner(`Tracking ${filesToTrack.length} installed files...`);
35419
- trackingSpinner.start();
35420
- const trackResult = await manifestWriter.addTrackedFilesBatch(filesToTrack, {
35421
- concurrency: getOptimalConcurrency(),
35422
- onProgress: (processed, total) => {
35423
- trackingSpinner.text = `Tracking files... (${processed}/${total})`;
35424
- }
35557
+ const filesToTrack = buildFileTrackingList({
35558
+ installedFiles,
35559
+ claudeDir,
35560
+ releaseManifest,
35561
+ installedVersion: release.tag_name,
35562
+ isGlobal: false
35563
+ });
35564
+ await trackFilesWithProgress(filesToTrack, {
35565
+ claudeDir,
35566
+ kitName: kitConfig.name,
35567
+ releaseTag: release.tag_name,
35568
+ mode: "local"
35425
35569
  });
35426
- trackingSpinner.succeed(`Tracked ${trackResult.success} files`);
35427
- await manifestWriter.writeManifest(claudeDir, kitConfig.name, release.tag_name, "local");
35428
35570
  return {
35429
35571
  releaseTag: release.tag_name,
35430
35572
  installedFiles,
35431
35573
  claudeDir
35432
35574
  };
35433
35575
  }
35434
-
35576
+ async function handleProjectCreation(ctx) {
35577
+ if (!ctx.kit || !ctx.resolvedDir) {
35578
+ return { ...ctx, cancelled: true };
35579
+ }
35580
+ const result = await projectCreation(ctx.kit, ctx.resolvedDir, ctx.options, ctx.isNonInteractive, ctx.prompts);
35581
+ if (!result) {
35582
+ return { ...ctx, cancelled: true };
35583
+ }
35584
+ return {
35585
+ ...ctx,
35586
+ releaseTag: result.releaseTag,
35587
+ installedFiles: result.installedFiles,
35588
+ claudeDir: result.claudeDir
35589
+ };
35590
+ }
35591
+ // src/commands/new/phases/post-setup.ts
35592
+ init_package_installer();
35593
+ init_logger();
35594
+ async function postSetup(resolvedDir, validOptions, isNonInteractive2, prompts) {
35595
+ let installOpenCode2 = validOptions.opencode;
35596
+ let installGemini2 = validOptions.gemini;
35597
+ let installSkills = validOptions.installSkills;
35598
+ if (!isNonInteractive2 && !installOpenCode2 && !installGemini2 && !installSkills) {
35599
+ const packageChoices = await prompts.promptPackageInstallations();
35600
+ installOpenCode2 = packageChoices.installOpenCode;
35601
+ installGemini2 = packageChoices.installGemini;
35602
+ installSkills = await prompts.promptSkillsInstallation();
35603
+ }
35604
+ if (installOpenCode2 || installGemini2) {
35605
+ logger.info("Installing optional packages...");
35606
+ try {
35607
+ const installationResults = await processPackageInstallations(installOpenCode2, installGemini2, resolvedDir);
35608
+ prompts.showPackageInstallationResults(installationResults);
35609
+ } catch (error) {
35610
+ logger.warning(`Package installation failed: ${error instanceof Error ? error.message : String(error)}`);
35611
+ logger.info("You can install these packages manually later using npm install -g <package>");
35612
+ }
35613
+ }
35614
+ if (installSkills) {
35615
+ const { handleSkillsInstallation: handleSkillsInstallation2 } = await Promise.resolve().then(() => (init_package_installer(), exports_package_installer));
35616
+ const skillsDir = PathResolver.buildSkillsPath(resolvedDir, false);
35617
+ await handleSkillsInstallation2(skillsDir, {
35618
+ skipConfirm: isNonInteractive2,
35619
+ withSudo: validOptions.withSudo
35620
+ });
35621
+ }
35622
+ }
35623
+ async function handlePostSetup(ctx) {
35624
+ if (!ctx.resolvedDir) {
35625
+ return { ...ctx, cancelled: true };
35626
+ }
35627
+ await postSetup(ctx.resolvedDir, ctx.options, ctx.isNonInteractive, ctx.prompts);
35628
+ return ctx;
35629
+ }
35435
35630
  // src/commands/new/new-command.ts
35631
+ function createNewContext(options, prompts) {
35632
+ return {
35633
+ options,
35634
+ prompts,
35635
+ isNonInteractive: !process.stdin.isTTY || process.env.CI === "true",
35636
+ cancelled: false
35637
+ };
35638
+ }
35436
35639
  async function newCommand(options) {
35437
35640
  const prompts = new PromptsManager;
35438
35641
  prompts.intro("\uD83D\uDE80 ClaudeKit - Create New Project");
35439
35642
  try {
35440
- const validOptions = NewCommandOptionsSchema.parse(options);
35441
- const setupResult = await directorySetup(validOptions, prompts);
35442
- if (!setupResult) {
35643
+ let ctx = createNewContext(NewCommandOptionsSchema.parse(options), prompts);
35644
+ ctx = await handleDirectorySetup(ctx);
35645
+ if (ctx.cancelled)
35443
35646
  return;
35444
- }
35445
- const { kit, resolvedDir, isNonInteractive: isNonInteractive2 } = setupResult;
35446
- const creationResult = await projectCreation(kit, resolvedDir, validOptions, isNonInteractive2, prompts);
35447
- if (!creationResult) {
35647
+ ctx = await handleProjectCreation(ctx);
35648
+ if (ctx.cancelled)
35448
35649
  return;
35449
- }
35450
- await postSetup(resolvedDir, validOptions, isNonInteractive2, prompts);
35451
- prompts.outro(`✨ Project created successfully at ${resolvedDir}`);
35650
+ ctx = await handlePostSetup(ctx);
35651
+ if (ctx.cancelled)
35652
+ return;
35653
+ prompts.outro(`✨ Project created successfully at ${ctx.resolvedDir}`);
35452
35654
  } catch (error) {
35453
35655
  logger.error(error instanceof Error ? error.message : "Unknown error occurred");
35454
35656
  process.exit(1);
@@ -35483,13 +35685,13 @@ async function detectInstallations() {
35483
35685
 
35484
35686
  // src/commands/uninstall/removal-handler.ts
35485
35687
  import { readdirSync as readdirSync2, rmSync as rmSync2 } from "node:fs";
35486
- import { join as join63 } from "node:path";
35688
+ import { join as join64 } from "node:path";
35487
35689
  init_logger();
35488
35690
  var import_fs_extra33 = __toESM(require_lib(), 1);
35489
35691
 
35490
35692
  // src/commands/uninstall/analysis-handler.ts
35491
35693
  import { readdirSync, rmSync } from "node:fs";
35492
- import { dirname as dirname8, join as join62 } from "node:path";
35694
+ import { dirname as dirname8, join as join63 } from "node:path";
35493
35695
  init_logger();
35494
35696
  var import_picocolors15 = __toESM(require_picocolors(), 1);
35495
35697
  function classifyFileByOwnership(ownership, forceOverwrite, deleteReason) {
@@ -35536,7 +35738,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
35536
35738
  if (uninstallManifest.isMultiKit && kit && metadata?.kits?.[kit]) {
35537
35739
  const kitFiles = metadata.kits[kit].files || [];
35538
35740
  for (const trackedFile of kitFiles) {
35539
- const filePath = join62(installation.path, trackedFile.path);
35741
+ const filePath = join63(installation.path, trackedFile.path);
35540
35742
  if (uninstallManifest.filesToPreserve.includes(trackedFile.path)) {
35541
35743
  result.toPreserve.push({ path: trackedFile.path, reason: "shared with other kit" });
35542
35744
  continue;
@@ -35566,7 +35768,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
35566
35768
  return result;
35567
35769
  }
35568
35770
  for (const trackedFile of allTrackedFiles) {
35569
- const filePath = join62(installation.path, trackedFile.path);
35771
+ const filePath = join63(installation.path, trackedFile.path);
35570
35772
  const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
35571
35773
  if (!ownershipResult.exists)
35572
35774
  continue;
@@ -35626,7 +35828,7 @@ async function removeInstallations(installations, options) {
35626
35828
  let removedCount = 0;
35627
35829
  let cleanedDirs = 0;
35628
35830
  for (const item of analysis.toDelete) {
35629
- const filePath = join63(installation.path, item.path);
35831
+ const filePath = join64(installation.path, item.path);
35630
35832
  if (await import_fs_extra33.pathExists(filePath)) {
35631
35833
  await import_fs_extra33.remove(filePath);
35632
35834
  removedCount++;
@@ -35661,10 +35863,11 @@ async function removeInstallations(installations, options) {
35661
35863
  }
35662
35864
 
35663
35865
  // src/commands/uninstall/uninstall-command.ts
35866
+ var prompts = new PromptsManager;
35664
35867
  function displayInstallations(installations, scope) {
35665
- intro("ClaudeKit Uninstaller");
35868
+ prompts.intro("ClaudeKit Uninstaller");
35666
35869
  const scopeLabel = scope === "all" ? "all" : scope === "local" ? "local only" : "global only";
35667
- note(installations.map((i) => ` ${i.type === "local" ? "Local " : "Global"}: ${i.path}`).join(`
35870
+ prompts.note(installations.map((i) => ` ${i.type === "local" ? "Local " : "Global"}: ${i.path}`).join(`
35668
35871
  `), `Detected ClaudeKit installations (${scopeLabel})`);
35669
35872
  log.warn("[!] This will permanently delete ClaudeKit files from the above paths.");
35670
35873
  }
@@ -35758,7 +35961,7 @@ async function uninstallCommand(options) {
35758
35961
  forceOverwrite: validOptions.forceOverwrite,
35759
35962
  kit: validOptions.kit
35760
35963
  });
35761
- outro("Dry-run complete. No changes were made.");
35964
+ prompts.outro("Dry-run complete. No changes were made.");
35762
35965
  return;
35763
35966
  }
35764
35967
  if (validOptions.forceOverwrite) {
@@ -35779,7 +35982,7 @@ ${import_picocolors16.default.yellow("User modifications will be permanently del
35779
35982
  kit: validOptions.kit
35780
35983
  });
35781
35984
  const kitMsg = validOptions.kit ? ` (${validOptions.kit} kit)` : "";
35782
- outro(`ClaudeKit${kitMsg} uninstalled successfully!`);
35985
+ prompts.outro(`ClaudeKit${kitMsg} uninstalled successfully!`);
35783
35986
  } catch (error) {
35784
35987
  logger.error(error instanceof Error ? error.message : "Unknown error");
35785
35988
  process.exit(1);
@@ -35932,7 +36135,7 @@ var import_compare_versions2 = __toESM(require_umd(), 1);
35932
36135
  // package.json
35933
36136
  var package_default = {
35934
36137
  name: "claudekit-cli",
35935
- version: "3.12.1",
36138
+ version: "3.13.0",
35936
36139
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
35937
36140
  type: "module",
35938
36141
  repository: {
@@ -36188,8 +36391,8 @@ ${import_picocolors17.default.bold(import_picocolors17.default.cyan(kitName))} -
36188
36391
  Showing ${releases.length} ${releases.length === 1 ? "release" : "releases"}`));
36189
36392
  }
36190
36393
  async function versionCommand(options) {
36191
- const prompts = new PromptsManager;
36192
- prompts.intro("\uD83D\uDCE6 ClaudeKit - Available Versions");
36394
+ const prompts2 = new PromptsManager;
36395
+ prompts2.intro("\uD83D\uDCE6 ClaudeKit - Available Versions");
36193
36396
  try {
36194
36397
  const validOptions = VersionCommandOptionsSchema.parse(options);
36195
36398
  const kitsToFetch = validOptions.kit ? [validOptions.kit] : Object.keys(AVAILABLE_KITS);
@@ -36225,7 +36428,7 @@ ${import_picocolors17.default.bold(import_picocolors17.default.cyan(result.kitCo
36225
36428
  displayKitReleases(result.kitConfig.name, result.releases);
36226
36429
  }
36227
36430
  }
36228
- prompts.outro("✨ Done");
36431
+ prompts2.outro("✨ Done");
36229
36432
  } catch (error) {
36230
36433
  logger.error(error instanceof Error ? error.message : "Unknown error occurred");
36231
36434
  process.exit(1);
@@ -36235,13 +36438,13 @@ ${import_picocolors17.default.bold(import_picocolors17.default.cyan(result.kitCo
36235
36438
  // src/cli/command-registry.ts
36236
36439
  init_logger();
36237
36440
  function registerCommands(cli) {
36238
- cli.command("new", "Bootstrap a new ClaudeKit project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to use (engineer, marketing)").option("-r, --release <version>", "Skip version selection, use specific version (e.g., latest, v1.0.0)").option("--force", "Overwrite existing files without confirmation").option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)").option("--opencode", "Install OpenCode CLI package (non-interactive mode)").option("--gemini", "Install Google Gemini CLI package (non-interactive mode)").option("--install-skills", "Install skills dependencies (non-interactive mode)").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("--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)").action(async (options) => {
36441
+ cli.command("new", "Bootstrap a new ClaudeKit project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to use (engineer, marketing)").option("-r, --release <version>", "Skip version selection, use specific version (e.g., latest, v1.0.0)").option("--force", "Overwrite existing files without confirmation").option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)").option("--opencode", "Install OpenCode CLI package (non-interactive mode)").option("--gemini", "Install Google Gemini CLI package (non-interactive mode)").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("--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)").action(async (options) => {
36239
36442
  if (options.exclude && !Array.isArray(options.exclude)) {
36240
36443
  options.exclude = [options.exclude];
36241
36444
  }
36242
36445
  await newCommand(options);
36243
36446
  });
36244
- 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)").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("--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)").action(async (options) => {
36447
+ 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)").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)").action(async (options) => {
36245
36448
  if (options.exclude && !Array.isArray(options.exclude)) {
36246
36449
  options.exclude = [options.exclude];
36247
36450
  }
@@ -36250,7 +36453,7 @@ function registerCommands(cli) {
36250
36453
  }
36251
36454
  await initCommand(options);
36252
36455
  });
36253
- cli.command("update", "Update ClaudeKit CLI to the latest version").option("-r, --release <version>", "Update to a specific version").option("--check", "Check for updates without installing").option("-y, --yes", "Skip confirmation prompt").option("--beta", "Update to the latest beta version").option("--registry <url>", "Custom npm registry URL").option("--kit <kit>", "[DEPRECATED] Use 'ck init --kit <kit>' instead").option("-g, --global", "[DEPRECATED] Use 'ck init --global' instead").action(async (options) => {
36456
+ cli.command("update", "Update ClaudeKit CLI to the latest version").option("-r, --release <version>", "Update to a specific version").option("--check", "Check for updates without installing").option("-y, --yes", "Non-interactive mode with sensible defaults (skip all prompts)").option("--beta", "Update to the latest beta version").option("--registry <url>", "Custom npm registry URL").option("--kit <kit>", "[DEPRECATED] Use 'ck init --kit <kit>' instead").option("-g, --global", "[DEPRECATED] Use 'ck init --global' instead").action(async (options) => {
36254
36457
  if (options.kit || options.global) {
36255
36458
  console.log();
36256
36459
  const deprecatedFlags = [options.kit && "--kit", options.global && "--global"].filter(Boolean).join(" and ");
@@ -36280,7 +36483,7 @@ function registerCommands(cli) {
36280
36483
  cli.command("doctor", "Comprehensive health check for ClaudeKit").option("--report", "Generate shareable diagnostic report").option("--fix", "Auto-fix all fixable issues").option("--check-only", "CI mode: no prompts, exit 1 on failures").option("--json", "Output JSON format").option("--full", "Include extended priority checks (slower)").action(async (options) => {
36281
36484
  await doctorCommand(options);
36282
36485
  });
36283
- cli.command("uninstall", "Remove ClaudeKit installations").option("-y, --yes", "Skip confirmation prompt").option("-l, --local", "Uninstall only local installation (current project)").option("-g, --global", "Uninstall only global installation (~/.claude/)").option("-A, --all", "Uninstall from both local and global locations").option("-k, --kit <type>", "Uninstall specific kit only (engineer, marketing)").option("--dry-run", "Preview what would be removed without deleting").option("--force-overwrite", "Delete even user-modified files (requires confirmation)").action(async (options) => {
36486
+ cli.command("uninstall", "Remove ClaudeKit installations").option("-y, --yes", "Non-interactive mode with sensible defaults (skip all prompts)").option("-l, --local", "Uninstall only local installation (current project)").option("-g, --global", "Uninstall only global installation (~/.claude/)").option("-A, --all", "Uninstall from both local and global locations").option("-k, --kit <type>", "Uninstall specific kit only (engineer, marketing)").option("--dry-run", "Preview what would be removed without deleting").option("--force-overwrite", "Delete even user-modified files (requires confirmation)").action(async (options) => {
36284
36487
  await uninstallCommand(options);
36285
36488
  });
36286
36489
  cli.command("easter-egg", "\uD83E\uDD5A Roll for a random discount code (Code Hunt 2025)").action(async () => {
@@ -36290,7 +36493,7 @@ function registerCommands(cli) {
36290
36493
 
36291
36494
  // src/cli/version-display.ts
36292
36495
  import { existsSync as existsSync17, readFileSync as readFileSync5 } from "node:fs";
36293
- import { join as join65 } from "node:path";
36496
+ import { join as join66 } from "node:path";
36294
36497
 
36295
36498
  // src/domains/versioning/checking/version-utils.ts
36296
36499
  var import_compare_versions3 = __toESM(require_umd(), 1);
@@ -36317,13 +36520,13 @@ init_types2();
36317
36520
  init_logger();
36318
36521
  import { existsSync as existsSync16 } from "node:fs";
36319
36522
  import { mkdir as mkdir18, readFile as readFile20, writeFile as writeFile17 } from "node:fs/promises";
36320
- import { join as join64 } from "node:path";
36523
+ import { join as join65 } from "node:path";
36321
36524
  class VersionCacheManager {
36322
36525
  static CACHE_FILENAME = "version-check.json";
36323
36526
  static CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
36324
36527
  static getCacheFile() {
36325
36528
  const cacheDir = PathResolver.getCacheDir(false);
36326
- return join64(cacheDir, VersionCacheManager.CACHE_FILENAME);
36529
+ return join65(cacheDir, VersionCacheManager.CACHE_FILENAME);
36327
36530
  }
36328
36531
  static async load() {
36329
36532
  const cacheFile = VersionCacheManager.getCacheFile();
@@ -36566,9 +36769,9 @@ async function displayVersion() {
36566
36769
  let localKitVersion = null;
36567
36770
  let isGlobalOnlyKit = false;
36568
36771
  const globalKitDir = PathResolver.getGlobalKitDir();
36569
- const globalMetadataPath = join65(globalKitDir, "metadata.json");
36772
+ const globalMetadataPath = join66(globalKitDir, "metadata.json");
36570
36773
  const prefix = PathResolver.getPathPrefix(false);
36571
- const localMetadataPath = prefix ? join65(process.cwd(), prefix, "metadata.json") : join65(process.cwd(), "metadata.json");
36774
+ const localMetadataPath = prefix ? join66(process.cwd(), prefix, "metadata.json") : join66(process.cwd(), "metadata.json");
36572
36775
  const isLocalSameAsGlobal = localMetadataPath === globalMetadataPath;
36573
36776
  if (!isLocalSameAsGlobal && existsSync17(localMetadataPath)) {
36574
36777
  try {
@@ -36635,6 +36838,7 @@ import { createWriteStream as createWriteStream3 } from "node:fs";
36635
36838
  class Logger2 {
36636
36839
  verboseEnabled = false;
36637
36840
  logFileStream;
36841
+ exitHandlerRegistered = false;
36638
36842
  info(message) {
36639
36843
  const symbols = output.getSymbols();
36640
36844
  console.log(import_picocolors19.default.blue(symbols.info), message);
@@ -36689,9 +36893,49 @@ class Logger2 {
36689
36893
  flags: "a",
36690
36894
  mode: 384
36691
36895
  });
36896
+ this.registerExitHandler();
36692
36897
  this.verbose(`Logging to file: ${path9}`);
36693
36898
  }
36694
36899
  }
36900
+ close() {
36901
+ if (this.logFileStream) {
36902
+ this.logFileStream.end();
36903
+ this.logFileStream = undefined;
36904
+ }
36905
+ }
36906
+ registerExitHandler() {
36907
+ if (this.exitHandlerRegistered)
36908
+ return;
36909
+ this.exitHandlerRegistered = true;
36910
+ const cleanup = () => {
36911
+ if (this.logFileStream) {
36912
+ try {
36913
+ this.logFileStream.end();
36914
+ } catch {}
36915
+ this.logFileStream = undefined;
36916
+ }
36917
+ };
36918
+ process.on("exit", cleanup);
36919
+ process.on("SIGINT", () => {
36920
+ cleanup();
36921
+ process.exit(130);
36922
+ });
36923
+ process.on("SIGTERM", () => {
36924
+ cleanup();
36925
+ process.exit(143);
36926
+ });
36927
+ process.on("uncaughtException", (error) => {
36928
+ if (this.logFileStream) {
36929
+ const timestamp = new Date().toISOString();
36930
+ this.logFileStream.write(`${timestamp} [FATAL] Uncaught exception: ${error.message}
36931
+ `);
36932
+ this.logFileStream.write(`${error.stack}
36933
+ `);
36934
+ }
36935
+ cleanup();
36936
+ process.exit(1);
36937
+ });
36938
+ }
36695
36939
  sanitize(text) {
36696
36940
  return text.replace(/ghp_[a-zA-Z0-9]{36}/g, "ghp_***").replace(/github_pat_[a-zA-Z0-9_]{82}/g, "github_pat_***").replace(/gho_[a-zA-Z0-9]{36}/g, "gho_***").replace(/ghu_[a-zA-Z0-9]{36}/g, "ghu_***").replace(/ghs_[a-zA-Z0-9]{36}/g, "ghs_***").replace(/ghr_[a-zA-Z0-9]{36}/g, "ghr_***").replace(/Bearer [a-zA-Z0-9_-]+/g, "Bearer ***").replace(/token=[a-zA-Z0-9_-]+/g, "token=***");
36697
36941
  }
@@ -36736,7 +36980,11 @@ var SYMBOLS2 = {
36736
36980
  line: "│",
36737
36981
  selected: "●",
36738
36982
  unselected: "○",
36739
- pointer: ">"
36983
+ pointer: ">",
36984
+ pass: "✓",
36985
+ warn: "⚠",
36986
+ fail: "✗",
36987
+ infoStatus: "ℹ"
36740
36988
  },
36741
36989
  ascii: {
36742
36990
  prompt: "?",
@@ -36747,10 +36995,13 @@ var SYMBOLS2 = {
36747
36995
  line: "|",
36748
36996
  selected: ">",
36749
36997
  unselected: " ",
36750
- pointer: ">"
36998
+ pointer: ">",
36999
+ pass: "[PASS]",
37000
+ warn: "[WARN]",
37001
+ fail: "[FAIL]",
37002
+ infoStatus: "[INFO]"
36751
37003
  }
36752
37004
  };
36753
-
36754
37005
  class OutputManager2 {
36755
37006
  config = {
36756
37007
  verbose: false,
@@ -36760,6 +37011,7 @@ class OutputManager2 {
36760
37011
  jsonBuffer = [];
36761
37012
  unicodeSupported;
36762
37013
  flushPromise = null;
37014
+ flushQueued = false;
36763
37015
  constructor() {
36764
37016
  this.unicodeSupported = supportsUnicode();
36765
37017
  }
@@ -36865,8 +37117,12 @@ class OutputManager2 {
36865
37117
  ...entry,
36866
37118
  timestamp: new Date().toISOString()
36867
37119
  });
36868
- if (this.jsonBuffer.length >= 1000 && !this.flushPromise) {
36869
- queueMicrotask(() => this.flushJson());
37120
+ if (this.jsonBuffer.length >= 1000 && !this.flushPromise && !this.flushQueued) {
37121
+ this.flushQueued = true;
37122
+ queueMicrotask(() => {
37123
+ this.flushQueued = false;
37124
+ this.flushJson();
37125
+ });
36870
37126
  }
36871
37127
  }
36872
37128
  addJsonResult(data) {
@@ -36896,6 +37152,7 @@ class OutputManager2 {
36896
37152
  this.config = { verbose: false, json: false, quiet: false };
36897
37153
  this.jsonBuffer = [];
36898
37154
  this.flushPromise = null;
37155
+ this.flushQueued = false;
36899
37156
  this.unicodeSupported = supportsUnicode();
36900
37157
  }
36901
37158
  }