claudekit-cli 4.3.1-dev.16 → 4.3.1-dev.17
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.
package/cli-manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.3.1-dev.
|
|
3
|
-
"generatedAt": "2026-05-
|
|
2
|
+
"version": "4.3.1-dev.17",
|
|
3
|
+
"generatedAt": "2026-05-27T03:32:56.469Z",
|
|
4
4
|
"commands": {
|
|
5
5
|
"agents": {
|
|
6
6
|
"name": "agents",
|
|
@@ -1188,6 +1188,10 @@
|
|
|
1188
1188
|
{
|
|
1189
1189
|
"flags": "--force-overwrite-settings",
|
|
1190
1190
|
"description": "Fully replace settings.json instead of selective merge"
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
"flags": "--restore-ck-hooks",
|
|
1194
|
+
"description": "Restore CK-managed hook registrations during update self-heal"
|
|
1191
1195
|
}
|
|
1192
1196
|
]
|
|
1193
1197
|
},
|
package/dist/index.js
CHANGED
|
@@ -15349,6 +15349,7 @@ var init_commands = __esm(() => {
|
|
|
15349
15349
|
dryRun: exports_external.boolean().default(false),
|
|
15350
15350
|
forceOverwrite: exports_external.boolean().default(false),
|
|
15351
15351
|
forceOverwriteSettings: exports_external.boolean().default(false),
|
|
15352
|
+
restoreCkHooks: exports_external.boolean().default(false),
|
|
15352
15353
|
skipSetup: exports_external.boolean().default(false),
|
|
15353
15354
|
refresh: exports_external.boolean().default(false),
|
|
15354
15355
|
docsDir: exports_external.string().optional(),
|
|
@@ -15891,8 +15892,12 @@ var init_ck_config = __esm(() => {
|
|
|
15891
15892
|
CkHooksConfigSchema = exports_external.object({
|
|
15892
15893
|
"session-init": exports_external.boolean().optional(),
|
|
15893
15894
|
"subagent-init": exports_external.boolean().optional(),
|
|
15895
|
+
"session-state": exports_external.boolean().optional(),
|
|
15896
|
+
"cook-after-plan-reminder": exports_external.boolean().optional(),
|
|
15894
15897
|
"descriptive-name": exports_external.boolean().optional(),
|
|
15895
15898
|
"dev-rules-reminder": exports_external.boolean().optional(),
|
|
15899
|
+
"plan-format-kanban": exports_external.boolean().optional(),
|
|
15900
|
+
"usage-quota-cache-refresh": exports_external.boolean().optional(),
|
|
15896
15901
|
"usage-context-awareness": exports_external.boolean().optional(),
|
|
15897
15902
|
"context-tracking": exports_external.boolean().optional(),
|
|
15898
15903
|
"scout-block": exports_external.boolean().optional(),
|
|
@@ -15989,8 +15994,12 @@ var init_ck_config = __esm(() => {
|
|
|
15989
15994
|
hooks: {
|
|
15990
15995
|
"session-init": true,
|
|
15991
15996
|
"subagent-init": true,
|
|
15997
|
+
"session-state": true,
|
|
15998
|
+
"cook-after-plan-reminder": true,
|
|
15992
15999
|
"descriptive-name": true,
|
|
15993
16000
|
"dev-rules-reminder": true,
|
|
16001
|
+
"plan-format-kanban": true,
|
|
16002
|
+
"usage-quota-cache-refresh": true,
|
|
15994
16003
|
"usage-context-awareness": true,
|
|
15995
16004
|
"context-tracking": true,
|
|
15996
16005
|
"scout-block": true,
|
|
@@ -16014,8 +16023,12 @@ var init_ck_config = __esm(() => {
|
|
|
16014
16023
|
CK_HOOK_NAMES = [
|
|
16015
16024
|
"session-init",
|
|
16016
16025
|
"subagent-init",
|
|
16026
|
+
"session-state",
|
|
16027
|
+
"cook-after-plan-reminder",
|
|
16017
16028
|
"descriptive-name",
|
|
16018
16029
|
"dev-rules-reminder",
|
|
16030
|
+
"plan-format-kanban",
|
|
16031
|
+
"usage-quota-cache-refresh",
|
|
16019
16032
|
"usage-context-awareness",
|
|
16020
16033
|
"context-tracking",
|
|
16021
16034
|
"scout-block",
|
|
@@ -45197,6 +45210,14 @@ var init_open = __esm(() => {
|
|
|
45197
45210
|
open_default = open;
|
|
45198
45211
|
});
|
|
45199
45212
|
|
|
45213
|
+
// src/shared/json-content.ts
|
|
45214
|
+
function stripJsonBom(content) {
|
|
45215
|
+
return content.charCodeAt(0) === 65279 ? content.slice(1) : content;
|
|
45216
|
+
}
|
|
45217
|
+
function parseJsonContent(content) {
|
|
45218
|
+
return JSON.parse(stripJsonBom(content));
|
|
45219
|
+
}
|
|
45220
|
+
|
|
45200
45221
|
// src/domains/config/config-manager.ts
|
|
45201
45222
|
import { existsSync as existsSync10 } from "node:fs";
|
|
45202
45223
|
import { mkdir as mkdir6, readFile as readFile7, rename as rename3, rm as rm3, writeFile as writeFile5 } from "node:fs/promises";
|
|
@@ -45225,7 +45246,7 @@ class ConfigManager {
|
|
|
45225
45246
|
try {
|
|
45226
45247
|
if (existsSync10(configFile)) {
|
|
45227
45248
|
const content = await readFile7(configFile, "utf-8");
|
|
45228
|
-
const data =
|
|
45249
|
+
const data = parseJsonContent(content);
|
|
45229
45250
|
ConfigManager.config = ConfigSchema.parse(data);
|
|
45230
45251
|
logger.debug(`Config loaded from ${configFile}`);
|
|
45231
45252
|
return ConfigManager.config;
|
|
@@ -45279,7 +45300,7 @@ class ConfigManager {
|
|
|
45279
45300
|
try {
|
|
45280
45301
|
if (existsSync10(configPath)) {
|
|
45281
45302
|
const content = await readFile7(configPath, "utf-8");
|
|
45282
|
-
const data =
|
|
45303
|
+
const data = parseJsonContent(content);
|
|
45283
45304
|
const folders = FoldersConfigSchema.parse(data.paths || data);
|
|
45284
45305
|
logger.debug(`Project config loaded from ${configPath}`);
|
|
45285
45306
|
return folders;
|
|
@@ -45300,7 +45321,7 @@ class ConfigManager {
|
|
|
45300
45321
|
if (existsSync10(configPath)) {
|
|
45301
45322
|
try {
|
|
45302
45323
|
const content = await readFile7(configPath, "utf-8");
|
|
45303
|
-
existingConfig =
|
|
45324
|
+
existingConfig = parseJsonContent(content);
|
|
45304
45325
|
} catch (error) {
|
|
45305
45326
|
logger.debug(`Could not parse existing config, starting fresh: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
45306
45327
|
}
|
|
@@ -45475,7 +45496,7 @@ class CkConfigManager {
|
|
|
45475
45496
|
if (!existsSync11(configPath))
|
|
45476
45497
|
return null;
|
|
45477
45498
|
const content = await readFile8(configPath, "utf-8");
|
|
45478
|
-
const data = normalizeCkConfigInput(
|
|
45499
|
+
const data = normalizeCkConfigInput(parseJsonContent(content));
|
|
45479
45500
|
return CkConfigSchema.parse(data);
|
|
45480
45501
|
} catch (error) {
|
|
45481
45502
|
logger.warning(`Failed to load config from ${configPath}: ${error instanceof Error ? error.message : "Unknown"}`);
|
|
@@ -45542,7 +45563,7 @@ class CkConfigManager {
|
|
|
45542
45563
|
if (!existingConfig && existsSync11(configPath)) {
|
|
45543
45564
|
try {
|
|
45544
45565
|
const content = await readFile8(configPath, "utf-8");
|
|
45545
|
-
const parsed =
|
|
45566
|
+
const parsed = parseJsonContent(content);
|
|
45546
45567
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
45547
45568
|
existingConfig = normalizeCkConfigInput(parsed);
|
|
45548
45569
|
}
|
|
@@ -45794,6 +45815,12 @@ function repairClaudeNodeCommandPath(cmd, root) {
|
|
|
45794
45815
|
const command = formatCanonicalClaudeCommand(nodePrefix, root, relativePath, suffix);
|
|
45795
45816
|
return { command, changed: command !== cmd, issue: "raw-relative" };
|
|
45796
45817
|
}
|
|
45818
|
+
const quotedRelativeMatch = cmd.match(/^(node\s+)["'](?:\.\/)?(\.claude[/\\][^"']+)["'](.*)$/);
|
|
45819
|
+
if (quotedRelativeMatch) {
|
|
45820
|
+
const [, nodePrefix, relativePath, suffix] = quotedRelativeMatch;
|
|
45821
|
+
const command = formatCanonicalClaudeCommand(nodePrefix, root, relativePath, suffix);
|
|
45822
|
+
return { command, changed: command !== cmd, issue: "raw-relative" };
|
|
45823
|
+
}
|
|
45797
45824
|
const embeddedQuotedMatch = cmd.match(/^(node\s+)"(?:\$HOME|\$CLAUDE_PROJECT_DIR|%USERPROFILE%|%CLAUDE_PROJECT_DIR%)[/\\](\.claude[/\\][^"]+)"(.*)$/);
|
|
45798
45825
|
if (embeddedQuotedMatch) {
|
|
45799
45826
|
const [, nodePrefix, relativePath, suffix] = embeddedQuotedMatch;
|
|
@@ -50122,6 +50149,16 @@ var init_ck_config_schema = __esm(() => {
|
|
|
50122
50149
|
default: true,
|
|
50123
50150
|
description: "SubagentStart hook - injects context to subagents"
|
|
50124
50151
|
},
|
|
50152
|
+
"session-state": {
|
|
50153
|
+
type: "boolean",
|
|
50154
|
+
default: true,
|
|
50155
|
+
description: "Stop/SubagentStop/PostToolUse hook - persists session state for handoff and statusline context"
|
|
50156
|
+
},
|
|
50157
|
+
"cook-after-plan-reminder": {
|
|
50158
|
+
type: "boolean",
|
|
50159
|
+
default: true,
|
|
50160
|
+
description: "SubagentStop hook - reminds agents to continue implementation after planning"
|
|
50161
|
+
},
|
|
50125
50162
|
"descriptive-name": {
|
|
50126
50163
|
type: "boolean",
|
|
50127
50164
|
default: true,
|
|
@@ -50132,6 +50169,16 @@ var init_ck_config_schema = __esm(() => {
|
|
|
50132
50169
|
default: true,
|
|
50133
50170
|
description: "UserPromptSubmit hook - injects dev rules context"
|
|
50134
50171
|
},
|
|
50172
|
+
"plan-format-kanban": {
|
|
50173
|
+
type: "boolean",
|
|
50174
|
+
default: true,
|
|
50175
|
+
description: "PostToolUse hook - keeps plan kanban metadata synchronized after edits"
|
|
50176
|
+
},
|
|
50177
|
+
"usage-quota-cache-refresh": {
|
|
50178
|
+
type: "boolean",
|
|
50179
|
+
default: true,
|
|
50180
|
+
description: "Lifecycle hook - refreshes cached usage quota information"
|
|
50181
|
+
},
|
|
50135
50182
|
"usage-context-awareness": {
|
|
50136
50183
|
type: "boolean",
|
|
50137
50184
|
default: true,
|
|
@@ -63806,7 +63853,7 @@ var package_default;
|
|
|
63806
63853
|
var init_package = __esm(() => {
|
|
63807
63854
|
package_default = {
|
|
63808
63855
|
name: "claudekit-cli",
|
|
63809
|
-
version: "4.3.1-dev.
|
|
63856
|
+
version: "4.3.1-dev.17",
|
|
63810
63857
|
description: "CLI tool for bootstrapping and updating ClaudeKit projects",
|
|
63811
63858
|
type: "module",
|
|
63812
63859
|
repository: {
|
|
@@ -64290,8 +64337,9 @@ var init_package_manager_runner = __esm(() => {
|
|
|
64290
64337
|
import { existsSync as existsSync44, readdirSync as readdirSync7 } from "node:fs";
|
|
64291
64338
|
import { homedir as homedir40 } from "node:os";
|
|
64292
64339
|
import { basename as basename23, dirname as dirname28, isAbsolute as isAbsolute10, resolve as resolve32, sep as sep11 } from "node:path";
|
|
64293
|
-
function pruneZombieEngineerWirings(settings, hookDir) {
|
|
64340
|
+
function pruneZombieEngineerWirings(settings, hookDir, preserveCommands = new Set) {
|
|
64294
64341
|
const pruned = [];
|
|
64342
|
+
const normalizedPreserveCommands = new Set(Array.from(preserveCommands).map((command) => normalizeCommand(command)));
|
|
64295
64343
|
if (!existsSync44(hookDir)) {
|
|
64296
64344
|
return { settings, pruned };
|
|
64297
64345
|
}
|
|
@@ -64311,14 +64359,14 @@ function pruneZombieEngineerWirings(settings, hookDir) {
|
|
|
64311
64359
|
for (const group of groups) {
|
|
64312
64360
|
if (!("hooks" in group) || !Array.isArray(group.hooks)) {
|
|
64313
64361
|
const entry = group;
|
|
64314
|
-
if (shouldPruneEntry(entry, hookDir, pruned)) {
|
|
64362
|
+
if (shouldPruneEntry(entry, hookDir, pruned, normalizedPreserveCommands)) {
|
|
64315
64363
|
continue;
|
|
64316
64364
|
}
|
|
64317
64365
|
keptGroups.push(group);
|
|
64318
64366
|
continue;
|
|
64319
64367
|
}
|
|
64320
64368
|
const keptHooks = group.hooks.filter((h2) => {
|
|
64321
|
-
return !shouldPruneEntry(h2, hookDir, pruned);
|
|
64369
|
+
return !shouldPruneEntry(h2, hookDir, pruned, normalizedPreserveCommands);
|
|
64322
64370
|
});
|
|
64323
64371
|
if (keptHooks.length > 0) {
|
|
64324
64372
|
keptGroups.push({ ...group, hooks: keptHooks });
|
|
@@ -64335,13 +64383,16 @@ function pruneZombieEngineerWirings(settings, hookDir) {
|
|
|
64335
64383
|
}
|
|
64336
64384
|
return { settings, pruned };
|
|
64337
64385
|
}
|
|
64338
|
-
function shouldPruneEntry(entry, hookDir, pruned) {
|
|
64386
|
+
function shouldPruneEntry(entry, hookDir, pruned, preserveCommands) {
|
|
64339
64387
|
if (isLegacyDescriptiveNamePrompt(entry)) {
|
|
64340
64388
|
pruned.push("legacy-descriptive-name-prompt");
|
|
64341
64389
|
return true;
|
|
64342
64390
|
}
|
|
64343
64391
|
if (entry._origin !== "engineer")
|
|
64344
64392
|
return false;
|
|
64393
|
+
if (entry.command && preserveCommands.has(normalizeCommand(entry.command))) {
|
|
64394
|
+
return false;
|
|
64395
|
+
}
|
|
64345
64396
|
const filePath = extractHookFilePath(entry.command, hookDir);
|
|
64346
64397
|
if (!filePath)
|
|
64347
64398
|
return false;
|
|
@@ -64426,7 +64477,9 @@ function extractHookFilePath(command, hookDir) {
|
|
|
64426
64477
|
}
|
|
64427
64478
|
return null;
|
|
64428
64479
|
}
|
|
64429
|
-
var init_zombie_wirings_pruner = () => {
|
|
64480
|
+
var init_zombie_wirings_pruner = __esm(() => {
|
|
64481
|
+
init_command_normalizer();
|
|
64482
|
+
});
|
|
64430
64483
|
|
|
64431
64484
|
// src/domains/health-checks/checkers/shared.ts
|
|
64432
64485
|
function shouldSkipExpensiveOperations2() {
|
|
@@ -67391,7 +67444,7 @@ async function readMetadataFile(claudeDir3) {
|
|
|
67391
67444
|
return null;
|
|
67392
67445
|
}
|
|
67393
67446
|
}
|
|
67394
|
-
function buildInitCommand(isGlobal, kit, beta, yes) {
|
|
67447
|
+
function buildInitCommand(isGlobal, kit, beta, yes, restoreCkHooks) {
|
|
67395
67448
|
const parts = ["ck init"];
|
|
67396
67449
|
if (isGlobal)
|
|
67397
67450
|
parts.push("-g");
|
|
@@ -67399,6 +67452,8 @@ function buildInitCommand(isGlobal, kit, beta, yes) {
|
|
|
67399
67452
|
parts.push(`--kit ${kit}`);
|
|
67400
67453
|
if (yes)
|
|
67401
67454
|
parts.push("--yes");
|
|
67455
|
+
if (restoreCkHooks)
|
|
67456
|
+
parts.push("--restore-ck-hooks");
|
|
67402
67457
|
parts.push("--install-skills");
|
|
67403
67458
|
if (beta)
|
|
67404
67459
|
parts.push("--beta");
|
|
@@ -67480,6 +67535,36 @@ async function promptKitUpdate(beta, yes, deps) {
|
|
|
67480
67535
|
logger.verbose("No ClaudeKit installations detected, skipping kit update prompt");
|
|
67481
67536
|
return;
|
|
67482
67537
|
}
|
|
67538
|
+
let forceKitReinstall = false;
|
|
67539
|
+
if (hasLocal && setup.project.path) {
|
|
67540
|
+
try {
|
|
67541
|
+
const missingHookDeps = await findMissingHookDepsFn(setup.project.path);
|
|
67542
|
+
if (missingHookDeps.length > 0) {
|
|
67543
|
+
logger.warning(`Detected ${missingHookDeps.length} local missing hook dependency(ies); reinstalling local kit content`);
|
|
67544
|
+
forceKitReinstall = true;
|
|
67545
|
+
}
|
|
67546
|
+
} catch (error) {
|
|
67547
|
+
logger.verbose(`Local hook dependency self-heal check skipped: ${error instanceof Error ? error.message : "unknown"}`);
|
|
67548
|
+
}
|
|
67549
|
+
try {
|
|
67550
|
+
const countMissingHookRefsFn = deps?.countMissingHookFileReferencesFn ?? countMissingHookFileReferences;
|
|
67551
|
+
const missingHookRefs = await countMissingHookRefsFn(dirname29(setup.project.path));
|
|
67552
|
+
if (missingHookRefs > 0) {
|
|
67553
|
+
logger.warning(`Detected ${missingHookRefs} local broken hook registration(s); reinstalling local kit content`);
|
|
67554
|
+
forceKitReinstall = true;
|
|
67555
|
+
}
|
|
67556
|
+
} catch (error) {
|
|
67557
|
+
logger.verbose(`Local hook registration self-heal check skipped: ${error instanceof Error ? error.message : "unknown"}`);
|
|
67558
|
+
}
|
|
67559
|
+
if (forceKitReinstall && selection.isGlobal) {
|
|
67560
|
+
const kit = localKits[0] || selection.kit;
|
|
67561
|
+
selection = {
|
|
67562
|
+
isGlobal: false,
|
|
67563
|
+
kit,
|
|
67564
|
+
promptMessage: `Update local project ClaudeKit content${kit ? ` (${kit})` : ""}?`
|
|
67565
|
+
};
|
|
67566
|
+
}
|
|
67567
|
+
}
|
|
67483
67568
|
let kitVersion = selection.kit ? selection.isGlobal ? globalMetadata?.kits?.[selection.kit]?.version : localMetadata?.kits?.[selection.kit]?.version : undefined;
|
|
67484
67569
|
const isBetaInstalled = isBetaVersion(kitVersion);
|
|
67485
67570
|
const promptMessage = selection.promptMessage;
|
|
@@ -67487,7 +67572,7 @@ async function promptKitUpdate(beta, yes, deps) {
|
|
|
67487
67572
|
logger.info(`Current kit version: ${selection.kit}@${kitVersion}`);
|
|
67488
67573
|
}
|
|
67489
67574
|
let alreadyAtLatest = false;
|
|
67490
|
-
if (yes && selection.kit && kitVersion) {
|
|
67575
|
+
if (yes && selection.kit && kitVersion && !forceKitReinstall) {
|
|
67491
67576
|
const getTagFn = deps?.getLatestReleaseTagFn ?? fetchLatestReleaseTag;
|
|
67492
67577
|
const latestTag = await getTagFn(selection.kit, beta || isBetaInstalled);
|
|
67493
67578
|
if (latestTag && versionsMatch(kitVersion, latestTag)) {
|
|
@@ -67503,6 +67588,7 @@ async function promptKitUpdate(beta, yes, deps) {
|
|
|
67503
67588
|
if (missingHookDeps.length > 0) {
|
|
67504
67589
|
logger.warning(`Detected ${missingHookDeps.length} missing hook dependency(ies); reinstalling kit content`);
|
|
67505
67590
|
alreadyAtLatest = false;
|
|
67591
|
+
forceKitReinstall = true;
|
|
67506
67592
|
}
|
|
67507
67593
|
} catch (error) {
|
|
67508
67594
|
logger.verbose(`Hook dependency self-heal check skipped: ${error instanceof Error ? error.message : "unknown"}`);
|
|
@@ -67516,6 +67602,7 @@ async function promptKitUpdate(beta, yes, deps) {
|
|
|
67516
67602
|
if (missingHookRefs > 0) {
|
|
67517
67603
|
logger.warning(`Detected ${missingHookRefs} broken hook registration(s); reinstalling kit content`);
|
|
67518
67604
|
alreadyAtLatest = false;
|
|
67605
|
+
forceKitReinstall = true;
|
|
67519
67606
|
if (setup.project.path && selection.isGlobal) {
|
|
67520
67607
|
selection = {
|
|
67521
67608
|
isGlobal: false,
|
|
@@ -67552,7 +67639,7 @@ async function promptKitUpdate(beta, yes, deps) {
|
|
|
67552
67639
|
}
|
|
67553
67640
|
const useBeta = beta || isBetaInstalled;
|
|
67554
67641
|
if (yes) {
|
|
67555
|
-
const initCmd = buildInitCommand(selection.isGlobal, selection.kit, useBeta, true);
|
|
67642
|
+
const initCmd = buildInitCommand(selection.isGlobal, selection.kit, useBeta, true, forceKitReinstall);
|
|
67556
67643
|
logger.info(`Running: ${initCmd}`);
|
|
67557
67644
|
const s = (deps?.spinnerFn ?? de)();
|
|
67558
67645
|
s.start("Updating ClaudeKit content...");
|
|
@@ -67585,6 +67672,8 @@ async function promptKitUpdate(beta, yes, deps) {
|
|
|
67585
67672
|
const args = ["init"];
|
|
67586
67673
|
if (selection.isGlobal)
|
|
67587
67674
|
args.push("-g");
|
|
67675
|
+
if (forceKitReinstall)
|
|
67676
|
+
args.push("--restore-ck-hooks");
|
|
67588
67677
|
args.push("--install-skills");
|
|
67589
67678
|
if (useBeta)
|
|
67590
67679
|
args.push("--beta");
|
|
@@ -67654,17 +67743,19 @@ async function promptMigrateUpdate(deps) {
|
|
|
67654
67743
|
} catch {}
|
|
67655
67744
|
try {
|
|
67656
67745
|
const cleanupFn = deps?.cleanupMigratedHooksFn ?? (await Promise.resolve().then(() => (init_migrated_hooks_cleanup(), exports_migrated_hooks_cleanup))).cleanupMigratedHooksForProviders;
|
|
67657
|
-
const cleanupProviders = allProviders.filter((p) => SAFE_PROVIDER_NAME.test(p));
|
|
67658
|
-
|
|
67659
|
-
|
|
67660
|
-
|
|
67661
|
-
|
|
67746
|
+
const cleanupProviders = allProviders.filter((p) => p !== "claude-code" && SAFE_PROVIDER_NAME.test(p));
|
|
67747
|
+
if (cleanupProviders.length > 0) {
|
|
67748
|
+
const cleanupResults = await cleanupFn(cleanupProviders, { global: isGlobal });
|
|
67749
|
+
const cleanupCount = cleanupResults.reduce((total, result) => total + result.hooksPruned + result.filesRemoved + result.registryEntriesRemoved, 0);
|
|
67750
|
+
if (cleanupCount > 0) {
|
|
67751
|
+
logger.info(`Cleaned up ${cleanupCount} generated-context hook artifact(s)`);
|
|
67752
|
+
}
|
|
67753
|
+
await repairLegacyHookPromptsSafely();
|
|
67754
|
+
await repairHookFileReferencesSafely();
|
|
67662
67755
|
}
|
|
67663
67756
|
} catch (error) {
|
|
67664
67757
|
logger.verbose(`Migrated hook cleanup skipped: ${error instanceof Error ? error.message : "unknown"}`);
|
|
67665
67758
|
}
|
|
67666
|
-
await repairLegacyHookPromptsSafely();
|
|
67667
|
-
await repairHookFileReferencesSafely();
|
|
67668
67759
|
const targets = allProviders.filter((p) => p !== "claude-code");
|
|
67669
67760
|
if (targets.length === 0) {
|
|
67670
67761
|
logger.verbose("No migration targets detected, skipping migrate step");
|
|
@@ -80723,6 +80814,10 @@ var init_init_command_help = __esm(() => {
|
|
|
80723
80814
|
{
|
|
80724
80815
|
flags: "--force-overwrite-settings",
|
|
80725
80816
|
description: "Fully replace settings.json instead of selective merge"
|
|
80817
|
+
},
|
|
80818
|
+
{
|
|
80819
|
+
flags: "--restore-ck-hooks",
|
|
80820
|
+
description: "Restore CK-managed hook registrations during update self-heal"
|
|
80726
80821
|
}
|
|
80727
80822
|
]
|
|
80728
80823
|
},
|
|
@@ -103373,7 +103468,7 @@ class InstalledSettingsTracker {
|
|
|
103373
103468
|
}
|
|
103374
103469
|
try {
|
|
103375
103470
|
const content = await readFile50(ckJsonPath, "utf-8");
|
|
103376
|
-
const data =
|
|
103471
|
+
const data = parseJsonContent(content);
|
|
103377
103472
|
const installed = data.kits?.[this.kitName]?.installedSettings;
|
|
103378
103473
|
if (installed) {
|
|
103379
103474
|
return installed;
|
|
@@ -103390,7 +103485,7 @@ class InstalledSettingsTracker {
|
|
|
103390
103485
|
let data = {};
|
|
103391
103486
|
if (existsSync66(ckJsonPath)) {
|
|
103392
103487
|
const content = await readFile50(ckJsonPath, "utf-8");
|
|
103393
|
-
data =
|
|
103488
|
+
data = parseJsonContent(content);
|
|
103394
103489
|
}
|
|
103395
103490
|
if (!data.kits) {
|
|
103396
103491
|
data.kits = {};
|
|
@@ -103456,6 +103551,7 @@ class SettingsProcessor {
|
|
|
103456
103551
|
installingKit;
|
|
103457
103552
|
cachedVersion = undefined;
|
|
103458
103553
|
deletionPatterns = [];
|
|
103554
|
+
restoreCkHooks = false;
|
|
103459
103555
|
zombiePrunerHookDir = null;
|
|
103460
103556
|
setGlobalFlag(isGlobal) {
|
|
103461
103557
|
this.isGlobal = isGlobal;
|
|
@@ -103463,6 +103559,9 @@ class SettingsProcessor {
|
|
|
103463
103559
|
setForceOverwriteSettings(force) {
|
|
103464
103560
|
this.forceOverwriteSettings = force;
|
|
103465
103561
|
}
|
|
103562
|
+
setRestoreCkHooks(restore) {
|
|
103563
|
+
this.restoreCkHooks = restore;
|
|
103564
|
+
}
|
|
103466
103565
|
setProjectDir(dir) {
|
|
103467
103566
|
this.projectDir = dir;
|
|
103468
103567
|
this.initTracker();
|
|
@@ -103507,6 +103606,7 @@ class SettingsProcessor {
|
|
|
103507
103606
|
} else {
|
|
103508
103607
|
try {
|
|
103509
103608
|
const parsedSettings = JSON.parse(transformedSource);
|
|
103609
|
+
await this.applyDisabledHookConfig(parsedSettings);
|
|
103510
103610
|
this.logHookCommandRepair(this.fixHookCommandPaths(parsedSettings), "fresh install");
|
|
103511
103611
|
await SettingsMerger.writeSettingsFile(destFile, parsedSettings);
|
|
103512
103612
|
try {
|
|
@@ -103536,6 +103636,7 @@ class SettingsProcessor {
|
|
|
103536
103636
|
let sourceSettings;
|
|
103537
103637
|
try {
|
|
103538
103638
|
sourceSettings = JSON.parse(transformedSourceContent);
|
|
103639
|
+
await this.applyDisabledHookConfig(sourceSettings);
|
|
103539
103640
|
} catch {
|
|
103540
103641
|
logger.warning("Failed to parse source settings.json, falling back to overwrite");
|
|
103541
103642
|
const formattedContent = this.formatJsonContent(transformedSourceContent);
|
|
@@ -103558,10 +103659,15 @@ class SettingsProcessor {
|
|
|
103558
103659
|
if (this.tracker) {
|
|
103559
103660
|
installedSettings = await this.tracker.loadInstalledSettings();
|
|
103560
103661
|
}
|
|
103662
|
+
const mergeInstalledSettings = this.restoreCkHooks ? { ...installedSettings, hooks: [] } : installedSettings;
|
|
103561
103663
|
const mergeResult = SettingsMerger.merge(sourceSettings, destSettings, {
|
|
103562
|
-
installedSettings,
|
|
103664
|
+
installedSettings: mergeInstalledSettings,
|
|
103563
103665
|
sourceKit: this.installingKit
|
|
103564
103666
|
});
|
|
103667
|
+
await this.applyDisabledHookConfig(mergeResult.merged);
|
|
103668
|
+
if (this.restoreCkHooks) {
|
|
103669
|
+
logger.info("Restored CK hook registrations while respecting .ck.json hook disables");
|
|
103670
|
+
}
|
|
103565
103671
|
logger.verbose("Settings merge details", {
|
|
103566
103672
|
hooksAdded: mergeResult.hooksAdded,
|
|
103567
103673
|
hooksPreserved: mergeResult.hooksPreserved,
|
|
@@ -103591,7 +103697,8 @@ class SettingsProcessor {
|
|
|
103591
103697
|
logger.info(`Pruned ${hooksPruned} stale hook(s) referencing deleted files`);
|
|
103592
103698
|
}
|
|
103593
103699
|
if (this.zombiePrunerHookDir) {
|
|
103594
|
-
const
|
|
103700
|
+
const sourceHookCommands = this.collectHookCommands(sourceSettings);
|
|
103701
|
+
const { pruned: zombiePruned } = pruneZombieEngineerWirings(mergeResult.merged, this.zombiePrunerHookDir, sourceHookCommands);
|
|
103595
103702
|
if (zombiePruned.length > 0) {
|
|
103596
103703
|
logger.info(`Pruned ${zombiePruned.length} zombie hook entries: ${zombiePruned.join(", ")}`);
|
|
103597
103704
|
}
|
|
@@ -103600,6 +103707,111 @@ class SettingsProcessor {
|
|
|
103600
103707
|
logger.success("Merged settings.json (user customizations preserved)");
|
|
103601
103708
|
await this.injectTeamHooksIfSupported(destFile, mergeResult.merged);
|
|
103602
103709
|
}
|
|
103710
|
+
async getDisabledHookNames() {
|
|
103711
|
+
if (!this.projectDir) {
|
|
103712
|
+
return new Set;
|
|
103713
|
+
}
|
|
103714
|
+
const disabled = new Set;
|
|
103715
|
+
const addFromConfig = async (configPath) => {
|
|
103716
|
+
const names = await this.readDisabledHookNamesFromConfig(configPath);
|
|
103717
|
+
for (const name2 of names)
|
|
103718
|
+
disabled.add(name2);
|
|
103719
|
+
};
|
|
103720
|
+
try {
|
|
103721
|
+
if (this.isGlobal) {
|
|
103722
|
+
await addFromConfig(join109(this.projectDir, ".ck.json"));
|
|
103723
|
+
} else {
|
|
103724
|
+
await addFromConfig(join109(PathResolver.getGlobalKitDir(), ".ck.json"));
|
|
103725
|
+
await addFromConfig(join109(this.projectDir, ".claude", ".ck.json"));
|
|
103726
|
+
}
|
|
103727
|
+
} catch (error) {
|
|
103728
|
+
logger.debug(`Failed to load .ck.json hook preferences: ${error instanceof Error ? error.message : "unknown"}`);
|
|
103729
|
+
}
|
|
103730
|
+
return disabled;
|
|
103731
|
+
}
|
|
103732
|
+
async readDisabledHookNamesFromConfig(configPath) {
|
|
103733
|
+
if (!await import_fs_extra14.pathExists(configPath))
|
|
103734
|
+
return new Set;
|
|
103735
|
+
const raw2 = parseJsonContent(await import_fs_extra14.readFile(configPath, "utf-8"));
|
|
103736
|
+
const hooks = raw2.hooks;
|
|
103737
|
+
if (!hooks || typeof hooks !== "object")
|
|
103738
|
+
return new Set;
|
|
103739
|
+
return new Set(Object.entries(hooks).filter(([, enabled]) => enabled === false).map(([name2]) => name2));
|
|
103740
|
+
}
|
|
103741
|
+
async applyDisabledHookConfig(settings) {
|
|
103742
|
+
const disabledHooks = await this.getDisabledHookNames();
|
|
103743
|
+
if (disabledHooks.size === 0 || !settings.hooks)
|
|
103744
|
+
return 0;
|
|
103745
|
+
let removed = 0;
|
|
103746
|
+
const hooksRecord = settings.hooks;
|
|
103747
|
+
for (const [eventName, entries] of Object.entries(hooksRecord)) {
|
|
103748
|
+
const filteredEntries = [];
|
|
103749
|
+
for (const entry of entries) {
|
|
103750
|
+
if (Array.isArray(entry.hooks)) {
|
|
103751
|
+
const keptHooks = entry.hooks.filter((hook) => {
|
|
103752
|
+
const command = typeof hook.command === "string" ? hook.command : "";
|
|
103753
|
+
const hookName = this.extractCkHookName(command);
|
|
103754
|
+
if (hookName && disabledHooks.has(hookName)) {
|
|
103755
|
+
removed++;
|
|
103756
|
+
return false;
|
|
103757
|
+
}
|
|
103758
|
+
return true;
|
|
103759
|
+
});
|
|
103760
|
+
if (keptHooks.length > 0) {
|
|
103761
|
+
filteredEntries.push({ ...entry, hooks: keptHooks });
|
|
103762
|
+
}
|
|
103763
|
+
} else {
|
|
103764
|
+
const command = typeof entry.command === "string" ? entry.command : "";
|
|
103765
|
+
const hookName = this.extractCkHookName(command);
|
|
103766
|
+
if (hookName && disabledHooks.has(hookName)) {
|
|
103767
|
+
removed++;
|
|
103768
|
+
continue;
|
|
103769
|
+
}
|
|
103770
|
+
filteredEntries.push(entry);
|
|
103771
|
+
}
|
|
103772
|
+
}
|
|
103773
|
+
if (filteredEntries.length > 0) {
|
|
103774
|
+
hooksRecord[eventName] = filteredEntries;
|
|
103775
|
+
} else {
|
|
103776
|
+
delete hooksRecord[eventName];
|
|
103777
|
+
}
|
|
103778
|
+
}
|
|
103779
|
+
if (Object.keys(hooksRecord).length === 0) {
|
|
103780
|
+
settings.hooks = undefined;
|
|
103781
|
+
}
|
|
103782
|
+
if (removed > 0) {
|
|
103783
|
+
logger.info(`Skipped ${removed} hook registration(s) disabled in .ck.json`);
|
|
103784
|
+
}
|
|
103785
|
+
return removed;
|
|
103786
|
+
}
|
|
103787
|
+
collectHookCommands(settings) {
|
|
103788
|
+
const commands = new Set;
|
|
103789
|
+
if (!settings.hooks)
|
|
103790
|
+
return commands;
|
|
103791
|
+
for (const entries of Object.values(settings.hooks)) {
|
|
103792
|
+
for (const entry of entries) {
|
|
103793
|
+
if ("hooks" in entry && Array.isArray(entry.hooks)) {
|
|
103794
|
+
for (const hook of entry.hooks) {
|
|
103795
|
+
if (typeof hook.command === "string") {
|
|
103796
|
+
commands.add(hook.command);
|
|
103797
|
+
}
|
|
103798
|
+
}
|
|
103799
|
+
continue;
|
|
103800
|
+
}
|
|
103801
|
+
if ("command" in entry && typeof entry.command === "string") {
|
|
103802
|
+
commands.add(entry.command);
|
|
103803
|
+
}
|
|
103804
|
+
}
|
|
103805
|
+
}
|
|
103806
|
+
return commands;
|
|
103807
|
+
}
|
|
103808
|
+
extractCkHookName(command) {
|
|
103809
|
+
if (!command.trim().startsWith("node "))
|
|
103810
|
+
return null;
|
|
103811
|
+
const normalized = command.replace(/\\/g, "/");
|
|
103812
|
+
const match2 = normalized.match(/\/hooks\/([^/"'\s]+)\.(?:cjs|mjs|js)(?:["'\s]|$)/);
|
|
103813
|
+
return match2?.[1] ?? null;
|
|
103814
|
+
}
|
|
103603
103815
|
migrateDeprecatedMatchers(destSettings, sourceSettings) {
|
|
103604
103816
|
if (!destSettings.hooks || !sourceSettings.hooks)
|
|
103605
103817
|
return;
|
|
@@ -103992,6 +104204,9 @@ class CopyExecutor {
|
|
|
103992
104204
|
setForceOverwriteSettings(force) {
|
|
103993
104205
|
this.settingsProcessor.setForceOverwriteSettings(force);
|
|
103994
104206
|
}
|
|
104207
|
+
setRestoreCkHooks(restore) {
|
|
104208
|
+
this.settingsProcessor.setRestoreCkHooks(restore);
|
|
104209
|
+
}
|
|
103995
104210
|
setDeletions(deletions) {
|
|
103996
104211
|
this.settingsProcessor.setDeletions(deletions);
|
|
103997
104212
|
}
|
|
@@ -104164,6 +104379,9 @@ class FileMerger {
|
|
|
104164
104379
|
setForceOverwriteSettings(force) {
|
|
104165
104380
|
this.copyExecutor.setForceOverwriteSettings(force);
|
|
104166
104381
|
}
|
|
104382
|
+
setRestoreCkHooks(restore) {
|
|
104383
|
+
this.copyExecutor.setRestoreCkHooks(restore);
|
|
104384
|
+
}
|
|
104167
104385
|
setProjectDir(dir) {
|
|
104168
104386
|
this.copyExecutor.setProjectDir(dir);
|
|
104169
104387
|
}
|
|
@@ -105487,6 +105705,7 @@ async function handleMerge(ctx) {
|
|
|
105487
105705
|
}
|
|
105488
105706
|
merger.setGlobalFlag(ctx.options.global);
|
|
105489
105707
|
merger.setForceOverwriteSettings(ctx.options.forceOverwriteSettings);
|
|
105708
|
+
merger.setRestoreCkHooks(ctx.options.restoreCkHooks);
|
|
105490
105709
|
merger.setProjectDir(ctx.resolvedDir);
|
|
105491
105710
|
merger.setKitName(ctx.kit.name);
|
|
105492
105711
|
merger.setZombiePrunerHookDir(join120(ctx.claudeDir, "hooks"));
|
|
@@ -106974,6 +107193,7 @@ async function resolveOptions(ctx) {
|
|
|
106974
107193
|
skipSetup: parsed.skipSetup ?? false,
|
|
106975
107194
|
forceOverwrite: parsed.forceOverwrite ?? false,
|
|
106976
107195
|
forceOverwriteSettings: parsed.forceOverwriteSettings ?? false,
|
|
107196
|
+
restoreCkHooks: parsed.restoreCkHooks ?? false,
|
|
106977
107197
|
dryRun: parsed.dryRun ?? false,
|
|
106978
107198
|
prefix: parsed.prefix ?? false,
|
|
106979
107199
|
sync: parsed.sync ?? false,
|
|
@@ -108016,7 +108236,7 @@ async function handleSelection(ctx) {
|
|
|
108016
108236
|
logger.info("--force has no effect without --yes (the version-match skip only applies in non-interactive mode)");
|
|
108017
108237
|
}
|
|
108018
108238
|
const releaseTag = release?.tag_name;
|
|
108019
|
-
if (ctx.options.yes && !ctx.options.fresh && !ctx.options.force && releaseTag && !isOfflineMode && !pendingKits?.length) {
|
|
108239
|
+
if (ctx.options.yes && !ctx.options.fresh && !ctx.options.force && !ctx.options.restoreCkHooks && releaseTag && !isOfflineMode && !pendingKits?.length) {
|
|
108020
108240
|
try {
|
|
108021
108241
|
const prefix = PathResolver.getPathPrefix(ctx.options.global);
|
|
108022
108242
|
const claudeDir3 = prefix ? join134(resolvedDir, prefix) : resolvedDir;
|
|
@@ -108990,6 +109210,7 @@ function createInitContext(rawOptions, prompts) {
|
|
|
108990
109210
|
skipSetup: false,
|
|
108991
109211
|
forceOverwrite: false,
|
|
108992
109212
|
forceOverwriteSettings: false,
|
|
109213
|
+
restoreCkHooks: false,
|
|
108993
109214
|
dryRun: false,
|
|
108994
109215
|
prefix: false,
|
|
108995
109216
|
sync: false,
|
|
@@ -113083,6 +113304,7 @@ async function handleKitSelection(ctx) {
|
|
|
113083
113304
|
dryRun: false,
|
|
113084
113305
|
forceOverwrite: false,
|
|
113085
113306
|
forceOverwriteSettings: false,
|
|
113307
|
+
restoreCkHooks: false,
|
|
113086
113308
|
skipSetup: true,
|
|
113087
113309
|
refresh: false,
|
|
113088
113310
|
sync: false,
|
|
@@ -116437,7 +116659,7 @@ function registerCommands(cli) {
|
|
|
116437
116659
|
}
|
|
116438
116660
|
await newCommand(options2);
|
|
116439
116661
|
});
|
|
116440
|
-
cli.command("init", "Initialize or update ClaudeKit project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to use: engineer, marketing, all, or comma-separated").option("-r, --release <version>", "Skip version selection, use specific version (e.g., latest, v1.0.0)").option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)").option("--only <pattern>", "Include only files matching glob pattern (can be used multiple times)").option("-g, --global", "Use platform-specific user configuration directory").option("--fresh", "Full reset: remove CK files, replace settings.json and CLAUDE.md, reinstall from scratch").option("--force", "Force reinstall even if already at latest version (use with --yes; re-onboards missing files without full reset)").option("--install-skills", "Install skills dependencies (non-interactive mode)").option("--with-sudo", "Include system packages requiring sudo (Linux: ffmpeg, imagemagick)").option("--prefix", "Add /ck: prefix to all slash commands by moving them to commands/ck/ subdirectory").option("--beta", "Show beta versions in selection prompt").option("--refresh", "Bypass release cache to fetch latest versions from GitHub").option("--dry-run", "Preview changes without applying them (requires --prefix)").option("--force-overwrite", "Override ownership protections and delete user-modified files (requires --prefix)").option("--force-overwrite-settings", "Fully replace settings.json instead of selective merge (destroys user customizations)").option("--skip-setup", "Skip interactive configuration wizard").option("--docs-dir <name>", "Custom docs folder name (default: docs)").option("--plans-dir <name>", "Custom plans folder name (default: plans)").option("-y, --yes", "Non-interactive mode with sensible defaults (skip all prompts)").option("--sync", "Sync config files from upstream with interactive hunk-by-hunk merge").option("--use-git", "Use git clone instead of GitHub API (uses SSH/HTTPS credentials)").option("--archive <path>", "Use local archive file instead of downloading (zip/tar.gz)").option("--kit-path <path>", "Use local kit directory instead of downloading").action(async (options2) => {
|
|
116662
|
+
cli.command("init", "Initialize or update ClaudeKit project (with interactive version selection)").option("--dir <dir>", "Target directory (default: .)").option("--kit <kit>", "Kit to use: engineer, marketing, all, or comma-separated").option("-r, --release <version>", "Skip version selection, use specific version (e.g., latest, v1.0.0)").option("--exclude <pattern>", "Exclude files matching glob pattern (can be used multiple times)").option("--only <pattern>", "Include only files matching glob pattern (can be used multiple times)").option("-g, --global", "Use platform-specific user configuration directory").option("--fresh", "Full reset: remove CK files, replace settings.json and CLAUDE.md, reinstall from scratch").option("--force", "Force reinstall even if already at latest version (use with --yes; re-onboards missing files without full reset)").option("--install-skills", "Install skills dependencies (non-interactive mode)").option("--with-sudo", "Include system packages requiring sudo (Linux: ffmpeg, imagemagick)").option("--prefix", "Add /ck: prefix to all slash commands by moving them to commands/ck/ subdirectory").option("--beta", "Show beta versions in selection prompt").option("--refresh", "Bypass release cache to fetch latest versions from GitHub").option("--dry-run", "Preview changes without applying them (requires --prefix)").option("--force-overwrite", "Override ownership protections and delete user-modified files (requires --prefix)").option("--force-overwrite-settings", "Fully replace settings.json instead of selective merge (destroys user customizations)").option("--restore-ck-hooks", "Restore CK-managed hook registrations during update self-heal").option("--skip-setup", "Skip interactive configuration wizard").option("--docs-dir <name>", "Custom docs folder name (default: docs)").option("--plans-dir <name>", "Custom plans folder name (default: plans)").option("-y, --yes", "Non-interactive mode with sensible defaults (skip all prompts)").option("--sync", "Sync config files from upstream with interactive hunk-by-hunk merge").option("--use-git", "Use git clone instead of GitHub API (uses SSH/HTTPS credentials)").option("--archive <path>", "Use local archive file instead of downloading (zip/tar.gz)").option("--kit-path <path>", "Use local kit directory instead of downloading").action(async (options2) => {
|
|
116441
116663
|
if (options2.exclude && !Array.isArray(options2.exclude)) {
|
|
116442
116664
|
options2.exclude = [options2.exclude];
|
|
116443
116665
|
}
|