claudekit-cli 3.35.0 → 3.36.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1643 -732
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -38479,7 +38479,10 @@ var init_metadata = __esm(() => {
|
|
|
38479
38479
|
files: exports_external.array(TrackedFileSchema).optional(),
|
|
38480
38480
|
lastUpdateCheck: exports_external.string().optional(),
|
|
38481
38481
|
dismissedVersion: exports_external.string().optional(),
|
|
38482
|
-
installedSettings: InstalledSettingsSchema.optional()
|
|
38482
|
+
installedSettings: InstalledSettingsSchema.optional(),
|
|
38483
|
+
pluginInstalled: exports_external.boolean().optional(),
|
|
38484
|
+
pluginInstalledAt: exports_external.string().optional(),
|
|
38485
|
+
pluginVersion: exports_external.string().optional()
|
|
38483
38486
|
});
|
|
38484
38487
|
MultiKitMetadataSchema = exports_external.object({
|
|
38485
38488
|
kits: exports_external.record(KitType, KitMetadataSchema).optional(),
|
|
@@ -53920,6 +53923,17 @@ var init_package_manager_detector = __esm(() => {
|
|
|
53920
53923
|
});
|
|
53921
53924
|
|
|
53922
53925
|
// src/domains/migration/metadata-migration.ts
|
|
53926
|
+
var exports_metadata_migration = {};
|
|
53927
|
+
__export(exports_metadata_migration, {
|
|
53928
|
+
updateKitPluginState: () => updateKitPluginState,
|
|
53929
|
+
needsMigration: () => needsMigration,
|
|
53930
|
+
migrateToMultiKit: () => migrateToMultiKit,
|
|
53931
|
+
getTrackedFilesForKit: () => getTrackedFilesForKit,
|
|
53932
|
+
getKitMetadata: () => getKitMetadata,
|
|
53933
|
+
getInstalledKits: () => getInstalledKits,
|
|
53934
|
+
getAllTrackedFiles: () => getAllTrackedFiles,
|
|
53935
|
+
detectMetadataFormat: () => detectMetadataFormat
|
|
53936
|
+
});
|
|
53923
53937
|
import { join as join35 } from "node:path";
|
|
53924
53938
|
async function detectMetadataFormat(claudeDir2) {
|
|
53925
53939
|
const metadataPath = join35(claudeDir2, "metadata.json");
|
|
@@ -53956,6 +53970,9 @@ async function detectMetadataFormat(claudeDir2) {
|
|
|
53956
53970
|
return { format: "none", metadata: null, detectedKit: null };
|
|
53957
53971
|
}
|
|
53958
53972
|
}
|
|
53973
|
+
function needsMigration(detection) {
|
|
53974
|
+
return detection.format === "legacy";
|
|
53975
|
+
}
|
|
53959
53976
|
async function migrateToMultiKit(claudeDir2) {
|
|
53960
53977
|
const detection = await detectMetadataFormat(claudeDir2);
|
|
53961
53978
|
if (detection.format === "multi-kit") {
|
|
@@ -54079,6 +54096,41 @@ function getInstalledKits(metadata) {
|
|
|
54079
54096
|
}
|
|
54080
54097
|
return [];
|
|
54081
54098
|
}
|
|
54099
|
+
async function updateKitPluginState(claudeDir2, kit, state) {
|
|
54100
|
+
const metadataPath = join35(claudeDir2, "metadata.json");
|
|
54101
|
+
if (!await import_fs_extra3.pathExists(metadataPath)) {
|
|
54102
|
+
logger.debug("No metadata.json found — skipping plugin state update");
|
|
54103
|
+
return;
|
|
54104
|
+
}
|
|
54105
|
+
try {
|
|
54106
|
+
let metadata = {};
|
|
54107
|
+
try {
|
|
54108
|
+
const raw = await import_fs_extra3.readFile(metadataPath, "utf-8");
|
|
54109
|
+
const parsed = JSON.parse(raw);
|
|
54110
|
+
if (parsed && typeof parsed === "object") {
|
|
54111
|
+
metadata = parsed;
|
|
54112
|
+
} else {
|
|
54113
|
+
logger.warning("metadata.json is not an object; rebuilding minimal plugin metadata structure");
|
|
54114
|
+
}
|
|
54115
|
+
} catch (error) {
|
|
54116
|
+
logger.warning(`metadata.json unreadable during plugin state update; rebuilding: ${error}`);
|
|
54117
|
+
}
|
|
54118
|
+
if (!metadata.kits || typeof metadata.kits !== "object") {
|
|
54119
|
+
metadata.kits = {};
|
|
54120
|
+
}
|
|
54121
|
+
const existingKitState = metadata.kits[kit];
|
|
54122
|
+
metadata.kits[kit] = {
|
|
54123
|
+
...existingKitState || {},
|
|
54124
|
+
version: existingKitState?.version ?? "",
|
|
54125
|
+
installedAt: existingKitState?.installedAt ?? state.pluginInstalledAt,
|
|
54126
|
+
...state
|
|
54127
|
+
};
|
|
54128
|
+
await import_fs_extra3.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
54129
|
+
logger.debug(`Plugin state saved: ${state.pluginInstalled ? "installed" : "failed"}`);
|
|
54130
|
+
} catch (error) {
|
|
54131
|
+
logger.debug(`Failed to update plugin state: ${error}`);
|
|
54132
|
+
}
|
|
54133
|
+
}
|
|
54082
54134
|
var import_fs_extra3;
|
|
54083
54135
|
var init_metadata_migration = __esm(() => {
|
|
54084
54136
|
init_logger();
|
|
@@ -54314,7 +54366,7 @@ var package_default;
|
|
|
54314
54366
|
var init_package = __esm(() => {
|
|
54315
54367
|
package_default = {
|
|
54316
54368
|
name: "claudekit-cli",
|
|
54317
|
-
version: "3.
|
|
54369
|
+
version: "3.36.0-dev.1",
|
|
54318
54370
|
description: "CLI tool for bootstrapping and updating ClaudeKit projects",
|
|
54319
54371
|
type: "module",
|
|
54320
54372
|
repository: {
|
|
@@ -61503,7 +61555,7 @@ var require_picomatch2 = __commonJS((exports, module) => {
|
|
|
61503
61555
|
import { exec as exec7, execFile as execFile7, spawn as spawn3 } from "node:child_process";
|
|
61504
61556
|
import { promisify as promisify13 } from "node:util";
|
|
61505
61557
|
function executeInteractiveScript(command, args, options2) {
|
|
61506
|
-
return new Promise((
|
|
61558
|
+
return new Promise((resolve14, reject) => {
|
|
61507
61559
|
const child = spawn3(command, args, {
|
|
61508
61560
|
stdio: ["ignore", "inherit", "inherit"],
|
|
61509
61561
|
cwd: options2?.cwd,
|
|
@@ -61524,7 +61576,7 @@ function executeInteractiveScript(command, args, options2) {
|
|
|
61524
61576
|
} else if (code !== 0) {
|
|
61525
61577
|
reject(new Error(`Command exited with code ${code}`));
|
|
61526
61578
|
} else {
|
|
61527
|
-
|
|
61579
|
+
resolve14();
|
|
61528
61580
|
}
|
|
61529
61581
|
});
|
|
61530
61582
|
child.on("error", (error) => {
|
|
@@ -61545,7 +61597,7 @@ var init_process_executor = __esm(() => {
|
|
|
61545
61597
|
});
|
|
61546
61598
|
|
|
61547
61599
|
// src/services/package-installer/validators.ts
|
|
61548
|
-
import { resolve as
|
|
61600
|
+
import { resolve as resolve14 } from "node:path";
|
|
61549
61601
|
function validatePackageName(packageName) {
|
|
61550
61602
|
if (!packageName || typeof packageName !== "string") {
|
|
61551
61603
|
throw new Error("Package name must be a non-empty string");
|
|
@@ -61558,8 +61610,8 @@ function validatePackageName(packageName) {
|
|
|
61558
61610
|
}
|
|
61559
61611
|
}
|
|
61560
61612
|
function validateScriptPath(skillsDir2, scriptPath) {
|
|
61561
|
-
const skillsDirResolved =
|
|
61562
|
-
const scriptPathResolved =
|
|
61613
|
+
const skillsDirResolved = resolve14(skillsDir2);
|
|
61614
|
+
const scriptPathResolved = resolve14(scriptPath);
|
|
61563
61615
|
const skillsDirNormalized = isWindows() ? skillsDirResolved.toLowerCase() : skillsDirResolved;
|
|
61564
61616
|
const scriptPathNormalized = isWindows() ? scriptPathResolved.toLowerCase() : scriptPathResolved;
|
|
61565
61617
|
if (!scriptPathNormalized.startsWith(skillsDirNormalized)) {
|
|
@@ -61746,7 +61798,7 @@ var init_gemini_installer = __esm(() => {
|
|
|
61746
61798
|
});
|
|
61747
61799
|
|
|
61748
61800
|
// src/services/package-installer/opencode-installer.ts
|
|
61749
|
-
import { join as
|
|
61801
|
+
import { join as join58 } from "node:path";
|
|
61750
61802
|
async function isOpenCodeInstalled() {
|
|
61751
61803
|
try {
|
|
61752
61804
|
await execAsync7("opencode --version", { timeout: 5000 });
|
|
@@ -61769,7 +61821,7 @@ async function installOpenCode() {
|
|
|
61769
61821
|
logger.info(`Installing ${displayName}...`);
|
|
61770
61822
|
const { unlink: unlink11 } = await import("node:fs/promises");
|
|
61771
61823
|
const { tmpdir: tmpdir4 } = await import("node:os");
|
|
61772
|
-
const tempScriptPath =
|
|
61824
|
+
const tempScriptPath = join58(tmpdir4(), "opencode-install.sh");
|
|
61773
61825
|
try {
|
|
61774
61826
|
logger.info("Downloading OpenCode installation script...");
|
|
61775
61827
|
await execFileAsync5("curl", ["-fsSL", "https://opencode.ai/install", "-o", tempScriptPath], {
|
|
@@ -61821,7 +61873,7 @@ var PARTIAL_INSTALL_VERSION = "partial", EXIT_CODE_CRITICAL_FAILURE = 1, EXIT_CO
|
|
|
61821
61873
|
|
|
61822
61874
|
// src/services/package-installer/install-error-handler.ts
|
|
61823
61875
|
import { existsSync as existsSync44, readFileSync as readFileSync9, unlinkSync as unlinkSync2 } from "node:fs";
|
|
61824
|
-
import { join as
|
|
61876
|
+
import { join as join59 } from "node:path";
|
|
61825
61877
|
function parseNameReason(str2) {
|
|
61826
61878
|
const colonIndex = str2.indexOf(":");
|
|
61827
61879
|
if (colonIndex === -1) {
|
|
@@ -61830,7 +61882,7 @@ function parseNameReason(str2) {
|
|
|
61830
61882
|
return [str2.slice(0, colonIndex).trim(), str2.slice(colonIndex + 1).trim()];
|
|
61831
61883
|
}
|
|
61832
61884
|
function displayInstallErrors(skillsDir2) {
|
|
61833
|
-
const summaryPath =
|
|
61885
|
+
const summaryPath = join59(skillsDir2, ".install-error-summary.json");
|
|
61834
61886
|
if (!existsSync44(summaryPath)) {
|
|
61835
61887
|
logger.error("Skills installation failed. Run with --verbose for details.");
|
|
61836
61888
|
return;
|
|
@@ -61921,7 +61973,7 @@ async function checkNeedsSudoPackages() {
|
|
|
61921
61973
|
}
|
|
61922
61974
|
}
|
|
61923
61975
|
function hasInstallState(skillsDir2) {
|
|
61924
|
-
const stateFilePath =
|
|
61976
|
+
const stateFilePath = join59(skillsDir2, ".install-state.json");
|
|
61925
61977
|
return existsSync44(stateFilePath);
|
|
61926
61978
|
}
|
|
61927
61979
|
var WHICH_COMMAND_TIMEOUT_MS = 5000;
|
|
@@ -61930,7 +61982,7 @@ var init_install_error_handler = __esm(() => {
|
|
|
61930
61982
|
});
|
|
61931
61983
|
|
|
61932
61984
|
// src/services/package-installer/skills-installer.ts
|
|
61933
|
-
import { join as
|
|
61985
|
+
import { join as join60 } from "node:path";
|
|
61934
61986
|
async function installSkillsDependencies(skillsDir2, options2 = {}) {
|
|
61935
61987
|
const { skipConfirm = false, withSudo = false } = options2;
|
|
61936
61988
|
const displayName = "Skills Dependencies";
|
|
@@ -61956,7 +62008,7 @@ async function installSkillsDependencies(skillsDir2, options2 = {}) {
|
|
|
61956
62008
|
const clack = await Promise.resolve().then(() => (init_dist2(), exports_dist));
|
|
61957
62009
|
const platform7 = process.platform;
|
|
61958
62010
|
const scriptName = platform7 === "win32" ? "install.ps1" : "install.sh";
|
|
61959
|
-
const scriptPath =
|
|
62011
|
+
const scriptPath = join60(skillsDir2, scriptName);
|
|
61960
62012
|
try {
|
|
61961
62013
|
validateScriptPath(skillsDir2, scriptPath);
|
|
61962
62014
|
} catch (error) {
|
|
@@ -61972,7 +62024,7 @@ async function installSkillsDependencies(skillsDir2, options2 = {}) {
|
|
|
61972
62024
|
logger.warning(`Skills installation script not found: ${scriptPath}`);
|
|
61973
62025
|
logger.info("");
|
|
61974
62026
|
logger.info("\uD83D\uDCD6 Manual Installation Instructions:");
|
|
61975
|
-
logger.info(` See: ${
|
|
62027
|
+
logger.info(` See: ${join60(skillsDir2, "INSTALLATION.md")}`);
|
|
61976
62028
|
logger.info("");
|
|
61977
62029
|
logger.info("Quick start:");
|
|
61978
62030
|
logger.info(" cd .claude/skills/ai-multimodal/scripts");
|
|
@@ -62019,7 +62071,7 @@ async function installSkillsDependencies(skillsDir2, options2 = {}) {
|
|
|
62019
62071
|
logger.info(` ${platform7 === "win32" ? `powershell -File "${scriptPath}"` : `bash ${scriptPath}`}`);
|
|
62020
62072
|
logger.info("");
|
|
62021
62073
|
logger.info("Or see complete guide:");
|
|
62022
|
-
logger.info(` ${
|
|
62074
|
+
logger.info(` ${join60(skillsDir2, "INSTALLATION.md")}`);
|
|
62023
62075
|
return {
|
|
62024
62076
|
success: false,
|
|
62025
62077
|
package: displayName,
|
|
@@ -62140,7 +62192,7 @@ async function installSkillsDependencies(skillsDir2, options2 = {}) {
|
|
|
62140
62192
|
logger.info("\uD83D\uDCD6 Manual Installation Instructions:");
|
|
62141
62193
|
logger.info("");
|
|
62142
62194
|
logger.info("See complete guide:");
|
|
62143
|
-
logger.info(` cat ${
|
|
62195
|
+
logger.info(` cat ${join60(skillsDir2, "INSTALLATION.md")}`);
|
|
62144
62196
|
logger.info("");
|
|
62145
62197
|
logger.info("Quick start:");
|
|
62146
62198
|
logger.info(" cd .claude/skills/ai-multimodal/scripts");
|
|
@@ -62186,7 +62238,7 @@ var init_skills_installer2 = __esm(() => {
|
|
|
62186
62238
|
// src/services/package-installer/gemini-mcp/config-manager.ts
|
|
62187
62239
|
import { existsSync as existsSync45 } from "node:fs";
|
|
62188
62240
|
import { mkdir as mkdir17, readFile as readFile35, writeFile as writeFile20 } from "node:fs/promises";
|
|
62189
|
-
import { dirname as
|
|
62241
|
+
import { dirname as dirname16, join as join61 } from "node:path";
|
|
62190
62242
|
async function readJsonFile(filePath) {
|
|
62191
62243
|
try {
|
|
62192
62244
|
const content = await readFile35(filePath, "utf-8");
|
|
@@ -62198,7 +62250,7 @@ async function readJsonFile(filePath) {
|
|
|
62198
62250
|
}
|
|
62199
62251
|
}
|
|
62200
62252
|
async function addGeminiToGitignore(projectDir) {
|
|
62201
|
-
const gitignorePath =
|
|
62253
|
+
const gitignorePath = join61(projectDir, ".gitignore");
|
|
62202
62254
|
const geminiPattern = ".gemini/";
|
|
62203
62255
|
try {
|
|
62204
62256
|
let content = "";
|
|
@@ -62226,7 +62278,7 @@ ${geminiPattern}
|
|
|
62226
62278
|
}
|
|
62227
62279
|
}
|
|
62228
62280
|
async function createNewSettingsWithMerge(geminiSettingsPath, mcpConfigPath) {
|
|
62229
|
-
const linkDir =
|
|
62281
|
+
const linkDir = dirname16(geminiSettingsPath);
|
|
62230
62282
|
if (!existsSync45(linkDir)) {
|
|
62231
62283
|
await mkdir17(linkDir, { recursive: true });
|
|
62232
62284
|
logger.debug(`Created directory: ${linkDir}`);
|
|
@@ -62290,12 +62342,12 @@ var init_config_manager2 = __esm(() => {
|
|
|
62290
62342
|
// src/services/package-installer/gemini-mcp/validation.ts
|
|
62291
62343
|
import { existsSync as existsSync46, lstatSync, readlinkSync } from "node:fs";
|
|
62292
62344
|
import { homedir as homedir27 } from "node:os";
|
|
62293
|
-
import { join as
|
|
62345
|
+
import { join as join62 } from "node:path";
|
|
62294
62346
|
function getGlobalMcpConfigPath() {
|
|
62295
|
-
return
|
|
62347
|
+
return join62(homedir27(), ".claude", ".mcp.json");
|
|
62296
62348
|
}
|
|
62297
62349
|
function getLocalMcpConfigPath(projectDir) {
|
|
62298
|
-
return
|
|
62350
|
+
return join62(projectDir, ".mcp.json");
|
|
62299
62351
|
}
|
|
62300
62352
|
function findMcpConfigPath(projectDir) {
|
|
62301
62353
|
const localPath = getLocalMcpConfigPath(projectDir);
|
|
@@ -62313,9 +62365,9 @@ function findMcpConfigPath(projectDir) {
|
|
|
62313
62365
|
}
|
|
62314
62366
|
function getGeminiSettingsPath(projectDir, isGlobal) {
|
|
62315
62367
|
if (isGlobal) {
|
|
62316
|
-
return
|
|
62368
|
+
return join62(homedir27(), ".gemini", "settings.json");
|
|
62317
62369
|
}
|
|
62318
|
-
return
|
|
62370
|
+
return join62(projectDir, ".gemini", "settings.json");
|
|
62319
62371
|
}
|
|
62320
62372
|
function checkExistingGeminiConfig(projectDir, isGlobal = false) {
|
|
62321
62373
|
const geminiSettingsPath = getGeminiSettingsPath(projectDir, isGlobal);
|
|
@@ -62345,9 +62397,9 @@ var init_validation = __esm(() => {
|
|
|
62345
62397
|
// src/services/package-installer/gemini-mcp/linker-core.ts
|
|
62346
62398
|
import { existsSync as existsSync47 } from "node:fs";
|
|
62347
62399
|
import { mkdir as mkdir18, symlink as symlink2 } from "node:fs/promises";
|
|
62348
|
-
import { dirname as
|
|
62400
|
+
import { dirname as dirname17, join as join63 } from "node:path";
|
|
62349
62401
|
async function createSymlink(targetPath, linkPath, projectDir, isGlobal) {
|
|
62350
|
-
const linkDir =
|
|
62402
|
+
const linkDir = dirname17(linkPath);
|
|
62351
62403
|
if (!existsSync47(linkDir)) {
|
|
62352
62404
|
await mkdir18(linkDir, { recursive: true });
|
|
62353
62405
|
logger.debug(`Created directory: ${linkDir}`);
|
|
@@ -62356,7 +62408,7 @@ async function createSymlink(targetPath, linkPath, projectDir, isGlobal) {
|
|
|
62356
62408
|
if (isGlobal) {
|
|
62357
62409
|
symlinkTarget = getGlobalMcpConfigPath();
|
|
62358
62410
|
} else {
|
|
62359
|
-
const localMcpPath =
|
|
62411
|
+
const localMcpPath = join63(projectDir, ".mcp.json");
|
|
62360
62412
|
const isLocalConfig = targetPath === localMcpPath;
|
|
62361
62413
|
symlinkTarget = isLocalConfig ? "../.mcp.json" : targetPath;
|
|
62362
62414
|
}
|
|
@@ -62389,10 +62441,10 @@ __export(exports_gemini_mcp_linker, {
|
|
|
62389
62441
|
checkExistingGeminiConfig: () => checkExistingGeminiConfig,
|
|
62390
62442
|
addGeminiToGitignore: () => addGeminiToGitignore
|
|
62391
62443
|
});
|
|
62392
|
-
import { resolve as
|
|
62444
|
+
import { resolve as resolve15 } from "node:path";
|
|
62393
62445
|
async function linkGeminiMcpConfig(projectDir, options2 = {}) {
|
|
62394
62446
|
const { skipGitignore = false, isGlobal = false } = options2;
|
|
62395
|
-
const resolvedProjectDir =
|
|
62447
|
+
const resolvedProjectDir = resolve15(projectDir);
|
|
62396
62448
|
const geminiSettingsPath = getGeminiSettingsPath(resolvedProjectDir, isGlobal);
|
|
62397
62449
|
const mcpConfigPath = findMcpConfigPath(resolvedProjectDir);
|
|
62398
62450
|
if (!mcpConfigPath) {
|
|
@@ -63040,7 +63092,7 @@ var require_get_stream = __commonJS((exports, module) => {
|
|
|
63040
63092
|
};
|
|
63041
63093
|
const { maxBuffer } = options2;
|
|
63042
63094
|
let stream;
|
|
63043
|
-
await new Promise((
|
|
63095
|
+
await new Promise((resolve17, reject) => {
|
|
63044
63096
|
const rejectPromise = (error) => {
|
|
63045
63097
|
if (error && stream.getBufferedLength() <= BufferConstants.MAX_LENGTH) {
|
|
63046
63098
|
error.bufferedData = stream.getBufferedValue();
|
|
@@ -63052,7 +63104,7 @@ var require_get_stream = __commonJS((exports, module) => {
|
|
|
63052
63104
|
rejectPromise(error);
|
|
63053
63105
|
return;
|
|
63054
63106
|
}
|
|
63055
|
-
|
|
63107
|
+
resolve17();
|
|
63056
63108
|
});
|
|
63057
63109
|
stream.on("data", () => {
|
|
63058
63110
|
if (stream.getBufferedLength() > maxBuffer) {
|
|
@@ -64413,7 +64465,7 @@ var require_extract_zip = __commonJS((exports, module) => {
|
|
|
64413
64465
|
debug("opening", this.zipPath, "with opts", this.opts);
|
|
64414
64466
|
this.zipfile = await openZip(this.zipPath, { lazyEntries: true });
|
|
64415
64467
|
this.canceled = false;
|
|
64416
|
-
return new Promise((
|
|
64468
|
+
return new Promise((resolve17, reject) => {
|
|
64417
64469
|
this.zipfile.on("error", (err) => {
|
|
64418
64470
|
this.canceled = true;
|
|
64419
64471
|
reject(err);
|
|
@@ -64422,7 +64474,7 @@ var require_extract_zip = __commonJS((exports, module) => {
|
|
|
64422
64474
|
this.zipfile.on("close", () => {
|
|
64423
64475
|
if (!this.canceled) {
|
|
64424
64476
|
debug("zip extraction complete");
|
|
64425
|
-
|
|
64477
|
+
resolve17();
|
|
64426
64478
|
}
|
|
64427
64479
|
});
|
|
64428
64480
|
this.zipfile.on("entry", async (entry) => {
|
|
@@ -64529,6 +64581,412 @@ var require_extract_zip = __commonJS((exports, module) => {
|
|
|
64529
64581
|
};
|
|
64530
64582
|
});
|
|
64531
64583
|
|
|
64584
|
+
// src/services/file-operations/manifest/manifest-reader.ts
|
|
64585
|
+
import { join as join74 } from "node:path";
|
|
64586
|
+
async function readManifest(claudeDir2) {
|
|
64587
|
+
const metadataPath = join74(claudeDir2, "metadata.json");
|
|
64588
|
+
if (!await import_fs_extra8.pathExists(metadataPath)) {
|
|
64589
|
+
return null;
|
|
64590
|
+
}
|
|
64591
|
+
try {
|
|
64592
|
+
const content = await import_fs_extra8.readFile(metadataPath, "utf-8");
|
|
64593
|
+
const parsed = JSON.parse(content);
|
|
64594
|
+
return MetadataSchema.parse(parsed);
|
|
64595
|
+
} catch (error) {
|
|
64596
|
+
logger.debug(`Failed to read manifest: ${error}`);
|
|
64597
|
+
return null;
|
|
64598
|
+
}
|
|
64599
|
+
}
|
|
64600
|
+
async function readKitManifest(claudeDir2, kit) {
|
|
64601
|
+
const metadata = await readManifest(claudeDir2);
|
|
64602
|
+
if (!metadata)
|
|
64603
|
+
return null;
|
|
64604
|
+
return getKitMetadata(metadata, kit);
|
|
64605
|
+
}
|
|
64606
|
+
async function findFileInInstalledKits(claudeDir2, relativePath, excludeKit) {
|
|
64607
|
+
const metadata = await readManifest(claudeDir2);
|
|
64608
|
+
if (!metadata?.kits) {
|
|
64609
|
+
return {
|
|
64610
|
+
exists: false,
|
|
64611
|
+
ownerKit: null,
|
|
64612
|
+
checksum: null,
|
|
64613
|
+
version: null,
|
|
64614
|
+
sourceTimestamp: null,
|
|
64615
|
+
installedAt: null
|
|
64616
|
+
};
|
|
64617
|
+
}
|
|
64618
|
+
for (const [kitName, kitMeta] of Object.entries(metadata.kits)) {
|
|
64619
|
+
const kit = kitName;
|
|
64620
|
+
if (kit === excludeKit)
|
|
64621
|
+
continue;
|
|
64622
|
+
if (!kitMeta.files)
|
|
64623
|
+
continue;
|
|
64624
|
+
const file = kitMeta.files.find((f3) => f3.path === relativePath);
|
|
64625
|
+
if (file) {
|
|
64626
|
+
return {
|
|
64627
|
+
exists: true,
|
|
64628
|
+
ownerKit: kit,
|
|
64629
|
+
checksum: file.checksum,
|
|
64630
|
+
version: kitMeta.version,
|
|
64631
|
+
sourceTimestamp: file.sourceTimestamp ?? null,
|
|
64632
|
+
installedAt: file.installedAt ?? null
|
|
64633
|
+
};
|
|
64634
|
+
}
|
|
64635
|
+
}
|
|
64636
|
+
return {
|
|
64637
|
+
exists: false,
|
|
64638
|
+
ownerKit: null,
|
|
64639
|
+
checksum: null,
|
|
64640
|
+
version: null,
|
|
64641
|
+
sourceTimestamp: null,
|
|
64642
|
+
installedAt: null
|
|
64643
|
+
};
|
|
64644
|
+
}
|
|
64645
|
+
async function getUninstallManifest(claudeDir2, kit) {
|
|
64646
|
+
const detection = await detectMetadataFormat(claudeDir2);
|
|
64647
|
+
if (detection.format === "multi-kit" && detection.metadata?.kits) {
|
|
64648
|
+
const installedKits = Object.keys(detection.metadata.kits);
|
|
64649
|
+
if (kit) {
|
|
64650
|
+
const kitMeta = detection.metadata.kits[kit];
|
|
64651
|
+
if (!kitMeta?.files) {
|
|
64652
|
+
return {
|
|
64653
|
+
filesToRemove: [],
|
|
64654
|
+
filesToPreserve: USER_CONFIG_PATTERNS,
|
|
64655
|
+
hasManifest: true,
|
|
64656
|
+
isMultiKit: true,
|
|
64657
|
+
remainingKits: installedKits.filter((k2) => k2 !== kit)
|
|
64658
|
+
};
|
|
64659
|
+
}
|
|
64660
|
+
const kitFiles = kitMeta.files.map((f3) => f3.path);
|
|
64661
|
+
const sharedFiles = new Set;
|
|
64662
|
+
for (const otherKit of installedKits) {
|
|
64663
|
+
if (otherKit !== kit) {
|
|
64664
|
+
const otherMeta = detection.metadata.kits[otherKit];
|
|
64665
|
+
if (otherMeta?.files) {
|
|
64666
|
+
for (const f3 of otherMeta.files) {
|
|
64667
|
+
sharedFiles.add(f3.path);
|
|
64668
|
+
}
|
|
64669
|
+
}
|
|
64670
|
+
}
|
|
64671
|
+
}
|
|
64672
|
+
const filesToRemove = kitFiles.filter((f3) => !sharedFiles.has(f3));
|
|
64673
|
+
const filesToPreserve = [
|
|
64674
|
+
...USER_CONFIG_PATTERNS,
|
|
64675
|
+
...kitFiles.filter((f3) => sharedFiles.has(f3))
|
|
64676
|
+
];
|
|
64677
|
+
return {
|
|
64678
|
+
filesToRemove,
|
|
64679
|
+
filesToPreserve,
|
|
64680
|
+
hasManifest: true,
|
|
64681
|
+
isMultiKit: true,
|
|
64682
|
+
remainingKits: installedKits.filter((k2) => k2 !== kit)
|
|
64683
|
+
};
|
|
64684
|
+
}
|
|
64685
|
+
const allFiles = getAllTrackedFiles(detection.metadata);
|
|
64686
|
+
return {
|
|
64687
|
+
filesToRemove: allFiles.map((f3) => f3.path),
|
|
64688
|
+
filesToPreserve: USER_CONFIG_PATTERNS,
|
|
64689
|
+
hasManifest: true,
|
|
64690
|
+
isMultiKit: true,
|
|
64691
|
+
remainingKits: []
|
|
64692
|
+
};
|
|
64693
|
+
}
|
|
64694
|
+
if (detection.format === "legacy" && detection.metadata) {
|
|
64695
|
+
const legacyFiles2 = detection.metadata.files?.map((f3) => f3.path) || [];
|
|
64696
|
+
const installedFiles = detection.metadata.installedFiles || [];
|
|
64697
|
+
const hasFiles = legacyFiles2.length > 0 || installedFiles.length > 0;
|
|
64698
|
+
if (!hasFiles) {
|
|
64699
|
+
const legacyDirs2 = ["commands", "agents", "skills", "rules", "workflows", "hooks", "scripts"];
|
|
64700
|
+
const legacyFileList = ["metadata.json"];
|
|
64701
|
+
return {
|
|
64702
|
+
filesToRemove: [...legacyDirs2, ...legacyFileList],
|
|
64703
|
+
filesToPreserve: USER_CONFIG_PATTERNS,
|
|
64704
|
+
hasManifest: false,
|
|
64705
|
+
isMultiKit: false,
|
|
64706
|
+
remainingKits: []
|
|
64707
|
+
};
|
|
64708
|
+
}
|
|
64709
|
+
return {
|
|
64710
|
+
filesToRemove: legacyFiles2.length > 0 ? legacyFiles2 : installedFiles,
|
|
64711
|
+
filesToPreserve: detection.metadata.userConfigFiles || USER_CONFIG_PATTERNS,
|
|
64712
|
+
hasManifest: true,
|
|
64713
|
+
isMultiKit: false,
|
|
64714
|
+
remainingKits: []
|
|
64715
|
+
};
|
|
64716
|
+
}
|
|
64717
|
+
const legacyDirs = ["commands", "agents", "skills", "rules", "workflows", "hooks", "scripts"];
|
|
64718
|
+
const legacyFiles = ["metadata.json"];
|
|
64719
|
+
return {
|
|
64720
|
+
filesToRemove: [...legacyDirs, ...legacyFiles],
|
|
64721
|
+
filesToPreserve: USER_CONFIG_PATTERNS,
|
|
64722
|
+
hasManifest: false,
|
|
64723
|
+
isMultiKit: false,
|
|
64724
|
+
remainingKits: []
|
|
64725
|
+
};
|
|
64726
|
+
}
|
|
64727
|
+
var import_fs_extra8;
|
|
64728
|
+
var init_manifest_reader = __esm(() => {
|
|
64729
|
+
init_metadata_migration();
|
|
64730
|
+
init_logger();
|
|
64731
|
+
init_types3();
|
|
64732
|
+
import_fs_extra8 = __toESM(require_lib3(), 1);
|
|
64733
|
+
});
|
|
64734
|
+
|
|
64735
|
+
// src/domains/installation/deletion-handler.ts
|
|
64736
|
+
var exports_deletion_handler = {};
|
|
64737
|
+
__export(exports_deletion_handler, {
|
|
64738
|
+
handleDeletions: () => handleDeletions,
|
|
64739
|
+
categorizeDeletions: () => categorizeDeletions
|
|
64740
|
+
});
|
|
64741
|
+
import {
|
|
64742
|
+
existsSync as existsSync48,
|
|
64743
|
+
lstatSync as lstatSync3,
|
|
64744
|
+
readdirSync as readdirSync3,
|
|
64745
|
+
realpathSync as realpathSync3,
|
|
64746
|
+
rmSync as rmSync2,
|
|
64747
|
+
rmdirSync,
|
|
64748
|
+
unlinkSync as unlinkSync3
|
|
64749
|
+
} from "node:fs";
|
|
64750
|
+
import { dirname as dirname19, join as join75, relative as relative10, resolve as resolve18, sep as sep3 } from "node:path";
|
|
64751
|
+
function findFileInMetadata(metadata, path13) {
|
|
64752
|
+
if (!metadata)
|
|
64753
|
+
return null;
|
|
64754
|
+
const normalizedPath = normalizeRelativePath(path13);
|
|
64755
|
+
if (metadata.kits) {
|
|
64756
|
+
for (const kitMeta of Object.values(metadata.kits)) {
|
|
64757
|
+
if (kitMeta?.files) {
|
|
64758
|
+
const found = kitMeta.files.find((f3) => normalizeRelativePath(f3.path) === normalizedPath);
|
|
64759
|
+
if (found)
|
|
64760
|
+
return found;
|
|
64761
|
+
}
|
|
64762
|
+
}
|
|
64763
|
+
}
|
|
64764
|
+
if (metadata.files) {
|
|
64765
|
+
const found = metadata.files.find((f3) => normalizeRelativePath(f3.path) === normalizedPath);
|
|
64766
|
+
if (found)
|
|
64767
|
+
return found;
|
|
64768
|
+
}
|
|
64769
|
+
return null;
|
|
64770
|
+
}
|
|
64771
|
+
function shouldDeletePath(path13, metadata) {
|
|
64772
|
+
const tracked = findFileInMetadata(metadata, path13);
|
|
64773
|
+
if (!tracked)
|
|
64774
|
+
return true;
|
|
64775
|
+
return tracked.ownership !== "user";
|
|
64776
|
+
}
|
|
64777
|
+
function collectFilesRecursively(dir, baseDir) {
|
|
64778
|
+
const results = [];
|
|
64779
|
+
if (!existsSync48(dir))
|
|
64780
|
+
return results;
|
|
64781
|
+
try {
|
|
64782
|
+
const entries = readdirSync3(dir, { withFileTypes: true });
|
|
64783
|
+
for (const entry of entries) {
|
|
64784
|
+
const fullPath = join75(dir, entry.name);
|
|
64785
|
+
const relativePath = relative10(baseDir, fullPath);
|
|
64786
|
+
if (entry.isDirectory()) {
|
|
64787
|
+
results.push(...collectFilesRecursively(fullPath, baseDir));
|
|
64788
|
+
} else {
|
|
64789
|
+
results.push(normalizeRelativePath(relativePath));
|
|
64790
|
+
}
|
|
64791
|
+
}
|
|
64792
|
+
} catch {}
|
|
64793
|
+
return results;
|
|
64794
|
+
}
|
|
64795
|
+
function expandGlobPatterns(patterns, claudeDir2) {
|
|
64796
|
+
const expanded = [];
|
|
64797
|
+
const allFiles = collectFilesRecursively(claudeDir2, claudeDir2);
|
|
64798
|
+
for (const pattern of patterns) {
|
|
64799
|
+
const normalizedPattern = normalizeRelativePath(pattern);
|
|
64800
|
+
if (PathResolver.isGlobPattern(normalizedPattern)) {
|
|
64801
|
+
const matcher = import_picomatch2.default(normalizedPattern);
|
|
64802
|
+
const matches = allFiles.filter((file) => matcher(file));
|
|
64803
|
+
expanded.push(...matches);
|
|
64804
|
+
if (matches.length > 0) {
|
|
64805
|
+
logger.debug(`Pattern "${normalizedPattern}" matched ${matches.length} files`);
|
|
64806
|
+
}
|
|
64807
|
+
} else {
|
|
64808
|
+
expanded.push(normalizedPattern);
|
|
64809
|
+
}
|
|
64810
|
+
}
|
|
64811
|
+
return [...new Set(expanded)];
|
|
64812
|
+
}
|
|
64813
|
+
function cleanupEmptyDirectories(filePath, claudeDir2) {
|
|
64814
|
+
const normalizedClaudeDir = resolve18(claudeDir2);
|
|
64815
|
+
let currentDir = resolve18(dirname19(filePath));
|
|
64816
|
+
let iterations = 0;
|
|
64817
|
+
while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir) && iterations < MAX_CLEANUP_ITERATIONS) {
|
|
64818
|
+
iterations++;
|
|
64819
|
+
try {
|
|
64820
|
+
const entries = readdirSync3(currentDir);
|
|
64821
|
+
if (entries.length === 0) {
|
|
64822
|
+
rmdirSync(currentDir);
|
|
64823
|
+
logger.debug(`Removed empty directory: ${currentDir}`);
|
|
64824
|
+
currentDir = resolve18(dirname19(currentDir));
|
|
64825
|
+
} else {
|
|
64826
|
+
break;
|
|
64827
|
+
}
|
|
64828
|
+
} catch {
|
|
64829
|
+
break;
|
|
64830
|
+
}
|
|
64831
|
+
}
|
|
64832
|
+
}
|
|
64833
|
+
function normalizeRelativePath(path13) {
|
|
64834
|
+
return path13.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
64835
|
+
}
|
|
64836
|
+
function isWithinBase(candidatePath, basePath) {
|
|
64837
|
+
return candidatePath === basePath || candidatePath.startsWith(`${basePath}${sep3}`);
|
|
64838
|
+
}
|
|
64839
|
+
function validateExistingDeletionTarget(fullPath, claudeDir2) {
|
|
64840
|
+
const normalizedPath = resolve18(fullPath);
|
|
64841
|
+
const normalizedClaudeDir = resolve18(claudeDir2);
|
|
64842
|
+
if (!isWithinBase(normalizedPath, normalizedClaudeDir)) {
|
|
64843
|
+
throw new Error(`Path traversal detected: ${fullPath}`);
|
|
64844
|
+
}
|
|
64845
|
+
let realBase = normalizedClaudeDir;
|
|
64846
|
+
try {
|
|
64847
|
+
realBase = realpathSync3(normalizedClaudeDir);
|
|
64848
|
+
} catch {}
|
|
64849
|
+
try {
|
|
64850
|
+
const realParent = realpathSync3(dirname19(fullPath));
|
|
64851
|
+
if (!isWithinBase(realParent, realBase)) {
|
|
64852
|
+
throw new Error(`Path escapes base via symlink parent: ${fullPath}`);
|
|
64853
|
+
}
|
|
64854
|
+
} catch (error) {
|
|
64855
|
+
throw new Error(`Failed to validate deletion parent for ${fullPath}: ${String(error)}`);
|
|
64856
|
+
}
|
|
64857
|
+
try {
|
|
64858
|
+
const stat13 = lstatSync3(fullPath);
|
|
64859
|
+
if (!stat13.isSymbolicLink()) {
|
|
64860
|
+
const realTarget = realpathSync3(fullPath);
|
|
64861
|
+
if (!isWithinBase(realTarget, realBase)) {
|
|
64862
|
+
throw new Error(`Path escapes base via symlink target: ${fullPath}`);
|
|
64863
|
+
}
|
|
64864
|
+
}
|
|
64865
|
+
} catch (error) {
|
|
64866
|
+
throw new Error(`Failed to validate deletion target ${fullPath}: ${String(error)}`);
|
|
64867
|
+
}
|
|
64868
|
+
}
|
|
64869
|
+
function deletePath(fullPath, claudeDir2) {
|
|
64870
|
+
validateExistingDeletionTarget(fullPath, claudeDir2);
|
|
64871
|
+
try {
|
|
64872
|
+
const stat13 = lstatSync3(fullPath);
|
|
64873
|
+
if (stat13.isDirectory()) {
|
|
64874
|
+
rmSync2(fullPath, { recursive: true, force: true });
|
|
64875
|
+
} else {
|
|
64876
|
+
unlinkSync3(fullPath);
|
|
64877
|
+
cleanupEmptyDirectories(fullPath, claudeDir2);
|
|
64878
|
+
}
|
|
64879
|
+
} catch (error) {
|
|
64880
|
+
throw new Error(`Failed to delete ${fullPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
64881
|
+
}
|
|
64882
|
+
}
|
|
64883
|
+
async function updateMetadataAfterDeletion(claudeDir2, deletedPaths) {
|
|
64884
|
+
const metadataPath = join75(claudeDir2, "metadata.json");
|
|
64885
|
+
if (!await import_fs_extra9.pathExists(metadataPath)) {
|
|
64886
|
+
return;
|
|
64887
|
+
}
|
|
64888
|
+
let content;
|
|
64889
|
+
try {
|
|
64890
|
+
content = await import_fs_extra9.readFile(metadataPath, "utf-8");
|
|
64891
|
+
} catch {
|
|
64892
|
+
logger.debug("Failed to read metadata.json for cleanup");
|
|
64893
|
+
return;
|
|
64894
|
+
}
|
|
64895
|
+
let metadata;
|
|
64896
|
+
try {
|
|
64897
|
+
metadata = JSON.parse(content);
|
|
64898
|
+
} catch {
|
|
64899
|
+
logger.debug("Failed to parse metadata.json for cleanup");
|
|
64900
|
+
return;
|
|
64901
|
+
}
|
|
64902
|
+
const deletedSet = new Set(deletedPaths);
|
|
64903
|
+
const isDeletedOrInDeletedDir = (path13) => {
|
|
64904
|
+
if (deletedSet.has(path13))
|
|
64905
|
+
return true;
|
|
64906
|
+
for (const deleted of deletedPaths) {
|
|
64907
|
+
if (path13.startsWith(`${deleted}/`))
|
|
64908
|
+
return true;
|
|
64909
|
+
}
|
|
64910
|
+
return false;
|
|
64911
|
+
};
|
|
64912
|
+
if (metadata.kits) {
|
|
64913
|
+
for (const kitName of Object.keys(metadata.kits)) {
|
|
64914
|
+
const kit = metadata.kits[kitName];
|
|
64915
|
+
if (kit?.files) {
|
|
64916
|
+
kit.files = kit.files.filter((f3) => !isDeletedOrInDeletedDir(f3.path));
|
|
64917
|
+
}
|
|
64918
|
+
}
|
|
64919
|
+
}
|
|
64920
|
+
if (metadata.files) {
|
|
64921
|
+
metadata.files = metadata.files.filter((f3) => !isDeletedOrInDeletedDir(f3.path));
|
|
64922
|
+
}
|
|
64923
|
+
try {
|
|
64924
|
+
await import_fs_extra9.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
64925
|
+
logger.debug(`Updated metadata.json, removed ${deletedPaths.length} entries`);
|
|
64926
|
+
} catch {
|
|
64927
|
+
logger.debug("Failed to write updated metadata.json");
|
|
64928
|
+
}
|
|
64929
|
+
}
|
|
64930
|
+
function categorizeDeletions(deletions) {
|
|
64931
|
+
const immediate = [];
|
|
64932
|
+
const deferred = [];
|
|
64933
|
+
for (const path13 of deletions) {
|
|
64934
|
+
if (path13.startsWith("skills/") || path13.startsWith("skills\\")) {
|
|
64935
|
+
deferred.push(path13);
|
|
64936
|
+
} else {
|
|
64937
|
+
immediate.push(path13);
|
|
64938
|
+
}
|
|
64939
|
+
}
|
|
64940
|
+
return { immediate, deferred };
|
|
64941
|
+
}
|
|
64942
|
+
async function handleDeletions(sourceMetadata, claudeDir2) {
|
|
64943
|
+
const deletionPatterns = sourceMetadata.deletions || [];
|
|
64944
|
+
if (deletionPatterns.length === 0) {
|
|
64945
|
+
return { deletedPaths: [], preservedPaths: [], errors: [] };
|
|
64946
|
+
}
|
|
64947
|
+
const deletions = expandGlobPatterns(deletionPatterns, claudeDir2);
|
|
64948
|
+
const userMetadata = await readManifest(claudeDir2);
|
|
64949
|
+
const result = { deletedPaths: [], preservedPaths: [], errors: [] };
|
|
64950
|
+
for (const path13 of deletions) {
|
|
64951
|
+
const normalizedRelativePath = normalizeRelativePath(path13);
|
|
64952
|
+
const fullPath = join75(claudeDir2, normalizedRelativePath);
|
|
64953
|
+
const normalizedResolvedPath = resolve18(fullPath);
|
|
64954
|
+
const normalizedClaudeDir = resolve18(claudeDir2);
|
|
64955
|
+
if (!isWithinBase(normalizedResolvedPath, normalizedClaudeDir)) {
|
|
64956
|
+
logger.warning(`Skipping invalid path: ${normalizedRelativePath}`);
|
|
64957
|
+
result.errors.push(normalizedRelativePath);
|
|
64958
|
+
continue;
|
|
64959
|
+
}
|
|
64960
|
+
if (!shouldDeletePath(normalizedRelativePath, userMetadata)) {
|
|
64961
|
+
result.preservedPaths.push(normalizedRelativePath);
|
|
64962
|
+
logger.verbose(`Preserved user file: ${normalizedRelativePath}`);
|
|
64963
|
+
continue;
|
|
64964
|
+
}
|
|
64965
|
+
if (existsSync48(fullPath)) {
|
|
64966
|
+
try {
|
|
64967
|
+
deletePath(fullPath, claudeDir2);
|
|
64968
|
+
result.deletedPaths.push(normalizedRelativePath);
|
|
64969
|
+
logger.verbose(`Deleted: ${normalizedRelativePath}`);
|
|
64970
|
+
} catch (error) {
|
|
64971
|
+
result.errors.push(normalizedRelativePath);
|
|
64972
|
+
logger.debug(`Failed to delete ${normalizedRelativePath}: ${error}`);
|
|
64973
|
+
}
|
|
64974
|
+
}
|
|
64975
|
+
}
|
|
64976
|
+
if (result.deletedPaths.length > 0) {
|
|
64977
|
+
await updateMetadataAfterDeletion(claudeDir2, result.deletedPaths);
|
|
64978
|
+
}
|
|
64979
|
+
return result;
|
|
64980
|
+
}
|
|
64981
|
+
var import_fs_extra9, import_picomatch2, MAX_CLEANUP_ITERATIONS = 50;
|
|
64982
|
+
var init_deletion_handler = __esm(() => {
|
|
64983
|
+
init_manifest_reader();
|
|
64984
|
+
init_logger();
|
|
64985
|
+
init_path_resolver();
|
|
64986
|
+
import_fs_extra9 = __toESM(require_lib3(), 1);
|
|
64987
|
+
import_picomatch2 = __toESM(require_picomatch2(), 1);
|
|
64988
|
+
});
|
|
64989
|
+
|
|
64532
64990
|
// src/domains/ui/ownership-display.ts
|
|
64533
64991
|
var exports_ownership_display = {};
|
|
64534
64992
|
__export(exports_ownership_display, {
|
|
@@ -64665,6 +65123,481 @@ var init_ownership_display = __esm(() => {
|
|
|
64665
65123
|
import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
64666
65124
|
});
|
|
64667
65125
|
|
|
65126
|
+
// src/shared/claude-exec-options.ts
|
|
65127
|
+
function buildExecOptions(timeout2) {
|
|
65128
|
+
const env2 = { ...process.env };
|
|
65129
|
+
for (const key of Object.keys(env2)) {
|
|
65130
|
+
if (key.startsWith("CLAUDE") && key !== "CLAUDE_CONFIG_DIR") {
|
|
65131
|
+
delete env2[key];
|
|
65132
|
+
}
|
|
65133
|
+
}
|
|
65134
|
+
return {
|
|
65135
|
+
timeout: timeout2,
|
|
65136
|
+
env: env2,
|
|
65137
|
+
shell: process.platform === "win32"
|
|
65138
|
+
};
|
|
65139
|
+
}
|
|
65140
|
+
|
|
65141
|
+
// src/services/cc-version-checker.ts
|
|
65142
|
+
var exports_cc_version_checker = {};
|
|
65143
|
+
__export(exports_cc_version_checker, {
|
|
65144
|
+
requireCCPluginSupport: () => requireCCPluginSupport,
|
|
65145
|
+
parseVersion: () => parseVersion,
|
|
65146
|
+
isCCPluginSupportError: () => isCCPluginSupportError,
|
|
65147
|
+
getCCVersion: () => getCCVersion,
|
|
65148
|
+
compareVersions: () => compareVersions9,
|
|
65149
|
+
MIN_PLUGIN_VERSION: () => MIN_PLUGIN_VERSION,
|
|
65150
|
+
CCPluginSupportError: () => CCPluginSupportError
|
|
65151
|
+
});
|
|
65152
|
+
import { execFile as execFile9 } from "node:child_process";
|
|
65153
|
+
import { promisify as promisify14 } from "node:util";
|
|
65154
|
+
function parseVersion(version) {
|
|
65155
|
+
const match2 = version.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
65156
|
+
if (!match2)
|
|
65157
|
+
return null;
|
|
65158
|
+
return [Number(match2[1]), Number(match2[2]), Number(match2[3])];
|
|
65159
|
+
}
|
|
65160
|
+
function compareVersions9(a3, b3) {
|
|
65161
|
+
const parsedA = parseVersion(a3);
|
|
65162
|
+
const parsedB = parseVersion(b3);
|
|
65163
|
+
if (!parsedA || !parsedB)
|
|
65164
|
+
return -1;
|
|
65165
|
+
for (let i = 0;i < 3; i++) {
|
|
65166
|
+
if (parsedA[i] !== parsedB[i])
|
|
65167
|
+
return parsedA[i] - parsedB[i];
|
|
65168
|
+
}
|
|
65169
|
+
return 0;
|
|
65170
|
+
}
|
|
65171
|
+
async function getCCVersionResult() {
|
|
65172
|
+
try {
|
|
65173
|
+
const { stdout: stdout2, stderr } = await execFileAsync6("claude", ["--version"], buildExecOptions(5000));
|
|
65174
|
+
const output2 = stdout2.trim();
|
|
65175
|
+
const match2 = output2.match(/(\d+\.\d+\.\d+)/);
|
|
65176
|
+
if (!match2) {
|
|
65177
|
+
return {
|
|
65178
|
+
version: null,
|
|
65179
|
+
error: new CCPluginSupportError("cc_version_unparseable", "Failed to parse Claude Code version output", output2 || stderr?.trim() || "empty output")
|
|
65180
|
+
};
|
|
65181
|
+
}
|
|
65182
|
+
return { version: match2[1] };
|
|
65183
|
+
} catch (error) {
|
|
65184
|
+
const err = error;
|
|
65185
|
+
if (err.code === "ENOENT") {
|
|
65186
|
+
return {
|
|
65187
|
+
version: null,
|
|
65188
|
+
error: new CCPluginSupportError("cc_not_found", "Claude Code CLI not found on PATH")
|
|
65189
|
+
};
|
|
65190
|
+
}
|
|
65191
|
+
return {
|
|
65192
|
+
version: null,
|
|
65193
|
+
error: new CCPluginSupportError("cc_exec_failed", "Failed to run 'claude --version'", (err.stderr ?? err.message ?? "Unknown error").trim())
|
|
65194
|
+
};
|
|
65195
|
+
}
|
|
65196
|
+
}
|
|
65197
|
+
async function getCCVersion() {
|
|
65198
|
+
const result = await getCCVersionResult();
|
|
65199
|
+
return result.version;
|
|
65200
|
+
}
|
|
65201
|
+
async function requireCCPluginSupport() {
|
|
65202
|
+
const result = await getCCVersionResult();
|
|
65203
|
+
const version = result.version;
|
|
65204
|
+
if (!version) {
|
|
65205
|
+
throw result.error ?? new CCPluginSupportError("cc_exec_failed", "Failed to determine Claude Code version");
|
|
65206
|
+
}
|
|
65207
|
+
if (compareVersions9(version, MIN_PLUGIN_VERSION) < 0) {
|
|
65208
|
+
throw new CCPluginSupportError("cc_version_too_old", `Claude Code ${version} does not support plugins (requires >= ${MIN_PLUGIN_VERSION})`);
|
|
65209
|
+
}
|
|
65210
|
+
logger.debug(`CC version ${version} supports plugins (>= ${MIN_PLUGIN_VERSION})`);
|
|
65211
|
+
}
|
|
65212
|
+
function isCCPluginSupportError(error) {
|
|
65213
|
+
return error instanceof CCPluginSupportError;
|
|
65214
|
+
}
|
|
65215
|
+
var execFileAsync6, MIN_PLUGIN_VERSION = "1.0.33", CCPluginSupportError;
|
|
65216
|
+
var init_cc_version_checker = __esm(() => {
|
|
65217
|
+
init_logger();
|
|
65218
|
+
execFileAsync6 = promisify14(execFile9);
|
|
65219
|
+
CCPluginSupportError = class CCPluginSupportError extends Error {
|
|
65220
|
+
code;
|
|
65221
|
+
details;
|
|
65222
|
+
constructor(code2, message, details) {
|
|
65223
|
+
super(message);
|
|
65224
|
+
this.code = code2;
|
|
65225
|
+
this.details = details;
|
|
65226
|
+
this.name = "CCPluginSupportError";
|
|
65227
|
+
}
|
|
65228
|
+
};
|
|
65229
|
+
});
|
|
65230
|
+
|
|
65231
|
+
// src/services/plugin-installer.ts
|
|
65232
|
+
var exports_plugin_installer = {};
|
|
65233
|
+
__export(exports_plugin_installer, {
|
|
65234
|
+
handlePluginUninstall: () => handlePluginUninstall,
|
|
65235
|
+
handlePluginInstall: () => handlePluginInstall
|
|
65236
|
+
});
|
|
65237
|
+
import { execFile as execFile10 } from "node:child_process";
|
|
65238
|
+
import { rename as rename6 } from "node:fs/promises";
|
|
65239
|
+
import { join as join99 } from "node:path";
|
|
65240
|
+
import { promisify as promisify15 } from "node:util";
|
|
65241
|
+
function getMarketplacePath() {
|
|
65242
|
+
const dataDir = PathResolver.getClaudeKitDir();
|
|
65243
|
+
return join99(dataDir, MARKETPLACE_DIR_NAME);
|
|
65244
|
+
}
|
|
65245
|
+
function escapeRegex2(value) {
|
|
65246
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
65247
|
+
}
|
|
65248
|
+
function outputContainsToken(output2, token) {
|
|
65249
|
+
const tokenPattern = new RegExp(`(^|\\s)${escapeRegex2(token)}(?=\\s|$)`, "i");
|
|
65250
|
+
return output2.split(/\r?\n/).map((line) => line.trim()).some((line) => tokenPattern.test(line));
|
|
65251
|
+
}
|
|
65252
|
+
async function runClaudePlugin(args) {
|
|
65253
|
+
try {
|
|
65254
|
+
const { stdout: stdout2, stderr } = await execFileAsync7("claude", ["plugin", ...args], buildExecOptions(30000));
|
|
65255
|
+
return { success: true, stdout: stdout2.trim(), stderr: stderr.trim() };
|
|
65256
|
+
} catch (error) {
|
|
65257
|
+
const err = error;
|
|
65258
|
+
return {
|
|
65259
|
+
success: false,
|
|
65260
|
+
stdout: (err.stdout ?? "").trim(),
|
|
65261
|
+
stderr: (err.stderr ?? err.message ?? "Unknown error").trim()
|
|
65262
|
+
};
|
|
65263
|
+
}
|
|
65264
|
+
}
|
|
65265
|
+
async function isClaudeAvailable() {
|
|
65266
|
+
try {
|
|
65267
|
+
await execFileAsync7("claude", ["--version"], buildExecOptions(5000));
|
|
65268
|
+
return true;
|
|
65269
|
+
} catch {
|
|
65270
|
+
return false;
|
|
65271
|
+
}
|
|
65272
|
+
}
|
|
65273
|
+
async function copyPluginToMarketplace(extractDir) {
|
|
65274
|
+
const marketplacePath = getMarketplacePath();
|
|
65275
|
+
const sourceMarketplaceJson = join99(extractDir, ".claude-plugin", "marketplace.json");
|
|
65276
|
+
const sourcePluginDir = join99(extractDir, "plugins", PLUGIN_NAME);
|
|
65277
|
+
if (!await import_fs_extra30.pathExists(sourceMarketplaceJson) || !await import_fs_extra30.pathExists(sourcePluginDir)) {
|
|
65278
|
+
logger.debug("Kit release does not contain plugin structure — skipping plugin install");
|
|
65279
|
+
return false;
|
|
65280
|
+
}
|
|
65281
|
+
await import_fs_extra30.ensureDir(join99(marketplacePath, ".claude-plugin"));
|
|
65282
|
+
await import_fs_extra30.ensureDir(join99(marketplacePath, "plugins"));
|
|
65283
|
+
const suffix = `${process.pid}-${Date.now()}`;
|
|
65284
|
+
const destMarketplaceJson = join99(marketplacePath, ".claude-plugin", "marketplace.json");
|
|
65285
|
+
const destPluginDir = join99(marketplacePath, "plugins", PLUGIN_NAME);
|
|
65286
|
+
const tempMarketplaceJson = `${destMarketplaceJson}.tmp-${suffix}`;
|
|
65287
|
+
const tempPluginDir = `${destPluginDir}.tmp-${suffix}`;
|
|
65288
|
+
const backupMarketplaceJson = `${destMarketplaceJson}.bak-${suffix}`;
|
|
65289
|
+
const backupPluginDir = `${destPluginDir}.bak-${suffix}`;
|
|
65290
|
+
await import_fs_extra30.remove(tempMarketplaceJson).catch(() => {});
|
|
65291
|
+
await import_fs_extra30.remove(tempPluginDir).catch(() => {});
|
|
65292
|
+
await import_fs_extra30.remove(backupMarketplaceJson).catch(() => {});
|
|
65293
|
+
await import_fs_extra30.remove(backupPluginDir).catch(() => {});
|
|
65294
|
+
await import_fs_extra30.copy(sourceMarketplaceJson, tempMarketplaceJson, { overwrite: true });
|
|
65295
|
+
await import_fs_extra30.copy(sourcePluginDir, tempPluginDir, { overwrite: true });
|
|
65296
|
+
let backupMarketplaceCreated = false;
|
|
65297
|
+
let backupPluginCreated = false;
|
|
65298
|
+
let marketplaceReplaced = false;
|
|
65299
|
+
let pluginReplaced = false;
|
|
65300
|
+
try {
|
|
65301
|
+
if (await import_fs_extra30.pathExists(destMarketplaceJson)) {
|
|
65302
|
+
await rename6(destMarketplaceJson, backupMarketplaceJson);
|
|
65303
|
+
backupMarketplaceCreated = true;
|
|
65304
|
+
}
|
|
65305
|
+
await rename6(tempMarketplaceJson, destMarketplaceJson);
|
|
65306
|
+
marketplaceReplaced = true;
|
|
65307
|
+
if (await import_fs_extra30.pathExists(destPluginDir)) {
|
|
65308
|
+
await rename6(destPluginDir, backupPluginDir);
|
|
65309
|
+
backupPluginCreated = true;
|
|
65310
|
+
}
|
|
65311
|
+
await rename6(tempPluginDir, destPluginDir);
|
|
65312
|
+
pluginReplaced = true;
|
|
65313
|
+
} catch (error) {
|
|
65314
|
+
if (marketplaceReplaced) {
|
|
65315
|
+
await import_fs_extra30.remove(destMarketplaceJson).catch(() => {});
|
|
65316
|
+
}
|
|
65317
|
+
if (pluginReplaced) {
|
|
65318
|
+
await import_fs_extra30.remove(destPluginDir).catch(() => {});
|
|
65319
|
+
}
|
|
65320
|
+
if (backupMarketplaceCreated) {
|
|
65321
|
+
await rename6(backupMarketplaceJson, destMarketplaceJson).catch(() => {});
|
|
65322
|
+
}
|
|
65323
|
+
if (backupPluginCreated) {
|
|
65324
|
+
await rename6(backupPluginDir, destPluginDir).catch(() => {});
|
|
65325
|
+
}
|
|
65326
|
+
await import_fs_extra30.remove(tempMarketplaceJson).catch(() => {});
|
|
65327
|
+
await import_fs_extra30.remove(tempPluginDir).catch(() => {});
|
|
65328
|
+
throw error;
|
|
65329
|
+
}
|
|
65330
|
+
await import_fs_extra30.remove(backupMarketplaceJson).catch(() => {});
|
|
65331
|
+
await import_fs_extra30.remove(backupPluginDir).catch(() => {});
|
|
65332
|
+
await import_fs_extra30.remove(tempMarketplaceJson).catch(() => {});
|
|
65333
|
+
await import_fs_extra30.remove(tempPluginDir).catch(() => {});
|
|
65334
|
+
logger.debug("Plugin copied to marketplace");
|
|
65335
|
+
return true;
|
|
65336
|
+
}
|
|
65337
|
+
async function registerMarketplace() {
|
|
65338
|
+
const marketplacePath = getMarketplacePath();
|
|
65339
|
+
const listResult = await runClaudePlugin(["marketplace", "list"]);
|
|
65340
|
+
const alreadyRegistered = listResult.success && outputContainsToken(listResult.stdout, MARKETPLACE_NAME);
|
|
65341
|
+
if (alreadyRegistered) {
|
|
65342
|
+
const addResult = await runClaudePlugin(["marketplace", "add", marketplacePath]);
|
|
65343
|
+
if (addResult.success) {
|
|
65344
|
+
logger.debug("Marketplace re-registered successfully");
|
|
65345
|
+
return true;
|
|
65346
|
+
}
|
|
65347
|
+
logger.debug("Marketplace add failed while registered; removing stale entry and retrying");
|
|
65348
|
+
const removeResult = await runClaudePlugin(["marketplace", "remove", MARKETPLACE_NAME]);
|
|
65349
|
+
if (!removeResult.success) {
|
|
65350
|
+
logger.debug(`Marketplace remove failed: ${removeResult.stderr}`);
|
|
65351
|
+
return false;
|
|
65352
|
+
}
|
|
65353
|
+
const retryResult = await runClaudePlugin(["marketplace", "add", marketplacePath]);
|
|
65354
|
+
if (!retryResult.success) {
|
|
65355
|
+
logger.warning(`Marketplace remove succeeded but retry-add failed: ${retryResult.stderr}`);
|
|
65356
|
+
return false;
|
|
65357
|
+
}
|
|
65358
|
+
logger.debug("Marketplace re-registered after remove+retry");
|
|
65359
|
+
return true;
|
|
65360
|
+
}
|
|
65361
|
+
const result = await runClaudePlugin(["marketplace", "add", marketplacePath]);
|
|
65362
|
+
if (!result.success) {
|
|
65363
|
+
logger.debug(`Marketplace registration failed: ${result.stderr}`);
|
|
65364
|
+
return false;
|
|
65365
|
+
}
|
|
65366
|
+
logger.debug("Marketplace registered successfully");
|
|
65367
|
+
return true;
|
|
65368
|
+
}
|
|
65369
|
+
async function installOrUpdatePlugin() {
|
|
65370
|
+
const pluginRef = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
|
|
65371
|
+
const listResult = await runClaudePlugin(["list"]);
|
|
65372
|
+
const isInstalled = listResult.success && outputContainsToken(listResult.stdout, pluginRef);
|
|
65373
|
+
if (isInstalled) {
|
|
65374
|
+
const result2 = await runClaudePlugin(["update", pluginRef]);
|
|
65375
|
+
if (result2.success) {
|
|
65376
|
+
logger.debug("Plugin updated successfully");
|
|
65377
|
+
return true;
|
|
65378
|
+
}
|
|
65379
|
+
logger.debug(`Plugin update failed (${result2.stderr}); re-verifying install state`);
|
|
65380
|
+
const stillInstalled = await verifyPluginInstalled();
|
|
65381
|
+
if (stillInstalled) {
|
|
65382
|
+
logger.debug("Plugin update failed but plugin is still installed — treating as success");
|
|
65383
|
+
return true;
|
|
65384
|
+
}
|
|
65385
|
+
logger.debug("Plugin update failed and plugin is no longer listed");
|
|
65386
|
+
return false;
|
|
65387
|
+
}
|
|
65388
|
+
const result = await runClaudePlugin(["install", pluginRef]);
|
|
65389
|
+
if (!result.success) {
|
|
65390
|
+
logger.debug(`Plugin install failed: ${result.stderr}`);
|
|
65391
|
+
return false;
|
|
65392
|
+
}
|
|
65393
|
+
logger.debug("Plugin installed successfully");
|
|
65394
|
+
return true;
|
|
65395
|
+
}
|
|
65396
|
+
async function verifyPluginInstalled() {
|
|
65397
|
+
const pluginRef = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
|
|
65398
|
+
const result = await runClaudePlugin(["list"]);
|
|
65399
|
+
if (!result.success)
|
|
65400
|
+
return false;
|
|
65401
|
+
return outputContainsToken(result.stdout, pluginRef);
|
|
65402
|
+
}
|
|
65403
|
+
async function handlePluginInstall(extractDir) {
|
|
65404
|
+
if (!await isClaudeAvailable()) {
|
|
65405
|
+
return {
|
|
65406
|
+
installed: false,
|
|
65407
|
+
marketplaceRegistered: false,
|
|
65408
|
+
verified: false,
|
|
65409
|
+
error: "Claude Code CLI not found on PATH"
|
|
65410
|
+
};
|
|
65411
|
+
}
|
|
65412
|
+
logger.debug("Registering CK plugin with Claude Code...");
|
|
65413
|
+
let copied = false;
|
|
65414
|
+
try {
|
|
65415
|
+
copied = await copyPluginToMarketplace(extractDir);
|
|
65416
|
+
} catch (error) {
|
|
65417
|
+
return {
|
|
65418
|
+
installed: false,
|
|
65419
|
+
marketplaceRegistered: false,
|
|
65420
|
+
verified: false,
|
|
65421
|
+
error: `Plugin copy failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
65422
|
+
};
|
|
65423
|
+
}
|
|
65424
|
+
if (!copied) {
|
|
65425
|
+
return {
|
|
65426
|
+
installed: false,
|
|
65427
|
+
marketplaceRegistered: false,
|
|
65428
|
+
verified: false,
|
|
65429
|
+
error: "No plugin found in kit release"
|
|
65430
|
+
};
|
|
65431
|
+
}
|
|
65432
|
+
const registered = await registerMarketplace();
|
|
65433
|
+
if (!registered) {
|
|
65434
|
+
return {
|
|
65435
|
+
installed: false,
|
|
65436
|
+
marketplaceRegistered: false,
|
|
65437
|
+
verified: false,
|
|
65438
|
+
error: "Marketplace registration failed"
|
|
65439
|
+
};
|
|
65440
|
+
}
|
|
65441
|
+
const installOk = await installOrUpdatePlugin();
|
|
65442
|
+
if (!installOk) {
|
|
65443
|
+
return {
|
|
65444
|
+
installed: false,
|
|
65445
|
+
marketplaceRegistered: true,
|
|
65446
|
+
verified: false,
|
|
65447
|
+
error: "Plugin install/update failed"
|
|
65448
|
+
};
|
|
65449
|
+
}
|
|
65450
|
+
const verified = await verifyPluginInstalled();
|
|
65451
|
+
if (verified) {
|
|
65452
|
+
logger.success("CK plugin installed — skills available as /ck:* commands");
|
|
65453
|
+
} else {
|
|
65454
|
+
logger.warning("Plugin installed but verification failed — skills may not be available");
|
|
65455
|
+
}
|
|
65456
|
+
return {
|
|
65457
|
+
installed: true,
|
|
65458
|
+
marketplaceRegistered: true,
|
|
65459
|
+
verified,
|
|
65460
|
+
error: verified ? undefined : "Post-install verification failed"
|
|
65461
|
+
};
|
|
65462
|
+
}
|
|
65463
|
+
async function handlePluginUninstall() {
|
|
65464
|
+
if (!await isClaudeAvailable()) {
|
|
65465
|
+
logger.debug("Claude Code CLI not found — skipping plugin cleanup");
|
|
65466
|
+
return;
|
|
65467
|
+
}
|
|
65468
|
+
const isInstalled = await verifyPluginInstalled();
|
|
65469
|
+
if (isInstalled) {
|
|
65470
|
+
const pluginRef = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
|
|
65471
|
+
const uninstallResult = await runClaudePlugin(["uninstall", pluginRef]);
|
|
65472
|
+
if (uninstallResult.success) {
|
|
65473
|
+
logger.debug("CK plugin uninstalled from Claude Code");
|
|
65474
|
+
} else {
|
|
65475
|
+
logger.debug(`Plugin uninstall failed: ${uninstallResult.stderr}`);
|
|
65476
|
+
}
|
|
65477
|
+
}
|
|
65478
|
+
const marketplaceRemoveResult = await runClaudePlugin([
|
|
65479
|
+
"marketplace",
|
|
65480
|
+
"remove",
|
|
65481
|
+
MARKETPLACE_NAME
|
|
65482
|
+
]);
|
|
65483
|
+
if (!marketplaceRemoveResult.success) {
|
|
65484
|
+
logger.debug(`Marketplace remove failed: ${marketplaceRemoveResult.stderr}`);
|
|
65485
|
+
}
|
|
65486
|
+
const marketplacePath = getMarketplacePath();
|
|
65487
|
+
if (await import_fs_extra30.pathExists(marketplacePath)) {
|
|
65488
|
+
try {
|
|
65489
|
+
await import_fs_extra30.remove(marketplacePath);
|
|
65490
|
+
logger.debug("Marketplace directory cleaned up");
|
|
65491
|
+
} catch (error) {
|
|
65492
|
+
logger.warning(`Failed to clean marketplace directory: ${error instanceof Error ? error.message : String(error)}`);
|
|
65493
|
+
}
|
|
65494
|
+
}
|
|
65495
|
+
}
|
|
65496
|
+
var import_fs_extra30, execFileAsync7, MARKETPLACE_DIR_NAME = "marketplace", PLUGIN_NAME = "ck", MARKETPLACE_NAME = "claudekit";
|
|
65497
|
+
var init_plugin_installer = __esm(() => {
|
|
65498
|
+
init_logger();
|
|
65499
|
+
init_path_resolver();
|
|
65500
|
+
import_fs_extra30 = __toESM(require_lib3(), 1);
|
|
65501
|
+
execFileAsync7 = promisify15(execFile10);
|
|
65502
|
+
});
|
|
65503
|
+
|
|
65504
|
+
// src/services/skill-migration-merger.ts
|
|
65505
|
+
var exports_skill_migration_merger = {};
|
|
65506
|
+
__export(exports_skill_migration_merger, {
|
|
65507
|
+
migrateUserSkills: () => migrateUserSkills
|
|
65508
|
+
});
|
|
65509
|
+
import { readFile as readFile47 } from "node:fs/promises";
|
|
65510
|
+
import { join as join100 } from "node:path";
|
|
65511
|
+
async function migrateUserSkills(claudeDir2, pluginVerified) {
|
|
65512
|
+
const result = {
|
|
65513
|
+
preserved: [],
|
|
65514
|
+
deleted: [],
|
|
65515
|
+
userOwned: [],
|
|
65516
|
+
canDelete: false
|
|
65517
|
+
};
|
|
65518
|
+
if (!pluginVerified) {
|
|
65519
|
+
return result;
|
|
65520
|
+
}
|
|
65521
|
+
const metadataPath = join100(claudeDir2, "metadata.json");
|
|
65522
|
+
if (!await import_fs_extra31.pathExists(metadataPath)) {
|
|
65523
|
+
return result;
|
|
65524
|
+
}
|
|
65525
|
+
let trackedFiles;
|
|
65526
|
+
try {
|
|
65527
|
+
const content = await readFile47(metadataPath, "utf-8");
|
|
65528
|
+
const metadata = JSON.parse(content);
|
|
65529
|
+
trackedFiles = extractTrackedSkillFiles(metadata);
|
|
65530
|
+
} catch {
|
|
65531
|
+
logger.debug("Could not read metadata for skill migration");
|
|
65532
|
+
return result;
|
|
65533
|
+
}
|
|
65534
|
+
if (trackedFiles.length === 0) {
|
|
65535
|
+
return result;
|
|
65536
|
+
}
|
|
65537
|
+
result.canDelete = true;
|
|
65538
|
+
const preservedSet = new Set;
|
|
65539
|
+
const deletedSet = new Set;
|
|
65540
|
+
const userOwnedSet = new Set;
|
|
65541
|
+
for (const file of trackedFiles) {
|
|
65542
|
+
const skillDir = extractSkillDir(file.path);
|
|
65543
|
+
if (!skillDir)
|
|
65544
|
+
continue;
|
|
65545
|
+
switch (file.ownership) {
|
|
65546
|
+
case "user":
|
|
65547
|
+
userOwnedSet.add(skillDir);
|
|
65548
|
+
break;
|
|
65549
|
+
case "ck-modified":
|
|
65550
|
+
preservedSet.add(skillDir);
|
|
65551
|
+
break;
|
|
65552
|
+
case "ck":
|
|
65553
|
+
deletedSet.add(skillDir);
|
|
65554
|
+
break;
|
|
65555
|
+
}
|
|
65556
|
+
}
|
|
65557
|
+
for (const dir of preservedSet) {
|
|
65558
|
+
deletedSet.delete(dir);
|
|
65559
|
+
}
|
|
65560
|
+
result.preserved = [...preservedSet];
|
|
65561
|
+
result.deleted = [...deletedSet];
|
|
65562
|
+
result.userOwned = [...userOwnedSet];
|
|
65563
|
+
for (const dir of result.preserved) {
|
|
65564
|
+
const skillName = dir.split("/")[1];
|
|
65565
|
+
logger.info(`Preserved customizations: /${skillName} (standalone alongside /ck:${skillName})`);
|
|
65566
|
+
}
|
|
65567
|
+
return result;
|
|
65568
|
+
}
|
|
65569
|
+
function extractTrackedSkillFiles(metadata) {
|
|
65570
|
+
const files = [];
|
|
65571
|
+
if (metadata.kits && typeof metadata.kits === "object") {
|
|
65572
|
+
for (const kit of Object.values(metadata.kits)) {
|
|
65573
|
+
if (kit.files) {
|
|
65574
|
+
files.push(...kit.files.filter((f3) => isSkillPath(f3.path)));
|
|
65575
|
+
}
|
|
65576
|
+
}
|
|
65577
|
+
}
|
|
65578
|
+
if (Array.isArray(metadata.files)) {
|
|
65579
|
+
files.push(...metadata.files.filter((f3) => isSkillPath(f3.path)));
|
|
65580
|
+
}
|
|
65581
|
+
return files;
|
|
65582
|
+
}
|
|
65583
|
+
function isSkillPath(path14) {
|
|
65584
|
+
const normalized = path14.replace(/\\/g, "/");
|
|
65585
|
+
return normalized.startsWith("skills/");
|
|
65586
|
+
}
|
|
65587
|
+
function extractSkillDir(path14) {
|
|
65588
|
+
const normalized = path14.replace(/\\/g, "/");
|
|
65589
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
65590
|
+
if (parts.length < 2 || parts[0] !== "skills") {
|
|
65591
|
+
return null;
|
|
65592
|
+
}
|
|
65593
|
+
return `skills/${parts[1]}`;
|
|
65594
|
+
}
|
|
65595
|
+
var import_fs_extra31;
|
|
65596
|
+
var init_skill_migration_merger = __esm(() => {
|
|
65597
|
+
init_logger();
|
|
65598
|
+
import_fs_extra31 = __toESM(require_lib3(), 1);
|
|
65599
|
+
});
|
|
65600
|
+
|
|
64668
65601
|
// src/domains/help/commands/common-options.ts
|
|
64669
65602
|
var filterOptionsGroup, folderOptionsGroup;
|
|
64670
65603
|
var init_common_options = __esm(() => {
|
|
@@ -65782,7 +66715,7 @@ function getPagerArgs(pagerCmd) {
|
|
|
65782
66715
|
return [];
|
|
65783
66716
|
}
|
|
65784
66717
|
async function trySystemPager(content) {
|
|
65785
|
-
return new Promise((
|
|
66718
|
+
return new Promise((resolve27) => {
|
|
65786
66719
|
const pagerCmd = process.env.PAGER || "less";
|
|
65787
66720
|
const pagerArgs = getPagerArgs(pagerCmd);
|
|
65788
66721
|
try {
|
|
@@ -65792,20 +66725,20 @@ async function trySystemPager(content) {
|
|
|
65792
66725
|
});
|
|
65793
66726
|
const timeout2 = setTimeout(() => {
|
|
65794
66727
|
pager.kill();
|
|
65795
|
-
|
|
66728
|
+
resolve27(false);
|
|
65796
66729
|
}, 30000);
|
|
65797
66730
|
pager.stdin.write(content);
|
|
65798
66731
|
pager.stdin.end();
|
|
65799
66732
|
pager.on("close", (code2) => {
|
|
65800
66733
|
clearTimeout(timeout2);
|
|
65801
|
-
|
|
66734
|
+
resolve27(code2 === 0);
|
|
65802
66735
|
});
|
|
65803
66736
|
pager.on("error", () => {
|
|
65804
66737
|
clearTimeout(timeout2);
|
|
65805
|
-
|
|
66738
|
+
resolve27(false);
|
|
65806
66739
|
});
|
|
65807
66740
|
} catch {
|
|
65808
|
-
|
|
66741
|
+
resolve27(false);
|
|
65809
66742
|
}
|
|
65810
66743
|
});
|
|
65811
66744
|
}
|
|
@@ -65832,16 +66765,16 @@ async function basicPager(content) {
|
|
|
65832
66765
|
break;
|
|
65833
66766
|
}
|
|
65834
66767
|
const remaining = lines.length - currentLine;
|
|
65835
|
-
await new Promise((
|
|
66768
|
+
await new Promise((resolve27) => {
|
|
65836
66769
|
rl.question(`-- More (${remaining} lines) [Enter/q] --`, (answer) => {
|
|
65837
66770
|
if (answer.toLowerCase() === "q") {
|
|
65838
66771
|
rl.close();
|
|
65839
66772
|
process.exitCode = 0;
|
|
65840
|
-
|
|
66773
|
+
resolve27();
|
|
65841
66774
|
return;
|
|
65842
66775
|
}
|
|
65843
66776
|
process.stdout.write("\x1B[1A\x1B[2K");
|
|
65844
|
-
|
|
66777
|
+
resolve27();
|
|
65845
66778
|
});
|
|
65846
66779
|
});
|
|
65847
66780
|
}
|
|
@@ -72514,7 +73447,7 @@ class ConfigVersionChecker {
|
|
|
72514
73447
|
}
|
|
72515
73448
|
// src/domains/sync/sync-engine.ts
|
|
72516
73449
|
import { lstat as lstat3, readFile as readFile34, readlink, realpath as realpath3, stat as stat9 } from "node:fs/promises";
|
|
72517
|
-
import {
|
|
73450
|
+
import { dirname as dirname15, isAbsolute as isAbsolute3, normalize as normalize7, relative as relative7, resolve as resolve13 } from "node:path";
|
|
72518
73451
|
|
|
72519
73452
|
// src/services/file-operations/ownership-checker.ts
|
|
72520
73453
|
init_metadata_migration();
|
|
@@ -73875,7 +74808,7 @@ async function validateSymlinkChain(path5, basePath, maxDepth = MAX_SYMLINK_DEPT
|
|
|
73875
74808
|
if (!stats.isSymbolicLink())
|
|
73876
74809
|
break;
|
|
73877
74810
|
const target = await readlink(current);
|
|
73878
|
-
const resolvedTarget = isAbsolute3(target) ? target :
|
|
74811
|
+
const resolvedTarget = isAbsolute3(target) ? target : resolve13(dirname15(current), target);
|
|
73879
74812
|
const normalizedTarget = normalize7(resolvedTarget);
|
|
73880
74813
|
const rel = relative7(basePath, normalizedTarget);
|
|
73881
74814
|
if (rel.startsWith("..") || isAbsolute3(rel)) {
|
|
@@ -73894,6 +74827,10 @@ async function validateSymlinkChain(path5, basePath, maxDepth = MAX_SYMLINK_DEPT
|
|
|
73894
74827
|
throw new Error(`Symlink chain too deep (>${maxDepth}): ${path5}`);
|
|
73895
74828
|
}
|
|
73896
74829
|
}
|
|
74830
|
+
function isOutsideBase(candidatePath, basePath) {
|
|
74831
|
+
const rel = relative7(basePath, candidatePath);
|
|
74832
|
+
return rel.startsWith("..") || isAbsolute3(rel);
|
|
74833
|
+
}
|
|
73897
74834
|
async function validateSyncPath(basePath, filePath) {
|
|
73898
74835
|
if (!filePath || filePath.trim() === "") {
|
|
73899
74836
|
throw new Error("Empty file path not allowed");
|
|
@@ -73908,37 +74845,44 @@ async function validateSyncPath(basePath, filePath) {
|
|
|
73908
74845
|
if (isAbsolute3(normalized)) {
|
|
73909
74846
|
throw new Error(`Absolute paths not allowed: ${filePath}`);
|
|
73910
74847
|
}
|
|
73911
|
-
|
|
74848
|
+
const pathParts = normalized.split(/[\\/]+/).filter(Boolean);
|
|
74849
|
+
if (pathParts.includes("..")) {
|
|
73912
74850
|
throw new Error(`Path traversal not allowed: ${filePath}`);
|
|
73913
74851
|
}
|
|
73914
|
-
const
|
|
73915
|
-
const
|
|
73916
|
-
|
|
74852
|
+
const lexicalBase = resolve13(basePath);
|
|
74853
|
+
const fullPath = resolve13(basePath, normalized);
|
|
74854
|
+
const resolvedBase = await realpath3(basePath).catch(() => lexicalBase);
|
|
74855
|
+
if (isOutsideBase(fullPath, lexicalBase)) {
|
|
73917
74856
|
throw new Error(`Path escapes base directory: ${filePath}`);
|
|
73918
74857
|
}
|
|
73919
|
-
await validateSymlinkChain(fullPath,
|
|
74858
|
+
await validateSymlinkChain(fullPath, resolvedBase);
|
|
73920
74859
|
try {
|
|
73921
|
-
const resolvedBase = await realpath3(basePath);
|
|
73922
74860
|
const resolvedFull = await realpath3(fullPath);
|
|
73923
|
-
|
|
73924
|
-
if (resolvedRel.startsWith("..") || isAbsolute3(resolvedRel)) {
|
|
74861
|
+
if (isOutsideBase(resolvedFull, resolvedBase)) {
|
|
73925
74862
|
throw new Error(`Symlink escapes base directory: ${filePath}`);
|
|
73926
74863
|
}
|
|
73927
74864
|
} catch (error) {
|
|
73928
74865
|
if (error.code === "ENOENT") {
|
|
73929
|
-
|
|
73930
|
-
|
|
73931
|
-
|
|
73932
|
-
|
|
73933
|
-
|
|
73934
|
-
|
|
73935
|
-
|
|
73936
|
-
|
|
73937
|
-
|
|
73938
|
-
|
|
73939
|
-
|
|
74866
|
+
let ancestor = dirname15(fullPath);
|
|
74867
|
+
while (ancestor !== lexicalBase) {
|
|
74868
|
+
try {
|
|
74869
|
+
await lstat3(ancestor);
|
|
74870
|
+
break;
|
|
74871
|
+
} catch (ancestorError) {
|
|
74872
|
+
if (ancestorError.code !== "ENOENT") {
|
|
74873
|
+
throw ancestorError;
|
|
74874
|
+
}
|
|
74875
|
+
const nextAncestor = dirname15(ancestor);
|
|
74876
|
+
if (nextAncestor === ancestor) {
|
|
74877
|
+
break;
|
|
74878
|
+
}
|
|
74879
|
+
ancestor = nextAncestor;
|
|
73940
74880
|
}
|
|
73941
74881
|
}
|
|
74882
|
+
const resolvedAncestor = await realpath3(ancestor).catch(() => null);
|
|
74883
|
+
if (!resolvedAncestor || isOutsideBase(resolvedAncestor, resolvedBase)) {
|
|
74884
|
+
throw new Error(`Parent symlink escapes base directory: ${filePath}`);
|
|
74885
|
+
}
|
|
73942
74886
|
} else {
|
|
73943
74887
|
throw error;
|
|
73944
74888
|
}
|
|
@@ -74090,14 +75034,24 @@ class SyncEngine {
|
|
|
74090
75034
|
}
|
|
74091
75035
|
static async loadFileContent(filePath) {
|
|
74092
75036
|
try {
|
|
74093
|
-
const
|
|
74094
|
-
if (
|
|
75037
|
+
const beforeStats = await lstat3(filePath);
|
|
75038
|
+
if (beforeStats.isSymbolicLink()) {
|
|
74095
75039
|
throw new Error(`Symlink not allowed for sync: ${filePath}`);
|
|
74096
75040
|
}
|
|
74097
|
-
if (
|
|
74098
|
-
throw new Error(`File too large for sync (${Math.round(
|
|
75041
|
+
if (beforeStats.size > MAX_SYNC_FILE_SIZE) {
|
|
75042
|
+
throw new Error(`File too large for sync (${Math.round(beforeStats.size / 1024 / 1024)}MB > ${MAX_SYNC_FILE_SIZE / 1024 / 1024}MB limit)`);
|
|
74099
75043
|
}
|
|
74100
75044
|
const buffer = await readFile34(filePath);
|
|
75045
|
+
const afterStats = await lstat3(filePath);
|
|
75046
|
+
if (afterStats.isSymbolicLink()) {
|
|
75047
|
+
throw new Error(`File became symlink during read: ${filePath}`);
|
|
75048
|
+
}
|
|
75049
|
+
if (beforeStats.dev !== afterStats.dev || beforeStats.ino !== afterStats.ino) {
|
|
75050
|
+
throw new Error(`File changed identity during read: ${filePath}`);
|
|
75051
|
+
}
|
|
75052
|
+
if (beforeStats.mtimeMs !== afterStats.mtimeMs || beforeStats.size !== afterStats.size) {
|
|
75053
|
+
throw new Error(`File changed during read: ${filePath}`);
|
|
75054
|
+
}
|
|
74101
75055
|
if (buffer.includes(0)) {
|
|
74102
75056
|
return { content: "", isBinary: true };
|
|
74103
75057
|
}
|
|
@@ -75108,7 +76062,7 @@ init_logger();
|
|
|
75108
76062
|
var import_proper_lockfile4 = __toESM(require_proper_lockfile(), 1);
|
|
75109
76063
|
import { mkdir as mkdir19 } from "node:fs/promises";
|
|
75110
76064
|
import os5 from "node:os";
|
|
75111
|
-
import { join as
|
|
76065
|
+
import { join as join64 } from "node:path";
|
|
75112
76066
|
var LOCK_CONFIG = {
|
|
75113
76067
|
stale: 60000,
|
|
75114
76068
|
retries: 0
|
|
@@ -75116,12 +76070,12 @@ var LOCK_CONFIG = {
|
|
|
75116
76070
|
var activeLocks = new Set;
|
|
75117
76071
|
var cleanupRegistered = false;
|
|
75118
76072
|
function getLocksDir() {
|
|
75119
|
-
return
|
|
76073
|
+
return join64(os5.homedir(), ".claudekit", "locks");
|
|
75120
76074
|
}
|
|
75121
76075
|
function cleanupLocks() {
|
|
75122
76076
|
for (const name of activeLocks) {
|
|
75123
76077
|
try {
|
|
75124
|
-
const lockPath =
|
|
76078
|
+
const lockPath = join64(getLocksDir(), `${name}.lock`);
|
|
75125
76079
|
import_proper_lockfile4.default.unlockSync(lockPath, { realpath: false });
|
|
75126
76080
|
} catch {
|
|
75127
76081
|
try {
|
|
@@ -75144,7 +76098,7 @@ async function ensureLocksDir() {
|
|
|
75144
76098
|
async function withProcessLock(lockName, fn) {
|
|
75145
76099
|
registerCleanupHandlers();
|
|
75146
76100
|
await ensureLocksDir();
|
|
75147
|
-
const lockPath =
|
|
76101
|
+
const lockPath = join64(getLocksDir(), `${lockName}.lock`);
|
|
75148
76102
|
let release;
|
|
75149
76103
|
try {
|
|
75150
76104
|
release = await import_proper_lockfile4.default.lock(lockPath, { ...LOCK_CONFIG, realpath: false });
|
|
@@ -75175,7 +76129,7 @@ init_logger();
|
|
|
75175
76129
|
init_logger();
|
|
75176
76130
|
init_path_resolver();
|
|
75177
76131
|
var import_fs_extra7 = __toESM(require_lib3(), 1);
|
|
75178
|
-
import { join as
|
|
76132
|
+
import { join as join65 } from "node:path";
|
|
75179
76133
|
async function handleConflicts(ctx) {
|
|
75180
76134
|
if (ctx.cancelled)
|
|
75181
76135
|
return ctx;
|
|
@@ -75184,7 +76138,7 @@ async function handleConflicts(ctx) {
|
|
|
75184
76138
|
if (PathResolver.isLocalSameAsGlobal()) {
|
|
75185
76139
|
return ctx;
|
|
75186
76140
|
}
|
|
75187
|
-
const localSettingsPath =
|
|
76141
|
+
const localSettingsPath = join65(process.cwd(), ".claude", "settings.json");
|
|
75188
76142
|
if (!await import_fs_extra7.pathExists(localSettingsPath)) {
|
|
75189
76143
|
return ctx;
|
|
75190
76144
|
}
|
|
@@ -75199,7 +76153,7 @@ async function handleConflicts(ctx) {
|
|
|
75199
76153
|
return { ...ctx, cancelled: true };
|
|
75200
76154
|
}
|
|
75201
76155
|
if (choice === "remove") {
|
|
75202
|
-
const localClaudeDir =
|
|
76156
|
+
const localClaudeDir = join65(process.cwd(), ".claude");
|
|
75203
76157
|
try {
|
|
75204
76158
|
await import_fs_extra7.remove(localClaudeDir);
|
|
75205
76159
|
logger.success("Removed local .claude/ directory");
|
|
@@ -75296,7 +76250,7 @@ init_logger();
|
|
|
75296
76250
|
init_safe_spinner();
|
|
75297
76251
|
import { mkdir as mkdir25, stat as stat12 } from "node:fs/promises";
|
|
75298
76252
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
75299
|
-
import { join as
|
|
76253
|
+
import { join as join72 } from "node:path";
|
|
75300
76254
|
|
|
75301
76255
|
// src/shared/temp-cleanup.ts
|
|
75302
76256
|
init_logger();
|
|
@@ -75315,7 +76269,7 @@ init_logger();
|
|
|
75315
76269
|
init_output_manager();
|
|
75316
76270
|
import { createWriteStream as createWriteStream2, rmSync } from "node:fs";
|
|
75317
76271
|
import { mkdir as mkdir20 } from "node:fs/promises";
|
|
75318
|
-
import { join as
|
|
76272
|
+
import { join as join66 } from "node:path";
|
|
75319
76273
|
|
|
75320
76274
|
// src/shared/progress-bar.ts
|
|
75321
76275
|
init_output_manager();
|
|
@@ -75480,10 +76434,10 @@ init_types3();
|
|
|
75480
76434
|
// src/domains/installation/utils/path-security.ts
|
|
75481
76435
|
init_types3();
|
|
75482
76436
|
import { lstatSync as lstatSync2, realpathSync as realpathSync2 } from "node:fs";
|
|
75483
|
-
import { relative as relative8, resolve as
|
|
76437
|
+
import { relative as relative8, resolve as resolve16 } from "node:path";
|
|
75484
76438
|
var MAX_EXTRACTION_SIZE = 500 * 1024 * 1024;
|
|
75485
76439
|
function isPathSafe(basePath, targetPath) {
|
|
75486
|
-
const resolvedBase =
|
|
76440
|
+
const resolvedBase = resolve16(basePath);
|
|
75487
76441
|
try {
|
|
75488
76442
|
const stat10 = lstatSync2(targetPath);
|
|
75489
76443
|
if (stat10.isSymbolicLink()) {
|
|
@@ -75493,7 +76447,7 @@ function isPathSafe(basePath, targetPath) {
|
|
|
75493
76447
|
}
|
|
75494
76448
|
}
|
|
75495
76449
|
} catch {}
|
|
75496
|
-
const resolvedTarget =
|
|
76450
|
+
const resolvedTarget = resolve16(targetPath);
|
|
75497
76451
|
const relativePath = relative8(resolvedBase, resolvedTarget);
|
|
75498
76452
|
return !relativePath.startsWith("..") && !relativePath.startsWith("/") && resolvedTarget.startsWith(resolvedBase);
|
|
75499
76453
|
}
|
|
@@ -75525,7 +76479,7 @@ var MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024;
|
|
|
75525
76479
|
class FileDownloader {
|
|
75526
76480
|
async downloadAsset(asset, destDir) {
|
|
75527
76481
|
try {
|
|
75528
|
-
const destPath =
|
|
76482
|
+
const destPath = join66(destDir, asset.name);
|
|
75529
76483
|
await mkdir20(destDir, { recursive: true });
|
|
75530
76484
|
output.info(`Downloading ${asset.name} (${formatBytes(asset.size)})...`);
|
|
75531
76485
|
logger.verbose("Download details", {
|
|
@@ -75581,7 +76535,7 @@ class FileDownloader {
|
|
|
75581
76535
|
}
|
|
75582
76536
|
if (downloadedSize !== totalSize) {
|
|
75583
76537
|
fileStream.end();
|
|
75584
|
-
await new Promise((
|
|
76538
|
+
await new Promise((resolve17) => fileStream.once("close", resolve17));
|
|
75585
76539
|
try {
|
|
75586
76540
|
rmSync(destPath, { force: true });
|
|
75587
76541
|
} catch (cleanupError) {
|
|
@@ -75595,7 +76549,7 @@ class FileDownloader {
|
|
|
75595
76549
|
return destPath;
|
|
75596
76550
|
} catch (error) {
|
|
75597
76551
|
fileStream.end();
|
|
75598
|
-
await new Promise((
|
|
76552
|
+
await new Promise((resolve17) => fileStream.once("close", resolve17));
|
|
75599
76553
|
try {
|
|
75600
76554
|
rmSync(destPath, { force: true });
|
|
75601
76555
|
} catch (cleanupError) {
|
|
@@ -75610,7 +76564,7 @@ class FileDownloader {
|
|
|
75610
76564
|
}
|
|
75611
76565
|
async downloadFile(params) {
|
|
75612
76566
|
const { url, name, size, destDir, token } = params;
|
|
75613
|
-
const destPath =
|
|
76567
|
+
const destPath = join66(destDir, name);
|
|
75614
76568
|
await mkdir20(destDir, { recursive: true });
|
|
75615
76569
|
output.info(`Downloading ${name}${size ? ` (${formatBytes(size)})` : ""}...`);
|
|
75616
76570
|
const headers = {};
|
|
@@ -75661,7 +76615,7 @@ class FileDownloader {
|
|
|
75661
76615
|
const expectedSize = Number(response.headers.get("content-length"));
|
|
75662
76616
|
if (expectedSize > 0 && downloadedSize !== expectedSize) {
|
|
75663
76617
|
fileStream.end();
|
|
75664
|
-
await new Promise((
|
|
76618
|
+
await new Promise((resolve17) => fileStream.once("close", resolve17));
|
|
75665
76619
|
try {
|
|
75666
76620
|
rmSync(destPath, { force: true });
|
|
75667
76621
|
} catch (cleanupError) {
|
|
@@ -75679,7 +76633,7 @@ class FileDownloader {
|
|
|
75679
76633
|
return destPath;
|
|
75680
76634
|
} catch (error) {
|
|
75681
76635
|
fileStream.end();
|
|
75682
|
-
await new Promise((
|
|
76636
|
+
await new Promise((resolve17) => fileStream.once("close", resolve17));
|
|
75683
76637
|
try {
|
|
75684
76638
|
rmSync(destPath, { force: true });
|
|
75685
76639
|
} catch (cleanupError) {
|
|
@@ -75696,7 +76650,7 @@ init_logger();
|
|
|
75696
76650
|
init_types3();
|
|
75697
76651
|
import { constants as constants4 } from "node:fs";
|
|
75698
76652
|
import { access as access4, readdir as readdir13 } from "node:fs/promises";
|
|
75699
|
-
import { join as
|
|
76653
|
+
import { join as join67 } from "node:path";
|
|
75700
76654
|
async function validateExtraction(extractDir) {
|
|
75701
76655
|
try {
|
|
75702
76656
|
const entries = await readdir13(extractDir, { encoding: "utf8" });
|
|
@@ -75708,7 +76662,7 @@ async function validateExtraction(extractDir) {
|
|
|
75708
76662
|
const missingPaths = [];
|
|
75709
76663
|
for (const path5 of criticalPaths) {
|
|
75710
76664
|
try {
|
|
75711
|
-
await access4(
|
|
76665
|
+
await access4(join67(extractDir, path5), constants4.F_OK);
|
|
75712
76666
|
logger.debug(`Found: ${path5}`);
|
|
75713
76667
|
} catch {
|
|
75714
76668
|
logger.warning(`Expected path not found: ${path5}`);
|
|
@@ -75730,7 +76684,7 @@ async function validateExtraction(extractDir) {
|
|
|
75730
76684
|
// src/domains/installation/extraction/tar-extractor.ts
|
|
75731
76685
|
init_logger();
|
|
75732
76686
|
import { copyFile as copyFile4, mkdir as mkdir23, readdir as readdir15, rm as rm8, stat as stat10 } from "node:fs/promises";
|
|
75733
|
-
import { join as
|
|
76687
|
+
import { join as join70 } from "node:path";
|
|
75734
76688
|
|
|
75735
76689
|
// node_modules/@isaacs/fs-minipass/dist/esm/index.js
|
|
75736
76690
|
import EE from "events";
|
|
@@ -76278,10 +77232,10 @@ class Minipass extends EventEmitter3 {
|
|
|
76278
77232
|
return this[ENCODING] ? buf.join("") : Buffer.concat(buf, buf.dataLength);
|
|
76279
77233
|
}
|
|
76280
77234
|
async promise() {
|
|
76281
|
-
return new Promise((
|
|
77235
|
+
return new Promise((resolve17, reject) => {
|
|
76282
77236
|
this.on(DESTROYED, () => reject(new Error("stream destroyed")));
|
|
76283
77237
|
this.on("error", (er) => reject(er));
|
|
76284
|
-
this.on("end", () =>
|
|
77238
|
+
this.on("end", () => resolve17());
|
|
76285
77239
|
});
|
|
76286
77240
|
}
|
|
76287
77241
|
[Symbol.asyncIterator]() {
|
|
@@ -76300,7 +77254,7 @@ class Minipass extends EventEmitter3 {
|
|
|
76300
77254
|
return Promise.resolve({ done: false, value: res });
|
|
76301
77255
|
if (this[EOF])
|
|
76302
77256
|
return stop();
|
|
76303
|
-
let
|
|
77257
|
+
let resolve17;
|
|
76304
77258
|
let reject;
|
|
76305
77259
|
const onerr = (er) => {
|
|
76306
77260
|
this.off("data", ondata);
|
|
@@ -76314,19 +77268,19 @@ class Minipass extends EventEmitter3 {
|
|
|
76314
77268
|
this.off("end", onend);
|
|
76315
77269
|
this.off(DESTROYED, ondestroy);
|
|
76316
77270
|
this.pause();
|
|
76317
|
-
|
|
77271
|
+
resolve17({ value, done: !!this[EOF] });
|
|
76318
77272
|
};
|
|
76319
77273
|
const onend = () => {
|
|
76320
77274
|
this.off("error", onerr);
|
|
76321
77275
|
this.off("data", ondata);
|
|
76322
77276
|
this.off(DESTROYED, ondestroy);
|
|
76323
77277
|
stop();
|
|
76324
|
-
|
|
77278
|
+
resolve17({ done: true, value: undefined });
|
|
76325
77279
|
};
|
|
76326
77280
|
const ondestroy = () => onerr(new Error("stream destroyed"));
|
|
76327
77281
|
return new Promise((res2, rej) => {
|
|
76328
77282
|
reject = rej;
|
|
76329
|
-
|
|
77283
|
+
resolve17 = res2;
|
|
76330
77284
|
this.once(DESTROYED, ondestroy);
|
|
76331
77285
|
this.once("error", onerr);
|
|
76332
77286
|
this.once("end", onend);
|
|
@@ -76783,7 +77737,7 @@ import path7 from "node:path";
|
|
|
76783
77737
|
|
|
76784
77738
|
// node_modules/tar/dist/esm/list.js
|
|
76785
77739
|
import fs9 from "node:fs";
|
|
76786
|
-
import { dirname as
|
|
77740
|
+
import { dirname as dirname18, parse as parse3 } from "path";
|
|
76787
77741
|
|
|
76788
77742
|
// node_modules/tar/dist/esm/options.js
|
|
76789
77743
|
var argmap = new Map([
|
|
@@ -77432,10 +78386,10 @@ class Minipass2 extends EventEmitter4 {
|
|
|
77432
78386
|
return this[ENCODING2] ? buf.join("") : Buffer.concat(buf, buf.dataLength);
|
|
77433
78387
|
}
|
|
77434
78388
|
async promise() {
|
|
77435
|
-
return new Promise((
|
|
78389
|
+
return new Promise((resolve17, reject) => {
|
|
77436
78390
|
this.on(DESTROYED2, () => reject(new Error("stream destroyed")));
|
|
77437
78391
|
this.on("error", (er) => reject(er));
|
|
77438
|
-
this.on("end", () =>
|
|
78392
|
+
this.on("end", () => resolve17());
|
|
77439
78393
|
});
|
|
77440
78394
|
}
|
|
77441
78395
|
[Symbol.asyncIterator]() {
|
|
@@ -77454,7 +78408,7 @@ class Minipass2 extends EventEmitter4 {
|
|
|
77454
78408
|
return Promise.resolve({ done: false, value: res });
|
|
77455
78409
|
if (this[EOF2])
|
|
77456
78410
|
return stop();
|
|
77457
|
-
let
|
|
78411
|
+
let resolve17;
|
|
77458
78412
|
let reject;
|
|
77459
78413
|
const onerr = (er) => {
|
|
77460
78414
|
this.off("data", ondata);
|
|
@@ -77468,19 +78422,19 @@ class Minipass2 extends EventEmitter4 {
|
|
|
77468
78422
|
this.off("end", onend);
|
|
77469
78423
|
this.off(DESTROYED2, ondestroy);
|
|
77470
78424
|
this.pause();
|
|
77471
|
-
|
|
78425
|
+
resolve17({ value, done: !!this[EOF2] });
|
|
77472
78426
|
};
|
|
77473
78427
|
const onend = () => {
|
|
77474
78428
|
this.off("error", onerr);
|
|
77475
78429
|
this.off("data", ondata);
|
|
77476
78430
|
this.off(DESTROYED2, ondestroy);
|
|
77477
78431
|
stop();
|
|
77478
|
-
|
|
78432
|
+
resolve17({ done: true, value: undefined });
|
|
77479
78433
|
};
|
|
77480
78434
|
const ondestroy = () => onerr(new Error("stream destroyed"));
|
|
77481
78435
|
return new Promise((res2, rej) => {
|
|
77482
78436
|
reject = rej;
|
|
77483
|
-
|
|
78437
|
+
resolve17 = res2;
|
|
77484
78438
|
this.once(DESTROYED2, ondestroy);
|
|
77485
78439
|
this.once("error", onerr);
|
|
77486
78440
|
this.once("end", onend);
|
|
@@ -78908,10 +79862,10 @@ class Minipass3 extends EventEmitter5 {
|
|
|
78908
79862
|
return this[ENCODING3] ? buf.join("") : Buffer.concat(buf, buf.dataLength);
|
|
78909
79863
|
}
|
|
78910
79864
|
async promise() {
|
|
78911
|
-
return new Promise((
|
|
79865
|
+
return new Promise((resolve17, reject) => {
|
|
78912
79866
|
this.on(DESTROYED3, () => reject(new Error("stream destroyed")));
|
|
78913
79867
|
this.on("error", (er) => reject(er));
|
|
78914
|
-
this.on("end", () =>
|
|
79868
|
+
this.on("end", () => resolve17());
|
|
78915
79869
|
});
|
|
78916
79870
|
}
|
|
78917
79871
|
[Symbol.asyncIterator]() {
|
|
@@ -78930,7 +79884,7 @@ class Minipass3 extends EventEmitter5 {
|
|
|
78930
79884
|
return Promise.resolve({ done: false, value: res });
|
|
78931
79885
|
if (this[EOF3])
|
|
78932
79886
|
return stop();
|
|
78933
|
-
let
|
|
79887
|
+
let resolve17;
|
|
78934
79888
|
let reject;
|
|
78935
79889
|
const onerr = (er) => {
|
|
78936
79890
|
this.off("data", ondata);
|
|
@@ -78944,19 +79898,19 @@ class Minipass3 extends EventEmitter5 {
|
|
|
78944
79898
|
this.off("end", onend);
|
|
78945
79899
|
this.off(DESTROYED3, ondestroy);
|
|
78946
79900
|
this.pause();
|
|
78947
|
-
|
|
79901
|
+
resolve17({ value, done: !!this[EOF3] });
|
|
78948
79902
|
};
|
|
78949
79903
|
const onend = () => {
|
|
78950
79904
|
this.off("error", onerr);
|
|
78951
79905
|
this.off("data", ondata);
|
|
78952
79906
|
this.off(DESTROYED3, ondestroy);
|
|
78953
79907
|
stop();
|
|
78954
|
-
|
|
79908
|
+
resolve17({ done: true, value: undefined });
|
|
78955
79909
|
};
|
|
78956
79910
|
const ondestroy = () => onerr(new Error("stream destroyed"));
|
|
78957
79911
|
return new Promise((res2, rej) => {
|
|
78958
79912
|
reject = rej;
|
|
78959
|
-
|
|
79913
|
+
resolve17 = res2;
|
|
78960
79914
|
this.once(DESTROYED3, ondestroy);
|
|
78961
79915
|
this.once("error", onerr);
|
|
78962
79916
|
this.once("end", onend);
|
|
@@ -79683,7 +80637,7 @@ var filesFilter = (opt, files) => {
|
|
|
79683
80637
|
if (m2 !== undefined) {
|
|
79684
80638
|
ret = m2;
|
|
79685
80639
|
} else {
|
|
79686
|
-
ret = mapHas(
|
|
80640
|
+
ret = mapHas(dirname18(file), root);
|
|
79687
80641
|
}
|
|
79688
80642
|
}
|
|
79689
80643
|
map.set(file, ret);
|
|
@@ -79727,9 +80681,9 @@ var listFile = (opt, _files) => {
|
|
|
79727
80681
|
const parse4 = new Parser(opt);
|
|
79728
80682
|
const readSize = opt.maxReadSize || 16 * 1024 * 1024;
|
|
79729
80683
|
const file = opt.file;
|
|
79730
|
-
const p = new Promise((
|
|
80684
|
+
const p = new Promise((resolve17, reject) => {
|
|
79731
80685
|
parse4.on("error", reject);
|
|
79732
|
-
parse4.on("end",
|
|
80686
|
+
parse4.on("end", resolve17);
|
|
79733
80687
|
fs9.stat(file, (er, stat10) => {
|
|
79734
80688
|
if (er) {
|
|
79735
80689
|
reject(er);
|
|
@@ -81492,7 +82446,7 @@ var mkdirSync = (dir, opt) => {
|
|
|
81492
82446
|
};
|
|
81493
82447
|
|
|
81494
82448
|
// node_modules/tar/dist/esm/path-reservations.js
|
|
81495
|
-
import { join as
|
|
82449
|
+
import { join as join68 } from "node:path";
|
|
81496
82450
|
|
|
81497
82451
|
// node_modules/tar/dist/esm/normalize-unicode.js
|
|
81498
82452
|
var normalizeCache = Object.create(null);
|
|
@@ -81525,7 +82479,7 @@ var getDirs = (path10) => {
|
|
|
81525
82479
|
const dirs = path10.split("/").slice(0, -1).reduce((set, path11) => {
|
|
81526
82480
|
const s = set[set.length - 1];
|
|
81527
82481
|
if (s !== undefined) {
|
|
81528
|
-
path11 =
|
|
82482
|
+
path11 = join68(s, path11);
|
|
81529
82483
|
}
|
|
81530
82484
|
set.push(path11 || "/");
|
|
81531
82485
|
return set;
|
|
@@ -81539,7 +82493,7 @@ class PathReservations {
|
|
|
81539
82493
|
#running = new Set;
|
|
81540
82494
|
reserve(paths, fn) {
|
|
81541
82495
|
paths = isWindows4 ? ["win32 parallelization disabled"] : paths.map((p) => {
|
|
81542
|
-
return stripTrailingSlashes(
|
|
82496
|
+
return stripTrailingSlashes(join68(normalizeUnicode(p))).toLowerCase();
|
|
81543
82497
|
});
|
|
81544
82498
|
const dirs = new Set(paths.map((path10) => getDirs(path10)).reduce((a3, b3) => a3.concat(b3)));
|
|
81545
82499
|
this.#reservations.set(fn, { dirs, paths });
|
|
@@ -82309,9 +83263,9 @@ var extractFile = (opt, _3) => {
|
|
|
82309
83263
|
const u = new Unpack(opt);
|
|
82310
83264
|
const readSize = opt.maxReadSize || 16 * 1024 * 1024;
|
|
82311
83265
|
const file = opt.file;
|
|
82312
|
-
const p = new Promise((
|
|
83266
|
+
const p = new Promise((resolve17, reject) => {
|
|
82313
83267
|
u.on("error", reject);
|
|
82314
|
-
u.on("close",
|
|
83268
|
+
u.on("close", resolve17);
|
|
82315
83269
|
fs16.stat(file, (er, stat10) => {
|
|
82316
83270
|
if (er) {
|
|
82317
83271
|
reject(er);
|
|
@@ -82444,7 +83398,7 @@ var replaceAsync = (opt, files) => {
|
|
|
82444
83398
|
};
|
|
82445
83399
|
fs17.read(fd, headBuf, 0, 512, position, onread);
|
|
82446
83400
|
};
|
|
82447
|
-
const promise = new Promise((
|
|
83401
|
+
const promise = new Promise((resolve17, reject) => {
|
|
82448
83402
|
p.on("error", reject);
|
|
82449
83403
|
let flag = "r+";
|
|
82450
83404
|
const onopen = (er, fd) => {
|
|
@@ -82469,7 +83423,7 @@ var replaceAsync = (opt, files) => {
|
|
|
82469
83423
|
});
|
|
82470
83424
|
p.pipe(stream);
|
|
82471
83425
|
stream.on("error", reject);
|
|
82472
|
-
stream.on("close",
|
|
83426
|
+
stream.on("close", resolve17);
|
|
82473
83427
|
addFilesAsync2(p, files);
|
|
82474
83428
|
});
|
|
82475
83429
|
});
|
|
@@ -82599,7 +83553,7 @@ function decodeFilePath(path12) {
|
|
|
82599
83553
|
init_logger();
|
|
82600
83554
|
init_types3();
|
|
82601
83555
|
import { copyFile as copyFile3, lstat as lstat4, mkdir as mkdir22, readdir as readdir14 } from "node:fs/promises";
|
|
82602
|
-
import { join as
|
|
83556
|
+
import { join as join69, relative as relative9 } from "node:path";
|
|
82603
83557
|
async function withRetry(fn, retries = 3) {
|
|
82604
83558
|
for (let i = 0;i < retries; i++) {
|
|
82605
83559
|
try {
|
|
@@ -82621,8 +83575,8 @@ async function moveDirectoryContents(sourceDir, destDir, shouldExclude, sizeTrac
|
|
|
82621
83575
|
await mkdir22(destDir, { recursive: true });
|
|
82622
83576
|
const entries = await readdir14(sourceDir, { encoding: "utf8" });
|
|
82623
83577
|
for (const entry of entries) {
|
|
82624
|
-
const sourcePath =
|
|
82625
|
-
const destPath =
|
|
83578
|
+
const sourcePath = join69(sourceDir, entry);
|
|
83579
|
+
const destPath = join69(destDir, entry);
|
|
82626
83580
|
const relativePath = relative9(sourceDir, sourcePath);
|
|
82627
83581
|
if (!isPathSafe(destDir, destPath)) {
|
|
82628
83582
|
logger.warning(`Skipping unsafe path: ${relativePath}`);
|
|
@@ -82649,8 +83603,8 @@ async function copyDirectory(sourceDir, destDir, shouldExclude, sizeTracker) {
|
|
|
82649
83603
|
await mkdir22(destDir, { recursive: true });
|
|
82650
83604
|
const entries = await readdir14(sourceDir, { encoding: "utf8" });
|
|
82651
83605
|
for (const entry of entries) {
|
|
82652
|
-
const sourcePath =
|
|
82653
|
-
const destPath =
|
|
83606
|
+
const sourcePath = join69(sourceDir, entry);
|
|
83607
|
+
const destPath = join69(destDir, entry);
|
|
82654
83608
|
const relativePath = relative9(sourceDir, sourcePath);
|
|
82655
83609
|
if (!isPathSafe(destDir, destPath)) {
|
|
82656
83610
|
logger.warning(`Skipping unsafe path: ${relativePath}`);
|
|
@@ -82698,7 +83652,7 @@ class TarExtractor {
|
|
|
82698
83652
|
logger.debug(`Root entries: ${entries.join(", ")}`);
|
|
82699
83653
|
if (entries.length === 1) {
|
|
82700
83654
|
const rootEntry = entries[0];
|
|
82701
|
-
const rootPath =
|
|
83655
|
+
const rootPath = join70(tempExtractDir, rootEntry);
|
|
82702
83656
|
const rootStat = await stat10(rootPath);
|
|
82703
83657
|
if (rootStat.isDirectory()) {
|
|
82704
83658
|
const rootContents = await readdir15(rootPath, { encoding: "utf8" });
|
|
@@ -82714,7 +83668,7 @@ class TarExtractor {
|
|
|
82714
83668
|
}
|
|
82715
83669
|
} else {
|
|
82716
83670
|
await mkdir23(destDir, { recursive: true });
|
|
82717
|
-
await copyFile4(rootPath,
|
|
83671
|
+
await copyFile4(rootPath, join70(destDir, rootEntry));
|
|
82718
83672
|
}
|
|
82719
83673
|
} else {
|
|
82720
83674
|
logger.debug("Multiple root entries - moving all");
|
|
@@ -82737,26 +83691,26 @@ init_logger();
|
|
|
82737
83691
|
var import_extract_zip = __toESM(require_extract_zip(), 1);
|
|
82738
83692
|
import { execFile as execFile8 } from "node:child_process";
|
|
82739
83693
|
import { copyFile as copyFile5, mkdir as mkdir24, readdir as readdir16, rm as rm9, stat as stat11 } from "node:fs/promises";
|
|
82740
|
-
import { join as
|
|
83694
|
+
import { join as join71 } from "node:path";
|
|
82741
83695
|
class ZipExtractor {
|
|
82742
83696
|
async tryNativeUnzip(archivePath, destDir) {
|
|
82743
83697
|
if (!isMacOS()) {
|
|
82744
83698
|
return false;
|
|
82745
83699
|
}
|
|
82746
|
-
return new Promise((
|
|
83700
|
+
return new Promise((resolve17) => {
|
|
82747
83701
|
mkdir24(destDir, { recursive: true }).then(() => {
|
|
82748
83702
|
execFile8("unzip", ["-o", "-q", archivePath, "-d", destDir], (error, _stdout, stderr) => {
|
|
82749
83703
|
if (error) {
|
|
82750
83704
|
logger.debug(`Native unzip failed: ${stderr || error.message}`);
|
|
82751
|
-
|
|
83705
|
+
resolve17(false);
|
|
82752
83706
|
return;
|
|
82753
83707
|
}
|
|
82754
83708
|
logger.debug("Native unzip succeeded");
|
|
82755
|
-
|
|
83709
|
+
resolve17(true);
|
|
82756
83710
|
});
|
|
82757
83711
|
}).catch((err) => {
|
|
82758
83712
|
logger.debug(`Failed to create directory for native unzip: ${err.message}`);
|
|
82759
|
-
|
|
83713
|
+
resolve17(false);
|
|
82760
83714
|
});
|
|
82761
83715
|
});
|
|
82762
83716
|
}
|
|
@@ -82785,7 +83739,7 @@ class ZipExtractor {
|
|
|
82785
83739
|
logger.debug(`Root entries: ${entries.join(", ")}`);
|
|
82786
83740
|
if (entries.length === 1) {
|
|
82787
83741
|
const rootEntry = entries[0];
|
|
82788
|
-
const rootPath =
|
|
83742
|
+
const rootPath = join71(tempExtractDir, rootEntry);
|
|
82789
83743
|
const rootStat = await stat11(rootPath);
|
|
82790
83744
|
if (rootStat.isDirectory()) {
|
|
82791
83745
|
const rootContents = await readdir16(rootPath, { encoding: "utf8" });
|
|
@@ -82801,7 +83755,7 @@ class ZipExtractor {
|
|
|
82801
83755
|
}
|
|
82802
83756
|
} else {
|
|
82803
83757
|
await mkdir24(destDir, { recursive: true });
|
|
82804
|
-
await copyFile5(rootPath,
|
|
83758
|
+
await copyFile5(rootPath, join71(destDir, rootEntry));
|
|
82805
83759
|
}
|
|
82806
83760
|
} else {
|
|
82807
83761
|
logger.debug("Multiple root entries - moving all");
|
|
@@ -82900,7 +83854,7 @@ class DownloadManager {
|
|
|
82900
83854
|
async createTempDir() {
|
|
82901
83855
|
const timestamp = Date.now();
|
|
82902
83856
|
const counter = DownloadManager.tempDirCounter++;
|
|
82903
|
-
const primaryTempDir =
|
|
83857
|
+
const primaryTempDir = join72(tmpdir4(), `claudekit-${timestamp}-${counter}`);
|
|
82904
83858
|
try {
|
|
82905
83859
|
await mkdir25(primaryTempDir, { recursive: true });
|
|
82906
83860
|
logger.debug(`Created temp directory: ${primaryTempDir}`);
|
|
@@ -82917,7 +83871,7 @@ Solutions:
|
|
|
82917
83871
|
2. Set HOME environment variable
|
|
82918
83872
|
3. Try running from a different directory`);
|
|
82919
83873
|
}
|
|
82920
|
-
const fallbackTempDir =
|
|
83874
|
+
const fallbackTempDir = join72(homeDir, ".claudekit", "tmp", `claudekit-${timestamp}-${counter}`);
|
|
82921
83875
|
try {
|
|
82922
83876
|
await mkdir25(fallbackTempDir, { recursive: true });
|
|
82923
83877
|
logger.debug(`Created temp directory (fallback): ${fallbackTempDir}`);
|
|
@@ -83266,349 +84220,8 @@ async function handleDownload(ctx) {
|
|
|
83266
84220
|
};
|
|
83267
84221
|
}
|
|
83268
84222
|
// src/commands/init/phases/merge-handler.ts
|
|
83269
|
-
|
|
83270
|
-
|
|
83271
|
-
// src/domains/installation/deletion-handler.ts
|
|
83272
|
-
import { existsSync as existsSync48, lstatSync as lstatSync3, readdirSync as readdirSync3, rmSync as rmSync2, rmdirSync, unlinkSync as unlinkSync3 } from "node:fs";
|
|
83273
|
-
import { dirname as dirname18, join as join76, relative as relative10, resolve as resolve17, sep as sep3 } from "node:path";
|
|
83274
|
-
|
|
83275
|
-
// src/services/file-operations/manifest/manifest-reader.ts
|
|
83276
|
-
init_metadata_migration();
|
|
83277
|
-
init_logger();
|
|
83278
|
-
init_types3();
|
|
83279
|
-
var import_fs_extra8 = __toESM(require_lib3(), 1);
|
|
83280
|
-
import { join as join75 } from "node:path";
|
|
83281
|
-
async function readManifest(claudeDir2) {
|
|
83282
|
-
const metadataPath = join75(claudeDir2, "metadata.json");
|
|
83283
|
-
if (!await import_fs_extra8.pathExists(metadataPath)) {
|
|
83284
|
-
return null;
|
|
83285
|
-
}
|
|
83286
|
-
try {
|
|
83287
|
-
const content = await import_fs_extra8.readFile(metadataPath, "utf-8");
|
|
83288
|
-
const parsed = JSON.parse(content);
|
|
83289
|
-
return MetadataSchema.parse(parsed);
|
|
83290
|
-
} catch (error) {
|
|
83291
|
-
logger.debug(`Failed to read manifest: ${error}`);
|
|
83292
|
-
return null;
|
|
83293
|
-
}
|
|
83294
|
-
}
|
|
83295
|
-
async function readKitManifest(claudeDir2, kit) {
|
|
83296
|
-
const metadata = await readManifest(claudeDir2);
|
|
83297
|
-
if (!metadata)
|
|
83298
|
-
return null;
|
|
83299
|
-
return getKitMetadata(metadata, kit);
|
|
83300
|
-
}
|
|
83301
|
-
async function findFileInInstalledKits(claudeDir2, relativePath, excludeKit) {
|
|
83302
|
-
const metadata = await readManifest(claudeDir2);
|
|
83303
|
-
if (!metadata?.kits) {
|
|
83304
|
-
return {
|
|
83305
|
-
exists: false,
|
|
83306
|
-
ownerKit: null,
|
|
83307
|
-
checksum: null,
|
|
83308
|
-
version: null,
|
|
83309
|
-
sourceTimestamp: null,
|
|
83310
|
-
installedAt: null
|
|
83311
|
-
};
|
|
83312
|
-
}
|
|
83313
|
-
for (const [kitName, kitMeta] of Object.entries(metadata.kits)) {
|
|
83314
|
-
const kit = kitName;
|
|
83315
|
-
if (kit === excludeKit)
|
|
83316
|
-
continue;
|
|
83317
|
-
if (!kitMeta.files)
|
|
83318
|
-
continue;
|
|
83319
|
-
const file = kitMeta.files.find((f3) => f3.path === relativePath);
|
|
83320
|
-
if (file) {
|
|
83321
|
-
return {
|
|
83322
|
-
exists: true,
|
|
83323
|
-
ownerKit: kit,
|
|
83324
|
-
checksum: file.checksum,
|
|
83325
|
-
version: kitMeta.version,
|
|
83326
|
-
sourceTimestamp: file.sourceTimestamp ?? null,
|
|
83327
|
-
installedAt: file.installedAt ?? null
|
|
83328
|
-
};
|
|
83329
|
-
}
|
|
83330
|
-
}
|
|
83331
|
-
return {
|
|
83332
|
-
exists: false,
|
|
83333
|
-
ownerKit: null,
|
|
83334
|
-
checksum: null,
|
|
83335
|
-
version: null,
|
|
83336
|
-
sourceTimestamp: null,
|
|
83337
|
-
installedAt: null
|
|
83338
|
-
};
|
|
83339
|
-
}
|
|
83340
|
-
async function getUninstallManifest(claudeDir2, kit) {
|
|
83341
|
-
const detection = await detectMetadataFormat(claudeDir2);
|
|
83342
|
-
if (detection.format === "multi-kit" && detection.metadata?.kits) {
|
|
83343
|
-
const installedKits = Object.keys(detection.metadata.kits);
|
|
83344
|
-
if (kit) {
|
|
83345
|
-
const kitMeta = detection.metadata.kits[kit];
|
|
83346
|
-
if (!kitMeta?.files) {
|
|
83347
|
-
return {
|
|
83348
|
-
filesToRemove: [],
|
|
83349
|
-
filesToPreserve: USER_CONFIG_PATTERNS,
|
|
83350
|
-
hasManifest: true,
|
|
83351
|
-
isMultiKit: true,
|
|
83352
|
-
remainingKits: installedKits.filter((k2) => k2 !== kit)
|
|
83353
|
-
};
|
|
83354
|
-
}
|
|
83355
|
-
const kitFiles = kitMeta.files.map((f3) => f3.path);
|
|
83356
|
-
const sharedFiles = new Set;
|
|
83357
|
-
for (const otherKit of installedKits) {
|
|
83358
|
-
if (otherKit !== kit) {
|
|
83359
|
-
const otherMeta = detection.metadata.kits[otherKit];
|
|
83360
|
-
if (otherMeta?.files) {
|
|
83361
|
-
for (const f3 of otherMeta.files) {
|
|
83362
|
-
sharedFiles.add(f3.path);
|
|
83363
|
-
}
|
|
83364
|
-
}
|
|
83365
|
-
}
|
|
83366
|
-
}
|
|
83367
|
-
const filesToRemove = kitFiles.filter((f3) => !sharedFiles.has(f3));
|
|
83368
|
-
const filesToPreserve = [
|
|
83369
|
-
...USER_CONFIG_PATTERNS,
|
|
83370
|
-
...kitFiles.filter((f3) => sharedFiles.has(f3))
|
|
83371
|
-
];
|
|
83372
|
-
return {
|
|
83373
|
-
filesToRemove,
|
|
83374
|
-
filesToPreserve,
|
|
83375
|
-
hasManifest: true,
|
|
83376
|
-
isMultiKit: true,
|
|
83377
|
-
remainingKits: installedKits.filter((k2) => k2 !== kit)
|
|
83378
|
-
};
|
|
83379
|
-
}
|
|
83380
|
-
const allFiles = getAllTrackedFiles(detection.metadata);
|
|
83381
|
-
return {
|
|
83382
|
-
filesToRemove: allFiles.map((f3) => f3.path),
|
|
83383
|
-
filesToPreserve: USER_CONFIG_PATTERNS,
|
|
83384
|
-
hasManifest: true,
|
|
83385
|
-
isMultiKit: true,
|
|
83386
|
-
remainingKits: []
|
|
83387
|
-
};
|
|
83388
|
-
}
|
|
83389
|
-
if (detection.format === "legacy" && detection.metadata) {
|
|
83390
|
-
const legacyFiles2 = detection.metadata.files?.map((f3) => f3.path) || [];
|
|
83391
|
-
const installedFiles = detection.metadata.installedFiles || [];
|
|
83392
|
-
const hasFiles = legacyFiles2.length > 0 || installedFiles.length > 0;
|
|
83393
|
-
if (!hasFiles) {
|
|
83394
|
-
const legacyDirs2 = ["commands", "agents", "skills", "rules", "workflows", "hooks", "scripts"];
|
|
83395
|
-
const legacyFileList = ["metadata.json"];
|
|
83396
|
-
return {
|
|
83397
|
-
filesToRemove: [...legacyDirs2, ...legacyFileList],
|
|
83398
|
-
filesToPreserve: USER_CONFIG_PATTERNS,
|
|
83399
|
-
hasManifest: false,
|
|
83400
|
-
isMultiKit: false,
|
|
83401
|
-
remainingKits: []
|
|
83402
|
-
};
|
|
83403
|
-
}
|
|
83404
|
-
return {
|
|
83405
|
-
filesToRemove: legacyFiles2.length > 0 ? legacyFiles2 : installedFiles,
|
|
83406
|
-
filesToPreserve: detection.metadata.userConfigFiles || USER_CONFIG_PATTERNS,
|
|
83407
|
-
hasManifest: true,
|
|
83408
|
-
isMultiKit: false,
|
|
83409
|
-
remainingKits: []
|
|
83410
|
-
};
|
|
83411
|
-
}
|
|
83412
|
-
const legacyDirs = ["commands", "agents", "skills", "rules", "workflows", "hooks", "scripts"];
|
|
83413
|
-
const legacyFiles = ["metadata.json"];
|
|
83414
|
-
return {
|
|
83415
|
-
filesToRemove: [...legacyDirs, ...legacyFiles],
|
|
83416
|
-
filesToPreserve: USER_CONFIG_PATTERNS,
|
|
83417
|
-
hasManifest: false,
|
|
83418
|
-
isMultiKit: false,
|
|
83419
|
-
remainingKits: []
|
|
83420
|
-
};
|
|
83421
|
-
}
|
|
83422
|
-
|
|
83423
|
-
// src/domains/installation/deletion-handler.ts
|
|
83424
|
-
init_logger();
|
|
83425
|
-
init_path_resolver();
|
|
83426
|
-
var import_fs_extra9 = __toESM(require_lib3(), 1);
|
|
83427
|
-
var import_picomatch2 = __toESM(require_picomatch2(), 1);
|
|
83428
|
-
function findFileInMetadata(metadata, path13) {
|
|
83429
|
-
if (!metadata)
|
|
83430
|
-
return null;
|
|
83431
|
-
if (metadata.kits) {
|
|
83432
|
-
for (const kitMeta of Object.values(metadata.kits)) {
|
|
83433
|
-
if (kitMeta?.files) {
|
|
83434
|
-
const found = kitMeta.files.find((f3) => f3.path === path13);
|
|
83435
|
-
if (found)
|
|
83436
|
-
return found;
|
|
83437
|
-
}
|
|
83438
|
-
}
|
|
83439
|
-
}
|
|
83440
|
-
if (metadata.files) {
|
|
83441
|
-
const found = metadata.files.find((f3) => f3.path === path13);
|
|
83442
|
-
if (found)
|
|
83443
|
-
return found;
|
|
83444
|
-
}
|
|
83445
|
-
return null;
|
|
83446
|
-
}
|
|
83447
|
-
function shouldDeletePath(path13, metadata) {
|
|
83448
|
-
const tracked = findFileInMetadata(metadata, path13);
|
|
83449
|
-
if (!tracked)
|
|
83450
|
-
return true;
|
|
83451
|
-
return tracked.ownership !== "user";
|
|
83452
|
-
}
|
|
83453
|
-
function collectFilesRecursively(dir, baseDir) {
|
|
83454
|
-
const results = [];
|
|
83455
|
-
if (!existsSync48(dir))
|
|
83456
|
-
return results;
|
|
83457
|
-
try {
|
|
83458
|
-
const entries = readdirSync3(dir, { withFileTypes: true });
|
|
83459
|
-
for (const entry of entries) {
|
|
83460
|
-
const fullPath = join76(dir, entry.name);
|
|
83461
|
-
const relativePath = relative10(baseDir, fullPath);
|
|
83462
|
-
if (entry.isDirectory()) {
|
|
83463
|
-
results.push(...collectFilesRecursively(fullPath, baseDir));
|
|
83464
|
-
} else {
|
|
83465
|
-
results.push(relativePath);
|
|
83466
|
-
}
|
|
83467
|
-
}
|
|
83468
|
-
} catch {}
|
|
83469
|
-
return results;
|
|
83470
|
-
}
|
|
83471
|
-
function expandGlobPatterns(patterns, claudeDir2) {
|
|
83472
|
-
const expanded = [];
|
|
83473
|
-
const allFiles = collectFilesRecursively(claudeDir2, claudeDir2);
|
|
83474
|
-
for (const pattern of patterns) {
|
|
83475
|
-
if (PathResolver.isGlobPattern(pattern)) {
|
|
83476
|
-
const matcher = import_picomatch2.default(pattern);
|
|
83477
|
-
const matches = allFiles.filter((file) => matcher(file));
|
|
83478
|
-
expanded.push(...matches);
|
|
83479
|
-
if (matches.length > 0) {
|
|
83480
|
-
logger.debug(`Pattern "${pattern}" matched ${matches.length} files`);
|
|
83481
|
-
}
|
|
83482
|
-
} else {
|
|
83483
|
-
expanded.push(pattern);
|
|
83484
|
-
}
|
|
83485
|
-
}
|
|
83486
|
-
return [...new Set(expanded)];
|
|
83487
|
-
}
|
|
83488
|
-
var MAX_CLEANUP_ITERATIONS = 50;
|
|
83489
|
-
function cleanupEmptyDirectories(filePath, claudeDir2) {
|
|
83490
|
-
const normalizedClaudeDir = resolve17(claudeDir2);
|
|
83491
|
-
let currentDir = resolve17(dirname18(filePath));
|
|
83492
|
-
let iterations = 0;
|
|
83493
|
-
while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir) && iterations < MAX_CLEANUP_ITERATIONS) {
|
|
83494
|
-
iterations++;
|
|
83495
|
-
try {
|
|
83496
|
-
const entries = readdirSync3(currentDir);
|
|
83497
|
-
if (entries.length === 0) {
|
|
83498
|
-
rmdirSync(currentDir);
|
|
83499
|
-
logger.debug(`Removed empty directory: ${currentDir}`);
|
|
83500
|
-
currentDir = resolve17(dirname18(currentDir));
|
|
83501
|
-
} else {
|
|
83502
|
-
break;
|
|
83503
|
-
}
|
|
83504
|
-
} catch {
|
|
83505
|
-
break;
|
|
83506
|
-
}
|
|
83507
|
-
}
|
|
83508
|
-
}
|
|
83509
|
-
function deletePath(fullPath, claudeDir2) {
|
|
83510
|
-
const normalizedPath = resolve17(fullPath);
|
|
83511
|
-
const normalizedClaudeDir = resolve17(claudeDir2);
|
|
83512
|
-
if (!normalizedPath.startsWith(`${normalizedClaudeDir}${sep3}`) && normalizedPath !== normalizedClaudeDir) {
|
|
83513
|
-
throw new Error(`Path traversal detected: ${fullPath}`);
|
|
83514
|
-
}
|
|
83515
|
-
try {
|
|
83516
|
-
const stat13 = lstatSync3(fullPath);
|
|
83517
|
-
if (stat13.isDirectory()) {
|
|
83518
|
-
rmSync2(fullPath, { recursive: true, force: true });
|
|
83519
|
-
} else {
|
|
83520
|
-
unlinkSync3(fullPath);
|
|
83521
|
-
cleanupEmptyDirectories(fullPath, claudeDir2);
|
|
83522
|
-
}
|
|
83523
|
-
} catch (error) {
|
|
83524
|
-
throw new Error(`Failed to delete ${fullPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
83525
|
-
}
|
|
83526
|
-
}
|
|
83527
|
-
async function updateMetadataAfterDeletion(claudeDir2, deletedPaths) {
|
|
83528
|
-
const metadataPath = join76(claudeDir2, "metadata.json");
|
|
83529
|
-
if (!await import_fs_extra9.pathExists(metadataPath)) {
|
|
83530
|
-
return;
|
|
83531
|
-
}
|
|
83532
|
-
let content;
|
|
83533
|
-
try {
|
|
83534
|
-
content = await import_fs_extra9.readFile(metadataPath, "utf-8");
|
|
83535
|
-
} catch {
|
|
83536
|
-
logger.debug("Failed to read metadata.json for cleanup");
|
|
83537
|
-
return;
|
|
83538
|
-
}
|
|
83539
|
-
let metadata;
|
|
83540
|
-
try {
|
|
83541
|
-
metadata = JSON.parse(content);
|
|
83542
|
-
} catch {
|
|
83543
|
-
logger.debug("Failed to parse metadata.json for cleanup");
|
|
83544
|
-
return;
|
|
83545
|
-
}
|
|
83546
|
-
const deletedSet = new Set(deletedPaths);
|
|
83547
|
-
const isDeletedOrInDeletedDir = (path13) => {
|
|
83548
|
-
if (deletedSet.has(path13))
|
|
83549
|
-
return true;
|
|
83550
|
-
for (const deleted of deletedPaths) {
|
|
83551
|
-
if (path13.startsWith(`${deleted}/`))
|
|
83552
|
-
return true;
|
|
83553
|
-
}
|
|
83554
|
-
return false;
|
|
83555
|
-
};
|
|
83556
|
-
if (metadata.kits) {
|
|
83557
|
-
for (const kitName of Object.keys(metadata.kits)) {
|
|
83558
|
-
const kit = metadata.kits[kitName];
|
|
83559
|
-
if (kit?.files) {
|
|
83560
|
-
kit.files = kit.files.filter((f3) => !isDeletedOrInDeletedDir(f3.path));
|
|
83561
|
-
}
|
|
83562
|
-
}
|
|
83563
|
-
}
|
|
83564
|
-
if (metadata.files) {
|
|
83565
|
-
metadata.files = metadata.files.filter((f3) => !isDeletedOrInDeletedDir(f3.path));
|
|
83566
|
-
}
|
|
83567
|
-
try {
|
|
83568
|
-
await import_fs_extra9.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
83569
|
-
logger.debug(`Updated metadata.json, removed ${deletedPaths.length} entries`);
|
|
83570
|
-
} catch {
|
|
83571
|
-
logger.debug("Failed to write updated metadata.json");
|
|
83572
|
-
}
|
|
83573
|
-
}
|
|
83574
|
-
async function handleDeletions(sourceMetadata, claudeDir2) {
|
|
83575
|
-
const deletionPatterns = sourceMetadata.deletions || [];
|
|
83576
|
-
if (deletionPatterns.length === 0) {
|
|
83577
|
-
return { deletedPaths: [], preservedPaths: [], errors: [] };
|
|
83578
|
-
}
|
|
83579
|
-
const deletions = expandGlobPatterns(deletionPatterns, claudeDir2);
|
|
83580
|
-
const userMetadata = await readManifest(claudeDir2);
|
|
83581
|
-
const result = { deletedPaths: [], preservedPaths: [], errors: [] };
|
|
83582
|
-
for (const path13 of deletions) {
|
|
83583
|
-
const fullPath = join76(claudeDir2, path13);
|
|
83584
|
-
const normalizedPath = resolve17(fullPath);
|
|
83585
|
-
const normalizedClaudeDir = resolve17(claudeDir2);
|
|
83586
|
-
if (!normalizedPath.startsWith(`${normalizedClaudeDir}${sep3}`)) {
|
|
83587
|
-
logger.warning(`Skipping invalid path: ${path13}`);
|
|
83588
|
-
result.errors.push(path13);
|
|
83589
|
-
continue;
|
|
83590
|
-
}
|
|
83591
|
-
if (!shouldDeletePath(path13, userMetadata)) {
|
|
83592
|
-
result.preservedPaths.push(path13);
|
|
83593
|
-
logger.verbose(`Preserved user file: ${path13}`);
|
|
83594
|
-
continue;
|
|
83595
|
-
}
|
|
83596
|
-
if (existsSync48(fullPath)) {
|
|
83597
|
-
try {
|
|
83598
|
-
deletePath(fullPath, claudeDir2);
|
|
83599
|
-
result.deletedPaths.push(path13);
|
|
83600
|
-
logger.verbose(`Deleted: ${path13}`);
|
|
83601
|
-
} catch (error) {
|
|
83602
|
-
result.errors.push(path13);
|
|
83603
|
-
logger.debug(`Failed to delete ${path13}: ${error}`);
|
|
83604
|
-
}
|
|
83605
|
-
}
|
|
83606
|
-
}
|
|
83607
|
-
if (result.deletedPaths.length > 0) {
|
|
83608
|
-
await updateMetadataAfterDeletion(claudeDir2, result.deletedPaths);
|
|
83609
|
-
}
|
|
83610
|
-
return result;
|
|
83611
|
-
}
|
|
84223
|
+
init_deletion_handler();
|
|
84224
|
+
import { join as join88 } from "node:path";
|
|
83612
84225
|
|
|
83613
84226
|
// src/domains/installation/file-merger.ts
|
|
83614
84227
|
init_logger();
|
|
@@ -83620,9 +84233,10 @@ init_logger();
|
|
|
83620
84233
|
init_types3();
|
|
83621
84234
|
var import_fs_extra12 = __toESM(require_lib3(), 1);
|
|
83622
84235
|
var import_ignore3 = __toESM(require_ignore(), 1);
|
|
83623
|
-
import { dirname as
|
|
84236
|
+
import { dirname as dirname21, join as join78, relative as relative12 } from "node:path";
|
|
83624
84237
|
|
|
83625
84238
|
// src/domains/installation/selective-merger.ts
|
|
84239
|
+
init_manifest_reader();
|
|
83626
84240
|
import { stat as stat13 } from "node:fs/promises";
|
|
83627
84241
|
init_logger();
|
|
83628
84242
|
var import_semver2 = __toESM(require_semver2(), 1);
|
|
@@ -83799,7 +84413,7 @@ init_logger();
|
|
|
83799
84413
|
var import_fs_extra10 = __toESM(require_lib3(), 1);
|
|
83800
84414
|
var import_ignore2 = __toESM(require_ignore(), 1);
|
|
83801
84415
|
import { relative as relative11 } from "node:path";
|
|
83802
|
-
import { join as
|
|
84416
|
+
import { join as join76 } from "node:path";
|
|
83803
84417
|
|
|
83804
84418
|
// node_modules/@isaacs/balanced-match/dist/esm/index.js
|
|
83805
84419
|
var balanced = (a3, b3, str2) => {
|
|
@@ -85255,7 +85869,7 @@ class FileScanner {
|
|
|
85255
85869
|
const files = [];
|
|
85256
85870
|
const entries = await import_fs_extra10.readdir(dir, { encoding: "utf8" });
|
|
85257
85871
|
for (const entry of entries) {
|
|
85258
|
-
const fullPath =
|
|
85872
|
+
const fullPath = join76(dir, entry);
|
|
85259
85873
|
const relativePath = relative11(baseDir, fullPath);
|
|
85260
85874
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
85261
85875
|
const stats = await import_fs_extra10.lstat(fullPath);
|
|
@@ -85295,7 +85909,7 @@ import { execSync as execSync4 } from "node:child_process";
|
|
|
85295
85909
|
init_shared();
|
|
85296
85910
|
import { existsSync as existsSync49 } from "node:fs";
|
|
85297
85911
|
import { mkdir as mkdir26, readFile as readFile38, writeFile as writeFile22 } from "node:fs/promises";
|
|
85298
|
-
import { dirname as
|
|
85912
|
+
import { dirname as dirname20, join as join77 } from "node:path";
|
|
85299
85913
|
var CK_JSON_FILE = ".ck.json";
|
|
85300
85914
|
|
|
85301
85915
|
class InstalledSettingsTracker {
|
|
@@ -85309,9 +85923,9 @@ class InstalledSettingsTracker {
|
|
|
85309
85923
|
}
|
|
85310
85924
|
getCkJsonPath() {
|
|
85311
85925
|
if (this.isGlobal) {
|
|
85312
|
-
return
|
|
85926
|
+
return join77(this.projectDir, CK_JSON_FILE);
|
|
85313
85927
|
}
|
|
85314
|
-
return
|
|
85928
|
+
return join77(this.projectDir, ".claude", CK_JSON_FILE);
|
|
85315
85929
|
}
|
|
85316
85930
|
async loadInstalledSettings() {
|
|
85317
85931
|
const ckJsonPath = this.getCkJsonPath();
|
|
@@ -85346,7 +85960,7 @@ class InstalledSettingsTracker {
|
|
|
85346
85960
|
data.kits[this.kitName] = {};
|
|
85347
85961
|
}
|
|
85348
85962
|
data.kits[this.kitName].installedSettings = settings;
|
|
85349
|
-
await mkdir26(
|
|
85963
|
+
await mkdir26(dirname20(ckJsonPath), { recursive: true });
|
|
85350
85964
|
await writeFile22(ckJsonPath, JSON.stringify(data, null, 2), "utf-8");
|
|
85351
85965
|
logger.debug(`Saved installed settings to ${ckJsonPath}`);
|
|
85352
85966
|
} catch (error) {
|
|
@@ -85864,7 +86478,7 @@ class CopyExecutor {
|
|
|
85864
86478
|
for (const file of files) {
|
|
85865
86479
|
const relativePath = relative12(sourceDir, file);
|
|
85866
86480
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
85867
|
-
const destPath =
|
|
86481
|
+
const destPath = join78(destDir, relativePath);
|
|
85868
86482
|
if (await import_fs_extra12.pathExists(destPath)) {
|
|
85869
86483
|
if (this.fileScanner.shouldNeverCopy(normalizedRelativePath)) {
|
|
85870
86484
|
logger.debug(`Security-sensitive file exists but won't be overwritten: ${normalizedRelativePath}`);
|
|
@@ -85886,7 +86500,7 @@ class CopyExecutor {
|
|
|
85886
86500
|
for (const file of files) {
|
|
85887
86501
|
const relativePath = relative12(sourceDir, file);
|
|
85888
86502
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
85889
|
-
const destPath =
|
|
86503
|
+
const destPath = join78(destDir, relativePath);
|
|
85890
86504
|
if (this.fileScanner.shouldNeverCopy(normalizedRelativePath)) {
|
|
85891
86505
|
logger.debug(`Skipping security-sensitive file: ${normalizedRelativePath}`);
|
|
85892
86506
|
skippedCount++;
|
|
@@ -85963,10 +86577,10 @@ class CopyExecutor {
|
|
|
85963
86577
|
}
|
|
85964
86578
|
trackInstalledFile(relativePath) {
|
|
85965
86579
|
this.installedFiles.add(relativePath);
|
|
85966
|
-
let dir =
|
|
86580
|
+
let dir = dirname21(relativePath);
|
|
85967
86581
|
while (dir && dir !== "." && dir !== "/") {
|
|
85968
86582
|
this.installedDirectories.add(`${dir}/`);
|
|
85969
|
-
dir =
|
|
86583
|
+
dir = dirname21(dir);
|
|
85970
86584
|
}
|
|
85971
86585
|
}
|
|
85972
86586
|
}
|
|
@@ -86056,15 +86670,19 @@ class FileMerger {
|
|
|
86056
86670
|
|
|
86057
86671
|
// src/domains/migration/legacy-migration.ts
|
|
86058
86672
|
import { readdir as readdir18, stat as stat14 } from "node:fs/promises";
|
|
86059
|
-
import { join as
|
|
86673
|
+
import { join as join82, relative as relative13 } from "node:path";
|
|
86674
|
+
|
|
86675
|
+
// src/services/file-operations/manifest/index.ts
|
|
86676
|
+
init_manifest_reader();
|
|
86677
|
+
|
|
86060
86678
|
// src/services/file-operations/manifest/manifest-tracker.ts
|
|
86061
|
-
import { join as
|
|
86679
|
+
import { join as join81 } from "node:path";
|
|
86062
86680
|
|
|
86063
86681
|
// src/domains/migration/release-manifest.ts
|
|
86064
86682
|
init_logger();
|
|
86065
86683
|
init_zod();
|
|
86066
86684
|
var import_fs_extra13 = __toESM(require_lib3(), 1);
|
|
86067
|
-
import { join as
|
|
86685
|
+
import { join as join79 } from "node:path";
|
|
86068
86686
|
var ReleaseManifestFileSchema = exports_external.object({
|
|
86069
86687
|
path: exports_external.string(),
|
|
86070
86688
|
checksum: exports_external.string().regex(/^[a-f0-9]{64}$/),
|
|
@@ -86079,7 +86697,7 @@ var ReleaseManifestSchema = exports_external.object({
|
|
|
86079
86697
|
|
|
86080
86698
|
class ReleaseManifestLoader {
|
|
86081
86699
|
static async load(extractDir) {
|
|
86082
|
-
const manifestPath =
|
|
86700
|
+
const manifestPath = join79(extractDir, "release-manifest.json");
|
|
86083
86701
|
try {
|
|
86084
86702
|
const content = await import_fs_extra13.readFile(manifestPath, "utf-8");
|
|
86085
86703
|
const parsed = JSON.parse(content);
|
|
@@ -86103,11 +86721,12 @@ init_safe_spinner();
|
|
|
86103
86721
|
init_metadata_migration();
|
|
86104
86722
|
init_logger();
|
|
86105
86723
|
init_types3();
|
|
86724
|
+
init_manifest_reader();
|
|
86106
86725
|
var import_fs_extra14 = __toESM(require_lib3(), 1);
|
|
86107
86726
|
var import_proper_lockfile5 = __toESM(require_proper_lockfile(), 1);
|
|
86108
|
-
import { join as
|
|
86727
|
+
import { join as join80 } from "node:path";
|
|
86109
86728
|
async function writeManifest(claudeDir2, kitName, version, scope, kitType, trackedFiles, userConfigFiles) {
|
|
86110
|
-
const metadataPath =
|
|
86729
|
+
const metadataPath = join80(claudeDir2, "metadata.json");
|
|
86111
86730
|
const kit = kitType || (/\bmarketing\b/i.test(kitName) ? "marketing" : "engineer");
|
|
86112
86731
|
await import_fs_extra14.ensureFile(metadataPath);
|
|
86113
86732
|
let release = null;
|
|
@@ -86127,20 +86746,54 @@ async function writeManifest(claudeDir2, kitName, version, scope, kitType, track
|
|
|
86127
86746
|
const content = await import_fs_extra14.readFile(metadataPath, "utf-8");
|
|
86128
86747
|
const parsed = JSON.parse(content);
|
|
86129
86748
|
if (parsed && typeof parsed === "object" && Object.keys(parsed).length > 0) {
|
|
86130
|
-
|
|
86749
|
+
const validatedExisting = MetadataSchema.safeParse(parsed);
|
|
86750
|
+
if (validatedExisting.success) {
|
|
86751
|
+
existingMetadata = validatedExisting.data;
|
|
86752
|
+
} else {
|
|
86753
|
+
logger.warning("Existing metadata.json is invalid; preserving recoverable fields and rebuilding the rest");
|
|
86754
|
+
const raw2 = parsed;
|
|
86755
|
+
const recoveredKits = {};
|
|
86756
|
+
if (raw2.kits && typeof raw2.kits === "object") {
|
|
86757
|
+
for (const [rawKitName, rawKitValue] of Object.entries(raw2.kits)) {
|
|
86758
|
+
if ((rawKitName === "engineer" || rawKitName === "marketing") && rawKitValue && typeof rawKitValue === "object") {
|
|
86759
|
+
const recoveredKit = rawKitValue;
|
|
86760
|
+
if (typeof recoveredKit.version === "string" && typeof recoveredKit.installedAt === "string") {
|
|
86761
|
+
recoveredKits[rawKitName] = recoveredKit;
|
|
86762
|
+
}
|
|
86763
|
+
}
|
|
86764
|
+
}
|
|
86765
|
+
}
|
|
86766
|
+
existingMetadata = {
|
|
86767
|
+
kits: recoveredKits,
|
|
86768
|
+
scope: raw2.scope === "local" || raw2.scope === "global" ? raw2.scope : undefined,
|
|
86769
|
+
name: typeof raw2.name === "string" ? raw2.name : undefined,
|
|
86770
|
+
version: typeof raw2.version === "string" ? raw2.version : undefined,
|
|
86771
|
+
installedAt: typeof raw2.installedAt === "string" ? raw2.installedAt : undefined,
|
|
86772
|
+
userConfigFiles: Array.isArray(raw2.userConfigFiles) ? raw2.userConfigFiles.filter((entry) => typeof entry === "string") : undefined
|
|
86773
|
+
};
|
|
86774
|
+
}
|
|
86131
86775
|
}
|
|
86132
86776
|
} catch (error) {
|
|
86133
86777
|
logger.debug(`Could not read existing metadata: ${error}`);
|
|
86134
86778
|
}
|
|
86135
86779
|
}
|
|
86136
86780
|
const installedAt = new Date().toISOString();
|
|
86781
|
+
const existingKits = existingMetadata.kits || {};
|
|
86782
|
+
const existingKitMetadata = existingKits[kit];
|
|
86137
86783
|
const kitMetadata = {
|
|
86784
|
+
...existingKitMetadata,
|
|
86138
86785
|
version,
|
|
86139
86786
|
installedAt,
|
|
86140
86787
|
files: trackedFiles.length > 0 ? trackedFiles : undefined
|
|
86141
86788
|
};
|
|
86142
|
-
const existingKits = existingMetadata.kits || {};
|
|
86143
86789
|
const otherKitsExist = Object.keys(existingKits).some((k2) => k2 !== kit);
|
|
86790
|
+
const mergedUserConfigFiles = [
|
|
86791
|
+
...new Set([
|
|
86792
|
+
...existingMetadata.userConfigFiles || [],
|
|
86793
|
+
...USER_CONFIG_PATTERNS,
|
|
86794
|
+
...userConfigFiles
|
|
86795
|
+
])
|
|
86796
|
+
];
|
|
86144
86797
|
const metadata = {
|
|
86145
86798
|
kits: {
|
|
86146
86799
|
...existingKits,
|
|
@@ -86150,7 +86803,7 @@ async function writeManifest(claudeDir2, kitName, version, scope, kitType, track
|
|
|
86150
86803
|
name: otherKitsExist ? existingMetadata.name ?? kitName : kitName,
|
|
86151
86804
|
version: otherKitsExist ? existingMetadata.version ?? version : version,
|
|
86152
86805
|
installedAt: otherKitsExist ? existingMetadata.installedAt ?? installedAt : installedAt,
|
|
86153
|
-
userConfigFiles:
|
|
86806
|
+
userConfigFiles: mergedUserConfigFiles
|
|
86154
86807
|
};
|
|
86155
86808
|
const validated = MetadataSchema.parse(metadata);
|
|
86156
86809
|
await import_fs_extra14.writeFile(metadataPath, JSON.stringify(validated, null, 2), "utf-8");
|
|
@@ -86163,7 +86816,7 @@ async function writeManifest(claudeDir2, kitName, version, scope, kitType, track
|
|
|
86163
86816
|
}
|
|
86164
86817
|
}
|
|
86165
86818
|
async function removeKitFromManifest(claudeDir2, kit) {
|
|
86166
|
-
const metadataPath =
|
|
86819
|
+
const metadataPath = join80(claudeDir2, "metadata.json");
|
|
86167
86820
|
if (!await import_fs_extra14.pathExists(metadataPath))
|
|
86168
86821
|
return false;
|
|
86169
86822
|
let release = null;
|
|
@@ -86293,7 +86946,7 @@ function buildFileTrackingList(options2) {
|
|
|
86293
86946
|
if (!isGlobal && !installedPath.startsWith(".claude/"))
|
|
86294
86947
|
continue;
|
|
86295
86948
|
const relativePath = isGlobal ? installedPath : installedPath.replace(/^\.claude\//, "");
|
|
86296
|
-
const filePath =
|
|
86949
|
+
const filePath = join81(claudeDir2, relativePath);
|
|
86297
86950
|
const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
|
|
86298
86951
|
const ownership = manifestEntry ? "ck" : "user";
|
|
86299
86952
|
filesToTrack.push({
|
|
@@ -86400,7 +87053,7 @@ class LegacyMigration {
|
|
|
86400
87053
|
continue;
|
|
86401
87054
|
if (SKIP_DIRS_ALL.includes(entry))
|
|
86402
87055
|
continue;
|
|
86403
|
-
const fullPath =
|
|
87056
|
+
const fullPath = join82(dir, entry);
|
|
86404
87057
|
let stats;
|
|
86405
87058
|
try {
|
|
86406
87059
|
stats = await stat14(fullPath);
|
|
@@ -86502,7 +87155,7 @@ User-created files (sample):`);
|
|
|
86502
87155
|
];
|
|
86503
87156
|
if (filesToChecksum.length > 0) {
|
|
86504
87157
|
const checksumResults = await mapWithLimit(filesToChecksum, async ({ relativePath, ownership }) => {
|
|
86505
|
-
const fullPath =
|
|
87158
|
+
const fullPath = join82(claudeDir2, relativePath);
|
|
86506
87159
|
const checksum = await OwnershipChecker.calculateChecksum(fullPath);
|
|
86507
87160
|
return { relativePath, checksum, ownership };
|
|
86508
87161
|
});
|
|
@@ -86523,7 +87176,7 @@ User-created files (sample):`);
|
|
|
86523
87176
|
installedAt: new Date().toISOString(),
|
|
86524
87177
|
files: trackedFiles
|
|
86525
87178
|
};
|
|
86526
|
-
const metadataPath =
|
|
87179
|
+
const metadataPath = join82(claudeDir2, "metadata.json");
|
|
86527
87180
|
await import_fs_extra15.writeFile(metadataPath, JSON.stringify(updatedMetadata, null, 2));
|
|
86528
87181
|
logger.success(`Migration complete: tracked ${trackedFiles.length} files`);
|
|
86529
87182
|
return true;
|
|
@@ -86629,7 +87282,7 @@ function buildConflictSummary(fileConflicts, hookConflicts, mcpConflicts) {
|
|
|
86629
87282
|
init_logger();
|
|
86630
87283
|
init_skip_directories();
|
|
86631
87284
|
var import_fs_extra16 = __toESM(require_lib3(), 1);
|
|
86632
|
-
import { join as
|
|
87285
|
+
import { join as join83, relative as relative14, resolve as resolve19 } from "node:path";
|
|
86633
87286
|
|
|
86634
87287
|
class FileScanner2 {
|
|
86635
87288
|
static async getFiles(dirPath, relativeTo) {
|
|
@@ -86645,7 +87298,7 @@ class FileScanner2 {
|
|
|
86645
87298
|
logger.debug(`Skipping directory: ${entry}`);
|
|
86646
87299
|
continue;
|
|
86647
87300
|
}
|
|
86648
|
-
const fullPath =
|
|
87301
|
+
const fullPath = join83(dirPath, entry);
|
|
86649
87302
|
if (!FileScanner2.isSafePath(basePath, fullPath)) {
|
|
86650
87303
|
logger.warning(`Skipping potentially unsafe path: ${entry}`);
|
|
86651
87304
|
continue;
|
|
@@ -86680,8 +87333,8 @@ class FileScanner2 {
|
|
|
86680
87333
|
return files;
|
|
86681
87334
|
}
|
|
86682
87335
|
static async findCustomFiles(destDir, sourceDir, subPath) {
|
|
86683
|
-
const destSubDir =
|
|
86684
|
-
const sourceSubDir =
|
|
87336
|
+
const destSubDir = join83(destDir, subPath);
|
|
87337
|
+
const sourceSubDir = join83(sourceDir, subPath);
|
|
86685
87338
|
logger.debug(`findCustomFiles - destDir: ${destDir}`);
|
|
86686
87339
|
logger.debug(`findCustomFiles - sourceDir: ${sourceDir}`);
|
|
86687
87340
|
logger.debug(`findCustomFiles - subPath: "${subPath}"`);
|
|
@@ -86709,8 +87362,8 @@ class FileScanner2 {
|
|
|
86709
87362
|
return customFiles;
|
|
86710
87363
|
}
|
|
86711
87364
|
static isSafePath(basePath, targetPath) {
|
|
86712
|
-
const resolvedBase =
|
|
86713
|
-
const resolvedTarget =
|
|
87365
|
+
const resolvedBase = resolve19(basePath);
|
|
87366
|
+
const resolvedTarget = resolve19(targetPath);
|
|
86714
87367
|
return resolvedTarget.startsWith(resolvedBase);
|
|
86715
87368
|
}
|
|
86716
87369
|
static toPosixPath(path14) {
|
|
@@ -86722,12 +87375,12 @@ class FileScanner2 {
|
|
|
86722
87375
|
init_logger();
|
|
86723
87376
|
var import_fs_extra17 = __toESM(require_lib3(), 1);
|
|
86724
87377
|
import { lstat as lstat7, mkdir as mkdir27, readdir as readdir21, stat as stat15 } from "node:fs/promises";
|
|
86725
|
-
import { join as
|
|
87378
|
+
import { join as join85 } from "node:path";
|
|
86726
87379
|
|
|
86727
87380
|
// src/services/transformers/commands-prefix/content-transformer.ts
|
|
86728
87381
|
init_logger();
|
|
86729
87382
|
import { readFile as readFile42, readdir as readdir20, writeFile as writeFile26 } from "node:fs/promises";
|
|
86730
|
-
import { join as
|
|
87383
|
+
import { join as join84 } from "node:path";
|
|
86731
87384
|
var TRANSFORMABLE_EXTENSIONS = new Set([
|
|
86732
87385
|
".md",
|
|
86733
87386
|
".txt",
|
|
@@ -86788,7 +87441,7 @@ async function transformCommandReferences(directory, options2 = {}) {
|
|
|
86788
87441
|
async function processDirectory(dir) {
|
|
86789
87442
|
const entries = await readdir20(dir, { withFileTypes: true });
|
|
86790
87443
|
for (const entry of entries) {
|
|
86791
|
-
const fullPath =
|
|
87444
|
+
const fullPath = join84(dir, entry.name);
|
|
86792
87445
|
if (entry.isDirectory()) {
|
|
86793
87446
|
if (entry.name === "node_modules" || entry.name.startsWith(".") && entry.name !== ".claude") {
|
|
86794
87447
|
continue;
|
|
@@ -86863,14 +87516,14 @@ function shouldApplyPrefix(options2) {
|
|
|
86863
87516
|
// src/services/transformers/commands-prefix/prefix-applier.ts
|
|
86864
87517
|
async function applyPrefix(extractDir) {
|
|
86865
87518
|
validatePath(extractDir, "extractDir");
|
|
86866
|
-
const commandsDir =
|
|
87519
|
+
const commandsDir = join85(extractDir, ".claude", "commands");
|
|
86867
87520
|
if (!await import_fs_extra17.pathExists(commandsDir)) {
|
|
86868
87521
|
logger.verbose("No commands directory found, skipping prefix application");
|
|
86869
87522
|
return;
|
|
86870
87523
|
}
|
|
86871
87524
|
logger.info("Applying /ck: prefix to slash commands...");
|
|
86872
|
-
const backupDir =
|
|
86873
|
-
const tempDir =
|
|
87525
|
+
const backupDir = join85(extractDir, ".commands-backup");
|
|
87526
|
+
const tempDir = join85(extractDir, ".commands-prefix-temp");
|
|
86874
87527
|
try {
|
|
86875
87528
|
const entries = await readdir21(commandsDir);
|
|
86876
87529
|
if (entries.length === 0) {
|
|
@@ -86878,7 +87531,7 @@ async function applyPrefix(extractDir) {
|
|
|
86878
87531
|
return;
|
|
86879
87532
|
}
|
|
86880
87533
|
if (entries.length === 1 && entries[0] === "ck") {
|
|
86881
|
-
const ckDir2 =
|
|
87534
|
+
const ckDir2 = join85(commandsDir, "ck");
|
|
86882
87535
|
const ckStat = await stat15(ckDir2);
|
|
86883
87536
|
if (ckStat.isDirectory()) {
|
|
86884
87537
|
logger.verbose("Commands already have /ck: prefix, skipping");
|
|
@@ -86888,17 +87541,17 @@ async function applyPrefix(extractDir) {
|
|
|
86888
87541
|
await import_fs_extra17.copy(commandsDir, backupDir);
|
|
86889
87542
|
logger.verbose("Created backup of commands directory");
|
|
86890
87543
|
await mkdir27(tempDir, { recursive: true });
|
|
86891
|
-
const ckDir =
|
|
87544
|
+
const ckDir = join85(tempDir, "ck");
|
|
86892
87545
|
await mkdir27(ckDir, { recursive: true });
|
|
86893
87546
|
let processedCount = 0;
|
|
86894
87547
|
for (const entry of entries) {
|
|
86895
|
-
const sourcePath =
|
|
87548
|
+
const sourcePath = join85(commandsDir, entry);
|
|
86896
87549
|
const stats = await lstat7(sourcePath);
|
|
86897
87550
|
if (stats.isSymbolicLink()) {
|
|
86898
87551
|
logger.warning(`Skipping symlink for security: ${entry}`);
|
|
86899
87552
|
continue;
|
|
86900
87553
|
}
|
|
86901
|
-
const destPath =
|
|
87554
|
+
const destPath = join85(ckDir, entry);
|
|
86902
87555
|
await import_fs_extra17.copy(sourcePath, destPath, {
|
|
86903
87556
|
overwrite: false,
|
|
86904
87557
|
errorOnExist: true
|
|
@@ -86916,7 +87569,7 @@ async function applyPrefix(extractDir) {
|
|
|
86916
87569
|
await import_fs_extra17.move(tempDir, commandsDir);
|
|
86917
87570
|
await import_fs_extra17.remove(backupDir);
|
|
86918
87571
|
logger.success("Successfully reorganized commands to /ck: prefix");
|
|
86919
|
-
const claudeDir2 =
|
|
87572
|
+
const claudeDir2 = join85(extractDir, ".claude");
|
|
86920
87573
|
logger.info("Transforming command references in file contents...");
|
|
86921
87574
|
const transformResult = await transformCommandReferences(claudeDir2, {
|
|
86922
87575
|
verbose: logger.isVerbose()
|
|
@@ -86954,20 +87607,20 @@ async function applyPrefix(extractDir) {
|
|
|
86954
87607
|
// src/services/transformers/commands-prefix/prefix-cleaner.ts
|
|
86955
87608
|
init_metadata_migration();
|
|
86956
87609
|
import { lstat as lstat9, readdir as readdir23 } from "node:fs/promises";
|
|
86957
|
-
import { join as
|
|
87610
|
+
import { join as join87 } from "node:path";
|
|
86958
87611
|
init_logger();
|
|
86959
87612
|
var import_fs_extra19 = __toESM(require_lib3(), 1);
|
|
86960
87613
|
|
|
86961
87614
|
// src/services/transformers/commands-prefix/file-processor.ts
|
|
86962
87615
|
import { lstat as lstat8, readdir as readdir22 } from "node:fs/promises";
|
|
86963
|
-
import { join as
|
|
87616
|
+
import { join as join86 } from "node:path";
|
|
86964
87617
|
init_logger();
|
|
86965
87618
|
var import_fs_extra18 = __toESM(require_lib3(), 1);
|
|
86966
87619
|
async function scanDirectoryFiles(dir) {
|
|
86967
87620
|
const files = [];
|
|
86968
87621
|
const entries = await readdir22(dir);
|
|
86969
87622
|
for (const entry of entries) {
|
|
86970
|
-
const fullPath =
|
|
87623
|
+
const fullPath = join86(dir, entry);
|
|
86971
87624
|
const stats = await lstat8(fullPath);
|
|
86972
87625
|
if (stats.isSymbolicLink()) {
|
|
86973
87626
|
continue;
|
|
@@ -87095,8 +87748,8 @@ function isDifferentKitDirectory(dirName, currentKit) {
|
|
|
87095
87748
|
async function cleanupCommandsDirectory(targetDir, isGlobal, options2 = {}) {
|
|
87096
87749
|
const { dryRun = false } = options2;
|
|
87097
87750
|
validatePath(targetDir, "targetDir");
|
|
87098
|
-
const claudeDir2 = isGlobal ? targetDir :
|
|
87099
|
-
const commandsDir =
|
|
87751
|
+
const claudeDir2 = isGlobal ? targetDir : join87(targetDir, ".claude");
|
|
87752
|
+
const commandsDir = join87(claudeDir2, "commands");
|
|
87100
87753
|
const accumulator = {
|
|
87101
87754
|
results: [],
|
|
87102
87755
|
deletedCount: 0,
|
|
@@ -87138,7 +87791,7 @@ async function cleanupCommandsDirectory(targetDir, isGlobal, options2 = {}) {
|
|
|
87138
87791
|
}
|
|
87139
87792
|
const metadataForChecks = options2.kitType ? createKitSpecificMetadata(metadata, options2.kitType) : metadata;
|
|
87140
87793
|
for (const entry of entries) {
|
|
87141
|
-
const entryPath =
|
|
87794
|
+
const entryPath = join87(commandsDir, entry);
|
|
87142
87795
|
const stats = await lstat9(entryPath);
|
|
87143
87796
|
if (stats.isSymbolicLink()) {
|
|
87144
87797
|
addSymlinkSkip(entry, accumulator);
|
|
@@ -87195,7 +87848,7 @@ async function handleMerge(ctx) {
|
|
|
87195
87848
|
let customClaudeFiles = [];
|
|
87196
87849
|
if (!ctx.options.fresh) {
|
|
87197
87850
|
logger.info("Scanning for custom .claude files...");
|
|
87198
|
-
const scanSourceDir = ctx.options.global ?
|
|
87851
|
+
const scanSourceDir = ctx.options.global ? join88(ctx.extractDir, ".claude") : ctx.extractDir;
|
|
87199
87852
|
const scanTargetSubdir = ctx.options.global ? "" : ".claude";
|
|
87200
87853
|
customClaudeFiles = await FileScanner2.findCustomFiles(ctx.resolvedDir, scanSourceDir, scanTargetSubdir);
|
|
87201
87854
|
} else {
|
|
@@ -87260,28 +87913,38 @@ async function handleMerge(ctx) {
|
|
|
87260
87913
|
return { ...ctx, cancelled: true };
|
|
87261
87914
|
}
|
|
87262
87915
|
}
|
|
87263
|
-
const sourceDir = ctx.options.global ?
|
|
87916
|
+
const sourceDir = ctx.options.global ? join88(ctx.extractDir, ".claude") : ctx.extractDir;
|
|
87264
87917
|
await merger.merge(sourceDir, ctx.resolvedDir, ctx.isNonInteractive);
|
|
87265
87918
|
const fileConflicts = merger.getFileConflicts();
|
|
87266
87919
|
if (fileConflicts.length > 0 && !ctx.isNonInteractive) {
|
|
87267
87920
|
const summary = buildConflictSummary(fileConflicts, [], []);
|
|
87268
87921
|
displayConflictSummary(summary);
|
|
87269
87922
|
}
|
|
87923
|
+
let deferredDeletions = [];
|
|
87270
87924
|
try {
|
|
87271
|
-
const sourceMetadataPath = ctx.options.global ?
|
|
87925
|
+
const sourceMetadataPath = ctx.options.global ? join88(sourceDir, "metadata.json") : join88(sourceDir, ".claude", "metadata.json");
|
|
87272
87926
|
if (await import_fs_extra20.pathExists(sourceMetadataPath)) {
|
|
87273
87927
|
const metadataContent = await import_fs_extra20.readFile(sourceMetadataPath, "utf-8");
|
|
87274
87928
|
const sourceMetadata = JSON.parse(metadataContent);
|
|
87275
87929
|
if (sourceMetadata.deletions && sourceMetadata.deletions.length > 0) {
|
|
87276
|
-
const
|
|
87277
|
-
|
|
87278
|
-
|
|
87279
|
-
|
|
87280
|
-
|
|
87930
|
+
const { categorizeDeletions: categorizeDeletions2 } = await Promise.resolve().then(() => (init_deletion_handler(), exports_deletion_handler));
|
|
87931
|
+
const categorized = categorizeDeletions2(sourceMetadata.deletions);
|
|
87932
|
+
if (categorized.immediate.length > 0) {
|
|
87933
|
+
const immediateMetadata = { ...sourceMetadata, deletions: categorized.immediate };
|
|
87934
|
+
const deletionResult = await handleDeletions(immediateMetadata, ctx.claudeDir);
|
|
87935
|
+
if (deletionResult.deletedPaths.length > 0) {
|
|
87936
|
+
logger.info(`Removed ${deletionResult.deletedPaths.length} deprecated file(s)`);
|
|
87937
|
+
for (const path14 of deletionResult.deletedPaths) {
|
|
87938
|
+
logger.verbose(` - ${path14}`);
|
|
87939
|
+
}
|
|
87940
|
+
}
|
|
87941
|
+
if (deletionResult.preservedPaths.length > 0) {
|
|
87942
|
+
logger.verbose(`Preserved ${deletionResult.preservedPaths.length} user-owned file(s)`);
|
|
87281
87943
|
}
|
|
87282
87944
|
}
|
|
87283
|
-
if (
|
|
87284
|
-
|
|
87945
|
+
if (categorized.deferred.length > 0) {
|
|
87946
|
+
deferredDeletions = categorized.deferred;
|
|
87947
|
+
logger.debug(`Deferred ${categorized.deferred.length} skill deletion(s) to post-install phase`);
|
|
87285
87948
|
}
|
|
87286
87949
|
}
|
|
87287
87950
|
} else {
|
|
@@ -87308,11 +87971,12 @@ async function handleMerge(ctx) {
|
|
|
87308
87971
|
return {
|
|
87309
87972
|
...ctx,
|
|
87310
87973
|
customClaudeFiles,
|
|
87311
|
-
includePatterns
|
|
87974
|
+
includePatterns,
|
|
87975
|
+
deferredDeletions
|
|
87312
87976
|
};
|
|
87313
87977
|
}
|
|
87314
87978
|
// src/commands/init/phases/migration-handler.ts
|
|
87315
|
-
import { join as
|
|
87979
|
+
import { join as join96 } from "node:path";
|
|
87316
87980
|
|
|
87317
87981
|
// src/domains/skills/skills-detector.ts
|
|
87318
87982
|
init_logger();
|
|
@@ -87328,7 +87992,7 @@ init_types3();
|
|
|
87328
87992
|
var import_fs_extra21 = __toESM(require_lib3(), 1);
|
|
87329
87993
|
import { createHash as createHash4 } from "node:crypto";
|
|
87330
87994
|
import { readFile as readFile44, readdir as readdir24, writeFile as writeFile27 } from "node:fs/promises";
|
|
87331
|
-
import { join as
|
|
87995
|
+
import { join as join89, relative as relative15 } from "node:path";
|
|
87332
87996
|
|
|
87333
87997
|
class SkillsManifestManager {
|
|
87334
87998
|
static MANIFEST_FILENAME = ".skills-manifest.json";
|
|
@@ -87350,12 +88014,12 @@ class SkillsManifestManager {
|
|
|
87350
88014
|
return manifest;
|
|
87351
88015
|
}
|
|
87352
88016
|
static async writeManifest(skillsDir2, manifest) {
|
|
87353
|
-
const manifestPath =
|
|
88017
|
+
const manifestPath = join89(skillsDir2, SkillsManifestManager.MANIFEST_FILENAME);
|
|
87354
88018
|
await writeFile27(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
87355
88019
|
logger.debug(`Wrote manifest to: ${manifestPath}`);
|
|
87356
88020
|
}
|
|
87357
88021
|
static async readManifest(skillsDir2) {
|
|
87358
|
-
const manifestPath =
|
|
88022
|
+
const manifestPath = join89(skillsDir2, SkillsManifestManager.MANIFEST_FILENAME);
|
|
87359
88023
|
if (!await import_fs_extra21.pathExists(manifestPath)) {
|
|
87360
88024
|
logger.debug(`No manifest found at: ${manifestPath}`);
|
|
87361
88025
|
return null;
|
|
@@ -87378,7 +88042,7 @@ class SkillsManifestManager {
|
|
|
87378
88042
|
return "flat";
|
|
87379
88043
|
}
|
|
87380
88044
|
for (const dir of dirs.slice(0, 3)) {
|
|
87381
|
-
const dirPath =
|
|
88045
|
+
const dirPath = join89(skillsDir2, dir.name);
|
|
87382
88046
|
const subEntries = await readdir24(dirPath, { withFileTypes: true });
|
|
87383
88047
|
const hasSubdirs = subEntries.some((entry) => entry.isDirectory());
|
|
87384
88048
|
if (hasSubdirs) {
|
|
@@ -87397,7 +88061,7 @@ class SkillsManifestManager {
|
|
|
87397
88061
|
const entries = await readdir24(skillsDir2, { withFileTypes: true });
|
|
87398
88062
|
for (const entry of entries) {
|
|
87399
88063
|
if (entry.isDirectory() && !BUILD_ARTIFACT_DIRS.includes(entry.name) && !entry.name.startsWith(".")) {
|
|
87400
|
-
const skillPath =
|
|
88064
|
+
const skillPath = join89(skillsDir2, entry.name);
|
|
87401
88065
|
const hash = await SkillsManifestManager.hashDirectory(skillPath);
|
|
87402
88066
|
skills.push({
|
|
87403
88067
|
name: entry.name,
|
|
@@ -87409,11 +88073,11 @@ class SkillsManifestManager {
|
|
|
87409
88073
|
const categories = await readdir24(skillsDir2, { withFileTypes: true });
|
|
87410
88074
|
for (const category of categories) {
|
|
87411
88075
|
if (category.isDirectory() && !BUILD_ARTIFACT_DIRS.includes(category.name) && !category.name.startsWith(".")) {
|
|
87412
|
-
const categoryPath =
|
|
88076
|
+
const categoryPath = join89(skillsDir2, category.name);
|
|
87413
88077
|
const skillEntries = await readdir24(categoryPath, { withFileTypes: true });
|
|
87414
88078
|
for (const skillEntry of skillEntries) {
|
|
87415
88079
|
if (skillEntry.isDirectory() && !skillEntry.name.startsWith(".")) {
|
|
87416
|
-
const skillPath =
|
|
88080
|
+
const skillPath = join89(categoryPath, skillEntry.name);
|
|
87417
88081
|
const hash = await SkillsManifestManager.hashDirectory(skillPath);
|
|
87418
88082
|
skills.push({
|
|
87419
88083
|
name: skillEntry.name,
|
|
@@ -87443,7 +88107,7 @@ class SkillsManifestManager {
|
|
|
87443
88107
|
const files = [];
|
|
87444
88108
|
const entries = await readdir24(dirPath, { withFileTypes: true });
|
|
87445
88109
|
for (const entry of entries) {
|
|
87446
|
-
const fullPath =
|
|
88110
|
+
const fullPath = join89(dirPath, entry.name);
|
|
87447
88111
|
if (entry.name.startsWith(".") || BUILD_ARTIFACT_DIRS.includes(entry.name)) {
|
|
87448
88112
|
continue;
|
|
87449
88113
|
}
|
|
@@ -87565,7 +88229,7 @@ function getPathMapping(skillName, oldBasePath, newBasePath) {
|
|
|
87565
88229
|
// src/domains/skills/detection/script-detector.ts
|
|
87566
88230
|
var import_fs_extra22 = __toESM(require_lib3(), 1);
|
|
87567
88231
|
import { readdir as readdir25 } from "node:fs/promises";
|
|
87568
|
-
import { join as
|
|
88232
|
+
import { join as join90 } from "node:path";
|
|
87569
88233
|
async function scanDirectory(skillsDir2) {
|
|
87570
88234
|
if (!await import_fs_extra22.pathExists(skillsDir2)) {
|
|
87571
88235
|
return ["flat", []];
|
|
@@ -87578,12 +88242,12 @@ async function scanDirectory(skillsDir2) {
|
|
|
87578
88242
|
let totalSkillLikeCount = 0;
|
|
87579
88243
|
const allSkills = [];
|
|
87580
88244
|
for (const dir of dirs) {
|
|
87581
|
-
const dirPath =
|
|
88245
|
+
const dirPath = join90(skillsDir2, dir.name);
|
|
87582
88246
|
const subEntries = await readdir25(dirPath, { withFileTypes: true });
|
|
87583
88247
|
const subdirs = subEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
|
|
87584
88248
|
if (subdirs.length > 0) {
|
|
87585
88249
|
for (const subdir of subdirs.slice(0, 3)) {
|
|
87586
|
-
const subdirPath =
|
|
88250
|
+
const subdirPath = join90(dirPath, subdir.name);
|
|
87587
88251
|
const subdirFiles = await readdir25(subdirPath, { withFileTypes: true });
|
|
87588
88252
|
const hasSkillMarker = subdirFiles.some((file) => file.isFile() && (file.name === "skill.md" || file.name === "README.md" || file.name === "readme.md" || file.name === "config.json" || file.name === "package.json"));
|
|
87589
88253
|
if (hasSkillMarker) {
|
|
@@ -87740,12 +88404,12 @@ class SkillsMigrationDetector {
|
|
|
87740
88404
|
// src/domains/skills/skills-migrator.ts
|
|
87741
88405
|
init_logger();
|
|
87742
88406
|
init_types3();
|
|
87743
|
-
import { join as
|
|
88407
|
+
import { join as join95 } from "node:path";
|
|
87744
88408
|
|
|
87745
88409
|
// src/domains/skills/migrator/migration-executor.ts
|
|
87746
88410
|
init_logger();
|
|
87747
88411
|
import { copyFile as copyFile6, mkdir as mkdir28, readdir as readdir26, rm as rm10 } from "node:fs/promises";
|
|
87748
|
-
import { join as
|
|
88412
|
+
import { join as join91 } from "node:path";
|
|
87749
88413
|
var import_fs_extra24 = __toESM(require_lib3(), 1);
|
|
87750
88414
|
|
|
87751
88415
|
// src/domains/skills/skills-migration-prompts.ts
|
|
@@ -87910,8 +88574,8 @@ async function copySkillDirectory(sourceDir, destDir) {
|
|
|
87910
88574
|
await mkdir28(destDir, { recursive: true });
|
|
87911
88575
|
const entries = await readdir26(sourceDir, { withFileTypes: true });
|
|
87912
88576
|
for (const entry of entries) {
|
|
87913
|
-
const sourcePath =
|
|
87914
|
-
const destPath =
|
|
88577
|
+
const sourcePath = join91(sourceDir, entry.name);
|
|
88578
|
+
const destPath = join91(destDir, entry.name);
|
|
87915
88579
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
|
|
87916
88580
|
continue;
|
|
87917
88581
|
}
|
|
@@ -87926,7 +88590,7 @@ async function executeInternal(mappings, customizations, currentSkillsDir, inter
|
|
|
87926
88590
|
const migrated = [];
|
|
87927
88591
|
const preserved = [];
|
|
87928
88592
|
const errors2 = [];
|
|
87929
|
-
const tempDir =
|
|
88593
|
+
const tempDir = join91(currentSkillsDir, "..", ".skills-migration-temp");
|
|
87930
88594
|
await mkdir28(tempDir, { recursive: true });
|
|
87931
88595
|
try {
|
|
87932
88596
|
for (const mapping of mappings) {
|
|
@@ -87947,9 +88611,9 @@ async function executeInternal(mappings, customizations, currentSkillsDir, inter
|
|
|
87947
88611
|
}
|
|
87948
88612
|
}
|
|
87949
88613
|
const category = mapping.category;
|
|
87950
|
-
const targetPath = category ?
|
|
88614
|
+
const targetPath = category ? join91(tempDir, category, skillName) : join91(tempDir, skillName);
|
|
87951
88615
|
if (category) {
|
|
87952
|
-
await mkdir28(
|
|
88616
|
+
await mkdir28(join91(tempDir, category), { recursive: true });
|
|
87953
88617
|
}
|
|
87954
88618
|
await copySkillDirectory(currentSkillPath, targetPath);
|
|
87955
88619
|
migrated.push(skillName);
|
|
@@ -88016,7 +88680,7 @@ init_logger();
|
|
|
88016
88680
|
init_types3();
|
|
88017
88681
|
var import_fs_extra25 = __toESM(require_lib3(), 1);
|
|
88018
88682
|
import { copyFile as copyFile7, mkdir as mkdir29, readdir as readdir27, rm as rm11, stat as stat16 } from "node:fs/promises";
|
|
88019
|
-
import { basename as basename10, join as
|
|
88683
|
+
import { basename as basename10, join as join92, normalize as normalize8 } from "node:path";
|
|
88020
88684
|
function validatePath2(path14, paramName) {
|
|
88021
88685
|
if (!path14 || typeof path14 !== "string") {
|
|
88022
88686
|
throw new SkillsMigrationError(`${paramName} must be a non-empty string`);
|
|
@@ -88042,7 +88706,7 @@ class SkillsBackupManager {
|
|
|
88042
88706
|
const timestamp = Date.now();
|
|
88043
88707
|
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
88044
88708
|
const backupDirName = `${SkillsBackupManager.BACKUP_PREFIX}${timestamp}-${randomSuffix}`;
|
|
88045
|
-
const backupDir = parentDir ?
|
|
88709
|
+
const backupDir = parentDir ? join92(parentDir, backupDirName) : join92(skillsDir2, "..", backupDirName);
|
|
88046
88710
|
logger.info(`Creating backup at: ${backupDir}`);
|
|
88047
88711
|
try {
|
|
88048
88712
|
await mkdir29(backupDir, { recursive: true });
|
|
@@ -88093,7 +88757,7 @@ class SkillsBackupManager {
|
|
|
88093
88757
|
}
|
|
88094
88758
|
try {
|
|
88095
88759
|
const entries = await readdir27(parentDir, { withFileTypes: true });
|
|
88096
|
-
const backups = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(SkillsBackupManager.BACKUP_PREFIX)).map((entry) =>
|
|
88760
|
+
const backups = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(SkillsBackupManager.BACKUP_PREFIX)).map((entry) => join92(parentDir, entry.name));
|
|
88097
88761
|
backups.sort().reverse();
|
|
88098
88762
|
return backups;
|
|
88099
88763
|
} catch (error) {
|
|
@@ -88121,8 +88785,8 @@ class SkillsBackupManager {
|
|
|
88121
88785
|
static async copyDirectory(sourceDir, destDir) {
|
|
88122
88786
|
const entries = await readdir27(sourceDir, { withFileTypes: true });
|
|
88123
88787
|
for (const entry of entries) {
|
|
88124
|
-
const sourcePath =
|
|
88125
|
-
const destPath =
|
|
88788
|
+
const sourcePath = join92(sourceDir, entry.name);
|
|
88789
|
+
const destPath = join92(destDir, entry.name);
|
|
88126
88790
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
|
|
88127
88791
|
continue;
|
|
88128
88792
|
}
|
|
@@ -88138,7 +88802,7 @@ class SkillsBackupManager {
|
|
|
88138
88802
|
let size = 0;
|
|
88139
88803
|
const entries = await readdir27(dirPath, { withFileTypes: true });
|
|
88140
88804
|
for (const entry of entries) {
|
|
88141
|
-
const fullPath =
|
|
88805
|
+
const fullPath = join92(dirPath, entry.name);
|
|
88142
88806
|
if (entry.isSymbolicLink()) {
|
|
88143
88807
|
continue;
|
|
88144
88808
|
}
|
|
@@ -88174,12 +88838,12 @@ init_skip_directories();
|
|
|
88174
88838
|
import { createHash as createHash5 } from "node:crypto";
|
|
88175
88839
|
import { createReadStream as createReadStream3 } from "node:fs";
|
|
88176
88840
|
import { readFile as readFile45, readdir as readdir28 } from "node:fs/promises";
|
|
88177
|
-
import { join as
|
|
88841
|
+
import { join as join93, relative as relative16 } from "node:path";
|
|
88178
88842
|
async function getAllFiles(dirPath) {
|
|
88179
88843
|
const files = [];
|
|
88180
88844
|
const entries = await readdir28(dirPath, { withFileTypes: true });
|
|
88181
88845
|
for (const entry of entries) {
|
|
88182
|
-
const fullPath =
|
|
88846
|
+
const fullPath = join93(dirPath, entry.name);
|
|
88183
88847
|
if (entry.name.startsWith(".") || BUILD_ARTIFACT_DIRS.includes(entry.name) || entry.isSymbolicLink()) {
|
|
88184
88848
|
continue;
|
|
88185
88849
|
}
|
|
@@ -88193,12 +88857,12 @@ async function getAllFiles(dirPath) {
|
|
|
88193
88857
|
return files;
|
|
88194
88858
|
}
|
|
88195
88859
|
async function hashFile(filePath) {
|
|
88196
|
-
return new Promise((
|
|
88860
|
+
return new Promise((resolve20, reject) => {
|
|
88197
88861
|
const hash = createHash5("sha256");
|
|
88198
88862
|
const stream = createReadStream3(filePath);
|
|
88199
88863
|
stream.on("data", (chunk) => hash.update(chunk));
|
|
88200
88864
|
stream.on("end", () => {
|
|
88201
|
-
|
|
88865
|
+
resolve20(hash.digest("hex"));
|
|
88202
88866
|
});
|
|
88203
88867
|
stream.on("error", (error) => {
|
|
88204
88868
|
stream.destroy();
|
|
@@ -88306,7 +88970,7 @@ async function detectFileChanges(currentSkillPath, baselineSkillPath) {
|
|
|
88306
88970
|
init_types3();
|
|
88307
88971
|
var import_fs_extra27 = __toESM(require_lib3(), 1);
|
|
88308
88972
|
import { readdir as readdir29 } from "node:fs/promises";
|
|
88309
|
-
import { join as
|
|
88973
|
+
import { join as join94, normalize as normalize9 } from "node:path";
|
|
88310
88974
|
function validatePath3(path14, paramName) {
|
|
88311
88975
|
if (!path14 || typeof path14 !== "string") {
|
|
88312
88976
|
throw new SkillsMigrationError(`${paramName} must be a non-empty string`);
|
|
@@ -88327,13 +88991,13 @@ async function scanSkillsDirectory(skillsDir2) {
|
|
|
88327
88991
|
if (dirs.length === 0) {
|
|
88328
88992
|
return ["flat", []];
|
|
88329
88993
|
}
|
|
88330
|
-
const firstDirPath =
|
|
88994
|
+
const firstDirPath = join94(skillsDir2, dirs[0].name);
|
|
88331
88995
|
const subEntries = await readdir29(firstDirPath, { withFileTypes: true });
|
|
88332
88996
|
const subdirs = subEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
|
|
88333
88997
|
if (subdirs.length > 0) {
|
|
88334
88998
|
let skillLikeCount = 0;
|
|
88335
88999
|
for (const subdir of subdirs.slice(0, 3)) {
|
|
88336
|
-
const subdirPath =
|
|
89000
|
+
const subdirPath = join94(firstDirPath, subdir.name);
|
|
88337
89001
|
const subdirFiles = await readdir29(subdirPath, { withFileTypes: true });
|
|
88338
89002
|
const hasSkillMarker = subdirFiles.some((file) => file.isFile() && (file.name === "skill.md" || file.name === "README.md" || file.name === "readme.md" || file.name === "config.json" || file.name === "package.json"));
|
|
88339
89003
|
if (hasSkillMarker) {
|
|
@@ -88343,7 +89007,7 @@ async function scanSkillsDirectory(skillsDir2) {
|
|
|
88343
89007
|
if (skillLikeCount > 0) {
|
|
88344
89008
|
const skills = [];
|
|
88345
89009
|
for (const dir of dirs) {
|
|
88346
|
-
const categoryPath =
|
|
89010
|
+
const categoryPath = join94(skillsDir2, dir.name);
|
|
88347
89011
|
const skillDirs = await readdir29(categoryPath, { withFileTypes: true });
|
|
88348
89012
|
skills.push(...skillDirs.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name));
|
|
88349
89013
|
}
|
|
@@ -88353,7 +89017,7 @@ async function scanSkillsDirectory(skillsDir2) {
|
|
|
88353
89017
|
return ["flat", dirs.map((dir) => dir.name)];
|
|
88354
89018
|
}
|
|
88355
89019
|
async function findSkillPath(skillsDir2, skillName) {
|
|
88356
|
-
const flatPath =
|
|
89020
|
+
const flatPath = join94(skillsDir2, skillName);
|
|
88357
89021
|
if (await import_fs_extra27.pathExists(flatPath)) {
|
|
88358
89022
|
return { path: flatPath, category: undefined };
|
|
88359
89023
|
}
|
|
@@ -88362,8 +89026,8 @@ async function findSkillPath(skillsDir2, skillName) {
|
|
|
88362
89026
|
if (!entry.isDirectory() || entry.name.startsWith(".") || entry.name === "node_modules") {
|
|
88363
89027
|
continue;
|
|
88364
89028
|
}
|
|
88365
|
-
const categoryPath =
|
|
88366
|
-
const skillPath =
|
|
89029
|
+
const categoryPath = join94(skillsDir2, entry.name);
|
|
89030
|
+
const skillPath = join94(categoryPath, skillName);
|
|
88367
89031
|
if (await import_fs_extra27.pathExists(skillPath)) {
|
|
88368
89032
|
return { path: skillPath, category: entry.name };
|
|
88369
89033
|
}
|
|
@@ -88457,7 +89121,7 @@ class SkillsMigrator {
|
|
|
88457
89121
|
}
|
|
88458
89122
|
}
|
|
88459
89123
|
if (options2.backup && !options2.dryRun) {
|
|
88460
|
-
const claudeDir2 =
|
|
89124
|
+
const claudeDir2 = join95(currentSkillsDir, "..");
|
|
88461
89125
|
result.backupPath = await SkillsBackupManager.createBackup(currentSkillsDir, claudeDir2);
|
|
88462
89126
|
logger.success(`Backup created at: ${result.backupPath}`);
|
|
88463
89127
|
}
|
|
@@ -88518,7 +89182,7 @@ async function handleMigration(ctx) {
|
|
|
88518
89182
|
logger.debug("Skipping skills migration (fresh installation)");
|
|
88519
89183
|
return ctx;
|
|
88520
89184
|
}
|
|
88521
|
-
const newSkillsDir =
|
|
89185
|
+
const newSkillsDir = join96(ctx.extractDir, ".claude", "skills");
|
|
88522
89186
|
const currentSkillsDir = PathResolver.buildSkillsPath(ctx.resolvedDir, ctx.options.global);
|
|
88523
89187
|
if (!await import_fs_extra28.pathExists(newSkillsDir) || !await import_fs_extra28.pathExists(currentSkillsDir)) {
|
|
88524
89188
|
return ctx;
|
|
@@ -88542,13 +89206,13 @@ async function handleMigration(ctx) {
|
|
|
88542
89206
|
}
|
|
88543
89207
|
// src/commands/init/phases/opencode-handler.ts
|
|
88544
89208
|
import { cp as cp3, readdir as readdir31, rm as rm12 } from "node:fs/promises";
|
|
88545
|
-
import { join as
|
|
89209
|
+
import { join as join98 } from "node:path";
|
|
88546
89210
|
|
|
88547
89211
|
// src/services/transformers/opencode-path-transformer.ts
|
|
88548
89212
|
init_logger();
|
|
88549
89213
|
import { readFile as readFile46, readdir as readdir30, writeFile as writeFile28 } from "node:fs/promises";
|
|
88550
89214
|
import { platform as platform12 } from "node:os";
|
|
88551
|
-
import { extname as extname5, join as
|
|
89215
|
+
import { extname as extname5, join as join97 } from "node:path";
|
|
88552
89216
|
var IS_WINDOWS2 = platform12() === "win32";
|
|
88553
89217
|
function getOpenCodeGlobalPath() {
|
|
88554
89218
|
return "$HOME/.config/opencode/";
|
|
@@ -88609,7 +89273,7 @@ async function transformPathsForGlobalOpenCode(directory, options2 = {}) {
|
|
|
88609
89273
|
async function processDirectory2(dir) {
|
|
88610
89274
|
const entries = await readdir30(dir, { withFileTypes: true });
|
|
88611
89275
|
for (const entry of entries) {
|
|
88612
|
-
const fullPath =
|
|
89276
|
+
const fullPath = join97(dir, entry.name);
|
|
88613
89277
|
if (entry.isDirectory()) {
|
|
88614
89278
|
if (entry.name === "node_modules" || entry.name.startsWith(".")) {
|
|
88615
89279
|
continue;
|
|
@@ -88648,7 +89312,7 @@ async function handleOpenCode(ctx) {
|
|
|
88648
89312
|
if (ctx.cancelled || !ctx.extractDir || !ctx.resolvedDir) {
|
|
88649
89313
|
return ctx;
|
|
88650
89314
|
}
|
|
88651
|
-
const openCodeSource =
|
|
89315
|
+
const openCodeSource = join98(ctx.extractDir, ".opencode");
|
|
88652
89316
|
if (!await import_fs_extra29.pathExists(openCodeSource)) {
|
|
88653
89317
|
logger.debug("No .opencode directory in archive, skipping");
|
|
88654
89318
|
return ctx;
|
|
@@ -88666,8 +89330,8 @@ async function handleOpenCode(ctx) {
|
|
|
88666
89330
|
await import_fs_extra29.ensureDir(targetDir);
|
|
88667
89331
|
const entries = await readdir31(openCodeSource, { withFileTypes: true });
|
|
88668
89332
|
for (const entry of entries) {
|
|
88669
|
-
const sourcePath =
|
|
88670
|
-
const targetPath =
|
|
89333
|
+
const sourcePath = join98(openCodeSource, entry.name);
|
|
89334
|
+
const targetPath = join98(targetDir, entry.name);
|
|
88671
89335
|
if (await import_fs_extra29.pathExists(targetPath)) {
|
|
88672
89336
|
if (!ctx.options.forceOverwrite) {
|
|
88673
89337
|
logger.verbose(`Skipping existing: ${entry.name}`);
|
|
@@ -88764,20 +89428,21 @@ Please use only one download method.`);
|
|
|
88764
89428
|
}
|
|
88765
89429
|
// src/commands/init/phases/post-install-handler.ts
|
|
88766
89430
|
init_projects_registry();
|
|
88767
|
-
import { join as
|
|
89431
|
+
import { join as join101 } from "node:path";
|
|
89432
|
+
init_cc_version_checker();
|
|
88768
89433
|
init_logger();
|
|
88769
89434
|
init_path_resolver();
|
|
88770
|
-
var
|
|
89435
|
+
var import_fs_extra32 = __toESM(require_lib3(), 1);
|
|
88771
89436
|
async function handlePostInstall(ctx) {
|
|
88772
89437
|
if (ctx.cancelled || !ctx.extractDir || !ctx.resolvedDir || !ctx.claudeDir) {
|
|
88773
89438
|
return ctx;
|
|
88774
89439
|
}
|
|
88775
89440
|
if (ctx.options.global) {
|
|
88776
|
-
const claudeMdSource =
|
|
88777
|
-
const claudeMdDest =
|
|
88778
|
-
if (await
|
|
88779
|
-
if (ctx.options.fresh || !await
|
|
88780
|
-
await
|
|
89441
|
+
const claudeMdSource = join101(ctx.extractDir, "CLAUDE.md");
|
|
89442
|
+
const claudeMdDest = join101(ctx.resolvedDir, "CLAUDE.md");
|
|
89443
|
+
if (await import_fs_extra32.pathExists(claudeMdSource)) {
|
|
89444
|
+
if (ctx.options.fresh || !await import_fs_extra32.pathExists(claudeMdDest)) {
|
|
89445
|
+
await import_fs_extra32.copy(claudeMdSource, claudeMdDest);
|
|
88781
89446
|
logger.success(ctx.options.fresh ? "Replaced CLAUDE.md in global directory (fresh install)" : "Copied CLAUDE.md to global directory");
|
|
88782
89447
|
} else {
|
|
88783
89448
|
logger.debug("CLAUDE.md already exists in global directory (preserved)");
|
|
@@ -88796,6 +89461,77 @@ async function handlePostInstall(ctx) {
|
|
|
88796
89461
|
withSudo: ctx.options.withSudo
|
|
88797
89462
|
});
|
|
88798
89463
|
}
|
|
89464
|
+
let pluginSupported = false;
|
|
89465
|
+
try {
|
|
89466
|
+
const { requireCCPluginSupport: requireCCPluginSupport2 } = await Promise.resolve().then(() => (init_cc_version_checker(), exports_cc_version_checker));
|
|
89467
|
+
await requireCCPluginSupport2();
|
|
89468
|
+
pluginSupported = true;
|
|
89469
|
+
} catch (error) {
|
|
89470
|
+
if (error instanceof CCPluginSupportError) {
|
|
89471
|
+
logger.info(`Plugin install skipped: ${error.message}`);
|
|
89472
|
+
if (error.code === "cc_version_too_old") {
|
|
89473
|
+
logger.info("Upgrade: brew upgrade claude-code (or npm i -g @anthropic-ai/claude-code)");
|
|
89474
|
+
}
|
|
89475
|
+
if (error.code === "cc_not_found") {
|
|
89476
|
+
logger.info("Install Claude Code CLI, then re-run ck init to enable /ck:* plugin skills");
|
|
89477
|
+
}
|
|
89478
|
+
} else {
|
|
89479
|
+
logger.info(`Plugin install skipped: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
89480
|
+
}
|
|
89481
|
+
}
|
|
89482
|
+
let pluginVerified = false;
|
|
89483
|
+
if (pluginSupported) {
|
|
89484
|
+
try {
|
|
89485
|
+
const { handlePluginInstall: handlePluginInstall2 } = await Promise.resolve().then(() => (init_plugin_installer(), exports_plugin_installer));
|
|
89486
|
+
const pluginResult = await handlePluginInstall2(ctx.extractDir);
|
|
89487
|
+
pluginVerified = pluginResult.verified;
|
|
89488
|
+
if (pluginResult.error) {
|
|
89489
|
+
logger.info(`Plugin install issue: ${pluginResult.error}`);
|
|
89490
|
+
}
|
|
89491
|
+
} catch (error) {
|
|
89492
|
+
logger.debug(`Plugin install skipped: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
89493
|
+
}
|
|
89494
|
+
}
|
|
89495
|
+
if (ctx.claudeDir && ctx.kitType) {
|
|
89496
|
+
try {
|
|
89497
|
+
const { updateKitPluginState: updateKitPluginState2 } = await Promise.resolve().then(() => (init_metadata_migration(), exports_metadata_migration));
|
|
89498
|
+
await updateKitPluginState2(ctx.claudeDir, ctx.kitType, {
|
|
89499
|
+
pluginInstalled: pluginVerified,
|
|
89500
|
+
pluginInstalledAt: new Date().toISOString(),
|
|
89501
|
+
pluginVersion: ctx.selectedVersion || "unknown"
|
|
89502
|
+
});
|
|
89503
|
+
} catch {}
|
|
89504
|
+
}
|
|
89505
|
+
if (ctx.deferredDeletions?.length && ctx.claudeDir) {
|
|
89506
|
+
try {
|
|
89507
|
+
const { migrateUserSkills: migrateUserSkills2 } = await Promise.resolve().then(() => (init_skill_migration_merger(), exports_skill_migration_merger));
|
|
89508
|
+
const migration = await migrateUserSkills2(ctx.claudeDir, pluginVerified);
|
|
89509
|
+
if (pluginVerified) {
|
|
89510
|
+
if (!migration.canDelete) {
|
|
89511
|
+
logger.warning("Skill migration metadata unavailable — preserving existing skills (fail-safe)");
|
|
89512
|
+
return { ...ctx, installSkills, pluginSupported };
|
|
89513
|
+
}
|
|
89514
|
+
const preservedDirs = new Set(migration.preserved.map(normalizeSkillDir));
|
|
89515
|
+
const safeDeletions = ctx.deferredDeletions.filter((d3) => {
|
|
89516
|
+
const dirPath = extractSkillDirFromDeletionPath(d3);
|
|
89517
|
+
if (!dirPath)
|
|
89518
|
+
return true;
|
|
89519
|
+
return !preservedDirs.has(normalizeSkillDir(dirPath));
|
|
89520
|
+
});
|
|
89521
|
+
if (safeDeletions.length > 0) {
|
|
89522
|
+
const { handleDeletions: handleDeletions2 } = await Promise.resolve().then(() => (init_deletion_handler(), exports_deletion_handler));
|
|
89523
|
+
const deferredResult = await handleDeletions2({ deletions: safeDeletions }, ctx.claudeDir);
|
|
89524
|
+
if (deferredResult.deletedPaths.length > 0) {
|
|
89525
|
+
logger.info(`Removed ${deferredResult.deletedPaths.length} old skill file(s) (replaced by plugin)`);
|
|
89526
|
+
}
|
|
89527
|
+
}
|
|
89528
|
+
} else {
|
|
89529
|
+
logger.info("Plugin not verified — keeping existing skills as fallback");
|
|
89530
|
+
}
|
|
89531
|
+
} catch (error) {
|
|
89532
|
+
logger.debug(`Deferred skill deletion failed: ${error}`);
|
|
89533
|
+
}
|
|
89534
|
+
}
|
|
88799
89535
|
if (!ctx.isNonInteractive) {
|
|
88800
89536
|
const { isGeminiInstalled: isGeminiInstalled2 } = await Promise.resolve().then(() => (init_package_installer(), exports_package_installer));
|
|
88801
89537
|
const { checkExistingGeminiConfig: checkExistingGeminiConfig2, findMcpConfigPath: findMcpConfigPath2, processGeminiMcpLinking: processGeminiMcpLinking2 } = await Promise.resolve().then(() => (init_gemini_mcp_linker(), exports_gemini_mcp_linker));
|
|
@@ -88822,7 +89558,7 @@ async function handlePostInstall(ctx) {
|
|
|
88822
89558
|
}
|
|
88823
89559
|
if (!ctx.options.skipSetup) {
|
|
88824
89560
|
await promptSetupWizardIfNeeded({
|
|
88825
|
-
envPath:
|
|
89561
|
+
envPath: join101(ctx.claudeDir, ".env"),
|
|
88826
89562
|
claudeDir: ctx.claudeDir,
|
|
88827
89563
|
isGlobal: ctx.options.global,
|
|
88828
89564
|
isNonInteractive: ctx.isNonInteractive,
|
|
@@ -88839,14 +89575,26 @@ async function handlePostInstall(ctx) {
|
|
|
88839
89575
|
}
|
|
88840
89576
|
return {
|
|
88841
89577
|
...ctx,
|
|
88842
|
-
installSkills
|
|
89578
|
+
installSkills,
|
|
89579
|
+
pluginSupported
|
|
88843
89580
|
};
|
|
88844
89581
|
}
|
|
89582
|
+
function normalizeSkillDir(path14) {
|
|
89583
|
+
return path14.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
89584
|
+
}
|
|
89585
|
+
function extractSkillDirFromDeletionPath(path14) {
|
|
89586
|
+
const normalized = normalizeSkillDir(path14);
|
|
89587
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
89588
|
+
if (parts.length < 2 || parts[0] !== "skills") {
|
|
89589
|
+
return null;
|
|
89590
|
+
}
|
|
89591
|
+
return `skills/${parts[1]}`;
|
|
89592
|
+
}
|
|
88845
89593
|
// src/commands/init/phases/selection-handler.ts
|
|
88846
89594
|
init_config_manager();
|
|
88847
89595
|
init_github_client();
|
|
88848
89596
|
import { mkdir as mkdir30 } from "node:fs/promises";
|
|
88849
|
-
import { join as
|
|
89597
|
+
import { join as join103, resolve as resolve21 } from "node:path";
|
|
88850
89598
|
|
|
88851
89599
|
// src/domains/github/kit-access-checker.ts
|
|
88852
89600
|
init_logger();
|
|
@@ -88878,8 +89626,8 @@ async function detectAccessibleKits() {
|
|
|
88878
89626
|
// src/domains/github/preflight-checker.ts
|
|
88879
89627
|
init_logger();
|
|
88880
89628
|
import { exec as exec8 } from "node:child_process";
|
|
88881
|
-
import { promisify as
|
|
88882
|
-
var execAsync8 =
|
|
89629
|
+
import { promisify as promisify16 } from "node:util";
|
|
89630
|
+
var execAsync8 = promisify16(exec8);
|
|
88883
89631
|
function createSuccessfulPreflightResult() {
|
|
88884
89632
|
return {
|
|
88885
89633
|
success: true,
|
|
@@ -88976,11 +89724,12 @@ async function runPreflightChecks() {
|
|
|
88976
89724
|
|
|
88977
89725
|
// src/domains/installation/fresh-installer.ts
|
|
88978
89726
|
init_metadata_migration();
|
|
88979
|
-
|
|
88980
|
-
import { dirname as dirname21, join as join101, resolve as resolve19 } from "node:path";
|
|
89727
|
+
init_manifest_reader();
|
|
88981
89728
|
init_logger();
|
|
88982
89729
|
init_safe_spinner();
|
|
88983
|
-
var
|
|
89730
|
+
var import_fs_extra33 = __toESM(require_lib3(), 1);
|
|
89731
|
+
import { existsSync as existsSync50, readdirSync as readdirSync4, rmSync as rmSync3, rmdirSync as rmdirSync2, unlinkSync as unlinkSync4 } from "node:fs";
|
|
89732
|
+
import { dirname as dirname22, join as join102, resolve as resolve20 } from "node:path";
|
|
88984
89733
|
var CLAUDEKIT_SUBDIRECTORIES = ["commands", "agents", "skills", "rules", "hooks"];
|
|
88985
89734
|
async function analyzeFreshInstallation(claudeDir2) {
|
|
88986
89735
|
const metadata = await readManifest(claudeDir2);
|
|
@@ -89025,15 +89774,15 @@ async function analyzeFreshInstallation(claudeDir2) {
|
|
|
89025
89774
|
};
|
|
89026
89775
|
}
|
|
89027
89776
|
function cleanupEmptyDirectories2(filePath, claudeDir2) {
|
|
89028
|
-
const normalizedClaudeDir =
|
|
89029
|
-
let currentDir =
|
|
89777
|
+
const normalizedClaudeDir = resolve20(claudeDir2);
|
|
89778
|
+
let currentDir = resolve20(dirname22(filePath));
|
|
89030
89779
|
while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir)) {
|
|
89031
89780
|
try {
|
|
89032
89781
|
const entries = readdirSync4(currentDir);
|
|
89033
89782
|
if (entries.length === 0) {
|
|
89034
89783
|
rmdirSync2(currentDir);
|
|
89035
89784
|
logger.debug(`Removed empty directory: ${currentDir}`);
|
|
89036
|
-
currentDir =
|
|
89785
|
+
currentDir = resolve20(dirname22(currentDir));
|
|
89037
89786
|
} else {
|
|
89038
89787
|
break;
|
|
89039
89788
|
}
|
|
@@ -89050,7 +89799,7 @@ async function removeFilesByOwnership(claudeDir2, analysis, includeModified) {
|
|
|
89050
89799
|
const filesToRemove = includeModified ? [...analysis.ckFiles, ...analysis.ckModifiedFiles] : analysis.ckFiles;
|
|
89051
89800
|
const filesToPreserve = includeModified ? analysis.userFiles : [...analysis.ckModifiedFiles, ...analysis.userFiles];
|
|
89052
89801
|
for (const file of filesToRemove) {
|
|
89053
|
-
const fullPath =
|
|
89802
|
+
const fullPath = join102(claudeDir2, file.path);
|
|
89054
89803
|
try {
|
|
89055
89804
|
if (existsSync50(fullPath)) {
|
|
89056
89805
|
unlinkSync4(fullPath);
|
|
@@ -89075,13 +89824,13 @@ async function removeFilesByOwnership(claudeDir2, analysis, includeModified) {
|
|
|
89075
89824
|
};
|
|
89076
89825
|
}
|
|
89077
89826
|
async function updateMetadataAfterFresh(claudeDir2, removedFiles) {
|
|
89078
|
-
const metadataPath =
|
|
89079
|
-
if (!await
|
|
89827
|
+
const metadataPath = join102(claudeDir2, "metadata.json");
|
|
89828
|
+
if (!await import_fs_extra33.pathExists(metadataPath)) {
|
|
89080
89829
|
return;
|
|
89081
89830
|
}
|
|
89082
89831
|
let content;
|
|
89083
89832
|
try {
|
|
89084
|
-
content = await
|
|
89833
|
+
content = await import_fs_extra33.readFile(metadataPath, "utf-8");
|
|
89085
89834
|
} catch (readError) {
|
|
89086
89835
|
logger.warning(`Failed to read metadata.json: ${readError instanceof Error ? readError.message : String(readError)}`);
|
|
89087
89836
|
return;
|
|
@@ -89107,7 +89856,7 @@ async function updateMetadataAfterFresh(claudeDir2, removedFiles) {
|
|
|
89107
89856
|
metadata.files = metadata.files.filter((f3) => !removedSet.has(f3.path));
|
|
89108
89857
|
}
|
|
89109
89858
|
try {
|
|
89110
|
-
await
|
|
89859
|
+
await import_fs_extra33.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
89111
89860
|
logger.debug(`Updated metadata.json, removed ${removedFiles.length} file entries`);
|
|
89112
89861
|
} catch (writeError) {
|
|
89113
89862
|
logger.warning(`Failed to write metadata.json: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
|
|
@@ -89118,16 +89867,16 @@ async function removeSubdirectoriesFallback(claudeDir2) {
|
|
|
89118
89867
|
const removedFiles = [];
|
|
89119
89868
|
let removedDirCount = 0;
|
|
89120
89869
|
for (const subdir of CLAUDEKIT_SUBDIRECTORIES) {
|
|
89121
|
-
const subdirPath =
|
|
89122
|
-
if (await
|
|
89870
|
+
const subdirPath = join102(claudeDir2, subdir);
|
|
89871
|
+
if (await import_fs_extra33.pathExists(subdirPath)) {
|
|
89123
89872
|
rmSync3(subdirPath, { recursive: true, force: true });
|
|
89124
89873
|
removedDirCount++;
|
|
89125
89874
|
removedFiles.push(`${subdir}/ (entire directory)`);
|
|
89126
89875
|
logger.debug(`Removed subdirectory: ${subdir}/`);
|
|
89127
89876
|
}
|
|
89128
89877
|
}
|
|
89129
|
-
const metadataPath =
|
|
89130
|
-
if (await
|
|
89878
|
+
const metadataPath = join102(claudeDir2, "metadata.json");
|
|
89879
|
+
if (await import_fs_extra33.pathExists(metadataPath)) {
|
|
89131
89880
|
unlinkSync4(metadataPath);
|
|
89132
89881
|
removedFiles.push("metadata.json");
|
|
89133
89882
|
}
|
|
@@ -89140,7 +89889,7 @@ async function removeSubdirectoriesFallback(claudeDir2) {
|
|
|
89140
89889
|
};
|
|
89141
89890
|
}
|
|
89142
89891
|
async function handleFreshInstallation(claudeDir2, prompts) {
|
|
89143
|
-
if (!await
|
|
89892
|
+
if (!await import_fs_extra33.pathExists(claudeDir2)) {
|
|
89144
89893
|
logger.info(".claude directory does not exist, proceeding with fresh installation");
|
|
89145
89894
|
return true;
|
|
89146
89895
|
}
|
|
@@ -89172,10 +89921,11 @@ async function handleFreshInstallation(claudeDir2, prompts) {
|
|
|
89172
89921
|
|
|
89173
89922
|
// src/commands/init/phases/selection-handler.ts
|
|
89174
89923
|
init_claudekit_scanner();
|
|
89924
|
+
init_manifest_reader();
|
|
89175
89925
|
init_logger();
|
|
89176
89926
|
init_path_resolver();
|
|
89177
89927
|
init_types3();
|
|
89178
|
-
var
|
|
89928
|
+
var import_fs_extra34 = __toESM(require_lib3(), 1);
|
|
89179
89929
|
|
|
89180
89930
|
// src/commands/init/types.ts
|
|
89181
89931
|
function isSyncContext(ctx) {
|
|
@@ -89344,7 +90094,7 @@ async function handleSelection(ctx) {
|
|
|
89344
90094
|
}
|
|
89345
90095
|
}
|
|
89346
90096
|
}
|
|
89347
|
-
const resolvedDir =
|
|
90097
|
+
const resolvedDir = resolve21(targetDir);
|
|
89348
90098
|
logger.info(`Target directory: ${resolvedDir}`);
|
|
89349
90099
|
if (!ctx.options.global && PathResolver.isLocalSameAsGlobal(resolvedDir)) {
|
|
89350
90100
|
logger.warning("You're at HOME directory. Installing here modifies your GLOBAL ClaudeKit.");
|
|
@@ -89366,7 +90116,7 @@ async function handleSelection(ctx) {
|
|
|
89366
90116
|
return { ...ctx, cancelled: true };
|
|
89367
90117
|
}
|
|
89368
90118
|
}
|
|
89369
|
-
if (!await
|
|
90119
|
+
if (!await import_fs_extra34.pathExists(resolvedDir)) {
|
|
89370
90120
|
if (ctx.options.global) {
|
|
89371
90121
|
await mkdir30(resolvedDir, { recursive: true });
|
|
89372
90122
|
logger.info(`Created global directory: ${resolvedDir}`);
|
|
@@ -89378,7 +90128,7 @@ async function handleSelection(ctx) {
|
|
|
89378
90128
|
}
|
|
89379
90129
|
if (!ctx.options.fresh) {
|
|
89380
90130
|
const prefix = PathResolver.getPathPrefix(ctx.options.global);
|
|
89381
|
-
const claudeDir2 = prefix ?
|
|
90131
|
+
const claudeDir2 = prefix ? join103(resolvedDir, prefix) : resolvedDir;
|
|
89382
90132
|
try {
|
|
89383
90133
|
const existingMetadata = await readManifest(claudeDir2);
|
|
89384
90134
|
if (existingMetadata?.kits) {
|
|
@@ -89410,7 +90160,7 @@ async function handleSelection(ctx) {
|
|
|
89410
90160
|
}
|
|
89411
90161
|
if (ctx.options.fresh) {
|
|
89412
90162
|
const prefix = PathResolver.getPathPrefix(ctx.options.global);
|
|
89413
|
-
const claudeDir2 = prefix ?
|
|
90163
|
+
const claudeDir2 = prefix ? join103(resolvedDir, prefix) : resolvedDir;
|
|
89414
90164
|
const canProceed = await handleFreshInstallation(claudeDir2, ctx.prompts);
|
|
89415
90165
|
if (!canProceed) {
|
|
89416
90166
|
return { ...ctx, cancelled: true };
|
|
@@ -89429,7 +90179,7 @@ async function handleSelection(ctx) {
|
|
|
89429
90179
|
logger.info("Fetching available versions...");
|
|
89430
90180
|
let currentVersion = null;
|
|
89431
90181
|
try {
|
|
89432
|
-
const metadataPath = ctx.options.global ?
|
|
90182
|
+
const metadataPath = ctx.options.global ? join103(PathResolver.getGlobalKitDir(), "metadata.json") : join103(resolvedDir, ".claude", "metadata.json");
|
|
89433
90183
|
const metadata = await readClaudeKitMetadata(metadataPath);
|
|
89434
90184
|
currentVersion = metadata?.version || null;
|
|
89435
90185
|
if (currentVersion) {
|
|
@@ -89503,25 +90253,27 @@ async function handleSelection(ctx) {
|
|
|
89503
90253
|
};
|
|
89504
90254
|
}
|
|
89505
90255
|
// src/commands/init/phases/sync-handler.ts
|
|
89506
|
-
import { copyFile as copyFile8, mkdir as mkdir31, open as open4, readFile as
|
|
89507
|
-
import { dirname as
|
|
90256
|
+
import { copyFile as copyFile8, mkdir as mkdir31, open as open4, readFile as readFile49, rename as rename7, stat as stat17, unlink as unlink11, writeFile as writeFile30 } from "node:fs/promises";
|
|
90257
|
+
import { dirname as dirname23, join as join104, resolve as resolve22 } from "node:path";
|
|
90258
|
+
init_cc_version_checker();
|
|
90259
|
+
init_manifest_reader();
|
|
89508
90260
|
init_logger();
|
|
89509
90261
|
init_path_resolver();
|
|
89510
|
-
var
|
|
90262
|
+
var import_fs_extra35 = __toESM(require_lib3(), 1);
|
|
89511
90263
|
var import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
89512
90264
|
async function handleSync(ctx) {
|
|
89513
90265
|
if (!ctx.options.sync) {
|
|
89514
90266
|
return ctx;
|
|
89515
90267
|
}
|
|
89516
|
-
const resolvedDir = ctx.options.global ? PathResolver.getGlobalKitDir() :
|
|
89517
|
-
const claudeDir2 = ctx.options.global ? resolvedDir :
|
|
89518
|
-
if (!await
|
|
90268
|
+
const resolvedDir = ctx.options.global ? PathResolver.getGlobalKitDir() : resolve22(ctx.options.dir || ".");
|
|
90269
|
+
const claudeDir2 = ctx.options.global ? resolvedDir : join104(resolvedDir, ".claude");
|
|
90270
|
+
if (!await import_fs_extra35.pathExists(claudeDir2)) {
|
|
89519
90271
|
logger.error("Cannot sync: no .claude directory found");
|
|
89520
90272
|
ctx.prompts.note("Run 'ck init' without --sync to install first.", "No Installation Found");
|
|
89521
90273
|
return { ...ctx, cancelled: true };
|
|
89522
90274
|
}
|
|
89523
|
-
const metadataPath =
|
|
89524
|
-
if (!await
|
|
90275
|
+
const metadataPath = join104(claudeDir2, "metadata.json");
|
|
90276
|
+
if (!await import_fs_extra35.pathExists(metadataPath)) {
|
|
89525
90277
|
logger.error("Cannot sync: no metadata.json found");
|
|
89526
90278
|
ctx.prompts.note(`Your installation may be from an older version.
|
|
89527
90279
|
Run 'ck init' to update.`, "Legacy Installation");
|
|
@@ -89618,17 +90370,42 @@ function getLockTimeout() {
|
|
|
89618
90370
|
return timeoutMs;
|
|
89619
90371
|
}
|
|
89620
90372
|
var STALE_LOCK_THRESHOLD_MS = 5 * 60 * 1000;
|
|
90373
|
+
function isProcessAlive(pid) {
|
|
90374
|
+
try {
|
|
90375
|
+
process.kill(pid, 0);
|
|
90376
|
+
return true;
|
|
90377
|
+
} catch (error) {
|
|
90378
|
+
const code2 = error.code;
|
|
90379
|
+
return code2 === "EPERM";
|
|
90380
|
+
}
|
|
90381
|
+
}
|
|
90382
|
+
async function readLockPayload(lockPath) {
|
|
90383
|
+
try {
|
|
90384
|
+
const raw2 = await readFile49(lockPath, "utf-8");
|
|
90385
|
+
const parsed = JSON.parse(raw2);
|
|
90386
|
+
if (typeof parsed.pid === "number" && Number.isInteger(parsed.pid) && parsed.pid > 0) {
|
|
90387
|
+
return {
|
|
90388
|
+
pid: parsed.pid,
|
|
90389
|
+
startedAt: typeof parsed.startedAt === "string" && parsed.startedAt.length > 0 ? parsed.startedAt : "unknown"
|
|
90390
|
+
};
|
|
90391
|
+
}
|
|
90392
|
+
} catch {}
|
|
90393
|
+
return null;
|
|
90394
|
+
}
|
|
89621
90395
|
async function acquireSyncLock(global3) {
|
|
89622
90396
|
const cacheDir = PathResolver.getCacheDir(global3);
|
|
89623
|
-
const lockPath =
|
|
90397
|
+
const lockPath = join104(cacheDir, ".sync-lock");
|
|
89624
90398
|
const startTime = Date.now();
|
|
89625
90399
|
const lockTimeout = getLockTimeout();
|
|
89626
|
-
await mkdir31(
|
|
90400
|
+
await mkdir31(dirname23(lockPath), { recursive: true });
|
|
89627
90401
|
while (Date.now() - startTime < lockTimeout) {
|
|
89628
90402
|
try {
|
|
89629
90403
|
const handle = await open4(lockPath, "wx");
|
|
90404
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid, startedAt: new Date().toISOString() }), "utf-8");
|
|
89630
90405
|
return async () => {
|
|
89631
|
-
|
|
90406
|
+
try {
|
|
90407
|
+
await handle.close();
|
|
90408
|
+
} catch {}
|
|
89632
90409
|
await unlink11(lockPath).catch(() => {});
|
|
89633
90410
|
};
|
|
89634
90411
|
} catch (err) {
|
|
@@ -89636,18 +90413,29 @@ async function acquireSyncLock(global3) {
|
|
|
89636
90413
|
try {
|
|
89637
90414
|
const lockStat = await stat17(lockPath);
|
|
89638
90415
|
const lockAge = Math.abs(Date.now() - lockStat.mtimeMs);
|
|
89639
|
-
|
|
89640
|
-
|
|
90416
|
+
const lockOwner = await readLockPayload(lockPath);
|
|
90417
|
+
if (lockOwner?.pid === process.pid) {
|
|
90418
|
+
throw new Error("Sync lock is already held by current process");
|
|
90419
|
+
}
|
|
90420
|
+
const ownerAlive = lockOwner?.pid ? isProcessAlive(lockOwner.pid) : null;
|
|
90421
|
+
if (lockAge > STALE_LOCK_THRESHOLD_MS && ownerAlive !== true) {
|
|
90422
|
+
logger.warning(`Removing stale sync lock (age: ${Math.round(lockAge / 1000)}s${lockOwner?.pid ? `, pid=${lockOwner.pid}` : ""})`);
|
|
89641
90423
|
await unlink11(lockPath).catch(() => {});
|
|
89642
90424
|
continue;
|
|
89643
90425
|
}
|
|
90426
|
+
if (lockAge > STALE_LOCK_THRESHOLD_MS && ownerAlive === true) {
|
|
90427
|
+
logger.debug(`Sync lock older than threshold but owner pid=${lockOwner?.pid} still alive; waiting`);
|
|
90428
|
+
}
|
|
89644
90429
|
} catch (statError) {
|
|
90430
|
+
if (statError instanceof Error && statError.message.includes("already held by current process")) {
|
|
90431
|
+
throw statError;
|
|
90432
|
+
}
|
|
89645
90433
|
if (statError.code === "ENOENT") {
|
|
89646
90434
|
continue;
|
|
89647
90435
|
}
|
|
89648
90436
|
logger.debug(`Lock stat failed: ${statError}`);
|
|
89649
90437
|
}
|
|
89650
|
-
await new Promise((
|
|
90438
|
+
await new Promise((resolve23) => setTimeout(resolve23, 100));
|
|
89651
90439
|
continue;
|
|
89652
90440
|
}
|
|
89653
90441
|
throw err;
|
|
@@ -89666,18 +90454,20 @@ async function executeSyncMerge(ctx) {
|
|
|
89666
90454
|
const releaseLock = await acquireSyncLock(ctx.options.global);
|
|
89667
90455
|
try {
|
|
89668
90456
|
const trackedFiles = ctx.syncTrackedFiles;
|
|
89669
|
-
const upstreamDir = ctx.options.global ?
|
|
90457
|
+
const upstreamDir = ctx.options.global ? join104(ctx.extractDir, ".claude") : ctx.extractDir;
|
|
89670
90458
|
let deletions = [];
|
|
89671
90459
|
try {
|
|
89672
|
-
const sourceMetadataPath =
|
|
89673
|
-
if (await
|
|
89674
|
-
const content = await
|
|
90460
|
+
const sourceMetadataPath = join104(upstreamDir, "metadata.json");
|
|
90461
|
+
if (await import_fs_extra35.pathExists(sourceMetadataPath)) {
|
|
90462
|
+
const content = await readFile49(sourceMetadataPath, "utf-8");
|
|
89675
90463
|
const sourceMetadata = JSON.parse(content);
|
|
89676
90464
|
deletions = sourceMetadata.deletions || [];
|
|
89677
90465
|
}
|
|
89678
90466
|
} catch (error) {
|
|
89679
90467
|
logger.debug(`Failed to load source metadata for deletion filtering: ${error}`);
|
|
89680
90468
|
}
|
|
90469
|
+
const { categorizeDeletions: categorizeDeletions2, handleDeletions: handleDeletions2 } = await Promise.resolve().then(() => (init_deletion_handler(), exports_deletion_handler));
|
|
90470
|
+
const categorizedDeletions = categorizeDeletions2(deletions);
|
|
89681
90471
|
const filteredTrackedFiles = filterDeletionPaths(trackedFiles, deletions);
|
|
89682
90472
|
if (deletions.length > 0) {
|
|
89683
90473
|
const filtered = trackedFiles.length - filteredTrackedFiles.length;
|
|
@@ -89685,23 +90475,44 @@ async function executeSyncMerge(ctx) {
|
|
|
89685
90475
|
}
|
|
89686
90476
|
logger.info("Analyzing file changes...");
|
|
89687
90477
|
const plan = await SyncEngine.createSyncPlan(filteredTrackedFiles, ctx.claudeDir, upstreamDir);
|
|
90478
|
+
const forceOverwriteNonInteractive = ctx.isNonInteractive && ctx.options.forceOverwrite;
|
|
90479
|
+
const autoUpdateQueue = forceOverwriteNonInteractive ? dedupeTrackedFiles([...plan.autoUpdate, ...plan.needsReview]) : plan.autoUpdate;
|
|
90480
|
+
const reviewQueue = forceOverwriteNonInteractive ? [] : plan.needsReview;
|
|
89688
90481
|
displaySyncPlan(plan);
|
|
89689
|
-
if (
|
|
90482
|
+
if (autoUpdateQueue.length === 0 && reviewQueue.length === 0 && categorizedDeletions.immediate.length === 0 && categorizedDeletions.deferred.length === 0) {
|
|
89690
90483
|
ctx.prompts.note("All files are up to date or user-owned.", "No Changes Needed");
|
|
90484
|
+
}
|
|
90485
|
+
if (reviewQueue.length > 0 && ctx.isNonInteractive) {
|
|
90486
|
+
logger.error(`Cannot complete sync: ${reviewQueue.length} file(s) require interactive review`);
|
|
90487
|
+
ctx.prompts.note(`The following files have local modifications:
|
|
90488
|
+
${reviewQueue.slice(0, 5).map((f3) => ` • ${f3.path}`).join(`
|
|
90489
|
+
`)}${reviewQueue.length > 5 ? `
|
|
90490
|
+
... and ${reviewQueue.length - 5} more` : ""}
|
|
90491
|
+
|
|
90492
|
+
Options:
|
|
90493
|
+
1. Run 'ck init --sync' without --yes for interactive merge
|
|
90494
|
+
2. Use --force-overwrite to accept all upstream changes
|
|
90495
|
+
3. Manually resolve conflicts before syncing`, "Sync Blocked");
|
|
89691
90496
|
return { ...ctx, cancelled: true };
|
|
89692
90497
|
}
|
|
89693
|
-
|
|
89694
|
-
|
|
89695
|
-
|
|
89696
|
-
|
|
89697
|
-
|
|
90498
|
+
if (forceOverwriteNonInteractive && plan.needsReview.length > 0) {
|
|
90499
|
+
logger.info(`--force-overwrite enabled: auto-updating ${plan.needsReview.length} locally modified file(s)`);
|
|
90500
|
+
}
|
|
90501
|
+
const willModifyFiles = autoUpdateQueue.length > 0 || reviewQueue.length > 0 || categorizedDeletions.immediate.length > 0 || categorizedDeletions.deferred.length > 0;
|
|
90502
|
+
if (willModifyFiles) {
|
|
90503
|
+
const backupDir = PathResolver.getBackupDir();
|
|
90504
|
+
await createBackup(ctx.claudeDir, trackedFiles, backupDir);
|
|
90505
|
+
logger.success(`Backup created at ${import_picocolors23.default.dim(backupDir)}`);
|
|
90506
|
+
}
|
|
90507
|
+
if (autoUpdateQueue.length > 0) {
|
|
90508
|
+
logger.info(`Auto-updating ${autoUpdateQueue.length} file(s)...`);
|
|
89698
90509
|
let updateSuccess = 0;
|
|
89699
90510
|
let updateFailed = 0;
|
|
89700
|
-
for (const file of
|
|
90511
|
+
for (const file of autoUpdateQueue) {
|
|
89701
90512
|
try {
|
|
89702
90513
|
const sourcePath = await validateSyncPath(upstreamDir, file.path);
|
|
89703
90514
|
const targetPath = await validateSyncPath(ctx.claudeDir, file.path);
|
|
89704
|
-
const targetDir =
|
|
90515
|
+
const targetDir = dirname23(targetPath);
|
|
89705
90516
|
try {
|
|
89706
90517
|
await mkdir31(targetDir, { recursive: true });
|
|
89707
90518
|
} catch (mkdirError) {
|
|
@@ -89711,7 +90522,7 @@ async function executeSyncMerge(ctx) {
|
|
|
89711
90522
|
ctx.prompts.note("Your disk is full. Free up space and try again.", "Sync Failed");
|
|
89712
90523
|
return { ...ctx, cancelled: true };
|
|
89713
90524
|
}
|
|
89714
|
-
if (errCode === "EROFS" || errCode === "EACCES") {
|
|
90525
|
+
if (errCode === "EROFS" || errCode === "EACCES" || errCode === "EPERM") {
|
|
89715
90526
|
logger.warning(`Cannot create directory ${file.path}: ${errCode}`);
|
|
89716
90527
|
updateFailed++;
|
|
89717
90528
|
continue;
|
|
@@ -89729,7 +90540,7 @@ async function executeSyncMerge(ctx) {
|
|
|
89729
90540
|
ctx.prompts.note("Your disk is full. Free up space and try again.", "Sync Failed");
|
|
89730
90541
|
return { ...ctx, cancelled: true };
|
|
89731
90542
|
}
|
|
89732
|
-
if (errCode === "EACCES" || errCode === "EPERM") {
|
|
90543
|
+
if (errCode === "EACCES" || errCode === "EPERM" || errCode === "EROFS") {
|
|
89733
90544
|
logger.warning(`Permission denied: ${file.path} - check file permissions`);
|
|
89734
90545
|
updateFailed++;
|
|
89735
90546
|
} else if (errMsg.includes("Symlink") || errMsg.includes("Path")) {
|
|
@@ -89744,19 +90555,22 @@ async function executeSyncMerge(ctx) {
|
|
|
89744
90555
|
if (updateSuccess > 0) {
|
|
89745
90556
|
logger.success(`Auto-updated ${updateSuccess} file(s)${updateFailed > 0 ? ` (${updateFailed} failed)` : ""}`);
|
|
89746
90557
|
}
|
|
90558
|
+
if (updateSuccess === 0 && updateFailed > 0) {
|
|
90559
|
+
logger.warning("No files were updated due to write errors");
|
|
90560
|
+
}
|
|
89747
90561
|
}
|
|
89748
|
-
if (
|
|
89749
|
-
logger.info(`${
|
|
90562
|
+
if (reviewQueue.length > 0 && !ctx.isNonInteractive) {
|
|
90563
|
+
logger.info(`${reviewQueue.length} file(s) need interactive review...`);
|
|
89750
90564
|
let totalApplied = 0;
|
|
89751
90565
|
let totalRejected = 0;
|
|
89752
90566
|
let skippedFiles = 0;
|
|
89753
|
-
for (const file of
|
|
90567
|
+
for (const file of reviewQueue) {
|
|
89754
90568
|
let currentPath;
|
|
89755
90569
|
let upstreamPath;
|
|
89756
90570
|
try {
|
|
89757
90571
|
currentPath = await validateSyncPath(ctx.claudeDir, file.path);
|
|
89758
90572
|
upstreamPath = await validateSyncPath(upstreamDir, file.path);
|
|
89759
|
-
} catch
|
|
90573
|
+
} catch {
|
|
89760
90574
|
logger.warning(`Skipping invalid path during review: ${file.path}`);
|
|
89761
90575
|
skippedFiles++;
|
|
89762
90576
|
continue;
|
|
@@ -89783,7 +90597,7 @@ async function executeSyncMerge(ctx) {
|
|
|
89783
90597
|
const tempPath = `${currentPath}.tmp.${Date.now()}`;
|
|
89784
90598
|
try {
|
|
89785
90599
|
await writeFile30(tempPath, result.result, "utf-8");
|
|
89786
|
-
await
|
|
90600
|
+
await rename7(tempPath, currentPath);
|
|
89787
90601
|
} catch (atomicError) {
|
|
89788
90602
|
await unlink11(tempPath).catch(() => {});
|
|
89789
90603
|
throw atomicError;
|
|
@@ -89804,8 +90618,8 @@ async function executeSyncMerge(ctx) {
|
|
|
89804
90618
|
console.log("");
|
|
89805
90619
|
console.log(import_picocolors23.default.bold("Sync Summary:"));
|
|
89806
90620
|
console.log(import_picocolors23.default.dim("─".repeat(40)));
|
|
89807
|
-
if (
|
|
89808
|
-
console.log(import_picocolors23.default.green(` ✓ ${
|
|
90621
|
+
if (autoUpdateQueue.length > 0) {
|
|
90622
|
+
console.log(import_picocolors23.default.green(` ✓ ${autoUpdateQueue.length} file(s) auto-updated`));
|
|
89809
90623
|
}
|
|
89810
90624
|
if (totalApplied > 0) {
|
|
89811
90625
|
console.log(import_picocolors23.default.green(` ✓ ${totalApplied} hunk(s) applied`));
|
|
@@ -89819,23 +90633,96 @@ async function executeSyncMerge(ctx) {
|
|
|
89819
90633
|
if (plan.skipped.length > 0) {
|
|
89820
90634
|
console.log(import_picocolors23.default.dim(` ─ ${plan.skipped.length} user-owned file(s) unchanged`));
|
|
89821
90635
|
}
|
|
89822
|
-
}
|
|
89823
|
-
|
|
89824
|
-
|
|
89825
|
-
|
|
89826
|
-
|
|
89827
|
-
|
|
89828
|
-
|
|
89829
|
-
|
|
89830
|
-
|
|
89831
|
-
|
|
89832
|
-
|
|
89833
|
-
|
|
90636
|
+
}
|
|
90637
|
+
if (categorizedDeletions.immediate.length > 0) {
|
|
90638
|
+
try {
|
|
90639
|
+
const deletionResult = await handleDeletions2({ deletions: categorizedDeletions.immediate }, ctx.claudeDir);
|
|
90640
|
+
if (deletionResult.deletedPaths.length > 0) {
|
|
90641
|
+
logger.info(`Removed ${deletionResult.deletedPaths.length} deprecated file(s)`);
|
|
90642
|
+
}
|
|
90643
|
+
if (deletionResult.preservedPaths.length > 0) {
|
|
90644
|
+
logger.verbose(`Preserved ${deletionResult.preservedPaths.length} user-owned file(s)`);
|
|
90645
|
+
}
|
|
90646
|
+
} catch (error) {
|
|
90647
|
+
logger.debug(`Immediate deletion cleanup failed during sync: ${error}`);
|
|
90648
|
+
}
|
|
90649
|
+
}
|
|
90650
|
+
let pluginSupported = false;
|
|
90651
|
+
let pluginVerified = false;
|
|
90652
|
+
try {
|
|
90653
|
+
const { requireCCPluginSupport: requireCCPluginSupport2 } = await Promise.resolve().then(() => (init_cc_version_checker(), exports_cc_version_checker));
|
|
90654
|
+
await requireCCPluginSupport2();
|
|
90655
|
+
pluginSupported = true;
|
|
90656
|
+
} catch (error) {
|
|
90657
|
+
if (error instanceof CCPluginSupportError) {
|
|
90658
|
+
logger.info(`Plugin install skipped during sync: ${error.message}`);
|
|
90659
|
+
if (error.code === "cc_version_too_old") {
|
|
90660
|
+
logger.info("Upgrade Claude Code, then re-run: ck init --sync (plugin migration requires >= 1.0.33)");
|
|
90661
|
+
}
|
|
90662
|
+
if (error.code === "cc_not_found") {
|
|
90663
|
+
logger.info("Install Claude Code CLI to enable /ck:* plugin skills");
|
|
90664
|
+
}
|
|
90665
|
+
} else {
|
|
90666
|
+
logger.debug(`Plugin version check failed during sync: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
90667
|
+
}
|
|
90668
|
+
}
|
|
90669
|
+
if (pluginSupported && ctx.extractDir) {
|
|
90670
|
+
try {
|
|
90671
|
+
const { handlePluginInstall: handlePluginInstall2 } = await Promise.resolve().then(() => (init_plugin_installer(), exports_plugin_installer));
|
|
90672
|
+
const pluginResult = await handlePluginInstall2(ctx.extractDir);
|
|
90673
|
+
pluginVerified = pluginResult.verified;
|
|
90674
|
+
if (pluginResult.error) {
|
|
90675
|
+
logger.debug(`Plugin install issue: ${pluginResult.error}`);
|
|
90676
|
+
}
|
|
90677
|
+
} catch (error) {
|
|
90678
|
+
logger.debug(`Plugin install skipped: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
90679
|
+
}
|
|
90680
|
+
}
|
|
90681
|
+
if (ctx.claudeDir && ctx.kitType) {
|
|
90682
|
+
try {
|
|
90683
|
+
const { updateKitPluginState: updateKitPluginState2 } = await Promise.resolve().then(() => (init_metadata_migration(), exports_metadata_migration));
|
|
90684
|
+
await updateKitPluginState2(ctx.claudeDir, ctx.kitType, {
|
|
90685
|
+
pluginInstalled: pluginVerified,
|
|
90686
|
+
pluginInstalledAt: new Date().toISOString(),
|
|
90687
|
+
pluginVersion: ctx.selectedVersion || ctx.syncLatestVersion || "unknown"
|
|
90688
|
+
});
|
|
90689
|
+
} catch {}
|
|
90690
|
+
}
|
|
90691
|
+
if (categorizedDeletions.deferred.length > 0) {
|
|
90692
|
+
if (pluginVerified) {
|
|
90693
|
+
try {
|
|
90694
|
+
const { migrateUserSkills: migrateUserSkills2 } = await Promise.resolve().then(() => (init_skill_migration_merger(), exports_skill_migration_merger));
|
|
90695
|
+
const migration = await migrateUserSkills2(ctx.claudeDir, pluginVerified);
|
|
90696
|
+
if (!migration.canDelete) {
|
|
90697
|
+
logger.warning("Skill migration metadata unavailable during sync — preserving existing skills");
|
|
90698
|
+
} else {
|
|
90699
|
+
const preservedDirs = new Set(migration.preserved.map(normalizeSkillDir2));
|
|
90700
|
+
const safeDeletions = categorizedDeletions.deferred.filter((path14) => {
|
|
90701
|
+
const skillDir = extractSkillDirFromDeletionPath2(path14);
|
|
90702
|
+
return !skillDir || !preservedDirs.has(normalizeSkillDir2(skillDir));
|
|
90703
|
+
});
|
|
90704
|
+
if (safeDeletions.length > 0) {
|
|
90705
|
+
const deferredResult = await handleDeletions2({ deletions: safeDeletions }, ctx.claudeDir);
|
|
90706
|
+
if (deferredResult.deletedPaths.length > 0) {
|
|
90707
|
+
logger.info(`Removed ${deferredResult.deletedPaths.length} old skill file(s) during sync`);
|
|
90708
|
+
}
|
|
90709
|
+
}
|
|
90710
|
+
}
|
|
90711
|
+
} catch (error) {
|
|
90712
|
+
logger.debug(`Deferred skill cleanup failed during sync: ${error}`);
|
|
90713
|
+
}
|
|
90714
|
+
} else {
|
|
90715
|
+
logger.info("Plugin not verified during sync — keeping existing skills as fallback");
|
|
90716
|
+
}
|
|
89834
90717
|
}
|
|
89835
90718
|
ctx.prompts.outro("Config sync completed successfully");
|
|
89836
90719
|
return { ...ctx, cancelled: true };
|
|
89837
90720
|
} finally {
|
|
89838
|
-
|
|
90721
|
+
try {
|
|
90722
|
+
await releaseLock();
|
|
90723
|
+
} catch (error) {
|
|
90724
|
+
logger.debug(`Failed to release sync lock: ${error}`);
|
|
90725
|
+
}
|
|
89839
90726
|
}
|
|
89840
90727
|
}
|
|
89841
90728
|
function displaySyncPlan(plan) {
|
|
@@ -89870,9 +90757,9 @@ async function createBackup(claudeDir2, files, backupDir) {
|
|
|
89870
90757
|
for (const file of files) {
|
|
89871
90758
|
try {
|
|
89872
90759
|
const sourcePath = await validateSyncPath(claudeDir2, file.path);
|
|
89873
|
-
if (await
|
|
90760
|
+
if (await import_fs_extra35.pathExists(sourcePath)) {
|
|
89874
90761
|
const targetPath = await validateSyncPath(backupDir, file.path);
|
|
89875
|
-
const targetDir =
|
|
90762
|
+
const targetDir = dirname23(targetPath);
|
|
89876
90763
|
await mkdir31(targetDir, { recursive: true });
|
|
89877
90764
|
await copyFile8(sourcePath, targetPath);
|
|
89878
90765
|
}
|
|
@@ -89885,9 +90772,27 @@ async function createBackup(claudeDir2, files, backupDir) {
|
|
|
89885
90772
|
}
|
|
89886
90773
|
}
|
|
89887
90774
|
}
|
|
90775
|
+
function dedupeTrackedFiles(files) {
|
|
90776
|
+
const deduped = new Map;
|
|
90777
|
+
for (const file of files) {
|
|
90778
|
+
deduped.set(file.path, file);
|
|
90779
|
+
}
|
|
90780
|
+
return [...deduped.values()];
|
|
90781
|
+
}
|
|
90782
|
+
function normalizeSkillDir2(path14) {
|
|
90783
|
+
return path14.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
90784
|
+
}
|
|
90785
|
+
function extractSkillDirFromDeletionPath2(path14) {
|
|
90786
|
+
const normalized = normalizeSkillDir2(path14);
|
|
90787
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
90788
|
+
if (parts.length < 2 || parts[0] !== "skills") {
|
|
90789
|
+
return null;
|
|
90790
|
+
}
|
|
90791
|
+
return `skills/${parts[1]}`;
|
|
90792
|
+
}
|
|
89888
90793
|
// src/commands/init/phases/transform-handler.ts
|
|
89889
90794
|
init_config_manager();
|
|
89890
|
-
import { join as
|
|
90795
|
+
import { join as join108 } from "node:path";
|
|
89891
90796
|
|
|
89892
90797
|
// src/services/transformers/folder-path-transformer.ts
|
|
89893
90798
|
init_logger();
|
|
@@ -89896,40 +90801,40 @@ init_types3();
|
|
|
89896
90801
|
// src/services/transformers/folder-transform/folder-renamer.ts
|
|
89897
90802
|
init_logger();
|
|
89898
90803
|
init_types3();
|
|
89899
|
-
var
|
|
89900
|
-
import { rename as
|
|
89901
|
-
import { join as
|
|
90804
|
+
var import_fs_extra36 = __toESM(require_lib3(), 1);
|
|
90805
|
+
import { rename as rename8, rm as rm13 } from "node:fs/promises";
|
|
90806
|
+
import { join as join105, relative as relative18 } from "node:path";
|
|
89902
90807
|
async function collectDirsToRename(extractDir, folders) {
|
|
89903
90808
|
const dirsToRename = [];
|
|
89904
90809
|
if (folders.docs !== DEFAULT_FOLDERS.docs) {
|
|
89905
|
-
const docsPath =
|
|
89906
|
-
if (await
|
|
90810
|
+
const docsPath = join105(extractDir, DEFAULT_FOLDERS.docs);
|
|
90811
|
+
if (await import_fs_extra36.pathExists(docsPath)) {
|
|
89907
90812
|
dirsToRename.push({
|
|
89908
90813
|
from: docsPath,
|
|
89909
|
-
to:
|
|
90814
|
+
to: join105(extractDir, folders.docs)
|
|
89910
90815
|
});
|
|
89911
90816
|
}
|
|
89912
|
-
const claudeDocsPath =
|
|
89913
|
-
if (await
|
|
90817
|
+
const claudeDocsPath = join105(extractDir, ".claude", DEFAULT_FOLDERS.docs);
|
|
90818
|
+
if (await import_fs_extra36.pathExists(claudeDocsPath)) {
|
|
89914
90819
|
dirsToRename.push({
|
|
89915
90820
|
from: claudeDocsPath,
|
|
89916
|
-
to:
|
|
90821
|
+
to: join105(extractDir, ".claude", folders.docs)
|
|
89917
90822
|
});
|
|
89918
90823
|
}
|
|
89919
90824
|
}
|
|
89920
90825
|
if (folders.plans !== DEFAULT_FOLDERS.plans) {
|
|
89921
|
-
const plansPath =
|
|
89922
|
-
if (await
|
|
90826
|
+
const plansPath = join105(extractDir, DEFAULT_FOLDERS.plans);
|
|
90827
|
+
if (await import_fs_extra36.pathExists(plansPath)) {
|
|
89923
90828
|
dirsToRename.push({
|
|
89924
90829
|
from: plansPath,
|
|
89925
|
-
to:
|
|
90830
|
+
to: join105(extractDir, folders.plans)
|
|
89926
90831
|
});
|
|
89927
90832
|
}
|
|
89928
|
-
const claudePlansPath =
|
|
89929
|
-
if (await
|
|
90833
|
+
const claudePlansPath = join105(extractDir, ".claude", DEFAULT_FOLDERS.plans);
|
|
90834
|
+
if (await import_fs_extra36.pathExists(claudePlansPath)) {
|
|
89930
90835
|
dirsToRename.push({
|
|
89931
90836
|
from: claudePlansPath,
|
|
89932
|
-
to:
|
|
90837
|
+
to: join105(extractDir, ".claude", folders.plans)
|
|
89933
90838
|
});
|
|
89934
90839
|
}
|
|
89935
90840
|
}
|
|
@@ -89937,11 +90842,11 @@ async function collectDirsToRename(extractDir, folders) {
|
|
|
89937
90842
|
}
|
|
89938
90843
|
async function moveAcrossDevices(src, dest) {
|
|
89939
90844
|
try {
|
|
89940
|
-
await
|
|
90845
|
+
await rename8(src, dest);
|
|
89941
90846
|
} catch (e2) {
|
|
89942
90847
|
if (e2.code === "EXDEV") {
|
|
89943
90848
|
logger.debug(`Cross-device move detected, using copy+delete: ${src} -> ${dest}`);
|
|
89944
|
-
await
|
|
90849
|
+
await import_fs_extra36.copy(src, dest, { overwrite: true });
|
|
89945
90850
|
await rm13(src, { recursive: true, force: true });
|
|
89946
90851
|
} else {
|
|
89947
90852
|
throw e2;
|
|
@@ -89969,8 +90874,8 @@ async function renameFolders(dirsToRename, extractDir, options2) {
|
|
|
89969
90874
|
// src/services/transformers/folder-transform/path-replacer.ts
|
|
89970
90875
|
init_logger();
|
|
89971
90876
|
init_types3();
|
|
89972
|
-
import { readFile as
|
|
89973
|
-
import { join as
|
|
90877
|
+
import { readFile as readFile50, readdir as readdir32, writeFile as writeFile31 } from "node:fs/promises";
|
|
90878
|
+
import { join as join106, relative as relative19 } from "node:path";
|
|
89974
90879
|
var TRANSFORMABLE_FILE_PATTERNS = [
|
|
89975
90880
|
".md",
|
|
89976
90881
|
".txt",
|
|
@@ -90023,7 +90928,7 @@ async function transformFileContents(dir, compiledReplacements, options2) {
|
|
|
90023
90928
|
let replacementsCount = 0;
|
|
90024
90929
|
const entries = await readdir32(dir, { withFileTypes: true });
|
|
90025
90930
|
for (const entry of entries) {
|
|
90026
|
-
const fullPath =
|
|
90931
|
+
const fullPath = join106(dir, entry.name);
|
|
90027
90932
|
if (entry.isDirectory()) {
|
|
90028
90933
|
if (entry.name === "node_modules" || entry.name === ".git") {
|
|
90029
90934
|
continue;
|
|
@@ -90036,7 +90941,7 @@ async function transformFileContents(dir, compiledReplacements, options2) {
|
|
|
90036
90941
|
if (!shouldTransform)
|
|
90037
90942
|
continue;
|
|
90038
90943
|
try {
|
|
90039
|
-
const content = await
|
|
90944
|
+
const content = await readFile50(fullPath, "utf-8");
|
|
90040
90945
|
let newContent = content;
|
|
90041
90946
|
let changeCount = 0;
|
|
90042
90947
|
for (const { regex: regex2, replacement } of compiledReplacements) {
|
|
@@ -90158,9 +91063,9 @@ async function transformFolderPaths(extractDir, folders, options2 = {}) {
|
|
|
90158
91063
|
|
|
90159
91064
|
// src/services/transformers/global-path-transformer.ts
|
|
90160
91065
|
init_logger();
|
|
90161
|
-
import { readFile as
|
|
91066
|
+
import { readFile as readFile51, readdir as readdir33, writeFile as writeFile32 } from "node:fs/promises";
|
|
90162
91067
|
import { platform as platform13 } from "node:os";
|
|
90163
|
-
import { extname as extname6, join as
|
|
91068
|
+
import { extname as extname6, join as join107 } from "node:path";
|
|
90164
91069
|
var IS_WINDOWS3 = platform13() === "win32";
|
|
90165
91070
|
var HOME_PREFIX = IS_WINDOWS3 ? "%USERPROFILE%" : "$HOME";
|
|
90166
91071
|
function getHomeDirPrefix() {
|
|
@@ -90270,7 +91175,7 @@ async function transformPathsForGlobalInstall(directory, options2 = {}) {
|
|
|
90270
91175
|
async function processDirectory2(dir) {
|
|
90271
91176
|
const entries = await readdir33(dir, { withFileTypes: true });
|
|
90272
91177
|
for (const entry of entries) {
|
|
90273
|
-
const fullPath =
|
|
91178
|
+
const fullPath = join107(dir, entry.name);
|
|
90274
91179
|
if (entry.isDirectory()) {
|
|
90275
91180
|
if (entry.name === "node_modules" || entry.name.startsWith(".") && entry.name !== ".claude") {
|
|
90276
91181
|
continue;
|
|
@@ -90278,7 +91183,7 @@ async function transformPathsForGlobalInstall(directory, options2 = {}) {
|
|
|
90278
91183
|
await processDirectory2(fullPath);
|
|
90279
91184
|
} else if (entry.isFile() && shouldTransformFile3(entry.name)) {
|
|
90280
91185
|
try {
|
|
90281
|
-
const content = await
|
|
91186
|
+
const content = await readFile51(fullPath, "utf-8");
|
|
90282
91187
|
const { transformed, changes } = transformContent(content);
|
|
90283
91188
|
if (changes > 0) {
|
|
90284
91189
|
await writeFile32(fullPath, transformed, "utf-8");
|
|
@@ -90346,7 +91251,7 @@ async function handleTransforms(ctx) {
|
|
|
90346
91251
|
logger.debug(ctx.options.global ? "Saved folder configuration to ~/.claude/.ck.json" : "Saved folder configuration to .claude/.ck.json");
|
|
90347
91252
|
}
|
|
90348
91253
|
}
|
|
90349
|
-
const claudeDir2 = ctx.options.global ? ctx.resolvedDir :
|
|
91254
|
+
const claudeDir2 = ctx.options.global ? ctx.resolvedDir : join108(ctx.resolvedDir, ".claude");
|
|
90350
91255
|
return {
|
|
90351
91256
|
...ctx,
|
|
90352
91257
|
foldersConfig,
|
|
@@ -90540,8 +91445,8 @@ init_checksum_utils();
|
|
|
90540
91445
|
init_config_discovery();
|
|
90541
91446
|
var import_picocolors25 = __toESM(require_picocolors(), 1);
|
|
90542
91447
|
import { existsSync as existsSync51 } from "node:fs";
|
|
90543
|
-
import { readFile as
|
|
90544
|
-
import { resolve as
|
|
91448
|
+
import { readFile as readFile52, rm as rm14, unlink as unlink12 } from "node:fs/promises";
|
|
91449
|
+
import { resolve as resolve23 } from "node:path";
|
|
90545
91450
|
|
|
90546
91451
|
// src/commands/portable/conflict-resolver.ts
|
|
90547
91452
|
init_dist2();
|
|
@@ -90888,7 +91793,7 @@ function shouldExecuteAction2(action) {
|
|
|
90888
91793
|
}
|
|
90889
91794
|
async function executeDeleteAction(action, options2) {
|
|
90890
91795
|
const preservePaths = options2?.preservePaths ?? new Set;
|
|
90891
|
-
const shouldPreserveTarget = action.targetPath.length > 0 && preservePaths.has(
|
|
91796
|
+
const shouldPreserveTarget = action.targetPath.length > 0 && preservePaths.has(resolve23(action.targetPath));
|
|
90892
91797
|
try {
|
|
90893
91798
|
if (!shouldPreserveTarget && action.targetPath && existsSync51(action.targetPath)) {
|
|
90894
91799
|
await rm14(action.targetPath, { recursive: true, force: true });
|
|
@@ -91113,7 +92018,7 @@ async function migrateCommand(options2) {
|
|
|
91113
92018
|
for (const action of conflictActions) {
|
|
91114
92019
|
if (!action.diff && action.targetPath && existsSync51(action.targetPath)) {
|
|
91115
92020
|
try {
|
|
91116
|
-
const targetContent = await
|
|
92021
|
+
const targetContent = await readFile52(action.targetPath, "utf-8");
|
|
91117
92022
|
const sourceItem = agents2.find((a3) => a3.name === action.item) || commands.find((c2) => c2.name === action.item) || (configItem?.name === action.item ? configItem : null) || ruleItems.find((r2) => r2.name === action.item);
|
|
91118
92023
|
if (sourceItem) {
|
|
91119
92024
|
const providerConfig = providers[action.provider];
|
|
@@ -91204,7 +92109,7 @@ async function migrateCommand(options2) {
|
|
|
91204
92109
|
allResults.push(...await installSkillDirectories(skills, skillProviders, installOpts));
|
|
91205
92110
|
}
|
|
91206
92111
|
}
|
|
91207
|
-
const writtenPaths = new Set(allResults.filter((result) => result.success && !result.skipped && result.path.length > 0).map((result) =>
|
|
92112
|
+
const writtenPaths = new Set(allResults.filter((result) => result.success && !result.skipped && result.path.length > 0).map((result) => resolve23(result.path)));
|
|
91208
92113
|
for (const deleteAction of plannedDeleteActions) {
|
|
91209
92114
|
allResults.push(await executeDeleteAction(deleteAction, {
|
|
91210
92115
|
preservePaths: writtenPaths
|
|
@@ -91320,7 +92225,7 @@ async function computeTargetStates(selectedProviders, global3) {
|
|
|
91320
92225
|
exists: true
|
|
91321
92226
|
};
|
|
91322
92227
|
try {
|
|
91323
|
-
const content = await
|
|
92228
|
+
const content = await readFile52(entry.path, "utf-8");
|
|
91324
92229
|
state.currentChecksum = computeContentChecksum(content);
|
|
91325
92230
|
} catch (error) {
|
|
91326
92231
|
logger.debug(`[migrate] Failed to read target for checksum: ${entry.path} (${String(error)})`);
|
|
@@ -91379,11 +92284,11 @@ var import_picocolors26 = __toESM(require_picocolors(), 1);
|
|
|
91379
92284
|
|
|
91380
92285
|
// src/commands/new/phases/directory-setup.ts
|
|
91381
92286
|
init_config_manager();
|
|
91382
|
-
import { resolve as
|
|
92287
|
+
import { resolve as resolve24 } from "node:path";
|
|
91383
92288
|
init_logger();
|
|
91384
92289
|
init_path_resolver();
|
|
91385
92290
|
init_types3();
|
|
91386
|
-
var
|
|
92291
|
+
var import_fs_extra37 = __toESM(require_lib3(), 1);
|
|
91387
92292
|
async function directorySetup(validOptions, prompts) {
|
|
91388
92293
|
const isNonInteractive2 = !process.stdin.isTTY || process.env.CI === "true" || process.env.NON_INTERACTIVE === "true";
|
|
91389
92294
|
const config = await ConfigManager.get();
|
|
@@ -91464,7 +92369,7 @@ async function directorySetup(validOptions, prompts) {
|
|
|
91464
92369
|
targetDir = await prompts.getDirectory(targetDir);
|
|
91465
92370
|
}
|
|
91466
92371
|
}
|
|
91467
|
-
const resolvedDir =
|
|
92372
|
+
const resolvedDir = resolve24(targetDir);
|
|
91468
92373
|
logger.info(`Target directory: ${resolvedDir}`);
|
|
91469
92374
|
if (PathResolver.isLocalSameAsGlobal(resolvedDir)) {
|
|
91470
92375
|
logger.warning("You're creating a project at HOME directory.");
|
|
@@ -91482,8 +92387,8 @@ async function directorySetup(validOptions, prompts) {
|
|
|
91482
92387
|
return null;
|
|
91483
92388
|
}
|
|
91484
92389
|
}
|
|
91485
|
-
if (await
|
|
91486
|
-
const files = await
|
|
92390
|
+
if (await import_fs_extra37.pathExists(resolvedDir)) {
|
|
92391
|
+
const files = await import_fs_extra37.readdir(resolvedDir);
|
|
91487
92392
|
const isEmpty = files.length === 0;
|
|
91488
92393
|
if (!isEmpty) {
|
|
91489
92394
|
if (isNonInteractive2) {
|
|
@@ -91521,7 +92426,7 @@ async function handleDirectorySetup(ctx) {
|
|
|
91521
92426
|
// src/commands/new/phases/project-creation.ts
|
|
91522
92427
|
init_config_manager();
|
|
91523
92428
|
init_github_client();
|
|
91524
|
-
import { join as
|
|
92429
|
+
import { join as join109 } from "node:path";
|
|
91525
92430
|
init_logger();
|
|
91526
92431
|
init_output_manager();
|
|
91527
92432
|
init_types3();
|
|
@@ -91647,7 +92552,7 @@ async function projectCreation(kit, resolvedDir, validOptions, isNonInteractive2
|
|
|
91647
92552
|
output.section("Installing");
|
|
91648
92553
|
logger.verbose("Installation target", { directory: resolvedDir });
|
|
91649
92554
|
const merger = new FileMerger;
|
|
91650
|
-
const claudeDir2 =
|
|
92555
|
+
const claudeDir2 = join109(resolvedDir, ".claude");
|
|
91651
92556
|
merger.setMultiKitContext(claudeDir2, kit);
|
|
91652
92557
|
if (validOptions.exclude && validOptions.exclude.length > 0) {
|
|
91653
92558
|
merger.addIgnorePatterns(validOptions.exclude);
|
|
@@ -91694,7 +92599,7 @@ async function handleProjectCreation(ctx) {
|
|
|
91694
92599
|
}
|
|
91695
92600
|
// src/commands/new/phases/post-setup.ts
|
|
91696
92601
|
init_projects_registry();
|
|
91697
|
-
import { join as
|
|
92602
|
+
import { join as join110 } from "node:path";
|
|
91698
92603
|
init_package_installer();
|
|
91699
92604
|
init_logger();
|
|
91700
92605
|
init_path_resolver();
|
|
@@ -91726,9 +92631,9 @@ async function postSetup(resolvedDir, validOptions, isNonInteractive2, prompts)
|
|
|
91726
92631
|
withSudo: validOptions.withSudo
|
|
91727
92632
|
});
|
|
91728
92633
|
}
|
|
91729
|
-
const claudeDir2 =
|
|
92634
|
+
const claudeDir2 = join110(resolvedDir, ".claude");
|
|
91730
92635
|
await promptSetupWizardIfNeeded({
|
|
91731
|
-
envPath:
|
|
92636
|
+
envPath: join110(claudeDir2, ".env"),
|
|
91732
92637
|
claudeDir: claudeDir2,
|
|
91733
92638
|
isGlobal: false,
|
|
91734
92639
|
isNonInteractive: isNonInteractive2,
|
|
@@ -91801,11 +92706,11 @@ init_logger();
|
|
|
91801
92706
|
init_safe_prompts();
|
|
91802
92707
|
var import_picocolors27 = __toESM(require_picocolors(), 1);
|
|
91803
92708
|
import { existsSync as existsSync52 } from "node:fs";
|
|
91804
|
-
import { resolve as
|
|
92709
|
+
import { resolve as resolve25 } from "node:path";
|
|
91805
92710
|
async function handleAdd(projectPath, options2) {
|
|
91806
92711
|
logger.debug(`Adding project: ${projectPath}, options: ${JSON.stringify(options2)}`);
|
|
91807
92712
|
intro("Add Project");
|
|
91808
|
-
const absolutePath =
|
|
92713
|
+
const absolutePath = resolve25(projectPath);
|
|
91809
92714
|
if (!existsSync52(absolutePath)) {
|
|
91810
92715
|
log.error(`Path does not exist: ${absolutePath}`);
|
|
91811
92716
|
process.exitCode = 1;
|
|
@@ -92695,7 +93600,7 @@ var import_picocolors32 = __toESM(require_picocolors(), 1);
|
|
|
92695
93600
|
// src/commands/uninstall/installation-detector.ts
|
|
92696
93601
|
init_claudekit_scanner();
|
|
92697
93602
|
init_path_resolver();
|
|
92698
|
-
var
|
|
93603
|
+
var import_fs_extra38 = __toESM(require_lib3(), 1);
|
|
92699
93604
|
function hasClaudeKitComponents(components) {
|
|
92700
93605
|
return components.agents > 0 || components.commands > 0 || components.rules > 0 || components.skills > 0;
|
|
92701
93606
|
}
|
|
@@ -92710,7 +93615,7 @@ async function detectInstallations() {
|
|
|
92710
93615
|
installations.push({
|
|
92711
93616
|
type: "local",
|
|
92712
93617
|
path: setup.project.path,
|
|
92713
|
-
exists: await
|
|
93618
|
+
exists: await import_fs_extra38.pathExists(setup.project.path),
|
|
92714
93619
|
hasMetadata,
|
|
92715
93620
|
components: setup.project.components
|
|
92716
93621
|
});
|
|
@@ -92723,7 +93628,7 @@ async function detectInstallations() {
|
|
|
92723
93628
|
installations.push({
|
|
92724
93629
|
type: "global",
|
|
92725
93630
|
path: setup.global.path,
|
|
92726
|
-
exists: await
|
|
93631
|
+
exists: await import_fs_extra38.pathExists(setup.global.path),
|
|
92727
93632
|
hasMetadata,
|
|
92728
93633
|
components: setup.global.components
|
|
92729
93634
|
});
|
|
@@ -92734,16 +93639,16 @@ async function detectInstallations() {
|
|
|
92734
93639
|
|
|
92735
93640
|
// src/commands/uninstall/removal-handler.ts
|
|
92736
93641
|
import { readdirSync as readdirSync6, rmSync as rmSync5 } from "node:fs";
|
|
92737
|
-
import { join as
|
|
93642
|
+
import { join as join112, resolve as resolve26, sep as sep5 } from "node:path";
|
|
92738
93643
|
init_logger();
|
|
92739
93644
|
init_safe_prompts();
|
|
92740
93645
|
init_safe_spinner();
|
|
92741
|
-
var
|
|
93646
|
+
var import_fs_extra39 = __toESM(require_lib3(), 1);
|
|
92742
93647
|
|
|
92743
93648
|
// src/commands/uninstall/analysis-handler.ts
|
|
92744
93649
|
init_metadata_migration();
|
|
92745
93650
|
import { readdirSync as readdirSync5, rmSync as rmSync4 } from "node:fs";
|
|
92746
|
-
import { dirname as
|
|
93651
|
+
import { dirname as dirname24, join as join111 } from "node:path";
|
|
92747
93652
|
init_logger();
|
|
92748
93653
|
init_safe_prompts();
|
|
92749
93654
|
var import_picocolors31 = __toESM(require_picocolors(), 1);
|
|
@@ -92761,7 +93666,7 @@ function classifyFileByOwnership(ownership, forceOverwrite, deleteReason) {
|
|
|
92761
93666
|
}
|
|
92762
93667
|
async function cleanupEmptyDirectories3(filePath, installationRoot) {
|
|
92763
93668
|
let cleaned = 0;
|
|
92764
|
-
let currentDir =
|
|
93669
|
+
let currentDir = dirname24(filePath);
|
|
92765
93670
|
while (currentDir !== installationRoot && currentDir.startsWith(installationRoot)) {
|
|
92766
93671
|
try {
|
|
92767
93672
|
const entries = readdirSync5(currentDir);
|
|
@@ -92769,7 +93674,7 @@ async function cleanupEmptyDirectories3(filePath, installationRoot) {
|
|
|
92769
93674
|
rmSync4(currentDir, { recursive: true });
|
|
92770
93675
|
cleaned++;
|
|
92771
93676
|
logger.debug(`Removed empty directory: ${currentDir}`);
|
|
92772
|
-
currentDir =
|
|
93677
|
+
currentDir = dirname24(currentDir);
|
|
92773
93678
|
} else {
|
|
92774
93679
|
break;
|
|
92775
93680
|
}
|
|
@@ -92791,7 +93696,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
|
|
|
92791
93696
|
if (uninstallManifest.isMultiKit && kit && metadata?.kits?.[kit]) {
|
|
92792
93697
|
const kitFiles = metadata.kits[kit].files || [];
|
|
92793
93698
|
for (const trackedFile of kitFiles) {
|
|
92794
|
-
const filePath =
|
|
93699
|
+
const filePath = join111(installation.path, trackedFile.path);
|
|
92795
93700
|
if (uninstallManifest.filesToPreserve.includes(trackedFile.path)) {
|
|
92796
93701
|
result.toPreserve.push({ path: trackedFile.path, reason: "shared with other kit" });
|
|
92797
93702
|
continue;
|
|
@@ -92821,7 +93726,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
|
|
|
92821
93726
|
return result;
|
|
92822
93727
|
}
|
|
92823
93728
|
for (const trackedFile of allTrackedFiles) {
|
|
92824
|
-
const filePath =
|
|
93729
|
+
const filePath = join111(installation.path, trackedFile.path);
|
|
92825
93730
|
const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
|
|
92826
93731
|
if (!ownershipResult.exists)
|
|
92827
93732
|
continue;
|
|
@@ -92866,7 +93771,7 @@ function displayDryRunPreview(analysis, installationType) {
|
|
|
92866
93771
|
// src/commands/uninstall/removal-handler.ts
|
|
92867
93772
|
async function isDirectory(filePath) {
|
|
92868
93773
|
try {
|
|
92869
|
-
const stats = await
|
|
93774
|
+
const stats = await import_fs_extra39.lstat(filePath);
|
|
92870
93775
|
return stats.isDirectory();
|
|
92871
93776
|
} catch {
|
|
92872
93777
|
logger.debug(`Failed to check if path is directory: ${filePath}`);
|
|
@@ -92875,16 +93780,16 @@ async function isDirectory(filePath) {
|
|
|
92875
93780
|
}
|
|
92876
93781
|
async function isPathSafeToRemove(filePath, baseDir) {
|
|
92877
93782
|
try {
|
|
92878
|
-
const resolvedPath =
|
|
92879
|
-
const resolvedBase =
|
|
93783
|
+
const resolvedPath = resolve26(filePath);
|
|
93784
|
+
const resolvedBase = resolve26(baseDir);
|
|
92880
93785
|
if (!resolvedPath.startsWith(resolvedBase + sep5) && resolvedPath !== resolvedBase) {
|
|
92881
93786
|
logger.debug(`Path outside installation directory: ${filePath}`);
|
|
92882
93787
|
return false;
|
|
92883
93788
|
}
|
|
92884
|
-
const stats = await
|
|
93789
|
+
const stats = await import_fs_extra39.lstat(filePath);
|
|
92885
93790
|
if (stats.isSymbolicLink()) {
|
|
92886
|
-
const realPath = await
|
|
92887
|
-
const resolvedReal =
|
|
93791
|
+
const realPath = await import_fs_extra39.realpath(filePath);
|
|
93792
|
+
const resolvedReal = resolve26(realPath);
|
|
92888
93793
|
if (!resolvedReal.startsWith(resolvedBase + sep5) && resolvedReal !== resolvedBase) {
|
|
92889
93794
|
logger.debug(`Symlink points outside installation directory: ${filePath} -> ${realPath}`);
|
|
92890
93795
|
return false;
|
|
@@ -92918,15 +93823,15 @@ async function removeInstallations(installations, options2) {
|
|
|
92918
93823
|
let removedCount = 0;
|
|
92919
93824
|
let cleanedDirs = 0;
|
|
92920
93825
|
for (const item of analysis.toDelete) {
|
|
92921
|
-
const filePath =
|
|
92922
|
-
if (!await
|
|
93826
|
+
const filePath = join112(installation.path, item.path);
|
|
93827
|
+
if (!await import_fs_extra39.pathExists(filePath))
|
|
92923
93828
|
continue;
|
|
92924
93829
|
if (!await isPathSafeToRemove(filePath, installation.path)) {
|
|
92925
93830
|
logger.debug(`Skipping unsafe path: ${item.path}`);
|
|
92926
93831
|
continue;
|
|
92927
93832
|
}
|
|
92928
93833
|
const isDir = await isDirectory(filePath);
|
|
92929
|
-
await
|
|
93834
|
+
await import_fs_extra39.remove(filePath);
|
|
92930
93835
|
removedCount++;
|
|
92931
93836
|
logger.debug(`Removed ${isDir ? "directory" : "file"}: ${item.path}`);
|
|
92932
93837
|
if (!isDir) {
|
|
@@ -93110,6 +94015,12 @@ ${import_picocolors32.default.yellow("User modifications will be permanently del
|
|
|
93110
94015
|
forceOverwrite: validOptions.forceOverwrite,
|
|
93111
94016
|
kit: validOptions.kit
|
|
93112
94017
|
});
|
|
94018
|
+
if (!validOptions.kit || validOptions.kit === "engineer") {
|
|
94019
|
+
try {
|
|
94020
|
+
const { handlePluginUninstall: handlePluginUninstall2 } = await Promise.resolve().then(() => (init_plugin_installer(), exports_plugin_installer));
|
|
94021
|
+
await handlePluginUninstall2();
|
|
94022
|
+
} catch {}
|
|
94023
|
+
}
|
|
93113
94024
|
const kitMsg = validOptions.kit ? ` (${validOptions.kit} kit)` : "";
|
|
93114
94025
|
prompts.outro(`ClaudeKit${kitMsg} uninstalled successfully!`);
|
|
93115
94026
|
} catch (error) {
|
|
@@ -93311,7 +94222,7 @@ init_logger();
|
|
|
93311
94222
|
init_path_resolver();
|
|
93312
94223
|
init_types3();
|
|
93313
94224
|
import { existsSync as existsSync53, readFileSync as readFileSync10 } from "node:fs";
|
|
93314
|
-
import { join as
|
|
94225
|
+
import { join as join113 } from "node:path";
|
|
93315
94226
|
var packageVersion = package_default.version;
|
|
93316
94227
|
function formatInstalledKits(metadata) {
|
|
93317
94228
|
if (!metadata.kits || Object.keys(metadata.kits).length === 0) {
|
|
@@ -93343,9 +94254,9 @@ async function displayVersion() {
|
|
|
93343
94254
|
let localKitVersion = null;
|
|
93344
94255
|
let isGlobalOnlyKit = false;
|
|
93345
94256
|
const globalKitDir = PathResolver.getGlobalKitDir();
|
|
93346
|
-
const globalMetadataPath =
|
|
94257
|
+
const globalMetadataPath = join113(globalKitDir, "metadata.json");
|
|
93347
94258
|
const prefix = PathResolver.getPathPrefix(false);
|
|
93348
|
-
const localMetadataPath = prefix ?
|
|
94259
|
+
const localMetadataPath = prefix ? join113(process.cwd(), prefix, "metadata.json") : join113(process.cwd(), "metadata.json");
|
|
93349
94260
|
const isLocalSameAsGlobal = localMetadataPath === globalMetadataPath;
|
|
93350
94261
|
if (!isLocalSameAsGlobal && existsSync53(localMetadataPath)) {
|
|
93351
94262
|
try {
|
|
@@ -93738,7 +94649,7 @@ var output2 = new OutputManager2;
|
|
|
93738
94649
|
|
|
93739
94650
|
// src/shared/temp-cleanup.ts
|
|
93740
94651
|
init_logger();
|
|
93741
|
-
var
|
|
94652
|
+
var import_fs_extra40 = __toESM(require_lib3(), 1);
|
|
93742
94653
|
import { rmSync as rmSync6 } from "node:fs";
|
|
93743
94654
|
var tempDirs2 = new Set;
|
|
93744
94655
|
async function cleanup() {
|
|
@@ -93747,7 +94658,7 @@ async function cleanup() {
|
|
|
93747
94658
|
logger.debug(`Cleaning up ${tempDirs2.size} temporary director(ies)...`);
|
|
93748
94659
|
for (const dir of tempDirs2) {
|
|
93749
94660
|
try {
|
|
93750
|
-
await
|
|
94661
|
+
await import_fs_extra40.remove(dir);
|
|
93751
94662
|
logger.debug(`Cleaned up temp directory: ${dir}`);
|
|
93752
94663
|
} catch (error) {
|
|
93753
94664
|
logger.debug(`Failed to clean temp directory ${dir}: ${error}`);
|