claudekit-cli 3.25.0 → 3.26.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 +250 -77
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6297,7 +6297,7 @@ var require_jsonfile = __commonJS((exports, module) => {
6297
6297
  return obj;
6298
6298
  }
6299
6299
  var readFile = universalify.fromPromise(_readFile);
6300
- function readFileSync3(file, options = {}) {
6300
+ function readFileSync4(file, options = {}) {
6301
6301
  if (typeof options === "string") {
6302
6302
  options = { encoding: options };
6303
6303
  }
@@ -6329,7 +6329,7 @@ var require_jsonfile = __commonJS((exports, module) => {
6329
6329
  }
6330
6330
  module.exports = {
6331
6331
  readFile,
6332
- readFileSync: readFileSync3,
6332
+ readFileSync: readFileSync4,
6333
6333
  writeFile,
6334
6334
  writeFileSync
6335
6335
  };
@@ -8208,7 +8208,7 @@ var init_opencode_installer = __esm(() => {
8208
8208
  var PARTIAL_INSTALL_VERSION = "partial", EXIT_CODE_CRITICAL_FAILURE = 1, EXIT_CODE_PARTIAL_SUCCESS = 2;
8209
8209
 
8210
8210
  // src/services/package-installer/install-error-handler.ts
8211
- import { existsSync as existsSync13, readFileSync as readFileSync5, unlinkSync as unlinkSync2 } from "node:fs";
8211
+ import { existsSync as existsSync13, readFileSync as readFileSync6, unlinkSync as unlinkSync2 } from "node:fs";
8212
8212
  import { join as join21 } from "node:path";
8213
8213
  function parseNameReason(str) {
8214
8214
  const colonIndex = str.indexOf(":");
@@ -8225,7 +8225,7 @@ function displayInstallErrors(skillsDir) {
8225
8225
  }
8226
8226
  let summary;
8227
8227
  try {
8228
- summary = JSON.parse(readFileSync5(summaryPath, "utf-8"));
8228
+ summary = JSON.parse(readFileSync6(summaryPath, "utf-8"));
8229
8229
  } catch (parseError) {
8230
8230
  logger.error("Failed to parse error summary. File may be corrupted.");
8231
8231
  logger.debug(`Parse error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
@@ -16442,7 +16442,7 @@ var init_update_command_help = __esm(() => {
16442
16442
  sections: [
16443
16443
  {
16444
16444
  title: "Note",
16445
- content: "'ck update' updates the CLI tool only. To update kit content (skills, commands, workflows), use 'ck init' for local or 'ck init -g' for global."
16445
+ content: "'ck update' updates the CLI tool only. To update kit content (skills, commands, rules), use 'ck init' for local or 'ck init -g' for global."
16446
16446
  }
16447
16447
  ]
16448
16448
  };
@@ -17682,6 +17682,70 @@ class CheckRunner {
17682
17682
  import { exec as exec4 } from "node:child_process";
17683
17683
  import { promisify as promisify4 } from "node:util";
17684
17684
 
17685
+ // src/domains/github/gh-cli-utils.ts
17686
+ init_logger();
17687
+ import { readFileSync } from "node:fs";
17688
+ var MIN_GH_CLI_VERSION = "2.20.0";
17689
+ var GH_COMMAND_TIMEOUT_MS = 1e4;
17690
+ function compareVersions(a, b) {
17691
+ const partsA = a.split(".").map(Number);
17692
+ const partsB = b.split(".").map(Number);
17693
+ const maxLen = Math.max(partsA.length, partsB.length);
17694
+ for (let i = 0;i < maxLen; i++) {
17695
+ const numA = partsA[i] ?? 0;
17696
+ const numB = partsB[i] ?? 0;
17697
+ if (numA < numB)
17698
+ return -1;
17699
+ if (numA > numB)
17700
+ return 1;
17701
+ }
17702
+ return 0;
17703
+ }
17704
+ function isWSL() {
17705
+ if (process.platform !== "linux")
17706
+ return false;
17707
+ try {
17708
+ const release = readFileSync("/proc/version", "utf-8").toLowerCase();
17709
+ return release.includes("microsoft") || release.includes("wsl");
17710
+ } catch (error) {
17711
+ logger.debug(`WSL detection skipped: ${error instanceof Error ? error.message : "unknown error"}`);
17712
+ return false;
17713
+ }
17714
+ }
17715
+ function shouldSkipExpensiveOperations() {
17716
+ if (process.env.CK_TEST_HOME) {
17717
+ return false;
17718
+ }
17719
+ return process.env.CI === "true" || process.env.CI_SAFE_MODE === "true";
17720
+ }
17721
+ function getGhUpgradeInstructions(currentVersion) {
17722
+ const platform = process.platform;
17723
+ const wsl = isWSL();
17724
+ const lines = [];
17725
+ lines.push(`✗ GitHub CLI v${currentVersion} is outdated`);
17726
+ lines.push(` Minimum required: v${MIN_GH_CLI_VERSION}`);
17727
+ lines.push("");
17728
+ if (wsl) {
17729
+ lines.push("Upgrade GitHub CLI (WSL/Ubuntu):");
17730
+ lines.push(" curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg");
17731
+ lines.push(' echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null');
17732
+ lines.push(" sudo apt update && sudo apt install gh");
17733
+ } else if (platform === "darwin") {
17734
+ lines.push("Upgrade GitHub CLI:");
17735
+ lines.push(" brew upgrade gh");
17736
+ } else if (platform === "win32") {
17737
+ lines.push("Upgrade GitHub CLI:");
17738
+ lines.push(" winget upgrade GitHub.cli");
17739
+ } else {
17740
+ lines.push("Upgrade GitHub CLI:");
17741
+ lines.push(" sudo apt update && sudo apt upgrade gh");
17742
+ lines.push(" Or visit: https://cli.github.com");
17743
+ }
17744
+ lines.push("");
17745
+ lines.push("After upgrade: gh auth login -h github.com");
17746
+ return lines;
17747
+ }
17748
+
17685
17749
  // src/services/package-installer/dependency-checker.ts
17686
17750
  import { exec } from "node:child_process";
17687
17751
  import { promisify } from "node:util";
@@ -17697,7 +17761,7 @@ function notFoundError(type, name, hint) {
17697
17761
  // src/services/package-installer/dependency-checker.ts
17698
17762
  init_logger();
17699
17763
  var execAsync = promisify(exec);
17700
- function shouldSkipExpensiveOperations() {
17764
+ function shouldSkipExpensiveOperations2() {
17701
17765
  if (process.env.CK_TEST_HOME) {
17702
17766
  return false;
17703
17767
  }
@@ -17709,9 +17773,9 @@ function getOSInfo() {
17709
17773
  const isWindows = platform === "win32";
17710
17774
  const isMacOS = platform === "darwin";
17711
17775
  const isLinux = platform === "linux";
17712
- const isWSL = isLinux && process.env.WSL_DISTRO_NAME !== undefined;
17776
+ const isWSL2 = isLinux && process.env.WSL_DISTRO_NAME !== undefined;
17713
17777
  let details = `${platform}-${arch}`;
17714
- if (isWSL) {
17778
+ if (isWSL2) {
17715
17779
  details += ` (WSL: ${process.env.WSL_DISTRO_NAME})`;
17716
17780
  }
17717
17781
  return {
@@ -17720,7 +17784,7 @@ function getOSInfo() {
17720
17784
  isWindows,
17721
17785
  isMacOS,
17722
17786
  isLinux,
17723
- isWSL,
17787
+ isWSL: isWSL2,
17724
17788
  details
17725
17789
  };
17726
17790
  }
@@ -17791,7 +17855,7 @@ var DEPENDENCIES = {
17791
17855
  }
17792
17856
  };
17793
17857
  async function commandExists(command) {
17794
- if (shouldSkipExpensiveOperations()) {
17858
+ if (shouldSkipExpensiveOperations2()) {
17795
17859
  const supportedCommands = ["node", "python", "python3", "pip", "pip3", "claude"];
17796
17860
  return supportedCommands.includes(command);
17797
17861
  }
@@ -17807,7 +17871,7 @@ async function commandExists(command) {
17807
17871
  }
17808
17872
  }
17809
17873
  async function getCommandPath(command) {
17810
- if (shouldSkipExpensiveOperations()) {
17874
+ if (shouldSkipExpensiveOperations2()) {
17811
17875
  const ciPath = getCICommandPath(command);
17812
17876
  if (ciPath)
17813
17877
  return ciPath;
@@ -17826,7 +17890,7 @@ async function getCommandPath(command) {
17826
17890
  }
17827
17891
  }
17828
17892
  async function getCommandVersion(command, versionFlag, versionRegex) {
17829
- if (shouldSkipExpensiveOperations()) {
17893
+ if (shouldSkipExpensiveOperations2()) {
17830
17894
  const mockVersions = {
17831
17895
  npm: "10.0.0",
17832
17896
  node: "20.0.0",
@@ -17852,7 +17916,7 @@ async function getCommandVersion(command, versionFlag, versionRegex) {
17852
17916
  return null;
17853
17917
  }
17854
17918
  }
17855
- function compareVersions(current, required) {
17919
+ function compareVersions2(current, required) {
17856
17920
  const parseCurrent = current.split(".").map((n) => Number.parseInt(n, 10));
17857
17921
  const parseRequired = required.split(".").map((n) => Number.parseInt(n, 10));
17858
17922
  for (let i = 0;i < 3; i++) {
@@ -17876,7 +17940,7 @@ async function checkDependency(config) {
17876
17940
  let meetsRequirements = true;
17877
17941
  let message;
17878
17942
  if (config.minVersion && version) {
17879
- meetsRequirements = compareVersions(version, config.minVersion);
17943
+ meetsRequirements = compareVersions2(version, config.minVersion);
17880
17944
  if (!meetsRequirements) {
17881
17945
  message = `Version ${version} is below minimum ${config.minVersion}`;
17882
17946
  }
@@ -18135,27 +18199,6 @@ async function installDependency(dependency, method) {
18135
18199
  // src/domains/health-checks/system-checker.ts
18136
18200
  init_logger();
18137
18201
  var execAsync4 = promisify4(exec4);
18138
- var MIN_GH_CLI_VERSION = "2.20.0";
18139
- function compareVersions2(a, b) {
18140
- const partsA = a.split(".").map(Number);
18141
- const partsB = b.split(".").map(Number);
18142
- const maxLen = Math.max(partsA.length, partsB.length);
18143
- for (let i = 0;i < maxLen; i++) {
18144
- const numA = partsA[i] ?? 0;
18145
- const numB = partsB[i] ?? 0;
18146
- if (numA < numB)
18147
- return -1;
18148
- if (numA > numB)
18149
- return 1;
18150
- }
18151
- return 0;
18152
- }
18153
- function shouldSkipExpensiveOperations2() {
18154
- if (process.env.CK_TEST_HOME) {
18155
- return false;
18156
- }
18157
- return process.env.CI === "true" || process.env.CI_SAFE_MODE === "true";
18158
- }
18159
18202
 
18160
18203
  class SystemChecker {
18161
18204
  group = "system";
@@ -18170,7 +18213,7 @@ class SystemChecker {
18170
18213
  logger.verbose(`SystemChecker: Processing ${dep.name}`);
18171
18214
  results.push(await this.mapDependencyToCheck(dep));
18172
18215
  }
18173
- if (!shouldSkipExpensiveOperations2()) {
18216
+ if (!shouldSkipExpensiveOperations()) {
18174
18217
  logger.verbose("SystemChecker: Checking git");
18175
18218
  results.push(await this.checkGit());
18176
18219
  logger.verbose("SystemChecker: Checking GitHub CLI");
@@ -18292,7 +18335,7 @@ class SystemChecker {
18292
18335
  const { stdout } = await execAsync4("gh --version");
18293
18336
  const match = stdout.match(/(\d+\.\d+\.\d+)/);
18294
18337
  const version = match?.[1];
18295
- if (version && compareVersions2(version, MIN_GH_CLI_VERSION) < 0) {
18338
+ if (version && compareVersions(version, MIN_GH_CLI_VERSION) < 0) {
18296
18339
  return {
18297
18340
  id: "gh-cli-version",
18298
18341
  name: "GitHub CLI",
@@ -18370,18 +18413,18 @@ class SystemChecker {
18370
18413
  import { join as join2 } from "node:path";
18371
18414
 
18372
18415
  // src/shared/path-resolver.ts
18373
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
18416
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
18374
18417
  import { homedir, platform } from "node:os";
18375
18418
  import { join, normalize } from "node:path";
18376
- function isWSL() {
18419
+ function isWSL2() {
18377
18420
  try {
18378
- return process.platform === "linux" && existsSync2("/proc/version") && readFileSync2("/proc/version", "utf8").toLowerCase().includes("microsoft");
18421
+ return process.platform === "linux" && existsSync2("/proc/version") && readFileSync3("/proc/version", "utf8").toLowerCase().includes("microsoft");
18379
18422
  } catch {
18380
18423
  return false;
18381
18424
  }
18382
18425
  }
18383
18426
  function normalizeWSLPath(p) {
18384
- if (!isWSL())
18427
+ if (!isWSL2())
18385
18428
  return p;
18386
18429
  const windowsMatch = p.match(/^([A-Za-z]):(.*)/);
18387
18430
  if (windowsMatch) {
@@ -18499,7 +18542,7 @@ class PathResolver {
18499
18542
  }
18500
18543
  static buildComponentPath(baseDir, component, global2) {
18501
18544
  if (!PathResolver.isValidComponentName(component)) {
18502
- throw new Error(`Invalid component name: "${component}" contains path traversal patterns. Valid names are simple directory names like "agents", "commands", "workflows", "skills", or "hooks".`);
18545
+ throw new Error(`Invalid component name: "${component}" contains path traversal patterns. Valid names are simple directory names like "agents", "commands", "rules", "skills", or "hooks".`);
18503
18546
  }
18504
18547
  const prefix = PathResolver.getPathPrefix(global2);
18505
18548
  if (prefix) {
@@ -18524,7 +18567,7 @@ class PathResolver {
18524
18567
  return normalizeWSLPath(p);
18525
18568
  }
18526
18569
  static isWSL() {
18527
- return isWSL();
18570
+ return isWSL2();
18528
18571
  }
18529
18572
  static isAtHomeDirectory(cwd) {
18530
18573
  const currentDir = normalize(cwd || process.cwd());
@@ -18577,7 +18620,7 @@ async function scanClaudeKitDirectory(directoryPath) {
18577
18620
  const counts = {
18578
18621
  agents: 0,
18579
18622
  commands: 0,
18580
- workflows: 0,
18623
+ rules: 0,
18581
18624
  skills: 0
18582
18625
  };
18583
18626
  try {
@@ -18595,10 +18638,14 @@ async function scanClaudeKitDirectory(directoryPath) {
18595
18638
  const commandFiles = await import_fs_extra.readdir(commandsPath);
18596
18639
  counts.commands = commandFiles.filter((file) => file.endsWith(".md")).length;
18597
18640
  }
18598
- if (items.includes("workflows")) {
18641
+ if (items.includes("rules")) {
18642
+ const rulesPath = join2(directoryPath, "rules");
18643
+ const ruleFiles = await import_fs_extra.readdir(rulesPath);
18644
+ counts.rules = ruleFiles.filter((file) => file.endsWith(".md")).length;
18645
+ } else if (items.includes("workflows")) {
18599
18646
  const workflowsPath = join2(directoryPath, "workflows");
18600
18647
  const workflowFiles = await import_fs_extra.readdir(workflowsPath);
18601
- counts.workflows = workflowFiles.filter((file) => file.endsWith(".md")).length;
18648
+ counts.rules = workflowFiles.filter((file) => file.endsWith(".md")).length;
18602
18649
  }
18603
18650
  if (items.includes("skills")) {
18604
18651
  const skillsPath = join2(directoryPath, "skills");
@@ -18639,12 +18686,12 @@ async function getClaudeKitSetup(projectDir = process.cwd()) {
18639
18686
  global: {
18640
18687
  path: "",
18641
18688
  metadata: null,
18642
- components: { agents: 0, commands: 0, workflows: 0, skills: 0 }
18689
+ components: { agents: 0, commands: 0, rules: 0, skills: 0 }
18643
18690
  },
18644
18691
  project: {
18645
18692
  path: "",
18646
18693
  metadata: null,
18647
- components: { agents: 0, commands: 0, workflows: 0, skills: 0 }
18694
+ components: { agents: 0, commands: 0, rules: 0, skills: 0 }
18648
18695
  }
18649
18696
  };
18650
18697
  const globalDir = getGlobalInstallDir();
@@ -19170,7 +19217,7 @@ function checkClaudeMdFile(path, name, id) {
19170
19217
  }
19171
19218
  }
19172
19219
  // src/domains/health-checks/checkers/active-plan-checker.ts
19173
- import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
19220
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
19174
19221
  import { join as join5 } from "node:path";
19175
19222
  function checkActivePlan(projectDir) {
19176
19223
  const activePlanPath = join5(projectDir, ".claude", "active-plan");
@@ -19186,7 +19233,7 @@ function checkActivePlan(projectDir) {
19186
19233
  };
19187
19234
  }
19188
19235
  try {
19189
- const targetPath = readFileSync3(activePlanPath, "utf-8").trim();
19236
+ const targetPath = readFileSync4(activePlanPath, "utf-8").trim();
19190
19237
  const fullPath = join5(projectDir, targetPath);
19191
19238
  if (!existsSync5(fullPath)) {
19192
19239
  return {
@@ -19267,16 +19314,16 @@ function checkComponentCounts(setup) {
19267
19314
  const project = setup.project.components;
19268
19315
  const totalAgents = global2.agents + project.agents;
19269
19316
  const totalCommands = global2.commands + project.commands;
19270
- const totalWorkflows = global2.workflows + project.workflows;
19317
+ const totalRules = global2.rules + project.rules;
19271
19318
  const totalSkills = global2.skills + project.skills;
19272
- const totalComponents = totalAgents + totalCommands + totalWorkflows + totalSkills;
19319
+ const totalComponents = totalAgents + totalCommands + totalRules + totalSkills;
19273
19320
  return {
19274
19321
  id: "ck-component-counts",
19275
19322
  name: "ClaudeKit Components",
19276
19323
  group: "claudekit",
19277
19324
  priority: "standard",
19278
19325
  status: totalComponents > 0 ? "info" : "warn",
19279
- message: totalComponents > 0 ? `${totalAgents} agents, ${totalCommands} commands, ${totalWorkflows} workflows, ${totalSkills} skills` : "No components found",
19326
+ message: totalComponents > 0 ? `${totalAgents} agents, ${totalCommands} commands, ${totalRules} rules, ${totalSkills} skills` : "No components found",
19280
19327
  suggestion: totalComponents === 0 ? "Install ClaudeKit: ck new --kit engineer" : undefined,
19281
19328
  autoFixable: false
19282
19329
  };
@@ -19637,7 +19684,7 @@ async function checkProjectConfigCompleteness(setup, projectDir) {
19637
19684
  };
19638
19685
  }
19639
19686
  const projectClaudeDir = join11(projectDir, ".claude");
19640
- const requiredDirs = ["agents", "commands", "workflows", "skills"];
19687
+ const requiredDirs = ["agents", "commands", "skills"];
19641
19688
  const missingDirs = [];
19642
19689
  for (const dir of requiredDirs) {
19643
19690
  const dirPath = join11(projectClaudeDir, dir);
@@ -19645,9 +19692,14 @@ async function checkProjectConfigCompleteness(setup, projectDir) {
19645
19692
  missingDirs.push(dir);
19646
19693
  }
19647
19694
  }
19695
+ const hasRulesOrWorkflows = existsSync10(join11(projectClaudeDir, "rules")) || existsSync10(join11(projectClaudeDir, "workflows"));
19696
+ if (!hasRulesOrWorkflows) {
19697
+ missingDirs.push("rules");
19698
+ }
19648
19699
  const files = await readdir3(projectClaudeDir).catch(() => []);
19649
19700
  const hasOnlyClaudeMd = files.length === 1 && files.includes("CLAUDE.md");
19650
- if (hasOnlyClaudeMd || missingDirs.length === requiredDirs.length) {
19701
+ const totalRequired = requiredDirs.length + 1;
19702
+ if (hasOnlyClaudeMd || missingDirs.length === totalRequired) {
19651
19703
  return {
19652
19704
  id: "ck-project-config-complete",
19653
19705
  name: "Project Config Completeness",
@@ -19655,7 +19707,7 @@ async function checkProjectConfigCompleteness(setup, projectDir) {
19655
19707
  priority: "standard",
19656
19708
  status: "fail",
19657
19709
  message: "Incomplete configuration",
19658
- details: "Only CLAUDE.md found - missing agents, commands, workflows, skills",
19710
+ details: "Only CLAUDE.md found - missing agents, commands, rules, skills",
19659
19711
  suggestion: "Run 'ck init' to install complete ClaudeKit in project",
19660
19712
  autoFixable: false
19661
19713
  };
@@ -21547,7 +21599,7 @@ class AutoHealer {
21547
21599
  }
21548
21600
  // src/domains/health-checks/report-generator.ts
21549
21601
  import { execSync as execSync4, spawnSync } from "node:child_process";
21550
- import { readFileSync as readFileSync4, unlinkSync, writeFileSync } from "node:fs";
21602
+ import { readFileSync as readFileSync5, unlinkSync, writeFileSync } from "node:fs";
21551
21603
  import { tmpdir as tmpdir2 } from "node:os";
21552
21604
  import { dirname as dirname2, join as join16 } from "node:path";
21553
21605
  import { fileURLToPath } from "node:url";
@@ -21558,7 +21610,7 @@ function getCliVersion() {
21558
21610
  try {
21559
21611
  const __dirname2 = dirname2(fileURLToPath(import.meta.url));
21560
21612
  const pkgPath = join16(__dirname2, "../../../package.json");
21561
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
21613
+ const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
21562
21614
  return pkg.version || "unknown";
21563
21615
  } catch (err) {
21564
21616
  logger.debug(`Failed to read CLI version: ${err}`);
@@ -24614,9 +24666,9 @@ async function promptDirectorySelection(global2 = false) {
24614
24666
  { key: "agents", label: "Agents", pattern: prefix ? `${prefix}/agents` : "agents" },
24615
24667
  { key: "commands", label: "Commands", pattern: prefix ? `${prefix}/commands` : "commands" },
24616
24668
  {
24617
- key: "workflows",
24618
- label: "Workflows",
24619
- pattern: prefix ? `${prefix}/workflows` : "workflows"
24669
+ key: "rules",
24670
+ label: "Rules",
24671
+ pattern: prefix ? `${prefix}/rules` : "rules"
24620
24672
  },
24621
24673
  { key: "skills", label: "Skills", pattern: prefix ? `${prefix}/skills` : "skills" },
24622
24674
  { key: "hooks", label: "Hooks", pattern: prefix ? `${prefix}/hooks` : "hooks" }
@@ -34425,7 +34477,7 @@ async function getUninstallManifest(claudeDir, kit) {
34425
34477
  const installedFiles = detection.metadata.installedFiles || [];
34426
34478
  const hasFiles = legacyFiles2.length > 0 || installedFiles.length > 0;
34427
34479
  if (!hasFiles) {
34428
- const legacyDirs2 = ["commands", "agents", "skills", "workflows", "hooks", "scripts"];
34480
+ const legacyDirs2 = ["commands", "agents", "skills", "rules", "workflows", "hooks", "scripts"];
34429
34481
  const legacyFileList = ["metadata.json"];
34430
34482
  return {
34431
34483
  filesToRemove: [...legacyDirs2, ...legacyFileList],
@@ -34443,7 +34495,7 @@ async function getUninstallManifest(claudeDir, kit) {
34443
34495
  remainingKits: []
34444
34496
  };
34445
34497
  }
34446
- const legacyDirs = ["commands", "agents", "skills", "workflows", "hooks", "scripts"];
34498
+ const legacyDirs = ["commands", "agents", "skills", "rules", "workflows", "hooks", "scripts"];
34447
34499
  const legacyFiles = ["metadata.json"];
34448
34500
  return {
34449
34501
  filesToRemove: [...legacyDirs, ...legacyFiles],
@@ -40314,11 +40366,110 @@ async function detectAccessibleKits() {
40314
40366
  return accessible;
40315
40367
  }
40316
40368
 
40369
+ // src/domains/github/preflight-checker.ts
40370
+ init_logger();
40371
+ import { exec as exec7 } from "node:child_process";
40372
+ import { promisify as promisify7 } from "node:util";
40373
+ var execAsync7 = promisify7(exec7);
40374
+ function createSuccessfulPreflightResult() {
40375
+ return {
40376
+ success: true,
40377
+ ghInstalled: true,
40378
+ ghVersion: MIN_GH_CLI_VERSION,
40379
+ ghVersionOk: true,
40380
+ ghAuthenticated: true,
40381
+ errorLines: []
40382
+ };
40383
+ }
40384
+ function isTimeoutError(error) {
40385
+ if (error instanceof Error) {
40386
+ const msg = error.message.toLowerCase();
40387
+ return msg.includes("timeout") || msg.includes("timed out") || msg.includes("etimedout");
40388
+ }
40389
+ return false;
40390
+ }
40391
+ async function runPreflightChecks() {
40392
+ logger.debug("Running GitHub CLI pre-flight checks");
40393
+ if (shouldSkipExpensiveOperations()) {
40394
+ logger.debug("Skipping preflight checks in test/CI environment");
40395
+ return createSuccessfulPreflightResult();
40396
+ }
40397
+ const result = {
40398
+ success: false,
40399
+ ghInstalled: false,
40400
+ ghVersion: null,
40401
+ ghVersionOk: false,
40402
+ ghAuthenticated: false,
40403
+ errorLines: []
40404
+ };
40405
+ try {
40406
+ const { stdout: stdout2 } = await execAsync7("gh --version", { timeout: GH_COMMAND_TIMEOUT_MS });
40407
+ const match2 = stdout2.match(/(\d+\.\d+\.\d+)/);
40408
+ if (!match2) {
40409
+ logger.debug(`GitHub CLI version not detected from output: ${stdout2.trim()}`);
40410
+ result.ghInstalled = true;
40411
+ result.errorLines.push("✗ GitHub CLI installed but version could not be detected");
40412
+ result.errorLines.push(` Output: ${stdout2.trim().slice(0, 100)}`);
40413
+ result.errorLines.push(" Try running: gh --version");
40414
+ return result;
40415
+ }
40416
+ result.ghVersion = match2[1];
40417
+ result.ghInstalled = true;
40418
+ logger.debug(`GitHub CLI detected: v${result.ghVersion}`);
40419
+ } catch (error) {
40420
+ if (isTimeoutError(error)) {
40421
+ logger.debug("GitHub CLI check timed out");
40422
+ result.errorLines.push("✗ GitHub CLI check timed out");
40423
+ result.errorLines.push(" This may indicate a slow system or network issue");
40424
+ result.errorLines.push(" Try running: gh --version");
40425
+ } else {
40426
+ logger.debug(`GitHub CLI not found: ${error instanceof Error ? error.message : "unknown error"}`);
40427
+ result.errorLines.push("✗ GitHub CLI not installed");
40428
+ result.errorLines.push(" Install from: https://cli.github.com");
40429
+ result.errorLines.push("");
40430
+ result.errorLines.push("After install: gh auth login -h github.com");
40431
+ }
40432
+ return result;
40433
+ }
40434
+ if (result.ghVersion) {
40435
+ const comparison = compareVersions(result.ghVersion, MIN_GH_CLI_VERSION);
40436
+ result.ghVersionOk = comparison >= 0;
40437
+ if (!result.ghVersionOk) {
40438
+ logger.debug(`GitHub CLI version ${result.ghVersion} is below minimum ${MIN_GH_CLI_VERSION}`);
40439
+ result.errorLines.push(...getGhUpgradeInstructions(result.ghVersion));
40440
+ return result;
40441
+ }
40442
+ }
40443
+ try {
40444
+ await execAsync7("gh auth status -h github.com", {
40445
+ timeout: GH_COMMAND_TIMEOUT_MS,
40446
+ env: { ...process.env, GH_NO_UPDATE_NOTIFIER: "1" }
40447
+ });
40448
+ result.ghAuthenticated = true;
40449
+ logger.debug("GitHub CLI authenticated for github.com");
40450
+ } catch (error) {
40451
+ if (isTimeoutError(error)) {
40452
+ logger.debug("GitHub CLI auth check timed out");
40453
+ result.errorLines.push("✗ GitHub CLI auth check timed out");
40454
+ result.errorLines.push(" This may indicate a network issue");
40455
+ result.errorLines.push(" Try running: gh auth status -h github.com");
40456
+ } else {
40457
+ logger.debug(`GitHub CLI not authenticated: ${error instanceof Error ? error.message : "unknown error"}`);
40458
+ result.errorLines.push("✗ GitHub CLI not authenticated");
40459
+ result.errorLines.push(" Run: gh auth login -h github.com");
40460
+ }
40461
+ return result;
40462
+ }
40463
+ result.success = true;
40464
+ logger.debug("All GitHub CLI pre-flight checks passed");
40465
+ return result;
40466
+ }
40467
+
40317
40468
  // src/domains/installation/fresh-installer.ts
40318
40469
  init_logger();
40319
40470
  import { join as join65 } from "node:path";
40320
40471
  var import_fs_extra29 = __toESM(require_lib(), 1);
40321
- var CLAUDEKIT_SUBDIRECTORIES = ["commands", "agents", "skills", "workflows", "hooks"];
40472
+ var CLAUDEKIT_SUBDIRECTORIES = ["commands", "agents", "skills", "rules", "hooks"];
40322
40473
  async function handleFreshInstallation(claudeDir, prompts) {
40323
40474
  if (!await import_fs_extra29.pathExists(claudeDir)) {
40324
40475
  logger.info(".claude directory does not exist, proceeding with fresh installation");
@@ -40378,10 +40529,25 @@ async function handleSelection(ctx) {
40378
40529
  const config = await ConfigManager.get();
40379
40530
  let accessibleKits;
40380
40531
  if (!ctx.options.useGit && !ctx.options.kitPath && !ctx.options.archive) {
40532
+ const preflight = await runPreflightChecks();
40533
+ if (!preflight.success) {
40534
+ for (const line of preflight.errorLines) {
40535
+ if (line.startsWith("✗")) {
40536
+ logger.error(line);
40537
+ } else {
40538
+ logger.info(line);
40539
+ }
40540
+ }
40541
+ logger.info("");
40542
+ logger.info("Full diagnostics: ck doctor");
40543
+ return { ...ctx, cancelled: true };
40544
+ }
40381
40545
  accessibleKits = await detectAccessibleKits();
40382
40546
  if (accessibleKits.length === 0) {
40383
- logger.error("No ClaudeKit access found.");
40384
- logger.info("Purchase at https://claudekit.cc");
40547
+ logger.error("No ClaudeKit repository access found.");
40548
+ logger.info("Check email for GitHub invitation, or purchase at https://claudekit.cc");
40549
+ logger.info("");
40550
+ logger.info("Full diagnostics: ck doctor");
40385
40551
  return { ...ctx, cancelled: true };
40386
40552
  }
40387
40553
  }
@@ -42401,9 +42567,9 @@ ${import_picocolors22.default.yellow("User modifications will be permanently del
42401
42567
  }
42402
42568
  }
42403
42569
  // src/commands/update-cli.ts
42404
- import { exec as exec7 } from "node:child_process";
42570
+ import { exec as exec8 } from "node:child_process";
42405
42571
  import { join as join75 } from "node:path";
42406
- import { promisify as promisify7 } from "node:util";
42572
+ import { promisify as promisify8 } from "node:util";
42407
42573
 
42408
42574
  // src/domains/github/npm-registry.ts
42409
42575
  init_logger();
@@ -42549,7 +42715,7 @@ var import_fs_extra36 = __toESM(require_lib(), 1);
42549
42715
  // package.json
42550
42716
  var package_default = {
42551
42717
  name: "claudekit-cli",
42552
- version: "3.25.0",
42718
+ version: "3.26.1",
42553
42719
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
42554
42720
  type: "module",
42555
42721
  repository: {
@@ -42640,7 +42806,7 @@ var package_default = {
42640
42806
  };
42641
42807
 
42642
42808
  // src/commands/update-cli.ts
42643
- var execAsync7 = promisify7(exec7);
42809
+ var execAsync8 = promisify8(exec8);
42644
42810
 
42645
42811
  class CliUpdateError extends ClaudeKitError {
42646
42812
  constructor(message) {
@@ -42660,6 +42826,11 @@ function buildInitCommand(isGlobal, kit, beta) {
42660
42826
  parts.push("--beta");
42661
42827
  return parts.join(" ");
42662
42828
  }
42829
+ function isBetaVersion(version) {
42830
+ if (!version)
42831
+ return false;
42832
+ return /-(beta|alpha|rc)[.\d]/i.test(version);
42833
+ }
42663
42834
  function selectKitForUpdate(params) {
42664
42835
  const { hasLocal, hasGlobal, localKits, globalKits } = params;
42665
42836
  const hasLocalKit = localKits.length > 0 || hasLocal;
@@ -42723,7 +42894,9 @@ async function promptKitUpdate(beta) {
42723
42894
  logger.verbose("No ClaudeKit installations detected, skipping kit update prompt");
42724
42895
  return;
42725
42896
  }
42726
- const initCmd = buildInitCommand(selection.isGlobal, selection.kit, beta);
42897
+ const kitVersion = selection.kit ? selection.isGlobal ? globalMetadata?.kits?.[selection.kit]?.version : localMetadata?.kits?.[selection.kit]?.version : undefined;
42898
+ const isBetaInstalled = isBetaVersion(kitVersion);
42899
+ const initCmd = buildInitCommand(selection.isGlobal, selection.kit, beta || isBetaInstalled);
42727
42900
  const promptMessage = selection.promptMessage;
42728
42901
  logger.info("");
42729
42902
  const shouldUpdate = await se({
@@ -42737,7 +42910,7 @@ async function promptKitUpdate(beta) {
42737
42910
  const s = de();
42738
42911
  s.start("Updating ClaudeKit content...");
42739
42912
  try {
42740
- await execAsync7(initCmd, {
42913
+ await execAsync8(initCmd, {
42741
42914
  timeout: 300000
42742
42915
  });
42743
42916
  s.stop("Kit content updated");
@@ -42827,7 +43000,7 @@ Run 'ck update' to install`, "Update Check");
42827
43000
  logger.info(`Running: ${updateCmd}`);
42828
43001
  s.start("Updating CLI...");
42829
43002
  try {
42830
- await execAsync7(updateCmd, {
43003
+ await execAsync8(updateCmd, {
42831
43004
  timeout: 120000
42832
43005
  });
42833
43006
  s.stop("Update completed");
@@ -42845,7 +43018,7 @@ Manual update: ${updateCmd}`);
42845
43018
  }
42846
43019
  s.start("Verifying installation...");
42847
43020
  try {
42848
- const { stdout: stdout2 } = await execAsync7("ck --version", { timeout: 5000 });
43021
+ const { stdout: stdout2 } = await execAsync8("ck --version", { timeout: 5000 });
42849
43022
  const newVersionMatch = stdout2.match(/CLI Version:\s*(\S+)/);
42850
43023
  const newVersion = newVersionMatch ? newVersionMatch[1] : targetVersion;
42851
43024
  s.stop(`Installed version: ${newVersion}`);
@@ -43020,7 +43193,7 @@ function registerCommands(cli) {
43020
43193
  }
43021
43194
 
43022
43195
  // src/cli/version-display.ts
43023
- import { existsSync as existsSync20, readFileSync as readFileSync6 } from "node:fs";
43196
+ import { existsSync as existsSync20, readFileSync as readFileSync7 } from "node:fs";
43024
43197
  import { join as join77 } from "node:path";
43025
43198
 
43026
43199
  // src/domains/versioning/checking/version-utils.ts
@@ -43327,7 +43500,7 @@ async function displayVersion() {
43327
43500
  const isLocalSameAsGlobal = localMetadataPath === globalMetadataPath;
43328
43501
  if (!isLocalSameAsGlobal && existsSync20(localMetadataPath)) {
43329
43502
  try {
43330
- const rawMetadata = JSON.parse(readFileSync6(localMetadataPath, "utf-8"));
43503
+ const rawMetadata = JSON.parse(readFileSync7(localMetadataPath, "utf-8"));
43331
43504
  const metadata = MetadataSchema.parse(rawMetadata);
43332
43505
  const kitsDisplay = formatInstalledKits(metadata);
43333
43506
  if (kitsDisplay) {
@@ -43341,7 +43514,7 @@ async function displayVersion() {
43341
43514
  }
43342
43515
  if (existsSync20(globalMetadataPath)) {
43343
43516
  try {
43344
- const rawMetadata = JSON.parse(readFileSync6(globalMetadataPath, "utf-8"));
43517
+ const rawMetadata = JSON.parse(readFileSync7(globalMetadataPath, "utf-8"));
43345
43518
  const metadata = MetadataSchema.parse(rawMetadata);
43346
43519
  const kitsDisplay = formatInstalledKits(metadata);
43347
43520
  if (kitsDisplay) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudekit-cli",
3
- "version": "3.25.0",
3
+ "version": "3.26.1",
4
4
  "description": "CLI tool for bootstrapping and updating ClaudeKit projects",
5
5
  "type": "module",
6
6
  "repository": {