claudekit-cli 3.10.2 → 3.11.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 +1582 -372
  2. package/package.json +4 -3
package/dist/index.js CHANGED
@@ -6600,7 +6600,8 @@ var init_commands = __esm(() => {
6600
6600
  global: exports_external.boolean().default(false),
6601
6601
  all: exports_external.boolean().default(false),
6602
6602
  dryRun: exports_external.boolean().default(false),
6603
- forceOverwrite: exports_external.boolean().default(false)
6603
+ forceOverwrite: exports_external.boolean().default(false),
6604
+ kit: KitType.optional()
6604
6605
  }).merge(GlobalOutputOptionsSchema);
6605
6606
  UpdateCliOptionsSchema = exports_external.object({
6606
6607
  release: exports_external.string().optional(),
@@ -6643,7 +6644,7 @@ var init_github = __esm(() => {
6643
6644
  });
6644
6645
 
6645
6646
  // src/types/metadata.ts
6646
- var TrackedFileSchema, MetadataSchema, ConfigSchema;
6647
+ var TrackedFileSchema, KitMetadataSchema, MultiKitMetadataSchema, LegacyMetadataSchema, MetadataSchema, ConfigSchema;
6647
6648
  var init_metadata = __esm(() => {
6648
6649
  init_zod();
6649
6650
  init_commands();
@@ -6654,7 +6655,22 @@ var init_metadata = __esm(() => {
6654
6655
  ownership: exports_external.enum(["ck", "user", "ck-modified"]),
6655
6656
  installedVersion: exports_external.string()
6656
6657
  });
6657
- MetadataSchema = exports_external.object({
6658
+ KitMetadataSchema = exports_external.object({
6659
+ version: exports_external.string(),
6660
+ installedAt: exports_external.string(),
6661
+ files: exports_external.array(TrackedFileSchema).optional()
6662
+ });
6663
+ MultiKitMetadataSchema = exports_external.object({
6664
+ kits: exports_external.record(KitType, KitMetadataSchema).optional(),
6665
+ scope: exports_external.enum(["local", "global"]).optional(),
6666
+ name: exports_external.string().optional(),
6667
+ version: exports_external.string().optional(),
6668
+ installedAt: exports_external.string().optional(),
6669
+ installedFiles: exports_external.array(exports_external.string()).optional(),
6670
+ userConfigFiles: exports_external.array(exports_external.string()).optional(),
6671
+ files: exports_external.array(TrackedFileSchema).optional()
6672
+ });
6673
+ LegacyMetadataSchema = exports_external.object({
6658
6674
  name: exports_external.string().optional(),
6659
6675
  version: exports_external.string().optional(),
6660
6676
  installedAt: exports_external.string().optional(),
@@ -6663,6 +6679,7 @@ var init_metadata = __esm(() => {
6663
6679
  userConfigFiles: exports_external.array(exports_external.string()).optional(),
6664
6680
  files: exports_external.array(TrackedFileSchema).optional()
6665
6681
  });
6682
+ MetadataSchema = MultiKitMetadataSchema;
6666
6683
  ConfigSchema = exports_external.object({
6667
6684
  defaults: exports_external.object({
6668
6685
  kit: KitType.optional(),
@@ -6747,8 +6764,11 @@ __export(exports_types, {
6747
6764
  PROTECTED_PATTERNS: () => PROTECTED_PATTERNS,
6748
6765
  NewCommandOptionsSchema: () => NewCommandOptionsSchema,
6749
6766
  NEVER_COPY_PATTERNS: () => NEVER_COPY_PATTERNS,
6767
+ MultiKitMetadataSchema: () => MultiKitMetadataSchema,
6750
6768
  MetadataSchema: () => MetadataSchema,
6769
+ LegacyMetadataSchema: () => LegacyMetadataSchema,
6751
6770
  KitType: () => KitType,
6771
+ KitMetadataSchema: () => KitMetadataSchema,
6752
6772
  KitConfigSchema: () => KitConfigSchema,
6753
6773
  GitHubReleaseSchema: () => GitHubReleaseSchema,
6754
6774
  GitHubReleaseAssetSchema: () => GitHubReleaseAssetSchema,
@@ -12253,9 +12273,751 @@ var require_ignore = __commonJS((exports, module) => {
12253
12273
  }
12254
12274
  });
12255
12275
 
12276
+ // node_modules/retry/lib/retry_operation.js
12277
+ var require_retry_operation = __commonJS((exports, module) => {
12278
+ function RetryOperation(timeouts, options) {
12279
+ if (typeof options === "boolean") {
12280
+ options = { forever: options };
12281
+ }
12282
+ this._originalTimeouts = JSON.parse(JSON.stringify(timeouts));
12283
+ this._timeouts = timeouts;
12284
+ this._options = options || {};
12285
+ this._maxRetryTime = options && options.maxRetryTime || Infinity;
12286
+ this._fn = null;
12287
+ this._errors = [];
12288
+ this._attempts = 1;
12289
+ this._operationTimeout = null;
12290
+ this._operationTimeoutCb = null;
12291
+ this._timeout = null;
12292
+ this._operationStart = null;
12293
+ if (this._options.forever) {
12294
+ this._cachedTimeouts = this._timeouts.slice(0);
12295
+ }
12296
+ }
12297
+ module.exports = RetryOperation;
12298
+ RetryOperation.prototype.reset = function() {
12299
+ this._attempts = 1;
12300
+ this._timeouts = this._originalTimeouts;
12301
+ };
12302
+ RetryOperation.prototype.stop = function() {
12303
+ if (this._timeout) {
12304
+ clearTimeout(this._timeout);
12305
+ }
12306
+ this._timeouts = [];
12307
+ this._cachedTimeouts = null;
12308
+ };
12309
+ RetryOperation.prototype.retry = function(err) {
12310
+ if (this._timeout) {
12311
+ clearTimeout(this._timeout);
12312
+ }
12313
+ if (!err) {
12314
+ return false;
12315
+ }
12316
+ var currentTime = new Date().getTime();
12317
+ if (err && currentTime - this._operationStart >= this._maxRetryTime) {
12318
+ this._errors.unshift(new Error("RetryOperation timeout occurred"));
12319
+ return false;
12320
+ }
12321
+ this._errors.push(err);
12322
+ var timeout = this._timeouts.shift();
12323
+ if (timeout === undefined) {
12324
+ if (this._cachedTimeouts) {
12325
+ this._errors.splice(this._errors.length - 1, this._errors.length);
12326
+ this._timeouts = this._cachedTimeouts.slice(0);
12327
+ timeout = this._timeouts.shift();
12328
+ } else {
12329
+ return false;
12330
+ }
12331
+ }
12332
+ var self2 = this;
12333
+ var timer = setTimeout(function() {
12334
+ self2._attempts++;
12335
+ if (self2._operationTimeoutCb) {
12336
+ self2._timeout = setTimeout(function() {
12337
+ self2._operationTimeoutCb(self2._attempts);
12338
+ }, self2._operationTimeout);
12339
+ if (self2._options.unref) {
12340
+ self2._timeout.unref();
12341
+ }
12342
+ }
12343
+ self2._fn(self2._attempts);
12344
+ }, timeout);
12345
+ if (this._options.unref) {
12346
+ timer.unref();
12347
+ }
12348
+ return true;
12349
+ };
12350
+ RetryOperation.prototype.attempt = function(fn, timeoutOps) {
12351
+ this._fn = fn;
12352
+ if (timeoutOps) {
12353
+ if (timeoutOps.timeout) {
12354
+ this._operationTimeout = timeoutOps.timeout;
12355
+ }
12356
+ if (timeoutOps.cb) {
12357
+ this._operationTimeoutCb = timeoutOps.cb;
12358
+ }
12359
+ }
12360
+ var self2 = this;
12361
+ if (this._operationTimeoutCb) {
12362
+ this._timeout = setTimeout(function() {
12363
+ self2._operationTimeoutCb();
12364
+ }, self2._operationTimeout);
12365
+ }
12366
+ this._operationStart = new Date().getTime();
12367
+ this._fn(this._attempts);
12368
+ };
12369
+ RetryOperation.prototype.try = function(fn) {
12370
+ console.log("Using RetryOperation.try() is deprecated");
12371
+ this.attempt(fn);
12372
+ };
12373
+ RetryOperation.prototype.start = function(fn) {
12374
+ console.log("Using RetryOperation.start() is deprecated");
12375
+ this.attempt(fn);
12376
+ };
12377
+ RetryOperation.prototype.start = RetryOperation.prototype.try;
12378
+ RetryOperation.prototype.errors = function() {
12379
+ return this._errors;
12380
+ };
12381
+ RetryOperation.prototype.attempts = function() {
12382
+ return this._attempts;
12383
+ };
12384
+ RetryOperation.prototype.mainError = function() {
12385
+ if (this._errors.length === 0) {
12386
+ return null;
12387
+ }
12388
+ var counts = {};
12389
+ var mainError = null;
12390
+ var mainErrorCount = 0;
12391
+ for (var i = 0;i < this._errors.length; i++) {
12392
+ var error = this._errors[i];
12393
+ var message = error.message;
12394
+ var count = (counts[message] || 0) + 1;
12395
+ counts[message] = count;
12396
+ if (count >= mainErrorCount) {
12397
+ mainError = error;
12398
+ mainErrorCount = count;
12399
+ }
12400
+ }
12401
+ return mainError;
12402
+ };
12403
+ });
12404
+
12405
+ // node_modules/retry/lib/retry.js
12406
+ var require_retry = __commonJS((exports) => {
12407
+ var RetryOperation = require_retry_operation();
12408
+ exports.operation = function(options) {
12409
+ var timeouts = exports.timeouts(options);
12410
+ return new RetryOperation(timeouts, {
12411
+ forever: options && options.forever,
12412
+ unref: options && options.unref,
12413
+ maxRetryTime: options && options.maxRetryTime
12414
+ });
12415
+ };
12416
+ exports.timeouts = function(options) {
12417
+ if (options instanceof Array) {
12418
+ return [].concat(options);
12419
+ }
12420
+ var opts = {
12421
+ retries: 10,
12422
+ factor: 2,
12423
+ minTimeout: 1 * 1000,
12424
+ maxTimeout: Infinity,
12425
+ randomize: false
12426
+ };
12427
+ for (var key in options) {
12428
+ opts[key] = options[key];
12429
+ }
12430
+ if (opts.minTimeout > opts.maxTimeout) {
12431
+ throw new Error("minTimeout is greater than maxTimeout");
12432
+ }
12433
+ var timeouts = [];
12434
+ for (var i = 0;i < opts.retries; i++) {
12435
+ timeouts.push(this.createTimeout(i, opts));
12436
+ }
12437
+ if (options && options.forever && !timeouts.length) {
12438
+ timeouts.push(this.createTimeout(i, opts));
12439
+ }
12440
+ timeouts.sort(function(a3, b3) {
12441
+ return a3 - b3;
12442
+ });
12443
+ return timeouts;
12444
+ };
12445
+ exports.createTimeout = function(attempt, opts) {
12446
+ var random = opts.randomize ? Math.random() + 1 : 1;
12447
+ var timeout = Math.round(random * opts.minTimeout * Math.pow(opts.factor, attempt));
12448
+ timeout = Math.min(timeout, opts.maxTimeout);
12449
+ return timeout;
12450
+ };
12451
+ exports.wrap = function(obj, options, methods) {
12452
+ if (options instanceof Array) {
12453
+ methods = options;
12454
+ options = null;
12455
+ }
12456
+ if (!methods) {
12457
+ methods = [];
12458
+ for (var key in obj) {
12459
+ if (typeof obj[key] === "function") {
12460
+ methods.push(key);
12461
+ }
12462
+ }
12463
+ }
12464
+ for (var i = 0;i < methods.length; i++) {
12465
+ var method = methods[i];
12466
+ var original = obj[method];
12467
+ obj[method] = function retryWrapper(original2) {
12468
+ var op = exports.operation(options);
12469
+ var args = Array.prototype.slice.call(arguments, 1);
12470
+ var callback = args.pop();
12471
+ args.push(function(err) {
12472
+ if (op.retry(err)) {
12473
+ return;
12474
+ }
12475
+ if (err) {
12476
+ arguments[0] = op.mainError();
12477
+ }
12478
+ callback.apply(this, arguments);
12479
+ });
12480
+ op.attempt(function() {
12481
+ original2.apply(obj, args);
12482
+ });
12483
+ }.bind(obj, original);
12484
+ obj[method].options = options;
12485
+ }
12486
+ };
12487
+ });
12488
+
12489
+ // node_modules/signal-exit/signals.js
12490
+ var require_signals = __commonJS((exports, module) => {
12491
+ module.exports = [
12492
+ "SIGABRT",
12493
+ "SIGALRM",
12494
+ "SIGHUP",
12495
+ "SIGINT",
12496
+ "SIGTERM"
12497
+ ];
12498
+ if (process.platform !== "win32") {
12499
+ module.exports.push("SIGVTALRM", "SIGXCPU", "SIGXFSZ", "SIGUSR2", "SIGTRAP", "SIGSYS", "SIGQUIT", "SIGIOT");
12500
+ }
12501
+ if (process.platform === "linux") {
12502
+ module.exports.push("SIGIO", "SIGPOLL", "SIGPWR", "SIGSTKFLT", "SIGUNUSED");
12503
+ }
12504
+ });
12505
+
12506
+ // node_modules/signal-exit/index.js
12507
+ var require_signal_exit = __commonJS((exports, module) => {
12508
+ var process10 = global.process;
12509
+ var processOk2 = function(process11) {
12510
+ return process11 && typeof process11 === "object" && typeof process11.removeListener === "function" && typeof process11.emit === "function" && typeof process11.reallyExit === "function" && typeof process11.listeners === "function" && typeof process11.kill === "function" && typeof process11.pid === "number" && typeof process11.on === "function";
12511
+ };
12512
+ if (!processOk2(process10)) {
12513
+ module.exports = function() {
12514
+ return function() {};
12515
+ };
12516
+ } else {
12517
+ assert3 = __require("assert");
12518
+ signals2 = require_signals();
12519
+ isWin = /^win/i.test(process10.platform);
12520
+ EE3 = __require("events");
12521
+ if (typeof EE3 !== "function") {
12522
+ EE3 = EE3.EventEmitter;
12523
+ }
12524
+ if (process10.__signal_exit_emitter__) {
12525
+ emitter = process10.__signal_exit_emitter__;
12526
+ } else {
12527
+ emitter = process10.__signal_exit_emitter__ = new EE3;
12528
+ emitter.count = 0;
12529
+ emitter.emitted = {};
12530
+ }
12531
+ if (!emitter.infinite) {
12532
+ emitter.setMaxListeners(Infinity);
12533
+ emitter.infinite = true;
12534
+ }
12535
+ module.exports = function(cb, opts) {
12536
+ if (!processOk2(global.process)) {
12537
+ return function() {};
12538
+ }
12539
+ assert3.equal(typeof cb, "function", "a callback must be provided for exit handler");
12540
+ if (loaded === false) {
12541
+ load2();
12542
+ }
12543
+ var ev = "exit";
12544
+ if (opts && opts.alwaysLast) {
12545
+ ev = "afterexit";
12546
+ }
12547
+ var remove = function() {
12548
+ emitter.removeListener(ev, cb);
12549
+ if (emitter.listeners("exit").length === 0 && emitter.listeners("afterexit").length === 0) {
12550
+ unload2();
12551
+ }
12552
+ };
12553
+ emitter.on(ev, cb);
12554
+ return remove;
12555
+ };
12556
+ unload2 = function unload() {
12557
+ if (!loaded || !processOk2(global.process)) {
12558
+ return;
12559
+ }
12560
+ loaded = false;
12561
+ signals2.forEach(function(sig) {
12562
+ try {
12563
+ process10.removeListener(sig, sigListeners[sig]);
12564
+ } catch (er) {}
12565
+ });
12566
+ process10.emit = originalProcessEmit;
12567
+ process10.reallyExit = originalProcessReallyExit;
12568
+ emitter.count -= 1;
12569
+ };
12570
+ module.exports.unload = unload2;
12571
+ emit = function emit(event, code2, signal) {
12572
+ if (emitter.emitted[event]) {
12573
+ return;
12574
+ }
12575
+ emitter.emitted[event] = true;
12576
+ emitter.emit(event, code2, signal);
12577
+ };
12578
+ sigListeners = {};
12579
+ signals2.forEach(function(sig) {
12580
+ sigListeners[sig] = function listener() {
12581
+ if (!processOk2(global.process)) {
12582
+ return;
12583
+ }
12584
+ var listeners = process10.listeners(sig);
12585
+ if (listeners.length === emitter.count) {
12586
+ unload2();
12587
+ emit("exit", null, sig);
12588
+ emit("afterexit", null, sig);
12589
+ if (isWin && sig === "SIGHUP") {
12590
+ sig = "SIGINT";
12591
+ }
12592
+ process10.kill(process10.pid, sig);
12593
+ }
12594
+ };
12595
+ });
12596
+ module.exports.signals = function() {
12597
+ return signals2;
12598
+ };
12599
+ loaded = false;
12600
+ load2 = function load() {
12601
+ if (loaded || !processOk2(global.process)) {
12602
+ return;
12603
+ }
12604
+ loaded = true;
12605
+ emitter.count += 1;
12606
+ signals2 = signals2.filter(function(sig) {
12607
+ try {
12608
+ process10.on(sig, sigListeners[sig]);
12609
+ return true;
12610
+ } catch (er) {
12611
+ return false;
12612
+ }
12613
+ });
12614
+ process10.emit = processEmit;
12615
+ process10.reallyExit = processReallyExit;
12616
+ };
12617
+ module.exports.load = load2;
12618
+ originalProcessReallyExit = process10.reallyExit;
12619
+ processReallyExit = function processReallyExit(code2) {
12620
+ if (!processOk2(global.process)) {
12621
+ return;
12622
+ }
12623
+ process10.exitCode = code2 || 0;
12624
+ emit("exit", process10.exitCode, null);
12625
+ emit("afterexit", process10.exitCode, null);
12626
+ originalProcessReallyExit.call(process10, process10.exitCode);
12627
+ };
12628
+ originalProcessEmit = process10.emit;
12629
+ processEmit = function processEmit(ev, arg) {
12630
+ if (ev === "exit" && processOk2(global.process)) {
12631
+ if (arg !== undefined) {
12632
+ process10.exitCode = arg;
12633
+ }
12634
+ var ret = originalProcessEmit.apply(this, arguments);
12635
+ emit("exit", process10.exitCode, null);
12636
+ emit("afterexit", process10.exitCode, null);
12637
+ return ret;
12638
+ } else {
12639
+ return originalProcessEmit.apply(this, arguments);
12640
+ }
12641
+ };
12642
+ }
12643
+ var assert3;
12644
+ var signals2;
12645
+ var isWin;
12646
+ var EE3;
12647
+ var emitter;
12648
+ var unload2;
12649
+ var emit;
12650
+ var sigListeners;
12651
+ var loaded;
12652
+ var load2;
12653
+ var originalProcessReallyExit;
12654
+ var processReallyExit;
12655
+ var originalProcessEmit;
12656
+ var processEmit;
12657
+ });
12658
+
12659
+ // node_modules/proper-lockfile/lib/mtime-precision.js
12660
+ var require_mtime_precision = __commonJS((exports, module) => {
12661
+ var cacheSymbol = Symbol();
12662
+ function probe(file, fs12, callback) {
12663
+ const cachedPrecision = fs12[cacheSymbol];
12664
+ if (cachedPrecision) {
12665
+ return fs12.stat(file, (err, stat3) => {
12666
+ if (err) {
12667
+ return callback(err);
12668
+ }
12669
+ callback(null, stat3.mtime, cachedPrecision);
12670
+ });
12671
+ }
12672
+ const mtime = new Date(Math.ceil(Date.now() / 1000) * 1000 + 5);
12673
+ fs12.utimes(file, mtime, mtime, (err) => {
12674
+ if (err) {
12675
+ return callback(err);
12676
+ }
12677
+ fs12.stat(file, (err2, stat3) => {
12678
+ if (err2) {
12679
+ return callback(err2);
12680
+ }
12681
+ const precision = stat3.mtime.getTime() % 1000 === 0 ? "s" : "ms";
12682
+ Object.defineProperty(fs12, cacheSymbol, { value: precision });
12683
+ callback(null, stat3.mtime, precision);
12684
+ });
12685
+ });
12686
+ }
12687
+ function getMtime(precision) {
12688
+ let now = Date.now();
12689
+ if (precision === "s") {
12690
+ now = Math.ceil(now / 1000) * 1000;
12691
+ }
12692
+ return new Date(now);
12693
+ }
12694
+ exports.probe = probe;
12695
+ exports.getMtime = getMtime;
12696
+ });
12697
+
12698
+ // node_modules/proper-lockfile/lib/lockfile.js
12699
+ var require_lockfile = __commonJS((exports, module) => {
12700
+ var path9 = __require("path");
12701
+ var fs12 = require_graceful_fs();
12702
+ var retry = require_retry();
12703
+ var onExit2 = require_signal_exit();
12704
+ var mtimePrecision = require_mtime_precision();
12705
+ var locks = {};
12706
+ function getLockFile(file, options) {
12707
+ return options.lockfilePath || `${file}.lock`;
12708
+ }
12709
+ function resolveCanonicalPath(file, options, callback) {
12710
+ if (!options.realpath) {
12711
+ return callback(null, path9.resolve(file));
12712
+ }
12713
+ options.fs.realpath(file, callback);
12714
+ }
12715
+ function acquireLock(file, options, callback) {
12716
+ const lockfilePath = getLockFile(file, options);
12717
+ options.fs.mkdir(lockfilePath, (err) => {
12718
+ if (!err) {
12719
+ return mtimePrecision.probe(lockfilePath, options.fs, (err2, mtime, mtimePrecision2) => {
12720
+ if (err2) {
12721
+ options.fs.rmdir(lockfilePath, () => {});
12722
+ return callback(err2);
12723
+ }
12724
+ callback(null, mtime, mtimePrecision2);
12725
+ });
12726
+ }
12727
+ if (err.code !== "EEXIST") {
12728
+ return callback(err);
12729
+ }
12730
+ if (options.stale <= 0) {
12731
+ return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
12732
+ }
12733
+ options.fs.stat(lockfilePath, (err2, stat3) => {
12734
+ if (err2) {
12735
+ if (err2.code === "ENOENT") {
12736
+ return acquireLock(file, { ...options, stale: 0 }, callback);
12737
+ }
12738
+ return callback(err2);
12739
+ }
12740
+ if (!isLockStale(stat3, options)) {
12741
+ return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
12742
+ }
12743
+ removeLock(file, options, (err3) => {
12744
+ if (err3) {
12745
+ return callback(err3);
12746
+ }
12747
+ acquireLock(file, { ...options, stale: 0 }, callback);
12748
+ });
12749
+ });
12750
+ });
12751
+ }
12752
+ function isLockStale(stat3, options) {
12753
+ return stat3.mtime.getTime() < Date.now() - options.stale;
12754
+ }
12755
+ function removeLock(file, options, callback) {
12756
+ options.fs.rmdir(getLockFile(file, options), (err) => {
12757
+ if (err && err.code !== "ENOENT") {
12758
+ return callback(err);
12759
+ }
12760
+ callback();
12761
+ });
12762
+ }
12763
+ function updateLock(file, options) {
12764
+ const lock2 = locks[file];
12765
+ if (lock2.updateTimeout) {
12766
+ return;
12767
+ }
12768
+ lock2.updateDelay = lock2.updateDelay || options.update;
12769
+ lock2.updateTimeout = setTimeout(() => {
12770
+ lock2.updateTimeout = null;
12771
+ options.fs.stat(lock2.lockfilePath, (err, stat3) => {
12772
+ const isOverThreshold = lock2.lastUpdate + options.stale < Date.now();
12773
+ if (err) {
12774
+ if (err.code === "ENOENT" || isOverThreshold) {
12775
+ return setLockAsCompromised(file, lock2, Object.assign(err, { code: "ECOMPROMISED" }));
12776
+ }
12777
+ lock2.updateDelay = 1000;
12778
+ return updateLock(file, options);
12779
+ }
12780
+ const isMtimeOurs = lock2.mtime.getTime() === stat3.mtime.getTime();
12781
+ if (!isMtimeOurs) {
12782
+ return setLockAsCompromised(file, lock2, Object.assign(new Error("Unable to update lock within the stale threshold"), { code: "ECOMPROMISED" }));
12783
+ }
12784
+ const mtime = mtimePrecision.getMtime(lock2.mtimePrecision);
12785
+ options.fs.utimes(lock2.lockfilePath, mtime, mtime, (err2) => {
12786
+ const isOverThreshold2 = lock2.lastUpdate + options.stale < Date.now();
12787
+ if (lock2.released) {
12788
+ return;
12789
+ }
12790
+ if (err2) {
12791
+ if (err2.code === "ENOENT" || isOverThreshold2) {
12792
+ return setLockAsCompromised(file, lock2, Object.assign(err2, { code: "ECOMPROMISED" }));
12793
+ }
12794
+ lock2.updateDelay = 1000;
12795
+ return updateLock(file, options);
12796
+ }
12797
+ lock2.mtime = mtime;
12798
+ lock2.lastUpdate = Date.now();
12799
+ lock2.updateDelay = null;
12800
+ updateLock(file, options);
12801
+ });
12802
+ });
12803
+ }, lock2.updateDelay);
12804
+ if (lock2.updateTimeout.unref) {
12805
+ lock2.updateTimeout.unref();
12806
+ }
12807
+ }
12808
+ function setLockAsCompromised(file, lock2, err) {
12809
+ lock2.released = true;
12810
+ if (lock2.updateTimeout) {
12811
+ clearTimeout(lock2.updateTimeout);
12812
+ }
12813
+ if (locks[file] === lock2) {
12814
+ delete locks[file];
12815
+ }
12816
+ lock2.options.onCompromised(err);
12817
+ }
12818
+ function lock(file, options, callback) {
12819
+ options = {
12820
+ stale: 1e4,
12821
+ update: null,
12822
+ realpath: true,
12823
+ retries: 0,
12824
+ fs: fs12,
12825
+ onCompromised: (err) => {
12826
+ throw err;
12827
+ },
12828
+ ...options
12829
+ };
12830
+ options.retries = options.retries || 0;
12831
+ options.retries = typeof options.retries === "number" ? { retries: options.retries } : options.retries;
12832
+ options.stale = Math.max(options.stale || 0, 2000);
12833
+ options.update = options.update == null ? options.stale / 2 : options.update || 0;
12834
+ options.update = Math.max(Math.min(options.update, options.stale / 2), 1000);
12835
+ resolveCanonicalPath(file, options, (err, file2) => {
12836
+ if (err) {
12837
+ return callback(err);
12838
+ }
12839
+ const operation = retry.operation(options.retries);
12840
+ operation.attempt(() => {
12841
+ acquireLock(file2, options, (err2, mtime, mtimePrecision2) => {
12842
+ if (operation.retry(err2)) {
12843
+ return;
12844
+ }
12845
+ if (err2) {
12846
+ return callback(operation.mainError());
12847
+ }
12848
+ const lock2 = locks[file2] = {
12849
+ lockfilePath: getLockFile(file2, options),
12850
+ mtime,
12851
+ mtimePrecision: mtimePrecision2,
12852
+ options,
12853
+ lastUpdate: Date.now()
12854
+ };
12855
+ updateLock(file2, options);
12856
+ callback(null, (releasedCallback) => {
12857
+ if (lock2.released) {
12858
+ return releasedCallback && releasedCallback(Object.assign(new Error("Lock is already released"), { code: "ERELEASED" }));
12859
+ }
12860
+ unlock(file2, { ...options, realpath: false }, releasedCallback);
12861
+ });
12862
+ });
12863
+ });
12864
+ });
12865
+ }
12866
+ function unlock(file, options, callback) {
12867
+ options = {
12868
+ fs: fs12,
12869
+ realpath: true,
12870
+ ...options
12871
+ };
12872
+ resolveCanonicalPath(file, options, (err, file2) => {
12873
+ if (err) {
12874
+ return callback(err);
12875
+ }
12876
+ const lock2 = locks[file2];
12877
+ if (!lock2) {
12878
+ return callback(Object.assign(new Error("Lock is not acquired/owned by you"), { code: "ENOTACQUIRED" }));
12879
+ }
12880
+ lock2.updateTimeout && clearTimeout(lock2.updateTimeout);
12881
+ lock2.released = true;
12882
+ delete locks[file2];
12883
+ removeLock(file2, options, callback);
12884
+ });
12885
+ }
12886
+ function check(file, options, callback) {
12887
+ options = {
12888
+ stale: 1e4,
12889
+ realpath: true,
12890
+ fs: fs12,
12891
+ ...options
12892
+ };
12893
+ options.stale = Math.max(options.stale || 0, 2000);
12894
+ resolveCanonicalPath(file, options, (err, file2) => {
12895
+ if (err) {
12896
+ return callback(err);
12897
+ }
12898
+ options.fs.stat(getLockFile(file2, options), (err2, stat3) => {
12899
+ if (err2) {
12900
+ return err2.code === "ENOENT" ? callback(null, false) : callback(err2);
12901
+ }
12902
+ return callback(null, !isLockStale(stat3, options));
12903
+ });
12904
+ });
12905
+ }
12906
+ function getLocks() {
12907
+ return locks;
12908
+ }
12909
+ onExit2(() => {
12910
+ for (const file in locks) {
12911
+ const options = locks[file].options;
12912
+ try {
12913
+ options.fs.rmdirSync(getLockFile(file, options));
12914
+ } catch (e2) {}
12915
+ }
12916
+ });
12917
+ exports.lock = lock;
12918
+ exports.unlock = unlock;
12919
+ exports.check = check;
12920
+ exports.getLocks = getLocks;
12921
+ });
12922
+
12923
+ // node_modules/proper-lockfile/lib/adapter.js
12924
+ var require_adapter = __commonJS((exports, module) => {
12925
+ var fs12 = require_graceful_fs();
12926
+ function createSyncFs(fs13) {
12927
+ const methods = ["mkdir", "realpath", "stat", "rmdir", "utimes"];
12928
+ const newFs = { ...fs13 };
12929
+ methods.forEach((method) => {
12930
+ newFs[method] = (...args) => {
12931
+ const callback = args.pop();
12932
+ let ret;
12933
+ try {
12934
+ ret = fs13[`${method}Sync`](...args);
12935
+ } catch (err) {
12936
+ return callback(err);
12937
+ }
12938
+ callback(null, ret);
12939
+ };
12940
+ });
12941
+ return newFs;
12942
+ }
12943
+ function toPromise(method) {
12944
+ return (...args) => new Promise((resolve3, reject) => {
12945
+ args.push((err, result) => {
12946
+ if (err) {
12947
+ reject(err);
12948
+ } else {
12949
+ resolve3(result);
12950
+ }
12951
+ });
12952
+ method(...args);
12953
+ });
12954
+ }
12955
+ function toSync(method) {
12956
+ return (...args) => {
12957
+ let err;
12958
+ let result;
12959
+ args.push((_err, _result) => {
12960
+ err = _err;
12961
+ result = _result;
12962
+ });
12963
+ method(...args);
12964
+ if (err) {
12965
+ throw err;
12966
+ }
12967
+ return result;
12968
+ };
12969
+ }
12970
+ function toSyncOptions(options) {
12971
+ options = { ...options };
12972
+ options.fs = createSyncFs(options.fs || fs12);
12973
+ if (typeof options.retries === "number" && options.retries > 0 || options.retries && typeof options.retries.retries === "number" && options.retries.retries > 0) {
12974
+ throw Object.assign(new Error("Cannot use retries with the sync api"), { code: "ESYNC" });
12975
+ }
12976
+ return options;
12977
+ }
12978
+ module.exports = {
12979
+ toPromise,
12980
+ toSync,
12981
+ toSyncOptions
12982
+ };
12983
+ });
12984
+
12985
+ // node_modules/proper-lockfile/index.js
12986
+ var require_proper_lockfile = __commonJS((exports, module) => {
12987
+ var lockfile = require_lockfile();
12988
+ var { toPromise, toSync, toSyncOptions } = require_adapter();
12989
+ async function lock(file, options) {
12990
+ const release = await toPromise(lockfile.lock)(file, options);
12991
+ return toPromise(release);
12992
+ }
12993
+ function lockSync(file, options) {
12994
+ const release = toSync(lockfile.lock)(file, toSyncOptions(options));
12995
+ return toSync(release);
12996
+ }
12997
+ function unlock(file, options) {
12998
+ return toPromise(lockfile.unlock)(file, options);
12999
+ }
13000
+ function unlockSync(file, options) {
13001
+ return toSync(lockfile.unlock)(file, toSyncOptions(options));
13002
+ }
13003
+ function check(file, options) {
13004
+ return toPromise(lockfile.check)(file, options);
13005
+ }
13006
+ function checkSync(file, options) {
13007
+ return toSync(lockfile.check)(file, toSyncOptions(options));
13008
+ }
13009
+ module.exports = lock;
13010
+ module.exports.lock = lock;
13011
+ module.exports.unlock = unlock;
13012
+ module.exports.lockSync = lockSync;
13013
+ module.exports.unlockSync = unlockSync;
13014
+ module.exports.check = check;
13015
+ module.exports.checkSync = checkSync;
13016
+ });
13017
+
12256
13018
  // src/services/package-installer/install-error-handler.ts
12257
13019
  import { existsSync as existsSync6, readFileSync as readFileSync4, unlinkSync as unlinkSync2 } from "node:fs";
12258
- import { join as join24 } from "node:path";
13020
+ import { join as join25 } from "node:path";
12259
13021
  function parseNameReason(str) {
12260
13022
  const colonIndex = str.indexOf(":");
12261
13023
  if (colonIndex === -1) {
@@ -12264,7 +13026,7 @@ function parseNameReason(str) {
12264
13026
  return [str.slice(0, colonIndex).trim(), str.slice(colonIndex + 1).trim()];
12265
13027
  }
12266
13028
  function displayInstallErrors(skillsDir) {
12267
- const summaryPath = join24(skillsDir, ".install-error-summary.json");
13029
+ const summaryPath = join25(skillsDir, ".install-error-summary.json");
12268
13030
  if (!existsSync6(summaryPath)) {
12269
13031
  logger.error("Skills installation failed. Run with --verbose for details.");
12270
13032
  return;
@@ -12355,7 +13117,7 @@ async function checkNeedsSudoPackages() {
12355
13117
  }
12356
13118
  }
12357
13119
  function hasInstallState(skillsDir) {
12358
- const stateFilePath = join24(skillsDir, ".install-state.json");
13120
+ const stateFilePath = join25(skillsDir, ".install-state.json");
12359
13121
  return existsSync6(stateFilePath);
12360
13122
  }
12361
13123
  var WHICH_COMMAND_TIMEOUT_MS = 5000;
@@ -12374,14 +13136,14 @@ __export(exports_gemini_mcp_linker, {
12374
13136
  addGeminiToGitignore: () => addGeminiToGitignore
12375
13137
  });
12376
13138
  import { existsSync as existsSync7, lstatSync, readlinkSync } from "node:fs";
12377
- import { mkdir as mkdir9, readFile as readFile14, symlink as symlink2, writeFile as writeFile12 } from "node:fs/promises";
13139
+ import { mkdir as mkdir9, readFile as readFile15, symlink as symlink2, writeFile as writeFile13 } from "node:fs/promises";
12378
13140
  import { homedir as homedir4 } from "node:os";
12379
- import { dirname as dirname6, join as join25, resolve as resolve3 } from "node:path";
13141
+ import { dirname as dirname6, join as join26, resolve as resolve3 } from "node:path";
12380
13142
  function getGlobalMcpConfigPath() {
12381
- return join25(homedir4(), ".claude", ".mcp.json");
13143
+ return join26(homedir4(), ".claude", ".mcp.json");
12382
13144
  }
12383
13145
  function getLocalMcpConfigPath(projectDir) {
12384
- return join25(projectDir, ".mcp.json");
13146
+ return join26(projectDir, ".mcp.json");
12385
13147
  }
12386
13148
  function findMcpConfigPath(projectDir) {
12387
13149
  const localPath = getLocalMcpConfigPath(projectDir);
@@ -12399,9 +13161,9 @@ function findMcpConfigPath(projectDir) {
12399
13161
  }
12400
13162
  function getGeminiSettingsPath(projectDir, isGlobal) {
12401
13163
  if (isGlobal) {
12402
- return join25(homedir4(), ".gemini", "settings.json");
13164
+ return join26(homedir4(), ".gemini", "settings.json");
12403
13165
  }
12404
- return join25(projectDir, ".gemini", "settings.json");
13166
+ return join26(projectDir, ".gemini", "settings.json");
12405
13167
  }
12406
13168
  function checkExistingGeminiConfig(projectDir, isGlobal = false) {
12407
13169
  const geminiSettingsPath = getGeminiSettingsPath(projectDir, isGlobal);
@@ -12426,7 +13188,7 @@ function checkExistingGeminiConfig(projectDir, isGlobal = false) {
12426
13188
  }
12427
13189
  async function readJsonFile(filePath) {
12428
13190
  try {
12429
- const content = await readFile14(filePath, "utf-8");
13191
+ const content = await readFile15(filePath, "utf-8");
12430
13192
  return JSON.parse(content);
12431
13193
  } catch (error) {
12432
13194
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
@@ -12445,7 +13207,7 @@ async function createSymlink(targetPath, linkPath, projectDir, isGlobal) {
12445
13207
  if (isGlobal) {
12446
13208
  symlinkTarget = getGlobalMcpConfigPath();
12447
13209
  } else {
12448
- const localMcpPath = join25(projectDir, ".mcp.json");
13210
+ const localMcpPath = join26(projectDir, ".mcp.json");
12449
13211
  const isLocalConfig = targetPath === localMcpPath;
12450
13212
  symlinkTarget = isLocalConfig ? "../.mcp.json" : targetPath;
12451
13213
  }
@@ -12478,7 +13240,7 @@ async function createNewSettingsWithMerge(geminiSettingsPath, mcpConfigPath) {
12478
13240
  }
12479
13241
  const newSettings = { mcpServers };
12480
13242
  try {
12481
- await writeFile12(geminiSettingsPath, JSON.stringify(newSettings, null, 2), "utf-8");
13243
+ await writeFile13(geminiSettingsPath, JSON.stringify(newSettings, null, 2), "utf-8");
12482
13244
  logger.debug(`Created new Gemini settings with mcpServers: ${geminiSettingsPath}`);
12483
13245
  return { success: true, method: "merge", targetPath: mcpConfigPath };
12484
13246
  } catch (error) {
@@ -12508,7 +13270,7 @@ async function mergeGeminiSettings(geminiSettingsPath, mcpConfigPath) {
12508
13270
  mcpServers
12509
13271
  };
12510
13272
  try {
12511
- await writeFile12(geminiSettingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
13273
+ await writeFile13(geminiSettingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
12512
13274
  logger.debug(`Merged mcpServers into: ${geminiSettingsPath}`);
12513
13275
  return { success: true, method: "merge", targetPath: mcpConfigPath };
12514
13276
  } catch (error) {
@@ -12521,12 +13283,12 @@ async function mergeGeminiSettings(geminiSettingsPath, mcpConfigPath) {
12521
13283
  }
12522
13284
  }
12523
13285
  async function addGeminiToGitignore(projectDir) {
12524
- const gitignorePath = join25(projectDir, ".gitignore");
13286
+ const gitignorePath = join26(projectDir, ".gitignore");
12525
13287
  const geminiPattern = ".gemini/";
12526
13288
  try {
12527
13289
  let content = "";
12528
13290
  if (existsSync7(gitignorePath)) {
12529
- content = await readFile14(gitignorePath, "utf-8");
13291
+ content = await readFile15(gitignorePath, "utf-8");
12530
13292
  const lines = content.split(`
12531
13293
  `).map((line) => line.trim()).filter((line) => !line.startsWith("#"));
12532
13294
  const geminiPatterns = [".gemini/", ".gemini", "/.gemini/", "/.gemini"];
@@ -12539,7 +13301,7 @@ async function addGeminiToGitignore(projectDir) {
12539
13301
  `) || content === "" ? "" : `
12540
13302
  `;
12541
13303
  const comment = "# Gemini CLI settings (contains user-specific config)";
12542
- await writeFile12(gitignorePath, `${content}${newLine}${comment}
13304
+ await writeFile13(gitignorePath, `${content}${newLine}${comment}
12543
13305
  ${geminiPattern}
12544
13306
  `, "utf-8");
12545
13307
  logger.debug(`Added ${geminiPattern} to .gitignore`);
@@ -12631,7 +13393,7 @@ __export(exports_package_installer, {
12631
13393
  getPackageVersion: () => getPackageVersion
12632
13394
  });
12633
13395
  import { exec as exec5, execFile as execFile2, spawn } from "node:child_process";
12634
- import { join as join26, resolve as resolve4 } from "node:path";
13396
+ import { join as join27, resolve as resolve4 } from "node:path";
12635
13397
  import { promisify as promisify5 } from "node:util";
12636
13398
  function executeInteractiveScript(command, args, options) {
12637
13399
  return new Promise((resolve5, reject) => {
@@ -12826,7 +13588,7 @@ async function installOpenCode() {
12826
13588
  logger.info(`Installing ${displayName}...`);
12827
13589
  const { unlink: unlink5 } = await import("node:fs/promises");
12828
13590
  const { tmpdir: tmpdir3 } = await import("node:os");
12829
- const tempScriptPath = join26(tmpdir3(), "opencode-install.sh");
13591
+ const tempScriptPath = join27(tmpdir3(), "opencode-install.sh");
12830
13592
  try {
12831
13593
  logger.info("Downloading OpenCode installation script...");
12832
13594
  await execFileAsync("curl", ["-fsSL", "https://opencode.ai/install", "-o", tempScriptPath], {
@@ -12945,7 +13707,7 @@ async function installSkillsDependencies(skillsDir) {
12945
13707
  const clack = await Promise.resolve().then(() => (init_dist2(), exports_dist));
12946
13708
  const platform9 = process.platform;
12947
13709
  const scriptName = platform9 === "win32" ? "install.ps1" : "install.sh";
12948
- const scriptPath = join26(skillsDir, scriptName);
13710
+ const scriptPath = join27(skillsDir, scriptName);
12949
13711
  try {
12950
13712
  validateScriptPath(skillsDir, scriptPath);
12951
13713
  } catch (error) {
@@ -12961,7 +13723,7 @@ async function installSkillsDependencies(skillsDir) {
12961
13723
  logger.warning(`Skills installation script not found: ${scriptPath}`);
12962
13724
  logger.info("");
12963
13725
  logger.info("\uD83D\uDCD6 Manual Installation Instructions:");
12964
- logger.info(` See: ${join26(skillsDir, "INSTALLATION.md")}`);
13726
+ logger.info(` See: ${join27(skillsDir, "INSTALLATION.md")}`);
12965
13727
  logger.info("");
12966
13728
  logger.info("Quick start:");
12967
13729
  logger.info(" cd .claude/skills/ai-multimodal/scripts");
@@ -12977,8 +13739,8 @@ async function installSkillsDependencies(skillsDir) {
12977
13739
  logger.info(` Platform: ${platform9 === "win32" ? "Windows (PowerShell)" : "Unix (bash)"}`);
12978
13740
  if (logger.isVerbose()) {
12979
13741
  try {
12980
- const { readFile: readFile15 } = await import("node:fs/promises");
12981
- const scriptContent = await readFile15(scriptPath, "utf-8");
13742
+ const { readFile: readFile16 } = await import("node:fs/promises");
13743
+ const scriptContent = await readFile16(scriptPath, "utf-8");
12982
13744
  const previewLines = scriptContent.split(`
12983
13745
  `).slice(0, 20);
12984
13746
  logger.verbose("Script preview (first 20 lines):");
@@ -13004,7 +13766,7 @@ async function installSkillsDependencies(skillsDir) {
13004
13766
  logger.info(` ${platform9 === "win32" ? `powershell -File "${scriptPath}"` : `bash ${scriptPath}`}`);
13005
13767
  logger.info("");
13006
13768
  logger.info("Or see complete guide:");
13007
- logger.info(` ${join26(skillsDir, "INSTALLATION.md")}`);
13769
+ logger.info(` ${join27(skillsDir, "INSTALLATION.md")}`);
13008
13770
  return {
13009
13771
  success: false,
13010
13772
  package: displayName,
@@ -13105,7 +13867,7 @@ async function installSkillsDependencies(skillsDir) {
13105
13867
  logger.info("\uD83D\uDCD6 Manual Installation Instructions:");
13106
13868
  logger.info("");
13107
13869
  logger.info("See complete guide:");
13108
- logger.info(` cat ${join26(skillsDir, "INSTALLATION.md")}`);
13870
+ logger.info(` cat ${join27(skillsDir, "INSTALLATION.md")}`);
13109
13871
  logger.info("");
13110
13872
  logger.info("Quick start:");
13111
13873
  logger.info(" cd .claude/skills/ai-multimodal/scripts");
@@ -14099,7 +14861,7 @@ var init_help_interceptor = __esm(() => {
14099
14861
 
14100
14862
  // src/index.ts
14101
14863
  import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
14102
- import { join as join36 } from "path";
14864
+ import { join as join37 } from "path";
14103
14865
 
14104
14866
  // node_modules/cac/dist/index.mjs
14105
14867
  import { EventEmitter } from "events";
@@ -14704,7 +15466,7 @@ var cac = (name = "") => new CAC(name);
14704
15466
  // package.json
14705
15467
  var package_default = {
14706
15468
  name: "claudekit-cli",
14707
- version: "3.10.2",
15469
+ version: "3.11.0",
14708
15470
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
14709
15471
  type: "module",
14710
15472
  repository: {
@@ -14724,7 +15486,7 @@ var package_default = {
14724
15486
  ],
14725
15487
  scripts: {
14726
15488
  dev: "bun run src/index.ts",
14727
- build: "bun build src/index.ts --outdir dist --target node --external keytar --external @octokit/rest",
15489
+ build: "bun build src/index.ts --outdir dist --target node --external @octokit/rest",
14728
15490
  compile: "bun build src/index.ts --compile --outfile ck",
14729
15491
  "compile:binary": "bun build src/index.ts --compile --outfile bin/ck",
14730
15492
  "compile:binaries": "node scripts/build-all-binaries.js",
@@ -14765,11 +15527,11 @@ var package_default = {
14765
15527
  "extract-zip": "^2.0.1",
14766
15528
  "fs-extra": "^11.2.0",
14767
15529
  ignore: "^5.3.2",
14768
- keytar: "^7.9.0",
14769
15530
  minimatch: "^10.1.1",
14770
15531
  ora: "^8.0.0",
14771
15532
  "p-limit": "^7.2.0",
14772
15533
  picocolors: "^1.1.1",
15534
+ "proper-lockfile": "^4.1.2",
14773
15535
  tar: "^7.4.3",
14774
15536
  tmp: "^0.2.3",
14775
15537
  zod: "^3.23.8"
@@ -14782,6 +15544,7 @@ var package_default = {
14782
15544
  "@types/cli-progress": "^3.11.6",
14783
15545
  "@types/fs-extra": "^11.0.4",
14784
15546
  "@types/node": "^22.10.1",
15547
+ "@types/proper-lockfile": "^4.1.4",
14785
15548
  "@types/tar": "^6.1.13",
14786
15549
  "@types/tmp": "^0.2.6",
14787
15550
  "semantic-release": "^24.2.0",
@@ -18650,7 +19413,7 @@ async function doctorCommand(options = {}) {
18650
19413
  }
18651
19414
 
18652
19415
  // src/commands/init.ts
18653
- import { join as join31, resolve as resolve6 } from "node:path";
19416
+ import { join as join32, resolve as resolve6 } from "node:path";
18654
19417
 
18655
19418
  // src/domains/config/config-manager.ts
18656
19419
  init_logger();
@@ -27647,7 +28410,7 @@ Solutions:
27647
28410
  }
27648
28411
 
27649
28412
  // src/domains/installation/file-merger.ts
27650
- import { dirname as dirname5, join as join12, relative as relative2 } from "node:path";
28413
+ import { dirname as dirname5, join as join12, relative as relative3 } from "node:path";
27651
28414
 
27652
28415
  // src/domains/config/settings-merger.ts
27653
28416
  init_logger();
@@ -29293,7 +30056,130 @@ minimatch.Minimatch = Minimatch;
29293
30056
  minimatch.escape = escape;
29294
30057
  minimatch.unescape = unescape;
29295
30058
 
29296
- // src/domains/installation/file-merger.ts
30059
+ // src/domains/installation/selective-merger.ts
30060
+ import { stat as stat2 } from "node:fs/promises";
30061
+
30062
+ // src/services/file-operations/ownership-checker.ts
30063
+ import { createHash } from "node:crypto";
30064
+ import { createReadStream } from "node:fs";
30065
+ import { stat } from "node:fs/promises";
30066
+ import { relative as relative2 } from "node:path";
30067
+
30068
+ class OwnershipChecker {
30069
+ static async calculateChecksum(filePath) {
30070
+ return new Promise((resolve3, reject) => {
30071
+ const hash = createHash("sha256");
30072
+ const stream = createReadStream(filePath);
30073
+ stream.on("data", (chunk) => hash.update(chunk));
30074
+ stream.on("end", () => {
30075
+ resolve3(hash.digest("hex"));
30076
+ });
30077
+ stream.on("error", (err) => {
30078
+ stream.destroy();
30079
+ reject(new Error(`Failed to calculate checksum for "${filePath}": ${err.message}`));
30080
+ });
30081
+ });
30082
+ }
30083
+ static async checkOwnership(filePath, metadata, claudeDir) {
30084
+ try {
30085
+ await stat(filePath);
30086
+ } catch {
30087
+ return { path: filePath, ownership: "user", exists: false };
30088
+ }
30089
+ if (!metadata || !metadata.files || metadata.files.length === 0) {
30090
+ return { path: filePath, ownership: "user", exists: true };
30091
+ }
30092
+ const relativePath = relative2(claudeDir, filePath).replace(/\\/g, "/");
30093
+ const tracked = metadata.files.find((f3) => f3.path === relativePath);
30094
+ if (!tracked) {
30095
+ return { path: filePath, ownership: "user", exists: true };
30096
+ }
30097
+ const actualChecksum = await OwnershipChecker.calculateChecksum(filePath);
30098
+ if (actualChecksum === tracked.checksum) {
30099
+ return {
30100
+ path: filePath,
30101
+ ownership: "ck",
30102
+ expectedChecksum: tracked.checksum,
30103
+ actualChecksum,
30104
+ exists: true
30105
+ };
30106
+ }
30107
+ return {
30108
+ path: filePath,
30109
+ ownership: "ck-modified",
30110
+ expectedChecksum: tracked.checksum,
30111
+ actualChecksum,
30112
+ exists: true
30113
+ };
30114
+ }
30115
+ static async checkBatch(filePaths, metadata, claudeDir) {
30116
+ const results = await Promise.all(filePaths.map((path9) => OwnershipChecker.checkOwnership(path9, metadata, claudeDir)));
30117
+ return new Map(results.map((r2) => [r2.path, r2]));
30118
+ }
30119
+ }
30120
+
30121
+ // src/domains/installation/selective-merger.ts
30122
+ init_logger();
30123
+
30124
+ class SelectiveMerger {
30125
+ manifest;
30126
+ manifestMap;
30127
+ constructor(manifest) {
30128
+ this.manifest = manifest;
30129
+ this.manifestMap = new Map;
30130
+ if (manifest) {
30131
+ for (const file of manifest.files) {
30132
+ this.manifestMap.set(file.path, file);
30133
+ }
30134
+ }
30135
+ }
30136
+ async shouldCopyFile(destPath, relativePath) {
30137
+ let destStat;
30138
+ try {
30139
+ destStat = await stat2(destPath);
30140
+ } catch {
30141
+ return { changed: true, reason: "new" };
30142
+ }
30143
+ const manifestEntry = this.manifestMap.get(relativePath);
30144
+ if (!manifestEntry) {
30145
+ logger.debug(`No manifest entry for ${relativePath}, will copy`);
30146
+ return { changed: true, reason: "new" };
30147
+ }
30148
+ if (destStat.size !== manifestEntry.size) {
30149
+ logger.debug(`Size differs for ${relativePath}: ${destStat.size} vs ${manifestEntry.size}`);
30150
+ return {
30151
+ changed: true,
30152
+ reason: "size-differ",
30153
+ sourceChecksum: manifestEntry.checksum
30154
+ };
30155
+ }
30156
+ const destChecksum = await OwnershipChecker.calculateChecksum(destPath);
30157
+ if (destChecksum !== manifestEntry.checksum) {
30158
+ logger.debug(`Checksum differs for ${relativePath}`);
30159
+ return {
30160
+ changed: true,
30161
+ reason: "checksum-differ",
30162
+ sourceChecksum: manifestEntry.checksum,
30163
+ destChecksum
30164
+ };
30165
+ }
30166
+ logger.debug(`Unchanged: ${relativePath}`);
30167
+ return {
30168
+ changed: false,
30169
+ reason: "unchanged",
30170
+ sourceChecksum: manifestEntry.checksum,
30171
+ destChecksum
30172
+ };
30173
+ }
30174
+ hasManifest() {
30175
+ return this.manifest !== null && this.manifestMap.size > 0;
30176
+ }
30177
+ getManifestFileCount() {
30178
+ return this.manifestMap.size;
30179
+ }
30180
+ }
30181
+
30182
+ // src/domains/installation/file-merger.ts
29297
30183
  class FileMerger {
29298
30184
  neverCopyChecker = import_ignore2.default().add(NEVER_COPY_PATTERNS);
29299
30185
  userConfigChecker = import_ignore2.default().add(USER_CONFIG_PATTERNS);
@@ -29302,6 +30188,8 @@ class FileMerger {
29302
30188
  forceOverwriteSettings = false;
29303
30189
  installedFiles = new Set;
29304
30190
  installedDirectories = new Set;
30191
+ selectiveMerger = null;
30192
+ unchangedSkipped = 0;
29305
30193
  setIncludePatterns(patterns) {
29306
30194
  this.includePatterns = patterns;
29307
30195
  }
@@ -29311,6 +30199,12 @@ class FileMerger {
29311
30199
  setForceOverwriteSettings(force) {
29312
30200
  this.forceOverwriteSettings = force;
29313
30201
  }
30202
+ setManifest(manifest) {
30203
+ this.selectiveMerger = manifest ? new SelectiveMerger(manifest) : null;
30204
+ if (manifest && this.selectiveMerger?.hasManifest()) {
30205
+ logger.debug(`Selective merge enabled with ${this.selectiveMerger.getManifestFileCount()} tracked files`);
30206
+ }
30207
+ }
29314
30208
  async merge(sourceDir, destDir, skipConfirmation = false) {
29315
30209
  const conflicts = await this.detectConflicts(sourceDir, destDir);
29316
30210
  if (conflicts.length > 0 && !skipConfirmation) {
@@ -29332,7 +30226,7 @@ class FileMerger {
29332
30226
  const conflicts = [];
29333
30227
  const files = await this.getFiles(sourceDir, sourceDir);
29334
30228
  for (const file of files) {
29335
- const relativePath = relative2(sourceDir, file);
30229
+ const relativePath = relative3(sourceDir, file);
29336
30230
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
29337
30231
  const destPath = join12(destDir, relativePath);
29338
30232
  if (await import_fs_extra3.pathExists(destPath)) {
@@ -29354,7 +30248,7 @@ class FileMerger {
29354
30248
  let copiedCount = 0;
29355
30249
  let skippedCount = 0;
29356
30250
  for (const file of files) {
29357
- const relativePath = relative2(sourceDir, file);
30251
+ const relativePath = relative3(sourceDir, file);
29358
30252
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
29359
30253
  const destPath = join12(destDir, relativePath);
29360
30254
  if (this.neverCopyChecker.ignores(normalizedRelativePath)) {
@@ -29377,11 +30271,24 @@ class FileMerger {
29377
30271
  copiedCount++;
29378
30272
  continue;
29379
30273
  }
30274
+ if (this.selectiveMerger?.hasManifest()) {
30275
+ const compareResult = await this.selectiveMerger.shouldCopyFile(destPath, normalizedRelativePath);
30276
+ if (!compareResult.changed) {
30277
+ logger.debug(`Skipping unchanged: ${normalizedRelativePath}`);
30278
+ this.unchangedSkipped++;
30279
+ this.trackInstalledFile(normalizedRelativePath);
30280
+ continue;
30281
+ }
30282
+ }
29380
30283
  await import_fs_extra3.copy(file, destPath, { overwrite: true });
29381
30284
  this.trackInstalledFile(normalizedRelativePath);
29382
30285
  copiedCount++;
29383
30286
  }
29384
- logger.success(`Copied ${copiedCount} file(s), skipped ${skippedCount} protected file(s)`);
30287
+ if (this.unchangedSkipped > 0) {
30288
+ logger.success(`Updated ${copiedCount} file(s), skipped ${this.unchangedSkipped} unchanged, skipped ${skippedCount} protected`);
30289
+ } else {
30290
+ logger.success(`Copied ${copiedCount} file(s), skipped ${skippedCount} protected file(s)`);
30291
+ }
29385
30292
  }
29386
30293
  async processSettingsJson(sourceFile, destFile) {
29387
30294
  try {
@@ -29472,7 +30379,7 @@ class FileMerger {
29472
30379
  const entries = await import_fs_extra3.readdir(dir, { encoding: "utf8" });
29473
30380
  for (const entry of entries) {
29474
30381
  const fullPath = join12(dir, entry);
29475
- const relativePath = relative2(baseDir, fullPath);
30382
+ const relativePath = relative3(baseDir, fullPath);
29476
30383
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
29477
30384
  const stats = await import_fs_extra3.lstat(fullPath);
29478
30385
  if (stats.isSymbolicLink()) {
@@ -29731,14 +30638,165 @@ async function runSetupWizard(options) {
29731
30638
  }
29732
30639
 
29733
30640
  // src/domains/migration/legacy-migration.ts
29734
- import { readdir as readdir4, stat as stat2 } from "node:fs/promises";
29735
- import { join as join18, relative as relative4 } from "node:path";
30641
+ import { readdir as readdir4, stat as stat3 } from "node:fs/promises";
30642
+ import { join as join19, relative as relative4 } from "node:path";
29736
30643
 
29737
30644
  // src/services/file-operations/manifest-writer.ts
30645
+ import { join as join17 } from "node:path";
30646
+
30647
+ // src/domains/migration/metadata-migration.ts
29738
30648
  init_logger();
29739
- init_types2();
29740
30649
  var import_fs_extra7 = __toESM(require_lib(), 1);
29741
30650
  import { join as join16 } from "node:path";
30651
+ async function detectMetadataFormat(claudeDir) {
30652
+ const metadataPath = join16(claudeDir, "metadata.json");
30653
+ if (!await import_fs_extra7.pathExists(metadataPath)) {
30654
+ return { format: "none", metadata: null, detectedKit: null };
30655
+ }
30656
+ try {
30657
+ const content = await import_fs_extra7.readFile(metadataPath, "utf-8");
30658
+ const parsed = JSON.parse(content);
30659
+ if (parsed.kits && Object.keys(parsed.kits).length > 0) {
30660
+ const installedKits = Object.keys(parsed.kits);
30661
+ return {
30662
+ format: "multi-kit",
30663
+ metadata: parsed,
30664
+ detectedKit: installedKits[0] || null
30665
+ };
30666
+ }
30667
+ if (parsed.name || parsed.version || parsed.files) {
30668
+ let detectedKit = null;
30669
+ const nameToCheck = parsed.name || "";
30670
+ if (/\bengineer\b/i.test(nameToCheck)) {
30671
+ detectedKit = "engineer";
30672
+ } else if (/\bmarketing\b/i.test(nameToCheck)) {
30673
+ detectedKit = "marketing";
30674
+ } else {
30675
+ detectedKit = "engineer";
30676
+ }
30677
+ return { format: "legacy", metadata: parsed, detectedKit };
30678
+ }
30679
+ logger.warning("Metadata file exists but has unrecognized format (missing kits, name, version, or files)");
30680
+ return { format: "none", metadata: null, detectedKit: null };
30681
+ } catch (error) {
30682
+ logger.warning(`Failed to read metadata file (may be corrupted): ${error}`);
30683
+ return { format: "none", metadata: null, detectedKit: null };
30684
+ }
30685
+ }
30686
+ async function migrateToMultiKit(claudeDir) {
30687
+ const detection = await detectMetadataFormat(claudeDir);
30688
+ if (detection.format === "multi-kit") {
30689
+ return {
30690
+ success: true,
30691
+ migrated: false,
30692
+ fromFormat: "multi-kit",
30693
+ toFormat: "multi-kit"
30694
+ };
30695
+ }
30696
+ if (detection.format === "none") {
30697
+ return {
30698
+ success: true,
30699
+ migrated: false,
30700
+ fromFormat: "none",
30701
+ toFormat: "multi-kit"
30702
+ };
30703
+ }
30704
+ const metadataPath = join16(claudeDir, "metadata.json");
30705
+ const legacy = detection.metadata;
30706
+ if (!legacy) {
30707
+ return {
30708
+ success: false,
30709
+ migrated: false,
30710
+ fromFormat: "legacy",
30711
+ toFormat: "multi-kit",
30712
+ error: "Metadata exists but could not be read"
30713
+ };
30714
+ }
30715
+ const legacyKit = detection.detectedKit || "engineer";
30716
+ try {
30717
+ const kitMetadata = {
30718
+ version: legacy.version || "unknown",
30719
+ installedAt: legacy.installedAt || new Date().toISOString(),
30720
+ files: legacy.files || []
30721
+ };
30722
+ const multiKit = {
30723
+ kits: {
30724
+ [legacyKit]: kitMetadata
30725
+ },
30726
+ scope: legacy.scope,
30727
+ name: legacy.name,
30728
+ version: legacy.version,
30729
+ installedAt: legacy.installedAt,
30730
+ installedFiles: legacy.installedFiles,
30731
+ userConfigFiles: legacy.userConfigFiles,
30732
+ files: legacy.files
30733
+ };
30734
+ await import_fs_extra7.writeFile(metadataPath, JSON.stringify(multiKit, null, 2), "utf-8");
30735
+ logger.info(`Migrated metadata from legacy format to multi-kit (detected: ${legacyKit})`);
30736
+ return {
30737
+ success: true,
30738
+ migrated: true,
30739
+ fromFormat: "legacy",
30740
+ toFormat: "multi-kit"
30741
+ };
30742
+ } catch (error) {
30743
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
30744
+ logger.error(`Metadata migration failed: ${errorMsg}`);
30745
+ return {
30746
+ success: false,
30747
+ migrated: false,
30748
+ fromFormat: "legacy",
30749
+ toFormat: "multi-kit",
30750
+ error: errorMsg
30751
+ };
30752
+ }
30753
+ }
30754
+ function getKitMetadata(metadata, kit) {
30755
+ if (metadata.kits?.[kit]) {
30756
+ return metadata.kits[kit];
30757
+ }
30758
+ if (!metadata.kits && metadata.version) {
30759
+ return {
30760
+ version: metadata.version,
30761
+ installedAt: metadata.installedAt || "",
30762
+ files: metadata.files
30763
+ };
30764
+ }
30765
+ return null;
30766
+ }
30767
+ function getAllTrackedFiles(metadata) {
30768
+ if (metadata.kits) {
30769
+ const allFiles = [];
30770
+ for (const kit of Object.values(metadata.kits)) {
30771
+ if (kit.files) {
30772
+ allFiles.push(...kit.files);
30773
+ }
30774
+ }
30775
+ return allFiles;
30776
+ }
30777
+ return metadata.files || [];
30778
+ }
30779
+ function getInstalledKits(metadata) {
30780
+ if (metadata.kits) {
30781
+ return Object.keys(metadata.kits);
30782
+ }
30783
+ const nameToCheck = metadata.name || "";
30784
+ if (/\bengineer\b/i.test(nameToCheck)) {
30785
+ return ["engineer"];
30786
+ }
30787
+ if (/\bmarketing\b/i.test(nameToCheck)) {
30788
+ return ["marketing"];
30789
+ }
30790
+ if (metadata.version) {
30791
+ return ["engineer"];
30792
+ }
30793
+ return [];
30794
+ }
30795
+
30796
+ // src/services/file-operations/manifest-writer.ts
30797
+ init_logger();
30798
+ init_types2();
30799
+ var import_fs_extra8 = __toESM(require_lib(), 1);
29742
30800
 
29743
30801
  // node_modules/yocto-queue/index.js
29744
30802
  class Node2 {
@@ -29880,66 +30938,8 @@ function validateConcurrency(concurrency) {
29880
30938
  }
29881
30939
  }
29882
30940
 
29883
- // src/services/file-operations/ownership-checker.ts
29884
- import { createHash } from "node:crypto";
29885
- import { createReadStream } from "node:fs";
29886
- import { stat } from "node:fs/promises";
29887
- import { relative as relative3 } from "node:path";
29888
-
29889
- class OwnershipChecker {
29890
- static async calculateChecksum(filePath) {
29891
- return new Promise((resolve3, reject) => {
29892
- const hash = createHash("sha256");
29893
- const stream = createReadStream(filePath);
29894
- stream.on("data", (chunk) => hash.update(chunk));
29895
- stream.on("end", () => {
29896
- resolve3(hash.digest("hex"));
29897
- });
29898
- stream.on("error", (err) => {
29899
- stream.destroy();
29900
- reject(new Error(`Failed to calculate checksum for "${filePath}": ${err.message}`));
29901
- });
29902
- });
29903
- }
29904
- static async checkOwnership(filePath, metadata, claudeDir) {
29905
- try {
29906
- await stat(filePath);
29907
- } catch {
29908
- return { path: filePath, ownership: "user", exists: false };
29909
- }
29910
- if (!metadata || !metadata.files || metadata.files.length === 0) {
29911
- return { path: filePath, ownership: "user", exists: true };
29912
- }
29913
- const relativePath = relative3(claudeDir, filePath).replace(/\\/g, "/");
29914
- const tracked = metadata.files.find((f3) => f3.path === relativePath);
29915
- if (!tracked) {
29916
- return { path: filePath, ownership: "user", exists: true };
29917
- }
29918
- const actualChecksum = await OwnershipChecker.calculateChecksum(filePath);
29919
- if (actualChecksum === tracked.checksum) {
29920
- return {
29921
- path: filePath,
29922
- ownership: "ck",
29923
- expectedChecksum: tracked.checksum,
29924
- actualChecksum,
29925
- exists: true
29926
- };
29927
- }
29928
- return {
29929
- path: filePath,
29930
- ownership: "ck-modified",
29931
- expectedChecksum: tracked.checksum,
29932
- actualChecksum,
29933
- exists: true
29934
- };
29935
- }
29936
- static async checkBatch(filePaths, metadata, claudeDir) {
29937
- const results = await Promise.all(filePaths.map((path9) => OwnershipChecker.checkOwnership(path9, metadata, claudeDir)));
29938
- return new Map(results.map((r2) => [r2.path, r2]));
29939
- }
29940
- }
29941
-
29942
30941
  // src/services/file-operations/manifest-writer.ts
30942
+ var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
29943
30943
  class ManifestWriter {
29944
30944
  installedFiles = new Set;
29945
30945
  userConfigFiles = new Set;
@@ -29995,13 +30995,16 @@ class ManifestWriter {
29995
30995
  return false;
29996
30996
  }
29997
30997
  }));
29998
- let completed = 0;
29999
30998
  const progressInterval = Math.max(1, Math.floor(total / 20));
30000
- const results = await Promise.all(tasks.map(async (task) => {
30999
+ let reportedProgress = 0;
31000
+ const results = await Promise.all(tasks.map(async (task, index) => {
30001
31001
  const result = await task;
30002
- completed++;
31002
+ const completed = index + 1;
30003
31003
  if (completed % progressInterval === 0 || completed === total) {
30004
- onProgress?.(completed, total);
31004
+ if (completed > reportedProgress) {
31005
+ reportedProgress = completed;
31006
+ onProgress?.(completed, total);
31007
+ }
30005
31008
  }
30006
31009
  return result;
30007
31010
  }));
@@ -30015,39 +31018,70 @@ class ManifestWriter {
30015
31018
  getTrackedFiles() {
30016
31019
  return Array.from(this.trackedFiles.values()).sort((a3, b3) => a3.path.localeCompare(b3.path));
30017
31020
  }
30018
- async writeManifest(claudeDir, kitName, version, scope) {
30019
- const metadataPath = join16(claudeDir, "metadata.json");
30020
- let existingMetadata = {};
30021
- if (await import_fs_extra7.pathExists(metadataPath)) {
30022
- try {
30023
- const content = await import_fs_extra7.readFile(metadataPath, "utf-8");
30024
- existingMetadata = JSON.parse(content);
30025
- } catch (error) {
30026
- logger.debug(`Could not read existing metadata: ${error}`);
31021
+ async writeManifest(claudeDir, kitName, version, scope, kitType) {
31022
+ const metadataPath = join17(claudeDir, "metadata.json");
31023
+ const kit = kitType || (/\bmarketing\b/i.test(kitName) ? "marketing" : "engineer");
31024
+ await import_fs_extra8.ensureFile(metadataPath);
31025
+ let release = null;
31026
+ try {
31027
+ release = await import_proper_lockfile.lock(metadataPath, {
31028
+ retries: { retries: 5, minTimeout: 100, maxTimeout: 1000 },
31029
+ stale: 60000
31030
+ });
31031
+ logger.debug(`Acquired lock on ${metadataPath}`);
31032
+ const migrationResult = await migrateToMultiKit(claudeDir);
31033
+ if (!migrationResult.success) {
31034
+ logger.warning(`Metadata migration warning: ${migrationResult.error}`);
31035
+ }
31036
+ let existingMetadata = { kits: {} };
31037
+ if (await import_fs_extra8.pathExists(metadataPath)) {
31038
+ try {
31039
+ const content = await import_fs_extra8.readFile(metadataPath, "utf-8");
31040
+ const parsed = JSON.parse(content);
31041
+ if (parsed && typeof parsed === "object" && Object.keys(parsed).length > 0) {
31042
+ existingMetadata = parsed;
31043
+ }
31044
+ } catch (error) {
31045
+ logger.debug(`Could not read existing metadata: ${error}`);
31046
+ }
31047
+ }
31048
+ const trackedFiles = this.getTrackedFiles();
31049
+ const installedAt = new Date().toISOString();
31050
+ const kitMetadata = {
31051
+ version,
31052
+ installedAt,
31053
+ files: trackedFiles.length > 0 ? trackedFiles : undefined
31054
+ };
31055
+ const metadata = {
31056
+ kits: {
31057
+ ...existingMetadata.kits,
31058
+ [kit]: kitMetadata
31059
+ },
31060
+ scope,
31061
+ name: kitName,
31062
+ version,
31063
+ installedAt,
31064
+ installedFiles: this.getInstalledFiles(),
31065
+ userConfigFiles: [...USER_CONFIG_PATTERNS, ...this.getUserConfigFiles()],
31066
+ files: trackedFiles.length > 0 ? trackedFiles : undefined
31067
+ };
31068
+ const validated = MetadataSchema.parse(metadata);
31069
+ await import_fs_extra8.writeFile(metadataPath, JSON.stringify(validated, null, 2), "utf-8");
31070
+ logger.debug(`Wrote manifest for kit "${kit}" with ${trackedFiles.length} tracked files`);
31071
+ } finally {
31072
+ if (release) {
31073
+ await release();
31074
+ logger.debug(`Released lock on ${metadataPath}`);
30027
31075
  }
30028
31076
  }
30029
- const trackedFiles = this.getTrackedFiles();
30030
- const metadata = {
30031
- ...existingMetadata,
30032
- name: kitName,
30033
- version,
30034
- installedAt: new Date().toISOString(),
30035
- scope,
30036
- installedFiles: this.getInstalledFiles(),
30037
- userConfigFiles: [...USER_CONFIG_PATTERNS, ...this.getUserConfigFiles()],
30038
- files: trackedFiles.length > 0 ? trackedFiles : undefined
30039
- };
30040
- const validated = MetadataSchema.parse(metadata);
30041
- await import_fs_extra7.writeFile(metadataPath, JSON.stringify(validated, null, 2), "utf-8");
30042
- logger.debug(`Wrote manifest with ${this.installedFiles.size} installed files, ${trackedFiles.length} tracked`);
30043
31077
  }
30044
31078
  static async readManifest(claudeDir) {
30045
- const metadataPath = join16(claudeDir, "metadata.json");
30046
- if (!await import_fs_extra7.pathExists(metadataPath)) {
31079
+ const metadataPath = join17(claudeDir, "metadata.json");
31080
+ if (!await import_fs_extra8.pathExists(metadataPath)) {
30047
31081
  return null;
30048
31082
  }
30049
31083
  try {
30050
- const content = await import_fs_extra7.readFile(metadataPath, "utf-8");
31084
+ const content = await import_fs_extra8.readFile(metadataPath, "utf-8");
30051
31085
  const parsed = JSON.parse(content);
30052
31086
  return MetadataSchema.parse(parsed);
30053
31087
  } catch (error) {
@@ -30055,13 +31089,82 @@ class ManifestWriter {
30055
31089
  return null;
30056
31090
  }
30057
31091
  }
30058
- static async getUninstallManifest(claudeDir) {
31092
+ static async readKitManifest(claudeDir, kit) {
30059
31093
  const metadata = await ManifestWriter.readManifest(claudeDir);
30060
- if (metadata?.installedFiles && metadata.installedFiles.length > 0) {
31094
+ if (!metadata)
31095
+ return null;
31096
+ return getKitMetadata(metadata, kit);
31097
+ }
31098
+ static async getUninstallManifest(claudeDir, kit) {
31099
+ const detection = await detectMetadataFormat(claudeDir);
31100
+ if (detection.format === "multi-kit" && detection.metadata?.kits) {
31101
+ const installedKits = Object.keys(detection.metadata.kits);
31102
+ if (kit) {
31103
+ const kitMeta = detection.metadata.kits[kit];
31104
+ if (!kitMeta?.files) {
31105
+ return {
31106
+ filesToRemove: [],
31107
+ filesToPreserve: USER_CONFIG_PATTERNS,
31108
+ hasManifest: true,
31109
+ isMultiKit: true,
31110
+ remainingKits: installedKits.filter((k2) => k2 !== kit)
31111
+ };
31112
+ }
31113
+ const kitFiles = kitMeta.files.map((f3) => f3.path);
31114
+ const sharedFiles = new Set;
31115
+ for (const otherKit of installedKits) {
31116
+ if (otherKit !== kit) {
31117
+ const otherMeta = detection.metadata.kits[otherKit];
31118
+ if (otherMeta?.files) {
31119
+ for (const f3 of otherMeta.files) {
31120
+ sharedFiles.add(f3.path);
31121
+ }
31122
+ }
31123
+ }
31124
+ }
31125
+ const filesToRemove = kitFiles.filter((f3) => !sharedFiles.has(f3));
31126
+ const filesToPreserve = [
31127
+ ...USER_CONFIG_PATTERNS,
31128
+ ...kitFiles.filter((f3) => sharedFiles.has(f3))
31129
+ ];
31130
+ return {
31131
+ filesToRemove,
31132
+ filesToPreserve,
31133
+ hasManifest: true,
31134
+ isMultiKit: true,
31135
+ remainingKits: installedKits.filter((k2) => k2 !== kit)
31136
+ };
31137
+ }
31138
+ const allFiles = getAllTrackedFiles(detection.metadata);
31139
+ return {
31140
+ filesToRemove: allFiles.map((f3) => f3.path),
31141
+ filesToPreserve: USER_CONFIG_PATTERNS,
31142
+ hasManifest: true,
31143
+ isMultiKit: true,
31144
+ remainingKits: []
31145
+ };
31146
+ }
31147
+ if (detection.format === "legacy" && detection.metadata) {
31148
+ const legacyFiles2 = detection.metadata.files?.map((f3) => f3.path) || [];
31149
+ const installedFiles = detection.metadata.installedFiles || [];
31150
+ const hasFiles = legacyFiles2.length > 0 || installedFiles.length > 0;
31151
+ if (!hasFiles) {
31152
+ const legacyDirs2 = ["commands", "agents", "skills", "workflows", "hooks", "scripts"];
31153
+ const legacyFileList = ["metadata.json"];
31154
+ return {
31155
+ filesToRemove: [...legacyDirs2, ...legacyFileList],
31156
+ filesToPreserve: USER_CONFIG_PATTERNS,
31157
+ hasManifest: false,
31158
+ isMultiKit: false,
31159
+ remainingKits: []
31160
+ };
31161
+ }
30061
31162
  return {
30062
- filesToRemove: metadata.installedFiles,
30063
- filesToPreserve: metadata.userConfigFiles || USER_CONFIG_PATTERNS,
30064
- hasManifest: true
31163
+ filesToRemove: legacyFiles2.length > 0 ? legacyFiles2 : installedFiles,
31164
+ filesToPreserve: detection.metadata.userConfigFiles || USER_CONFIG_PATTERNS,
31165
+ hasManifest: true,
31166
+ isMultiKit: false,
31167
+ remainingKits: []
30065
31168
  };
30066
31169
  }
30067
31170
  const legacyDirs = ["commands", "agents", "skills", "workflows", "hooks", "scripts"];
@@ -30069,20 +31172,55 @@ class ManifestWriter {
30069
31172
  return {
30070
31173
  filesToRemove: [...legacyDirs, ...legacyFiles],
30071
31174
  filesToPreserve: USER_CONFIG_PATTERNS,
30072
- hasManifest: false
31175
+ hasManifest: false,
31176
+ isMultiKit: false,
31177
+ remainingKits: []
30073
31178
  };
30074
31179
  }
31180
+ static async removeKitFromManifest(claudeDir, kit) {
31181
+ const metadataPath = join17(claudeDir, "metadata.json");
31182
+ if (!await import_fs_extra8.pathExists(metadataPath))
31183
+ return false;
31184
+ let release = null;
31185
+ try {
31186
+ release = await import_proper_lockfile.lock(metadataPath, {
31187
+ retries: { retries: 5, minTimeout: 100, maxTimeout: 1000 },
31188
+ stale: 60000
31189
+ });
31190
+ logger.debug(`Acquired lock on ${metadataPath} for kit removal`);
31191
+ const metadata = await ManifestWriter.readManifest(claudeDir);
31192
+ if (!metadata?.kits?.[kit])
31193
+ return false;
31194
+ const { [kit]: _removed, ...remainingKits } = metadata.kits;
31195
+ if (Object.keys(remainingKits).length === 0) {
31196
+ logger.debug("No kits remaining, metadata.json will be cleaned up");
31197
+ return true;
31198
+ }
31199
+ const updated = {
31200
+ ...metadata,
31201
+ kits: remainingKits
31202
+ };
31203
+ await import_fs_extra8.writeFile(metadataPath, JSON.stringify(updated, null, 2), "utf-8");
31204
+ logger.debug(`Removed kit "${kit}" from metadata, ${Object.keys(remainingKits).length} kit(s) remaining`);
31205
+ return true;
31206
+ } finally {
31207
+ if (release) {
31208
+ await release();
31209
+ logger.debug(`Released lock on ${metadataPath}`);
31210
+ }
31211
+ }
31212
+ }
30075
31213
  }
30076
31214
 
30077
31215
  // src/domains/migration/legacy-migration.ts
30078
31216
  init_logger();
30079
- var import_fs_extra9 = __toESM(require_lib(), 1);
31217
+ var import_fs_extra10 = __toESM(require_lib(), 1);
30080
31218
 
30081
31219
  // src/domains/migration/release-manifest.ts
30082
31220
  init_logger();
30083
31221
  init_zod();
30084
- var import_fs_extra8 = __toESM(require_lib(), 1);
30085
- import { join as join17 } from "node:path";
31222
+ var import_fs_extra9 = __toESM(require_lib(), 1);
31223
+ import { join as join18 } from "node:path";
30086
31224
  var ReleaseManifestFileSchema = exports_external.object({
30087
31225
  path: exports_external.string(),
30088
31226
  checksum: exports_external.string().regex(/^[a-f0-9]{64}$/),
@@ -30096,9 +31234,9 @@ var ReleaseManifestSchema = exports_external.object({
30096
31234
 
30097
31235
  class ReleaseManifestLoader {
30098
31236
  static async load(extractDir) {
30099
- const manifestPath = join17(extractDir, "release-manifest.json");
31237
+ const manifestPath = join18(extractDir, "release-manifest.json");
30100
31238
  try {
30101
- const content = await import_fs_extra8.readFile(manifestPath, "utf-8");
31239
+ const content = await import_fs_extra9.readFile(manifestPath, "utf-8");
30102
31240
  const parsed = JSON.parse(content);
30103
31241
  return ReleaseManifestSchema.parse(parsed);
30104
31242
  } catch (error) {
@@ -30142,10 +31280,10 @@ class LegacyMigration {
30142
31280
  for (const entry of entries) {
30143
31281
  if (entry === "metadata.json")
30144
31282
  continue;
30145
- const fullPath = join18(dir, entry);
31283
+ const fullPath = join19(dir, entry);
30146
31284
  let stats;
30147
31285
  try {
30148
- stats = await stat2(fullPath);
31286
+ stats = await stat3(fullPath);
30149
31287
  } catch (err) {
30150
31288
  const error = err;
30151
31289
  if (error.code === "ENOENT") {
@@ -30244,7 +31382,7 @@ User-created files (sample):`);
30244
31382
  ];
30245
31383
  if (filesToChecksum.length > 0) {
30246
31384
  const checksumResults = await Promise.all(filesToChecksum.map(async ({ relativePath, ownership }) => {
30247
- const fullPath = join18(claudeDir, relativePath);
31385
+ const fullPath = join19(claudeDir, relativePath);
30248
31386
  const checksum = await OwnershipChecker.calculateChecksum(fullPath);
30249
31387
  return { relativePath, checksum, ownership };
30250
31388
  }));
@@ -30265,8 +31403,8 @@ User-created files (sample):`);
30265
31403
  installedAt: new Date().toISOString(),
30266
31404
  files: trackedFiles
30267
31405
  };
30268
- const metadataPath = join18(claudeDir, "metadata.json");
30269
- await import_fs_extra9.writeFile(metadataPath, JSON.stringify(updatedMetadata, null, 2));
31406
+ const metadataPath = join19(claudeDir, "metadata.json");
31407
+ await import_fs_extra10.writeFile(metadataPath, JSON.stringify(updatedMetadata, null, 2));
30270
31408
  logger.success(`Migration complete: tracked ${trackedFiles.length} files`);
30271
31409
  return true;
30272
31410
  }
@@ -30274,24 +31412,24 @@ User-created files (sample):`);
30274
31412
 
30275
31413
  // src/domains/skills/skills-detector.ts
30276
31414
  init_logger();
30277
- var import_fs_extra11 = __toESM(require_lib(), 1);
31415
+ var import_fs_extra12 = __toESM(require_lib(), 1);
30278
31416
  import { readdir as readdir6 } from "node:fs/promises";
30279
- import { join as join20 } from "node:path";
31417
+ import { join as join21 } from "node:path";
30280
31418
 
30281
31419
  // src/domains/skills/skills-manifest.ts
30282
31420
  init_logger();
30283
31421
  init_types2();
30284
- var import_fs_extra10 = __toESM(require_lib(), 1);
31422
+ var import_fs_extra11 = __toESM(require_lib(), 1);
30285
31423
  import { createHash as createHash2 } from "node:crypto";
30286
- import { readFile as readFile12, readdir as readdir5, writeFile as writeFile11 } from "node:fs/promises";
30287
- import { join as join19, relative as relative5 } from "node:path";
31424
+ import { readFile as readFile13, readdir as readdir5, writeFile as writeFile12 } from "node:fs/promises";
31425
+ import { join as join20, relative as relative5 } from "node:path";
30288
31426
 
30289
31427
  class SkillsManifestManager {
30290
31428
  static MANIFEST_FILENAME = ".skills-manifest.json";
30291
31429
  static MANIFEST_VERSION = "1.0.0";
30292
31430
  static async generateManifest(skillsDir) {
30293
31431
  logger.debug(`Generating manifest for: ${skillsDir}`);
30294
- if (!await import_fs_extra10.pathExists(skillsDir)) {
31432
+ if (!await import_fs_extra11.pathExists(skillsDir)) {
30295
31433
  throw new SkillsMigrationError(`Skills directory does not exist: ${skillsDir}`);
30296
31434
  }
30297
31435
  const structure = await SkillsManifestManager.detectStructure(skillsDir);
@@ -30306,18 +31444,18 @@ class SkillsManifestManager {
30306
31444
  return manifest;
30307
31445
  }
30308
31446
  static async writeManifest(skillsDir, manifest) {
30309
- const manifestPath = join19(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
30310
- await writeFile11(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
31447
+ const manifestPath = join20(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
31448
+ await writeFile12(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
30311
31449
  logger.debug(`Wrote manifest to: ${manifestPath}`);
30312
31450
  }
30313
31451
  static async readManifest(skillsDir) {
30314
- const manifestPath = join19(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
30315
- if (!await import_fs_extra10.pathExists(manifestPath)) {
31452
+ const manifestPath = join20(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
31453
+ if (!await import_fs_extra11.pathExists(manifestPath)) {
30316
31454
  logger.debug(`No manifest found at: ${manifestPath}`);
30317
31455
  return null;
30318
31456
  }
30319
31457
  try {
30320
- const content = await readFile12(manifestPath, "utf-8");
31458
+ const content = await readFile13(manifestPath, "utf-8");
30321
31459
  const data = JSON.parse(content);
30322
31460
  const manifest = SkillsManifestSchema.parse(data);
30323
31461
  logger.debug(`Read manifest from: ${manifestPath}`);
@@ -30334,7 +31472,7 @@ class SkillsManifestManager {
30334
31472
  return "flat";
30335
31473
  }
30336
31474
  for (const dir of dirs.slice(0, 3)) {
30337
- const dirPath = join19(skillsDir, dir.name);
31475
+ const dirPath = join20(skillsDir, dir.name);
30338
31476
  const subEntries = await readdir5(dirPath, { withFileTypes: true });
30339
31477
  const hasSubdirs = subEntries.some((entry) => entry.isDirectory());
30340
31478
  if (hasSubdirs) {
@@ -30353,7 +31491,7 @@ class SkillsManifestManager {
30353
31491
  const entries = await readdir5(skillsDir, { withFileTypes: true });
30354
31492
  for (const entry of entries) {
30355
31493
  if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) {
30356
- const skillPath = join19(skillsDir, entry.name);
31494
+ const skillPath = join20(skillsDir, entry.name);
30357
31495
  const hash = await SkillsManifestManager.hashDirectory(skillPath);
30358
31496
  skills.push({
30359
31497
  name: entry.name,
@@ -30365,11 +31503,11 @@ class SkillsManifestManager {
30365
31503
  const categories = await readdir5(skillsDir, { withFileTypes: true });
30366
31504
  for (const category of categories) {
30367
31505
  if (category.isDirectory() && category.name !== "node_modules" && !category.name.startsWith(".")) {
30368
- const categoryPath = join19(skillsDir, category.name);
31506
+ const categoryPath = join20(skillsDir, category.name);
30369
31507
  const skillEntries = await readdir5(categoryPath, { withFileTypes: true });
30370
31508
  for (const skillEntry of skillEntries) {
30371
31509
  if (skillEntry.isDirectory() && !skillEntry.name.startsWith(".")) {
30372
- const skillPath = join19(categoryPath, skillEntry.name);
31510
+ const skillPath = join20(categoryPath, skillEntry.name);
30373
31511
  const hash = await SkillsManifestManager.hashDirectory(skillPath);
30374
31512
  skills.push({
30375
31513
  name: skillEntry.name,
@@ -30389,7 +31527,7 @@ class SkillsManifestManager {
30389
31527
  files.sort();
30390
31528
  for (const file of files) {
30391
31529
  const relativePath = relative5(dirPath, file);
30392
- const content = await readFile12(file);
31530
+ const content = await readFile13(file);
30393
31531
  hash.update(relativePath);
30394
31532
  hash.update(content);
30395
31533
  }
@@ -30399,7 +31537,7 @@ class SkillsManifestManager {
30399
31537
  const files = [];
30400
31538
  const entries = await readdir5(dirPath, { withFileTypes: true });
30401
31539
  for (const entry of entries) {
30402
- const fullPath = join19(dirPath, entry.name);
31540
+ const fullPath = join20(dirPath, entry.name);
30403
31541
  if (entry.name.startsWith(".") || entry.name === "node_modules") {
30404
31542
  continue;
30405
31543
  }
@@ -30519,8 +31657,8 @@ function getPathMapping(skillName, oldBasePath, newBasePath) {
30519
31657
  class SkillsMigrationDetector {
30520
31658
  static async detectMigration(oldSkillsDir, currentSkillsDir) {
30521
31659
  logger.debug("Detecting skills migration need...");
30522
- const oldExists = await import_fs_extra11.pathExists(oldSkillsDir);
30523
- const currentExists = await import_fs_extra11.pathExists(currentSkillsDir);
31660
+ const oldExists = await import_fs_extra12.pathExists(oldSkillsDir);
31661
+ const currentExists = await import_fs_extra12.pathExists(currentSkillsDir);
30524
31662
  if (!oldExists && !currentExists) {
30525
31663
  logger.debug("No skills directories found, migration not needed");
30526
31664
  return {
@@ -30631,7 +31769,7 @@ class SkillsMigrationDetector {
30631
31769
  };
30632
31770
  }
30633
31771
  static async scanDirectory(skillsDir) {
30634
- if (!await import_fs_extra11.pathExists(skillsDir)) {
31772
+ if (!await import_fs_extra12.pathExists(skillsDir)) {
30635
31773
  return ["flat", []];
30636
31774
  }
30637
31775
  const entries = await readdir6(skillsDir, { withFileTypes: true });
@@ -30642,12 +31780,12 @@ class SkillsMigrationDetector {
30642
31780
  let totalSkillLikeCount = 0;
30643
31781
  const allSkills = [];
30644
31782
  for (const dir of dirs) {
30645
- const dirPath = join20(skillsDir, dir.name);
31783
+ const dirPath = join21(skillsDir, dir.name);
30646
31784
  const subEntries = await readdir6(dirPath, { withFileTypes: true });
30647
31785
  const subdirs = subEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
30648
31786
  if (subdirs.length > 0) {
30649
31787
  for (const subdir of subdirs.slice(0, 3)) {
30650
- const subdirPath = join20(dirPath, subdir.name);
31788
+ const subdirPath = join21(dirPath, subdir.name);
30651
31789
  const subdirFiles = await readdir6(subdirPath, { withFileTypes: true });
30652
31790
  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"));
30653
31791
  if (hasSkillMarker) {
@@ -30684,16 +31822,16 @@ class SkillsMigrationDetector {
30684
31822
  // src/domains/skills/skills-migrator.ts
30685
31823
  init_logger();
30686
31824
  init_types2();
30687
- var import_fs_extra14 = __toESM(require_lib(), 1);
31825
+ var import_fs_extra15 = __toESM(require_lib(), 1);
30688
31826
  import { copyFile as copyFile2, mkdir as mkdir8, readdir as readdir9, rm as rm3 } from "node:fs/promises";
30689
- import { join as join23 } from "node:path";
31827
+ import { join as join24 } from "node:path";
30690
31828
 
30691
31829
  // src/domains/skills/skills-backup-manager.ts
30692
31830
  init_logger();
30693
31831
  init_types2();
30694
- var import_fs_extra12 = __toESM(require_lib(), 1);
30695
- import { copyFile, mkdir as mkdir7, readdir as readdir7, rm as rm2, stat as stat3 } from "node:fs/promises";
30696
- import { basename as basename2, join as join21, normalize as normalize4 } from "node:path";
31832
+ var import_fs_extra13 = __toESM(require_lib(), 1);
31833
+ import { copyFile, mkdir as mkdir7, readdir as readdir7, rm as rm2, stat as stat4 } from "node:fs/promises";
31834
+ import { basename as basename2, join as join22, normalize as normalize4 } from "node:path";
30697
31835
  function validatePath(path9, paramName) {
30698
31836
  if (!path9 || typeof path9 !== "string") {
30699
31837
  throw new SkillsMigrationError(`${paramName} must be a non-empty string`);
@@ -30713,13 +31851,13 @@ class SkillsBackupManager {
30713
31851
  if (parentDir) {
30714
31852
  validatePath(parentDir, "parentDir");
30715
31853
  }
30716
- if (!await import_fs_extra12.pathExists(skillsDir)) {
31854
+ if (!await import_fs_extra13.pathExists(skillsDir)) {
30717
31855
  throw new SkillsMigrationError(`Cannot create backup: Skills directory does not exist: ${skillsDir}`);
30718
31856
  }
30719
31857
  const timestamp = Date.now();
30720
31858
  const randomSuffix = Math.random().toString(36).substring(2, 8);
30721
31859
  const backupDirName = `${SkillsBackupManager.BACKUP_PREFIX}${timestamp}-${randomSuffix}`;
30722
- const backupDir = parentDir ? join21(parentDir, backupDirName) : join21(skillsDir, "..", backupDirName);
31860
+ const backupDir = parentDir ? join22(parentDir, backupDirName) : join22(skillsDir, "..", backupDirName);
30723
31861
  logger.info(`Creating backup at: ${backupDir}`);
30724
31862
  try {
30725
31863
  await mkdir7(backupDir, { recursive: true });
@@ -30736,12 +31874,12 @@ class SkillsBackupManager {
30736
31874
  static async restoreBackup(backupDir, targetDir) {
30737
31875
  validatePath(backupDir, "backupDir");
30738
31876
  validatePath(targetDir, "targetDir");
30739
- if (!await import_fs_extra12.pathExists(backupDir)) {
31877
+ if (!await import_fs_extra13.pathExists(backupDir)) {
30740
31878
  throw new SkillsMigrationError(`Cannot restore: Backup directory does not exist: ${backupDir}`);
30741
31879
  }
30742
31880
  logger.info(`Restoring from backup: ${backupDir}`);
30743
31881
  try {
30744
- if (await import_fs_extra12.pathExists(targetDir)) {
31882
+ if (await import_fs_extra13.pathExists(targetDir)) {
30745
31883
  await rm2(targetDir, { recursive: true, force: true });
30746
31884
  }
30747
31885
  await mkdir7(targetDir, { recursive: true });
@@ -30752,7 +31890,7 @@ class SkillsBackupManager {
30752
31890
  }
30753
31891
  }
30754
31892
  static async deleteBackup(backupDir) {
30755
- if (!await import_fs_extra12.pathExists(backupDir)) {
31893
+ if (!await import_fs_extra13.pathExists(backupDir)) {
30756
31894
  logger.warning(`Backup directory does not exist: ${backupDir}`);
30757
31895
  return;
30758
31896
  }
@@ -30765,12 +31903,12 @@ class SkillsBackupManager {
30765
31903
  }
30766
31904
  }
30767
31905
  static async listBackups(parentDir) {
30768
- if (!await import_fs_extra12.pathExists(parentDir)) {
31906
+ if (!await import_fs_extra13.pathExists(parentDir)) {
30769
31907
  return [];
30770
31908
  }
30771
31909
  try {
30772
31910
  const entries = await readdir7(parentDir, { withFileTypes: true });
30773
- const backups = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(SkillsBackupManager.BACKUP_PREFIX)).map((entry) => join21(parentDir, entry.name));
31911
+ const backups = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(SkillsBackupManager.BACKUP_PREFIX)).map((entry) => join22(parentDir, entry.name));
30774
31912
  backups.sort().reverse();
30775
31913
  return backups;
30776
31914
  } catch (error) {
@@ -30790,7 +31928,7 @@ class SkillsBackupManager {
30790
31928
  }
30791
31929
  }
30792
31930
  static async getBackupSize(backupDir) {
30793
- if (!await import_fs_extra12.pathExists(backupDir)) {
31931
+ if (!await import_fs_extra13.pathExists(backupDir)) {
30794
31932
  return 0;
30795
31933
  }
30796
31934
  return await SkillsBackupManager.getDirectorySize(backupDir);
@@ -30798,8 +31936,8 @@ class SkillsBackupManager {
30798
31936
  static async copyDirectory(sourceDir, destDir) {
30799
31937
  const entries = await readdir7(sourceDir, { withFileTypes: true });
30800
31938
  for (const entry of entries) {
30801
- const sourcePath = join21(sourceDir, entry.name);
30802
- const destPath = join21(destDir, entry.name);
31939
+ const sourcePath = join22(sourceDir, entry.name);
31940
+ const destPath = join22(destDir, entry.name);
30803
31941
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
30804
31942
  continue;
30805
31943
  }
@@ -30815,14 +31953,14 @@ class SkillsBackupManager {
30815
31953
  let size = 0;
30816
31954
  const entries = await readdir7(dirPath, { withFileTypes: true });
30817
31955
  for (const entry of entries) {
30818
- const fullPath = join21(dirPath, entry.name);
31956
+ const fullPath = join22(dirPath, entry.name);
30819
31957
  if (entry.isSymbolicLink()) {
30820
31958
  continue;
30821
31959
  }
30822
31960
  if (entry.isDirectory()) {
30823
31961
  size += await SkillsBackupManager.getDirectorySize(fullPath);
30824
31962
  } else if (entry.isFile()) {
30825
- const stats = await stat3(fullPath);
31963
+ const stats = await stat4(fullPath);
30826
31964
  size += stats.size;
30827
31965
  }
30828
31966
  }
@@ -30842,11 +31980,11 @@ class SkillsBackupManager {
30842
31980
  // src/domains/skills/skills-customization-scanner.ts
30843
31981
  init_logger();
30844
31982
  init_types2();
30845
- var import_fs_extra13 = __toESM(require_lib(), 1);
31983
+ var import_fs_extra14 = __toESM(require_lib(), 1);
30846
31984
  import { createHash as createHash3 } from "node:crypto";
30847
31985
  import { createReadStream as createReadStream2 } from "node:fs";
30848
- import { readFile as readFile13, readdir as readdir8 } from "node:fs/promises";
30849
- import { join as join22, normalize as normalize5, relative as relative6 } from "node:path";
31986
+ import { readFile as readFile14, readdir as readdir8 } from "node:fs/promises";
31987
+ import { join as join23, normalize as normalize5, relative as relative6 } from "node:path";
30850
31988
  function validatePath2(path9, paramName) {
30851
31989
  if (!path9 || typeof path9 !== "string") {
30852
31990
  throw new SkillsMigrationError(`${paramName} must be a non-empty string`);
@@ -30925,7 +32063,7 @@ class SkillsCustomizationScanner {
30925
32063
  static async detectFileChanges(currentSkillPath, baselineSkillPath) {
30926
32064
  const changes = [];
30927
32065
  const currentFiles = await SkillsCustomizationScanner.getAllFiles(currentSkillPath);
30928
- const baselineFiles = await import_fs_extra13.pathExists(baselineSkillPath) ? await SkillsCustomizationScanner.getAllFiles(baselineSkillPath) : [];
32066
+ const baselineFiles = await import_fs_extra14.pathExists(baselineSkillPath) ? await SkillsCustomizationScanner.getAllFiles(baselineSkillPath) : [];
30929
32067
  const currentFileMap = new Map(await Promise.all(currentFiles.map(async (f3) => {
30930
32068
  const relPath = relative6(currentSkillPath, f3);
30931
32069
  const hash = await SkillsCustomizationScanner.hashFile(f3);
@@ -30985,7 +32123,7 @@ class SkillsCustomizationScanner {
30985
32123
  return false;
30986
32124
  }
30987
32125
  static async scanSkillsDirectory(skillsDir) {
30988
- if (!await import_fs_extra13.pathExists(skillsDir)) {
32126
+ if (!await import_fs_extra14.pathExists(skillsDir)) {
30989
32127
  return ["flat", []];
30990
32128
  }
30991
32129
  const entries = await readdir8(skillsDir, { withFileTypes: true });
@@ -30993,13 +32131,13 @@ class SkillsCustomizationScanner {
30993
32131
  if (dirs.length === 0) {
30994
32132
  return ["flat", []];
30995
32133
  }
30996
- const firstDirPath = join22(skillsDir, dirs[0].name);
32134
+ const firstDirPath = join23(skillsDir, dirs[0].name);
30997
32135
  const subEntries = await readdir8(firstDirPath, { withFileTypes: true });
30998
32136
  const subdirs = subEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
30999
32137
  if (subdirs.length > 0) {
31000
32138
  let skillLikeCount = 0;
31001
32139
  for (const subdir of subdirs.slice(0, 3)) {
31002
- const subdirPath = join22(firstDirPath, subdir.name);
32140
+ const subdirPath = join23(firstDirPath, subdir.name);
31003
32141
  const subdirFiles = await readdir8(subdirPath, { withFileTypes: true });
31004
32142
  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"));
31005
32143
  if (hasSkillMarker) {
@@ -31009,7 +32147,7 @@ class SkillsCustomizationScanner {
31009
32147
  if (skillLikeCount > 0) {
31010
32148
  const skills = [];
31011
32149
  for (const dir of dirs) {
31012
- const categoryPath = join22(skillsDir, dir.name);
32150
+ const categoryPath = join23(skillsDir, dir.name);
31013
32151
  const skillDirs = await readdir8(categoryPath, { withFileTypes: true });
31014
32152
  skills.push(...skillDirs.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name));
31015
32153
  }
@@ -31019,8 +32157,8 @@ class SkillsCustomizationScanner {
31019
32157
  return ["flat", dirs.map((dir) => dir.name)];
31020
32158
  }
31021
32159
  static async findSkillPath(skillsDir, skillName) {
31022
- const flatPath = join22(skillsDir, skillName);
31023
- if (await import_fs_extra13.pathExists(flatPath)) {
32160
+ const flatPath = join23(skillsDir, skillName);
32161
+ if (await import_fs_extra14.pathExists(flatPath)) {
31024
32162
  return { path: flatPath, category: undefined };
31025
32163
  }
31026
32164
  const entries = await readdir8(skillsDir, { withFileTypes: true });
@@ -31028,9 +32166,9 @@ class SkillsCustomizationScanner {
31028
32166
  if (!entry.isDirectory() || entry.name.startsWith(".") || entry.name === "node_modules") {
31029
32167
  continue;
31030
32168
  }
31031
- const categoryPath = join22(skillsDir, entry.name);
31032
- const skillPath = join22(categoryPath, skillName);
31033
- if (await import_fs_extra13.pathExists(skillPath)) {
32169
+ const categoryPath = join23(skillsDir, entry.name);
32170
+ const skillPath = join23(categoryPath, skillName);
32171
+ if (await import_fs_extra14.pathExists(skillPath)) {
31034
32172
  return { path: skillPath, category: entry.name };
31035
32173
  }
31036
32174
  }
@@ -31040,7 +32178,7 @@ class SkillsCustomizationScanner {
31040
32178
  const files = [];
31041
32179
  const entries = await readdir8(dirPath, { withFileTypes: true });
31042
32180
  for (const entry of entries) {
31043
- const fullPath = join22(dirPath, entry.name);
32181
+ const fullPath = join23(dirPath, entry.name);
31044
32182
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
31045
32183
  continue;
31046
32184
  }
@@ -31073,7 +32211,7 @@ class SkillsCustomizationScanner {
31073
32211
  files.sort();
31074
32212
  for (const file of files) {
31075
32213
  const relativePath = relative6(dirPath, file);
31076
- const content = await readFile13(file);
32214
+ const content = await readFile14(file);
31077
32215
  hash.update(relativePath);
31078
32216
  hash.update(content);
31079
32217
  }
@@ -31283,7 +32421,7 @@ class SkillsMigrator {
31283
32421
  }
31284
32422
  }
31285
32423
  if (options.backup && !options.dryRun) {
31286
- const claudeDir = join23(currentSkillsDir, "..");
32424
+ const claudeDir = join24(currentSkillsDir, "..");
31287
32425
  result.backupPath = await SkillsBackupManager.createBackup(currentSkillsDir, claudeDir);
31288
32426
  logger.success(`Backup created at: ${result.backupPath}`);
31289
32427
  }
@@ -31335,14 +32473,14 @@ class SkillsMigrator {
31335
32473
  const migrated = [];
31336
32474
  const preserved = [];
31337
32475
  const errors2 = [];
31338
- const tempDir = join23(currentSkillsDir, "..", ".skills-migration-temp");
32476
+ const tempDir = join24(currentSkillsDir, "..", ".skills-migration-temp");
31339
32477
  await mkdir8(tempDir, { recursive: true });
31340
32478
  try {
31341
32479
  for (const mapping of mappings) {
31342
32480
  try {
31343
32481
  const skillName = mapping.skillName;
31344
32482
  const currentSkillPath = mapping.oldPath;
31345
- if (!await import_fs_extra14.pathExists(currentSkillPath)) {
32483
+ if (!await import_fs_extra15.pathExists(currentSkillPath)) {
31346
32484
  logger.warning(`Skill not found, skipping: ${skillName}`);
31347
32485
  continue;
31348
32486
  }
@@ -31356,9 +32494,9 @@ class SkillsMigrator {
31356
32494
  }
31357
32495
  }
31358
32496
  const category = mapping.category;
31359
- const targetPath = category ? join23(tempDir, category, skillName) : join23(tempDir, skillName);
32497
+ const targetPath = category ? join24(tempDir, category, skillName) : join24(tempDir, skillName);
31360
32498
  if (category) {
31361
- await mkdir8(join23(tempDir, category), { recursive: true });
32499
+ await mkdir8(join24(tempDir, category), { recursive: true });
31362
32500
  }
31363
32501
  await SkillsMigrator.copySkillDirectory(currentSkillPath, targetPath);
31364
32502
  migrated.push(skillName);
@@ -31392,8 +32530,8 @@ class SkillsMigrator {
31392
32530
  await mkdir8(destDir, { recursive: true });
31393
32531
  const entries = await readdir9(sourceDir, { withFileTypes: true });
31394
32532
  for (const entry of entries) {
31395
- const sourcePath = join23(sourceDir, entry.name);
31396
- const destPath = join23(destDir, entry.name);
32533
+ const sourcePath = join24(sourceDir, entry.name);
32534
+ const destPath = join24(destDir, entry.name);
31397
32535
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
31398
32536
  continue;
31399
32537
  }
@@ -31981,31 +33119,31 @@ class PromptsManager {
31981
33119
 
31982
33120
  // src/services/file-operations/file-scanner.ts
31983
33121
  init_logger();
31984
- import { join as join27, relative as relative7, resolve as resolve5 } from "node:path";
31985
- var import_fs_extra15 = __toESM(require_lib(), 1);
33122
+ import { join as join28, relative as relative7, resolve as resolve5 } from "node:path";
33123
+ var import_fs_extra16 = __toESM(require_lib(), 1);
31986
33124
 
31987
33125
  class FileScanner {
31988
33126
  static async getFiles(dirPath, relativeTo) {
31989
33127
  const basePath = relativeTo || dirPath;
31990
33128
  const files = [];
31991
- if (!await import_fs_extra15.pathExists(dirPath)) {
33129
+ if (!await import_fs_extra16.pathExists(dirPath)) {
31992
33130
  return files;
31993
33131
  }
31994
33132
  try {
31995
- const entries = await import_fs_extra15.readdir(dirPath, { encoding: "utf8" });
33133
+ const entries = await import_fs_extra16.readdir(dirPath, { encoding: "utf8" });
31996
33134
  for (const entry of entries) {
31997
33135
  if (SKIP_DIRS_ALL.includes(entry)) {
31998
33136
  logger.debug(`Skipping directory: ${entry}`);
31999
33137
  continue;
32000
33138
  }
32001
- const fullPath = join27(dirPath, entry);
33139
+ const fullPath = join28(dirPath, entry);
32002
33140
  if (!FileScanner.isSafePath(basePath, fullPath)) {
32003
33141
  logger.warning(`Skipping potentially unsafe path: ${entry}`);
32004
33142
  continue;
32005
33143
  }
32006
33144
  let stats;
32007
33145
  try {
32008
- stats = await import_fs_extra15.lstat(fullPath);
33146
+ stats = await import_fs_extra16.lstat(fullPath);
32009
33147
  } catch (error) {
32010
33148
  if (error instanceof Error && "code" in error && (error.code === "EACCES" || error.code === "EPERM")) {
32011
33149
  logger.warning(`Skipping inaccessible path: ${entry}`);
@@ -32033,8 +33171,8 @@ class FileScanner {
32033
33171
  return files;
32034
33172
  }
32035
33173
  static async findCustomFiles(destDir, sourceDir, subPath) {
32036
- const destSubDir = join27(destDir, subPath);
32037
- const sourceSubDir = join27(sourceDir, subPath);
33174
+ const destSubDir = join28(destDir, subPath);
33175
+ const sourceSubDir = join28(sourceDir, subPath);
32038
33176
  logger.debug(`findCustomFiles - destDir: ${destDir}`);
32039
33177
  logger.debug(`findCustomFiles - sourceDir: ${sourceDir}`);
32040
33178
  logger.debug(`findCustomFiles - subPath: "${subPath}"`);
@@ -32044,7 +33182,7 @@ class FileScanner {
32044
33182
  const sourceFiles = await FileScanner.getFiles(sourceSubDir, sourceDir);
32045
33183
  logger.debug(`findCustomFiles - destFiles count: ${destFiles.length}`);
32046
33184
  logger.debug(`findCustomFiles - sourceFiles count: ${sourceFiles.length}`);
32047
- const sourceExists = await import_fs_extra15.pathExists(sourceSubDir);
33185
+ const sourceExists = await import_fs_extra16.pathExists(sourceSubDir);
32048
33186
  if (sourceExists && sourceFiles.length === 0 && destFiles.length > 100) {
32049
33187
  logger.warning(`Source directory exists but is empty while destination has ${destFiles.length} files. This may indicate an extraction issue. Skipping custom file detection.`);
32050
33188
  return [];
@@ -32072,10 +33210,10 @@ class FileScanner {
32072
33210
  }
32073
33211
 
32074
33212
  // src/services/transformers/commands-prefix.ts
32075
- import { lstat as lstat3, mkdir as mkdir10, readdir as readdir11, stat as stat4 } from "node:fs/promises";
32076
- import { join as join28 } from "node:path";
33213
+ import { lstat as lstat3, mkdir as mkdir10, readdir as readdir11, stat as stat5 } from "node:fs/promises";
33214
+ import { join as join29 } from "node:path";
32077
33215
  init_logger();
32078
- var import_fs_extra16 = __toESM(require_lib(), 1);
33216
+ var import_fs_extra17 = __toESM(require_lib(), 1);
32079
33217
  function stripWindowsDrivePrefix(path9) {
32080
33218
  if (path9.length >= 2 && /[a-zA-Z]/.test(path9[0]) && path9[1] === ":") {
32081
33219
  return path9.slice(2);
@@ -32114,14 +33252,14 @@ function validatePath4(path9, paramName) {
32114
33252
  class CommandsPrefix {
32115
33253
  static async applyPrefix(extractDir) {
32116
33254
  validatePath4(extractDir, "extractDir");
32117
- const commandsDir = join28(extractDir, ".claude", "commands");
32118
- if (!await import_fs_extra16.pathExists(commandsDir)) {
33255
+ const commandsDir = join29(extractDir, ".claude", "commands");
33256
+ if (!await import_fs_extra17.pathExists(commandsDir)) {
32119
33257
  logger.verbose("No commands directory found, skipping prefix application");
32120
33258
  return;
32121
33259
  }
32122
33260
  logger.info("Applying /ck: prefix to slash commands...");
32123
- const backupDir = join28(extractDir, ".commands-backup");
32124
- const tempDir = join28(extractDir, ".commands-prefix-temp");
33261
+ const backupDir = join29(extractDir, ".commands-backup");
33262
+ const tempDir = join29(extractDir, ".commands-prefix-temp");
32125
33263
  try {
32126
33264
  const entries = await readdir11(commandsDir);
32127
33265
  if (entries.length === 0) {
@@ -32129,28 +33267,28 @@ class CommandsPrefix {
32129
33267
  return;
32130
33268
  }
32131
33269
  if (entries.length === 1 && entries[0] === "ck") {
32132
- const ckDir2 = join28(commandsDir, "ck");
32133
- const ckStat = await stat4(ckDir2);
33270
+ const ckDir2 = join29(commandsDir, "ck");
33271
+ const ckStat = await stat5(ckDir2);
32134
33272
  if (ckStat.isDirectory()) {
32135
33273
  logger.verbose("Commands already have /ck: prefix, skipping");
32136
33274
  return;
32137
33275
  }
32138
33276
  }
32139
- await import_fs_extra16.copy(commandsDir, backupDir);
33277
+ await import_fs_extra17.copy(commandsDir, backupDir);
32140
33278
  logger.verbose("Created backup of commands directory");
32141
33279
  await mkdir10(tempDir, { recursive: true });
32142
- const ckDir = join28(tempDir, "ck");
33280
+ const ckDir = join29(tempDir, "ck");
32143
33281
  await mkdir10(ckDir, { recursive: true });
32144
33282
  let processedCount = 0;
32145
33283
  for (const entry of entries) {
32146
- const sourcePath = join28(commandsDir, entry);
33284
+ const sourcePath = join29(commandsDir, entry);
32147
33285
  const stats = await lstat3(sourcePath);
32148
33286
  if (stats.isSymbolicLink()) {
32149
33287
  logger.warning(`Skipping symlink for security: ${entry}`);
32150
33288
  continue;
32151
33289
  }
32152
- const destPath = join28(ckDir, entry);
32153
- await import_fs_extra16.copy(sourcePath, destPath, {
33290
+ const destPath = join29(ckDir, entry);
33291
+ await import_fs_extra17.copy(sourcePath, destPath, {
32154
33292
  overwrite: false,
32155
33293
  errorOnExist: true
32156
33294
  });
@@ -32159,35 +33297,35 @@ class CommandsPrefix {
32159
33297
  }
32160
33298
  if (processedCount === 0) {
32161
33299
  logger.warning("No files to move (all were symlinks or invalid)");
32162
- await import_fs_extra16.remove(backupDir);
32163
- await import_fs_extra16.remove(tempDir);
33300
+ await import_fs_extra17.remove(backupDir);
33301
+ await import_fs_extra17.remove(tempDir);
32164
33302
  return;
32165
33303
  }
32166
- await import_fs_extra16.remove(commandsDir);
32167
- await import_fs_extra16.move(tempDir, commandsDir);
32168
- await import_fs_extra16.remove(backupDir);
33304
+ await import_fs_extra17.remove(commandsDir);
33305
+ await import_fs_extra17.move(tempDir, commandsDir);
33306
+ await import_fs_extra17.remove(backupDir);
32169
33307
  logger.success("Successfully applied /ck: prefix to all commands");
32170
33308
  } catch (error) {
32171
- if (await import_fs_extra16.pathExists(backupDir)) {
33309
+ if (await import_fs_extra17.pathExists(backupDir)) {
32172
33310
  try {
32173
- await import_fs_extra16.remove(commandsDir).catch(() => {});
32174
- await import_fs_extra16.move(backupDir, commandsDir);
33311
+ await import_fs_extra17.remove(commandsDir).catch(() => {});
33312
+ await import_fs_extra17.move(backupDir, commandsDir);
32175
33313
  logger.info("Restored original commands directory from backup");
32176
33314
  } catch (rollbackError) {
32177
33315
  logger.error(`Rollback failed: ${rollbackError}`);
32178
33316
  }
32179
33317
  }
32180
- if (await import_fs_extra16.pathExists(tempDir)) {
32181
- await import_fs_extra16.remove(tempDir).catch(() => {});
33318
+ if (await import_fs_extra17.pathExists(tempDir)) {
33319
+ await import_fs_extra17.remove(tempDir).catch(() => {});
32182
33320
  }
32183
33321
  logger.error("Failed to apply /ck: prefix to commands");
32184
33322
  throw error;
32185
33323
  } finally {
32186
- if (await import_fs_extra16.pathExists(backupDir)) {
32187
- await import_fs_extra16.remove(backupDir).catch(() => {});
33324
+ if (await import_fs_extra17.pathExists(backupDir)) {
33325
+ await import_fs_extra17.remove(backupDir).catch(() => {});
32188
33326
  }
32189
- if (await import_fs_extra16.pathExists(tempDir)) {
32190
- await import_fs_extra16.remove(tempDir).catch(() => {});
33327
+ if (await import_fs_extra17.pathExists(tempDir)) {
33328
+ await import_fs_extra17.remove(tempDir).catch(() => {});
32191
33329
  }
32192
33330
  }
32193
33331
  }
@@ -32197,15 +33335,15 @@ class CommandsPrefix {
32197
33335
  static async cleanupCommandsDirectory(targetDir, isGlobal, options = {}) {
32198
33336
  const { dryRun = false, forceOverwrite = false } = options;
32199
33337
  validatePath4(targetDir, "targetDir");
32200
- const claudeDir = isGlobal ? targetDir : join28(targetDir, ".claude");
32201
- const commandsDir = join28(claudeDir, "commands");
33338
+ const claudeDir = isGlobal ? targetDir : join29(targetDir, ".claude");
33339
+ const commandsDir = join29(claudeDir, "commands");
32202
33340
  const result = {
32203
33341
  results: [],
32204
33342
  deletedCount: 0,
32205
33343
  preservedCount: 0,
32206
33344
  wasDryRun: dryRun
32207
33345
  };
32208
- if (!await import_fs_extra16.pathExists(commandsDir)) {
33346
+ if (!await import_fs_extra17.pathExists(commandsDir)) {
32209
33347
  logger.verbose(`Commands directory does not exist: ${commandsDir}`);
32210
33348
  return result;
32211
33349
  }
@@ -32226,7 +33364,7 @@ class CommandsPrefix {
32226
33364
  return result;
32227
33365
  }
32228
33366
  for (const entry of entries) {
32229
- const entryPath = join28(commandsDir, entry);
33367
+ const entryPath = join29(commandsDir, entry);
32230
33368
  const stats = await lstat3(entryPath);
32231
33369
  if (stats.isSymbolicLink()) {
32232
33370
  logger.warning(`Skipping symlink: ${entry}`);
@@ -32252,7 +33390,7 @@ class CommandsPrefix {
32252
33390
  action: "delete"
32253
33391
  });
32254
33392
  if (!dryRun) {
32255
- await import_fs_extra16.remove(file);
33393
+ await import_fs_extra17.remove(file);
32256
33394
  logger.verbose(`Deleted CK file: ${relativePath}`);
32257
33395
  }
32258
33396
  result.deletedCount++;
@@ -32265,7 +33403,7 @@ class CommandsPrefix {
32265
33403
  reason: "force overwrite"
32266
33404
  });
32267
33405
  if (!dryRun) {
32268
- await import_fs_extra16.remove(file);
33406
+ await import_fs_extra17.remove(file);
32269
33407
  logger.verbose(`Force-deleted modified file: ${relativePath}`);
32270
33408
  }
32271
33409
  result.deletedCount++;
@@ -32289,7 +33427,7 @@ class CommandsPrefix {
32289
33427
  reason: "force overwrite"
32290
33428
  });
32291
33429
  if (!dryRun) {
32292
- await import_fs_extra16.remove(file);
33430
+ await import_fs_extra17.remove(file);
32293
33431
  logger.verbose(`Force-deleted user file: ${relativePath}`);
32294
33432
  }
32295
33433
  result.deletedCount++;
@@ -32307,7 +33445,7 @@ class CommandsPrefix {
32307
33445
  }
32308
33446
  }
32309
33447
  if (canDeleteDir && !dryRun) {
32310
- await import_fs_extra16.remove(entryPath);
33448
+ await import_fs_extra17.remove(entryPath);
32311
33449
  logger.verbose(`Removed directory: ${entry}`);
32312
33450
  }
32313
33451
  } else {
@@ -32320,7 +33458,7 @@ class CommandsPrefix {
32320
33458
  action: "delete"
32321
33459
  });
32322
33460
  if (!dryRun) {
32323
- await import_fs_extra16.remove(entryPath);
33461
+ await import_fs_extra17.remove(entryPath);
32324
33462
  logger.verbose(`Deleted CK file: ${entry}`);
32325
33463
  }
32326
33464
  result.deletedCount++;
@@ -32333,7 +33471,7 @@ class CommandsPrefix {
32333
33471
  reason: "force overwrite"
32334
33472
  });
32335
33473
  if (!dryRun) {
32336
- await import_fs_extra16.remove(entryPath);
33474
+ await import_fs_extra17.remove(entryPath);
32337
33475
  logger.verbose(`Force-deleted modified file: ${entry}`);
32338
33476
  }
32339
33477
  result.deletedCount++;
@@ -32356,7 +33494,7 @@ class CommandsPrefix {
32356
33494
  reason: "force overwrite"
32357
33495
  });
32358
33496
  if (!dryRun) {
32359
- await import_fs_extra16.remove(entryPath);
33497
+ await import_fs_extra17.remove(entryPath);
32360
33498
  logger.verbose(`Force-deleted user file: ${entry}`);
32361
33499
  }
32362
33500
  result.deletedCount++;
@@ -32392,7 +33530,7 @@ class CommandsPrefix {
32392
33530
  const files = [];
32393
33531
  const entries = await readdir11(dir);
32394
33532
  for (const entry of entries) {
32395
- const fullPath = join28(dir, entry);
33533
+ const fullPath = join29(dir, entry);
32396
33534
  const stats = await lstat3(fullPath);
32397
33535
  if (stats.isSymbolicLink()) {
32398
33536
  continue;
@@ -32410,9 +33548,9 @@ class CommandsPrefix {
32410
33548
  // src/services/transformers/folder-path-transformer.ts
32411
33549
  init_logger();
32412
33550
  init_types2();
32413
- var import_fs_extra17 = __toESM(require_lib(), 1);
32414
- import { readFile as readFile15, readdir as readdir12, rename as rename3, writeFile as writeFile13 } from "node:fs/promises";
32415
- import { join as join29, relative as relative8 } from "node:path";
33551
+ var import_fs_extra18 = __toESM(require_lib(), 1);
33552
+ import { readFile as readFile16, readdir as readdir12, rename as rename3, writeFile as writeFile14 } from "node:fs/promises";
33553
+ import { join as join30, relative as relative8 } from "node:path";
32416
33554
  var TRANSFORMABLE_FILE_PATTERNS = [
32417
33555
  ".md",
32418
33556
  ".txt",
@@ -32458,34 +33596,34 @@ async function transformFolderPaths(extractDir, folders, options = {}) {
32458
33596
  }
32459
33597
  const dirsToRename = [];
32460
33598
  if (folders.docs !== DEFAULT_FOLDERS.docs) {
32461
- const docsPath = join29(extractDir, DEFAULT_FOLDERS.docs);
32462
- if (await import_fs_extra17.pathExists(docsPath)) {
33599
+ const docsPath = join30(extractDir, DEFAULT_FOLDERS.docs);
33600
+ if (await import_fs_extra18.pathExists(docsPath)) {
32463
33601
  dirsToRename.push({
32464
33602
  from: docsPath,
32465
- to: join29(extractDir, folders.docs)
33603
+ to: join30(extractDir, folders.docs)
32466
33604
  });
32467
33605
  }
32468
- const claudeDocsPath = join29(extractDir, ".claude", DEFAULT_FOLDERS.docs);
32469
- if (await import_fs_extra17.pathExists(claudeDocsPath)) {
33606
+ const claudeDocsPath = join30(extractDir, ".claude", DEFAULT_FOLDERS.docs);
33607
+ if (await import_fs_extra18.pathExists(claudeDocsPath)) {
32470
33608
  dirsToRename.push({
32471
33609
  from: claudeDocsPath,
32472
- to: join29(extractDir, ".claude", folders.docs)
33610
+ to: join30(extractDir, ".claude", folders.docs)
32473
33611
  });
32474
33612
  }
32475
33613
  }
32476
33614
  if (folders.plans !== DEFAULT_FOLDERS.plans) {
32477
- const plansPath = join29(extractDir, DEFAULT_FOLDERS.plans);
32478
- if (await import_fs_extra17.pathExists(plansPath)) {
33615
+ const plansPath = join30(extractDir, DEFAULT_FOLDERS.plans);
33616
+ if (await import_fs_extra18.pathExists(plansPath)) {
32479
33617
  dirsToRename.push({
32480
33618
  from: plansPath,
32481
- to: join29(extractDir, folders.plans)
33619
+ to: join30(extractDir, folders.plans)
32482
33620
  });
32483
33621
  }
32484
- const claudePlansPath = join29(extractDir, ".claude", DEFAULT_FOLDERS.plans);
32485
- if (await import_fs_extra17.pathExists(claudePlansPath)) {
33622
+ const claudePlansPath = join30(extractDir, ".claude", DEFAULT_FOLDERS.plans);
33623
+ if (await import_fs_extra18.pathExists(claudePlansPath)) {
32486
33624
  dirsToRename.push({
32487
33625
  from: claudePlansPath,
32488
- to: join29(extractDir, ".claude", folders.plans)
33626
+ to: join30(extractDir, ".claude", folders.plans)
32489
33627
  });
32490
33628
  }
32491
33629
  }
@@ -32522,7 +33660,7 @@ async function transformFileContents(dir, compiledReplacements, options) {
32522
33660
  let replacementsCount = 0;
32523
33661
  const entries = await readdir12(dir, { withFileTypes: true });
32524
33662
  for (const entry of entries) {
32525
- const fullPath = join29(dir, entry.name);
33663
+ const fullPath = join30(dir, entry.name);
32526
33664
  if (entry.isDirectory()) {
32527
33665
  if (entry.name === "node_modules" || entry.name === ".git") {
32528
33666
  continue;
@@ -32535,7 +33673,7 @@ async function transformFileContents(dir, compiledReplacements, options) {
32535
33673
  if (!shouldTransform)
32536
33674
  continue;
32537
33675
  try {
32538
- const content = await readFile15(fullPath, "utf-8");
33676
+ const content = await readFile16(fullPath, "utf-8");
32539
33677
  let newContent = content;
32540
33678
  let changeCount = 0;
32541
33679
  for (const { regex: regex2, replacement } of compiledReplacements) {
@@ -32551,7 +33689,7 @@ async function transformFileContents(dir, compiledReplacements, options) {
32551
33689
  if (options.dryRun) {
32552
33690
  logger.debug(`[dry-run] Would update ${relative8(dir, fullPath)}: ${changeCount} replacement(s)`);
32553
33691
  } else {
32554
- await writeFile13(fullPath, newContent, "utf-8");
33692
+ await writeFile14(fullPath, newContent, "utf-8");
32555
33693
  logger.debug(`Updated ${relative8(dir, fullPath)}: ${changeCount} replacement(s)`);
32556
33694
  }
32557
33695
  filesChanged++;
@@ -32631,9 +33769,9 @@ function validateFolderName(name2) {
32631
33769
 
32632
33770
  // src/services/transformers/global-path-transformer.ts
32633
33771
  init_logger();
32634
- import { readFile as readFile16, readdir as readdir13, writeFile as writeFile14 } from "node:fs/promises";
33772
+ import { readFile as readFile17, readdir as readdir13, writeFile as writeFile15 } from "node:fs/promises";
32635
33773
  import { platform as platform9 } from "node:os";
32636
- import { extname, join as join30 } from "node:path";
33774
+ import { extname, join as join31 } from "node:path";
32637
33775
  var IS_WINDOWS2 = platform9() === "win32";
32638
33776
  var HOME_PREFIX = IS_WINDOWS2 ? "%USERPROFILE%" : "$HOME";
32639
33777
  function getHomeDirPrefix() {
@@ -32725,7 +33863,7 @@ async function transformPathsForGlobalInstall(directory, options = {}) {
32725
33863
  async function processDirectory(dir) {
32726
33864
  const entries = await readdir13(dir, { withFileTypes: true });
32727
33865
  for (const entry of entries) {
32728
- const fullPath = join30(dir, entry.name);
33866
+ const fullPath = join31(dir, entry.name);
32729
33867
  if (entry.isDirectory()) {
32730
33868
  if (entry.name === "node_modules" || entry.name.startsWith(".") && entry.name !== ".claude") {
32731
33869
  continue;
@@ -32733,10 +33871,10 @@ async function transformPathsForGlobalInstall(directory, options = {}) {
32733
33871
  await processDirectory(fullPath);
32734
33872
  } else if (entry.isFile() && shouldTransformFile(entry.name)) {
32735
33873
  try {
32736
- const content = await readFile16(fullPath, "utf-8");
33874
+ const content = await readFile17(fullPath, "utf-8");
32737
33875
  const { transformed, changes } = transformContent(content);
32738
33876
  if (changes > 0) {
32739
- await writeFile14(fullPath, transformed, "utf-8");
33877
+ await writeFile15(fullPath, transformed, "utf-8");
32740
33878
  filesTransformed++;
32741
33879
  totalChanges += changes;
32742
33880
  if (options.verbose) {
@@ -32767,7 +33905,7 @@ init_environment();
32767
33905
  init_logger();
32768
33906
  init_output_manager();
32769
33907
  init_types2();
32770
- var import_fs_extra18 = __toESM(require_lib(), 1);
33908
+ var import_fs_extra19 = __toESM(require_lib(), 1);
32771
33909
  async function initCommand(options) {
32772
33910
  const prompts = new PromptsManager;
32773
33911
  prompts.intro("\uD83D\uDD27 ClaudeKit - Initialize/Update Project");
@@ -32786,8 +33924,8 @@ async function initCommand(options) {
32786
33924
  const globalKitDir = PathResolver.getGlobalKitDir();
32787
33925
  const cwdResolved = resolve6(process.cwd());
32788
33926
  const isInGlobalDir = cwdResolved === globalKitDir || cwdResolved === resolve6(globalKitDir, "..");
32789
- const localSettingsPath = join31(process.cwd(), ".claude", "settings.json");
32790
- if (!isInGlobalDir && await import_fs_extra18.pathExists(localSettingsPath)) {
33927
+ const localSettingsPath = join32(process.cwd(), ".claude", "settings.json");
33928
+ if (!isInGlobalDir && await import_fs_extra19.pathExists(localSettingsPath)) {
32791
33929
  if (isNonInteractive2) {
32792
33930
  logger.warning("Local .claude/settings.json detected. Local settings take precedence over global.");
32793
33931
  logger.warning("Consider removing local installation: rm -rf .claude");
@@ -32798,9 +33936,9 @@ async function initCommand(options) {
32798
33936
  return;
32799
33937
  }
32800
33938
  if (choice === "remove") {
32801
- const localClaudeDir = join31(process.cwd(), ".claude");
33939
+ const localClaudeDir = join32(process.cwd(), ".claude");
32802
33940
  try {
32803
- await import_fs_extra18.remove(localClaudeDir);
33941
+ await import_fs_extra19.remove(localClaudeDir);
32804
33942
  logger.success("Removed local .claude/ directory");
32805
33943
  } catch (error) {
32806
33944
  logger.error(`Failed to remove local installation: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -32844,7 +33982,7 @@ async function initCommand(options) {
32844
33982
  }
32845
33983
  const resolvedDir = resolve6(targetDir);
32846
33984
  logger.info(`Target directory: ${resolvedDir}`);
32847
- if (!await import_fs_extra18.pathExists(resolvedDir)) {
33985
+ if (!await import_fs_extra19.pathExists(resolvedDir)) {
32848
33986
  if (validOptions.global) {
32849
33987
  const { mkdir: mkdir11 } = await import("node:fs/promises");
32850
33988
  await mkdir11(resolvedDir, { recursive: true });
@@ -32857,7 +33995,7 @@ async function initCommand(options) {
32857
33995
  }
32858
33996
  if (validOptions.fresh) {
32859
33997
  const prefix = PathResolver.getPathPrefix(validOptions.global);
32860
- const claudeDir2 = prefix ? join31(resolvedDir, prefix) : resolvedDir;
33998
+ const claudeDir2 = prefix ? join32(resolvedDir, prefix) : resolvedDir;
32861
33999
  const canProceed = await handleFreshInstallation(claudeDir2, prompts);
32862
34000
  if (!canProceed) {
32863
34001
  return;
@@ -32885,7 +34023,7 @@ async function initCommand(options) {
32885
34023
  logger.info("Fetching available versions...");
32886
34024
  let currentVersion = null;
32887
34025
  try {
32888
- const metadataPath = validOptions.global ? join31(PathResolver.getGlobalKitDir(), "metadata.json") : join31(resolvedDir, ".claude", "metadata.json");
34026
+ const metadataPath = validOptions.global ? join32(PathResolver.getGlobalKitDir(), "metadata.json") : join32(resolvedDir, ".claude", "metadata.json");
32889
34027
  const metadata = await readClaudeKitMetadata(metadataPath);
32890
34028
  currentVersion = metadata?.version || null;
32891
34029
  if (currentVersion) {
@@ -33010,9 +34148,9 @@ async function initCommand(options) {
33010
34148
  }
33011
34149
  }
33012
34150
  if (!validOptions.fresh) {
33013
- const newSkillsDir = join31(extractDir, ".claude", "skills");
34151
+ const newSkillsDir = join32(extractDir, ".claude", "skills");
33014
34152
  const currentSkillsDir = PathResolver.buildSkillsPath(resolvedDir, validOptions.global);
33015
- if (await import_fs_extra18.pathExists(newSkillsDir) && await import_fs_extra18.pathExists(currentSkillsDir)) {
34153
+ if (await import_fs_extra19.pathExists(newSkillsDir) && await import_fs_extra19.pathExists(currentSkillsDir)) {
33016
34154
  logger.info("Checking for skills directory migration...");
33017
34155
  const migrationDetection = await SkillsMigrationDetector.detectMigration(newSkillsDir, currentSkillsDir);
33018
34156
  if (migrationDetection.status === "recommended" || migrationDetection.status === "required") {
@@ -33035,7 +34173,7 @@ async function initCommand(options) {
33035
34173
  let customClaudeFiles = [];
33036
34174
  if (!validOptions.fresh) {
33037
34175
  logger.info("Scanning for custom .claude files...");
33038
- const scanSourceDir = validOptions.global ? join31(extractDir, ".claude") : extractDir;
34176
+ const scanSourceDir = validOptions.global ? join32(extractDir, ".claude") : extractDir;
33039
34177
  const scanTargetSubdir = validOptions.global ? "" : ".claude";
33040
34178
  customClaudeFiles = await FileScanner.findCustomFiles(resolvedDir, scanSourceDir, scanTargetSubdir);
33041
34179
  } else {
@@ -33070,9 +34208,12 @@ async function initCommand(options) {
33070
34208
  }
33071
34209
  merger.setGlobalFlag(validOptions.global);
33072
34210
  merger.setForceOverwriteSettings(validOptions.forceOverwriteSettings);
33073
- const claudeDir = validOptions.global ? resolvedDir : join31(resolvedDir, ".claude");
34211
+ const claudeDir = validOptions.global ? resolvedDir : join32(resolvedDir, ".claude");
33074
34212
  const releaseManifest = await ReleaseManifestLoader.load(extractDir);
33075
- if (!validOptions.fresh && await import_fs_extra18.pathExists(claudeDir)) {
34213
+ if (releaseManifest) {
34214
+ merger.setManifest(releaseManifest);
34215
+ }
34216
+ if (!validOptions.fresh && await import_fs_extra19.pathExists(claudeDir)) {
33076
34217
  const legacyDetection = await LegacyMigration.detectLegacy(claudeDir);
33077
34218
  if (legacyDetection.isLegacy && releaseManifest) {
33078
34219
  logger.info("Legacy installation detected - migrating to ownership tracking...");
@@ -33092,7 +34233,7 @@ async function initCommand(options) {
33092
34233
  return;
33093
34234
  }
33094
34235
  }
33095
- const sourceDir = validOptions.global ? join31(extractDir, ".claude") : extractDir;
34236
+ const sourceDir = validOptions.global ? join32(extractDir, ".claude") : extractDir;
33096
34237
  await merger.merge(sourceDir, resolvedDir, false);
33097
34238
  const manifestWriter = new ManifestWriter;
33098
34239
  const installedFiles = merger.getAllInstalledFiles();
@@ -33101,7 +34242,7 @@ async function initCommand(options) {
33101
34242
  if (!validOptions.global && !installedPath.startsWith(".claude/"))
33102
34243
  continue;
33103
34244
  const relativePath = validOptions.global ? installedPath : installedPath.replace(/^\.claude\//, "");
33104
- const filePath = join31(claudeDir, relativePath);
34245
+ const filePath = join32(claudeDir, relativePath);
33105
34246
  const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
33106
34247
  const ownership = manifestEntry ? "ck" : "user";
33107
34248
  filesToTrack.push({
@@ -33120,13 +34261,13 @@ async function initCommand(options) {
33120
34261
  }
33121
34262
  });
33122
34263
  trackingSpinner.succeed(`Tracked ${trackResult.success} files`);
33123
- await manifestWriter.writeManifest(claudeDir, kitConfig.name, release.tag_name, validOptions.global ? "global" : "local");
34264
+ await manifestWriter.writeManifest(claudeDir, kitConfig.name, release.tag_name, validOptions.global ? "global" : "local", kit);
33124
34265
  if (validOptions.global) {
33125
- const claudeMdSource = join31(extractDir, "CLAUDE.md");
33126
- const claudeMdDest = join31(resolvedDir, "CLAUDE.md");
33127
- if (await import_fs_extra18.pathExists(claudeMdSource)) {
33128
- if (!await import_fs_extra18.pathExists(claudeMdDest)) {
33129
- await import_fs_extra18.copy(claudeMdSource, claudeMdDest);
34266
+ const claudeMdSource = join32(extractDir, "CLAUDE.md");
34267
+ const claudeMdDest = join32(resolvedDir, "CLAUDE.md");
34268
+ if (await import_fs_extra19.pathExists(claudeMdSource)) {
34269
+ if (!await import_fs_extra19.pathExists(claudeMdDest)) {
34270
+ await import_fs_extra19.copy(claudeMdSource, claudeMdDest);
33130
34271
  logger.success("Copied CLAUDE.md to global directory");
33131
34272
  } else {
33132
34273
  logger.debug("CLAUDE.md already exists in global directory (preserved)");
@@ -33165,8 +34306,8 @@ async function initCommand(options) {
33165
34306
  }
33166
34307
  }
33167
34308
  if (!validOptions.skipSetup && !isNonInteractive2) {
33168
- const envPath = join31(claudeDir, ".env");
33169
- if (!await import_fs_extra18.pathExists(envPath)) {
34309
+ const envPath = join32(claudeDir, ".env");
34310
+ if (!await import_fs_extra19.pathExists(envPath)) {
33170
34311
  const shouldSetup = await prompts.confirm("Set up API keys now? (Gemini API key for ai-multimodal skill, optional webhooks)");
33171
34312
  if (shouldSetup) {
33172
34313
  await runSetupWizard({
@@ -33196,13 +34337,13 @@ Protected files (.env, etc.) were not modified.`;
33196
34337
  }
33197
34338
 
33198
34339
  // src/commands/new.ts
33199
- import { join as join32, resolve as resolve7 } from "node:path";
34340
+ import { join as join33, resolve as resolve7 } from "node:path";
33200
34341
  init_package_installer();
33201
34342
  init_environment();
33202
34343
  init_logger();
33203
34344
  init_output_manager();
33204
34345
  init_types2();
33205
- var import_fs_extra19 = __toESM(require_lib(), 1);
34346
+ var import_fs_extra20 = __toESM(require_lib(), 1);
33206
34347
  async function newCommand(options) {
33207
34348
  const prompts = new PromptsManager;
33208
34349
  prompts.intro("\uD83D\uDE80 ClaudeKit - Create New Project");
@@ -33229,8 +34370,8 @@ async function newCommand(options) {
33229
34370
  }
33230
34371
  const resolvedDir = resolve7(targetDir);
33231
34372
  logger.info(`Target directory: ${resolvedDir}`);
33232
- if (await import_fs_extra19.pathExists(resolvedDir)) {
33233
- const files = await import_fs_extra19.readdir(resolvedDir);
34373
+ if (await import_fs_extra20.pathExists(resolvedDir)) {
34374
+ const files = await import_fs_extra20.readdir(resolvedDir);
33234
34375
  const isEmpty = files.length === 0;
33235
34376
  if (!isEmpty) {
33236
34377
  if (isNonInteractive2) {
@@ -33377,7 +34518,7 @@ async function newCommand(options) {
33377
34518
  await CommandsPrefix.cleanupCommandsDirectory(resolvedDir, false);
33378
34519
  }
33379
34520
  await merger.merge(extractDir, resolvedDir, true);
33380
- const claudeDir = join32(resolvedDir, ".claude");
34521
+ const claudeDir = join33(resolvedDir, ".claude");
33381
34522
  const manifestWriter = new ManifestWriter;
33382
34523
  const releaseManifest = await ReleaseManifestLoader.load(extractDir);
33383
34524
  const installedFiles = merger.getAllInstalledFiles();
@@ -33386,7 +34527,7 @@ async function newCommand(options) {
33386
34527
  if (!installedPath.startsWith(".claude/"))
33387
34528
  continue;
33388
34529
  const relativePath = installedPath.replace(/^\.claude\//, "");
33389
- const filePath = join32(claudeDir, relativePath);
34530
+ const filePath = join33(claudeDir, relativePath);
33390
34531
  const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
33391
34532
  const ownership = manifestEntry ? "ck" : "user";
33392
34533
  filesToTrack.push({
@@ -33439,10 +34580,10 @@ async function newCommand(options) {
33439
34580
 
33440
34581
  // src/commands/uninstall.ts
33441
34582
  import { readdirSync, rmSync } from "node:fs";
33442
- import { dirname as dirname7, join as join33 } from "node:path";
34583
+ import { dirname as dirname7, join as join34 } from "node:path";
33443
34584
  init_logger();
33444
34585
  init_types2();
33445
- var import_fs_extra20 = __toESM(require_lib(), 1);
34586
+ var import_fs_extra21 = __toESM(require_lib(), 1);
33446
34587
  var import_picocolors13 = __toESM(require_picocolors(), 1);
33447
34588
  async function detectInstallations() {
33448
34589
  const installations = [];
@@ -33451,14 +34592,14 @@ async function detectInstallations() {
33451
34592
  installations.push({
33452
34593
  type: "local",
33453
34594
  path: setup.project.path,
33454
- exists: await import_fs_extra20.pathExists(setup.project.path)
34595
+ exists: await import_fs_extra21.pathExists(setup.project.path)
33455
34596
  });
33456
34597
  }
33457
34598
  if (setup.global.path && setup.global.metadata) {
33458
34599
  installations.push({
33459
34600
  type: "global",
33460
34601
  path: setup.global.path,
33461
- exists: await import_fs_extra20.pathExists(setup.global.path)
34602
+ exists: await import_fs_extra21.pathExists(setup.global.path)
33462
34603
  });
33463
34604
  }
33464
34605
  return installations.filter((i) => i.exists);
@@ -33491,14 +34632,26 @@ async function promptScope(installations) {
33491
34632
  }
33492
34633
  return selected;
33493
34634
  }
33494
- async function confirmUninstall(scope) {
34635
+ async function confirmUninstall(scope, kitLabel = "") {
33495
34636
  const scopeText = scope === "all" ? "all ClaudeKit installations" : scope === "local" ? "local ClaudeKit installation" : "global ClaudeKit installation";
33496
34637
  const confirmed = await se({
33497
- message: `Continue with uninstalling ${scopeText}?`,
34638
+ message: `Continue with uninstalling ${scopeText}${kitLabel}?`,
33498
34639
  initialValue: false
33499
34640
  });
33500
34641
  return confirmed === true;
33501
34642
  }
34643
+ function classifyFileByOwnership(ownership, forceOverwrite, deleteReason) {
34644
+ if (ownership === "ck") {
34645
+ return { action: "delete", reason: deleteReason };
34646
+ }
34647
+ if (ownership === "ck-modified") {
34648
+ if (forceOverwrite) {
34649
+ return { action: "delete", reason: "force overwrite" };
34650
+ }
34651
+ return { action: "preserve", reason: "modified by user" };
34652
+ }
34653
+ return { action: "preserve", reason: "user-created" };
34654
+ }
33502
34655
  async function cleanupEmptyDirectories(filePath, installationRoot) {
33503
34656
  let cleaned = 0;
33504
34657
  let currentDir = dirname7(filePath);
@@ -33519,33 +34672,56 @@ async function cleanupEmptyDirectories(filePath, installationRoot) {
33519
34672
  }
33520
34673
  return cleaned;
33521
34674
  }
33522
- async function analyzeInstallation(installation, forceOverwrite) {
33523
- const result = { toDelete: [], toPreserve: [] };
34675
+ async function analyzeInstallation(installation, forceOverwrite, kit) {
34676
+ const result = {
34677
+ toDelete: [],
34678
+ toPreserve: [],
34679
+ remainingKits: []
34680
+ };
33524
34681
  const metadata = await ManifestWriter.readManifest(installation.path);
34682
+ const uninstallManifest = await ManifestWriter.getUninstallManifest(installation.path, kit);
34683
+ result.remainingKits = uninstallManifest.remainingKits;
34684
+ if (uninstallManifest.isMultiKit && kit && metadata?.kits?.[kit]) {
34685
+ const kitFiles = metadata.kits[kit].files || [];
34686
+ for (const trackedFile of kitFiles) {
34687
+ const filePath = join34(installation.path, trackedFile.path);
34688
+ if (uninstallManifest.filesToPreserve.includes(trackedFile.path)) {
34689
+ result.toPreserve.push({ path: trackedFile.path, reason: "shared with other kit" });
34690
+ continue;
34691
+ }
34692
+ const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
34693
+ if (!ownershipResult.exists)
34694
+ continue;
34695
+ const classification = classifyFileByOwnership(ownershipResult.ownership, forceOverwrite, `${kit} kit (pristine)`);
34696
+ if (classification.action === "delete") {
34697
+ result.toDelete.push({ path: trackedFile.path, reason: classification.reason });
34698
+ } else {
34699
+ result.toPreserve.push({ path: trackedFile.path, reason: classification.reason });
34700
+ }
34701
+ }
34702
+ if (result.remainingKits.length === 0) {
34703
+ result.toDelete.push({ path: "metadata.json", reason: "metadata file" });
34704
+ }
34705
+ return result;
34706
+ }
33525
34707
  if (!metadata?.files || metadata.files.length === 0) {
33526
- const { filesToRemove, filesToPreserve } = await ManifestWriter.getUninstallManifest(installation.path);
33527
- for (const item of filesToRemove) {
33528
- if (!filesToPreserve.includes(item)) {
34708
+ for (const item of uninstallManifest.filesToRemove) {
34709
+ if (!uninstallManifest.filesToPreserve.includes(item)) {
33529
34710
  result.toDelete.push({ path: item, reason: "legacy installation" });
33530
34711
  }
33531
34712
  }
33532
34713
  return result;
33533
34714
  }
33534
- for (const trackedFile of metadata.files) {
33535
- const filePath = join33(installation.path, trackedFile.path);
34715
+ for (const trackedFile of metadata.files || []) {
34716
+ const filePath = join34(installation.path, trackedFile.path);
33536
34717
  const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
33537
34718
  if (!ownershipResult.exists)
33538
34719
  continue;
33539
- if (ownershipResult.ownership === "ck") {
33540
- result.toDelete.push({ path: trackedFile.path, reason: "CK-owned (pristine)" });
33541
- } else if (ownershipResult.ownership === "ck-modified") {
33542
- if (forceOverwrite) {
33543
- result.toDelete.push({ path: trackedFile.path, reason: "force overwrite" });
33544
- } else {
33545
- result.toPreserve.push({ path: trackedFile.path, reason: "modified by user" });
33546
- }
34720
+ const classification = classifyFileByOwnership(ownershipResult.ownership, forceOverwrite, "CK-owned (pristine)");
34721
+ if (classification.action === "delete") {
34722
+ result.toDelete.push({ path: trackedFile.path, reason: classification.reason });
33547
34723
  } else {
33548
- result.toPreserve.push({ path: trackedFile.path, reason: "user-created" });
34724
+ result.toPreserve.push({ path: trackedFile.path, reason: classification.reason });
33549
34725
  }
33550
34726
  }
33551
34727
  result.toDelete.push({ path: "metadata.json", reason: "metadata file" });
@@ -33580,24 +34756,32 @@ function displayDryRunPreview(analysis, installationType) {
33580
34756
  }
33581
34757
  async function removeInstallations(installations, options) {
33582
34758
  for (const installation of installations) {
33583
- const analysis = await analyzeInstallation(installation, options.forceOverwrite);
34759
+ const analysis = await analyzeInstallation(installation, options.forceOverwrite, options.kit);
33584
34760
  if (options.dryRun) {
33585
- displayDryRunPreview(analysis, installation.type);
34761
+ const label = options.kit ? `${installation.type} (${options.kit} kit)` : installation.type;
34762
+ displayDryRunPreview(analysis, label);
34763
+ if (analysis.remainingKits.length > 0) {
34764
+ log.info(`Remaining kits after uninstall: ${analysis.remainingKits.join(", ")}`);
34765
+ }
33586
34766
  continue;
33587
34767
  }
33588
- const spinner = createSpinner(`Removing ${installation.type} ClaudeKit files...`).start();
34768
+ const kitLabel = options.kit ? ` ${options.kit} kit` : "";
34769
+ const spinner = createSpinner(`Removing ${installation.type}${kitLabel} ClaudeKit files...`).start();
33589
34770
  try {
33590
34771
  let removedCount = 0;
33591
34772
  let cleanedDirs = 0;
33592
34773
  for (const item of analysis.toDelete) {
33593
- const filePath = join33(installation.path, item.path);
33594
- if (await import_fs_extra20.pathExists(filePath)) {
33595
- await import_fs_extra20.remove(filePath);
34774
+ const filePath = join34(installation.path, item.path);
34775
+ if (await import_fs_extra21.pathExists(filePath)) {
34776
+ await import_fs_extra21.remove(filePath);
33596
34777
  removedCount++;
33597
34778
  logger.debug(`Removed: ${item.path}`);
33598
34779
  cleanedDirs += await cleanupEmptyDirectories(filePath, installation.path);
33599
34780
  }
33600
34781
  }
34782
+ if (options.kit && analysis.remainingKits.length > 0) {
34783
+ await ManifestWriter.removeKitFromManifest(installation.path, options.kit);
34784
+ }
33601
34785
  try {
33602
34786
  const remaining = readdirSync(installation.path);
33603
34787
  if (remaining.length === 0) {
@@ -33605,7 +34789,8 @@ async function removeInstallations(installations, options) {
33605
34789
  logger.debug(`Removed empty installation directory: ${installation.path}`);
33606
34790
  }
33607
34791
  } catch {}
33608
- spinner.succeed(`Removed ${removedCount} files${cleanedDirs > 0 ? `, cleaned ${cleanedDirs} empty directories` : ""}, preserved ${analysis.toPreserve.length} customizations`);
34792
+ const kitsInfo = analysis.remainingKits.length > 0 ? `, ${analysis.remainingKits.join(", ")} kit(s) preserved` : "";
34793
+ spinner.succeed(`Removed ${removedCount} files${cleanedDirs > 0 ? `, cleaned ${cleanedDirs} empty directories` : ""}, preserved ${analysis.toPreserve.length} customizations${kitsInfo}`);
33609
34794
  if (analysis.toPreserve.length > 0) {
33610
34795
  log.info("Preserved customizations:");
33611
34796
  analysis.toPreserve.slice(0, 5).forEach((f3) => log.message(` - ${f3.path} (${f3.reason})`));
@@ -33627,6 +34812,23 @@ async function uninstallCommand(options) {
33627
34812
  logger.info("No ClaudeKit installations found.");
33628
34813
  return;
33629
34814
  }
34815
+ if (validOptions.kit) {
34816
+ let kitFound = false;
34817
+ for (const inst of allInstallations) {
34818
+ const metadata = await ManifestWriter.readManifest(inst.path);
34819
+ if (metadata) {
34820
+ const installedKits = getInstalledKits(metadata);
34821
+ if (installedKits.includes(validOptions.kit)) {
34822
+ kitFound = true;
34823
+ break;
34824
+ }
34825
+ }
34826
+ }
34827
+ if (!kitFound) {
34828
+ logger.info(`Kit "${validOptions.kit}" is not installed.`);
34829
+ return;
34830
+ }
34831
+ }
33630
34832
  let scope;
33631
34833
  if (validOptions.all || validOptions.local && validOptions.global) {
33632
34834
  scope = "all";
@@ -33653,11 +34855,15 @@ async function uninstallCommand(options) {
33653
34855
  return;
33654
34856
  }
33655
34857
  displayInstallations(installations, scope);
34858
+ if (validOptions.kit) {
34859
+ log.info(import_picocolors13.default.cyan(`Kit-scoped uninstall: ${validOptions.kit} kit only`));
34860
+ }
33656
34861
  if (validOptions.dryRun) {
33657
34862
  log.info(import_picocolors13.default.yellow("DRY RUN MODE - No files will be deleted"));
33658
34863
  await removeInstallations(installations, {
33659
34864
  dryRun: true,
33660
- forceOverwrite: validOptions.forceOverwrite
34865
+ forceOverwrite: validOptions.forceOverwrite,
34866
+ kit: validOptions.kit
33661
34867
  });
33662
34868
  outro("Dry-run complete. No changes were made.");
33663
34869
  return;
@@ -33667,7 +34873,8 @@ async function uninstallCommand(options) {
33667
34873
  ${import_picocolors13.default.yellow("User modifications will be permanently deleted!")}`);
33668
34874
  }
33669
34875
  if (!validOptions.yes) {
33670
- const confirmed = await confirmUninstall(scope);
34876
+ const kitLabel = validOptions.kit ? ` (${validOptions.kit} kit only)` : "";
34877
+ const confirmed = await confirmUninstall(scope, kitLabel);
33671
34878
  if (!confirmed) {
33672
34879
  logger.info("Uninstall cancelled.");
33673
34880
  return;
@@ -33675,9 +34882,11 @@ ${import_picocolors13.default.yellow("User modifications will be permanently del
33675
34882
  }
33676
34883
  await removeInstallations(installations, {
33677
34884
  dryRun: false,
33678
- forceOverwrite: validOptions.forceOverwrite
34885
+ forceOverwrite: validOptions.forceOverwrite,
34886
+ kit: validOptions.kit
33679
34887
  });
33680
- outro("ClaudeKit uninstalled successfully!");
34888
+ const kitMsg = validOptions.kit ? ` (${validOptions.kit} kit)` : "";
34889
+ outro(`ClaudeKit${kitMsg} uninstalled successfully!`);
33681
34890
  } catch (error) {
33682
34891
  logger.error(error instanceof Error ? error.message : "Unknown error");
33683
34892
  process.exit(1);
@@ -33831,7 +35040,7 @@ var import_compare_versions2 = __toESM(require_umd(), 1);
33831
35040
  // package.json
33832
35041
  var package_default2 = {
33833
35042
  name: "claudekit-cli",
33834
- version: "3.10.2",
35043
+ version: "3.11.0",
33835
35044
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
33836
35045
  type: "module",
33837
35046
  repository: {
@@ -33851,7 +35060,7 @@ var package_default2 = {
33851
35060
  ],
33852
35061
  scripts: {
33853
35062
  dev: "bun run src/index.ts",
33854
- build: "bun build src/index.ts --outdir dist --target node --external keytar --external @octokit/rest",
35063
+ build: "bun build src/index.ts --outdir dist --target node --external @octokit/rest",
33855
35064
  compile: "bun build src/index.ts --compile --outfile ck",
33856
35065
  "compile:binary": "bun build src/index.ts --compile --outfile bin/ck",
33857
35066
  "compile:binaries": "node scripts/build-all-binaries.js",
@@ -33892,11 +35101,11 @@ var package_default2 = {
33892
35101
  "extract-zip": "^2.0.1",
33893
35102
  "fs-extra": "^11.2.0",
33894
35103
  ignore: "^5.3.2",
33895
- keytar: "^7.9.0",
33896
35104
  minimatch: "^10.1.1",
33897
35105
  ora: "^8.0.0",
33898
35106
  "p-limit": "^7.2.0",
33899
35107
  picocolors: "^1.1.1",
35108
+ "proper-lockfile": "^4.1.2",
33900
35109
  tar: "^7.4.3",
33901
35110
  tmp: "^0.2.3",
33902
35111
  zod: "^3.23.8"
@@ -33909,6 +35118,7 @@ var package_default2 = {
33909
35118
  "@types/cli-progress": "^3.11.6",
33910
35119
  "@types/fs-extra": "^11.0.4",
33911
35120
  "@types/node": "^22.10.1",
35121
+ "@types/proper-lockfile": "^4.1.4",
33912
35122
  "@types/tar": "^6.1.13",
33913
35123
  "@types/tmp": "^0.2.6",
33914
35124
  "semantic-release": "^24.2.0",
@@ -34139,14 +35349,14 @@ var import_picocolors15 = __toESM(require_picocolors(), 1);
34139
35349
  // src/domains/versioning/version-cache.ts
34140
35350
  init_logger();
34141
35351
  import { existsSync as existsSync8 } from "node:fs";
34142
- import { mkdir as mkdir11, readFile as readFile17, writeFile as writeFile15 } from "node:fs/promises";
34143
- import { join as join34 } from "node:path";
35352
+ import { mkdir as mkdir11, readFile as readFile18, writeFile as writeFile16 } from "node:fs/promises";
35353
+ import { join as join35 } from "node:path";
34144
35354
  class VersionCacheManager {
34145
35355
  static CACHE_FILENAME = "version-check.json";
34146
35356
  static CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
34147
35357
  static getCacheFile() {
34148
35358
  const cacheDir = PathResolver.getCacheDir(false);
34149
- return join34(cacheDir, VersionCacheManager.CACHE_FILENAME);
35359
+ return join35(cacheDir, VersionCacheManager.CACHE_FILENAME);
34150
35360
  }
34151
35361
  static async load() {
34152
35362
  const cacheFile = VersionCacheManager.getCacheFile();
@@ -34155,7 +35365,7 @@ class VersionCacheManager {
34155
35365
  logger.debug("Version check cache not found");
34156
35366
  return null;
34157
35367
  }
34158
- const content = await readFile17(cacheFile, "utf-8");
35368
+ const content = await readFile18(cacheFile, "utf-8");
34159
35369
  const cache2 = JSON.parse(content);
34160
35370
  if (!cache2.lastCheck || !cache2.currentVersion || !cache2.latestVersion) {
34161
35371
  logger.debug("Invalid cache structure, ignoring");
@@ -34175,7 +35385,7 @@ class VersionCacheManager {
34175
35385
  if (!existsSync8(cacheDir)) {
34176
35386
  await mkdir11(cacheDir, { recursive: true, mode: 448 });
34177
35387
  }
34178
- await writeFile15(cacheFile, JSON.stringify(cache2, null, 2), "utf-8");
35388
+ await writeFile16(cacheFile, JSON.stringify(cache2, null, 2), "utf-8");
34179
35389
  logger.debug(`Version check cache saved to ${cacheFile}`);
34180
35390
  } catch (error) {
34181
35391
  logger.debug(`Failed to save version check cache: ${error}`);
@@ -34658,7 +35868,7 @@ var output2 = new OutputManager2;
34658
35868
 
34659
35869
  // src/shared/path-resolver.ts
34660
35870
  import { homedir as homedir5, platform as platform10 } from "node:os";
34661
- import { join as join35, normalize as normalize6 } from "node:path";
35871
+ import { join as join36, normalize as normalize6 } from "node:path";
34662
35872
 
34663
35873
  class PathResolver2 {
34664
35874
  static getTestHomeDir() {
@@ -34691,50 +35901,50 @@ class PathResolver2 {
34691
35901
  static getConfigDir(global3 = false) {
34692
35902
  const testHome = PathResolver2.getTestHomeDir();
34693
35903
  if (testHome) {
34694
- return global3 ? join35(testHome, ".config", "claude") : join35(testHome, ".claudekit");
35904
+ return global3 ? join36(testHome, ".config", "claude") : join36(testHome, ".claudekit");
34695
35905
  }
34696
35906
  if (!global3) {
34697
- return join35(homedir5(), ".claudekit");
35907
+ return join36(homedir5(), ".claudekit");
34698
35908
  }
34699
35909
  const os2 = platform10();
34700
35910
  if (os2 === "win32") {
34701
- const localAppData = process.env.LOCALAPPDATA || join35(homedir5(), "AppData", "Local");
34702
- return join35(localAppData, "claude");
35911
+ const localAppData = process.env.LOCALAPPDATA || join36(homedir5(), "AppData", "Local");
35912
+ return join36(localAppData, "claude");
34703
35913
  }
34704
35914
  const xdgConfigHome = process.env.XDG_CONFIG_HOME;
34705
35915
  if (xdgConfigHome) {
34706
- return join35(xdgConfigHome, "claude");
35916
+ return join36(xdgConfigHome, "claude");
34707
35917
  }
34708
- return join35(homedir5(), ".config", "claude");
35918
+ return join36(homedir5(), ".config", "claude");
34709
35919
  }
34710
35920
  static getConfigFile(global3 = false) {
34711
- return join35(PathResolver2.getConfigDir(global3), "config.json");
35921
+ return join36(PathResolver2.getConfigDir(global3), "config.json");
34712
35922
  }
34713
35923
  static getCacheDir(global3 = false) {
34714
35924
  const testHome = PathResolver2.getTestHomeDir();
34715
35925
  if (testHome) {
34716
- return global3 ? join35(testHome, ".cache", "claude") : join35(testHome, ".claudekit", "cache");
35926
+ return global3 ? join36(testHome, ".cache", "claude") : join36(testHome, ".claudekit", "cache");
34717
35927
  }
34718
35928
  if (!global3) {
34719
- return join35(homedir5(), ".claudekit", "cache");
35929
+ return join36(homedir5(), ".claudekit", "cache");
34720
35930
  }
34721
35931
  const os2 = platform10();
34722
35932
  if (os2 === "win32") {
34723
- const localAppData = process.env.LOCALAPPDATA || join35(homedir5(), "AppData", "Local");
34724
- return join35(localAppData, "claude", "cache");
35933
+ const localAppData = process.env.LOCALAPPDATA || join36(homedir5(), "AppData", "Local");
35934
+ return join36(localAppData, "claude", "cache");
34725
35935
  }
34726
35936
  const xdgCacheHome = process.env.XDG_CACHE_HOME;
34727
35937
  if (xdgCacheHome) {
34728
- return join35(xdgCacheHome, "claude");
35938
+ return join36(xdgCacheHome, "claude");
34729
35939
  }
34730
- return join35(homedir5(), ".cache", "claude");
35940
+ return join36(homedir5(), ".cache", "claude");
34731
35941
  }
34732
35942
  static getGlobalKitDir() {
34733
35943
  const testHome = PathResolver2.getTestHomeDir();
34734
35944
  if (testHome) {
34735
- return join35(testHome, ".claude");
35945
+ return join36(testHome, ".claude");
34736
35946
  }
34737
- return join35(homedir5(), ".claude");
35947
+ return join36(homedir5(), ".claude");
34738
35948
  }
34739
35949
  static getPathPrefix(global3) {
34740
35950
  return global3 ? "" : ".claude";
@@ -34742,9 +35952,9 @@ class PathResolver2 {
34742
35952
  static buildSkillsPath(baseDir, global3) {
34743
35953
  const prefix = PathResolver2.getPathPrefix(global3);
34744
35954
  if (prefix) {
34745
- return join35(baseDir, prefix, "skills");
35955
+ return join36(baseDir, prefix, "skills");
34746
35956
  }
34747
- return join35(baseDir, "skills");
35957
+ return join36(baseDir, "skills");
34748
35958
  }
34749
35959
  static buildComponentPath(baseDir, component, global3) {
34750
35960
  if (!PathResolver2.isPathSafe(component)) {
@@ -34752,9 +35962,9 @@ class PathResolver2 {
34752
35962
  }
34753
35963
  const prefix = PathResolver2.getPathPrefix(global3);
34754
35964
  if (prefix) {
34755
- return join35(baseDir, prefix, component);
35965
+ return join36(baseDir, prefix, component);
34756
35966
  }
34757
- return join35(baseDir, component);
35967
+ return join36(baseDir, component);
34758
35968
  }
34759
35969
  }
34760
35970
 
@@ -34786,9 +35996,9 @@ async function displayVersion() {
34786
35996
  let localKitVersion = null;
34787
35997
  let isGlobalOnlyKit = false;
34788
35998
  const globalKitDir = PathResolver2.getGlobalKitDir();
34789
- const globalMetadataPath = join36(globalKitDir, "metadata.json");
35999
+ const globalMetadataPath = join37(globalKitDir, "metadata.json");
34790
36000
  const prefix = PathResolver2.getPathPrefix(false);
34791
- const localMetadataPath = prefix ? join36(process.cwd(), prefix, "metadata.json") : join36(process.cwd(), "metadata.json");
36001
+ const localMetadataPath = prefix ? join37(process.cwd(), prefix, "metadata.json") : join37(process.cwd(), "metadata.json");
34792
36002
  const isLocalSameAsGlobal = localMetadataPath === globalMetadataPath;
34793
36003
  if (!isLocalSameAsGlobal && existsSync9(localMetadataPath)) {
34794
36004
  try {
@@ -34892,7 +36102,7 @@ cli.command("versions", "List available versions of ClaudeKit repositories").opt
34892
36102
  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) => {
34893
36103
  await doctorCommand(options);
34894
36104
  });
34895
- 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("--dry-run", "Preview what would be removed without deleting").option("--force-overwrite", "Delete even user-modified files (requires confirmation)").action(async (options) => {
36105
+ 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) => {
34896
36106
  await uninstallCommand(options);
34897
36107
  });
34898
36108
  cli.option("-V, --version", "Display version number");