claudekit-cli 3.36.0-dev.18 → 3.36.0-dev.19

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 +544 -496
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -55904,108 +55904,6 @@ var init_metadata_migration = __esm(() => {
55904
55904
  import_fs_extra3 = __toESM(require_lib3(), 1);
55905
55905
  });
55906
55906
 
55907
- // src/services/file-operations/claudekit-scanner.ts
55908
- import { join as join40 } from "node:path";
55909
- async function scanClaudeKitDirectory(directoryPath) {
55910
- const counts = {
55911
- agents: 0,
55912
- commands: 0,
55913
- rules: 0,
55914
- skills: 0
55915
- };
55916
- try {
55917
- if (!await import_fs_extra4.pathExists(directoryPath)) {
55918
- return counts;
55919
- }
55920
- const items = await import_fs_extra4.readdir(directoryPath);
55921
- if (items.includes("agents")) {
55922
- const agentsPath = join40(directoryPath, "agents");
55923
- const agentFiles = await import_fs_extra4.readdir(agentsPath);
55924
- counts.agents = agentFiles.filter((file) => file.endsWith(".md")).length;
55925
- }
55926
- if (items.includes("commands")) {
55927
- const commandsPath = join40(directoryPath, "commands");
55928
- const commandFiles = await import_fs_extra4.readdir(commandsPath);
55929
- counts.commands = commandFiles.filter((file) => file.endsWith(".md")).length;
55930
- }
55931
- if (items.includes("rules")) {
55932
- const rulesPath = join40(directoryPath, "rules");
55933
- const ruleFiles = await import_fs_extra4.readdir(rulesPath);
55934
- counts.rules = ruleFiles.filter((file) => file.endsWith(".md")).length;
55935
- } else if (items.includes("workflows")) {
55936
- const workflowsPath = join40(directoryPath, "workflows");
55937
- const workflowFiles = await import_fs_extra4.readdir(workflowsPath);
55938
- counts.rules = workflowFiles.filter((file) => file.endsWith(".md")).length;
55939
- }
55940
- if (items.includes("skills")) {
55941
- const skillsPath = join40(directoryPath, "skills");
55942
- const skillItems = await import_fs_extra4.readdir(skillsPath);
55943
- let skillCount = 0;
55944
- for (const item of skillItems) {
55945
- if (SKIP_DIRS_CLAUDE_INTERNAL.includes(item)) {
55946
- continue;
55947
- }
55948
- const itemPath = join40(skillsPath, item);
55949
- const stat8 = await import_fs_extra4.readdir(itemPath).catch(() => null);
55950
- if (stat8?.includes("SKILL.md")) {
55951
- skillCount++;
55952
- }
55953
- }
55954
- counts.skills = skillCount;
55955
- }
55956
- } catch (error) {}
55957
- return counts;
55958
- }
55959
- async function readClaudeKitMetadata(metadataPath) {
55960
- try {
55961
- if (!await import_fs_extra4.pathExists(metadataPath)) {
55962
- return null;
55963
- }
55964
- const content = await import_fs_extra4.readFile(metadataPath, "utf8");
55965
- const metadata = JSON.parse(content);
55966
- return metadata;
55967
- } catch {
55968
- return null;
55969
- }
55970
- }
55971
- function getGlobalInstallDir() {
55972
- return PathResolver.getGlobalKitDir();
55973
- }
55974
- async function getClaudeKitSetup(projectDir = process.cwd()) {
55975
- const setup = {
55976
- global: {
55977
- path: "",
55978
- metadata: null,
55979
- components: { agents: 0, commands: 0, rules: 0, skills: 0 }
55980
- },
55981
- project: {
55982
- path: "",
55983
- metadata: null,
55984
- components: { agents: 0, commands: 0, rules: 0, skills: 0 }
55985
- }
55986
- };
55987
- const globalDir = getGlobalInstallDir();
55988
- if (await import_fs_extra4.pathExists(globalDir)) {
55989
- setup.global.path = globalDir;
55990
- setup.global.metadata = await readClaudeKitMetadata(join40(globalDir, "metadata.json"));
55991
- setup.global.components = await scanClaudeKitDirectory(globalDir);
55992
- }
55993
- const projectClaudeDir = join40(projectDir, ".claude");
55994
- const isLocalSameAsGlobal = projectClaudeDir === globalDir;
55995
- if (!isLocalSameAsGlobal && await import_fs_extra4.pathExists(projectClaudeDir)) {
55996
- setup.project.path = projectClaudeDir;
55997
- setup.project.metadata = await readClaudeKitMetadata(join40(projectClaudeDir, "metadata.json"));
55998
- setup.project.components = await scanClaudeKitDirectory(projectClaudeDir);
55999
- }
56000
- return setup;
56001
- }
56002
- var import_fs_extra4;
56003
- var init_claudekit_scanner = __esm(() => {
56004
- init_path_resolver();
56005
- init_skip_directories();
56006
- import_fs_extra4 = __toESM(require_lib3(), 1);
56007
- });
56008
-
56009
55907
  // node_modules/compare-versions/lib/umd/index.js
