claudekit-cli 3.41.4-dev.34 → 3.41.4-dev.35

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 +206 -21
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -60747,7 +60747,7 @@ var package_default;
60747
60747
  var init_package = __esm(() => {
60748
60748
  package_default = {
60749
60749
  name: "claudekit-cli",
60750
- version: "3.41.4-dev.34",
60750
+ version: "3.41.4-dev.35",
60751
60751
  description: "CLI tool for bootstrapping and updating ClaudeKit projects",
60752
60752
  type: "module",
60753
60753
  repository: {
@@ -84026,6 +84026,199 @@ async function checkHookCommandPaths(projectDir) {
84026
84026
  }
84027
84027
  };
84028
84028
  }
84029
+ function extractHookScriptPath(cmd) {
84030
+ if (!cmd)
84031
+ return null;
84032
+ const stripped = cmd.replace(/"/g, "");
84033
+ const match = stripped.match(/\bnode\s+(\S*?\.claude[/\\]\S+?\.cjs)(?:\s|$)/);
84034
+ if (!match)
84035
+ return null;
84036
+ return match[1];
84037
+ }
84038
+ function resolveHookScriptPath(scriptPath, projectDir) {
84039
+ let resolved = scriptPath.replace(/\\/g, "/");
84040
+ const home9 = homedir40();
84041
+ resolved = resolved.replace(/^\$\{?HOME\}?/, home9);
84042
+ resolved = resolved.replace(/^\$\{?CLAUDE_PROJECT_DIR\}?/, projectDir);
84043
+ resolved = resolved.replace(/^%USERPROFILE%/, home9);
84044
+ resolved = resolved.replace(/^%CLAUDE_PROJECT_DIR%/, projectDir);
84045
+ resolved = resolved.replace(/^~\//, `${home9}/`);
84046
+ if (resolved.startsWith(".claude/") || resolved === ".claude") {
84047
+ resolved = join84(projectDir, resolved);
84048
+ }
84049
+ return resolve26(resolved);
84050
+ }
84051
+ function collectMissingHookReferences(settings, settingsFile, projectDir) {
84052
+ if (!settings.hooks)
84053
+ return [];
84054
+ const findings = [];
84055
+ const seen = new Set;
84056
+ const consider = (eventName, command, matcher) => {
84057
+ if (!command)
84058
+ return;
84059
+ const scriptPath = extractHookScriptPath(command);
84060
+ if (!scriptPath)
84061
+ return;
84062
+ const resolvedPath = resolveHookScriptPath(scriptPath, projectDir);
84063
+ if (existsSync53(resolvedPath))
84064
+ return;
84065
+ const key = `${settingsFile.path}::${eventName}::${matcher ?? ""}::${scriptPath}`;
84066
+ if (seen.has(key))
84067
+ return;
84068
+ seen.add(key);
84069
+ findings.push({
84070
+ path: settingsFile.path,
84071
+ label: settingsFile.label,
84072
+ eventName,
84073
+ matcher,
84074
+ command,
84075
+ scriptPath,
84076
+ resolvedPath
84077
+ });
84078
+ };
84079
+ for (const [eventName, entries] of Object.entries(settings.hooks)) {
84080
+ for (const entry of entries) {
84081
+ if ("command" in entry && typeof entry.command === "string") {
84082
+ consider(eventName, entry.command, undefined);
84083
+ }
84084
+ if ("hooks" in entry && entry.hooks) {
84085
+ const matcher = "matcher" in entry ? entry.matcher : undefined;
84086
+ for (const hook of entry.hooks) {
84087
+ consider(eventName, hook.command, matcher);
84088
+ }
84089
+ }
84090
+ }
84091
+ }
84092
+ return findings;
84093
+ }
84094
+ async function pruneMissingHookReferencesInSettingsFile(settingsFile, projectDir) {
84095
+ const settings = await SettingsMerger.readSettingsFile(settingsFile.path);
84096
+ if (!settings?.hooks)
84097
+ return 0;
84098
+ let pruned = 0;
84099
+ const hookFileMissing = (command) => {
84100
+ if (!command)
84101
+ return false;
84102
+ const scriptPath = extractHookScriptPath(command);
84103
+ if (!scriptPath)
84104
+ return false;
84105
+ return !existsSync53(resolveHookScriptPath(scriptPath, projectDir));
84106
+ };
84107
+ const hooksRecord = settings.hooks;
84108
+ for (const [eventName, entries] of Object.entries(hooksRecord)) {
84109
+ const filteredEntries = [];
84110
+ for (const entry of entries) {
84111
+ const e2 = entry;
84112
+ if (typeof e2.command === "string") {
84113
+ if (hookFileMissing(e2.command)) {
84114
+ pruned++;
84115
+ continue;
84116
+ }
84117
+ filteredEntries.push(entry);
84118
+ continue;
84119
+ }
84120
+ if (Array.isArray(e2.hooks)) {
84121
+ const keptHooks = e2.hooks.filter((h2) => {
84122
+ if (hookFileMissing(h2.command)) {
84123
+ pruned++;
84124
+ return false;
84125
+ }
84126
+ return true;
84127
+ });
84128
+ if (keptHooks.length === 0) {
84129
+ continue;
84130
+ }
84131
+ filteredEntries.push({ ...e2, hooks: keptHooks });
84132
+ continue;
84133
+ }
84134
+ filteredEntries.push(entry);
84135
+ }
84136
+ if (filteredEntries.length === 0) {
84137
+ delete hooksRecord[eventName];
84138
+ } else {
84139
+ hooksRecord[eventName] = filteredEntries;
84140
+ }
84141
+ }
84142
+ if (Object.keys(hooksRecord).length === 0) {
84143
+ delete settings.hooks;
84144
+ }
84145
+ if (pruned > 0) {
84146
+ await SettingsMerger.writeSettingsFile(settingsFile.path, settings);
84147
+ }
84148
+ return pruned;
84149
+ }
84150
+ async function checkHookFileReferences(projectDir) {
84151
+ const settingsFiles = getClaudeSettingsFiles(projectDir);
84152
+ if (settingsFiles.length === 0) {
84153
+ return {
84154
+ id: "hook-file-references",
84155
+ name: "Hook File References",
84156
+ group: "claudekit",
84157
+ priority: "critical",
84158
+ status: "info",
84159
+ message: "No Claude settings files",
84160
+ autoFixable: false
84161
+ };
84162
+ }
84163
+ const findings = [];
84164
+ for (const settingsFile of settingsFiles) {
84165
+ const settings = await SettingsMerger.readSettingsFile(settingsFile.path);
84166
+ if (!settings)
84167
+ continue;
84168
+ findings.push(...collectMissingHookReferences(settings, settingsFile, projectDir));
84169
+ }
84170
+ if (findings.length === 0) {
84171
+ return {
84172
+ id: "hook-file-references",
84173
+ name: "Hook File References",
84174
+ group: "claudekit",
84175
+ priority: "critical",
84176
+ status: "pass",
84177
+ message: "All referenced hook files exist",
84178
+ autoFixable: false
84179
+ };
84180
+ }
84181
+ const details = findings.slice(0, 8).map((f3) => {
84182
+ const matcher = f3.matcher ? ` [${f3.matcher}]` : "";
84183
+ return `${f3.label} :: ${f3.eventName}${matcher} :: missing ${f3.scriptPath}`;
84184
+ }).join(`
84185
+ `);
84186
+ return {
84187
+ id: "hook-file-references",
84188
+ name: "Hook File References",
84189
+ group: "claudekit",
84190
+ priority: "critical",
84191
+ status: "fail",
84192
+ message: `${findings.length} settings hook(s) reference missing file(s)`,
84193
+ details,
84194
+ suggestion: "Run: ck doctor --fix (prunes stale entries), then 'ck init' to restore hooks",
84195
+ autoFixable: true,
84196
+ fix: {
84197
+ id: "fix-hook-file-references",
84198
+ description: "Prune stale hook entries whose script files are missing",
84199
+ execute: async () => {
84200
+ try {
84201
+ let pruned = 0;
84202
+ for (const settingsFile of settingsFiles) {
84203
+ pruned += await pruneMissingHookReferencesInSettingsFile(settingsFile, projectDir);
84204
+ }
84205
+ if (pruned === 0) {
84206
+ return { success: true, message: "No stale hook entries needed pruning" };
84207
+ }
84208
+ return {
84209
+ success: true,
84210
+ message: `Pruned ${pruned} stale hook entry(ies). Run 'ck init' to restore hooks if needed.`
84211
+ };
84212
+ } catch (error) {
84213
+ return {
84214
+ success: false,
84215
+ message: `Failed to prune stale hook entries: ${error}`
84216
+ };
84217
+ }
84218
+ }
84219
+ }
84220
+ };
84221
+ }
84029
84222
  async function checkHookConfig(projectDir) {
84030
84223
  const projectConfigPath = join84(projectDir, ".claude", ".ck.json");
84031
84224
  const globalConfigPath = join84(PathResolver.getGlobalKitDir(), ".ck.json");
@@ -84527,6 +84720,8 @@ class ClaudekitChecker {
84527
84720
  results.push(await checkHookRuntime(this.projectDir));
84528
84721
  logger.verbose("ClaudekitChecker: Checking hook command paths");
84529
84722
  results.push(await checkHookCommandPaths(this.projectDir));
84723
+ logger.verbose("ClaudekitChecker: Checking hook file references");
84724
+ results.push(await checkHookFileReferences(this.projectDir));
84530
84725
  logger.verbose("ClaudekitChecker: Checking hook config");
84531
84726
  results.push(await checkHookConfig(this.projectDir));
84532
84727
  logger.verbose("ClaudekitChecker: Checking hook crash logs");
@@ -103376,7 +103571,7 @@ import { readFile as readFile58, readdir as readdir42, writeFile as writeFile32
103376
103571
  import { homedir as homedir44, platform as platform15 } from "node:os";
103377
103572
  import { extname as extname6, join as join137 } from "node:path";
103378
103573
  var IS_WINDOWS3 = platform15() === "win32";
103379
- var HOME_PREFIX = IS_WINDOWS3 ? "%USERPROFILE%" : "$HOME";
103574
+ var HOME_PREFIX = "$HOME";
103380
103575
  function getHomeDirPrefix() {
103381
103576
  return HOME_PREFIX;
103382
103577
  }
@@ -103428,20 +103623,12 @@ function transformContent(content, options2 = {}) {
103428
103623
  const homePrefix = getHomeDirPrefix();
103429
103624
  const customGlobalClaudeDir = getCustomGlobalClaudeDir(options2.targetClaudeDir);
103430
103625
  const claudePath = getGlobalClaudePath(options2.targetClaudeDir);
103431
- if (IS_WINDOWS3) {
103432
- const homePathResult = replaceTracked(transformed, /\$HOME\/\.claude\//g, claudePath);
103433
- transformed = homePathResult.content;
103434
- changes += homePathResult.changes;
103435
- const braceHomePathResult = replaceTracked(transformed, /\$\{HOME\}\/\.claude\//g, claudePath);
103436
- transformed = braceHomePathResult.content;
103437
- changes += braceHomePathResult.changes;
103438
- const homePrefixResult = replaceTracked(transformed, /\$HOME(?=\/|\\)/g, homePrefix);
103439
- transformed = homePrefixResult.content;
103440
- changes += homePrefixResult.changes;
103441
- const braceHomePrefixResult = replaceTracked(transformed, /\$\{HOME\}(?=\/|\\)/g, homePrefix);
103442
- transformed = braceHomePrefixResult.content;
103443
- changes += braceHomePrefixResult.changes;
103444
- }
103626
+ const userProfileClaudeResult = replaceTracked(transformed, /%USERPROFILE%[\\/]\.claude[\\/]/g, claudePath);
103627
+ transformed = userProfileClaudeResult.content;
103628
+ changes += userProfileClaudeResult.changes;
103629
+ const userProfileStandaloneResult = replaceTracked(transformed, /%USERPROFILE%(?=[\\/])/g, homePrefix);
103630
+ transformed = userProfileStandaloneResult.content;
103631
+ changes += userProfileStandaloneResult.changes;
103445
103632
  const projectDirPathResult = replaceTracked(transformed, /\$CLAUDE_PROJECT_DIR\/\.claude\//g, claudePath);
103446
103633
  transformed = projectDirPathResult.content;
103447
103634
  changes += projectDirPathResult.changes;
@@ -103452,11 +103639,9 @@ function transformContent(content, options2 = {}) {
103452
103639
  const braceProjectDirPathResult = replaceTracked(transformed, /\$\{CLAUDE_PROJECT_DIR\}\/\.claude\//g, claudePath);
103453
103640
  transformed = braceProjectDirPathResult.content;
103454
103641
  changes += braceProjectDirPathResult.changes;
103455
- if (IS_WINDOWS3) {
103456
- const windowsProjectDirPathResult = replaceTracked(transformed, /%CLAUDE_PROJECT_DIR%\/\.claude\//g, claudePath);
103457
- transformed = windowsProjectDirPathResult.content;
103458
- changes += windowsProjectDirPathResult.changes;
103459
- }
103642
+ const windowsProjectDirPathResult = replaceTracked(transformed, /%CLAUDE_PROJECT_DIR%[\\/]\.claude[\\/]/g, claudePath);
103643
+ transformed = windowsProjectDirPathResult.content;
103644
+ changes += windowsProjectDirPathResult.changes;
103460
103645
  const relativeClaudePathResult = replaceTracked(transformed, /\.\/\.claude\//g, claudePath);
103461
103646
  transformed = relativeClaudePathResult.content;
103462
103647
  changes += relativeClaudePathResult.changes;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudekit-cli",
3
- "version": "3.41.4-dev.34",
3
+ "version": "3.41.4-dev.35",
4
4
  "description": "CLI tool for bootstrapping and updating ClaudeKit projects",
5
5
  "type": "module",
6
6
  "repository": {