claudekit-cli 4.3.1-dev.10 → 4.3.1-dev.12

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.
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("node:fs");
5
+ const os = require("node:os");
6
+ const path = require("node:path");
7
+
8
+ const MAX_SETTINGS_BYTES = 5 * 1024 * 1024;
9
+
10
+ function isLegacyDescriptiveNamePrompt(entry) {
11
+ if (!entry || entry.type !== "prompt" || typeof entry.prompt !== "string") return false;
12
+
13
+ const prompt = entry.prompt;
14
+ const lowerPrompt = prompt.toLowerCase();
15
+ const isOriginalLegacyPrompt =
16
+ prompt.includes("Use kebab-case file naming") &&
17
+ prompt.includes("self-documenting") &&
18
+ prompt.includes("Grep, Glob, Search");
19
+ const isDescriptiveNameKebabPrompt =
20
+ lowerPrompt.includes("descriptive-name") &&
21
+ lowerPrompt.includes("kebab-case") &&
22
+ (lowerPrompt.includes("file") || lowerPrompt.includes("filename"));
23
+
24
+ return isOriginalLegacyPrompt || isDescriptiveNameKebabPrompt;
25
+ }
26
+
27
+ function pruneHooks(settings) {
28
+ if (!settings || typeof settings !== "object" || !settings.hooks) return 0;
29
+
30
+ let pruned = 0;
31
+ for (const [eventName, entries] of Object.entries(settings.hooks)) {
32
+ if (!Array.isArray(entries)) continue;
33
+
34
+ const keptEntries = [];
35
+ for (const entry of entries) {
36
+ if (isLegacyDescriptiveNamePrompt(entry)) {
37
+ pruned++;
38
+ continue;
39
+ }
40
+
41
+ if (Array.isArray(entry?.hooks)) {
42
+ const keptHooks = entry.hooks.filter((hook) => {
43
+ if (isLegacyDescriptiveNamePrompt(hook)) {
44
+ pruned++;
45
+ return false;
46
+ }
47
+ return true;
48
+ });
49
+ if (keptHooks.length > 0) keptEntries.push({ ...entry, hooks: keptHooks });
50
+ continue;
51
+ }
52
+
53
+ keptEntries.push(entry);
54
+ }
55
+
56
+ if (keptEntries.length === 0) {
57
+ delete settings.hooks[eventName];
58
+ } else {
59
+ settings.hooks[eventName] = keptEntries;
60
+ }
61
+ }
62
+
63
+ if (Object.keys(settings.hooks).length === 0) settings.hooks = undefined;
64
+ return pruned;
65
+ }
66
+
67
+ function pruneSettingsFile(filePath) {
68
+ try {
69
+ if (!filePath || !fs.existsSync(filePath)) return 0;
70
+ const stat = fs.statSync(filePath);
71
+ if (!stat.isFile() || stat.size > MAX_SETTINGS_BYTES) return 0;
72
+
73
+ const settings = JSON.parse(fs.readFileSync(filePath, "utf8"));
74
+ const pruned = pruneHooks(settings);
75
+ if (pruned > 0) {
76
+ fs.writeFileSync(filePath, `${JSON.stringify(settings, null, 2)}\n`);
77
+ }
78
+ return pruned;
79
+ } catch {
80
+ return 0;
81
+ }
82
+ }
83
+
84
+ function getHomeDir() {
85
+ return process.env.CK_TEST_HOME || os.homedir();
86
+ }
87
+
88
+ function addIfSettingsFile(files, filePath) {
89
+ if (!filePath) return;
90
+ const base = path.basename(filePath);
91
+ if (
92
+ base === "settings.json" ||
93
+ base === "settings.local.json" ||
94
+ base.endsWith(".settings.json")
95
+ ) {
96
+ files.add(path.resolve(filePath));
97
+ }
98
+ }
99
+
100
+ function collectCandidateSettingsFiles() {
101
+ const files = new Set();
102
+ const initCwd = process.env.INIT_CWD;
103
+ if (initCwd && path.isAbsolute(initCwd)) {
104
+ addIfSettingsFile(files, path.join(initCwd, ".claude", "settings.json"));
105
+ addIfSettingsFile(files, path.join(initCwd, ".claude", "settings.local.json"));
106
+ }
107
+
108
+ const homeDir = getHomeDir();
109
+ const globalClaudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(homeDir, ".claude");
110
+ addIfSettingsFile(files, path.join(globalClaudeDir, "settings.json"));
111
+ addIfSettingsFile(files, path.join(globalClaudeDir, "settings.local.json"));
112
+
113
+ const ccsDir = process.env.CK_TEST_CCS_DIR || path.join(homeDir, ".ccs");
114
+ try {
115
+ for (const dirent of fs.readdirSync(ccsDir, { withFileTypes: true })) {
116
+ if (dirent.isFile()) addIfSettingsFile(files, path.join(ccsDir, dirent.name));
117
+ }
118
+ } catch {
119
+ // Optional compatibility directory; skip when absent or unreadable.
120
+ }
121
+
122
+ return [...files];
123
+ }
124
+
125
+ function main() {
126
+ let pruned = 0;
127
+ for (const filePath of collectCandidateSettingsFiles()) {
128
+ pruned += pruneSettingsFile(filePath);
129
+ }
130
+
131
+ if (pruned > 0 && process.env.CK_POSTINSTALL_DEBUG === "1") {
132
+ console.warn(`[claudekit-cli] Pruned ${pruned} legacy hook prompt(s)`);
133
+ }
134
+ }
135
+
136
+ main();
137
+
138
+ module.exports = {
139
+ collectCandidateSettingsFiles,
140
+ isLegacyDescriptiveNamePrompt,
141
+ pruneHooks,
142
+ pruneSettingsFile,
143
+ };
package/cli-manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "4.3.1-dev.10",
3
- "generatedAt": "2026-05-21T01:08:27.917Z",
2
+ "version": "4.3.1-dev.12",
3
+ "generatedAt": "2026-05-21T01:53:05.956Z",
4
4
  "commands": {
5
5
  "agents": {
6
6
  "name": "agents",
package/dist/index.js CHANGED
@@ -63357,7 +63357,7 @@ var package_default;
63357
63357
  var init_package = __esm(() => {
63358
63358
  package_default = {
63359
63359
  name: "claudekit-cli",
63360
- version: "4.3.1-dev.10",
63360
+ version: "4.3.1-dev.12",
63361
63361
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
63362
63362
  type: "module",
63363
63363
  repository: {
@@ -63373,6 +63373,7 @@ var init_package = __esm(() => {
63373
63373
  },
63374
63374
  files: [
63375
63375
  "bin/ck.js",
63376
+ "bin/postinstall-self-heal.cjs",
63376
63377
  "dist/index.js",
63377
63378
  "dist/ui/",
63378
63379
  "cli-manifest.json"
@@ -63404,6 +63405,7 @@ var init_package = __esm(() => {
63404
63405
  "ci:local": "bash scripts/ci-local.sh",
63405
63406
  "install:hooks": "./.githooks/install.sh",
63406
63407
  prepare: `node -e "try{require('child_process').execSync('git rev-parse --git-dir',{stdio:'ignore'});require('child_process').execSync('bash .githooks/install.sh',{stdio:'inherit'})}catch(e){console.warn('[i] Hook install skipped:',e.message)}"`,
63408
+ postinstall: "node bin/postinstall-self-heal.cjs",
63407
63409
  "help:check-parity": "bun run scripts/check-help-parity.ts",
63408
63410
  "manifest:generate": "bun run scripts/generate-cli-manifest.ts",
63409
63411
  "docs:generate": "bun run scripts/generate-cli-reference.ts",
@@ -63903,7 +63905,10 @@ function isLegacyDescriptiveNamePrompt(entry) {
63903
63905
  const prompt = entry.prompt;
63904
63906
  if (entry.type !== "prompt" || typeof prompt !== "string")
63905
63907
  return false;
63906
- return prompt.includes("Use kebab-case file naming") && prompt.includes("self-documenting") && prompt.includes("Grep, Glob, Search");
63908
+ const lowerPrompt = prompt.toLowerCase();
63909
+ const isOriginalLegacyPrompt = prompt.includes("Use kebab-case file naming") && prompt.includes("self-documenting") && prompt.includes("Grep, Glob, Search");
63910
+ const isDescriptiveNameKebabPrompt = lowerPrompt.includes("descriptive-name") && lowerPrompt.includes("kebab-case") && (lowerPrompt.includes("file") || lowerPrompt.includes("filename"));
63911
+ return isOriginalLegacyPrompt || isDescriptiveNameKebabPrompt;
63907
63912
  }
63908
63913
  function extractHookFilePath(command, hookDir) {
63909
63914
  if (!command)
@@ -63986,7 +63991,7 @@ var init_shared2 = __esm(() => {
63986
63991
 
63987
63992
  // src/domains/health-checks/checkers/hook-health-checker.ts
63988
63993
  import { spawnSync as spawnSync3 } from "node:child_process";
63989
- import { existsSync as existsSync45, readFileSync as readFileSync12, statSync as statSync9, writeFileSync as writeFileSync5 } from "node:fs";
63994
+ import { existsSync as existsSync45, readFileSync as readFileSync12, readdirSync as readdirSync8, statSync as statSync9, writeFileSync as writeFileSync5 } from "node:fs";
63990
63995
  import { readdir as readdir17 } from "node:fs/promises";
63991
63996
  import { homedir as homedir41, tmpdir } from "node:os";
63992
63997
  import { join as join64, resolve as resolve33 } from "node:path";
@@ -64023,6 +64028,7 @@ function getCanonicalGlobalCommandRoot() {
64023
64028
  }
64024
64029
  function getClaudeSettingsFiles(projectDir) {
64025
64030
  const globalClaudeDir = PathResolver.getGlobalKitDir();
64031
+ const ccsSettingsDir = join64(process.env.CK_TEST_HOME ?? homedir41(), ".ccs");
64026
64032
  const candidates = [
64027
64033
  {
64028
64034
  path: resolve33(projectDir, ".claude", "settings.json"),
@@ -64045,6 +64051,17 @@ function getClaudeSettingsFiles(projectDir) {
64045
64051
  root: getCanonicalGlobalCommandRoot()
64046
64052
  }
64047
64053
  ];
64054
+ try {
64055
+ for (const dirent of readdirSync8(ccsSettingsDir, { withFileTypes: true })) {
64056
+ if (!dirent.isFile() || !dirent.name.endsWith(".settings.json"))
64057
+ continue;
64058
+ candidates.push({
64059
+ path: resolve33(ccsSettingsDir, dirent.name),
64060
+ label: `.ccs/${dirent.name}`,
64061
+ root: "$HOME"
64062
+ });
64063
+ }
64064
+ } catch {}
64048
64065
  return candidates.filter((candidate) => existsSync45(candidate.path));
64049
64066
  }
64050
64067
  function isAlreadyCanonical(cmd) {
@@ -76416,7 +76433,7 @@ var init_content_validator = __esm(() => {
76416
76433
 
76417
76434
  // src/commands/content/phases/context-cache-manager.ts
76418
76435
  import { createHash as createHash9 } from "node:crypto";
76419
- import { existsSync as existsSync79, mkdirSync as mkdirSync5, readFileSync as readFileSync18, readdirSync as readdirSync12, statSync as statSync14 } from "node:fs";
76436
+ import { existsSync as existsSync79, mkdirSync as mkdirSync5, readFileSync as readFileSync18, readdirSync as readdirSync13, statSync as statSync14 } from "node:fs";
76420
76437
  import { rename as rename16, writeFile as writeFile40 } from "node:fs/promises";
76421
76438
  import { homedir as homedir55 } from "node:os";
76422
76439
  import { basename as basename34, join as join160 } from "node:path";
@@ -76465,7 +76482,7 @@ function getDocSourcePaths(repoPath) {
76465
76482
  const docsDir = join160(repoPath, "docs");
76466
76483
  if (existsSync79(docsDir)) {
76467
76484
  try {
76468
- const files = readdirSync12(docsDir);
76485
+ const files = readdirSync13(docsDir);
76469
76486
  for (const f3 of files) {
76470
76487
  if (f3.endsWith(".md"))
76471
76488
  paths.push(join160(docsDir, f3));
@@ -76478,7 +76495,7 @@ function getDocSourcePaths(repoPath) {
76478
76495
  const stylesDir = join160(repoPath, "assets", "writing-styles");
76479
76496
  if (existsSync79(stylesDir)) {
76480
76497
  try {
76481
- const files = readdirSync12(stylesDir);
76498
+ const files = readdirSync13(stylesDir);
76482
76499
  for (const f3 of files) {
76483
76500
  paths.push(join160(stylesDir, f3));
76484
76501
  }
@@ -76673,7 +76690,7 @@ function extractContentFromResponse(response) {
76673
76690
 
76674
76691
  // src/commands/content/phases/docs-summarizer.ts
76675
76692
  import { execSync as execSync7 } from "node:child_process";
76676
- import { existsSync as existsSync80, readFileSync as readFileSync19, readdirSync as readdirSync13 } from "node:fs";
76693
+ import { existsSync as existsSync80, readFileSync as readFileSync19, readdirSync as readdirSync14 } from "node:fs";
76677
76694
  import { join as join161 } from "node:path";
76678
76695
  async function summarizeProjectDocs(repoPath, contentLogger) {
76679
76696
  const rawContent = collectRawDocs(repoPath);
@@ -76731,7 +76748,7 @@ function collectRawDocs(repoPath) {
76731
76748
  const docsDir = join161(repoPath, "docs");
76732
76749
  if (existsSync80(docsDir)) {
76733
76750
  try {
76734
- const files = readdirSync13(docsDir).filter((f3) => f3.endsWith(".md")).sort();
76751
+ const files = readdirSync14(docsDir).filter((f3) => f3.endsWith(".md")).sort();
76735
76752
  for (const f3 of files) {
76736
76753
  const content = readCapped(join161(docsDir, f3), 5000);
76737
76754
  if (content) {
@@ -76755,7 +76772,7 @@ ${content}`);
76755
76772
  const stylesDir = join161(repoPath, "assets", "writing-styles");
76756
76773
  if (existsSync80(stylesDir)) {
76757
76774
  try {
76758
- const files = readdirSync13(stylesDir).slice(0, 3);
76775
+ const files = readdirSync14(stylesDir).slice(0, 3);
76759
76776
  styles3 = files.map((f3) => readCapped(join161(stylesDir, f3), 1000)).filter(Boolean).join(`
76760
76777
 
76761
76778
  `);
@@ -76946,7 +76963,7 @@ IMPORTANT: Generate the image and output the path as JSON: {"imagePath": "/path/
76946
76963
 
76947
76964
  // src/commands/content/phases/photo-generator.ts
76948
76965
  import { execSync as execSync8 } from "node:child_process";
76949
- import { existsSync as existsSync81, mkdirSync as mkdirSync6, readdirSync as readdirSync14 } from "node:fs";
76966
+ import { existsSync as existsSync81, mkdirSync as mkdirSync6, readdirSync as readdirSync15 } from "node:fs";
76950
76967
  import { homedir as homedir56 } from "node:os";
76951
76968
  import { join as join162 } from "node:path";
76952
76969
  async function generatePhoto(_content, context, config, platform18, contentId, contentLogger) {
@@ -76971,7 +76988,7 @@ async function generatePhoto(_content, context, config, platform18, contentId, c
76971
76988
  return { path: imagePath, ...dimensions, format: "png" };
76972
76989
  }
76973
76990
  }
76974
- const files = readdirSync14(mediaDir);
76991
+ const files = readdirSync15(mediaDir);
76975
76992
  const imageFile = files.find((f3) => /\.(png|jpg|jpeg|webp)$/i.test(f3));
76976
76993
  if (imageFile) {
76977
76994
  const ext2 = imageFile.split(".").pop() ?? "png";
@@ -77358,7 +77375,7 @@ function isNoiseCommit(title, author) {
77358
77375
 
77359
77376
  // src/commands/content/phases/change-detector.ts
77360
77377
  import { execSync as execSync10, spawnSync as spawnSync9 } from "node:child_process";
77361
- import { existsSync as existsSync84, readFileSync as readFileSync20, readdirSync as readdirSync15, statSync as statSync16 } from "node:fs";
77378
+ import { existsSync as existsSync84, readFileSync as readFileSync20, readdirSync as readdirSync16, statSync as statSync16 } from "node:fs";
77362
77379
  import { join as join164 } from "node:path";
77363
77380
  function detectCommits(repo, since) {
77364
77381
  try {
@@ -77474,7 +77491,7 @@ function detectCompletedPlans(repo, since) {
77474
77491
  const sinceMs = new Date(since).getTime();
77475
77492
  const events = [];
77476
77493
  try {
77477
- const entries = readdirSync15(plansDir, { withFileTypes: true });
77494
+ const entries = readdirSync16(plansDir, { withFileTypes: true });
77478
77495
  for (const entry of entries) {
77479
77496
  if (!entry.isDirectory())
77480
77497
  continue;
@@ -77555,7 +77572,7 @@ function classifyCommit(event) {
77555
77572
 
77556
77573
  // src/commands/content/phases/repo-discoverer.ts
77557
77574
  import { execSync as execSync11 } from "node:child_process";
77558
- import { readdirSync as readdirSync16 } from "node:fs";
77575
+ import { readdirSync as readdirSync17 } from "node:fs";
77559
77576
  import { join as join165 } from "node:path";
77560
77577
  function discoverRepos2(cwd2) {
77561
77578
  const repos = [];
@@ -77565,7 +77582,7 @@ function discoverRepos2(cwd2) {
77565
77582
  repos.push(info);
77566
77583
  }
77567
77584
  try {
77568
- const entries = readdirSync16(cwd2, { withFileTypes: true });
77585
+ const entries = readdirSync17(cwd2, { withFileTypes: true });
77569
77586
  for (const entry of entries) {
77570
77587
  if (!entry.isDirectory() || entry.name.startsWith("."))
77571
77588
  continue;
@@ -100779,7 +100796,7 @@ async function handleDownload(ctx) {
100779
100796
  import { join as join120 } from "node:path";
100780
100797
 
100781
100798
  // src/domains/installation/deletion-handler.ts
100782
- import { existsSync as existsSync65, lstatSync as lstatSync3, readdirSync as readdirSync8, rmSync as rmSync2, rmdirSync, unlinkSync as unlinkSync4 } from "node:fs";
100799
+ import { existsSync as existsSync65, lstatSync as lstatSync3, readdirSync as readdirSync9, rmSync as rmSync2, rmdirSync, unlinkSync as unlinkSync4 } from "node:fs";
100783
100800
  import { dirname as dirname35, join as join106, relative as relative21, resolve as resolve41, sep as sep12 } from "node:path";
100784
100801
 
100785
100802
  // src/services/file-operations/manifest/manifest-reader.ts
@@ -100973,7 +100990,7 @@ function collectFilesRecursively(dir, baseDir) {
100973
100990
  if (!existsSync65(dir))
100974
100991
  return results;
100975
100992
  try {
100976
- const entries = readdirSync8(dir, { withFileTypes: true });
100993
+ const entries = readdirSync9(dir, { withFileTypes: true });
100977
100994
  for (const entry of entries) {
100978
100995
  const fullPath = join106(dir, entry.name);
100979
100996
  const relativePath = relative21(baseDir, fullPath);
@@ -101011,7 +101028,7 @@ function cleanupEmptyDirectories(filePath, claudeDir3) {
101011
101028
  while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir) && iterations < MAX_CLEANUP_ITERATIONS) {
101012
101029
  iterations++;
101013
101030
  try {
101014
- const entries = readdirSync8(currentDir);
101031
+ const entries = readdirSync9(currentDir);
101015
101032
  if (entries.length === 0) {
101016
101033
  rmdirSync(currentDir);
101017
101034
  logger.debug(`Removed empty directory: ${currentDir}`);
@@ -106747,7 +106764,7 @@ async function runPreflightChecks() {
106747
106764
 
106748
106765
  // src/domains/installation/fresh-installer.ts
106749
106766
  init_metadata_migration();
106750
- import { existsSync as existsSync67, readdirSync as readdirSync9, rmSync as rmSync3, rmdirSync as rmdirSync2, unlinkSync as unlinkSync5 } from "node:fs";
106767
+ import { existsSync as existsSync67, readdirSync as readdirSync10, rmSync as rmSync3, rmdirSync as rmdirSync2, unlinkSync as unlinkSync5 } from "node:fs";
106751
106768
  import { basename as basename28, dirname as dirname39, join as join132, resolve as resolve45 } from "node:path";
106752
106769
  init_logger();
106753
106770
  init_safe_spinner();
@@ -106804,7 +106821,7 @@ function cleanupEmptyDirectories2(filePath, claudeDir3) {
106804
106821
  let currentDir = resolve45(dirname39(filePath));
106805
106822
  while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir)) {
106806
106823
  try {
106807
- const entries = readdirSync9(currentDir);
106824
+ const entries = readdirSync10(currentDir);
106808
106825
  if (entries.length === 0) {
106809
106826
  rmdirSync2(currentDir);
106810
106827
  logger.debug(`Removed empty directory: ${currentDir}`);
@@ -108487,6 +108504,19 @@ async function repairMissingHookFileReferencesAfterInit(ctx) {
108487
108504
  logger.debug(`Hook file reference repair skipped after init: ${error instanceof Error ? error.message : "unknown"}`);
108488
108505
  }
108489
108506
  }
108507
+ async function repairLegacyHookPromptsAfterInit(ctx) {
108508
+ if (!ctx.resolvedDir)
108509
+ return;
108510
+ const projectDir = ctx.options.global ? process.cwd() : ctx.resolvedDir;
108511
+ try {
108512
+ const repaired = await repairLegacyHookPrompts(projectDir);
108513
+ if (repaired > 0) {
108514
+ logger.info(`Pruned ${repaired} legacy hook prompt(s)`);
108515
+ }
108516
+ } catch (error) {
108517
+ logger.debug(`Legacy hook prompt repair skipped after init: ${error instanceof Error ? error.message : "unknown"}`);
108518
+ }
108519
+ }
108490
108520
  async function executeInit(options2, prompts) {
108491
108521
  let ctx = createInitContext(options2, prompts);
108492
108522
  ctx = await resolveOptions(ctx);
@@ -108560,6 +108590,7 @@ async function executeInit(options2, prompts) {
108560
108590
  Installed ${installedKits.length} kits: ${installedKits.map((k2) => AVAILABLE_KITS[k2].name).join(", ")}`);
108561
108591
  }
108562
108592
  if (!isSyncMode) {
108593
+ await repairLegacyHookPromptsAfterInit(ctx);
108563
108594
  await repairMissingHookFileReferencesAfterInit(ctx);
108564
108595
  }
108565
108596
  prompts.outro(`Project initialized successfully at ${ctx.resolvedDir}`);
@@ -113348,7 +113379,7 @@ async function detectInstallations() {
113348
113379
  }
113349
113380
 
113350
113381
  // src/commands/uninstall/removal-handler.ts
113351
- import { readdirSync as readdirSync11, rmSync as rmSync5 } from "node:fs";
113382
+ import { readdirSync as readdirSync12, rmSync as rmSync5 } from "node:fs";
113352
113383
  import { basename as basename33, join as join152, resolve as resolve58, sep as sep14 } from "node:path";
113353
113384
  init_logger();
113354
113385
  init_safe_prompts();
@@ -113357,7 +113388,7 @@ var import_fs_extra42 = __toESM(require_lib(), 1);
113357
113388
 
113358
113389
  // src/commands/uninstall/analysis-handler.ts
113359
113390
  init_metadata_migration();
113360
- import { readdirSync as readdirSync10, rmSync as rmSync4 } from "node:fs";
113391
+ import { readdirSync as readdirSync11, rmSync as rmSync4 } from "node:fs";
113361
113392
  import { dirname as dirname48, join as join151 } from "node:path";
113362
113393
  init_logger();
113363
113394
  init_safe_prompts();
@@ -113383,7 +113414,7 @@ async function cleanupEmptyDirectories3(filePath, installationRoot) {
113383
113414
  let currentDir = dirname48(filePath);
113384
113415
  while (currentDir !== installationRoot && currentDir.startsWith(installationRoot)) {
113385
113416
  try {
113386
- const entries = readdirSync10(currentDir);
113417
+ const entries = readdirSync11(currentDir);
113387
113418
  if (entries.length === 0) {
113388
113419
  rmSync4(currentDir, { recursive: true });
113389
113420
  cleaned++;
@@ -113627,7 +113658,7 @@ async function removeInstallations(installations, options2) {
113627
113658
  }
113628
113659
  }
113629
113660
  try {
113630
- const remaining = readdirSync11(installation.path);
113661
+ const remaining = readdirSync12(installation.path);
113631
113662
  if (remaining.length === 0) {
113632
113663
  rmSync5(installation.path, { recursive: true });
113633
113664
  logger.debug(`Removed empty installation directory: ${installation.path}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudekit-cli",
3
- "version": "4.3.1-dev.10",
3
+ "version": "4.3.1-dev.12",
4
4
  "description": "CLI tool for bootstrapping and updating ClaudeKit projects",
5
5
  "type": "module",
6
6
  "repository": {
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "files": [
18
18
  "bin/ck.js",
19
+ "bin/postinstall-self-heal.cjs",
19
20
  "dist/index.js",
20
21
  "dist/ui/",
21
22
  "cli-manifest.json"
@@ -47,6 +48,7 @@
47
48
  "ci:local": "bash scripts/ci-local.sh",
48
49
  "install:hooks": "./.githooks/install.sh",
49
50
  "prepare": "node -e \"try{require('child_process').execSync('git rev-parse --git-dir',{stdio:'ignore'});require('child_process').execSync('bash .githooks/install.sh',{stdio:'inherit'})}catch(e){console.warn('[i] Hook install skipped:',e.message)}\"",
51
+ "postinstall": "node bin/postinstall-self-heal.cjs",
50
52
  "help:check-parity": "bun run scripts/check-help-parity.ts",
51
53
  "manifest:generate": "bun run scripts/generate-cli-manifest.ts",
52
54
  "docs:generate": "bun run scripts/generate-cli-reference.ts",