56010
55908
  var require_umd = __commonJS((exports, module) => {
56011
55909
  (function(global3, factory) {
@@ -56128,12 +56026,158 @@ var require_umd = __commonJS((exports, module) => {
56128
56026
  });
56129
56027
  });
56130
56028
 
56029
+ // src/domains/versioning/checking/version-utils.ts
56030
+ function isUpdateCheckDisabled() {
56031
+ return process.env.NO_UPDATE_NOTIFIER === "1" || process.env.NO_UPDATE_NOTIFIER === "true" || !process.stdout.isTTY;
56032
+ }
56033
+ function normalizeVersion(version) {
56034
+ return version.replace(/^v/i, "");
56035
+ }
56036
+ function parseVersionParts(version) {
56037
+ const normalized = normalizeVersion(version);
56038
+ const [base, ...prereleaseParts] = normalized.split("-");
56039
+ return {
56040
+ base,
56041
+ prerelease: prereleaseParts.length > 0 ? prereleaseParts.join("-") : null
56042
+ };
56043
+ }
56044
+ function isDevPrereleaseOfSameBase(currentVersion, latestVersion) {
56045
+ const current = parseVersionParts(currentVersion);
56046
+ const latest = parseVersionParts(latestVersion);
56047
+ if (!current.prerelease?.startsWith("dev"))
56048
+ return false;
56049
+ if (latest.prerelease !== null)
56050
+ return false;
56051
+ return current.base === latest.base;
56052
+ }
56053
+ function versionsMatch(installed, latest) {
56054
+ return normalizeVersion(installed) === normalizeVersion(latest);
56055
+ }
56056
+ function isNewerVersion(currentVersion, latestVersion) {
56057
+ try {
56058
+ const current = normalizeVersion(currentVersion);
56059
+ const latest = normalizeVersion(latestVersion);
56060
+ if (isDevPrereleaseOfSameBase(current, latest)) {
56061
+ return false;
56062
+ }
56063
+ return import_compare_versions.compareVersions(latest, current) > 0;
56064
+ } catch {
56065
+ return false;
56066
+ }
56067
+ }
56068
+ var import_compare_versions;
56069
+ var init_version_utils = __esm(() => {
56070
+ import_compare_versions = __toESM(require_umd(), 1);
56071
+ });
56072
+
56073
+ // src/services/file-operations/claudekit-scanner.ts
56074
+ import { join as join40 } from "node:path";
56075
+ async function scanClaudeKitDirectory(directoryPath) {
56076
+ const counts = {
56077
+ agents: 0,
56078
+ commands: 0,
56079
+ rules: 0,
56080
+ skills: 0
56081
+ };
56082
+ try {
56083
+ if (!await import_fs_extra4.pathExists(directoryPath)) {
56084
+ return counts;
56085
+ }
56086
+ const items = await import_fs_extra4.readdir(directoryPath);
56087
+ if (items.includes("agents")) {
56088
+ const agentsPath = join40(directoryPath, "agents");
56089
+ const agentFiles = await import_fs_extra4.readdir(agentsPath);
56090
+ counts.agents = agentFiles.filter((file) => file.endsWith(".md")).length;
56091
+ }
56092
+ if (items.includes("commands")) {
56093
+ const commandsPath = join40(directoryPath, "commands");
56094
+ const commandFiles = await import_fs_extra4.readdir(commandsPath);
56095
+ counts.commands = commandFiles.filter((file) => file.endsWith(".md")).length;
56096
+ }
56097
+ if (items.includes("rules")) {
56098
+ const rulesPath = join40(directoryPath, "rules");
56099
+ const ruleFiles = await import_fs_extra4.readdir(rulesPath);
56100
+ counts.rules = ruleFiles.filter((file) => file.endsWith(".md")).length;
56101
+ } else if (items.includes("workflows")) {
56102
+ const workflowsPath = join40(directoryPath, "workflows");
56103
+ const workflowFiles = await import_fs_extra4.readdir(workflowsPath);
56104
+ counts.rules = workflowFiles.filter((file) => file.endsWith(".md")).length;
56105
+ }
56106
+ if (items.includes("skills")) {
56107
+ const skillsPath = join40(directoryPath, "skills");
56108
+ const skillItems = await import_fs_extra4.readdir(skillsPath);
56109
+ let skillCount = 0;
56110
+ for (const item of skillItems) {
56111
+ if (SKIP_DIRS_CLAUDE_INTERNAL.includes(item)) {
56112
+ continue;
56113
+ }
56114
+ const itemPath = join40(skillsPath, item);
56115
+ const stat8 = await import_fs_extra4.readdir(itemPath).catch(() => null);
56116
+ if (stat8?.includes("SKILL.md")) {
56117
+ skillCount++;
56118
+ }
56119
+ }
56120
+ counts.skills = skillCount;
56121
+ }
56122
+ } catch (error) {}
56123
+ return counts;
56124
+ }
56125
+ async function readClaudeKitMetadata(metadataPath) {
56126
+ try {
56127
+ if (!await import_fs_extra4.pathExists(metadataPath)) {
56128
+ return null;
56129
+ }
56130
+ const content = await import_fs_extra4.readFile(metadataPath, "utf8");
56131
+ const metadata = JSON.parse(content);
56132
+ return metadata;
56133
+ } catch {
56134
+ return null;
56135
+ }
56136
+ }
56137
+ function getGlobalInstallDir() {
56138
+ return PathResolver.getGlobalKitDir();
56139
+ }
56140
+ async function getClaudeKitSetup(projectDir = process.cwd()) {
56141
+ const setup = {
56142
+ global: {
56143
+ path: "",
56144
+ metadata: null,
56145
+ components: { agents: 0, commands: 0, rules: 0, skills: 0 }
56146
+ },
56147
+ project: {
56148
+ path: "",
56149
+ metadata: null,
56150
+ components: { agents: 0, commands: 0, rules: 0, skills: 0 }
56151
+ }
56152
+ };
56153
+ const globalDir = getGlobalInstallDir();
56154
+ if (await import_fs_extra4.pathExists(globalDir)) {
56155
+ setup.global.path = globalDir;
56156
+ setup.global.metadata = await readClaudeKitMetadata(join40(globalDir, "metadata.json"));
56157
+ setup.global.components = await scanClaudeKitDirectory(globalDir);
56158
+ }
56159
+ const projectClaudeDir = join40(projectDir, ".claude");
56160
+ const isLocalSameAsGlobal = projectClaudeDir === globalDir;
56161
+ if (!isLocalSameAsGlobal && await import_fs_extra4.pathExists(projectClaudeDir)) {
56162
+ setup.project.path = projectClaudeDir;
56163
+ setup.project.metadata = await readClaudeKitMetadata(join40(projectClaudeDir, "metadata.json"));
56164
+ setup.project.components = await scanClaudeKitDirectory(projectClaudeDir);
56165
+ }
56166
+ return setup;
56167
+ }
56168
+ var import_fs_extra4;
56169
+ var init_claudekit_scanner = __esm(() => {
56170
+ init_path_resolver();
56171
+ init_skip_directories();
56172
+ import_fs_extra4 = __toESM(require_lib3(), 1);
56173
+ });
56174
+
56131
56175
  // package.json
56132
56176
  var package_default;
56133
56177
  var init_package = __esm(() => {
56134
56178
  package_default = {
56135
56179
  name: "claudekit-cli",
56136
- version: "3.36.0-dev.18",
56180
+ version: "3.36.0-dev.19",
56137
56181
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
56138
56182
  type: "module",
56139
56183
  repository: {
@@ -56240,365 +56284,6 @@ var init_package = __esm(() => {
56240
56284
  };
56241
56285
  });
56242
56286
 
56243
- // src/commands/update-cli.ts
56244
- import { exec as exec2 } from "node:child_process";
56245
- import { join as join41 } from "node:path";
56246
- import { promisify as promisify8 } from "node:util";
56247
- function getDefaultUpdateCliCommandDeps() {
56248
- return {
56249
- currentVersion: package_default.version,
56250
- execAsyncFn: execAsync2,
56251
- packageManagerDetector: PackageManagerDetector,
56252
- npmRegistryClient: NpmRegistryClient,
56253
- promptKitUpdateFn: promptKitUpdate
56254
- };
56255
- }
56256
- function extractCommandStdout(result) {
56257
- if (typeof result === "string") {
56258
- return result;
56259
- }
56260
- if (result && typeof result.stdout === "string") {
56261
- return result.stdout;
56262
- }
56263
- return "";
56264
- }
56265
- function redactCommandForLog(command) {
56266
- if (!command)
56267
- return command;
56268
- const redactedRegistryFlags = command.replace(/(--registry(?:=|\s+))(['"]?)(\S+?)(\2)(?=\s|$)/g, (_match, prefix, quote, url) => `${prefix}${quote}${redactRegistryUrlForLog(url)}${quote}`);
56269
- return redactedRegistryFlags.replace(/https?:\/\/[^\s"']+/g, (url) => redactRegistryUrlForLog(url));
56270
- }
56271
- function buildInitCommand(isGlobal, kit, beta) {
56272
- const parts = ["ck init"];
56273
- if (isGlobal)
56274
- parts.push("-g");
56275
- if (kit)
56276
- parts.push(`--kit ${kit}`);
56277
- parts.push("--yes --install-skills");
56278
- if (beta)
56279
- parts.push("--beta");
56280
- return parts.join(" ");
56281
- }
56282
- function isBetaVersion(version) {
56283
- if (!version)
56284
- return false;
56285
- return /-(beta|alpha|rc|dev)[.\d]/i.test(version);
56286
- }
56287
- function parseCliVersionFromOutput(output2) {
56288
- if (!output2)
56289
- return null;
56290
- const match = output2.match(/CLI Version:\s*(\S+)/);
56291
- return match ? match[1] : null;
56292
- }
56293
- function selectKitForUpdate(params) {
56294
- const { hasLocal, hasGlobal, localKits, globalKits } = params;
56295
- const hasLocalKit = localKits.length > 0 || hasLocal;
56296
- const hasGlobalKit = globalKits.length > 0 || hasGlobal;
56297
- if (!hasLocalKit && !hasGlobalKit) {
56298
- return null;
56299
- }
56300
- if (hasGlobalKit && !hasLocalKit) {
56301
- const kit2 = globalKits[0] || localKits[0];
56302
- return {
56303
- isGlobal: true,
56304
- kit: kit2,
56305
- promptMessage: `Update global ClaudeKit content${kit2 ? ` (${kit2})` : ""}?`
56306
- };
56307
- }
56308
- if (hasLocalKit && !hasGlobalKit) {
56309
- const kit2 = localKits[0] || globalKits[0];
56310
- return {
56311
- isGlobal: false,
56312
- kit: kit2,
56313
- promptMessage: `Update local project ClaudeKit content${kit2 ? ` (${kit2})` : ""}?`
56314
- };
56315
- }
56316
- const kit = globalKits[0] || localKits[0];
56317
- return {
56318
- isGlobal: true,
56319
- kit,
56320
- promptMessage: `Update global ClaudeKit content${kit ? ` (${kit})` : ""}?`
56321
- };
56322
- }
56323
- async function readMetadataFile(claudeDir2) {
56324
- const metadataPath = join41(claudeDir2, "metadata.json");
56325
- try {
56326
- if (!await import_fs_extra5.pathExists(metadataPath)) {
56327
- return null;
56328
- }
56329
- const content = await import_fs_extra5.readFile(metadataPath, "utf-8");
56330
- const parsed = JSON.parse(content);
56331
- const validated = MetadataSchema.safeParse(parsed);
56332
- if (!validated.success) {
56333
- logger.verbose(`Invalid metadata format: ${validated.error.message}`);
56334
- return null;
56335
- }
56336
- return validated.data;
56337
- } catch (error) {
56338
- logger.verbose(`Failed to read metadata: ${error instanceof Error ? error.message : "unknown"}`);
56339
- return null;
56340
- }
56341
- }
56342
- async function promptKitUpdate(beta, yes, deps) {
56343
- try {
56344
- const execFn = deps?.execAsyncFn ?? execAsync2;
56345
- const setup = await (deps?.getSetupFn ?? getClaudeKitSetup)();
56346
- const hasLocal = !!setup.project.metadata;
56347
- const hasGlobal = !!setup.global.metadata;
56348
- const localMetadata = hasLocal ? await readMetadataFile(setup.project.path) : null;
56349
- const globalMetadata = hasGlobal ? await readMetadataFile(setup.global.path) : null;
56350
- const localKits = localMetadata ? getInstalledKits(localMetadata) : [];
56351
- const globalKits = globalMetadata ? getInstalledKits(globalMetadata) : [];
56352
- const selection = selectKitForUpdate({ hasLocal, hasGlobal, localKits, globalKits });
56353
- if (!selection) {
56354
- logger.verbose("No ClaudeKit installations detected, skipping kit update prompt");
56355
- return;
56356
- }
56357
- const kitVersion = selection.kit ? selection.isGlobal ? globalMetadata?.kits?.[selection.kit]?.version : localMetadata?.kits?.[selection.kit]?.version : undefined;
56358
- const isBetaInstalled = isBetaVersion(kitVersion);
56359
- const initCmd = buildInitCommand(selection.isGlobal, selection.kit, beta || isBetaInstalled);
56360
- const promptMessage = selection.promptMessage;
56361
- if (selection.kit && kitVersion) {
56362
- logger.info(`Current kit version: ${selection.kit}@${kitVersion}`);
56363
- }
56364
- if (!yes) {
56365
- logger.info("");
56366
- const shouldUpdate = await se({
56367
- message: promptMessage
56368
- });
56369
- if (lD(shouldUpdate) || !shouldUpdate) {
56370
- log.info("Skipped kit content update");
56371
- return;
56372
- }
56373
- } else {
56374
- logger.verbose("Auto-proceeding with kit update (--yes flag)");
56375
- }
56376
- logger.info(`Running: ${initCmd}`);
56377
- const s = (deps?.spinnerFn ?? de)();
56378
- s.start("Updating ClaudeKit content...");
56379
- try {
56380
- await execFn(initCmd, {
56381
- timeout: 300000
56382
- });
56383
- let newKitVersion;
56384
- try {
56385
- const claudeDir2 = selection.isGlobal ? setup.global.path : setup.project.path;
56386
- const updatedMetadata = await readMetadataFile(claudeDir2);
56387
- newKitVersion = selection.kit ? updatedMetadata?.kits?.[selection.kit]?.version : undefined;
56388
- } catch {}
56389
- if (selection.kit && kitVersion && newKitVersion && kitVersion !== newKitVersion) {
56390
- s.stop(`Kit updated: ${selection.kit}@${kitVersion} -> ${newKitVersion}`);
56391
- } else if (selection.kit && newKitVersion) {
56392
- s.stop(`Kit content updated (${selection.kit}@${newKitVersion})`);
56393
- } else {
56394
- s.stop("Kit content updated");
56395
- }
56396
- } catch (error) {
56397
- s.stop("Kit update finished");
56398
- const errorMsg = error instanceof Error ? error.message : "unknown";
56399
- if (errorMsg.includes("exit code") && !errorMsg.includes("exit code 0")) {
56400
- logger.warning("Kit content update may have encountered issues");
56401
- logger.verbose(`Error: ${errorMsg}`);
56402
- } else {
56403
- logger.verbose(`Init command completed: ${errorMsg}`);
56404
- }
56405
- }
56406
- } catch (error) {
56407
- logger.verbose(`Failed to prompt for kit update: ${error instanceof Error ? error.message : "unknown error"}`);
56408
- }
56409
- }
56410
- async function updateCliCommand(options2, deps = getDefaultUpdateCliCommandDeps()) {
56411
- const s = de();
56412
- intro("[>] ClaudeKit CLI - Update");
56413
- try {
56414
- const {
56415
- currentVersion,
56416
- execAsyncFn,
56417
- packageManagerDetector,
56418
- npmRegistryClient,
56419
- promptKitUpdateFn
56420
- } = deps;
56421
- const opts = UpdateCliOptionsSchema.parse(options2);
56422
- logger.info(`Current CLI version: ${currentVersion}`);
56423
- s.start("Detecting package manager...");
56424
- const pm = await packageManagerDetector.detect();
56425
- const pmVersion = await packageManagerDetector.getVersion(pm);
56426
- s.stop(`Using ${packageManagerDetector.getDisplayName(pm)}${pmVersion ? ` v${pmVersion}` : ""}`);
56427
- logger.verbose(`Detected package manager: ${pm}`);
56428
- let registryUrl = opts.registry;
56429
- if (!registryUrl && pm === "npm") {
56430
- const userRegistry = await packageManagerDetector.getNpmRegistryUrl();
56431
- if (userRegistry) {
56432
- registryUrl = userRegistry;
56433
- logger.verbose(`Using npm configured registry: ${redactRegistryUrlForLog(registryUrl)}`);
56434
- }
56435
- }
56436
- s.start("Checking for updates...");
56437
- let targetVersion = null;
56438
- if (opts.release && opts.release !== "latest") {
56439
- try {
56440
- const exists = await npmRegistryClient.versionExists(CLAUDEKIT_CLI_NPM_PACKAGE_NAME, opts.release, registryUrl);
56441
- if (!exists) {
56442
- s.stop("Version not found");
56443
- throw new CliUpdateError(`Version ${opts.release} does not exist on npm registry. Run 'ck versions' to see available versions.`);
56444
- }
56445
- } catch (error) {
56446
- if (error instanceof CliUpdateError) {
56447
- throw error;
56448
- }
56449
- s.stop("Version check failed");
56450
- const message = error instanceof Error ? error.message : "Unknown error";
56451
- logger.verbose(`Release check failed for ${opts.release}: ${message}`);
56452
- const registryHint = registryUrl ? ` (${redactRegistryUrlForLog(registryUrl)})` : " (default registry)";
56453
- throw new CliUpdateError(`Failed to verify version ${opts.release} on npm registry${registryHint}. Check registry settings/network connectivity and try again.`);
56454
- }
56455
- targetVersion = opts.release;
56456
- s.stop(`Target version: ${targetVersion}`);
56457
- } else if (opts.dev || opts.beta) {
56458
- targetVersion = await npmRegistryClient.getDevVersion(CLAUDEKIT_CLI_NPM_PACKAGE_NAME, registryUrl);
56459
- if (!targetVersion) {
56460
- s.stop("No dev version available");
56461
- logger.warning("No dev version found. Using latest stable version instead.");
56462
- targetVersion = await npmRegistryClient.getLatestVersion(CLAUDEKIT_CLI_NPM_PACKAGE_NAME, registryUrl);
56463
- } else {
56464
- s.stop(`Latest dev version: ${targetVersion}`);
56465
- }
56466
- } else {
56467
- targetVersion = await npmRegistryClient.getLatestVersion(CLAUDEKIT_CLI_NPM_PACKAGE_NAME, registryUrl);
56468
- s.stop(`Latest version: ${targetVersion || "unknown"}`);
56469
- }
56470
- if (!targetVersion) {
56471
- throw new CliUpdateError(`Failed to fetch version information from npm registry. Check your internet connection and try again. Manual update: ${packageManagerDetector.getUpdateCommand(pm, CLAUDEKIT_CLI_NPM_PACKAGE_NAME, undefined, registryUrl)}`);
56472
- }
56473
- const comparison = import_compare_versions.compareVersions(currentVersion, targetVersion);
56474
- if (comparison === 0) {
56475
- outro(`[+] Already on the latest CLI version (${currentVersion})`);
56476
- await promptKitUpdateFn(opts.dev || opts.beta, opts.yes);
56477
- return;
56478
- }
56479
- const isDevChannelSwitch = (opts.dev || opts.beta) && isBetaVersion(targetVersion) && !isBetaVersion(currentVersion);
56480
- if (comparison > 0 && !opts.release && !isDevChannelSwitch) {
56481
- outro(`[+] Current version (${currentVersion}) is newer than latest (${targetVersion})`);
56482
- return;
56483
- }
56484
- const isUpgrade = comparison < 0 || isDevChannelSwitch;
56485
- const changeType = isUpgrade ? "upgrade" : "downgrade";
56486
- logger.info(`${isUpgrade ? "[^]" : "[v]"} ${changeType}: ${currentVersion} -> ${targetVersion}`);
56487
- if (opts.check) {
56488
- note(`CLI update available: ${currentVersion} -> ${targetVersion}
56489
-
56490
- Run 'ck update' to install`, "Update Check");
56491
- await promptKitUpdateFn(opts.dev || opts.beta, opts.yes);
56492
- outro("Check complete");
56493
- return;
56494
- }
56495
- if (!opts.yes) {
56496
- const shouldUpdate = await se({
56497
- message: `${isUpgrade ? "Update" : "Downgrade"} CLI from ${currentVersion} to ${targetVersion}?`
56498
- });
56499
- if (lD(shouldUpdate) || !shouldUpdate) {
56500
- outro("Update cancelled");
56501
- return;
56502
- }
56503
- }
56504
- const updateCmd = packageManagerDetector.getUpdateCommand(pm, CLAUDEKIT_CLI_NPM_PACKAGE_NAME, targetVersion, registryUrl);
56505
- logger.info(`Running: ${redactCommandForLog(updateCmd)}`);
56506
- s.start("Updating CLI...");
56507
- try {
56508
- await execAsyncFn(updateCmd, {
56509
- timeout: 120000
56510
- });
56511
- s.stop("Update completed");
56512
- } catch (error) {
56513
- s.stop("Update failed");
56514
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
56515
- if (errorMessage.includes("EACCES") || errorMessage.includes("EPERM") || errorMessage.includes("permission") || errorMessage.includes("Access is denied")) {
56516
- const permHint = pm === "npm" ? `
56517
-
56518
- Or fix npm permissions: https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally` : "";
56519
- const isWindows3 = process.platform === "win32";
56520
- const elevationHint = isWindows3 ? `Run your terminal as Administrator and retry: ${updateCmd}` : `sudo ${updateCmd}`;
56521
- throw new CliUpdateError(`Permission denied. Try: ${elevationHint}${permHint}`);
56522
- }
56523
- logger.error(`Update failed: ${errorMessage}`);
56524
- logger.info(`Try running: ${redactCommandForLog(updateCmd)}`);
56525
- throw new CliUpdateError(`Update failed: ${errorMessage}
56526
-
56527
- Manual update: ${updateCmd}`);
56528
- }
56529
- s.start("Verifying installation...");
56530
- try {
56531
- const versionResult = await execAsyncFn("ck --version", { timeout: 5000 });
56532
- const stdout = extractCommandStdout(versionResult);
56533
- const activeVersion = parseCliVersionFromOutput(stdout);
56534
- if (!activeVersion) {
56535
- s.stop("Verification failed");
56536
- const message = `Update completed but could not parse 'ck --version' output.
56537
- Please restart your terminal and run 'ck --version'. Expected: ${targetVersion}
56538
-
56539
- Manual update: ${redactCommandForLog(updateCmd)}`;
56540
- logger.error(message);
56541
- throw new CliUpdateError(message);
56542
- }
56543
- s.stop(`Installed version: ${activeVersion}`);
56544
- if (activeVersion !== targetVersion) {
56545
- const mismatchMessage = `Update did not activate the requested version.
56546
- Expected: ${targetVersion}
56547
- Active ck: ${activeVersion}
56548
-
56549
- Likely causes: multiple global installations (npm/bun/pnpm/yarn) or stale shell shim/cache (common on Windows).
56550
- Run '${redactCommandForLog(updateCmd)}' manually, restart terminal, then check command resolution:
56551
- - Windows: where ck
56552
- - macOS/Linux: which -a ck`;
56553
- logger.error(mismatchMessage);
56554
- throw new CliUpdateError(mismatchMessage);
56555
- }
56556
- outro(`[+] Successfully updated ClaudeKit CLI to ${activeVersion}`);
56557
- await promptKitUpdateFn(opts.dev || opts.beta, opts.yes);
56558
- } catch (error) {
56559
- if (error instanceof CliUpdateError) {
56560
- throw error;
56561
- }
56562
- s.stop("Verification failed");
56563
- const message = `Update completed but automatic verification failed.
56564
- Please restart your terminal and run 'ck --version'. Expected: ${targetVersion}
56565
-
56566
- Manual update: ${redactCommandForLog(updateCmd)}`;
56567
- logger.error(message);
56568
- throw new CliUpdateError(message);
56569
- }
56570
- } catch (error) {
56571
- if (error instanceof CliUpdateError) {
56572
- throw error;
56573
- }
56574
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
56575
- logger.error(`Update failed: ${errorMessage}`);
56576
- throw new CliUpdateError(errorMessage);
56577
- }
56578
- }
56579
- var import_compare_versions, import_fs_extra5, execAsync2, CliUpdateError;
56580
- var init_update_cli = __esm(() => {
56581
- init_npm_registry();
56582
- init_package_manager_detector();
56583
- init_metadata_migration();
56584
- init_claudekit_scanner();
56585
- init_claudekit_constants();
56586
- init_logger();
56587
- init_safe_prompts();
56588
- init_types3();
56589
- init_types3();
56590
- init_package();
56591
- import_compare_versions = __toESM(require_umd(), 1);
56592
- import_fs_extra5 = __toESM(require_lib3(), 1);
56593
- execAsync2 = promisify8(exec2);
56594
- CliUpdateError = class CliUpdateError extends ClaudeKitError {
56595
- constructor(message) {
56596
- super(message, "CLI_UPDATE_ERROR");
56597
- this.name = "CliUpdateError";
56598
- }
56599
- };
56600
- });
56601
-
56602
56287
  // src/domains/github/github-auth.ts
56603
56288
  import { execSync } from "node:child_process";
56604
56289
 
@@ -57044,8 +56729,8 @@ var init_error_handler2 = __esm(() => {
57044
56729
 
57045
56730
  // src/domains/versioning/release-cache.ts
57046
56731
  import { existsSync as existsSync35 } from "node:fs";
57047
- import { mkdir as mkdir12, readFile as readFile26, unlink as unlink6, writeFile as writeFile14 } from "node:fs/promises";
57048
- import { join as join42 } from "node:path";
56732
+ import { mkdir as mkdir12, readFile as readFile25, unlink as unlink6, writeFile as writeFile14 } from "node:fs/promises";
56733
+ import { join as join41 } from "node:path";
57049
56734
  var ReleaseCacheEntrySchema, ReleaseCache;
57050
56735
  var init_release_cache = __esm(() => {
57051
56736
  init_logger();
@@ -57060,7 +56745,7 @@ var init_release_cache = __esm(() => {
57060
56745
  static CACHE_TTL_SECONDS = Number(process.env.CK_CACHE_TTL) || 3600;
57061
56746
  cacheDir;
57062
56747
  constructor() {
57063
- this.cacheDir = join42(PathResolver.getCacheDir(false), ReleaseCache.CACHE_DIR);
56748
+ this.cacheDir = join41(PathResolver.getCacheDir(false), ReleaseCache.CACHE_DIR);
57064
56749
  }
57065
56750
  async get(key) {
57066
56751
  const cacheFile = this.getCachePath(key);
@@ -57069,7 +56754,7 @@ var init_release_cache = __esm(() => {
57069
56754
  logger.debug(`Release cache not found for key: ${key}`);
57070
56755
  return null;
57071
56756
  }
57072
- const content = await readFile26(cacheFile, "utf-8");
56757
+ const content = await readFile25(cacheFile, "utf-8");
57073
56758
  const parsed = JSON.parse(content);
57074
56759
  const cacheEntry = ReleaseCacheEntrySchema.parse(parsed);
57075
56760
  if (this.isExpired(cacheEntry.timestamp)) {
@@ -57118,7 +56803,7 @@ var init_release_cache = __esm(() => {
57118
56803
  const files = await readdir10(this.cacheDir);
57119
56804
  for (const file of files) {
57120
56805
  if (file.endsWith(".json")) {
57121
- await unlink6(join42(this.cacheDir, file));
56806
+ await unlink6(join41(this.cacheDir, file));
57122
56807
  }
57123
56808
  }
57124
56809
  logger.debug("All release cache cleared");
@@ -57129,7 +56814,7 @@ var init_release_cache = __esm(() => {
57129
56814
  }
57130
56815
  getCachePath(key) {
57131
56816
  const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
57132
- return join42(this.cacheDir, `${safeKey}.json`);
56817
+ return join41(this.cacheDir, `${safeKey}.json`);
57133
56818
  }
57134
56819
  isExpired(timestamp) {
57135
56820
  const now = Date.now();
@@ -57727,6 +57412,11 @@ var init_client = __esm(() => {
57727
57412
  });
57728
57413
 
57729
57414
  // src/domains/github/github-client.ts
57415
+ var exports_github_client = {};
57416
+ __export(exports_github_client, {
57417
+ GitHubClient: () => GitHubClient
57418
+ });
57419
+
57730
57420
  class GitHubClient {
57731
57421
  releasesApi;
57732
57422
  repoApi;
@@ -57763,45 +57453,387 @@ var init_github_client = __esm(() => {
57763
57453
  init_client();
57764
57454
  });
57765
57455
 
57766
- // src/domains/versioning/checking/version-utils.ts
57767
- function isUpdateCheckDisabled() {
57768
- return process.env.NO_UPDATE_NOTIFIER === "1" || process.env.NO_UPDATE_NOTIFIER === "true" || !process.stdout.isTTY;
57456
+ // src/commands/update-cli.ts
57457
+ import { exec as exec2 } from "node:child_process";
57458
+ import { join as join42 } from "node:path";
57459
+ import { promisify as promisify8 } from "node:util";
57460
+ function getDefaultUpdateCliCommandDeps() {
57461
+ return {
57462
+ currentVersion: package_default.version,
57463
+ execAsyncFn: execAsync2,
57464
+ packageManagerDetector: PackageManagerDetector,
57465
+ npmRegistryClient: NpmRegistryClient,
57466
+ promptKitUpdateFn: promptKitUpdate
57467
+ };
57769
57468
  }
57770
- function normalizeVersion(version) {
57771
- return version.replace(/^v/i, "");
57469
+ function extractCommandStdout(result) {
57470
+ if (typeof result === "string") {
57471
+ return result;
57472
+ }
57473
+ if (result && typeof result.stdout === "string") {
57474
+ return result.stdout;
57475
+ }
57476
+ return "";
57772
57477
  }
57773
- function parseVersionParts(version) {
57774
- const normalized = normalizeVersion(version);
57775
- const [base, ...prereleaseParts] = normalized.split("-");
57478
+ function redactCommandForLog(command) {
57479
+ if (!command)
57480
+ return command;
57481
+ const redactedRegistryFlags = command.replace(/(--registry(?:=|\s+))(['"]?)(\S+?)(\2)(?=\s|$)/g, (_match, prefix, quote, url) => `${prefix}${quote}${redactRegistryUrlForLog(url)}${quote}`);
57482
+ return redactedRegistryFlags.replace(/https?:\/\/[^\s"']+/g, (url) => redactRegistryUrlForLog(url));
57483
+ }
57484
+ function buildInitCommand(isGlobal, kit, beta) {
57485
+ const parts = ["ck init"];
57486
+ if (isGlobal)
57487
+ parts.push("-g");
57488
+ if (kit)
57489
+ parts.push(`--kit ${kit}`);
57490
+ parts.push("--yes --install-skills");
57491
+ if (beta)
57492
+ parts.push("--beta");
57493
+ return parts.join(" ");
57494
+ }
57495
+ function isBetaVersion(version) {
57496
+ if (!version)
57497
+ return false;
57498
+ return /-(beta|alpha|rc|dev)[.\d]/i.test(version);
57499
+ }
57500
+ function parseCliVersionFromOutput(output2) {
57501
+ if (!output2)
57502
+ return null;
57503
+ const match = output2.match(/CLI Version:\s*(\S+)/);
57504
+ return match ? match[1] : null;
57505
+ }
57506
+ function selectKitForUpdate(params) {
57507
+ const { hasLocal, hasGlobal, localKits, globalKits } = params;
57508
+ const hasLocalKit = localKits.length > 0 || hasLocal;
57509
+ const hasGlobalKit = globalKits.length > 0 || hasGlobal;
57510
+ if (!hasLocalKit && !hasGlobalKit) {
57511
+ return null;
57512
+ }
57513
+ if (hasGlobalKit && !hasLocalKit) {
57514
+ const kit2 = globalKits[0] || localKits[0];
57515
+ return {
57516
+ isGlobal: true,
57517
+ kit: kit2,
57518
+ promptMessage: `Update global ClaudeKit content${kit2 ? ` (${kit2})` : ""}?`
57519
+ };
57520
+ }
57521
+ if (hasLocalKit && !hasGlobalKit) {
57522
+ const kit2 = localKits[0] || globalKits[0];
57523
+ return {
57524
+ isGlobal: false,
57525
+ kit: kit2,
57526
+ promptMessage: `Update local project ClaudeKit content${kit2 ? ` (${kit2})` : ""}?`
57527
+ };
57528
+ }
57529
+ const kit = globalKits[0] || localKits[0];
57776
57530
  return {
57777
- base,
57778
- prerelease: prereleaseParts.length > 0 ? prereleaseParts.join("-") : null
57531
+ isGlobal: true,
57532
+ kit,
57533
+ promptMessage: `Update global ClaudeKit content${kit ? ` (${kit})` : ""}?`
57779
57534
  };
57780
57535
  }
57781
- function isDevPrereleaseOfSameBase(currentVersion, latestVersion) {
57782
- const current = parseVersionParts(currentVersion);
57783
- const latest = parseVersionParts(latestVersion);
57784
- if (!current.prerelease?.startsWith("dev"))
57785
- return false;
57786
- if (latest.prerelease !== null)
57787
- return false;
57788
- return current.base === latest.base;
57536
+ async function readMetadataFile(claudeDir2) {
57537
+ const metadataPath = join42(claudeDir2, "metadata.json");
57538
+ try {
57539
+ if (!await import_fs_extra5.pathExists(metadataPath)) {
57540
+ return null;
57541
+ }
57542
+ const content = await import_fs_extra5.readFile(metadataPath, "utf-8");
57543
+ const parsed = JSON.parse(content);
57544
+ const validated = MetadataSchema.safeParse(parsed);
57545
+ if (!validated.success) {
57546
+ logger.verbose(`Invalid metadata format: ${validated.error.message}`);
57547
+ return null;
57548
+ }
57549
+ return validated.data;
57550
+ } catch (error) {
57551
+ logger.verbose(`Failed to read metadata: ${error instanceof Error ? error.message : "unknown"}`);
57552
+ return null;
57553
+ }
57789
57554
  }
57790
- function isNewerVersion(currentVersion, latestVersion) {
57555
+ async function fetchLatestReleaseTag(kit, beta) {
57791
57556
  try {
57792
- const current = normalizeVersion(currentVersion);
57793
- const latest = normalizeVersion(latestVersion);
57794
- if (isDevPrereleaseOfSameBase(current, latest)) {
57795
- return false;
57557
+ const { GitHubClient: GitHubClient2 } = await Promise.resolve().then(() => (init_github_client(), exports_github_client));
57558
+ const github = new GitHubClient2;
57559
+ const kitConfig = AVAILABLE_KITS[kit];
57560
+ const release = await github.getLatestRelease(kitConfig, beta);
57561
+ return release.tag_name;
57562
+ } catch (error) {
57563
+ logger.verbose(`Could not fetch latest release tag: ${error instanceof Error ? error.message : "unknown"}`);
57564
+ return null;
57565
+ }
57566
+ }
57567
+ async function promptKitUpdate(beta, yes, deps) {
57568
+ try {
57569
+ const execFn = deps?.execAsyncFn ?? execAsync2;
57570
+ const setup = await (deps?.getSetupFn ?? getClaudeKitSetup)();
57571
+ const hasLocal = !!setup.project.metadata;
57572
+ const hasGlobal = !!setup.global.metadata;
57573
+ const localMetadata = hasLocal ? await readMetadataFile(setup.project.path) : null;
57574
+ const globalMetadata = hasGlobal ? await readMetadataFile(setup.global.path) : null;
57575
+ const localKits = localMetadata ? getInstalledKits(localMetadata) : [];
57576
+ const globalKits = globalMetadata ? getInstalledKits(globalMetadata) : [];
57577
+ const selection = selectKitForUpdate({ hasLocal, hasGlobal, localKits, globalKits });
57578
+ if (!selection) {
57579
+ logger.verbose("No ClaudeKit installations detected, skipping kit update prompt");
57580
+ return;
57796
57581
  }
57797
- return import_compare_versions3.compareVersions(latest, current) > 0;
57798
- } catch {
57799
- return false;
57582
+ const kitVersion = selection.kit ? selection.isGlobal ? globalMetadata?.kits?.[selection.kit]?.version : localMetadata?.kits?.[selection.kit]?.version : undefined;
57583
+ const isBetaInstalled = isBetaVersion(kitVersion);
57584
+ const initCmd = buildInitCommand(selection.isGlobal, selection.kit, beta || isBetaInstalled);
57585
+ const promptMessage = selection.promptMessage;
57586
+ if (selection.kit && kitVersion) {
57587
+ logger.info(`Current kit version: ${selection.kit}@${kitVersion}`);
57588
+ }
57589
+ if (yes && selection.kit && kitVersion) {
57590
+ const getTagFn = deps?.getLatestReleaseTagFn ?? fetchLatestReleaseTag;
57591
+ const latestTag = await getTagFn(selection.kit, beta || isBetaInstalled);
57592
+ if (latestTag && versionsMatch(kitVersion, latestTag)) {
57593
+ logger.success(`Already at latest version (${selection.kit}@${kitVersion}), skipping reinstall`);
57594
+ return;
57595
+ }
57596
+ if (latestTag) {
57597
+ logger.info(`Kit update available: ${kitVersion} -> ${latestTag}`);
57598
+ }
57599
+ }
57600
+ if (!yes) {
57601
+ logger.info("");
57602
+ const shouldUpdate = await se({
57603
+ message: promptMessage
57604
+ });
57605
+ if (lD(shouldUpdate) || !shouldUpdate) {
57606
+ log.info("Skipped kit content update");
57607
+ return;
57608
+ }
57609
+ } else {
57610
+ logger.verbose("Auto-proceeding with kit update (--yes flag)");
57611
+ }
57612
+ logger.info(`Running: ${initCmd}`);
57613
+ const s = (deps?.spinnerFn ?? de)();
57614
+ s.start("Updating ClaudeKit content...");
57615
+ try {
57616
+ await execFn(initCmd, {
57617
+ timeout: 300000
57618
+ });
57619
+ let newKitVersion;
57620
+ try {
57621
+ const claudeDir2 = selection.isGlobal ? setup.global.path : setup.project.path;
57622
+ const updatedMetadata = await readMetadataFile(claudeDir2);
57623
+ newKitVersion = selection.kit ? updatedMetadata?.kits?.[selection.kit]?.version : undefined;
57624
+ } catch {}
57625
+ if (selection.kit && kitVersion && newKitVersion && kitVersion !== newKitVersion) {
57626
+ s.stop(`Kit updated: ${selection.kit}@${kitVersion} -> ${newKitVersion}`);
57627
+ } else if (selection.kit && newKitVersion) {
57628
+ s.stop(`Kit content updated (${selection.kit}@${newKitVersion})`);
57629
+ } else {
57630
+ s.stop("Kit content updated");
57631
+ }
57632
+ } catch (error) {
57633
+ s.stop("Kit update finished");
57634
+ const errorMsg = error instanceof Error ? error.message : "unknown";
57635
+ if (errorMsg.includes("exit code") && !errorMsg.includes("exit code 0")) {
57636
+ logger.warning("Kit content update may have encountered issues");
57637
+ logger.verbose(`Error: ${errorMsg}`);
57638
+ } else {
57639
+ logger.verbose(`Init command completed: ${errorMsg}`);
57640
+ }
57641
+ }
57642
+ } catch (error) {
57643
+ logger.verbose(`Failed to prompt for kit update: ${error instanceof Error ? error.message : "unknown error"}`);
57800
57644
  }
57801
57645
  }
57802
- var import_compare_versions3;
57803
- var init_version_utils = __esm(() => {
57646
+ async function updateCliCommand(options2, deps = getDefaultUpdateCliCommandDeps()) {
57647
+ const s = de();
57648
+ intro("[>] ClaudeKit CLI - Update");
57649
+ try {
57650
+ const {
57651
+ currentVersion,
57652
+ execAsyncFn,
57653
+ packageManagerDetector,
57654
+ npmRegistryClient,
57655
+ promptKitUpdateFn
57656
+ } = deps;
57657
+ const opts = UpdateCliOptionsSchema.parse(options2);
57658
+ logger.info(`Current CLI version: ${currentVersion}`);
57659
+ s.start("Detecting package manager...");
57660
+ const pm = await packageManagerDetector.detect();
57661
+ const pmVersion = await packageManagerDetector.getVersion(pm);
57662
+ s.stop(`Using ${packageManagerDetector.getDisplayName(pm)}${pmVersion ? ` v${pmVersion}` : ""}`);
57663
+ logger.verbose(`Detected package manager: ${pm}`);
57664
+ let registryUrl = opts.registry;
57665
+ if (!registryUrl && pm === "npm") {
57666
+ const userRegistry = await packageManagerDetector.getNpmRegistryUrl();
57667
+ if (userRegistry) {
57668
+ registryUrl = userRegistry;
57669
+ logger.verbose(`Using npm configured registry: ${redactRegistryUrlForLog(registryUrl)}`);
57670
+ }
57671
+ }
57672
+ s.start("Checking for updates...");
57673
+ let targetVersion = null;
57674
+ if (opts.release && opts.release !== "latest") {
57675
+ try {
57676
+ const exists = await npmRegistryClient.versionExists(CLAUDEKIT_CLI_NPM_PACKAGE_NAME, opts.release, registryUrl);
57677
+ if (!exists) {
57678
+ s.stop("Version not found");
57679
+ throw new CliUpdateError(`Version ${opts.release} does not exist on npm registry. Run 'ck versions' to see available versions.`);
57680
+ }
57681
+ } catch (error) {
57682
+ if (error instanceof CliUpdateError) {
57683
+ throw error;
57684
+ }
57685
+ s.stop("Version check failed");
57686
+ const message = error instanceof Error ? error.message : "Unknown error";
57687
+ logger.verbose(`Release check failed for ${opts.release}: ${message}`);
57688
+ const registryHint = registryUrl ? ` (${redactRegistryUrlForLog(registryUrl)})` : " (default registry)";
57689
+ throw new CliUpdateError(`Failed to verify version ${opts.release} on npm registry${registryHint}. Check registry settings/network connectivity and try again.`);
57690
+ }
57691
+ targetVersion = opts.release;
57692
+ s.stop(`Target version: ${targetVersion}`);
57693
+ } else if (opts.dev || opts.beta) {
57694
+ targetVersion = await npmRegistryClient.getDevVersion(CLAUDEKIT_CLI_NPM_PACKAGE_NAME, registryUrl);
57695
+ if (!targetVersion) {
57696
+ s.stop("No dev version available");
57697
+ logger.warning("No dev version found. Using latest stable version instead.");
57698
+ targetVersion = await npmRegistryClient.getLatestVersion(CLAUDEKIT_CLI_NPM_PACKAGE_NAME, registryUrl);
57699
+ } else {
57700
+ s.stop(`Latest dev version: ${targetVersion}`);
57701
+ }
57702
+ } else {
57703
+ targetVersion = await npmRegistryClient.getLatestVersion(CLAUDEKIT_CLI_NPM_PACKAGE_NAME, registryUrl);
57704
+ s.stop(`Latest version: ${targetVersion || "unknown"}`);
57705
+ }
57706
+ if (!targetVersion) {
57707
+ throw new CliUpdateError(`Failed to fetch version information from npm registry. Check your internet connection and try again. Manual update: ${packageManagerDetector.getUpdateCommand(pm, CLAUDEKIT_CLI_NPM_PACKAGE_NAME, undefined, registryUrl)}`);
57708
+ }
57709
+ const comparison = import_compare_versions3.compareVersions(currentVersion, targetVersion);
57710
+ if (comparison === 0) {
57711
+ outro(`[+] Already on the latest CLI version (${currentVersion})`);
57712
+ await promptKitUpdateFn(opts.dev || opts.beta, opts.yes);
57713
+ return;
57714
+ }
57715
+ const isDevChannelSwitch = (opts.dev || opts.beta) && isBetaVersion(targetVersion) && !isBetaVersion(currentVersion);
57716
+ if (comparison > 0 && !opts.release && !isDevChannelSwitch) {
57717
+ outro(`[+] Current version (${currentVersion}) is newer than latest (${targetVersion})`);
57718
+ return;
57719
+ }
57720
+ const isUpgrade = comparison < 0 || isDevChannelSwitch;
57721
+ const changeType = isUpgrade ? "upgrade" : "downgrade";
57722
+ logger.info(`${isUpgrade ? "[^]" : "[v]"} ${changeType}: ${currentVersion} -> ${targetVersion}`);
57723
+ if (opts.check) {
57724
+ note(`CLI update available: ${currentVersion} -> ${targetVersion}
57725
+
57726
+ Run 'ck update' to install`, "Update Check");
57727
+ await promptKitUpdateFn(opts.dev || opts.beta, opts.yes);
57728
+ outro("Check complete");
57729
+ return;
57730
+ }
57731
+ if (!opts.yes) {
57732
+ const shouldUpdate = await se({
57733
+ message: `${isUpgrade ? "Update" : "Downgrade"} CLI from ${currentVersion} to ${targetVersion}?`
57734
+ });
57735
+ if (lD(shouldUpdate) || !shouldUpdate) {
57736
+ outro("Update cancelled");
57737
+ return;
57738
+ }
57739
+ }
57740
+ const updateCmd = packageManagerDetector.getUpdateCommand(pm, CLAUDEKIT_CLI_NPM_PACKAGE_NAME, targetVersion, registryUrl);
57741
+ logger.info(`Running: ${redactCommandForLog(updateCmd)}`);
57742
+ s.start("Updating CLI...");
57743
+ try {
57744
+ await execAsyncFn(updateCmd, {
57745
+ timeout: 120000
57746
+ });
57747
+ s.stop("Update completed");
57748
+ } catch (error) {
57749
+ s.stop("Update failed");
57750
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
57751
+ if (errorMessage.includes("EACCES") || errorMessage.includes("EPERM") || errorMessage.includes("permission") || errorMessage.includes("Access is denied")) {
57752
+ const permHint = pm === "npm" ? `
57753
+
57754
+ Or fix npm permissions: https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally` : "";
57755
+ const isWindows3 = process.platform === "win32";
57756
+ const elevationHint = isWindows3 ? `Run your terminal as Administrator and retry: ${updateCmd}` : `sudo ${updateCmd}`;
57757
+ throw new CliUpdateError(`Permission denied. Try: ${elevationHint}${permHint}`);
57758
+ }
57759
+ logger.error(`Update failed: ${errorMessage}`);
57760
+ logger.info(`Try running: ${redactCommandForLog(updateCmd)}`);
57761
+ throw new CliUpdateError(`Update failed: ${errorMessage}
57762
+
57763
+ Manual update: ${updateCmd}`);
57764
+ }
57765
+ s.start("Verifying installation...");
57766
+ try {
57767
+ const versionResult = await execAsyncFn("ck --version", { timeout: 5000 });
57768
+ const stdout = extractCommandStdout(versionResult);
57769
+ const activeVersion = parseCliVersionFromOutput(stdout);
57770
+ if (!activeVersion) {
57771
+ s.stop("Verification failed");
57772
+ const message = `Update completed but could not parse 'ck --version' output.
57773
+ Please restart your terminal and run 'ck --version'. Expected: ${targetVersion}
57774
+
57775
+ Manual update: ${redactCommandForLog(updateCmd)}`;
57776
+ logger.error(message);
57777
+ throw new CliUpdateError(message);
57778
+ }
57779
+ s.stop(`Installed version: ${activeVersion}`);
57780
+ if (activeVersion !== targetVersion) {
57781
+ const mismatchMessage = `Update did not activate the requested version.
57782
+ Expected: ${targetVersion}
57783
+ Active ck: ${activeVersion}
57784
+
57785
+ Likely causes: multiple global installations (npm/bun/pnpm/yarn) or stale shell shim/cache (common on Windows).
57786
+ Run '${redactCommandForLog(updateCmd)}' manually, restart terminal, then check command resolution:
57787
+ - Windows: where ck
57788
+ - macOS/Linux: which -a ck`;
57789
+ logger.error(mismatchMessage);
57790
+ throw new CliUpdateError(mismatchMessage);
57791
+ }
57792
+ outro(`[+] Successfully updated ClaudeKit CLI to ${activeVersion}`);
57793
+ await promptKitUpdateFn(opts.dev || opts.beta, opts.yes);
57794
+ } catch (error) {
57795
+ if (error instanceof CliUpdateError) {
57796
+ throw error;
57797
+ }
57798
+ s.stop("Verification failed");
57799
+ const message = `Update completed but automatic verification failed.
57800
+ Please restart your terminal and run 'ck --version'. Expected: ${targetVersion}
57801
+
57802
+ Manual update: ${redactCommandForLog(updateCmd)}`;
57803
+ logger.error(message);
57804
+ throw new CliUpdateError(message);
57805
+ }
57806
+ } catch (error) {
57807
+ if (error instanceof CliUpdateError) {
57808
+ throw error;
57809
+ }
57810
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
57811
+ logger.error(`Update failed: ${errorMessage}`);
57812
+ throw new CliUpdateError(errorMessage);
57813
+ }
57814
+ }
57815
+ var import_compare_versions3, import_fs_extra5, execAsync2, CliUpdateError;
57816
+ var init_update_cli = __esm(() => {
57817
+ init_npm_registry();
57818
+ init_package_manager_detector();
57819
+ init_metadata_migration();
57820
+ init_version_utils();
57821
+ init_claudekit_scanner();
57822
+ init_claudekit_constants();
57823
+ init_logger();
57824
+ init_safe_prompts();
57825
+ init_types3();
57826
+ init_types3();
57827
+ init_package();
57804
57828
  import_compare_versions3 = __toESM(require_umd(), 1);
57829
+ import_fs_extra5 = __toESM(require_lib3(), 1);
57830
+ execAsync2 = promisify8(exec2);
57831
+ CliUpdateError = class CliUpdateError extends ClaudeKitError {
57832
+ constructor(message) {
57833
+ super(message, "CLI_UPDATE_ERROR");
57834
+ this.name = "CliUpdateError";
57835
+ }
57836
+ };
57805
57837
  });
57806
57838
 
57807
57839
  // src/domains/versioning/version-cache.ts
@@ -91097,6 +91129,7 @@ async function handleFreshInstallation(claudeDir2, prompts) {
91097
91129
  }
91098
91130
 
91099
91131
  // src/commands/init/phases/selection-handler.ts
91132
+ init_version_utils();
91100
91133
  init_claudekit_scanner();
91101
91134
  init_logger();
91102
91135
  init_path_resolver();
@@ -91417,6 +91450,21 @@ async function handleSelection(ctx) {
91417
91450
  logger.success(`Found: ${release.tag_name}`);
91418
91451
  }
91419
91452
  }
91453
+ const releaseTag = release?.tag_name;
91454
+ if (ctx.options.yes && !ctx.options.fresh && releaseTag && !isOfflineMode && !pendingKits?.length) {
91455
+ try {
91456
+ const prefix = PathResolver.getPathPrefix(ctx.options.global);
91457
+ const claudeDir2 = prefix ? join106(resolvedDir, prefix) : resolvedDir;
91458
+ const existingMetadata = await readManifest(claudeDir2);
91459
+ const installedKitVersion = existingMetadata?.kits?.[kitType]?.version;
91460
+ if (installedKitVersion && versionsMatch(installedKitVersion, releaseTag)) {
91461
+ logger.success(`Already at latest version (${kitType}@${installedKitVersion}), skipping reinstall`);
91462
+ return { ...ctx, cancelled: true };
91463
+ }
91464
+ } catch (error) {
91465
+ logger.verbose(`Metadata read failed, proceeding with installation: ${error instanceof Error ? error.message : "unknown"}`);
91466
+ }
91467
+ }
91420
91468
  return {
91421
91469
  ...ctx,
91422
91470
  kit,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudekit-cli",
3
- "version": "3.36.0-dev.18",
3
+ "version": "3.36.0-dev.19",
4
4
  "description": "CLI tool for bootstrapping and updating ClaudeKit projects",
5
5
  "type": "module",
6
6
  "repository": {