claudekit-cli 4.3.1-dev.11 → 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.11",
3
- "generatedAt": "2026-05-21T01:29:33.371Z",
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.11",
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",
@@ -63989,7 +63991,7 @@ var init_shared2 = __esm(() => {
63989
63991
 
63990
63992
  // src/domains/health-checks/checkers/hook-health-checker.ts
63991
63993
  import { spawnSync as spawnSync3 } from "node:child_process";
63992
- 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";
63993
63995
  import { readdir as readdir17 } from "node:fs/promises";
63994
63996
  import { homedir as homedir41, tmpdir } from "node:os";
63995
63997
  import { join as join64, resolve as resolve33 } from "node:path";
@@ -64026,6 +64028,7 @@ function getCanonicalGlobalCommandRoot() {
64026
64028
  }
64027
64029
  function getClaudeSettingsFiles(projectDir) {
64028
64030
  const globalClaudeDir = PathResolver.getGlobalKitDir();
64031
+ const ccsSettingsDir = join64(process.env.CK_TEST_HOME ?? homedir41(), ".ccs");
64029
64032
  const candidates = [
64030
64033
  {
64031
64034
  path: resolve33(projectDir, ".claude", "settings.json"),
@@ -64048,6 +64051,17 @@ function getClaudeSettingsFiles(projectDir) {
64048
64051
  root: getCanonicalGlobalCommandRoot()
64049
64052
  }
64050
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 {}
64051
64065
  return candidates.filter((candidate) => existsSync45(candidate.path));
64052
64066
  }
64053
64067
  function isAlreadyCanonical(cmd) {
@@ -76419,7 +76433,7 @@ var init_content_validator = __esm(() => {
76419
76433
 
76420
76434
  // src/commands/content/phases/context-cache-manager.ts
76421
76435
  import { createHash as createHash9 } from "node:crypto";
76422
- 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";
76423
76437
  import { rename as rename16, writeFile as writeFile40 } from "node:fs/promises";
76424
76438
  import { homedir as homedir55 } from "node:os";
76425
76439
  import { basename as basename34, join as join160 } from "node:path";
@@ -76468,7 +76482,7 @@ function getDocSourcePaths(repoPath) {
76468
76482
  const docsDir = join160(repoPath, "docs");
76469
76483
  if (existsSync79(docsDir)) {
76470
76484
  try {
76471
- const files = readdirSync12(docsDir);
76485
+ const files = readdirSync13(docsDir);
76472
76486
  for (const f3 of files) {
76473
76487
  if (f3.endsWith(".md"))
76474
76488
  paths.push(join160(docsDir, f3));
@@ -76481,7 +76495,7 @@ function getDocSourcePaths(repoPath) {
76481
76495
  const stylesDir = join160(repoPath, "assets", "writing-styles");
76482
76496
  if (existsSync79(stylesDir)) {
76483
76497
  try {
76484
- const files = readdirSync12(stylesDir);
76498
+ const files = readdirSync13(stylesDir);
76485
76499
  for (const f3 of files) {
76486
76500
  paths.push(join160(stylesDir, f3));
76487
76501
  }
@@ -76676,7 +76690,7 @@ function extractContentFromResponse(response) {
76676
76690
 
76677
76691
  // src/commands/content/phases/docs-summarizer.ts
76678
76692
  import { execSync as execSync7 } from "node:child_process";
76679
- 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";
76680
76694
  import { join as join161 } from "node:path";
76681
76695
  async function summarizeProjectDocs(repoPath, contentLogger) {
76682
76696
  const rawContent = collectRawDocs(repoPath);
@@ -76734,7 +76748,7 @@ function collectRawDocs(repoPath) {
76734
76748
  const docsDir = join161(repoPath, "docs");
76735
76749
  if (existsSync80(docsDir)) {
76736
76750
  try {
76737
- const files = readdirSync13(docsDir).filter((f3) => f3.endsWith(".md")).sort();
76751
+ const files = readdirSync14(docsDir).filter((f3) => f3.endsWith(".md")).sort();
76738
76752
  for (const f3 of files) {
76739
76753
  const content = readCapped(join161(docsDir, f3), 5000);
76740
76754
  if (content) {
@@ -76758,7 +76772,7 @@ ${content}`);
76758
76772
  const stylesDir = join161(repoPath, "assets", "writing-styles");
76759
76773
  if (existsSync80(stylesDir)) {
76760
76774
  try {
76761
- const files = readdirSync13(stylesDir).slice(0, 3);
76775
+ const files = readdirSync14(stylesDir).slice(0, 3);
76762
76776
  styles3 = files.map((f3) => readCapped(join161(stylesDir, f3), 1000)).filter(Boolean).join(`
76763
76777
 
76764
76778
  `);
@@ -76949,7 +76963,7 @@ IMPORTANT: Generate the image and output the path as JSON: {"imagePath": "/path/
76949
76963
 
76950
76964
  // src/commands/content/phases/photo-generator.ts
76951
76965
  import { execSync as execSync8 } from "node:child_process";
76952
- 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";
76953
76967
  import { homedir as homedir56 } from "node:os";
76954
76968
  import { join as join162 } from "node:path";
76955
76969
  async function generatePhoto(_content, context, config, platform18, contentId, contentLogger) {
@@ -76974,7 +76988,7 @@ async function generatePhoto(_content, context, config, platform18, contentId, c
76974
76988
  return { path: imagePath, ...dimensions, format: "png" };
76975
76989
  }
76976
76990
  }
76977
- const files = readdirSync14(mediaDir);
76991
+ const files = readdirSync15(mediaDir);
76978
76992
  const imageFile = files.find((f3) => /\.(png|jpg|jpeg|webp)$/i.test(f3));
76979
76993
  if (imageFile) {
76980
76994
  const ext2 = imageFile.split(".").pop() ?? "png";
@@ -77361,7 +77375,7 @@ function isNoiseCommit(title, author) {
77361
77375
 
77362
77376
  // src/commands/content/phases/change-detector.ts
77363
77377
  import { execSync as execSync10, spawnSync as spawnSync9 } from "node:child_process";
77364
- 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";
77365
77379
  import { join as join164 } from "node:path";
77366
77380
  function detectCommits(repo, since) {
77367
77381
  try {
@@ -77477,7 +77491,7 @@ function detectCompletedPlans(repo, since) {
77477
77491
  const sinceMs = new Date(since).getTime();
77478
77492
  const events = [];
77479
77493
  try {
77480
- const entries = readdirSync15(plansDir, { withFileTypes: true });
77494
+ const entries = readdirSync16(plansDir, { withFileTypes: true });
77481
77495
  for (const entry of entries) {
77482
77496
  if (!entry.isDirectory())
77483
77497
  continue;
@@ -77558,7 +77572,7 @@ function classifyCommit(event) {
77558
77572
 
77559
77573
  // src/commands/content/phases/repo-discoverer.ts
77560
77574
  import { execSync as execSync11 } from "node:child_process";
77561
- import { readdirSync as readdirSync16 } from "node:fs";
77575
+ import { readdirSync as readdirSync17 } from "node:fs";
77562
77576
  import { join as join165 } from "node:path";
77563
77577
  function discoverRepos2(cwd2) {
77564
77578
  const repos = [];
@@ -77568,7 +77582,7 @@ function discoverRepos2(cwd2) {
77568
77582
  repos.push(info);
77569
77583
  }
77570
77584
  try {
77571
- const entries = readdirSync16(cwd2, { withFileTypes: true });
77585
+ const entries = readdirSync17(cwd2, { withFileTypes: true });
77572
77586
  for (const entry of entries) {
77573
77587
  if (!entry.isDirectory() || entry.name.startsWith("."))
77574
77588
  continue;
@@ -100782,7 +100796,7 @@ async function handleDownload(ctx) {
100782
100796
  import { join as join120 } from "node:path";
100783
100797
 
100784
100798
  // src/domains/installation/deletion-handler.ts
100785
- 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";
100786
100800
  import { dirname as dirname35, join as join106, relative as relative21, resolve as resolve41, sep as sep12 } from "node:path";
100787
100801
 
100788
100802
  // src/services/file-operations/manifest/manifest-reader.ts
@@ -100976,7 +100990,7 @@ function collectFilesRecursively(dir, baseDir) {
100976
100990
  if (!existsSync65(dir))
100977
100991
  return results;
100978
100992
  try {
100979
- const entries = readdirSync8(dir, { withFileTypes: true });
100993
+ const entries = readdirSync9(dir, { withFileTypes: true });
100980
100994
  for (const entry of entries) {
100981
100995
  const fullPath = join106(dir, entry.name);
100982
100996
  const relativePath = relative21(baseDir, fullPath);
@@ -101014,7 +101028,7 @@ function cleanupEmptyDirectories(filePath, claudeDir3) {
101014
101028
  while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir) && iterations < MAX_CLEANUP_ITERATIONS) {
101015
101029
  iterations++;
101016
101030
  try {
101017
- const entries = readdirSync8(currentDir);
101031
+ const entries = readdirSync9(currentDir);
101018
101032
  if (entries.length === 0) {
101019
101033
  rmdirSync(currentDir);
101020
101034
  logger.debug(`Removed empty directory: ${currentDir}`);
@@ -106750,7 +106764,7 @@ async function runPreflightChecks() {
106750
106764
 
106751
106765
  // src/domains/installation/fresh-installer.ts
106752
106766
  init_metadata_migration();
106753
- 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";
106754
106768
  import { basename as basename28, dirname as dirname39, join as join132, resolve as resolve45 } from "node:path";
106755
106769
  init_logger();
106756
106770
  init_safe_spinner();
@@ -106807,7 +106821,7 @@ function cleanupEmptyDirectories2(filePath, claudeDir3) {
106807
106821
  let currentDir = resolve45(dirname39(filePath));
106808
106822
  while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir)) {
106809
106823
  try {
106810
- const entries = readdirSync9(currentDir);
106824
+ const entries = readdirSync10(currentDir);
106811
106825
  if (entries.length === 0) {
106812
106826
  rmdirSync2(currentDir);
106813
106827
  logger.debug(`Removed empty directory: ${currentDir}`);
@@ -113365,7 +113379,7 @@ async function detectInstallations() {
113365
113379
  }
113366
113380
 
113367
113381
  // src/commands/uninstall/removal-handler.ts
113368
- import { readdirSync as readdirSync11, rmSync as rmSync5 } from "node:fs";
113382
+ import { readdirSync as readdirSync12, rmSync as rmSync5 } from "node:fs";
113369
113383
  import { basename as basename33, join as join152, resolve as resolve58, sep as sep14 } from "node:path";
113370
113384
  init_logger();
113371
113385
  init_safe_prompts();
@@ -113374,7 +113388,7 @@ var import_fs_extra42 = __toESM(require_lib(), 1);
113374
113388
 
113375
113389
  // src/commands/uninstall/analysis-handler.ts
113376
113390
  init_metadata_migration();
113377
- import { readdirSync as readdirSync10, rmSync as rmSync4 } from "node:fs";
113391
+ import { readdirSync as readdirSync11, rmSync as rmSync4 } from "node:fs";
113378
113392
  import { dirname as dirname48, join as join151 } from "node:path";
113379
113393
  init_logger();
113380
113394
  init_safe_prompts();
@@ -113400,7 +113414,7 @@ async function cleanupEmptyDirectories3(filePath, installationRoot) {
113400
113414
  let currentDir = dirname48(filePath);
113401
113415
  while (currentDir !== installationRoot && currentDir.startsWith(installationRoot)) {
113402
113416
  try {
113403
- const entries = readdirSync10(currentDir);
113417
+ const entries = readdirSync11(currentDir);
113404
113418
  if (entries.length === 0) {
113405
113419
  rmSync4(currentDir, { recursive: true });
113406
113420
  cleaned++;
@@ -113644,7 +113658,7 @@ async function removeInstallations(installations, options2) {
113644
113658
  }
113645
113659
  }
113646
113660
  try {
113647
- const remaining = readdirSync11(installation.path);
113661
+ const remaining = readdirSync12(installation.path);
113648
113662
  if (remaining.length === 0) {
113649
113663
  rmSync5(installation.path, { recursive: true });
113650
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.11",
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",