claudekit-cli 2.6.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +327 -506
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5052,9 +5052,6 @@ var init_types2 = __esm(() => {
5052
5052
  files: exports_external.array(TrackedFileSchema).optional()
5053
5053
  });
5054
5054
  ConfigSchema = exports_external.object({
5055
- github: exports_external.object({
5056
- token: exports_external.string().optional()
5057
- }).optional(),
5058
5055
  defaults: exports_external.object({
5059
5056
  kit: KitType.optional(),
5060
5057
  dir: exports_external.string().optional()
@@ -5425,7 +5422,7 @@ var require_polyfills = __commonJS((exports, module) => {
5425
5422
  var constants = __require("constants");
5426
5423
  var origCwd = process.cwd;
5427
5424
  var cwd = null;
5428
- var platform3 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
5425
+ var platform2 = process.env.GRACEFUL_FS_PLATFORM || process.platform;
5429
5426
  process.cwd = function() {
5430
5427
  if (!cwd)
5431
5428
  cwd = origCwd.call(process);
@@ -5484,7 +5481,7 @@ var require_polyfills = __commonJS((exports, module) => {
5484
5481
  };
5485
5482
  fs.lchownSync = function() {};
5486
5483
  }
5487
- if (platform3 === "win32") {
5484
+ if (platform2 === "win32") {
5488
5485
  fs.rename = typeof fs.rename !== "function" ? fs.rename : function(fs$rename) {
5489
5486
  function rename(from, to, cb) {
5490
5487
  var start = Date.now();
@@ -5924,8 +5921,8 @@ GFS4: `);
5924
5921
  fs2.createReadStream = createReadStream;
5925
5922
  fs2.createWriteStream = createWriteStream2;
5926
5923
  var fs$readFile = fs2.readFile;
5927
- fs2.readFile = readFile3;
5928
- function readFile3(path, options, cb) {
5924
+ fs2.readFile = readFile2;
5925
+ function readFile2(path, options, cb) {
5929
5926
  if (typeof options === "function")
5930
5927
  cb = options, options = null;
5931
5928
  return go$readFile(path, options, cb);
@@ -5941,8 +5938,8 @@ GFS4: `);
5941
5938
  }
5942
5939
  }
5943
5940
  var fs$writeFile = fs2.writeFile;
5944
- fs2.writeFile = writeFile3;
5945
- function writeFile3(path, data, options, cb) {
5941
+ fs2.writeFile = writeFile2;
5942
+ function writeFile2(path, data, options, cb) {
5946
5943
  if (typeof options === "function")
5947
5944
  cb = options, options = null;
5948
5945
  return go$writeFile(path, data, options, cb);
@@ -6862,14 +6859,14 @@ var require_empty = __commonJS((exports, module) => {
6862
6859
  var u = require_universalify().fromPromise;
6863
6860
  var fs = require_fs();
6864
6861
  var path = __require("path");
6865
- var mkdir3 = require_mkdirs();
6862
+ var mkdir2 = require_mkdirs();
6866
6863
  var remove = require_remove();
6867
6864
  var emptyDir = u(async function emptyDir(dir) {
6868
6865
  let items;
6869
6866
  try {
6870
6867
  items = await fs.readdir(dir);
6871
6868
  } catch {
6872
- return mkdir3.mkdirs(dir);
6869
+ return mkdir2.mkdirs(dir);
6873
6870
  }
6874
6871
  return Promise.all(items.map((item) => remove.remove(path.join(dir, item))));
6875
6872
  });
@@ -6878,7 +6875,7 @@ var require_empty = __commonJS((exports, module) => {
6878
6875
  try {
6879
6876
  items = fs.readdirSync(dir);
6880
6877
  } catch {
6881
- return mkdir3.mkdirsSync(dir);
6878
+ return mkdir2.mkdirsSync(dir);
6882
6879
  }
6883
6880
  items.forEach((item) => {
6884
6881
  item = path.join(dir, item);
@@ -6898,7 +6895,7 @@ var require_file = __commonJS((exports, module) => {
6898
6895
  var u = require_universalify().fromPromise;
6899
6896
  var path = __require("path");
6900
6897
  var fs = require_fs();
6901
- var mkdir3 = require_mkdirs();
6898
+ var mkdir2 = require_mkdirs();
6902
6899
  async function createFile(file) {
6903
6900
  let stats;
6904
6901
  try {
@@ -6912,7 +6909,7 @@ var require_file = __commonJS((exports, module) => {
6912
6909
  dirStats = await fs.stat(dir);
6913
6910
  } catch (err) {
6914
6911
  if (err.code === "ENOENT") {
6915
- await mkdir3.mkdirs(dir);
6912
+ await mkdir2.mkdirs(dir);
6916
6913
  await fs.writeFile(file, "");
6917
6914
  return;
6918
6915
  } else {
@@ -6939,7 +6936,7 @@ var require_file = __commonJS((exports, module) => {
6939
6936
  }
6940
6937
  } catch (err) {
6941
6938
  if (err && err.code === "ENOENT")
6942
- mkdir3.mkdirsSync(dir);
6939
+ mkdir2.mkdirsSync(dir);
6943
6940
  else
6944
6941
  throw err;
6945
6942
  }
@@ -6956,7 +6953,7 @@ var require_link = __commonJS((exports, module) => {
6956
6953
  var u = require_universalify().fromPromise;
6957
6954
  var path = __require("path");
6958
6955
  var fs = require_fs();
6959
- var mkdir3 = require_mkdirs();
6956
+ var mkdir2 = require_mkdirs();
6960
6957
  var { pathExists } = require_path_exists();
6961
6958
  var { areIdentical } = require_stat();
6962
6959
  async function createLink(srcpath, dstpath) {
@@ -6976,7 +6973,7 @@ var require_link = __commonJS((exports, module) => {
6976
6973
  const dir = path.dirname(dstpath);
6977
6974
  const dirExists = await pathExists(dir);
6978
6975
  if (!dirExists) {
6979
- await mkdir3.mkdirs(dir);
6976
+ await mkdir2.mkdirs(dir);
6980
6977
  }
6981
6978
  await fs.link(srcpath, dstpath);
6982
6979
  }
@@ -6997,7 +6994,7 @@ var require_link = __commonJS((exports, module) => {
6997
6994
  const dirExists = fs.existsSync(dir);
6998
6995
  if (dirExists)
6999
6996
  return fs.linkSync(srcpath, dstpath);
7000
- mkdir3.mkdirsSync(dir);
6997
+ mkdir2.mkdirsSync(dir);
7001
6998
  return fs.linkSync(srcpath, dstpath);
7002
6999
  }
7003
7000
  module.exports = {
@@ -7237,7 +7234,7 @@ var require_jsonfile = __commonJS((exports, module) => {
7237
7234
  }
7238
7235
  return obj;
7239
7236
  }
7240
- var readFile3 = universalify.fromPromise(_readFile);
7237
+ var readFile2 = universalify.fromPromise(_readFile);
7241
7238
  function readFileSync(file, options = {}) {
7242
7239
  if (typeof options === "string") {
7243
7240
  options = { encoding: options };
@@ -7262,16 +7259,16 @@ var require_jsonfile = __commonJS((exports, module) => {
7262
7259
  const str = stringify(obj, options);
7263
7260
  await universalify.fromCallback(fs.writeFile)(file, str, options);
7264
7261
  }
7265
- var writeFile3 = universalify.fromPromise(_writeFile);
7262
+ var writeFile2 = universalify.fromPromise(_writeFile);
7266
7263
  function writeFileSync(file, obj, options = {}) {
7267
7264
  const fs = options.fs || _fs;
7268
7265
  const str = stringify(obj, options);
7269
7266
  return fs.writeFileSync(file, str, options);
7270
7267
  }
7271
7268
  module.exports = {
7272
- readFile: readFile3,
7269
+ readFile: readFile2,
7273
7270
  readFileSync,
7274
- writeFile: writeFile3,
7271
+ writeFile: writeFile2,
7275
7272
  writeFileSync
7276
7273
  };
7277
7274
  });
@@ -7292,19 +7289,19 @@ var require_output_file = __commonJS((exports, module) => {
7292
7289
  var u = require_universalify().fromPromise;
7293
7290
  var fs = require_fs();
7294
7291
  var path = __require("path");
7295
- var mkdir3 = require_mkdirs();
7292
+ var mkdir2 = require_mkdirs();
7296
7293
  var pathExists = require_path_exists().pathExists;
7297
7294
  async function outputFile(file, data, encoding = "utf-8") {
7298
7295
  const dir = path.dirname(file);
7299
7296
  if (!await pathExists(dir)) {
7300
- await mkdir3.mkdirs(dir);
7297
+ await mkdir2.mkdirs(dir);
7301
7298
  }
7302
7299
  return fs.writeFile(file, data, encoding);
7303
7300
  }
7304
7301
  function outputFileSync(file, ...args) {
7305
7302
  const dir = path.dirname(file);
7306
7303
  if (!fs.existsSync(dir)) {
7307
- mkdir3.mkdirsSync(dir);
7304
+ mkdir2.mkdirsSync(dir);
7308
7305
  }
7309
7306
  fs.writeFileSync(file, ...args);
7310
7307
  }
@@ -13045,7 +13042,7 @@ var cac = (name = "") => new CAC(name);
13045
13042
  // package.json
13046
13043
  var package_default = {
13047
13044
  name: "claudekit-cli",
13048
- version: "2.6.0",
13045
+ version: "3.0.1",
13049
13046
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
13050
13047
  type: "module",
13051
13048
  repository: {
@@ -13134,17 +13131,89 @@ init_dist2();
13134
13131
  import { execSync as execSync2 } from "node:child_process";
13135
13132
 
13136
13133
  // src/lib/auth.ts
13137
- init_dist2();
13138
13134
  init_types2();
13135
+ init_logger();
13139
13136
  import { execSync } from "node:child_process";
13140
13137
 
13141
- // src/utils/config.ts
13138
+ class AuthManager {
13139
+ static token = null;
13140
+ static ghCliInstalled = null;
13141
+ static async getToken() {
13142
+ if (AuthManager.token) {
13143
+ return { token: AuthManager.token, method: "gh-cli" };
13144
+ }
13145
+ if (!AuthManager.isGhCliInstalled()) {
13146
+ throw new AuthenticationError(`GitHub CLI is not installed.
13147
+
13148
+ ` + `ClaudeKit requires GitHub CLI for accessing private repositories.
13149
+
13150
+ ` + `To install:
13151
+ ` + ` macOS: brew install gh
13152
+ ` + ` Windows: winget install GitHub.cli
13153
+ ` + ` Linux: sudo apt install gh (or see: gh.io/install)
13154
+
13155
+ ` + "After installing, run: gh auth login");
13156
+ }
13157
+ const token = AuthManager.getFromGhCli();
13158
+ if (token) {
13159
+ AuthManager.token = token;
13160
+ logger.debug("Using GitHub CLI authentication");
13161
+ return { token, method: "gh-cli" };
13162
+ }
13163
+ throw new AuthenticationError(`GitHub CLI is not authenticated.
13164
+
13165
+ ` + `Run: gh auth login
13166
+
13167
+ ` + "Then follow the prompts to authenticate with your GitHub account.");
13168
+ }
13169
+ static isGhCliInstalled() {
13170
+ if (AuthManager.ghCliInstalled !== null) {
13171
+ return AuthManager.ghCliInstalled;
13172
+ }
13173
+ try {
13174
+ execSync("gh --version", {
13175
+ stdio: "ignore",
13176
+ timeout: 5000
13177
+ });
13178
+ AuthManager.ghCliInstalled = true;
13179
+ return true;
13180
+ } catch {
13181
+ AuthManager.ghCliInstalled = false;
13182
+ return false;
13183
+ }
13184
+ }
13185
+ static getFromGhCli() {
13186
+ try {
13187
+ const token = execSync("gh auth token", {
13188
+ encoding: "utf-8",
13189
+ stdio: ["pipe", "pipe", "ignore"],
13190
+ timeout: 5000
13191
+ }).trim();
13192
+ if (token && token.length > 0) {
13193
+ return token;
13194
+ }
13195
+ return null;
13196
+ } catch {
13197
+ return null;
13198
+ }
13199
+ }
13200
+ static async clearToken() {
13201
+ AuthManager.token = null;
13202
+ logger.debug("Cleared cached token");
13203
+ }
13204
+ }
13205
+
13206
+ // src/lib/github.ts
13142
13207
  init_types2();
13143
13208
  init_logger();
13209
+ import { Octokit } from "@octokit/rest";
13210
+
13211
+ // src/lib/release-cache.ts
13212
+ init_zod();
13213
+ init_logger();
13144
13214
  import { existsSync } from "node:fs";
13145
- import { mkdir, readFile, writeFile } from "node:fs/promises";
13146
- import { chmod } from "node:fs/promises";
13147
- import { platform as platform2 } from "node:os";
13215
+ import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
13216
+ import { join as join2 } from "node:path";
13148
13217
 
13149
13218
  // src/utils/path-resolver.ts
13150
13219
  import { homedir, platform } from "node:os";
@@ -13248,243 +13317,7 @@ class PathResolver {
13248
13317
  }
13249
13318
  }
13250
13319
 
13251
- // src/utils/config.ts
13252
- class ConfigManager {
13253
- static config = null;
13254
- static globalFlag = false;
13255
- static setGlobalFlag(global2) {
13256
- ConfigManager.globalFlag = global2;
13257
- ConfigManager.config = null;
13258
- }
13259
- static getGlobalFlag() {
13260
- return ConfigManager.globalFlag;
13261
- }
13262
- static async load() {
13263
- if (ConfigManager.config) {
13264
- return ConfigManager.config;
13265
- }
13266
- const configFile = PathResolver.getConfigFile(ConfigManager.globalFlag);
13267
- try {
13268
- if (existsSync(configFile)) {
13269
- const content = await readFile(configFile, "utf-8");
13270
- const data = JSON.parse(content);
13271
- ConfigManager.config = ConfigSchema.parse(data);
13272
- logger.debug(`Config loaded from ${configFile}`);
13273
- return ConfigManager.config;
13274
- }
13275
- } catch (error) {
13276
- logger.warning(`Failed to load config: ${error instanceof Error ? error.message : "Unknown error"}`);
13277
- }
13278
- ConfigManager.config = { github: {}, defaults: {} };
13279
- return ConfigManager.config;
13280
- }
13281
- static async save(config) {
13282
- try {
13283
- const validConfig = ConfigSchema.parse(config);
13284
- const configDir = PathResolver.getConfigDir(ConfigManager.globalFlag);
13285
- const configFile = PathResolver.getConfigFile(ConfigManager.globalFlag);
13286
- if (!existsSync(configDir)) {
13287
- await mkdir(configDir, { recursive: true });
13288
- if (platform2() !== "win32") {
13289
- await chmod(configDir, 448);
13290
- }
13291
- }
13292
- await writeFile(configFile, JSON.stringify(validConfig, null, 2), "utf-8");
13293
- if (platform2() !== "win32") {
13294
- await chmod(configFile, 384);
13295
- }
13296
- ConfigManager.config = validConfig;
13297
- logger.debug(`Config saved to ${configFile}`);
13298
- } catch (error) {
13299
- throw new Error(`Failed to save config: ${error instanceof Error ? error.message : "Unknown error"}`);
13300
- }
13301
- }
13302
- static async get() {
13303
- return ConfigManager.load();
13304
- }
13305
- static async set(key, value) {
13306
- const config = await ConfigManager.load();
13307
- const keys = key.split(".");
13308
- let current = config;
13309
- for (let i = 0;i < keys.length - 1; i++) {
13310
- if (!(keys[i] in current)) {
13311
- current[keys[i]] = {};
13312
- }
13313
- current = current[keys[i]];
13314
- }
13315
- current[keys[keys.length - 1]] = value;
13316
- await ConfigManager.save(config);
13317
- }
13318
- static async getToken() {
13319
- const config = await ConfigManager.load();
13320
- return config.github?.token;
13321
- }
13322
- static async setToken(token) {
13323
- await ConfigManager.set("github.token", token);
13324
- }
13325
- }
13326
-
13327
- // src/lib/auth.ts
13328
- init_logger();
13329
- var keytarModule = null;
13330
- async function getKeytar() {
13331
- if (keytarModule)
13332
- return keytarModule;
13333
- try {
13334
- keytarModule = await import("keytar");
13335
- return keytarModule;
13336
- } catch (error) {
13337
- logger.debug(`Keytar not available: ${String(error)}`);
13338
- return null;
13339
- }
13340
- }
13341
- var SERVICE_NAME = "claudekit-cli";
13342
- var ACCOUNT_NAME = "github-token";
13343
-
13344
- class AuthManager {
13345
- static token = null;
13346
- static authMethod = null;
13347
- static async getToken() {
13348
- if (AuthManager.token && AuthManager.authMethod) {
13349
- return { token: AuthManager.token, method: AuthManager.authMethod };
13350
- }
13351
- try {
13352
- const token = await AuthManager.getFromGhCli();
13353
- if (token) {
13354
- AuthManager.token = token;
13355
- AuthManager.authMethod = "gh-cli";
13356
- logger.debug("Using GitHub CLI authentication");
13357
- return { token, method: "gh-cli" };
13358
- }
13359
- } catch (error) {
13360
- logger.debug("GitHub CLI not available");
13361
- }
13362
- const envToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
13363
- if (envToken) {
13364
- AuthManager.token = envToken;
13365
- AuthManager.authMethod = "env-var";
13366
- logger.debug("Using environment variable authentication");
13367
- return { token: envToken, method: "env-var" };
13368
- }
13369
- try {
13370
- const configToken = await ConfigManager.getToken();
13371
- if (configToken) {
13372
- AuthManager.token = configToken;
13373
- AuthManager.authMethod = "env-var";
13374
- logger.debug("Using config file authentication");
13375
- return { token: configToken, method: "env-var" };
13376
- }
13377
- } catch (error) {
13378
- logger.debug("No token in config file");
13379
- }
13380
- try {
13381
- const keytar = await getKeytar();
13382
- if (keytar) {
13383
- const keychainToken = await keytar.getPassword(SERVICE_NAME, ACCOUNT_NAME);
13384
- if (keychainToken) {
13385
- AuthManager.token = keychainToken;
13386
- AuthManager.authMethod = "keychain";
13387
- logger.debug("Using keychain authentication");
13388
- return { token: keychainToken, method: "keychain" };
13389
- }
13390
- } else {
13391
- logger.debug("Keychain not available on this system");
13392
- }
13393
- } catch (error) {
13394
- logger.debug("No token in keychain");
13395
- }
13396
- const promptedToken = await AuthManager.promptForToken();
13397
- AuthManager.token = promptedToken;
13398
- AuthManager.authMethod = "prompt";
13399
- return { token: promptedToken, method: "prompt" };
13400
- }
13401
- static async getFromGhCli() {
13402
- try {
13403
- const token = execSync("gh auth token", {
13404
- encoding: "utf-8",
13405
- stdio: ["pipe", "pipe", "ignore"],
13406
- timeout: 5000
13407
- }).trim();
13408
- if (token && token.length > 0) {
13409
- return token;
13410
- }
13411
- return null;
13412
- } catch {
13413
- return null;
13414
- }
13415
- }
13416
- static async promptForToken() {
13417
- logger.info("");
13418
- logger.info("GitHub authentication required to access private repositories.");
13419
- logger.info("");
13420
- logger.info("\uD83D\uDCA1 Tip: For easier setup, use GitHub CLI instead:");
13421
- logger.info(" gh auth login");
13422
- logger.info("");
13423
- logger.info("Or create a Personal Access Token:");
13424
- logger.info(" https://github.com/settings/tokens/new?scopes=repo&description=ClaudeKit%20CLI");
13425
- logger.info("");
13426
- const token = await re({
13427
- message: "Enter your GitHub Personal Access Token:",
13428
- validate: (value) => {
13429
- if (!value || value.length === 0) {
13430
- return "Token is required";
13431
- }
13432
- if (!value.startsWith("ghp_") && !value.startsWith("github_pat_")) {
13433
- return 'Invalid token format. Token should start with "ghp_" or "github_pat_"';
13434
- }
13435
- return;
13436
- }
13437
- });
13438
- if (lD(token)) {
13439
- logger.info("");
13440
- logger.info("Alternative: Set GITHUB_TOKEN environment variable or use 'gh auth login'");
13441
- throw new AuthenticationError("Authentication cancelled by user");
13442
- }
13443
- const keytar = await getKeytar();
13444
- if (keytar) {
13445
- const save = await se({
13446
- message: "Save token securely in OS keychain?"
13447
- });
13448
- if (save && !lD(save)) {
13449
- try {
13450
- await keytar.setPassword(SERVICE_NAME, ACCOUNT_NAME, token);
13451
- logger.success("Token saved securely in keychain");
13452
- } catch (error) {
13453
- logger.warning("Failed to save token to keychain");
13454
- }
13455
- }
13456
- }
13457
- return token;
13458
- }
13459
- static async clearToken() {
13460
- AuthManager.token = null;
13461
- AuthManager.authMethod = null;
13462
- try {
13463
- const keytar = await getKeytar();
13464
- if (keytar) {
13465
- await keytar.deletePassword(SERVICE_NAME, ACCOUNT_NAME);
13466
- logger.success("Token cleared from keychain");
13467
- }
13468
- } catch (error) {
13469
- logger.warning("Failed to clear token from keychain");
13470
- }
13471
- }
13472
- static isValidTokenFormat(token) {
13473
- return token.startsWith("ghp_") || token.startsWith("github_pat_");
13474
- }
13475
- }
13476
-
13477
- // src/lib/github.ts
13478
- init_types2();
13479
- init_logger();
13480
- import { Octokit } from "@octokit/rest";
13481
-
13482
13320
  // src/lib/release-cache.ts
13483
- init_zod();
13484
- init_logger();
13485
- import { existsSync as existsSync2 } from "node:fs";
13486
- import { mkdir as mkdir2, readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
13487
- import { join as join2 } from "node:path";
13488
13321
  var ReleaseCacheEntrySchema = exports_external.object({
13489
13322
  timestamp: exports_external.number(),
13490
13323
  releases: exports_external.array(exports_external.any())
@@ -13500,11 +13333,11 @@ class ReleaseCache {
13500
13333
  async get(key) {
13501
13334
  const cacheFile = this.getCachePath(key);
13502
13335
  try {
13503
- if (!existsSync2(cacheFile)) {
13336
+ if (!existsSync(cacheFile)) {
13504
13337
  logger.debug(`Release cache not found for key: ${key}`);
13505
13338
  return null;
13506
13339
  }
13507
- const content = await readFile2(cacheFile, "utf-8");
13340
+ const content = await readFile(cacheFile, "utf-8");
13508
13341
  const parsed = JSON.parse(content);
13509
13342
  const cacheEntry = ReleaseCacheEntrySchema.parse(parsed);
13510
13343
  if (this.isExpired(cacheEntry.timestamp)) {
@@ -13525,12 +13358,12 @@ class ReleaseCache {
13525
13358
  async set(key, releases) {
13526
13359
  const cacheFile = this.getCachePath(key);
13527
13360
  try {
13528
- await mkdir2(this.cacheDir, { recursive: true, mode: 448 });
13361
+ await mkdir(this.cacheDir, { recursive: true, mode: 448 });
13529
13362
  const cacheEntry = {
13530
13363
  timestamp: Date.now(),
13531
13364
  releases
13532
13365
  };
13533
- await writeFile2(cacheFile, JSON.stringify(cacheEntry, null, 2), "utf-8");
13366
+ await writeFile(cacheFile, JSON.stringify(cacheEntry, null, 2), "utf-8");
13534
13367
  logger.debug(`Release cache set for key: ${key}, cached ${releases.length} releases`);
13535
13368
  } catch (error) {
13536
13369
  logger.debug(`Failed to set release cache for key ${key}: ${error}`);
@@ -13540,7 +13373,7 @@ class ReleaseCache {
13540
13373
  if (key) {
13541
13374
  const cacheFile = this.getCachePath(key);
13542
13375
  try {
13543
- if (existsSync2(cacheFile)) {
13376
+ if (existsSync(cacheFile)) {
13544
13377
  await unlink(cacheFile);
13545
13378
  logger.debug(`Release cache cleared for key: ${key}`);
13546
13379
  }
@@ -13850,50 +13683,57 @@ class GitHubClient {
13850
13683
  });
13851
13684
  return GitHubReleaseSchema.parse(data);
13852
13685
  } catch (error) {
13853
- if (error?.status === 404) {
13854
- throw new GitHubError(`Cannot access ${kit.name} repository.
13686
+ return this.handleHttpError(error, {
13687
+ kit,
13688
+ operation: "fetch release",
13689
+ verboseFlag: "ck new --verbose"
13690
+ });
13691
+ }
13692
+ }
13693
+ async invalidateAuth() {
13694
+ await AuthManager.clearToken();
13695
+ this.octokit = null;
13696
+ logger.debug("Invalidated cached authentication due to 401 error");
13697
+ }
13698
+ async handleHttpError(error, context) {
13699
+ const { kit, operation, verboseFlag = "ck new --verbose" } = context;
13700
+ if (error?.status === 401) {
13701
+ await this.invalidateAuth();
13702
+ throw new GitHubError(`Authentication failed.
13855
13703
 
13856
- Possible causes:
13857
- • You haven't accepted the GitHub repository invitation
13858
- • Your token lacks the 'repo' scope (needs full private repo access)
13859
- • You're not added as a collaborator yet
13860
- • Repository doesn't exist
13704
+ Your GitHub CLI session may have expired.
13861
13705
 
13862
- Solutions:
13863
- 1. Check email for GitHub invitation and accept it
13864
- 2. Use 'gh auth login' for automatic authentication (recommended)
13865
- 3. Recreate token with 'repo' scope: https://github.com/settings/tokens/new?scopes=repo
13866
- 4. Wait 2-5 minutes after accepting invitation for permissions to sync
13706
+ Solution: Re-authenticate with GitHub CLI
13707
+ gh auth login
13867
13708
 
13868
- Need help? Run with: ck new --verbose`, 404);
13869
- }
13870
- if (error?.status === 401) {
13871
- throw new GitHubError(`Authentication failed - token is invalid or expired.
13709
+ Need help? Run with: ${verboseFlag}`, 401);
13710
+ }
13711
+ if (error?.status === 403) {
13712
+ throw new GitHubError(`Access forbidden.
13872
13713
 
13873
- ` + `Solutions:
13874
- ` + ` 1. Use GitHub CLI (recommended): gh auth login
13875
- ` + ` 2. Create new token: https://github.com/settings/tokens/new?scopes=repo
13876
- ` + ` 3. Verify token format (should start with 'ghp_' or 'github_pat_')
13877
- ` + ` 4. Check token is set: echo $GITHUB_TOKEN
13714
+ Your GitHub CLI session may lack required permissions.
13878
13715
 
13879
- ` + "Need help? Run with: ck new --verbose", 401);
13880
- }
13881
- if (error?.status === 403) {
13882
- throw new GitHubError(`Access forbidden - token lacks required permissions.
13716
+ Solution: Re-authenticate with GitHub CLI
13717
+ gh auth login
13883
13718
 
13884
- ` + `Your token needs the 'repo' scope for private repositories.
13719
+ Need help? Run with: ${verboseFlag}`, 403);
13720
+ }
13721
+ if (error?.status === 404) {
13722
+ throw new GitHubError(`Cannot access ${kit.name} repository.
13885
13723
 
13886
- ` + `Solutions:
13887
- ` + ` 1. Use GitHub CLI (handles scopes automatically): gh auth login
13888
- ` + ` 2. Recreate token with 'repo' scope: https://github.com/settings/tokens/new?scopes=repo
13889
- ` + ` 3. Check existing token scopes: https://github.com/settings/tokens
13724
+ Possible causes:
13725
+ You haven't accepted the GitHub repository invitation
13726
+ You're not added as a collaborator yet
13727
+ Repository doesn't exist
13890
13728
 
13891
- ` + `Common mistake: Using 'public_repo' scope doesn't work for private repos.
13729
+ Solutions:
13730
+ 1. Check email for GitHub invitation and accept it
13731
+ 2. Re-authenticate: gh auth login
13732
+ 3. Wait 2-5 minutes after accepting invitation for permissions to sync
13892
13733
 
13893
- ` + "Need help? Run with: ck new --verbose", 403);
13894
- }
13895
- throw new GitHubError(`Failed to fetch release: ${error?.message || "Unknown error"}`, error?.status);
13734
+ Need help? Run with: ${verboseFlag}`, 404);
13896
13735
  }
13736
+ throw new GitHubError(`Failed to ${operation}: ${error?.message || "Unknown error"}`, error?.status);
13897
13737
  }
13898
13738
  async getReleaseByTag(kit, tag) {
13899
13739
  try {
@@ -13912,40 +13752,19 @@ Need help? Run with: ck new --verbose`, 404);
13912
13752
  Possible causes:
13913
13753
  • Release version doesn't exist (check: ck versions --kit ${kit.name.toLowerCase()})
13914
13754
  • You don't have repository access
13915
- • Your token lacks the 'repo' scope
13916
13755
 
13917
13756
  Solutions:
13918
13757
  1. List available versions: ck versions --kit ${kit.name.toLowerCase()}
13919
13758
  2. Check email for GitHub invitation and accept it
13920
- 3. Use 'gh auth login' for automatic authentication
13921
- 4. Recreate token: https://github.com/settings/tokens/new?scopes=repo
13759
+ 3. Re-authenticate: gh auth login
13922
13760
 
13923
13761
  Need help? Run with: ck new --verbose`, 404);
13924
13762
  }
13925
- if (error?.status === 401) {
13926
- throw new GitHubError(`Authentication failed - token is invalid or expired.
13927
-
13928
- ` + `Solutions:
13929
- ` + ` 1. Use GitHub CLI (recommended): gh auth login
13930
- ` + ` 2. Create new token: https://github.com/settings/tokens/new?scopes=repo
13931
- ` + ` 3. Verify token format (should start with 'ghp_' or 'github_pat_')
13932
- ` + ` 4. Check token is set: echo $GITHUB_TOKEN
13933
-
13934
- ` + "Need help? Run with: ck new --verbose", 401);
13935
- }
13936
- if (error?.status === 403) {
13937
- throw new GitHubError(`Access forbidden - token lacks required permissions.
13938
-
13939
- ` + `Your token needs the 'repo' scope for private repositories.
13940
-
13941
- ` + `Solutions:
13942
- ` + ` 1. Use GitHub CLI (handles scopes automatically): gh auth login
13943
- ` + ` 2. Recreate token with 'repo' scope: https://github.com/settings/tokens/new?scopes=repo
13944
- ` + ` 3. Check existing token scopes: https://github.com/settings/tokens
13945
-
13946
- ` + "Need help? Run with: ck new --verbose", 403);
13947
- }
13948
- throw new GitHubError(`Failed to fetch release: ${error?.message || "Unknown error"}`, error?.status);
13763
+ return this.handleHttpError(error, {
13764
+ kit,
13765
+ operation: "fetch release",
13766
+ verboseFlag: "ck new --verbose"
13767
+ });
13949
13768
  }
13950
13769
  }
13951
13770
  async listReleases(kit, limit = 10) {
@@ -13959,40 +13778,11 @@ Need help? Run with: ck new --verbose`, 404);
13959
13778
  });
13960
13779
  return data.map((release) => GitHubReleaseSchema.parse(release));
13961
13780
  } catch (error) {
13962
- if (error?.status === 401) {
13963
- throw new GitHubError(`Authentication failed - token is invalid or expired.
13964
-
13965
- ` + `Solutions:
13966
- ` + ` 1. Use GitHub CLI (recommended): gh auth login
13967
- ` + ` 2. Create new token: https://github.com/settings/tokens/new?scopes=repo
13968
- ` + ` 3. Verify token format (should start with 'ghp_' or 'github_pat_')
13969
-
13970
- ` + "Need help? Run with: ck versions --verbose", 401);
13971
- }
13972
- if (error?.status === 403) {
13973
- throw new GitHubError(`Access forbidden - token lacks required permissions.
13974
-
13975
- ` + `Your token needs the 'repo' scope for private repositories.
13976
-
13977
- ` + `Solutions:
13978
- ` + ` 1. Use GitHub CLI (handles scopes automatically): gh auth login
13979
- ` + ` 2. Recreate token with 'repo' scope: https://github.com/settings/tokens/new?scopes=repo
13980
-
13981
- ` + "Need help? Run with: ck versions --verbose", 403);
13982
- }
13983
- if (error?.status === 404) {
13984
- throw new GitHubError(`Cannot access ${kit.name} repository.
13985
-
13986
- You may not have been added as a collaborator yet.
13987
-
13988
- Solutions:
13989
- 1. Check email for GitHub invitation and accept it
13990
- 2. Contact support to verify repository access
13991
- 3. Use 'gh auth login' for automatic authentication
13992
-
13993
- Need help? Run with: ck versions --verbose`, 404);
13994
- }
13995
- throw new GitHubError(`Failed to list releases: ${error?.message || "Unknown error"}`, error?.status);
13781
+ return this.handleHttpError(error, {
13782
+ kit,
13783
+ operation: "list releases",
13784
+ verboseFlag: "ck versions --verbose"
13785
+ });
13996
13786
  }
13997
13787
  }
13998
13788
  async checkAccess(kit) {
@@ -14009,45 +13799,22 @@ Need help? Run with: ck versions --verbose`, 404);
14009
13799
 
14010
13800
  Possible causes:
14011
13801
  • You haven't accepted the GitHub repository invitation
14012
- • Your token lacks the 'repo' scope (needs full private repo access)
14013
13802
  • You're not added as a collaborator yet
14014
- Token belongs to a different GitHub account
13803
+ You're logged into a different GitHub account
14015
13804
 
14016
13805
  Solutions:
14017
13806
  1. Check email for GitHub invitation and accept it
14018
- 2. Use 'gh auth login' for automatic authentication (recommended)
14019
- 3. Recreate token with 'repo' scope: https://github.com/settings/tokens/new?scopes=repo
14020
- 4. Verify you're using the correct GitHub account
14021
- 5. Wait 2-5 minutes after accepting invitation for permissions to sync
13807
+ 2. Re-authenticate: gh auth login
13808
+ 3. Verify you're using the correct GitHub account
13809
+ 4. Wait 2-5 minutes after accepting invitation for permissions to sync
14022
13810
 
14023
13811
  Need help? Run with: ck new --verbose`, 404);
14024
13812
  }
14025
- if (error?.status === 403) {
14026
- throw new GitHubError(`Access forbidden - token lacks required permissions.
14027
-
14028
- ` + `Your token needs the 'repo' scope for private repositories.
14029
-
14030
- ` + `Solutions:
14031
- ` + ` 1. Use GitHub CLI (handles scopes automatically): gh auth login
14032
- ` + ` 2. Recreate token with 'repo' scope: https://github.com/settings/tokens/new?scopes=repo
14033
- ` + ` 3. Check existing token scopes: https://github.com/settings/tokens
14034
-
14035
- ` + `Common mistake: Using 'public_repo' scope doesn't work for private repos.
14036
-
14037
- ` + "Need help? Run with: ck new --verbose", 403);
14038
- }
14039
- if (error?.status === 401) {
14040
- throw new GitHubError(`Authentication failed - token is invalid or expired.
14041
-
14042
- ` + `Solutions:
14043
- ` + ` 1. Use GitHub CLI (recommended): gh auth login
14044
- ` + ` 2. Create new token: https://github.com/settings/tokens/new?scopes=repo
14045
- ` + ` 3. Verify token format (should start with 'ghp_' or 'github_pat_')
14046
- ` + ` 4. Check token is set: echo $GITHUB_TOKEN
14047
-
14048
- ` + "Need help? Run with: ck new --verbose", 401);
14049
- }
14050
- throw new GitHubError(`Failed to check repository access: ${error?.message || "Unknown error"}`, error?.status);
13813
+ return this.handleHttpError(error, {
13814
+ kit,
13815
+ operation: "check repository access",
13816
+ verboseFlag: "ck new --verbose"
13817
+ });
14051
13818
  }
14052
13819
  }
14053
13820
  async listReleasesWithCache(kit, options = {}) {
@@ -14174,18 +13941,18 @@ import { promisify } from "node:util";
14174
13941
  var execAsync = promisify(exec);
14175
13942
  var isCIEnvironment = process.env.CI === "true" || process.env.CI_SAFE_MODE === "true";
14176
13943
  function getOSInfo() {
14177
- const platform3 = process.platform;
13944
+ const platform2 = process.platform;
14178
13945
  const arch = process.arch;
14179
- const isWindows = platform3 === "win32";
14180
- const isMacOS = platform3 === "darwin";
14181
- const isLinux = platform3 === "linux";
13946
+ const isWindows = platform2 === "win32";
13947
+ const isMacOS = platform2 === "darwin";
13948
+ const isLinux = platform2 === "linux";
14182
13949
  const isWSL = isLinux && process.env.WSL_DISTRO_NAME !== undefined;
14183
- let details = `${platform3}-${arch}`;
13950
+ let details = `${platform2}-${arch}`;
14184
13951
  if (isWSL) {
14185
13952
  details += ` (WSL: ${process.env.WSL_DISTRO_NAME})`;
14186
13953
  }
14187
13954
  return {
14188
- platform: platform3,
13955
+ platform: platform2,
14189
13956
  arch,
14190
13957
  isWindows,
14191
13958
  isMacOS,
@@ -14462,68 +14229,40 @@ function checkEnvironmentVariables() {
14462
14229
  const githubToken = process.env.GITHUB_TOKEN;
14463
14230
  const ghToken = process.env.GH_TOKEN;
14464
14231
  if (githubToken || ghToken) {
14465
- const tokenToCheck = githubToken || ghToken;
14466
14232
  const tokenVar = githubToken ? "GITHUB_TOKEN" : "GH_TOKEN";
14467
- if (!tokenToCheck?.startsWith("ghp_") && !tokenToCheck?.startsWith("github_pat_")) {
14468
- return {
14469
- name: "Environment Variables",
14470
- status: "fail",
14471
- message: `${tokenVar} is set but has invalid format`,
14472
- details: "Token should start with 'ghp_' or 'github_pat_'",
14473
- suggestion: "Create new token: https://github.com/settings/tokens/new?scopes=repo"
14474
- };
14475
- }
14476
14233
  return {
14477
14234
  name: "Environment Variables",
14478
- status: "pass",
14479
- message: `${tokenVar} is set and has valid format`,
14480
- details: `Token: ${tokenToCheck.substring(0, 8)}...`
14235
+ status: "info",
14236
+ message: `${tokenVar} is set but PAT authentication is no longer supported`,
14237
+ details: "ClaudeKit now uses GitHub CLI exclusively for authentication",
14238
+ suggestion: "Run: gh auth login"
14481
14239
  };
14482
14240
  }
14483
14241
  return {
14484
14242
  name: "Environment Variables",
14485
14243
  status: "info",
14486
- message: "No GitHub token found in environment variables",
14487
- details: "GITHUB_TOKEN or GH_TOKEN not set",
14488
- suggestion: `Set token:
14489
- ` + ` • Unix/Mac: export GITHUB_TOKEN=ghp_xxx
14490
- ` + " • Windows: [System.Environment]::SetEnvironmentVariable('GITHUB_TOKEN', 'ghp_xxx', 'User')"
14244
+ message: "No GitHub token found in environment variables (expected)",
14245
+ details: "ClaudeKit uses GitHub CLI for authentication"
14491
14246
  };
14492
14247
  }
14493
14248
  async function checkAuthentication() {
14494
14249
  try {
14495
- const { token, method } = await AuthManager.getToken();
14496
- const methodLabels = {
14497
- "gh-cli": "GitHub CLI",
14498
- "env-var": "Environment Variable",
14499
- keychain: "OS Keychain",
14500
- prompt: "User Prompt"
14501
- };
14502
- if (!AuthManager.isValidTokenFormat(token)) {
14503
- return {
14504
- name: "Authentication",
14505
- status: "fail",
14506
- message: "Token has invalid format",
14507
- details: "Token should start with 'ghp_' or 'github_pat_'",
14508
- suggestion: "Create new token: https://github.com/settings/tokens/new?scopes=repo"
14509
- };
14510
- }
14250
+ const { token } = await AuthManager.getToken();
14511
14251
  return {
14512
14252
  name: "Authentication",
14513
14253
  status: "pass",
14514
- message: `Successfully authenticated via ${methodLabels[method]}`,
14254
+ message: "Successfully authenticated via GitHub CLI",
14515
14255
  details: `Token: ${token.substring(0, 8)}...`
14516
14256
  };
14517
14257
  } catch (error) {
14518
14258
  return {
14519
14259
  name: "Authentication",
14520
14260
  status: "fail",
14521
- message: "Failed to obtain authentication token",
14261
+ message: "GitHub CLI authentication required",
14522
14262
  details: error?.message || "Unknown error",
14523
- suggestion: `Options:
14524
- ` + ` 1. Use GitHub CLI: gh auth login
14525
- ` + ` 2. Set GITHUB_TOKEN environment variable
14526
- ` + " 3. Create token: https://github.com/settings/tokens/new?scopes=repo"
14263
+ suggestion: `To authenticate:
14264
+ ` + ` 1. Install GitHub CLI: https://cli.github.com
14265
+ ` + " 2. Run: gh auth login"
14527
14266
  };
14528
14267
  }
14529
14268
  }
@@ -14552,7 +14291,7 @@ async function checkRepositoryAccess(kit) {
14552
14291
  message: `Cannot access ${kitConfig.owner}/${kitConfig.repo}`,
14553
14292
  suggestion: `Solutions:
14554
14293
  ` + ` 1. Check email for GitHub invitation and accept it
14555
- ` + ` 2. Verify token has 'repo' scope (not just 'public_repo')
14294
+ ` + ` 2. Re-authenticate: gh auth login
14556
14295
  ` + ` 3. Wait 2-5 minutes after accepting invitation
14557
14296
  ` + " 4. Contact support if issue persists"
14558
14297
  };
@@ -14563,9 +14302,9 @@ async function checkRepositoryAccess(kit) {
14563
14302
  message: "Failed to check repository access",
14564
14303
  details: error?.message || "Unknown error",
14565
14304
  suggestion: `Possible causes:
14566
- ` + ` • Token lacks 'repo' scope
14567
14305
  ` + ` • You haven't been added as collaborator
14568
- ` + " • Network connectivity issues"
14306
+ ` + ` • Network connectivity issues
14307
+ ` + " • Try: gh auth login"
14569
14308
  };
14570
14309
  }
14571
14310
  }
@@ -14633,7 +14372,7 @@ function checkSystemInfo() {
14633
14372
 
14634
14373
  // src/commands/doctor.ts
14635
14374
  init_dist2();
14636
- import { existsSync as existsSync4 } from "node:fs";
14375
+ import { existsSync as existsSync3 } from "node:fs";
14637
14376
  import { join as join4 } from "node:path";
14638
14377
 
14639
14378
  // src/utils/claudekit-scanner.ts
@@ -14732,16 +14471,16 @@ import { promisify as promisify2 } from "node:util";
14732
14471
  init_logger();
14733
14472
  var execAsync2 = promisify2(exec2);
14734
14473
  async function detectOS() {
14735
- const platform3 = process.platform;
14736
- const info = { platform: platform3 };
14737
- if (platform3 === "darwin") {
14474
+ const platform2 = process.platform;
14475
+ const info = { platform: platform2 };
14476
+ if (platform2 === "darwin") {
14738
14477
  try {
14739
14478
  await execAsync2("which brew");
14740
14479
  info.hasHomebrew = true;
14741
14480
  } catch {
14742
14481
  info.hasHomebrew = false;
14743
14482
  }
14744
- } else if (platform3 === "linux") {
14483
+ } else if (platform2 === "linux") {
14745
14484
  try {
14746
14485
  if (fs.existsSync("/etc/os-release")) {
14747
14486
  const content = fs.readFileSync("/etc/os-release", "utf-8");
@@ -15013,14 +14752,14 @@ function getManualInstructions(dependency, osInfo) {
15013
14752
  init_environment();
15014
14753
  init_logger();
15015
14754
  function checkSkillsInstallation() {
15016
- const platform3 = process.platform;
15017
- const scriptName = platform3 === "win32" ? "install.ps1" : "install.sh";
14755
+ const platform2 = process.platform;
14756
+ const scriptName = platform2 === "win32" ? "install.ps1" : "install.sh";
15018
14757
  const globalSkillsDir = join4(PathResolver.getGlobalKitDir(), "skills");
15019
14758
  const globalScriptPath = join4(globalSkillsDir, scriptName);
15020
- const globalAvailable = existsSync4(globalScriptPath);
14759
+ const globalAvailable = existsSync3(globalScriptPath);
15021
14760
  const projectSkillsDir = join4(process.cwd(), ".claude", "skills");
15022
14761
  const projectScriptPath = join4(projectSkillsDir, scriptName);
15023
- const projectAvailable = existsSync4(projectScriptPath);
14762
+ const projectAvailable = existsSync3(projectScriptPath);
15024
14763
  return {
15025
14764
  global: {
15026
14765
  available: globalAvailable,
@@ -15292,7 +15031,7 @@ import { join as join22, resolve as resolve4 } from "node:path";
15292
15031
  // src/lib/commands-prefix.ts
15293
15032
  init_logger();
15294
15033
  var import_fs_extra3 = __toESM(require_lib(), 1);
15295
- import { lstat, mkdir as mkdir3, readdir as readdir2, stat as stat2 } from "node:fs/promises";
15034
+ import { lstat, mkdir as mkdir2, readdir as readdir2, stat as stat2 } from "node:fs/promises";
15296
15035
  import { join as join6 } from "node:path";
15297
15036
 
15298
15037
  // src/utils/manifest-writer.ts
@@ -15451,8 +15190,11 @@ class OwnershipChecker {
15451
15190
  const hash = createHash("sha256");
15452
15191
  const stream = createReadStream(filePath);
15453
15192
  stream.on("data", (chunk) => hash.update(chunk));
15454
- stream.on("end", () => resolve(hash.digest("hex")));
15193
+ stream.on("end", () => {
15194
+ resolve(hash.digest("hex"));
15195
+ });
15455
15196
  stream.on("error", (err) => {
15197
+ stream.destroy();
15456
15198
  reject(new Error(`Failed to calculate checksum for "${filePath}": ${err.message}`));
15457
15199
  });
15458
15200
  });
@@ -15696,9 +15438,9 @@ class CommandsPrefix {
15696
15438
  }
15697
15439
  await import_fs_extra3.copy(commandsDir, backupDir);
15698
15440
  logger.verbose("Created backup of commands directory");
15699
- await mkdir3(tempDir, { recursive: true });
15441
+ await mkdir2(tempDir, { recursive: true });
15700
15442
  const ckDir = join6(tempDir, "ck");
15701
- await mkdir3(ckDir, { recursive: true });
15443
+ await mkdir2(ckDir, { recursive: true });
15702
15444
  let processedCount = 0;
15703
15445
  for (const entry of entries) {
15704
15446
  const sourcePath = join6(commandsDir, entry);
@@ -15774,9 +15516,9 @@ class CommandsPrefix {
15774
15516
  }
15775
15517
  const metadata = await ManifestWriter.readManifest(claudeDir);
15776
15518
  if (!metadata || !metadata.files || metadata.files.length === 0) {
15777
- logger.warning("No ownership metadata found - aborting cleanup for safety");
15778
- logger.warning("Run 'ck init' to migrate legacy installation first");
15779
- throw new Error("Cannot cleanup without ownership metadata (legacy install detected)");
15519
+ logger.verbose("No ownership metadata found - skipping cleanup (legacy/fresh install)");
15520
+ logger.verbose("All existing files will be preserved as user-owned");
15521
+ return result;
15780
15522
  }
15781
15523
  const entries = await readdir2(commandsDir);
15782
15524
  if (entries.length === 0) {
@@ -15972,7 +15714,7 @@ var import_ignore = __toESM(require_ignore(), 1);
15972
15714
  import { Buffer as Buffer3 } from "node:buffer";
15973
15715
  import { execFile } from "node:child_process";
15974
15716
  import { createWriteStream as createWriteStream2 } from "node:fs";
15975
- import { mkdir as mkdir5 } from "node:fs/promises";
15717
+ import { mkdir as mkdir4 } from "node:fs/promises";
15976
15718
  import { tmpdir } from "node:os";
15977
15719
  import { join as join8, relative as relative2, resolve } from "node:path";
15978
15720
  import { TextDecoder } from "node:util";
@@ -19273,8 +19015,8 @@ class Minipass3 extends EventEmitter4 {
19273
19015
  }
19274
19016
 
19275
19017
  // node_modules/tar/dist/esm/normalize-windows-path.js
19276
- var platform3 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
19277
- var normalizeWindowsPath = platform3 !== "win32" ? (p) => p : (p) => p && p.replace(/\\/g, "/");
19018
+ var platform2 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
19019
+ var normalizeWindowsPath = platform2 !== "win32" ? (p) => p : (p) => p && p.replace(/\\/g, "/");
19278
19020
 
19279
19021
  // node_modules/tar/dist/esm/read-entry.js
19280
19022
  class ReadEntry extends Minipass3 {
@@ -21472,8 +21214,8 @@ import path6 from "node:path";
21472
21214
 
21473
21215
  // node_modules/tar/dist/esm/get-write-flag.js
21474
21216
  import fs6 from "fs";
21475
- var platform4 = process.env.__FAKE_PLATFORM__ || process.platform;
21476
- var isWindows2 = platform4 === "win32";
21217
+ var platform3 = process.env.__FAKE_PLATFORM__ || process.platform;
21218
+ var isWindows2 = platform3 === "win32";
21477
21219
  var { O_CREAT, O_TRUNC, O_WRONLY } = fs6.constants;
21478
21220
  var UV_FS_O_FILEMAP = Number(process.env.__FAKE_FS_O_FILENAME__) || fs6.constants.UV_FS_O_FILEMAP || 0;
21479
21221
  var fMapEnabled = isWindows2 && !!UV_FS_O_FILEMAP;
@@ -21604,7 +21346,7 @@ var checkCwd = (dir, cb) => {
21604
21346
  cb(er);
21605
21347
  });
21606
21348
  };
21607
- var mkdir4 = (dir, opt, cb) => {
21349
+ var mkdir3 = (dir, opt, cb) => {
21608
21350
  dir = normalizeWindowsPath(dir);
21609
21351
  const umask = opt.umask ?? 18;
21610
21352
  const mode = opt.mode | 448;
@@ -21764,8 +21506,8 @@ var normalizeUnicode = (s) => {
21764
21506
  };
21765
21507
 
21766
21508
  // node_modules/tar/dist/esm/path-reservations.js
21767
- var platform5 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
21768
- var isWindows3 = platform5 === "win32";
21509
+ var platform4 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
21510
+ var isWindows3 = platform4 === "win32";
21769
21511
  var getDirs = (path6) => {
21770
21512
  const dirs = path6.split("/").slice(0, -1).reduce((set, path7) => {
21771
21513
  const s = set[set.length - 1];
@@ -21911,8 +21653,8 @@ var DOCHOWN = Symbol("doChown");
21911
21653
  var UID = Symbol("uid");
21912
21654
  var GID = Symbol("gid");
21913
21655
  var CHECKED_CWD = Symbol("checkedCwd");
21914
- var platform6 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
21915
- var isWindows4 = platform6 === "win32";
21656
+ var platform5 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
21657
+ var isWindows4 = platform5 === "win32";
21916
21658
  var DEFAULT_MAX_DEPTH = 1024;
21917
21659
  var unlinkFile = (path7, cb) => {
21918
21660
  if (!isWindows4) {
@@ -22127,7 +21869,7 @@ class Unpack extends Parser {
22127
21869
  }
22128
21870
  }
22129
21871
  [MKDIR](dir, mode, cb) {
22130
- mkdir4(normalizeWindowsPath(dir), {
21872
+ mkdir3(normalizeWindowsPath(dir), {
22131
21873
  uid: this.uid,
22132
21874
  gid: this.gid,
22133
21875
  processUid: this.processUid,
@@ -25944,7 +25686,7 @@ class DownloadManager {
25944
25686
  async downloadAsset(asset, destDir) {
25945
25687
  try {
25946
25688
  const destPath = join8(destDir, asset.name);
25947
- await mkdir5(destDir, { recursive: true });
25689
+ await mkdir4(destDir, { recursive: true });
25948
25690
  logger.info(`Downloading ${asset.name} (${this.formatBytes(asset.size)})...`);
25949
25691
  const progressBar = new import_cli_progress.default.SingleBar({
25950
25692
  format: "Progress |{bar}| {percentage}% | {value}/{total} MB",
@@ -25994,7 +25736,7 @@ class DownloadManager {
25994
25736
  async downloadFile(params) {
25995
25737
  const { url, name: name2, size, destDir, token } = params;
25996
25738
  const destPath = join8(destDir, name2);
25997
- await mkdir5(destDir, { recursive: true });
25739
+ await mkdir4(destDir, { recursive: true });
25998
25740
  logger.info(`Downloading ${name2}${size ? ` (${this.formatBytes(size)})` : ""}...`);
25999
25741
  const headers = {};
26000
25742
  if (token && url.includes("api.github.com")) {
@@ -26058,7 +25800,7 @@ class DownloadManager {
26058
25800
  try {
26059
25801
  this.resetExtractionSize();
26060
25802
  const detectedType = archiveType || this.detectArchiveType(archivePath);
26061
- await mkdir5(destDir, { recursive: true });
25803
+ await mkdir4(destDir, { recursive: true });
26062
25804
  if (detectedType === "tar.gz") {
26063
25805
  await this.extractTarGz(archivePath, destDir);
26064
25806
  } else if (detectedType === "zip") {
@@ -26311,7 +26053,7 @@ class DownloadManager {
26311
26053
  const timestamp = Date.now();
26312
26054
  const primaryTempDir = join8(tmpdir(), `claudekit-${timestamp}`);
26313
26055
  try {
26314
- await mkdir5(primaryTempDir, { recursive: true });
26056
+ await mkdir4(primaryTempDir, { recursive: true });
26315
26057
  logger.debug(`Created temp directory: ${primaryTempDir}`);
26316
26058
  return primaryTempDir;
26317
26059
  } catch (primaryError) {
@@ -26327,7 +26069,7 @@ Solutions:
26327
26069
  }
26328
26070
  const fallbackTempDir = join8(homeDir, ".claudekit", "tmp", `claudekit-${timestamp}`);
26329
26071
  try {
26330
- await mkdir5(fallbackTempDir, { recursive: true });
26072
+ await mkdir4(fallbackTempDir, { recursive: true });
26331
26073
  logger.debug(`Created temp directory (fallback): ${fallbackTempDir}`);
26332
26074
  logger.warning(`Using fallback temp directory: ${fallbackTempDir}
26333
26075
  (OS temp directory was not accessible)`);
@@ -26396,10 +26138,10 @@ async function handleFreshInstallation(claudeDir, prompts) {
26396
26138
 
26397
26139
  // src/lib/global-path-transformer.ts
26398
26140
  init_logger();
26399
- import { readFile as readFile5, readdir as readdir3, writeFile as writeFile4 } from "node:fs/promises";
26400
- import { platform as platform7 } from "node:os";
26141
+ import { readFile as readFile4, readdir as readdir3, writeFile as writeFile3 } from "node:fs/promises";
26142
+ import { platform as platform6 } from "node:os";
26401
26143
  import { extname, join as join10 } from "node:path";
26402
- var IS_WINDOWS = platform7() === "win32";
26144
+ var IS_WINDOWS = platform6() === "win32";
26403
26145
  var HOME_PREFIX = IS_WINDOWS ? "%USERPROFILE%" : "$HOME";
26404
26146
  function getHomeDirPrefix() {
26405
26147
  return HOME_PREFIX;
@@ -26498,10 +26240,10 @@ async function transformPathsForGlobalInstall(directory, options = {}) {
26498
26240
  await processDirectory(fullPath);
26499
26241
  } else if (entry.isFile() && shouldTransformFile(entry.name)) {
26500
26242
  try {
26501
- const content = await readFile5(fullPath, "utf-8");
26243
+ const content = await readFile4(fullPath, "utf-8");
26502
26244
  const { transformed, changes } = transformContent(content);
26503
26245
  if (changes > 0) {
26504
- await writeFile4(fullPath, transformed, "utf-8");
26246
+ await writeFile3(fullPath, transformed, "utf-8");
26505
26247
  filesTransformed++;
26506
26248
  totalChanges += changes;
26507
26249
  if (options.verbose) {
@@ -29076,7 +28818,7 @@ init_types2();
29076
28818
  init_logger();
29077
28819
  var import_fs_extra10 = __toESM(require_lib(), 1);
29078
28820
  import { createHash as createHash2 } from "node:crypto";
29079
- import { readFile as readFile9, readdir as readdir6, writeFile as writeFile8 } from "node:fs/promises";
28821
+ import { readFile as readFile8, readdir as readdir6, writeFile as writeFile7 } from "node:fs/promises";
29080
28822
  import { join as join16, relative as relative5 } from "node:path";
29081
28823
 
29082
28824
  class SkillsManifestManager {
@@ -29100,7 +28842,7 @@ class SkillsManifestManager {
29100
28842
  }
29101
28843
  static async writeManifest(skillsDir, manifest) {
29102
28844
  const manifestPath = join16(skillsDir, SkillsManifestManager.MANIFEST_FILENAME);
29103
- await writeFile8(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
28845
+ await writeFile7(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
29104
28846
  logger.debug(`Wrote manifest to: ${manifestPath}`);
29105
28847
  }
29106
28848
  static async readManifest(skillsDir) {
@@ -29110,7 +28852,7 @@ class SkillsManifestManager {
29110
28852
  return null;
29111
28853
  }
29112
28854
  try {
29113
- const content = await readFile9(manifestPath, "utf-8");
28855
+ const content = await readFile8(manifestPath, "utf-8");
29114
28856
  const data = JSON.parse(content);
29115
28857
  const manifest = SkillsManifestSchema.parse(data);
29116
28858
  logger.debug(`Read manifest from: ${manifestPath}`);
@@ -29182,7 +28924,7 @@ class SkillsManifestManager {
29182
28924
  files.sort();
29183
28925
  for (const file of files) {
29184
28926
  const relativePath = relative5(dirPath, file);
29185
- const content = await readFile9(file);
28927
+ const content = await readFile8(file);
29186
28928
  hash.update(relativePath);
29187
28929
  hash.update(content);
29188
28930
  }
@@ -29478,14 +29220,14 @@ class SkillsMigrationDetector {
29478
29220
  init_types2();
29479
29221
  init_logger();
29480
29222
  var import_fs_extra14 = __toESM(require_lib(), 1);
29481
- import { copyFile as copyFile2, mkdir as mkdir7, readdir as readdir10, rm as rm2 } from "node:fs/promises";
29223
+ import { copyFile as copyFile2, mkdir as mkdir6, readdir as readdir10, rm as rm2 } from "node:fs/promises";
29482
29224
  import { join as join20 } from "node:path";
29483
29225
 
29484
29226
  // src/lib/skills-backup-manager.ts
29485
29227
  init_types2();
29486
29228
  init_logger();
29487
29229
  var import_fs_extra12 = __toESM(require_lib(), 1);
29488
- import { copyFile, mkdir as mkdir6, readdir as readdir8, rm, stat as stat4 } from "node:fs/promises";
29230
+ import { copyFile, mkdir as mkdir5, readdir as readdir8, rm, stat as stat4 } from "node:fs/promises";
29489
29231
  import { basename as basename2, join as join18, normalize as normalize2 } from "node:path";
29490
29232
  function validatePath2(path9, paramName) {
29491
29233
  if (!path9 || typeof path9 !== "string") {
@@ -29515,7 +29257,7 @@ class SkillsBackupManager {
29515
29257
  const backupDir = parentDir ? join18(parentDir, backupDirName) : join18(skillsDir, "..", backupDirName);
29516
29258
  logger.info(`Creating backup at: ${backupDir}`);
29517
29259
  try {
29518
- await mkdir6(backupDir, { recursive: true });
29260
+ await mkdir5(backupDir, { recursive: true });
29519
29261
  await SkillsBackupManager.copyDirectory(skillsDir, backupDir);
29520
29262
  logger.success("Backup created successfully");
29521
29263
  return backupDir;
@@ -29537,7 +29279,7 @@ class SkillsBackupManager {
29537
29279
  if (await import_fs_extra12.pathExists(targetDir)) {
29538
29280
  await rm(targetDir, { recursive: true, force: true });
29539
29281
  }
29540
- await mkdir6(targetDir, { recursive: true });
29282
+ await mkdir5(targetDir, { recursive: true });
29541
29283
  await SkillsBackupManager.copyDirectory(backupDir, targetDir);
29542
29284
  logger.success("Backup restored successfully");
29543
29285
  } catch (error2) {
@@ -29597,7 +29339,7 @@ class SkillsBackupManager {
29597
29339
  continue;
29598
29340
  }
29599
29341
  if (entry.isDirectory()) {
29600
- await mkdir6(destPath, { recursive: true });
29342
+ await mkdir5(destPath, { recursive: true });
29601
29343
  await SkillsBackupManager.copyDirectory(sourcePath, destPath);
29602
29344
  } else if (entry.isFile()) {
29603
29345
  await copyFile(sourcePath, destPath);
@@ -29638,7 +29380,7 @@ init_logger();
29638
29380
  var import_fs_extra13 = __toESM(require_lib(), 1);
29639
29381
  import { createHash as createHash3 } from "node:crypto";
29640
29382
  import { createReadStream as createReadStream2 } from "node:fs";
29641
- import { readFile as readFile10, readdir as readdir9 } from "node:fs/promises";
29383
+ import { readFile as readFile9, readdir as readdir9 } from "node:fs/promises";
29642
29384
  import { join as join19, normalize as normalize3, relative as relative6 } from "node:path";
29643
29385
  function validatePath3(path9, paramName) {
29644
29386
  if (!path9 || typeof path9 !== "string") {
@@ -29851,8 +29593,13 @@ class SkillsCustomizationScanner {
29851
29593
  const hash = createHash3("sha256");
29852
29594
  const stream = createReadStream2(filePath);
29853
29595
  stream.on("data", (chunk) => hash.update(chunk));
29854
- stream.on("end", () => resolve2(hash.digest("hex")));
29855
- stream.on("error", (error2) => reject(error2));
29596
+ stream.on("end", () => {
29597
+ resolve2(hash.digest("hex"));
29598
+ });
29599
+ stream.on("error", (error2) => {
29600
+ stream.destroy();
29601
+ reject(error2);
29602
+ });
29856
29603
  });
29857
29604
  }
29858
29605
  static async hashDirectory(dirPath) {
@@ -29861,7 +29608,7 @@ class SkillsCustomizationScanner {
29861
29608
  files.sort();
29862
29609
  for (const file of files) {
29863
29610
  const relativePath = relative6(dirPath, file);
29864
- const content = await readFile10(file);
29611
+ const content = await readFile9(file);
29865
29612
  hash.update(relativePath);
29866
29613
  hash.update(content);
29867
29614
  }
@@ -30124,7 +29871,7 @@ class SkillsMigrator {
30124
29871
  const preserved = [];
30125
29872
  const errors2 = [];
30126
29873
  const tempDir = join20(currentSkillsDir, "..", ".skills-migration-temp");
30127
- await mkdir7(tempDir, { recursive: true });
29874
+ await mkdir6(tempDir, { recursive: true });
30128
29875
  try {
30129
29876
  for (const mapping of mappings) {
30130
29877
  try {
@@ -30146,7 +29893,7 @@ class SkillsMigrator {
30146
29893
  const category = mapping.category;
30147
29894
  const targetPath = category ? join20(tempDir, category, skillName) : join20(tempDir, skillName);
30148
29895
  if (category) {
30149
- await mkdir7(join20(tempDir, category), { recursive: true });
29896
+ await mkdir6(join20(tempDir, category), { recursive: true });
30150
29897
  }
30151
29898
  await SkillsMigrator.copySkillDirectory(currentSkillPath, targetPath);
30152
29899
  migrated.push(skillName);
@@ -30165,7 +29912,7 @@ class SkillsMigrator {
30165
29912
  }
30166
29913
  }
30167
29914
  await rm2(currentSkillsDir, { recursive: true, force: true });
30168
- await mkdir7(currentSkillsDir, { recursive: true });
29915
+ await mkdir6(currentSkillsDir, { recursive: true });
30169
29916
  await SkillsMigrator.copySkillDirectory(tempDir, currentSkillsDir);
30170
29917
  await rm2(tempDir, { recursive: true, force: true });
30171
29918
  return { migrated, preserved, errors: errors2 };
@@ -30177,7 +29924,7 @@ class SkillsMigrator {
30177
29924
  }
30178
29925
  }
30179
29926
  static async copySkillDirectory(sourceDir, destDir) {
30180
- await mkdir7(destDir, { recursive: true });
29927
+ await mkdir6(destDir, { recursive: true });
30181
29928
  const entries = await readdir10(sourceDir, { withFileTypes: true });
30182
29929
  for (const entry of entries) {
30183
29930
  const sourcePath = join20(sourceDir, entry.name);
@@ -30196,6 +29943,83 @@ class SkillsMigrator {
30196
29943
 
30197
29944
  // src/commands/init.ts
30198
29945
  init_types2();
29946
+
29947
+ // src/utils/config.ts
29948
+ init_types2();
29949
+ init_logger();
29950
+ import { existsSync as existsSync4 } from "node:fs";
29951
+ import { mkdir as mkdir7, readFile as readFile10, writeFile as writeFile8 } from "node:fs/promises";
29952
+ import { chmod } from "node:fs/promises";
29953
+ import { platform as platform7 } from "node:os";
29954
+ class ConfigManager {
29955
+ static config = null;
29956
+ static globalFlag = false;
29957
+ static setGlobalFlag(global3) {
29958
+ ConfigManager.globalFlag = global3;
29959
+ ConfigManager.config = null;
29960
+ }
29961
+ static getGlobalFlag() {
29962
+ return ConfigManager.globalFlag;
29963
+ }
29964
+ static async load() {
29965
+ if (ConfigManager.config) {
29966
+ return ConfigManager.config;
29967
+ }
29968
+ const configFile = PathResolver.getConfigFile(ConfigManager.globalFlag);
29969
+ try {
29970
+ if (existsSync4(configFile)) {
29971
+ const content = await readFile10(configFile, "utf-8");
29972
+ const data = JSON.parse(content);
29973
+ ConfigManager.config = ConfigSchema.parse(data);
29974
+ logger.debug(`Config loaded from ${configFile}`);
29975
+ return ConfigManager.config;
29976
+ }
29977
+ } catch (error2) {
29978
+ logger.warning(`Failed to load config: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
29979
+ }
29980
+ ConfigManager.config = { defaults: {} };
29981
+ return ConfigManager.config;
29982
+ }
29983
+ static async save(config) {
29984
+ try {
29985
+ const validConfig = ConfigSchema.parse(config);
29986
+ const configDir = PathResolver.getConfigDir(ConfigManager.globalFlag);
29987
+ const configFile = PathResolver.getConfigFile(ConfigManager.globalFlag);
29988
+ if (!existsSync4(configDir)) {
29989
+ await mkdir7(configDir, { recursive: true });
29990
+ if (platform7() !== "win32") {
29991
+ await chmod(configDir, 448);
29992
+ }
29993
+ }
29994
+ await writeFile8(configFile, JSON.stringify(validConfig, null, 2), "utf-8");
29995
+ if (platform7() !== "win32") {
29996
+ await chmod(configFile, 384);
29997
+ }
29998
+ ConfigManager.config = validConfig;
29999
+ logger.debug(`Config saved to ${configFile}`);
30000
+ } catch (error2) {
30001
+ throw new Error(`Failed to save config: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
30002
+ }
30003
+ }
30004
+ static async get() {
30005
+ return ConfigManager.load();
30006
+ }
30007
+ static async set(key, value) {
30008
+ const config = await ConfigManager.load();
30009
+ const keys = key.split(".");
30010
+ let current = config;
30011
+ for (let i = 0;i < keys.length - 1; i++) {
30012
+ if (!(keys[i] in current)) {
30013
+ current[keys[i]] = {};
30014
+ }
30015
+ current = current[keys[i]];
30016
+ }
30017
+ current[keys[keys.length - 1]] = value;
30018
+ await ConfigManager.save(config);
30019
+ }
30020
+ }
30021
+
30022
+ // src/commands/init.ts
30199
30023
  init_environment();
30200
30024
 
30201
30025
  // src/utils/file-scanner.ts
@@ -31076,7 +30900,7 @@ import { promisify as promisify5 } from "node:util";
31076
30900
  // package.json
31077
30901
  var package_default2 = {
31078
30902
  name: "claudekit-cli",
31079
- version: "2.6.0",
30903
+ version: "3.0.1",
31080
30904
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
31081
30905
  type: "module",
31082
30906
  repository: {
@@ -32094,9 +31918,6 @@ var MetadataSchema2 = exports_external.object({
32094
31918
  files: exports_external.array(TrackedFileSchema2).optional()
32095
31919
  });
32096
31920
  var ConfigSchema2 = exports_external.object({
32097
- github: exports_external.object({
32098
- token: exports_external.string().optional()
32099
- }).optional(),
32100
31921
  defaults: exports_external.object({
32101
31922
  kit: KitType2.optional(),
32102
31923
  dir: exports_external.string().optional()