claudekit-cli 3.35.1-dev.1 → 3.36.0-dev.2
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 +1773 -734
- 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.2",
|
|
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,596 @@ 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
|
+
|
|
65601
|
+
// src/services/standalone-skill-cleanup.ts
|
|
65602
|
+
var exports_standalone_skill_cleanup = {};
|
|
65603
|
+
__export(exports_standalone_skill_cleanup, {
|
|
65604
|
+
cleanupOverlappingStandaloneSkills: () => cleanupOverlappingStandaloneSkills
|
|
65605
|
+
});
|
|
65606
|
+
import { readFile as readFile48, readdir as readdir32 } from "node:fs/promises";
|
|
65607
|
+
import { join as join101 } from "node:path";
|
|
65608
|
+
async function listSkillDirs(dir) {
|
|
65609
|
+
if (!await import_fs_extra32.pathExists(dir))
|
|
65610
|
+
return new Set;
|
|
65611
|
+
try {
|
|
65612
|
+
const entries = await readdir32(dir, { withFileTypes: true });
|
|
65613
|
+
const dirs = entries.filter((e2) => e2.isDirectory());
|
|
65614
|
+
const results = await Promise.all(dirs.map(async (e2) => {
|
|
65615
|
+
const exists = await import_fs_extra32.pathExists(join101(dir, e2.name, "SKILL.md"));
|
|
65616
|
+
return exists ? e2.name : null;
|
|
65617
|
+
}));
|
|
65618
|
+
return new Set(results.filter(Boolean));
|
|
65619
|
+
} catch {
|
|
65620
|
+
return new Set;
|
|
65621
|
+
}
|
|
65622
|
+
}
|
|
65623
|
+
async function getSkillOwnershipMap(claudeDir2) {
|
|
65624
|
+
const ownershipMap = new Map;
|
|
65625
|
+
const metadataPath = join101(claudeDir2, "metadata.json");
|
|
65626
|
+
if (!await import_fs_extra32.pathExists(metadataPath))
|
|
65627
|
+
return ownershipMap;
|
|
65628
|
+
let trackedFiles;
|
|
65629
|
+
try {
|
|
65630
|
+
const content = await readFile48(metadataPath, "utf-8");
|
|
65631
|
+
const metadata = JSON.parse(content);
|
|
65632
|
+
trackedFiles = extractTrackedSkillFiles2(metadata);
|
|
65633
|
+
} catch {
|
|
65634
|
+
logger.debug("standalone-skill-cleanup: could not read metadata");
|
|
65635
|
+
return ownershipMap;
|
|
65636
|
+
}
|
|
65637
|
+
for (const file of trackedFiles) {
|
|
65638
|
+
const skillDirName = extractSkillDirName(file.path);
|
|
65639
|
+
if (!skillDirName)
|
|
65640
|
+
continue;
|
|
65641
|
+
const existing = ownershipMap.get(skillDirName);
|
|
65642
|
+
if (file.ownership === "user" || file.ownership === "ck-modified") {
|
|
65643
|
+
ownershipMap.set(skillDirName, "user");
|
|
65644
|
+
} else if (!existing) {
|
|
65645
|
+
ownershipMap.set(skillDirName, "ck");
|
|
65646
|
+
}
|
|
65647
|
+
}
|
|
65648
|
+
return ownershipMap;
|
|
65649
|
+
}
|
|
65650
|
+
function extractTrackedSkillFiles2(metadata) {
|
|
65651
|
+
const files = [];
|
|
65652
|
+
if (metadata.kits && typeof metadata.kits === "object") {
|
|
65653
|
+
for (const kit of Object.values(metadata.kits)) {
|
|
65654
|
+
if (kit.files) {
|
|
65655
|
+
files.push(...kit.files.filter((f3) => f3.path?.startsWith("skills/")));
|
|
65656
|
+
}
|
|
65657
|
+
}
|
|
65658
|
+
}
|
|
65659
|
+
if (Array.isArray(metadata.files)) {
|
|
65660
|
+
files.push(...metadata.files.filter((f3) => f3.path?.startsWith("skills/")));
|
|
65661
|
+
}
|
|
65662
|
+
return files;
|
|
65663
|
+
}
|
|
65664
|
+
function extractSkillDirName(filePath) {
|
|
65665
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
65666
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
65667
|
+
if (parts.length < 2 || parts[0] !== "skills")
|
|
65668
|
+
return null;
|
|
65669
|
+
return parts[1];
|
|
65670
|
+
}
|
|
65671
|
+
async function cleanupOverlappingStandaloneSkills(claudeDir2, pluginSkillsDir) {
|
|
65672
|
+
const standaloneSkillsDir = join101(claudeDir2, "skills");
|
|
65673
|
+
const result = {
|
|
65674
|
+
removed: [],
|
|
65675
|
+
preserved: [],
|
|
65676
|
+
pluginSkillsDir
|
|
65677
|
+
};
|
|
65678
|
+
const [pluginSkills, standaloneSkills] = await Promise.all([
|
|
65679
|
+
listSkillDirs(pluginSkillsDir),
|
|
65680
|
+
listSkillDirs(standaloneSkillsDir)
|
|
65681
|
+
]);
|
|
65682
|
+
if (pluginSkills.size === 0 || standaloneSkills.size === 0) {
|
|
65683
|
+
logger.debug(`standalone-skill-cleanup: nothing to clean (plugin=${pluginSkills.size}, standalone=${standaloneSkills.size})`);
|
|
65684
|
+
return result;
|
|
65685
|
+
}
|
|
65686
|
+
const overlaps = [...standaloneSkills].filter((name2) => pluginSkills.has(name2));
|
|
65687
|
+
if (overlaps.length === 0)
|
|
65688
|
+
return result;
|
|
65689
|
+
const ownershipMap = await getSkillOwnershipMap(claudeDir2);
|
|
65690
|
+
for (const skillName of overlaps) {
|
|
65691
|
+
const ownership = ownershipMap.get(skillName);
|
|
65692
|
+
const skillPath = join101(standaloneSkillsDir, skillName);
|
|
65693
|
+
if (ownership === "user" || ownership === undefined) {
|
|
65694
|
+
result.preserved.push(skillName);
|
|
65695
|
+
const reason = ownership === "user" ? "user-owned/modified" : "untracked";
|
|
65696
|
+
logger.debug(`standalone-skill-cleanup: preserved ${skillName} (${reason})`);
|
|
65697
|
+
continue;
|
|
65698
|
+
}
|
|
65699
|
+
try {
|
|
65700
|
+
await import_fs_extra32.remove(skillPath);
|
|
65701
|
+
result.removed.push(skillName);
|
|
65702
|
+
logger.debug(`standalone-skill-cleanup: removed standalone ${skillName} (plugin has it)`);
|
|
65703
|
+
} catch (error) {
|
|
65704
|
+
result.preserved.push(skillName);
|
|
65705
|
+
logger.debug(`standalone-skill-cleanup: could not remove ${skillName}: ${error}`);
|
|
65706
|
+
}
|
|
65707
|
+
}
|
|
65708
|
+
return result;
|
|
65709
|
+
}
|
|
65710
|
+
var import_fs_extra32;
|
|
65711
|
+
var init_standalone_skill_cleanup = __esm(() => {
|
|
65712
|
+
init_logger();
|
|
65713
|
+
import_fs_extra32 = __toESM(require_lib3(), 1);
|
|
65714
|
+
});
|
|
65715
|
+
|
|
64668
65716
|
// src/domains/help/commands/common-options.ts
|
|
64669
65717
|
var filterOptionsGroup, folderOptionsGroup;
|
|
64670
65718
|
var init_common_options = __esm(() => {
|
|
@@ -65782,7 +66830,7 @@ function getPagerArgs(pagerCmd) {
|
|
|
65782
66830
|
return [];
|
|
65783
66831
|
}
|
|
65784
66832
|
async function trySystemPager(content) {
|
|
65785
|
-
return new Promise((
|
|
66833
|
+
return new Promise((resolve27) => {
|
|
65786
66834
|
const pagerCmd = process.env.PAGER || "less";
|
|
65787
66835
|
const pagerArgs = getPagerArgs(pagerCmd);
|
|
65788
66836
|
try {
|
|
@@ -65792,20 +66840,20 @@ async function trySystemPager(content) {
|
|
|
65792
66840
|
});
|
|
65793
66841
|
const timeout2 = setTimeout(() => {
|
|
65794
66842
|
pager.kill();
|
|
65795
|
-
|
|
66843
|
+
resolve27(false);
|
|
65796
66844
|
}, 30000);
|
|
65797
66845
|
pager.stdin.write(content);
|
|
65798
66846
|
pager.stdin.end();
|
|
65799
66847
|
pager.on("close", (code2) => {
|
|
65800
66848
|
clearTimeout(timeout2);
|
|
65801
|
-
|
|
66849
|
+
resolve27(code2 === 0);
|
|
65802
66850
|
});
|
|
65803
66851
|
pager.on("error", () => {
|
|
65804
66852
|
clearTimeout(timeout2);
|
|
65805
|
-
|
|
66853
|
+
resolve27(false);
|
|
65806
66854
|
});
|
|
65807
66855
|
} catch {
|
|
65808
|
-
|
|
66856
|
+
resolve27(false);
|
|
65809
66857
|
}
|
|
65810
66858
|
});
|
|
65811
66859
|
}
|
|
@@ -65832,16 +66880,16 @@ async function basicPager(content) {
|
|
|
65832
66880
|
break;
|
|
65833
66881
|
}
|
|
65834
66882
|
const remaining = lines.length - currentLine;
|
|
65835
|
-
await new Promise((
|
|
66883
|
+
await new Promise((resolve27) => {
|
|
65836
66884
|
rl.question(`-- More (${remaining} lines) [Enter/q] --`, (answer) => {
|
|
65837
66885
|
if (answer.toLowerCase() === "q") {
|
|
65838
66886
|
rl.close();
|
|
65839
66887
|
process.exitCode = 0;
|
|
65840
|
-
|
|
66888
|
+
resolve27();
|
|
65841
66889
|
return;
|
|
65842
66890
|
}
|
|
65843
66891
|
process.stdout.write("\x1B[1A\x1B[2K");
|
|
65844
|
-
|
|
66892
|
+
resolve27();
|
|
65845
66893
|
});
|
|
65846
66894
|
});
|
|
65847
66895
|
}
|
|
@@ -72514,7 +73562,7 @@ class ConfigVersionChecker {
|
|
|
72514
73562
|
}
|
|
72515
73563
|
// src/domains/sync/sync-engine.ts
|
|
72516
73564
|
import { lstat as lstat3, readFile as readFile34, readlink, realpath as realpath3, stat as stat9 } from "node:fs/promises";
|
|
72517
|
-
import {
|
|
73565
|
+
import { dirname as dirname15, isAbsolute as isAbsolute3, normalize as normalize7, relative as relative7, resolve as resolve13 } from "node:path";
|
|
72518
73566
|
|
|
72519
73567
|
// src/services/file-operations/ownership-checker.ts
|
|
72520
73568
|
init_metadata_migration();
|
|
@@ -73875,7 +74923,7 @@ async function validateSymlinkChain(path5, basePath, maxDepth = MAX_SYMLINK_DEPT
|
|
|
73875
74923
|
if (!stats.isSymbolicLink())
|
|
73876
74924
|
break;
|
|
73877
74925
|
const target = await readlink(current);
|
|
73878
|
-
const resolvedTarget = isAbsolute3(target) ? target :
|
|
74926
|
+
const resolvedTarget = isAbsolute3(target) ? target : resolve13(dirname15(current), target);
|
|
73879
74927
|
const normalizedTarget = normalize7(resolvedTarget);
|
|
73880
74928
|
const rel = relative7(basePath, normalizedTarget);
|
|
73881
74929
|
if (rel.startsWith("..") || isAbsolute3(rel)) {
|
|
@@ -73894,6 +74942,10 @@ async function validateSymlinkChain(path5, basePath, maxDepth = MAX_SYMLINK_DEPT
|
|
|
73894
74942
|
throw new Error(`Symlink chain too deep (>${maxDepth}): ${path5}`);
|
|
73895
74943
|
}
|
|
73896
74944
|
}
|
|
74945
|
+
function isOutsideBase(candidatePath, basePath) {
|
|
74946
|
+
const rel = relative7(basePath, candidatePath);
|
|
74947
|
+
return rel.startsWith("..") || isAbsolute3(rel);
|
|
74948
|
+
}
|
|
73897
74949
|
async function validateSyncPath(basePath, filePath) {
|
|
73898
74950
|
if (!filePath || filePath.trim() === "") {
|
|
73899
74951
|
throw new Error("Empty file path not allowed");
|
|
@@ -73908,37 +74960,44 @@ async function validateSyncPath(basePath, filePath) {
|
|
|
73908
74960
|
if (isAbsolute3(normalized)) {
|
|
73909
74961
|
throw new Error(`Absolute paths not allowed: ${filePath}`);
|
|
73910
74962
|
}
|
|
73911
|
-
|
|
74963
|
+
const pathParts = normalized.split(/[\\/]+/).filter(Boolean);
|
|
74964
|
+
if (pathParts.includes("..")) {
|
|
73912
74965
|
throw new Error(`Path traversal not allowed: ${filePath}`);
|
|
73913
74966
|
}
|
|
73914
|
-
const
|
|
73915
|
-
const
|
|
73916
|
-
|
|
74967
|
+
const lexicalBase = resolve13(basePath);
|
|
74968
|
+
const fullPath = resolve13(basePath, normalized);
|
|
74969
|
+
const resolvedBase = await realpath3(basePath).catch(() => lexicalBase);
|
|
74970
|
+
if (isOutsideBase(fullPath, lexicalBase)) {
|
|
73917
74971
|
throw new Error(`Path escapes base directory: ${filePath}`);
|
|
73918
74972
|
}
|
|
73919
|
-
await validateSymlinkChain(fullPath,
|
|
74973
|
+
await validateSymlinkChain(fullPath, resolvedBase);
|
|
73920
74974
|
try {
|
|
73921
|
-
const resolvedBase = await realpath3(basePath);
|
|
73922
74975
|
const resolvedFull = await realpath3(fullPath);
|
|
73923
|
-
|
|
73924
|
-
if (resolvedRel.startsWith("..") || isAbsolute3(resolvedRel)) {
|
|
74976
|
+
if (isOutsideBase(resolvedFull, resolvedBase)) {
|
|
73925
74977
|
throw new Error(`Symlink escapes base directory: ${filePath}`);
|
|
73926
74978
|
}
|
|
73927
74979
|
} catch (error) {
|
|
73928
74980
|
if (error.code === "ENOENT") {
|
|
73929
|
-
|
|
73930
|
-
|
|
73931
|
-
|
|
73932
|
-
|
|
73933
|
-
|
|
73934
|
-
|
|
73935
|
-
|
|
73936
|
-
|
|
73937
|
-
|
|
73938
|
-
|
|
73939
|
-
|
|
74981
|
+
let ancestor = dirname15(fullPath);
|
|
74982
|
+
while (ancestor !== lexicalBase) {
|
|
74983
|
+
try {
|
|
74984
|
+
await lstat3(ancestor);
|
|
74985
|
+
break;
|
|
74986
|
+
} catch (ancestorError) {
|
|
74987
|
+
if (ancestorError.code !== "ENOENT") {
|
|
74988
|
+
throw ancestorError;
|
|
74989
|
+
}
|
|
74990
|
+
const nextAncestor = dirname15(ancestor);
|
|
74991
|
+
if (nextAncestor === ancestor) {
|
|
74992
|
+
break;
|
|
74993
|
+
}
|
|
74994
|
+
ancestor = nextAncestor;
|
|
73940
74995
|
}
|
|
73941
74996
|
}
|
|
74997
|
+
const resolvedAncestor = await realpath3(ancestor).catch(() => null);
|
|
74998
|
+
if (!resolvedAncestor || isOutsideBase(resolvedAncestor, resolvedBase)) {
|
|
74999
|
+
throw new Error(`Parent symlink escapes base directory: ${filePath}`);
|
|
75000
|
+
}
|
|
73942
75001
|
} else {
|
|
73943
75002
|
throw error;
|
|
73944
75003
|
}
|
|
@@ -74090,14 +75149,24 @@ class SyncEngine {
|
|
|
74090
75149
|
}
|
|
74091
75150
|
static async loadFileContent(filePath) {
|
|
74092
75151
|
try {
|
|
74093
|
-
const
|
|
74094
|
-
if (
|
|
75152
|
+
const beforeStats = await lstat3(filePath);
|
|
75153
|
+
if (beforeStats.isSymbolicLink()) {
|
|
74095
75154
|
throw new Error(`Symlink not allowed for sync: ${filePath}`);
|
|
74096
75155
|
}
|
|
74097
|
-
if (
|
|
74098
|
-
throw new Error(`File too large for sync (${Math.round(
|
|
75156
|
+
if (beforeStats.size > MAX_SYNC_FILE_SIZE) {
|
|
75157
|
+
throw new Error(`File too large for sync (${Math.round(beforeStats.size / 1024 / 1024)}MB > ${MAX_SYNC_FILE_SIZE / 1024 / 1024}MB limit)`);
|
|
74099
75158
|
}
|
|
74100
75159
|
const buffer = await readFile34(filePath);
|
|
75160
|
+
const afterStats = await lstat3(filePath);
|
|
75161
|
+
if (afterStats.isSymbolicLink()) {
|
|
75162
|
+
throw new Error(`File became symlink during read: ${filePath}`);
|
|
75163
|
+
}
|
|
75164
|
+
if (beforeStats.dev !== afterStats.dev || beforeStats.ino !== afterStats.ino) {
|
|
75165
|
+
throw new Error(`File changed identity during read: ${filePath}`);
|
|
75166
|
+
}
|
|
75167
|
+
if (beforeStats.mtimeMs !== afterStats.mtimeMs || beforeStats.size !== afterStats.size) {
|
|
75168
|
+
throw new Error(`File changed during read: ${filePath}`);
|
|
75169
|
+
}
|
|
74101
75170
|
if (buffer.includes(0)) {
|
|
74102
75171
|
return { content: "", isBinary: true };
|
|
74103
75172
|
}
|
|
@@ -75108,7 +76177,7 @@ init_logger();
|
|
|
75108
76177
|
var import_proper_lockfile4 = __toESM(require_proper_lockfile(), 1);
|
|
75109
76178
|
import { mkdir as mkdir19 } from "node:fs/promises";
|
|
75110
76179
|
import os5 from "node:os";
|
|
75111
|
-
import { join as
|
|
76180
|
+
import { join as join64 } from "node:path";
|
|
75112
76181
|
var LOCK_CONFIG = {
|
|
75113
76182
|
stale: 60000,
|
|
75114
76183
|
retries: 0
|
|
@@ -75116,12 +76185,12 @@ var LOCK_CONFIG = {
|
|
|
75116
76185
|
var activeLocks = new Set;
|
|
75117
76186
|
var cleanupRegistered = false;
|
|
75118
76187
|
function getLocksDir() {
|
|
75119
|
-
return
|
|
76188
|
+
return join64(os5.homedir(), ".claudekit", "locks");
|
|
75120
76189
|
}
|
|
75121
76190
|
function cleanupLocks() {
|
|
75122
76191
|
for (const name of activeLocks) {
|
|
75123
76192
|
try {
|
|
75124
|
-
const lockPath =
|
|
76193
|
+
const lockPath = join64(getLocksDir(), `${name}.lock`);
|
|
75125
76194
|
import_proper_lockfile4.default.unlockSync(lockPath, { realpath: false });
|
|
75126
76195
|
} catch {
|
|
75127
76196
|
try {
|
|
@@ -75144,7 +76213,7 @@ async function ensureLocksDir() {
|
|
|
75144
76213
|
async function withProcessLock(lockName, fn) {
|
|
75145
76214
|
registerCleanupHandlers();
|
|
75146
76215
|
await ensureLocksDir();
|
|
75147
|
-
const lockPath =
|
|
76216
|
+
const lockPath = join64(getLocksDir(), `${lockName}.lock`);
|
|
75148
76217
|
let release;
|
|
75149
76218
|
try {
|
|
75150
76219
|
release = await import_proper_lockfile4.default.lock(lockPath, { ...LOCK_CONFIG, realpath: false });
|
|
@@ -75175,7 +76244,7 @@ init_logger();
|
|
|
75175
76244
|
init_logger();
|
|
75176
76245
|
init_path_resolver();
|
|
75177
76246
|
var import_fs_extra7 = __toESM(require_lib3(), 1);
|
|
75178
|
-
import { join as
|
|
76247
|
+
import { join as join65 } from "node:path";
|
|
75179
76248
|
async function handleConflicts(ctx) {
|
|
75180
76249
|
if (ctx.cancelled)
|
|
75181
76250
|
return ctx;
|
|
@@ -75184,7 +76253,7 @@ async function handleConflicts(ctx) {
|
|
|
75184
76253
|
if (PathResolver.isLocalSameAsGlobal()) {
|
|
75185
76254
|
return ctx;
|
|
75186
76255
|
}
|
|
75187
|
-
const localSettingsPath =
|
|
76256
|
+
const localSettingsPath = join65(process.cwd(), ".claude", "settings.json");
|
|
75188
76257
|
if (!await import_fs_extra7.pathExists(localSettingsPath)) {
|
|
75189
76258
|
return ctx;
|
|
75190
76259
|
}
|
|
@@ -75199,7 +76268,7 @@ async function handleConflicts(ctx) {
|
|
|
75199
76268
|
return { ...ctx, cancelled: true };
|
|
75200
76269
|
}
|
|
75201
76270
|
if (choice === "remove") {
|
|
75202
|
-
const localClaudeDir =
|
|
76271
|
+
const localClaudeDir = join65(process.cwd(), ".claude");
|
|
75203
76272
|
try {
|
|
75204
76273
|
await import_fs_extra7.remove(localClaudeDir);
|
|
75205
76274
|
logger.success("Removed local .claude/ directory");
|
|
@@ -75296,7 +76365,7 @@ init_logger();
|
|
|
75296
76365
|
init_safe_spinner();
|
|
75297
76366
|
import { mkdir as mkdir25, stat as stat12 } from "node:fs/promises";
|
|
75298
76367
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
75299
|
-
import { join as
|
|
76368
|
+
import { join as join72 } from "node:path";
|
|
75300
76369
|
|
|
75301
76370
|
// src/shared/temp-cleanup.ts
|
|
75302
76371
|
init_logger();
|
|
@@ -75315,7 +76384,7 @@ init_logger();
|
|
|
75315
76384
|
init_output_manager();
|
|
75316
76385
|
import { createWriteStream as createWriteStream2, rmSync } from "node:fs";
|
|
75317
76386
|
import { mkdir as mkdir20 } from "node:fs/promises";
|
|
75318
|
-
import { join as
|
|
76387
|
+
import { join as join66 } from "node:path";
|
|
75319
76388
|
|
|
75320
76389
|
// src/shared/progress-bar.ts
|
|
75321
76390
|
init_output_manager();
|
|
@@ -75480,10 +76549,10 @@ init_types3();
|
|
|
75480
76549
|
// src/domains/installation/utils/path-security.ts
|
|
75481
76550
|
init_types3();
|
|
75482
76551
|
import { lstatSync as lstatSync2, realpathSync as realpathSync2 } from "node:fs";
|
|
75483
|
-
import { relative as relative8, resolve as
|
|
76552
|
+
import { relative as relative8, resolve as resolve16 } from "node:path";
|
|
75484
76553
|
var MAX_EXTRACTION_SIZE = 500 * 1024 * 1024;
|
|
75485
76554
|
function isPathSafe(basePath, targetPath) {
|
|
75486
|
-
const resolvedBase =
|
|
76555
|
+
const resolvedBase = resolve16(basePath);
|
|
75487
76556
|
try {
|
|
75488
76557
|
const stat10 = lstatSync2(targetPath);
|
|
75489
76558
|
if (stat10.isSymbolicLink()) {
|
|
@@ -75493,7 +76562,7 @@ function isPathSafe(basePath, targetPath) {
|
|
|
75493
76562
|
}
|
|
75494
76563
|
}
|
|
75495
76564
|
} catch {}
|
|
75496
|
-
const resolvedTarget =
|
|
76565
|
+
const resolvedTarget = resolve16(targetPath);
|
|
75497
76566
|
const relativePath = relative8(resolvedBase, resolvedTarget);
|
|
75498
76567
|
return !relativePath.startsWith("..") && !relativePath.startsWith("/") && resolvedTarget.startsWith(resolvedBase);
|
|
75499
76568
|
}
|
|
@@ -75525,7 +76594,7 @@ var MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024;
|
|
|
75525
76594
|
class FileDownloader {
|
|
75526
76595
|
async downloadAsset(asset, destDir) {
|
|
75527
76596
|
try {
|
|
75528
|
-
const destPath =
|
|
76597
|
+
const destPath = join66(destDir, asset.name);
|
|
75529
76598
|
await mkdir20(destDir, { recursive: true });
|
|
75530
76599
|
output.info(`Downloading ${asset.name} (${formatBytes(asset.size)})...`);
|
|
75531
76600
|
logger.verbose("Download details", {
|
|
@@ -75581,7 +76650,7 @@ class FileDownloader {
|
|
|
75581
76650
|
}
|
|
75582
76651
|
if (downloadedSize !== totalSize) {
|
|
75583
76652
|
fileStream.end();
|
|
75584
|
-
await new Promise((
|
|
76653
|
+
await new Promise((resolve17) => fileStream.once("close", resolve17));
|
|
75585
76654
|
try {
|
|
75586
76655
|
rmSync(destPath, { force: true });
|
|
75587
76656
|
} catch (cleanupError) {
|
|
@@ -75595,7 +76664,7 @@ class FileDownloader {
|
|
|
75595
76664
|
return destPath;
|
|
75596
76665
|
} catch (error) {
|
|
75597
76666
|
fileStream.end();
|
|
75598
|
-
await new Promise((
|
|
76667
|
+
await new Promise((resolve17) => fileStream.once("close", resolve17));
|
|
75599
76668
|
try {
|
|
75600
76669
|
rmSync(destPath, { force: true });
|
|
75601
76670
|
} catch (cleanupError) {
|
|
@@ -75610,7 +76679,7 @@ class FileDownloader {
|
|
|
75610
76679
|
}
|
|
75611
76680
|
async downloadFile(params) {
|
|
75612
76681
|
const { url, name, size, destDir, token } = params;
|
|
75613
|
-
const destPath =
|
|
76682
|
+
const destPath = join66(destDir, name);
|
|
75614
76683
|
await mkdir20(destDir, { recursive: true });
|
|
75615
76684
|
output.info(`Downloading ${name}${size ? ` (${formatBytes(size)})` : ""}...`);
|
|
75616
76685
|
const headers = {};
|
|
@@ -75661,7 +76730,7 @@ class FileDownloader {
|
|
|
75661
76730
|
const expectedSize = Number(response.headers.get("content-length"));
|
|
75662
76731
|
if (expectedSize > 0 && downloadedSize !== expectedSize) {
|
|
75663
76732
|
fileStream.end();
|
|
75664
|
-
await new Promise((
|
|
76733
|
+
await new Promise((resolve17) => fileStream.once("close", resolve17));
|
|
75665
76734
|
try {
|
|
75666
76735
|
rmSync(destPath, { force: true });
|
|
75667
76736
|
} catch (cleanupError) {
|
|
@@ -75679,7 +76748,7 @@ class FileDownloader {
|
|
|
75679
76748
|
return destPath;
|
|
75680
76749
|
} catch (error) {
|
|
75681
76750
|
fileStream.end();
|
|
75682
|
-
await new Promise((
|
|
76751
|
+
await new Promise((resolve17) => fileStream.once("close", resolve17));
|
|
75683
76752
|
try {
|
|
75684
76753
|
rmSync(destPath, { force: true });
|
|
75685
76754
|
} catch (cleanupError) {
|
|
@@ -75696,7 +76765,7 @@ init_logger();
|
|
|
75696
76765
|
init_types3();
|
|
75697
76766
|
import { constants as constants4 } from "node:fs";
|
|
75698
76767
|
import { access as access4, readdir as readdir13 } from "node:fs/promises";
|
|
75699
|
-
import { join as
|
|
76768
|
+
import { join as join67 } from "node:path";
|
|
75700
76769
|
async function validateExtraction(extractDir) {
|
|
75701
76770
|
try {
|
|
75702
76771
|
const entries = await readdir13(extractDir, { encoding: "utf8" });
|
|
@@ -75708,7 +76777,7 @@ async function validateExtraction(extractDir) {
|
|
|
75708
76777
|
const missingPaths = [];
|
|
75709
76778
|
for (const path5 of criticalPaths) {
|
|
75710
76779
|
try {
|
|
75711
|
-
await access4(
|
|
76780
|
+
await access4(join67(extractDir, path5), constants4.F_OK);
|
|
75712
76781
|
logger.debug(`Found: ${path5}`);
|
|
75713
76782
|
} catch {
|
|
75714
76783
|
logger.warning(`Expected path not found: ${path5}`);
|
|
@@ -75730,7 +76799,7 @@ async function validateExtraction(extractDir) {
|
|
|
75730
76799
|
// src/domains/installation/extraction/tar-extractor.ts
|
|
75731
76800
|
init_logger();
|
|
75732
76801
|
import { copyFile as copyFile4, mkdir as mkdir23, readdir as readdir15, rm as rm8, stat as stat10 } from "node:fs/promises";
|
|
75733
|
-
import { join as
|
|
76802
|
+
import { join as join70 } from "node:path";
|
|
75734
76803
|
|
|
75735
76804
|
// node_modules/@isaacs/fs-minipass/dist/esm/index.js
|
|
75736
76805
|
import EE from "events";
|
|
@@ -76278,10 +77347,10 @@ class Minipass extends EventEmitter3 {
|
|
|
76278
77347
|
return this[ENCODING] ? buf.join("") : Buffer.concat(buf, buf.dataLength);
|
|
76279
77348
|
}
|
|
76280
77349
|
async promise() {
|
|
76281
|
-
return new Promise((
|
|
77350
|
+
return new Promise((resolve17, reject) => {
|
|
76282
77351
|
this.on(DESTROYED, () => reject(new Error("stream destroyed")));
|
|
76283
77352
|
this.on("error", (er) => reject(er));
|
|
76284
|
-
this.on("end", () =>
|
|
77353
|
+
this.on("end", () => resolve17());
|
|
76285
77354
|
});
|
|
76286
77355
|
}
|
|
76287
77356
|
[Symbol.asyncIterator]() {
|
|
@@ -76300,7 +77369,7 @@ class Minipass extends EventEmitter3 {
|
|
|
76300
77369
|
return Promise.resolve({ done: false, value: res });
|
|
76301
77370
|
if (this[EOF])
|
|
76302
77371
|
return stop();
|
|
76303
|
-
let
|
|
77372
|
+
let resolve17;
|
|
76304
77373
|
let reject;
|
|
76305
77374
|
const onerr = (er) => {
|
|
76306
77375
|
this.off("data", ondata);
|
|
@@ -76314,19 +77383,19 @@ class Minipass extends EventEmitter3 {
|
|
|
76314
77383
|
this.off("end", onend);
|
|
76315
77384
|
this.off(DESTROYED, ondestroy);
|
|
76316
77385
|
this.pause();
|
|
76317
|
-
|
|
77386
|
+
resolve17({ value, done: !!this[EOF] });
|
|
76318
77387
|
};
|
|
76319
77388
|
const onend = () => {
|
|
76320
77389
|
this.off("error", onerr);
|
|
76321
77390
|
this.off("data", ondata);
|
|
76322
77391
|
this.off(DESTROYED, ondestroy);
|
|
76323
77392
|
stop();
|
|
76324
|
-
|
|
77393
|
+
resolve17({ done: true, value: undefined });
|
|
76325
77394
|
};
|
|
76326
77395
|
const ondestroy = () => onerr(new Error("stream destroyed"));
|
|
76327
77396
|
return new Promise((res2, rej) => {
|
|
76328
77397
|
reject = rej;
|
|
76329
|
-
|
|
77398
|
+
resolve17 = res2;
|
|
76330
77399
|
this.once(DESTROYED, ondestroy);
|
|
76331
77400
|
this.once("error", onerr);
|
|
76332
77401
|
this.once("end", onend);
|
|
@@ -76783,7 +77852,7 @@ import path7 from "node:path";
|
|
|
76783
77852
|
|
|
76784
77853
|
// node_modules/tar/dist/esm/list.js
|
|
76785
77854
|
import fs9 from "node:fs";
|
|
76786
|
-
import { dirname as
|
|
77855
|
+
import { dirname as dirname18, parse as parse3 } from "path";
|
|
76787
77856
|
|
|
76788
77857
|
// node_modules/tar/dist/esm/options.js
|
|
76789
77858
|
var argmap = new Map([
|
|
@@ -77432,10 +78501,10 @@ class Minipass2 extends EventEmitter4 {
|
|
|
77432
78501
|
return this[ENCODING2] ? buf.join("") : Buffer.concat(buf, buf.dataLength);
|
|
77433
78502
|
}
|
|
77434
78503
|
async promise() {
|
|
77435
|
-
return new Promise((
|
|
78504
|
+
return new Promise((resolve17, reject) => {
|
|
77436
78505
|
this.on(DESTROYED2, () => reject(new Error("stream destroyed")));
|
|
77437
78506
|
this.on("error", (er) => reject(er));
|
|
77438
|
-
this.on("end", () =>
|
|
78507
|
+
this.on("end", () => resolve17());
|
|
77439
78508
|
});
|
|
77440
78509
|
}
|
|
77441
78510
|
[Symbol.asyncIterator]() {
|
|
@@ -77454,7 +78523,7 @@ class Minipass2 extends EventEmitter4 {
|
|
|
77454
78523
|
return Promise.resolve({ done: false, value: res });
|
|
77455
78524
|
if (this[EOF2])
|
|
77456
78525
|
return stop();
|
|
77457
|
-
let
|
|
78526
|
+
let resolve17;
|
|
77458
78527
|
let reject;
|
|
77459
78528
|
const onerr = (er) => {
|
|
77460
78529
|
this.off("data", ondata);
|
|
@@ -77468,19 +78537,19 @@ class Minipass2 extends EventEmitter4 {
|
|
|
77468
78537
|
this.off("end", onend);
|
|
77469
78538
|
this.off(DESTROYED2, ondestroy);
|
|
77470
78539
|
this.pause();
|
|
77471
|
-
|
|
78540
|
+
resolve17({ value, done: !!this[EOF2] });
|
|
77472
78541
|
};
|
|
77473
78542
|
const onend = () => {
|
|
77474
78543
|
this.off("error", onerr);
|
|
77475
78544
|
this.off("data", ondata);
|
|
77476
78545
|
this.off(DESTROYED2, ondestroy);
|
|
77477
78546
|
stop();
|
|
77478
|
-
|
|
78547
|
+
resolve17({ done: true, value: undefined });
|
|
77479
78548
|
};
|
|
77480
78549
|
const ondestroy = () => onerr(new Error("stream destroyed"));
|
|
77481
78550
|
return new Promise((res2, rej) => {
|
|
77482
78551
|
reject = rej;
|
|
77483
|
-
|
|
78552
|
+
resolve17 = res2;
|
|
77484
78553
|
this.once(DESTROYED2, ondestroy);
|
|
77485
78554
|
this.once("error", onerr);
|
|
77486
78555
|
this.once("end", onend);
|
|
@@ -78908,10 +79977,10 @@ class Minipass3 extends EventEmitter5 {
|
|
|
78908
79977
|
return this[ENCODING3] ? buf.join("") : Buffer.concat(buf, buf.dataLength);
|
|
78909
79978
|
}
|
|
78910
79979
|
async promise() {
|
|
78911
|
-
return new Promise((
|
|
79980
|
+
return new Promise((resolve17, reject) => {
|
|
78912
79981
|
this.on(DESTROYED3, () => reject(new Error("stream destroyed")));
|
|
78913
79982
|
this.on("error", (er) => reject(er));
|
|
78914
|
-
this.on("end", () =>
|
|
79983
|
+
this.on("end", () => resolve17());
|
|
78915
79984
|
});
|
|
78916
79985
|
}
|
|
78917
79986
|
[Symbol.asyncIterator]() {
|
|
@@ -78930,7 +79999,7 @@ class Minipass3 extends EventEmitter5 {
|
|
|
78930
79999
|
return Promise.resolve({ done: false, value: res });
|
|
78931
80000
|
if (this[EOF3])
|
|
78932
80001
|
return stop();
|
|
78933
|
-
let
|
|
80002
|
+
let resolve17;
|
|
78934
80003
|
let reject;
|
|
78935
80004
|
const onerr = (er) => {
|
|
78936
80005
|
this.off("data", ondata);
|
|
@@ -78944,19 +80013,19 @@ class Minipass3 extends EventEmitter5 {
|
|
|
78944
80013
|
this.off("end", onend);
|
|
78945
80014
|
this.off(DESTROYED3, ondestroy);
|
|
78946
80015
|
this.pause();
|
|
78947
|
-
|
|
80016
|
+
resolve17({ value, done: !!this[EOF3] });
|
|
78948
80017
|
};
|
|
78949
80018
|
const onend = () => {
|
|
78950
80019
|
this.off("error", onerr);
|
|
78951
80020
|
this.off("data", ondata);
|
|
78952
80021
|
this.off(DESTROYED3, ondestroy);
|
|
78953
80022
|
stop();
|
|
78954
|
-
|
|
80023
|
+
resolve17({ done: true, value: undefined });
|
|
78955
80024
|
};
|
|
78956
80025
|
const ondestroy = () => onerr(new Error("stream destroyed"));
|
|
78957
80026
|
return new Promise((res2, rej) => {
|
|
78958
80027
|
reject = rej;
|
|
78959
|
-
|
|
80028
|
+
resolve17 = res2;
|
|
78960
80029
|
this.once(DESTROYED3, ondestroy);
|
|
78961
80030
|
this.once("error", onerr);
|
|
78962
80031
|
this.once("end", onend);
|
|
@@ -79683,7 +80752,7 @@ var filesFilter = (opt, files) => {
|
|
|
79683
80752
|
if (m2 !== undefined) {
|
|
79684
80753
|
ret = m2;
|
|
79685
80754
|
} else {
|
|
79686
|
-
ret = mapHas(
|
|
80755
|
+
ret = mapHas(dirname18(file), root);
|
|
79687
80756
|
}
|
|
79688
80757
|
}
|
|
79689
80758
|
map.set(file, ret);
|
|
@@ -79727,9 +80796,9 @@ var listFile = (opt, _files) => {
|
|
|
79727
80796
|
const parse4 = new Parser(opt);
|
|
79728
80797
|
const readSize = opt.maxReadSize || 16 * 1024 * 1024;
|
|
79729
80798
|
const file = opt.file;
|
|
79730
|
-
const p = new Promise((
|
|
80799
|
+
const p = new Promise((resolve17, reject) => {
|
|
79731
80800
|
parse4.on("error", reject);
|
|
79732
|
-
parse4.on("end",
|
|
80801
|
+
parse4.on("end", resolve17);
|
|
79733
80802
|
fs9.stat(file, (er, stat10) => {
|
|
79734
80803
|
if (er) {
|
|
79735
80804
|
reject(er);
|
|
@@ -81492,7 +82561,7 @@ var mkdirSync = (dir, opt) => {
|
|
|
81492
82561
|
};
|
|
81493
82562
|
|
|
81494
82563
|
// node_modules/tar/dist/esm/path-reservations.js
|
|
81495
|
-
import { join as
|
|
82564
|
+
import { join as join68 } from "node:path";
|
|
81496
82565
|
|
|
81497
82566
|
// node_modules/tar/dist/esm/normalize-unicode.js
|
|
81498
82567
|
var normalizeCache = Object.create(null);
|
|
@@ -81525,7 +82594,7 @@ var getDirs = (path10) => {
|
|
|
81525
82594
|
const dirs = path10.split("/").slice(0, -1).reduce((set, path11) => {
|
|
81526
82595
|
const s = set[set.length - 1];
|
|
81527
82596
|
if (s !== undefined) {
|
|
81528
|
-
path11 =
|
|
82597
|
+
path11 = join68(s, path11);
|
|
81529
82598
|
}
|
|
81530
82599
|
set.push(path11 || "/");
|
|
81531
82600
|
return set;
|
|
@@ -81539,7 +82608,7 @@ class PathReservations {
|
|
|
81539
82608
|
#running = new Set;
|
|
81540
82609
|
reserve(paths, fn) {
|
|
81541
82610
|
paths = isWindows4 ? ["win32 parallelization disabled"] : paths.map((p) => {
|
|
81542
|
-
return stripTrailingSlashes(
|
|
82611
|
+
return stripTrailingSlashes(join68(normalizeUnicode(p))).toLowerCase();
|
|
81543
82612
|
});
|
|
81544
82613
|
const dirs = new Set(paths.map((path10) => getDirs(path10)).reduce((a3, b3) => a3.concat(b3)));
|
|
81545
82614
|
this.#reservations.set(fn, { dirs, paths });
|
|
@@ -82309,9 +83378,9 @@ var extractFile = (opt, _3) => {
|
|
|
82309
83378
|
const u = new Unpack(opt);
|
|
82310
83379
|
const readSize = opt.maxReadSize || 16 * 1024 * 1024;
|
|
82311
83380
|
const file = opt.file;
|
|
82312
|
-
const p = new Promise((
|
|
83381
|
+
const p = new Promise((resolve17, reject) => {
|
|
82313
83382
|
u.on("error", reject);
|
|
82314
|
-
u.on("close",
|
|
83383
|
+
u.on("close", resolve17);
|
|
82315
83384
|
fs16.stat(file, (er, stat10) => {
|
|
82316
83385
|
if (er) {
|
|
82317
83386
|
reject(er);
|
|
@@ -82444,7 +83513,7 @@ var replaceAsync = (opt, files) => {
|
|
|
82444
83513
|
};
|
|
82445
83514
|
fs17.read(fd, headBuf, 0, 512, position, onread);
|
|
82446
83515
|
};
|
|
82447
|
-
const promise = new Promise((
|
|
83516
|
+
const promise = new Promise((resolve17, reject) => {
|
|
82448
83517
|
p.on("error", reject);
|
|
82449
83518
|
let flag = "r+";
|
|
82450
83519
|
const onopen = (er, fd) => {
|
|
@@ -82469,7 +83538,7 @@ var replaceAsync = (opt, files) => {
|
|
|
82469
83538
|
});
|
|
82470
83539
|
p.pipe(stream);
|
|
82471
83540
|
stream.on("error", reject);
|
|
82472
|
-
stream.on("close",
|
|
83541
|
+
stream.on("close", resolve17);
|
|
82473
83542
|
addFilesAsync2(p, files);
|
|
82474
83543
|
});
|
|
82475
83544
|
});
|
|
@@ -82599,7 +83668,7 @@ function decodeFilePath(path12) {
|
|
|
82599
83668
|
init_logger();
|
|
82600
83669
|
init_types3();
|
|
82601
83670
|
import { copyFile as copyFile3, lstat as lstat4, mkdir as mkdir22, readdir as readdir14 } from "node:fs/promises";
|
|
82602
|
-
import { join as
|
|
83671
|
+
import { join as join69, relative as relative9 } from "node:path";
|
|
82603
83672
|
async function withRetry(fn, retries = 3) {
|
|
82604
83673
|
for (let i = 0;i < retries; i++) {
|
|
82605
83674
|
try {
|
|
@@ -82621,8 +83690,8 @@ async function moveDirectoryContents(sourceDir, destDir, shouldExclude, sizeTrac
|
|
|
82621
83690
|
await mkdir22(destDir, { recursive: true });
|
|
82622
83691
|
const entries = await readdir14(sourceDir, { encoding: "utf8" });
|
|
82623
83692
|
for (const entry of entries) {
|
|
82624
|
-
const sourcePath =
|
|
82625
|
-
const destPath =
|
|
83693
|
+
const sourcePath = join69(sourceDir, entry);
|
|
83694
|
+
const destPath = join69(destDir, entry);
|
|
82626
83695
|
const relativePath = relative9(sourceDir, sourcePath);
|
|
82627
83696
|
if (!isPathSafe(destDir, destPath)) {
|
|
82628
83697
|
logger.warning(`Skipping unsafe path: ${relativePath}`);
|
|
@@ -82649,8 +83718,8 @@ async function copyDirectory(sourceDir, destDir, shouldExclude, sizeTracker) {
|
|
|
82649
83718
|
await mkdir22(destDir, { recursive: true });
|
|
82650
83719
|
const entries = await readdir14(sourceDir, { encoding: "utf8" });
|
|
82651
83720
|
for (const entry of entries) {
|
|
82652
|
-
const sourcePath =
|
|
82653
|
-
const destPath =
|
|
83721
|
+
const sourcePath = join69(sourceDir, entry);
|
|
83722
|
+
const destPath = join69(destDir, entry);
|
|
82654
83723
|
const relativePath = relative9(sourceDir, sourcePath);
|
|
82655
83724
|
if (!isPathSafe(destDir, destPath)) {
|
|
82656
83725
|
logger.warning(`Skipping unsafe path: ${relativePath}`);
|
|
@@ -82698,7 +83767,7 @@ class TarExtractor {
|
|
|
82698
83767
|
logger.debug(`Root entries: ${entries.join(", ")}`);
|
|
82699
83768
|
if (entries.length === 1) {
|
|
82700
83769
|
const rootEntry = entries[0];
|
|
82701
|
-
const rootPath =
|
|
83770
|
+
const rootPath = join70(tempExtractDir, rootEntry);
|
|
82702
83771
|
const rootStat = await stat10(rootPath);
|
|
82703
83772
|
if (rootStat.isDirectory()) {
|
|
82704
83773
|
const rootContents = await readdir15(rootPath, { encoding: "utf8" });
|
|
@@ -82714,7 +83783,7 @@ class TarExtractor {
|
|
|
82714
83783
|
}
|
|
82715
83784
|
} else {
|
|
82716
83785
|
await mkdir23(destDir, { recursive: true });
|
|
82717
|
-
await copyFile4(rootPath,
|
|
83786
|
+
await copyFile4(rootPath, join70(destDir, rootEntry));
|
|
82718
83787
|
}
|
|
82719
83788
|
} else {
|
|
82720
83789
|
logger.debug("Multiple root entries - moving all");
|
|
@@ -82737,26 +83806,26 @@ init_logger();
|
|
|
82737
83806
|
var import_extract_zip = __toESM(require_extract_zip(), 1);
|
|
82738
83807
|
import { execFile as execFile8 } from "node:child_process";
|
|
82739
83808
|
import { copyFile as copyFile5, mkdir as mkdir24, readdir as readdir16, rm as rm9, stat as stat11 } from "node:fs/promises";
|
|
82740
|
-
import { join as
|
|
83809
|
+
import { join as join71 } from "node:path";
|
|
82741
83810
|
class ZipExtractor {
|
|
82742
83811
|
async tryNativeUnzip(archivePath, destDir) {
|
|
82743
83812
|
if (!isMacOS()) {
|
|
82744
83813
|
return false;
|
|
82745
83814
|
}
|
|
82746
|
-
return new Promise((
|
|
83815
|
+
return new Promise((resolve17) => {
|
|
82747
83816
|
mkdir24(destDir, { recursive: true }).then(() => {
|
|
82748
83817
|
execFile8("unzip", ["-o", "-q", archivePath, "-d", destDir], (error, _stdout, stderr) => {
|
|
82749
83818
|
if (error) {
|
|
82750
83819
|
logger.debug(`Native unzip failed: ${stderr || error.message}`);
|
|
82751
|
-
|
|
83820
|
+
resolve17(false);
|
|
82752
83821
|
return;
|
|
82753
83822
|
}
|
|
82754
83823
|
logger.debug("Native unzip succeeded");
|
|
82755
|
-
|
|
83824
|
+
resolve17(true);
|
|
82756
83825
|
});
|
|
82757
83826
|
}).catch((err) => {
|
|
82758
83827
|
logger.debug(`Failed to create directory for native unzip: ${err.message}`);
|
|
82759
|
-
|
|
83828
|
+
resolve17(false);
|
|
82760
83829
|
});
|
|
82761
83830
|
});
|
|
82762
83831
|
}
|
|
@@ -82785,7 +83854,7 @@ class ZipExtractor {
|
|
|
82785
83854
|
logger.debug(`Root entries: ${entries.join(", ")}`);
|
|
82786
83855
|
if (entries.length === 1) {
|
|
82787
83856
|
const rootEntry = entries[0];
|
|
82788
|
-
const rootPath =
|
|
83857
|
+
const rootPath = join71(tempExtractDir, rootEntry);
|
|
82789
83858
|
const rootStat = await stat11(rootPath);
|
|
82790
83859
|
if (rootStat.isDirectory()) {
|
|
82791
83860
|
const rootContents = await readdir16(rootPath, { encoding: "utf8" });
|
|
@@ -82801,7 +83870,7 @@ class ZipExtractor {
|
|
|
82801
83870
|
}
|
|
82802
83871
|
} else {
|
|
82803
83872
|
await mkdir24(destDir, { recursive: true });
|
|
82804
|
-
await copyFile5(rootPath,
|
|
83873
|
+
await copyFile5(rootPath, join71(destDir, rootEntry));
|
|
82805
83874
|
}
|
|
82806
83875
|
} else {
|
|
82807
83876
|
logger.debug("Multiple root entries - moving all");
|
|
@@ -82900,7 +83969,7 @@ class DownloadManager {
|
|
|
82900
83969
|
async createTempDir() {
|
|
82901
83970
|
const timestamp = Date.now();
|
|
82902
83971
|
const counter = DownloadManager.tempDirCounter++;
|
|
82903
|
-
const primaryTempDir =
|
|
83972
|
+
const primaryTempDir = join72(tmpdir4(), `claudekit-${timestamp}-${counter}`);
|
|
82904
83973
|
try {
|
|
82905
83974
|
await mkdir25(primaryTempDir, { recursive: true });
|
|
82906
83975
|
logger.debug(`Created temp directory: ${primaryTempDir}`);
|
|
@@ -82917,7 +83986,7 @@ Solutions:
|
|
|
82917
83986
|
2. Set HOME environment variable
|
|
82918
83987
|
3. Try running from a different directory`);
|
|
82919
83988
|
}
|
|
82920
|
-
const fallbackTempDir =
|
|
83989
|
+
const fallbackTempDir = join72(homeDir, ".claudekit", "tmp", `claudekit-${timestamp}-${counter}`);
|
|
82921
83990
|
try {
|
|
82922
83991
|
await mkdir25(fallbackTempDir, { recursive: true });
|
|
82923
83992
|
logger.debug(`Created temp directory (fallback): ${fallbackTempDir}`);
|
|
@@ -83266,349 +84335,8 @@ async function handleDownload(ctx) {
|
|
|
83266
84335
|
};
|
|
83267
84336
|
}
|
|
83268
84337
|
// 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
|
-
}
|
|
84338
|
+
init_deletion_handler();
|
|
84339
|
+
import { join as join88 } from "node:path";
|
|
83612
84340
|
|
|
83613
84341
|
// src/domains/installation/file-merger.ts
|
|
83614
84342
|
init_logger();
|
|
@@ -83620,9 +84348,10 @@ init_logger();
|
|
|
83620
84348
|
init_types3();
|
|
83621
84349
|
var import_fs_extra12 = __toESM(require_lib3(), 1);
|
|
83622
84350
|
var import_ignore3 = __toESM(require_ignore(), 1);
|
|
83623
|
-
import { dirname as
|
|
84351
|
+
import { dirname as dirname21, join as join78, relative as relative12 } from "node:path";
|
|
83624
84352
|
|
|
83625
84353
|
// src/domains/installation/selective-merger.ts
|
|
84354
|
+
init_manifest_reader();
|
|
83626
84355
|
import { stat as stat13 } from "node:fs/promises";
|
|
83627
84356
|
init_logger();
|
|
83628
84357
|
var import_semver2 = __toESM(require_semver2(), 1);
|
|
@@ -83799,7 +84528,7 @@ init_logger();
|
|
|
83799
84528
|
var import_fs_extra10 = __toESM(require_lib3(), 1);
|
|
83800
84529
|
var import_ignore2 = __toESM(require_ignore(), 1);
|
|
83801
84530
|
import { relative as relative11 } from "node:path";
|
|
83802
|
-
import { join as
|
|
84531
|
+
import { join as join76 } from "node:path";
|
|
83803
84532
|
|
|
83804
84533
|
// node_modules/@isaacs/balanced-match/dist/esm/index.js
|
|
83805
84534
|
var balanced = (a3, b3, str2) => {
|
|
@@ -85255,7 +85984,7 @@ class FileScanner {
|
|
|
85255
85984
|
const files = [];
|
|
85256
85985
|
const entries = await import_fs_extra10.readdir(dir, { encoding: "utf8" });
|
|
85257
85986
|
for (const entry of entries) {
|
|
85258
|
-
const fullPath =
|
|
85987
|
+
const fullPath = join76(dir, entry);
|
|
85259
85988
|
const relativePath = relative11(baseDir, fullPath);
|
|
85260
85989
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
85261
85990
|
const stats = await import_fs_extra10.lstat(fullPath);
|
|
@@ -85295,7 +86024,7 @@ import { execSync as execSync4 } from "node:child_process";
|
|
|
85295
86024
|
init_shared();
|
|
85296
86025
|
import { existsSync as existsSync49 } from "node:fs";
|
|
85297
86026
|
import { mkdir as mkdir26, readFile as readFile38, writeFile as writeFile22 } from "node:fs/promises";
|
|
85298
|
-
import { dirname as
|
|
86027
|
+
import { dirname as dirname20, join as join77 } from "node:path";
|
|
85299
86028
|
var CK_JSON_FILE = ".ck.json";
|
|
85300
86029
|
|
|
85301
86030
|
class InstalledSettingsTracker {
|
|
@@ -85309,9 +86038,9 @@ class InstalledSettingsTracker {
|
|
|
85309
86038
|
}
|
|
85310
86039
|
getCkJsonPath() {
|
|
85311
86040
|
if (this.isGlobal) {
|
|
85312
|
-
return
|
|
86041
|
+
return join77(this.projectDir, CK_JSON_FILE);
|
|
85313
86042
|
}
|
|
85314
|
-
return
|
|
86043
|
+
return join77(this.projectDir, ".claude", CK_JSON_FILE);
|
|
85315
86044
|
}
|
|
85316
86045
|
async loadInstalledSettings() {
|
|
85317
86046
|
const ckJsonPath = this.getCkJsonPath();
|
|
@@ -85346,7 +86075,7 @@ class InstalledSettingsTracker {
|
|
|
85346
86075
|
data.kits[this.kitName] = {};
|
|
85347
86076
|
}
|
|
85348
86077
|
data.kits[this.kitName].installedSettings = settings;
|
|
85349
|
-
await mkdir26(
|
|
86078
|
+
await mkdir26(dirname20(ckJsonPath), { recursive: true });
|
|
85350
86079
|
await writeFile22(ckJsonPath, JSON.stringify(data, null, 2), "utf-8");
|
|
85351
86080
|
logger.debug(`Saved installed settings to ${ckJsonPath}`);
|
|
85352
86081
|
} catch (error) {
|
|
@@ -85864,7 +86593,7 @@ class CopyExecutor {
|
|
|
85864
86593
|
for (const file of files) {
|
|
85865
86594
|
const relativePath = relative12(sourceDir, file);
|
|
85866
86595
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
85867
|
-
const destPath =
|
|
86596
|
+
const destPath = join78(destDir, relativePath);
|
|
85868
86597
|
if (await import_fs_extra12.pathExists(destPath)) {
|
|
85869
86598
|
if (this.fileScanner.shouldNeverCopy(normalizedRelativePath)) {
|
|
85870
86599
|
logger.debug(`Security-sensitive file exists but won't be overwritten: ${normalizedRelativePath}`);
|
|
@@ -85886,7 +86615,7 @@ class CopyExecutor {
|
|
|
85886
86615
|
for (const file of files) {
|
|
85887
86616
|
const relativePath = relative12(sourceDir, file);
|
|
85888
86617
|
const normalizedRelativePath = relativePath.replace(/\\/g, "/");
|
|
85889
|
-
const destPath =
|
|
86618
|
+
const destPath = join78(destDir, relativePath);
|
|
85890
86619
|
if (this.fileScanner.shouldNeverCopy(normalizedRelativePath)) {
|
|
85891
86620
|
logger.debug(`Skipping security-sensitive file: ${normalizedRelativePath}`);
|
|
85892
86621
|
skippedCount++;
|
|
@@ -85963,10 +86692,10 @@ class CopyExecutor {
|
|
|
85963
86692
|
}
|
|
85964
86693
|
trackInstalledFile(relativePath) {
|
|
85965
86694
|
this.installedFiles.add(relativePath);
|
|
85966
|
-
let dir =
|
|
86695
|
+
let dir = dirname21(relativePath);
|
|
85967
86696
|
while (dir && dir !== "." && dir !== "/") {
|
|
85968
86697
|
this.installedDirectories.add(`${dir}/`);
|
|
85969
|
-
dir =
|
|
86698
|
+
dir = dirname21(dir);
|
|
85970
86699
|
}
|
|
85971
86700
|
}
|
|
85972
86701
|
}
|
|
@@ -86056,15 +86785,19 @@ class FileMerger {
|
|
|
86056
86785
|
|
|
86057
86786
|
// src/domains/migration/legacy-migration.ts
|
|
86058
86787
|
import { readdir as readdir18, stat as stat14 } from "node:fs/promises";
|
|
86059
|
-
import { join as
|
|
86788
|
+
import { join as join82, relative as relative13 } from "node:path";
|
|
86789
|
+
|
|
86790
|
+
// src/services/file-operations/manifest/index.ts
|
|
86791
|
+
init_manifest_reader();
|
|
86792
|
+
|
|
86060
86793
|
// src/services/file-operations/manifest/manifest-tracker.ts
|
|
86061
|
-
import { join as
|
|
86794
|
+
import { join as join81 } from "node:path";
|
|
86062
86795
|
|
|
86063
86796
|
// src/domains/migration/release-manifest.ts
|
|
86064
86797
|
init_logger();
|
|
86065
86798
|
init_zod();
|
|
86066
86799
|
var import_fs_extra13 = __toESM(require_lib3(), 1);
|
|
86067
|
-
import { join as
|
|
86800
|
+
import { join as join79 } from "node:path";
|
|
86068
86801
|
var ReleaseManifestFileSchema = exports_external.object({
|
|
86069
86802
|
path: exports_external.string(),
|
|
86070
86803
|
checksum: exports_external.string().regex(/^[a-f0-9]{64}$/),
|
|
@@ -86079,7 +86812,7 @@ var ReleaseManifestSchema = exports_external.object({
|
|
|
86079
86812
|
|
|
86080
86813
|
class ReleaseManifestLoader {
|
|
86081
86814
|
static async load(extractDir) {
|
|
86082
|
-
const manifestPath =
|
|
86815
|
+
const manifestPath = join79(extractDir, "release-manifest.json");
|
|
86083
86816
|
try {
|
|
86084
86817
|
const content = await import_fs_extra13.readFile(manifestPath, "utf-8");
|
|
86085
86818
|
const parsed = JSON.parse(content);
|
|
@@ -86103,11 +86836,12 @@ init_safe_spinner();
|
|
|
86103
86836
|
init_metadata_migration();
|
|
86104
86837
|
init_logger();
|
|
86105
86838
|
init_types3();
|
|
86839
|
+
init_manifest_reader();
|
|
86106
86840
|
var import_fs_extra14 = __toESM(require_lib3(), 1);
|
|
86107
86841
|
var import_proper_lockfile5 = __toESM(require_proper_lockfile(), 1);
|
|
86108
|
-
import { join as
|
|
86842
|
+
import { join as join80 } from "node:path";
|
|
86109
86843
|
async function writeManifest(claudeDir2, kitName, version, scope, kitType, trackedFiles, userConfigFiles) {
|
|
86110
|
-
const metadataPath =
|
|
86844
|
+
const metadataPath = join80(claudeDir2, "metadata.json");
|
|
86111
86845
|
const kit = kitType || (/\bmarketing\b/i.test(kitName) ? "marketing" : "engineer");
|
|
86112
86846
|
await import_fs_extra14.ensureFile(metadataPath);
|
|
86113
86847
|
let release = null;
|
|
@@ -86127,20 +86861,54 @@ async function writeManifest(claudeDir2, kitName, version, scope, kitType, track
|
|
|
86127
86861
|
const content = await import_fs_extra14.readFile(metadataPath, "utf-8");
|
|
86128
86862
|
const parsed = JSON.parse(content);
|
|
86129
86863
|
if (parsed && typeof parsed === "object" && Object.keys(parsed).length > 0) {
|
|
86130
|
-
|
|
86864
|
+
const validatedExisting = MetadataSchema.safeParse(parsed);
|
|
86865
|
+
if (validatedExisting.success) {
|
|
86866
|
+
existingMetadata = validatedExisting.data;
|
|
86867
|
+
} else {
|
|
86868
|
+
logger.warning("Existing metadata.json is invalid; preserving recoverable fields and rebuilding the rest");
|
|
86869
|
+
const raw2 = parsed;
|
|
86870
|
+
const recoveredKits = {};
|
|
86871
|
+
if (raw2.kits && typeof raw2.kits === "object") {
|
|
86872
|
+
for (const [rawKitName, rawKitValue] of Object.entries(raw2.kits)) {
|
|
86873
|
+
if ((rawKitName === "engineer" || rawKitName === "marketing") && rawKitValue && typeof rawKitValue === "object") {
|
|
86874
|
+
const recoveredKit = rawKitValue;
|
|
86875
|
+
if (typeof recoveredKit.version === "string" && typeof recoveredKit.installedAt === "string") {
|
|
86876
|
+
recoveredKits[rawKitName] = recoveredKit;
|
|
86877
|
+
}
|
|
86878
|
+
}
|
|
86879
|
+
}
|
|
86880
|
+
}
|
|
86881
|
+
existingMetadata = {
|
|
86882
|
+
kits: recoveredKits,
|
|
86883
|
+
scope: raw2.scope === "local" || raw2.scope === "global" ? raw2.scope : undefined,
|
|
86884
|
+
name: typeof raw2.name === "string" ? raw2.name : undefined,
|
|
86885
|
+
version: typeof raw2.version === "string" ? raw2.version : undefined,
|
|
86886
|
+
installedAt: typeof raw2.installedAt === "string" ? raw2.installedAt : undefined,
|
|
86887
|
+
userConfigFiles: Array.isArray(raw2.userConfigFiles) ? raw2.userConfigFiles.filter((entry) => typeof entry === "string") : undefined
|
|
86888
|
+
};
|
|
86889
|
+
}
|
|
86131
86890
|
}
|
|
86132
86891
|
} catch (error) {
|
|
86133
86892
|
logger.debug(`Could not read existing metadata: ${error}`);
|
|
86134
86893
|
}
|
|
86135
86894
|
}
|
|
86136
86895
|
const installedAt = new Date().toISOString();
|
|
86896
|
+
const existingKits = existingMetadata.kits || {};
|
|
86897
|
+
const existingKitMetadata = existingKits[kit];
|
|
86137
86898
|
const kitMetadata = {
|
|
86899
|
+
...existingKitMetadata,
|
|
86138
86900
|
version,
|
|
86139
86901
|
installedAt,
|
|
86140
86902
|
files: trackedFiles.length > 0 ? trackedFiles : undefined
|
|
86141
86903
|
};
|
|
86142
|
-
const existingKits = existingMetadata.kits || {};
|
|
86143
86904
|
const otherKitsExist = Object.keys(existingKits).some((k2) => k2 !== kit);
|
|
86905
|
+
const mergedUserConfigFiles = [
|
|
86906
|
+
...new Set([
|
|
86907
|
+
...existingMetadata.userConfigFiles || [],
|
|
86908
|
+
...USER_CONFIG_PATTERNS,
|
|
86909
|
+
...userConfigFiles
|
|
86910
|
+
])
|
|
86911
|
+
];
|
|
86144
86912
|
const metadata = {
|
|
86145
86913
|
kits: {
|
|
86146
86914
|
...existingKits,
|
|
@@ -86150,7 +86918,7 @@ async function writeManifest(claudeDir2, kitName, version, scope, kitType, track
|
|
|
86150
86918
|
name: otherKitsExist ? existingMetadata.name ?? kitName : kitName,
|
|
86151
86919
|
version: otherKitsExist ? existingMetadata.version ?? version : version,
|
|
86152
86920
|
installedAt: otherKitsExist ? existingMetadata.installedAt ?? installedAt : installedAt,
|
|
86153
|
-
userConfigFiles:
|
|
86921
|
+
userConfigFiles: mergedUserConfigFiles
|
|
86154
86922
|
};
|
|
86155
86923
|
const validated = MetadataSchema.parse(metadata);
|
|
86156
86924
|
await import_fs_extra14.writeFile(metadataPath, JSON.stringify(validated, null, 2), "utf-8");
|
|
@@ -86163,7 +86931,7 @@ async function writeManifest(claudeDir2, kitName, version, scope, kitType, track
|
|
|
86163
86931
|
}
|
|
86164
86932
|
}
|
|
86165
86933
|
async function removeKitFromManifest(claudeDir2, kit) {
|
|
86166
|
-
const metadataPath =
|
|
86934
|
+
const metadataPath = join80(claudeDir2, "metadata.json");
|
|
86167
86935
|
if (!await import_fs_extra14.pathExists(metadataPath))
|
|
86168
86936
|
return false;
|
|
86169
86937
|
let release = null;
|
|
@@ -86293,7 +87061,7 @@ function buildFileTrackingList(options2) {
|
|
|
86293
87061
|
if (!isGlobal && !installedPath.startsWith(".claude/"))
|
|
86294
87062
|
continue;
|
|
86295
87063
|
const relativePath = isGlobal ? installedPath : installedPath.replace(/^\.claude\//, "");
|
|
86296
|
-
const filePath =
|
|
87064
|
+
const filePath = join81(claudeDir2, relativePath);
|
|
86297
87065
|
const manifestEntry = releaseManifest ? ReleaseManifestLoader.findFile(releaseManifest, installedPath) : null;
|
|
86298
87066
|
const ownership = manifestEntry ? "ck" : "user";
|
|
86299
87067
|
filesToTrack.push({
|
|
@@ -86400,7 +87168,7 @@ class LegacyMigration {
|
|
|
86400
87168
|
continue;
|
|
86401
87169
|
if (SKIP_DIRS_ALL.includes(entry))
|
|
86402
87170
|
continue;
|
|
86403
|
-
const fullPath =
|
|
87171
|
+
const fullPath = join82(dir, entry);
|
|
86404
87172
|
let stats;
|
|
86405
87173
|
try {
|
|
86406
87174
|
stats = await stat14(fullPath);
|
|
@@ -86502,7 +87270,7 @@ User-created files (sample):`);
|
|
|
86502
87270
|
];
|
|
86503
87271
|
if (filesToChecksum.length > 0) {
|
|
86504
87272
|
const checksumResults = await mapWithLimit(filesToChecksum, async ({ relativePath, ownership }) => {
|
|
86505
|
-
const fullPath =
|
|
87273
|
+
const fullPath = join82(claudeDir2, relativePath);
|
|
86506
87274
|
const checksum = await OwnershipChecker.calculateChecksum(fullPath);
|
|
86507
87275
|
return { relativePath, checksum, ownership };
|
|
86508
87276
|
});
|
|
@@ -86523,7 +87291,7 @@ User-created files (sample):`);
|
|
|
86523
87291
|
installedAt: new Date().toISOString(),
|
|
86524
87292
|
files: trackedFiles
|
|
86525
87293
|
};
|
|
86526
|
-
const metadataPath =
|
|
87294
|
+
const metadataPath = join82(claudeDir2, "metadata.json");
|
|
86527
87295
|
await import_fs_extra15.writeFile(metadataPath, JSON.stringify(updatedMetadata, null, 2));
|
|
86528
87296
|
logger.success(`Migration complete: tracked ${trackedFiles.length} files`);
|
|
86529
87297
|
return true;
|
|
@@ -86629,7 +87397,7 @@ function buildConflictSummary(fileConflicts, hookConflicts, mcpConflicts) {
|
|
|
86629
87397
|
init_logger();
|
|
86630
87398
|
init_skip_directories();
|
|
86631
87399
|
var import_fs_extra16 = __toESM(require_lib3(), 1);
|
|
86632
|
-
import { join as
|
|
87400
|
+
import { join as join83, relative as relative14, resolve as resolve19 } from "node:path";
|
|
86633
87401
|
|
|
86634
87402
|
class FileScanner2 {
|
|
86635
87403
|
static async getFiles(dirPath, relativeTo) {
|
|
@@ -86645,7 +87413,7 @@ class FileScanner2 {
|
|
|
86645
87413
|
logger.debug(`Skipping directory: ${entry}`);
|
|
86646
87414
|
continue;
|
|
86647
87415
|
}
|
|
86648
|
-
const fullPath =
|
|
87416
|
+
const fullPath = join83(dirPath, entry);
|
|
86649
87417
|
if (!FileScanner2.isSafePath(basePath, fullPath)) {
|
|
86650
87418
|
logger.warning(`Skipping potentially unsafe path: ${entry}`);
|
|
86651
87419
|
continue;
|
|
@@ -86680,8 +87448,8 @@ class FileScanner2 {
|
|
|
86680
87448
|
return files;
|
|
86681
87449
|
}
|
|
86682
87450
|
static async findCustomFiles(destDir, sourceDir, subPath) {
|
|
86683
|
-
const destSubDir =
|
|
86684
|
-
const sourceSubDir =
|
|
87451
|
+
const destSubDir = join83(destDir, subPath);
|
|
87452
|
+
const sourceSubDir = join83(sourceDir, subPath);
|
|
86685
87453
|
logger.debug(`findCustomFiles - destDir: ${destDir}`);
|
|
86686
87454
|
logger.debug(`findCustomFiles - sourceDir: ${sourceDir}`);
|
|
86687
87455
|
logger.debug(`findCustomFiles - subPath: "${subPath}"`);
|
|
@@ -86709,8 +87477,8 @@ class FileScanner2 {
|
|
|
86709
87477
|
return customFiles;
|
|
86710
87478
|
}
|
|
86711
87479
|
static isSafePath(basePath, targetPath) {
|
|
86712
|
-
const resolvedBase =
|
|
86713
|
-
const resolvedTarget =
|
|
87480
|
+
const resolvedBase = resolve19(basePath);
|
|
87481
|
+
const resolvedTarget = resolve19(targetPath);
|
|
86714
87482
|
return resolvedTarget.startsWith(resolvedBase);
|
|
86715
87483
|
}
|
|
86716
87484
|
static toPosixPath(path14) {
|
|
@@ -86722,12 +87490,12 @@ class FileScanner2 {
|
|
|
86722
87490
|
init_logger();
|
|
86723
87491
|
var import_fs_extra17 = __toESM(require_lib3(), 1);
|
|
86724
87492
|
import { lstat as lstat7, mkdir as mkdir27, readdir as readdir21, stat as stat15 } from "node:fs/promises";
|
|
86725
|
-
import { join as
|
|
87493
|
+
import { join as join85 } from "node:path";
|
|
86726
87494
|
|
|
86727
87495
|
// src/services/transformers/commands-prefix/content-transformer.ts
|
|
86728
87496
|
init_logger();
|
|
86729
87497
|
import { readFile as readFile42, readdir as readdir20, writeFile as writeFile26 } from "node:fs/promises";
|
|
86730
|
-
import { join as
|
|
87498
|
+
import { join as join84 } from "node:path";
|
|
86731
87499
|
var TRANSFORMABLE_EXTENSIONS = new Set([
|
|
86732
87500
|
".md",
|
|
86733
87501
|
".txt",
|
|
@@ -86788,7 +87556,7 @@ async function transformCommandReferences(directory, options2 = {}) {
|
|
|
86788
87556
|
async function processDirectory(dir) {
|
|
86789
87557
|
const entries = await readdir20(dir, { withFileTypes: true });
|
|
86790
87558
|
for (const entry of entries) {
|
|
86791
|
-
const fullPath =
|
|
87559
|
+
const fullPath = join84(dir, entry.name);
|
|
86792
87560
|
if (entry.isDirectory()) {
|
|
86793
87561
|
if (entry.name === "node_modules" || entry.name.startsWith(".") && entry.name !== ".claude") {
|
|
86794
87562
|
continue;
|
|
@@ -86863,14 +87631,14 @@ function shouldApplyPrefix(options2) {
|
|
|
86863
87631
|
// src/services/transformers/commands-prefix/prefix-applier.ts
|
|
86864
87632
|
async function applyPrefix(extractDir) {
|
|
86865
87633
|
validatePath(extractDir, "extractDir");
|
|
86866
|
-
const commandsDir =
|
|
87634
|
+
const commandsDir = join85(extractDir, ".claude", "commands");
|
|
86867
87635
|
if (!await import_fs_extra17.pathExists(commandsDir)) {
|
|
86868
87636
|
logger.verbose("No commands directory found, skipping prefix application");
|
|
86869
87637
|
return;
|
|
86870
87638
|
}
|
|
86871
87639
|
logger.info("Applying /ck: prefix to slash commands...");
|
|
86872
|
-
const backupDir =
|
|
86873
|
-
const tempDir =
|
|
87640
|
+
const backupDir = join85(extractDir, ".commands-backup");
|
|
87641
|
+
const tempDir = join85(extractDir, ".commands-prefix-temp");
|
|
86874
87642
|
try {
|
|
86875
87643
|
const entries = await readdir21(commandsDir);
|
|
86876
87644
|
if (entries.length === 0) {
|
|
@@ -86878,7 +87646,7 @@ async function applyPrefix(extractDir) {
|
|
|
86878
87646
|
return;
|
|
86879
87647
|
}
|
|
86880
87648
|
if (entries.length === 1 && entries[0] === "ck") {
|
|
86881
|
-
const ckDir2 =
|
|
87649
|
+
const ckDir2 = join85(commandsDir, "ck");
|
|
86882
87650
|
const ckStat = await stat15(ckDir2);
|
|
86883
87651
|
if (ckStat.isDirectory()) {
|
|
86884
87652
|
logger.verbose("Commands already have /ck: prefix, skipping");
|
|
@@ -86888,17 +87656,17 @@ async function applyPrefix(extractDir) {
|
|
|
86888
87656
|
await import_fs_extra17.copy(commandsDir, backupDir);
|
|
86889
87657
|
logger.verbose("Created backup of commands directory");
|
|
86890
87658
|
await mkdir27(tempDir, { recursive: true });
|
|
86891
|
-
const ckDir =
|
|
87659
|
+
const ckDir = join85(tempDir, "ck");
|
|
86892
87660
|
await mkdir27(ckDir, { recursive: true });
|
|
86893
87661
|
let processedCount = 0;
|
|
86894
87662
|
for (const entry of entries) {
|
|
86895
|
-
const sourcePath =
|
|
87663
|
+
const sourcePath = join85(commandsDir, entry);
|
|
86896
87664
|
const stats = await lstat7(sourcePath);
|
|
86897
87665
|
if (stats.isSymbolicLink()) {
|
|
86898
87666
|
logger.warning(`Skipping symlink for security: ${entry}`);
|
|
86899
87667
|
continue;
|
|
86900
87668
|
}
|
|
86901
|
-
const destPath =
|
|
87669
|
+
const destPath = join85(ckDir, entry);
|
|
86902
87670
|
await import_fs_extra17.copy(sourcePath, destPath, {
|
|
86903
87671
|
overwrite: false,
|
|
86904
87672
|
errorOnExist: true
|
|
@@ -86916,7 +87684,7 @@ async function applyPrefix(extractDir) {
|
|
|
86916
87684
|
await import_fs_extra17.move(tempDir, commandsDir);
|
|
86917
87685
|
await import_fs_extra17.remove(backupDir);
|
|
86918
87686
|
logger.success("Successfully reorganized commands to /ck: prefix");
|
|
86919
|
-
const claudeDir2 =
|
|
87687
|
+
const claudeDir2 = join85(extractDir, ".claude");
|
|
86920
87688
|
logger.info("Transforming command references in file contents...");
|
|
86921
87689
|
const transformResult = await transformCommandReferences(claudeDir2, {
|
|
86922
87690
|
verbose: logger.isVerbose()
|
|
@@ -86954,20 +87722,20 @@ async function applyPrefix(extractDir) {
|
|
|
86954
87722
|
// src/services/transformers/commands-prefix/prefix-cleaner.ts
|
|
86955
87723
|
init_metadata_migration();
|
|
86956
87724
|
import { lstat as lstat9, readdir as readdir23 } from "node:fs/promises";
|
|
86957
|
-
import { join as
|
|
87725
|
+
import { join as join87 } from "node:path";
|
|
86958
87726
|
init_logger();
|
|
86959
87727
|
var import_fs_extra19 = __toESM(require_lib3(), 1);
|
|
86960
87728
|
|
|
86961
87729
|
// src/services/transformers/commands-prefix/file-processor.ts
|
|
86962
87730
|
import { lstat as lstat8, readdir as readdir22 } from "node:fs/promises";
|
|
86963
|
-
import { join as
|
|
87731
|
+
import { join as join86 } from "node:path";
|
|
86964
87732
|
init_logger();
|
|
86965
87733
|
var import_fs_extra18 = __toESM(require_lib3(), 1);
|
|
86966
87734
|
async function scanDirectoryFiles(dir) {
|
|
86967
87735
|
const files = [];
|
|
86968
87736
|
const entries = await readdir22(dir);
|
|
86969
87737
|
for (const entry of entries) {
|
|
86970
|
-
const fullPath =
|
|
87738
|
+
const fullPath = join86(dir, entry);
|
|
86971
87739
|
const stats = await lstat8(fullPath);
|
|
86972
87740
|
if (stats.isSymbolicLink()) {
|
|
86973
87741
|
continue;
|
|
@@ -87095,8 +87863,8 @@ function isDifferentKitDirectory(dirName, currentKit) {
|
|
|
87095
87863
|
async function cleanupCommandsDirectory(targetDir, isGlobal, options2 = {}) {
|
|
87096
87864
|
const { dryRun = false } = options2;
|
|
87097
87865
|
validatePath(targetDir, "targetDir");
|
|
87098
|
-
const claudeDir2 = isGlobal ? targetDir :
|
|
87099
|
-
const commandsDir =
|
|
87866
|
+
const claudeDir2 = isGlobal ? targetDir : join87(targetDir, ".claude");
|
|
87867
|
+
const commandsDir = join87(claudeDir2, "commands");
|
|
87100
87868
|
const accumulator = {
|
|
87101
87869
|
results: [],
|
|
87102
87870
|
deletedCount: 0,
|
|
@@ -87138,7 +87906,7 @@ async function cleanupCommandsDirectory(targetDir, isGlobal, options2 = {}) {
|
|
|
87138
87906
|
}
|
|
87139
87907
|
const metadataForChecks = options2.kitType ? createKitSpecificMetadata(metadata, options2.kitType) : metadata;
|
|
87140
87908
|
for (const entry of entries) {
|
|
87141
|
-
const entryPath =
|
|
87909
|
+
const entryPath = join87(commandsDir, entry);
|
|
87142
87910
|
const stats = await lstat9(entryPath);
|
|
87143
87911
|
if (stats.isSymbolicLink()) {
|
|
87144
87912
|
addSymlinkSkip(entry, accumulator);
|
|
@@ -87195,7 +87963,7 @@ async function handleMerge(ctx) {
|
|
|
87195
87963
|
let customClaudeFiles = [];
|
|
87196
87964
|
if (!ctx.options.fresh) {
|
|
87197
87965
|
logger.info("Scanning for custom .claude files...");
|
|
87198
|
-
const scanSourceDir = ctx.options.global ?
|
|
87966
|
+
const scanSourceDir = ctx.options.global ? join88(ctx.extractDir, ".claude") : ctx.extractDir;
|
|
87199
87967
|
const scanTargetSubdir = ctx.options.global ? "" : ".claude";
|
|
87200
87968
|
customClaudeFiles = await FileScanner2.findCustomFiles(ctx.resolvedDir, scanSourceDir, scanTargetSubdir);
|
|
87201
87969
|
} else {
|
|
@@ -87260,28 +88028,38 @@ async function handleMerge(ctx) {
|
|
|
87260
88028
|
return { ...ctx, cancelled: true };
|
|
87261
88029
|
}
|
|
87262
88030
|
}
|
|
87263
|
-
const sourceDir = ctx.options.global ?
|
|
88031
|
+
const sourceDir = ctx.options.global ? join88(ctx.extractDir, ".claude") : ctx.extractDir;
|
|
87264
88032
|
await merger.merge(sourceDir, ctx.resolvedDir, ctx.isNonInteractive);
|
|
87265
88033
|
const fileConflicts = merger.getFileConflicts();
|
|
87266
88034
|
if (fileConflicts.length > 0 && !ctx.isNonInteractive) {
|
|
87267
88035
|
const summary = buildConflictSummary(fileConflicts, [], []);
|
|
87268
88036
|
displayConflictSummary(summary);
|
|
87269
88037
|
}
|
|
88038
|
+
let deferredDeletions = [];
|
|
87270
88039
|
try {
|
|
87271
|
-
const sourceMetadataPath = ctx.options.global ?
|
|
88040
|
+
const sourceMetadataPath = ctx.options.global ? join88(sourceDir, "metadata.json") : join88(sourceDir, ".claude", "metadata.json");
|
|
87272
88041
|
if (await import_fs_extra20.pathExists(sourceMetadataPath)) {
|
|
87273
88042
|
const metadataContent = await import_fs_extra20.readFile(sourceMetadataPath, "utf-8");
|
|
87274
88043
|
const sourceMetadata = JSON.parse(metadataContent);
|
|
87275
88044
|
if (sourceMetadata.deletions && sourceMetadata.deletions.length > 0) {
|
|
87276
|
-
const
|
|
87277
|
-
|
|
87278
|
-
|
|
87279
|
-
|
|
87280
|
-
|
|
88045
|
+
const { categorizeDeletions: categorizeDeletions2 } = await Promise.resolve().then(() => (init_deletion_handler(), exports_deletion_handler));
|
|
88046
|
+
const categorized = categorizeDeletions2(sourceMetadata.deletions);
|
|
88047
|
+
if (categorized.immediate.length > 0) {
|
|
88048
|
+
const immediateMetadata = { ...sourceMetadata, deletions: categorized.immediate };
|
|
88049
|
+
const deletionResult = await handleDeletions(immediateMetadata, ctx.claudeDir);
|
|
88050
|
+
if (deletionResult.deletedPaths.length > 0) {
|
|
88051
|
+
logger.info(`Removed ${deletionResult.deletedPaths.length} deprecated file(s)`);
|
|
88052
|
+
for (const path14 of deletionResult.deletedPaths) {
|
|
88053
|
+
logger.verbose(` - ${path14}`);
|
|
88054
|
+
}
|
|
88055
|
+
}
|
|
88056
|
+
if (deletionResult.preservedPaths.length > 0) {
|
|
88057
|
+
logger.verbose(`Preserved ${deletionResult.preservedPaths.length} user-owned file(s)`);
|
|
87281
88058
|
}
|
|
87282
88059
|
}
|
|
87283
|
-
if (
|
|
87284
|
-
|
|
88060
|
+
if (categorized.deferred.length > 0) {
|
|
88061
|
+
deferredDeletions = categorized.deferred;
|
|
88062
|
+
logger.debug(`Deferred ${categorized.deferred.length} skill deletion(s) to post-install phase`);
|
|
87285
88063
|
}
|
|
87286
88064
|
}
|
|
87287
88065
|
} else {
|
|
@@ -87308,11 +88086,12 @@ async function handleMerge(ctx) {
|
|
|
87308
88086
|
return {
|
|
87309
88087
|
...ctx,
|
|
87310
88088
|
customClaudeFiles,
|
|
87311
|
-
includePatterns
|
|
88089
|
+
includePatterns,
|
|
88090
|
+
deferredDeletions
|
|
87312
88091
|
};
|
|
87313
88092
|
}
|
|
87314
88093
|
// src/commands/init/phases/migration-handler.ts
|
|
87315
|
-
import { join as
|
|
88094
|
+
import { join as join96 } from "node:path";
|
|
87316
88095
|
|
|
87317
88096
|
// src/domains/skills/skills-detector.ts
|
|
87318
88097
|
init_logger();
|
|
@@ -87328,7 +88107,7 @@ init_types3();
|
|
|
87328
88107
|
var import_fs_extra21 = __toESM(require_lib3(), 1);
|
|
87329
88108
|
import { createHash as createHash4 } from "node:crypto";
|
|
87330
88109
|
import { readFile as readFile44, readdir as readdir24, writeFile as writeFile27 } from "node:fs/promises";
|
|
87331
|
-
import { join as
|
|
88110
|
+
import { join as join89, relative as relative15 } from "node:path";
|
|
87332
88111
|
|
|
87333
88112
|
class SkillsManifestManager {
|
|
87334
88113
|
static MANIFEST_FILENAME = ".skills-manifest.json";
|
|
@@ -87350,12 +88129,12 @@ class SkillsManifestManager {
|
|
|
87350
88129
|
return manifest;
|
|
87351
88130
|
}
|
|
87352
88131
|
static async writeManifest(skillsDir2, manifest) {
|
|
87353
|
-
const manifestPath =
|
|
88132
|
+
const manifestPath = join89(skillsDir2, SkillsManifestManager.MANIFEST_FILENAME);
|
|
87354
88133
|
await writeFile27(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
87355
88134
|
logger.debug(`Wrote manifest to: ${manifestPath}`);
|
|
87356
88135
|
}
|
|
87357
88136
|
static async readManifest(skillsDir2) {
|
|
87358
|
-
const manifestPath =
|
|
88137
|
+
const manifestPath = join89(skillsDir2, SkillsManifestManager.MANIFEST_FILENAME);
|
|
87359
88138
|
if (!await import_fs_extra21.pathExists(manifestPath)) {
|
|
87360
88139
|
logger.debug(`No manifest found at: ${manifestPath}`);
|
|
87361
88140
|
return null;
|
|
@@ -87378,7 +88157,7 @@ class SkillsManifestManager {
|
|
|
87378
88157
|
return "flat";
|
|
87379
88158
|
}
|
|
87380
88159
|
for (const dir of dirs.slice(0, 3)) {
|
|
87381
|
-
const dirPath =
|
|
88160
|
+
const dirPath = join89(skillsDir2, dir.name);
|
|
87382
88161
|
const subEntries = await readdir24(dirPath, { withFileTypes: true });
|
|
87383
88162
|
const hasSubdirs = subEntries.some((entry) => entry.isDirectory());
|
|
87384
88163
|
if (hasSubdirs) {
|
|
@@ -87397,7 +88176,7 @@ class SkillsManifestManager {
|
|
|
87397
88176
|
const entries = await readdir24(skillsDir2, { withFileTypes: true });
|
|
87398
88177
|
for (const entry of entries) {
|
|
87399
88178
|
if (entry.isDirectory() && !BUILD_ARTIFACT_DIRS.includes(entry.name) && !entry.name.startsWith(".")) {
|
|
87400
|
-
const skillPath =
|
|
88179
|
+
const skillPath = join89(skillsDir2, entry.name);
|
|
87401
88180
|
const hash = await SkillsManifestManager.hashDirectory(skillPath);
|
|
87402
88181
|
skills.push({
|
|
87403
88182
|
name: entry.name,
|
|
@@ -87409,11 +88188,11 @@ class SkillsManifestManager {
|
|
|
87409
88188
|
const categories = await readdir24(skillsDir2, { withFileTypes: true });
|
|
87410
88189
|
for (const category of categories) {
|
|
87411
88190
|
if (category.isDirectory() && !BUILD_ARTIFACT_DIRS.includes(category.name) && !category.name.startsWith(".")) {
|
|
87412
|
-
const categoryPath =
|
|
88191
|
+
const categoryPath = join89(skillsDir2, category.name);
|
|
87413
88192
|
const skillEntries = await readdir24(categoryPath, { withFileTypes: true });
|
|
87414
88193
|
for (const skillEntry of skillEntries) {
|
|
87415
88194
|
if (skillEntry.isDirectory() && !skillEntry.name.startsWith(".")) {
|
|
87416
|
-
const skillPath =
|
|
88195
|
+
const skillPath = join89(categoryPath, skillEntry.name);
|
|
87417
88196
|
const hash = await SkillsManifestManager.hashDirectory(skillPath);
|
|
87418
88197
|
skills.push({
|
|
87419
88198
|
name: skillEntry.name,
|
|
@@ -87443,7 +88222,7 @@ class SkillsManifestManager {
|
|
|
87443
88222
|
const files = [];
|
|
87444
88223
|
const entries = await readdir24(dirPath, { withFileTypes: true });
|
|
87445
88224
|
for (const entry of entries) {
|
|
87446
|
-
const fullPath =
|
|
88225
|
+
const fullPath = join89(dirPath, entry.name);
|
|
87447
88226
|
if (entry.name.startsWith(".") || BUILD_ARTIFACT_DIRS.includes(entry.name)) {
|
|
87448
88227
|
continue;
|
|
87449
88228
|
}
|
|
@@ -87565,7 +88344,7 @@ function getPathMapping(skillName, oldBasePath, newBasePath) {
|
|
|
87565
88344
|
// src/domains/skills/detection/script-detector.ts
|
|
87566
88345
|
var import_fs_extra22 = __toESM(require_lib3(), 1);
|
|
87567
88346
|
import { readdir as readdir25 } from "node:fs/promises";
|
|
87568
|
-
import { join as
|
|
88347
|
+
import { join as join90 } from "node:path";
|
|
87569
88348
|
async function scanDirectory(skillsDir2) {
|
|
87570
88349
|
if (!await import_fs_extra22.pathExists(skillsDir2)) {
|
|
87571
88350
|
return ["flat", []];
|
|
@@ -87578,12 +88357,12 @@ async function scanDirectory(skillsDir2) {
|
|
|
87578
88357
|
let totalSkillLikeCount = 0;
|
|
87579
88358
|
const allSkills = [];
|
|
87580
88359
|
for (const dir of dirs) {
|
|
87581
|
-
const dirPath =
|
|
88360
|
+
const dirPath = join90(skillsDir2, dir.name);
|
|
87582
88361
|
const subEntries = await readdir25(dirPath, { withFileTypes: true });
|
|
87583
88362
|
const subdirs = subEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
|
|
87584
88363
|
if (subdirs.length > 0) {
|
|
87585
88364
|
for (const subdir of subdirs.slice(0, 3)) {
|
|
87586
|
-
const subdirPath =
|
|
88365
|
+
const subdirPath = join90(dirPath, subdir.name);
|
|
87587
88366
|
const subdirFiles = await readdir25(subdirPath, { withFileTypes: true });
|
|
87588
88367
|
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
88368
|
if (hasSkillMarker) {
|
|
@@ -87740,12 +88519,12 @@ class SkillsMigrationDetector {
|
|
|
87740
88519
|
// src/domains/skills/skills-migrator.ts
|
|
87741
88520
|
init_logger();
|
|
87742
88521
|
init_types3();
|
|
87743
|
-
import { join as
|
|
88522
|
+
import { join as join95 } from "node:path";
|
|
87744
88523
|
|
|
87745
88524
|
// src/domains/skills/migrator/migration-executor.ts
|
|
87746
88525
|
init_logger();
|
|
87747
88526
|
import { copyFile as copyFile6, mkdir as mkdir28, readdir as readdir26, rm as rm10 } from "node:fs/promises";
|
|
87748
|
-
import { join as
|
|
88527
|
+
import { join as join91 } from "node:path";
|
|
87749
88528
|
var import_fs_extra24 = __toESM(require_lib3(), 1);
|
|
87750
88529
|
|
|
87751
88530
|
// src/domains/skills/skills-migration-prompts.ts
|
|
@@ -87910,8 +88689,8 @@ async function copySkillDirectory(sourceDir, destDir) {
|
|
|
87910
88689
|
await mkdir28(destDir, { recursive: true });
|
|
87911
88690
|
const entries = await readdir26(sourceDir, { withFileTypes: true });
|
|
87912
88691
|
for (const entry of entries) {
|
|
87913
|
-
const sourcePath =
|
|
87914
|
-
const destPath =
|
|
88692
|
+
const sourcePath = join91(sourceDir, entry.name);
|
|
88693
|
+
const destPath = join91(destDir, entry.name);
|
|
87915
88694
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
|
|
87916
88695
|
continue;
|
|
87917
88696
|
}
|
|
@@ -87926,7 +88705,7 @@ async function executeInternal(mappings, customizations, currentSkillsDir, inter
|
|
|
87926
88705
|
const migrated = [];
|
|
87927
88706
|
const preserved = [];
|
|
87928
88707
|
const errors2 = [];
|
|
87929
|
-
const tempDir =
|
|
88708
|
+
const tempDir = join91(currentSkillsDir, "..", ".skills-migration-temp");
|
|
87930
88709
|
await mkdir28(tempDir, { recursive: true });
|
|
87931
88710
|
try {
|
|
87932
88711
|
for (const mapping of mappings) {
|
|
@@ -87947,9 +88726,9 @@ async function executeInternal(mappings, customizations, currentSkillsDir, inter
|
|
|
87947
88726
|
}
|
|
87948
88727
|
}
|
|
87949
88728
|
const category = mapping.category;
|
|
87950
|
-
const targetPath = category ?
|
|
88729
|
+
const targetPath = category ? join91(tempDir, category, skillName) : join91(tempDir, skillName);
|
|
87951
88730
|
if (category) {
|
|
87952
|
-
await mkdir28(
|
|
88731
|
+
await mkdir28(join91(tempDir, category), { recursive: true });
|
|
87953
88732
|
}
|
|
87954
88733
|
await copySkillDirectory(currentSkillPath, targetPath);
|
|
87955
88734
|
migrated.push(skillName);
|
|
@@ -88016,7 +88795,7 @@ init_logger();
|
|
|
88016
88795
|
init_types3();
|
|
88017
88796
|
var import_fs_extra25 = __toESM(require_lib3(), 1);
|
|
88018
88797
|
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
|
|
88798
|
+
import { basename as basename10, join as join92, normalize as normalize8 } from "node:path";
|
|
88020
88799
|
function validatePath2(path14, paramName) {
|
|
88021
88800
|
if (!path14 || typeof path14 !== "string") {
|
|
88022
88801
|
throw new SkillsMigrationError(`${paramName} must be a non-empty string`);
|
|
@@ -88042,7 +88821,7 @@ class SkillsBackupManager {
|
|
|
88042
88821
|
const timestamp = Date.now();
|
|
88043
88822
|
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
88044
88823
|
const backupDirName = `${SkillsBackupManager.BACKUP_PREFIX}${timestamp}-${randomSuffix}`;
|
|
88045
|
-
const backupDir = parentDir ?
|
|
88824
|
+
const backupDir = parentDir ? join92(parentDir, backupDirName) : join92(skillsDir2, "..", backupDirName);
|
|
88046
88825
|
logger.info(`Creating backup at: ${backupDir}`);
|
|
88047
88826
|
try {
|
|
88048
88827
|
await mkdir29(backupDir, { recursive: true });
|
|
@@ -88093,7 +88872,7 @@ class SkillsBackupManager {
|
|
|
88093
88872
|
}
|
|
88094
88873
|
try {
|
|
88095
88874
|
const entries = await readdir27(parentDir, { withFileTypes: true });
|
|
88096
|
-
const backups = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(SkillsBackupManager.BACKUP_PREFIX)).map((entry) =>
|
|
88875
|
+
const backups = entries.filter((entry) => entry.isDirectory() && entry.name.startsWith(SkillsBackupManager.BACKUP_PREFIX)).map((entry) => join92(parentDir, entry.name));
|
|
88097
88876
|
backups.sort().reverse();
|
|
88098
88877
|
return backups;
|
|
88099
88878
|
} catch (error) {
|
|
@@ -88121,8 +88900,8 @@ class SkillsBackupManager {
|
|
|
88121
88900
|
static async copyDirectory(sourceDir, destDir) {
|
|
88122
88901
|
const entries = await readdir27(sourceDir, { withFileTypes: true });
|
|
88123
88902
|
for (const entry of entries) {
|
|
88124
|
-
const sourcePath =
|
|
88125
|
-
const destPath =
|
|
88903
|
+
const sourcePath = join92(sourceDir, entry.name);
|
|
88904
|
+
const destPath = join92(destDir, entry.name);
|
|
88126
88905
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink()) {
|
|
88127
88906
|
continue;
|
|
88128
88907
|
}
|
|
@@ -88138,7 +88917,7 @@ class SkillsBackupManager {
|
|
|
88138
88917
|
let size = 0;
|
|
88139
88918
|
const entries = await readdir27(dirPath, { withFileTypes: true });
|
|
88140
88919
|
for (const entry of entries) {
|
|
88141
|
-
const fullPath =
|
|
88920
|
+
const fullPath = join92(dirPath, entry.name);
|
|
88142
88921
|
if (entry.isSymbolicLink()) {
|
|
88143
88922
|
continue;
|
|
88144
88923
|
}
|
|
@@ -88174,12 +88953,12 @@ init_skip_directories();
|
|
|
88174
88953
|
import { createHash as createHash5 } from "node:crypto";
|
|
88175
88954
|
import { createReadStream as createReadStream3 } from "node:fs";
|
|
88176
88955
|
import { readFile as readFile45, readdir as readdir28 } from "node:fs/promises";
|
|
88177
|
-
import { join as
|
|
88956
|
+
import { join as join93, relative as relative16 } from "node:path";
|
|
88178
88957
|
async function getAllFiles(dirPath) {
|
|
88179
88958
|
const files = [];
|
|
88180
88959
|
const entries = await readdir28(dirPath, { withFileTypes: true });
|
|
88181
88960
|
for (const entry of entries) {
|
|
88182
|
-
const fullPath =
|
|
88961
|
+
const fullPath = join93(dirPath, entry.name);
|
|
88183
88962
|
if (entry.name.startsWith(".") || BUILD_ARTIFACT_DIRS.includes(entry.name) || entry.isSymbolicLink()) {
|
|
88184
88963
|
continue;
|
|
88185
88964
|
}
|
|
@@ -88193,12 +88972,12 @@ async function getAllFiles(dirPath) {
|
|
|
88193
88972
|
return files;
|
|
88194
88973
|
}
|
|
88195
88974
|
async function hashFile(filePath) {
|
|
88196
|
-
return new Promise((
|
|
88975
|
+
return new Promise((resolve20, reject) => {
|
|
88197
88976
|
const hash = createHash5("sha256");
|
|
88198
88977
|
const stream = createReadStream3(filePath);
|
|
88199
88978
|
stream.on("data", (chunk) => hash.update(chunk));
|
|
88200
88979
|
stream.on("end", () => {
|
|
88201
|
-
|
|
88980
|
+
resolve20(hash.digest("hex"));
|
|
88202
88981
|
});
|
|
88203
88982
|
stream.on("error", (error) => {
|
|
88204
88983
|
stream.destroy();
|
|
@@ -88306,7 +89085,7 @@ async function detectFileChanges(currentSkillPath, baselineSkillPath) {
|
|
|
88306
89085
|
init_types3();
|
|
88307
89086
|
var import_fs_extra27 = __toESM(require_lib3(), 1);
|
|
88308
89087
|
import { readdir as readdir29 } from "node:fs/promises";
|
|
88309
|
-
import { join as
|
|
89088
|
+
import { join as join94, normalize as normalize9 } from "node:path";
|
|
88310
89089
|
function validatePath3(path14, paramName) {
|
|
88311
89090
|
if (!path14 || typeof path14 !== "string") {
|
|
88312
89091
|
throw new SkillsMigrationError(`${paramName} must be a non-empty string`);
|
|
@@ -88327,13 +89106,13 @@ async function scanSkillsDirectory(skillsDir2) {
|
|
|
88327
89106
|
if (dirs.length === 0) {
|
|
88328
89107
|
return ["flat", []];
|
|
88329
89108
|
}
|
|
88330
|
-
const firstDirPath =
|
|
89109
|
+
const firstDirPath = join94(skillsDir2, dirs[0].name);
|
|
88331
89110
|
const subEntries = await readdir29(firstDirPath, { withFileTypes: true });
|
|
88332
89111
|
const subdirs = subEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
|
|
88333
89112
|
if (subdirs.length > 0) {
|
|
88334
89113
|
let skillLikeCount = 0;
|
|
88335
89114
|
for (const subdir of subdirs.slice(0, 3)) {
|
|
88336
|
-
const subdirPath =
|
|
89115
|
+
const subdirPath = join94(firstDirPath, subdir.name);
|
|
88337
89116
|
const subdirFiles = await readdir29(subdirPath, { withFileTypes: true });
|
|
88338
89117
|
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
89118
|
if (hasSkillMarker) {
|
|
@@ -88343,7 +89122,7 @@ async function scanSkillsDirectory(skillsDir2) {
|
|
|
88343
89122
|
if (skillLikeCount > 0) {
|
|
88344
89123
|
const skills = [];
|
|
88345
89124
|
for (const dir of dirs) {
|
|
88346
|
-
const categoryPath =
|
|
89125
|
+
const categoryPath = join94(skillsDir2, dir.name);
|
|
88347
89126
|
const skillDirs = await readdir29(categoryPath, { withFileTypes: true });
|
|
88348
89127
|
skills.push(...skillDirs.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name));
|
|
88349
89128
|
}
|
|
@@ -88353,7 +89132,7 @@ async function scanSkillsDirectory(skillsDir2) {
|
|
|
88353
89132
|
return ["flat", dirs.map((dir) => dir.name)];
|
|
88354
89133
|
}
|
|
88355
89134
|
async function findSkillPath(skillsDir2, skillName) {
|
|
88356
|
-
const flatPath =
|
|
89135
|
+
const flatPath = join94(skillsDir2, skillName);
|
|
88357
89136
|
if (await import_fs_extra27.pathExists(flatPath)) {
|
|
88358
89137
|
return { path: flatPath, category: undefined };
|
|
88359
89138
|
}
|
|
@@ -88362,8 +89141,8 @@ async function findSkillPath(skillsDir2, skillName) {
|
|
|
88362
89141
|
if (!entry.isDirectory() || entry.name.startsWith(".") || entry.name === "node_modules") {
|
|
88363
89142
|
continue;
|
|
88364
89143
|
}
|
|
88365
|
-
const categoryPath =
|
|
88366
|
-
const skillPath =
|
|
89144
|
+
const categoryPath = join94(skillsDir2, entry.name);
|
|
89145
|
+
const skillPath = join94(categoryPath, skillName);
|
|
88367
89146
|
if (await import_fs_extra27.pathExists(skillPath)) {
|
|
88368
89147
|
return { path: skillPath, category: entry.name };
|
|
88369
89148
|
}
|
|
@@ -88457,7 +89236,7 @@ class SkillsMigrator {
|
|
|
88457
89236
|
}
|
|
88458
89237
|
}
|
|
88459
89238
|
if (options2.backup && !options2.dryRun) {
|
|
88460
|
-
const claudeDir2 =
|
|
89239
|
+
const claudeDir2 = join95(currentSkillsDir, "..");
|
|
88461
89240
|
result.backupPath = await SkillsBackupManager.createBackup(currentSkillsDir, claudeDir2);
|
|
88462
89241
|
logger.success(`Backup created at: ${result.backupPath}`);
|
|
88463
89242
|
}
|
|
@@ -88518,7 +89297,7 @@ async function handleMigration(ctx) {
|
|
|
88518
89297
|
logger.debug("Skipping skills migration (fresh installation)");
|
|
88519
89298
|
return ctx;
|
|
88520
89299
|
}
|
|
88521
|
-
const newSkillsDir =
|
|
89300
|
+
const newSkillsDir = join96(ctx.extractDir, ".claude", "skills");
|
|
88522
89301
|
const currentSkillsDir = PathResolver.buildSkillsPath(ctx.resolvedDir, ctx.options.global);
|
|
88523
89302
|
if (!await import_fs_extra28.pathExists(newSkillsDir) || !await import_fs_extra28.pathExists(currentSkillsDir)) {
|
|
88524
89303
|
return ctx;
|
|
@@ -88542,13 +89321,13 @@ async function handleMigration(ctx) {
|
|
|
88542
89321
|
}
|
|
88543
89322
|
// src/commands/init/phases/opencode-handler.ts
|
|
88544
89323
|
import { cp as cp3, readdir as readdir31, rm as rm12 } from "node:fs/promises";
|
|
88545
|
-
import { join as
|
|
89324
|
+
import { join as join98 } from "node:path";
|
|
88546
89325
|
|
|
88547
89326
|
// src/services/transformers/opencode-path-transformer.ts
|
|
88548
89327
|
init_logger();
|
|
88549
89328
|
import { readFile as readFile46, readdir as readdir30, writeFile as writeFile28 } from "node:fs/promises";
|
|
88550
89329
|
import { platform as platform12 } from "node:os";
|
|
88551
|
-
import { extname as extname5, join as
|
|
89330
|
+
import { extname as extname5, join as join97 } from "node:path";
|
|
88552
89331
|
var IS_WINDOWS2 = platform12() === "win32";
|
|
88553
89332
|
function getOpenCodeGlobalPath() {
|
|
88554
89333
|
return "$HOME/.config/opencode/";
|
|
@@ -88609,7 +89388,7 @@ async function transformPathsForGlobalOpenCode(directory, options2 = {}) {
|
|
|
88609
89388
|
async function processDirectory2(dir) {
|
|
88610
89389
|
const entries = await readdir30(dir, { withFileTypes: true });
|
|
88611
89390
|
for (const entry of entries) {
|
|
88612
|
-
const fullPath =
|
|
89391
|
+
const fullPath = join97(dir, entry.name);
|
|
88613
89392
|
if (entry.isDirectory()) {
|
|
88614
89393
|
if (entry.name === "node_modules" || entry.name.startsWith(".")) {
|
|
88615
89394
|
continue;
|
|
@@ -88648,7 +89427,7 @@ async function handleOpenCode(ctx) {
|
|
|
88648
89427
|
if (ctx.cancelled || !ctx.extractDir || !ctx.resolvedDir) {
|
|
88649
89428
|
return ctx;
|
|
88650
89429
|
}
|
|
88651
|
-
const openCodeSource =
|
|
89430
|
+
const openCodeSource = join98(ctx.extractDir, ".opencode");
|
|
88652
89431
|
if (!await import_fs_extra29.pathExists(openCodeSource)) {
|
|
88653
89432
|
logger.debug("No .opencode directory in archive, skipping");
|
|
88654
89433
|
return ctx;
|
|
@@ -88666,8 +89445,8 @@ async function handleOpenCode(ctx) {
|
|
|
88666
89445
|
await import_fs_extra29.ensureDir(targetDir);
|
|
88667
89446
|
const entries = await readdir31(openCodeSource, { withFileTypes: true });
|
|
88668
89447
|
for (const entry of entries) {
|
|
88669
|
-
const sourcePath =
|
|
88670
|
-
const targetPath =
|
|
89448
|
+
const sourcePath = join98(openCodeSource, entry.name);
|
|
89449
|
+
const targetPath = join98(targetDir, entry.name);
|
|
88671
89450
|
if (await import_fs_extra29.pathExists(targetPath)) {
|
|
88672
89451
|
if (!ctx.options.forceOverwrite) {
|
|
88673
89452
|
logger.verbose(`Skipping existing: ${entry.name}`);
|
|
@@ -88764,20 +89543,21 @@ Please use only one download method.`);
|
|
|
88764
89543
|
}
|
|
88765
89544
|
// src/commands/init/phases/post-install-handler.ts
|
|
88766
89545
|
init_projects_registry();
|
|
88767
|
-
import { join as
|
|
89546
|
+
import { join as join102 } from "node:path";
|
|
89547
|
+
init_cc_version_checker();
|
|
88768
89548
|
init_logger();
|
|
88769
89549
|
init_path_resolver();
|
|
88770
|
-
var
|
|
89550
|
+
var import_fs_extra33 = __toESM(require_lib3(), 1);
|
|
88771
89551
|
async function handlePostInstall(ctx) {
|
|
88772
89552
|
if (ctx.cancelled || !ctx.extractDir || !ctx.resolvedDir || !ctx.claudeDir) {
|
|
88773
89553
|
return ctx;
|
|
88774
89554
|
}
|
|
88775
89555
|
if (ctx.options.global) {
|
|
88776
|
-
const claudeMdSource =
|
|
88777
|
-
const claudeMdDest =
|
|
88778
|
-
if (await
|
|
88779
|
-
if (ctx.options.fresh || !await
|
|
88780
|
-
await
|
|
89556
|
+
const claudeMdSource = join102(ctx.extractDir, "CLAUDE.md");
|
|
89557
|
+
const claudeMdDest = join102(ctx.resolvedDir, "CLAUDE.md");
|
|
89558
|
+
if (await import_fs_extra33.pathExists(claudeMdSource)) {
|
|
89559
|
+
if (ctx.options.fresh || !await import_fs_extra33.pathExists(claudeMdDest)) {
|
|
89560
|
+
await import_fs_extra33.copy(claudeMdSource, claudeMdDest);
|
|
88781
89561
|
logger.success(ctx.options.fresh ? "Replaced CLAUDE.md in global directory (fresh install)" : "Copied CLAUDE.md to global directory");
|
|
88782
89562
|
} else {
|
|
88783
89563
|
logger.debug("CLAUDE.md already exists in global directory (preserved)");
|
|
@@ -88796,6 +89576,90 @@ async function handlePostInstall(ctx) {
|
|
|
88796
89576
|
withSudo: ctx.options.withSudo
|
|
88797
89577
|
});
|
|
88798
89578
|
}
|
|
89579
|
+
let pluginSupported = false;
|
|
89580
|
+
try {
|
|
89581
|
+
const { requireCCPluginSupport: requireCCPluginSupport2 } = await Promise.resolve().then(() => (init_cc_version_checker(), exports_cc_version_checker));
|
|
89582
|
+
await requireCCPluginSupport2();
|
|
89583
|
+
pluginSupported = true;
|
|
89584
|
+
} catch (error) {
|
|
89585
|
+
if (error instanceof CCPluginSupportError) {
|
|
89586
|
+
logger.info(`Plugin install skipped: ${error.message}`);
|
|
89587
|
+
if (error.code === "cc_version_too_old") {
|
|
89588
|
+
logger.info("Upgrade: brew upgrade claude-code (or npm i -g @anthropic-ai/claude-code)");
|
|
89589
|
+
}
|
|
89590
|
+
if (error.code === "cc_not_found") {
|
|
89591
|
+
logger.info("Install Claude Code CLI, then re-run ck init to enable /ck:* plugin skills");
|
|
89592
|
+
}
|
|
89593
|
+
} else {
|
|
89594
|
+
logger.info(`Plugin install skipped: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
89595
|
+
}
|
|
89596
|
+
}
|
|
89597
|
+
let pluginVerified = false;
|
|
89598
|
+
if (pluginSupported) {
|
|
89599
|
+
try {
|
|
89600
|
+
const { handlePluginInstall: handlePluginInstall2 } = await Promise.resolve().then(() => (init_plugin_installer(), exports_plugin_installer));
|
|
89601
|
+
const pluginResult = await handlePluginInstall2(ctx.extractDir);
|
|
89602
|
+
pluginVerified = pluginResult.verified;
|
|
89603
|
+
if (pluginResult.error) {
|
|
89604
|
+
logger.info(`Plugin install issue: ${pluginResult.error}`);
|
|
89605
|
+
}
|
|
89606
|
+
} catch (error) {
|
|
89607
|
+
logger.debug(`Plugin install skipped: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
89608
|
+
}
|
|
89609
|
+
}
|
|
89610
|
+
if (ctx.claudeDir && ctx.kitType) {
|
|
89611
|
+
try {
|
|
89612
|
+
const { updateKitPluginState: updateKitPluginState2 } = await Promise.resolve().then(() => (init_metadata_migration(), exports_metadata_migration));
|
|
89613
|
+
await updateKitPluginState2(ctx.claudeDir, ctx.kitType, {
|
|
89614
|
+
pluginInstalled: pluginVerified,
|
|
89615
|
+
pluginInstalledAt: new Date().toISOString(),
|
|
89616
|
+
pluginVersion: ctx.selectedVersion || "unknown"
|
|
89617
|
+
});
|
|
89618
|
+
} catch {}
|
|
89619
|
+
}
|
|
89620
|
+
if (ctx.deferredDeletions?.length && ctx.claudeDir) {
|
|
89621
|
+
try {
|
|
89622
|
+
const { migrateUserSkills: migrateUserSkills2 } = await Promise.resolve().then(() => (init_skill_migration_merger(), exports_skill_migration_merger));
|
|
89623
|
+
const migration = await migrateUserSkills2(ctx.claudeDir, pluginVerified);
|
|
89624
|
+
if (pluginVerified) {
|
|
89625
|
+
if (!migration.canDelete) {
|
|
89626
|
+
logger.warning("Skill migration metadata unavailable — preserving existing skills (fail-safe)");
|
|
89627
|
+
return { ...ctx, installSkills, pluginSupported };
|
|
89628
|
+
}
|
|
89629
|
+
const preservedDirs = new Set(migration.preserved.map(normalizeSkillDir));
|
|
89630
|
+
const safeDeletions = ctx.deferredDeletions.filter((d3) => {
|
|
89631
|
+
const dirPath = extractSkillDirFromDeletionPath(d3);
|
|
89632
|
+
if (!dirPath)
|
|
89633
|
+
return true;
|
|
89634
|
+
return !preservedDirs.has(normalizeSkillDir(dirPath));
|
|
89635
|
+
});
|
|
89636
|
+
if (safeDeletions.length > 0) {
|
|
89637
|
+
const { handleDeletions: handleDeletions2 } = await Promise.resolve().then(() => (init_deletion_handler(), exports_deletion_handler));
|
|
89638
|
+
const deferredResult = await handleDeletions2({ deletions: safeDeletions }, ctx.claudeDir);
|
|
89639
|
+
if (deferredResult.deletedPaths.length > 0) {
|
|
89640
|
+
logger.info(`Removed ${deferredResult.deletedPaths.length} old skill file(s) (replaced by plugin)`);
|
|
89641
|
+
}
|
|
89642
|
+
}
|
|
89643
|
+
} else {
|
|
89644
|
+
logger.info("Plugin not verified — keeping existing skills as fallback");
|
|
89645
|
+
}
|
|
89646
|
+
} catch (error) {
|
|
89647
|
+
logger.debug(`Deferred skill deletion failed: ${error}`);
|
|
89648
|
+
}
|
|
89649
|
+
}
|
|
89650
|
+
if (pluginVerified) {
|
|
89651
|
+
try {
|
|
89652
|
+
const { cleanupOverlappingStandaloneSkills: cleanupOverlappingStandaloneSkills2 } = await Promise.resolve().then(() => (init_standalone_skill_cleanup(), exports_standalone_skill_cleanup));
|
|
89653
|
+
const globalClaudeDir = PathResolver.getGlobalKitDir();
|
|
89654
|
+
const pluginSkillsDir = join102(PathResolver.getClaudeKitDir(), "marketplace", "plugins", "ck", "skills");
|
|
89655
|
+
const overlap = await cleanupOverlappingStandaloneSkills2(globalClaudeDir, pluginSkillsDir);
|
|
89656
|
+
if (overlap.removed.length > 0) {
|
|
89657
|
+
logger.info(`Cleaned up ${overlap.removed.length} standalone skill(s) now provided by /ck:* plugin`);
|
|
89658
|
+
}
|
|
89659
|
+
} catch (error) {
|
|
89660
|
+
logger.info(`Standalone skill cleanup failed: ${error}`);
|
|
89661
|
+
}
|
|
89662
|
+
}
|
|
88799
89663
|
if (!ctx.isNonInteractive) {
|
|
88800
89664
|
const { isGeminiInstalled: isGeminiInstalled2 } = await Promise.resolve().then(() => (init_package_installer(), exports_package_installer));
|
|
88801
89665
|
const { checkExistingGeminiConfig: checkExistingGeminiConfig2, findMcpConfigPath: findMcpConfigPath2, processGeminiMcpLinking: processGeminiMcpLinking2 } = await Promise.resolve().then(() => (init_gemini_mcp_linker(), exports_gemini_mcp_linker));
|
|
@@ -88822,7 +89686,7 @@ async function handlePostInstall(ctx) {
|
|
|
88822
89686
|
}
|
|
88823
89687
|
if (!ctx.options.skipSetup) {
|
|
88824
89688
|
await promptSetupWizardIfNeeded({
|
|
88825
|
-
envPath:
|
|
89689
|
+
envPath: join102(ctx.claudeDir, ".env"),
|
|
88826
89690
|
claudeDir: ctx.claudeDir,
|
|
88827
89691
|
isGlobal: ctx.options.global,
|
|
88828
89692
|
isNonInteractive: ctx.isNonInteractive,
|
|
@@ -88839,14 +89703,26 @@ async function handlePostInstall(ctx) {
|
|
|
88839
89703
|
}
|
|
88840
89704
|
return {
|
|
88841
89705
|
...ctx,
|
|
88842
|
-
installSkills
|
|
89706
|
+
installSkills,
|
|
89707
|
+
pluginSupported
|
|
88843
89708
|
};
|
|
88844
89709
|
}
|
|
89710
|
+
function normalizeSkillDir(path14) {
|
|
89711
|
+
return path14.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
89712
|
+
}
|
|
89713
|
+
function extractSkillDirFromDeletionPath(path14) {
|
|
89714
|
+
const normalized = normalizeSkillDir(path14);
|
|
89715
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
89716
|
+
if (parts.length < 2 || parts[0] !== "skills") {
|
|
89717
|
+
return null;
|
|
89718
|
+
}
|
|
89719
|
+
return `skills/${parts[1]}`;
|
|
89720
|
+
}
|
|
88845
89721
|
// src/commands/init/phases/selection-handler.ts
|
|
88846
89722
|
init_config_manager();
|
|
88847
89723
|
init_github_client();
|
|
88848
89724
|
import { mkdir as mkdir30 } from "node:fs/promises";
|
|
88849
|
-
import { join as
|
|
89725
|
+
import { join as join104, resolve as resolve21 } from "node:path";
|
|
88850
89726
|
|
|
88851
89727
|
// src/domains/github/kit-access-checker.ts
|
|
88852
89728
|
init_logger();
|
|
@@ -88878,8 +89754,8 @@ async function detectAccessibleKits() {
|
|
|
88878
89754
|
// src/domains/github/preflight-checker.ts
|
|
88879
89755
|
init_logger();
|
|
88880
89756
|
import { exec as exec8 } from "node:child_process";
|
|
88881
|
-
import { promisify as
|
|
88882
|
-
var execAsync8 =
|
|
89757
|
+
import { promisify as promisify16 } from "node:util";
|
|
89758
|
+
var execAsync8 = promisify16(exec8);
|
|
88883
89759
|
function createSuccessfulPreflightResult() {
|
|
88884
89760
|
return {
|
|
88885
89761
|
success: true,
|
|
@@ -88976,11 +89852,12 @@ async function runPreflightChecks() {
|
|
|
88976
89852
|
|
|
88977
89853
|
// src/domains/installation/fresh-installer.ts
|
|
88978
89854
|
init_metadata_migration();
|
|
88979
|
-
|
|
88980
|
-
import { dirname as dirname21, join as join101, resolve as resolve19 } from "node:path";
|
|
89855
|
+
init_manifest_reader();
|
|
88981
89856
|
init_logger();
|
|
88982
89857
|
init_safe_spinner();
|
|
88983
|
-
var
|
|
89858
|
+
var import_fs_extra34 = __toESM(require_lib3(), 1);
|
|
89859
|
+
import { existsSync as existsSync50, readdirSync as readdirSync4, rmSync as rmSync3, rmdirSync as rmdirSync2, unlinkSync as unlinkSync4 } from "node:fs";
|
|
89860
|
+
import { dirname as dirname22, join as join103, resolve as resolve20 } from "node:path";
|
|
88984
89861
|
var CLAUDEKIT_SUBDIRECTORIES = ["commands", "agents", "skills", "rules", "hooks"];
|
|
88985
89862
|
async function analyzeFreshInstallation(claudeDir2) {
|
|
88986
89863
|
const metadata = await readManifest(claudeDir2);
|
|
@@ -89025,15 +89902,15 @@ async function analyzeFreshInstallation(claudeDir2) {
|
|
|
89025
89902
|
};
|
|
89026
89903
|
}
|
|
89027
89904
|
function cleanupEmptyDirectories2(filePath, claudeDir2) {
|
|
89028
|
-
const normalizedClaudeDir =
|
|
89029
|
-
let currentDir =
|
|
89905
|
+
const normalizedClaudeDir = resolve20(claudeDir2);
|
|
89906
|
+
let currentDir = resolve20(dirname22(filePath));
|
|
89030
89907
|
while (currentDir !== normalizedClaudeDir && currentDir.startsWith(normalizedClaudeDir)) {
|
|
89031
89908
|
try {
|
|
89032
89909
|
const entries = readdirSync4(currentDir);
|
|
89033
89910
|
if (entries.length === 0) {
|
|
89034
89911
|
rmdirSync2(currentDir);
|
|
89035
89912
|
logger.debug(`Removed empty directory: ${currentDir}`);
|
|
89036
|
-
currentDir =
|
|
89913
|
+
currentDir = resolve20(dirname22(currentDir));
|
|
89037
89914
|
} else {
|
|
89038
89915
|
break;
|
|
89039
89916
|
}
|
|
@@ -89050,7 +89927,7 @@ async function removeFilesByOwnership(claudeDir2, analysis, includeModified) {
|
|
|
89050
89927
|
const filesToRemove = includeModified ? [...analysis.ckFiles, ...analysis.ckModifiedFiles] : analysis.ckFiles;
|
|
89051
89928
|
const filesToPreserve = includeModified ? analysis.userFiles : [...analysis.ckModifiedFiles, ...analysis.userFiles];
|
|
89052
89929
|
for (const file of filesToRemove) {
|
|
89053
|
-
const fullPath =
|
|
89930
|
+
const fullPath = join103(claudeDir2, file.path);
|
|
89054
89931
|
try {
|
|
89055
89932
|
if (existsSync50(fullPath)) {
|
|
89056
89933
|
unlinkSync4(fullPath);
|
|
@@ -89075,13 +89952,13 @@ async function removeFilesByOwnership(claudeDir2, analysis, includeModified) {
|
|
|
89075
89952
|
};
|
|
89076
89953
|
}
|
|
89077
89954
|
async function updateMetadataAfterFresh(claudeDir2, removedFiles) {
|
|
89078
|
-
const metadataPath =
|
|
89079
|
-
if (!await
|
|
89955
|
+
const metadataPath = join103(claudeDir2, "metadata.json");
|
|
89956
|
+
if (!await import_fs_extra34.pathExists(metadataPath)) {
|
|
89080
89957
|
return;
|
|
89081
89958
|
}
|
|
89082
89959
|
let content;
|
|
89083
89960
|
try {
|
|
89084
|
-
content = await
|
|
89961
|
+
content = await import_fs_extra34.readFile(metadataPath, "utf-8");
|
|
89085
89962
|
} catch (readError) {
|
|
89086
89963
|
logger.warning(`Failed to read metadata.json: ${readError instanceof Error ? readError.message : String(readError)}`);
|
|
89087
89964
|
return;
|
|
@@ -89107,7 +89984,7 @@ async function updateMetadataAfterFresh(claudeDir2, removedFiles) {
|
|
|
89107
89984
|
metadata.files = metadata.files.filter((f3) => !removedSet.has(f3.path));
|
|
89108
89985
|
}
|
|
89109
89986
|
try {
|
|
89110
|
-
await
|
|
89987
|
+
await import_fs_extra34.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
|
|
89111
89988
|
logger.debug(`Updated metadata.json, removed ${removedFiles.length} file entries`);
|
|
89112
89989
|
} catch (writeError) {
|
|
89113
89990
|
logger.warning(`Failed to write metadata.json: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
|
|
@@ -89118,16 +89995,16 @@ async function removeSubdirectoriesFallback(claudeDir2) {
|
|
|
89118
89995
|
const removedFiles = [];
|
|
89119
89996
|
let removedDirCount = 0;
|
|
89120
89997
|
for (const subdir of CLAUDEKIT_SUBDIRECTORIES) {
|
|
89121
|
-
const subdirPath =
|
|
89122
|
-
if (await
|
|
89998
|
+
const subdirPath = join103(claudeDir2, subdir);
|
|
89999
|
+
if (await import_fs_extra34.pathExists(subdirPath)) {
|
|
89123
90000
|
rmSync3(subdirPath, { recursive: true, force: true });
|
|
89124
90001
|
removedDirCount++;
|
|
89125
90002
|
removedFiles.push(`${subdir}/ (entire directory)`);
|
|
89126
90003
|
logger.debug(`Removed subdirectory: ${subdir}/`);
|
|
89127
90004
|
}
|
|
89128
90005
|
}
|
|
89129
|
-
const metadataPath =
|
|
89130
|
-
if (await
|
|
90006
|
+
const metadataPath = join103(claudeDir2, "metadata.json");
|
|
90007
|
+
if (await import_fs_extra34.pathExists(metadataPath)) {
|
|
89131
90008
|
unlinkSync4(metadataPath);
|
|
89132
90009
|
removedFiles.push("metadata.json");
|
|
89133
90010
|
}
|
|
@@ -89140,7 +90017,7 @@ async function removeSubdirectoriesFallback(claudeDir2) {
|
|
|
89140
90017
|
};
|
|
89141
90018
|
}
|
|
89142
90019
|
async function handleFreshInstallation(claudeDir2, prompts) {
|
|
89143
|
-
if (!await
|
|
90020
|
+
if (!await import_fs_extra34.pathExists(claudeDir2)) {
|
|
89144
90021
|
logger.info(".claude directory does not exist, proceeding with fresh installation");
|
|
89145
90022
|
return true;
|
|
89146
90023
|
}
|
|
@@ -89172,10 +90049,11 @@ async function handleFreshInstallation(claudeDir2, prompts) {
|
|
|
89172
90049
|
|
|
89173
90050
|
// src/commands/init/phases/selection-handler.ts
|
|
89174
90051
|
init_claudekit_scanner();
|
|
90052
|
+
init_manifest_reader();
|
|
89175
90053
|
init_logger();
|
|
89176
90054
|
init_path_resolver();
|
|
89177
90055
|
init_types3();
|
|
89178
|
-
var
|
|
90056
|
+
var import_fs_extra35 = __toESM(require_lib3(), 1);
|
|
89179
90057
|
|
|
89180
90058
|
// src/commands/init/types.ts
|
|
89181
90059
|
function isSyncContext(ctx) {
|
|
@@ -89344,7 +90222,7 @@ async function handleSelection(ctx) {
|
|
|
89344
90222
|
}
|
|
89345
90223
|
}
|
|
89346
90224
|
}
|
|
89347
|
-
const resolvedDir =
|
|
90225
|
+
const resolvedDir = resolve21(targetDir);
|
|
89348
90226
|
logger.info(`Target directory: ${resolvedDir}`);
|
|
89349
90227
|
if (!ctx.options.global && PathResolver.isLocalSameAsGlobal(resolvedDir)) {
|
|
89350
90228
|
logger.warning("You're at HOME directory. Installing here modifies your GLOBAL ClaudeKit.");
|
|
@@ -89366,7 +90244,7 @@ async function handleSelection(ctx) {
|
|
|
89366
90244
|
return { ...ctx, cancelled: true };
|
|
89367
90245
|
}
|
|
89368
90246
|
}
|
|
89369
|
-
if (!await
|
|
90247
|
+
if (!await import_fs_extra35.pathExists(resolvedDir)) {
|
|
89370
90248
|
if (ctx.options.global) {
|
|
89371
90249
|
await mkdir30(resolvedDir, { recursive: true });
|
|
89372
90250
|
logger.info(`Created global directory: ${resolvedDir}`);
|
|
@@ -89378,7 +90256,7 @@ async function handleSelection(ctx) {
|
|
|
89378
90256
|
}
|
|
89379
90257
|
if (!ctx.options.fresh) {
|
|
89380
90258
|
const prefix = PathResolver.getPathPrefix(ctx.options.global);
|
|
89381
|
-
const claudeDir2 = prefix ?
|
|
90259
|
+
const claudeDir2 = prefix ? join104(resolvedDir, prefix) : resolvedDir;
|
|
89382
90260
|
try {
|
|
89383
90261
|
const existingMetadata = await readManifest(claudeDir2);
|
|
89384
90262
|
if (existingMetadata?.kits) {
|
|
@@ -89410,7 +90288,7 @@ async function handleSelection(ctx) {
|
|
|
89410
90288
|
}
|
|
89411
90289
|
if (ctx.options.fresh) {
|
|
89412
90290
|
const prefix = PathResolver.getPathPrefix(ctx.options.global);
|
|
89413
|
-
const claudeDir2 = prefix ?
|
|
90291
|
+
const claudeDir2 = prefix ? join104(resolvedDir, prefix) : resolvedDir;
|
|
89414
90292
|
const canProceed = await handleFreshInstallation(claudeDir2, ctx.prompts);
|
|
89415
90293
|
if (!canProceed) {
|
|
89416
90294
|
return { ...ctx, cancelled: true };
|
|
@@ -89429,7 +90307,7 @@ async function handleSelection(ctx) {
|
|
|
89429
90307
|
logger.info("Fetching available versions...");
|
|
89430
90308
|
let currentVersion = null;
|
|
89431
90309
|
try {
|
|
89432
|
-
const metadataPath = ctx.options.global ?
|
|
90310
|
+
const metadataPath = ctx.options.global ? join104(PathResolver.getGlobalKitDir(), "metadata.json") : join104(resolvedDir, ".claude", "metadata.json");
|
|
89433
90311
|
const metadata = await readClaudeKitMetadata(metadataPath);
|
|
89434
90312
|
currentVersion = metadata?.version || null;
|
|
89435
90313
|
if (currentVersion) {
|
|
@@ -89503,25 +90381,27 @@ async function handleSelection(ctx) {
|
|
|
89503
90381
|
};
|
|
89504
90382
|
}
|
|
89505
90383
|
// src/commands/init/phases/sync-handler.ts
|
|
89506
|
-
import { copyFile as copyFile8, mkdir as mkdir31, open as open4, readFile as
|
|
89507
|
-
import { dirname as
|
|
90384
|
+
import { copyFile as copyFile8, mkdir as mkdir31, open as open4, readFile as readFile50, rename as rename7, stat as stat17, unlink as unlink11, writeFile as writeFile30 } from "node:fs/promises";
|
|
90385
|
+
import { dirname as dirname23, join as join105, resolve as resolve22 } from "node:path";
|
|
90386
|
+
init_cc_version_checker();
|
|
90387
|
+
init_manifest_reader();
|
|
89508
90388
|
init_logger();
|
|
89509
90389
|
init_path_resolver();
|
|
89510
|
-
var
|
|
90390
|
+
var import_fs_extra36 = __toESM(require_lib3(), 1);
|
|
89511
90391
|
var import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
89512
90392
|
async function handleSync(ctx) {
|
|
89513
90393
|
if (!ctx.options.sync) {
|
|
89514
90394
|
return ctx;
|
|
89515
90395
|
}
|
|
89516
|
-
const resolvedDir = ctx.options.global ? PathResolver.getGlobalKitDir() :
|
|
89517
|
-
const claudeDir2 = ctx.options.global ? resolvedDir :
|
|
89518
|
-
if (!await
|
|
90396
|
+
const resolvedDir = ctx.options.global ? PathResolver.getGlobalKitDir() : resolve22(ctx.options.dir || ".");
|
|
90397
|
+
const claudeDir2 = ctx.options.global ? resolvedDir : join105(resolvedDir, ".claude");
|
|
90398
|
+
if (!await import_fs_extra36.pathExists(claudeDir2)) {
|
|
89519
90399
|
logger.error("Cannot sync: no .claude directory found");
|
|
89520
90400
|
ctx.prompts.note("Run 'ck init' without --sync to install first.", "No Installation Found");
|
|
89521
90401
|
return { ...ctx, cancelled: true };
|
|
89522
90402
|
}
|
|
89523
|
-
const metadataPath =
|
|
89524
|
-
if (!await
|
|
90403
|
+
const metadataPath = join105(claudeDir2, "metadata.json");
|
|
90404
|
+
if (!await import_fs_extra36.pathExists(metadataPath)) {
|
|
89525
90405
|
logger.error("Cannot sync: no metadata.json found");
|
|
89526
90406
|
ctx.prompts.note(`Your installation may be from an older version.
|
|
89527
90407
|
Run 'ck init' to update.`, "Legacy Installation");
|
|
@@ -89618,17 +90498,42 @@ function getLockTimeout() {
|
|
|
89618
90498
|
return timeoutMs;
|
|
89619
90499
|
}
|
|
89620
90500
|
var STALE_LOCK_THRESHOLD_MS = 5 * 60 * 1000;
|
|
90501
|
+
function isProcessAlive(pid) {
|
|
90502
|
+
try {
|
|
90503
|
+
process.kill(pid, 0);
|
|
90504
|
+
return true;
|
|
90505
|
+
} catch (error) {
|
|
90506
|
+
const code2 = error.code;
|
|
90507
|
+
return code2 === "EPERM";
|
|
90508
|
+
}
|
|
90509
|
+
}
|
|
90510
|
+
async function readLockPayload(lockPath) {
|
|
90511
|
+
try {
|
|
90512
|
+
const raw2 = await readFile50(lockPath, "utf-8");
|
|
90513
|
+
const parsed = JSON.parse(raw2);
|
|
90514
|
+
if (typeof parsed.pid === "number" && Number.isInteger(parsed.pid) && parsed.pid > 0) {
|
|
90515
|
+
return {
|
|
90516
|
+
pid: parsed.pid,
|
|
90517
|
+
startedAt: typeof parsed.startedAt === "string" && parsed.startedAt.length > 0 ? parsed.startedAt : "unknown"
|
|
90518
|
+
};
|
|
90519
|
+
}
|
|
90520
|
+
} catch {}
|
|
90521
|
+
return null;
|
|
90522
|
+
}
|
|
89621
90523
|
async function acquireSyncLock(global3) {
|
|
89622
90524
|
const cacheDir = PathResolver.getCacheDir(global3);
|
|
89623
|
-
const lockPath =
|
|
90525
|
+
const lockPath = join105(cacheDir, ".sync-lock");
|
|
89624
90526
|
const startTime = Date.now();
|
|
89625
90527
|
const lockTimeout = getLockTimeout();
|
|
89626
|
-
await mkdir31(
|
|
90528
|
+
await mkdir31(dirname23(lockPath), { recursive: true });
|
|
89627
90529
|
while (Date.now() - startTime < lockTimeout) {
|
|
89628
90530
|
try {
|
|
89629
90531
|
const handle = await open4(lockPath, "wx");
|
|
90532
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid, startedAt: new Date().toISOString() }), "utf-8");
|
|
89630
90533
|
return async () => {
|
|
89631
|
-
|
|
90534
|
+
try {
|
|
90535
|
+
await handle.close();
|
|
90536
|
+
} catch {}
|
|
89632
90537
|
await unlink11(lockPath).catch(() => {});
|
|
89633
90538
|
};
|
|
89634
90539
|
} catch (err) {
|
|
@@ -89636,18 +90541,29 @@ async function acquireSyncLock(global3) {
|
|
|
89636
90541
|
try {
|
|
89637
90542
|
const lockStat = await stat17(lockPath);
|
|
89638
90543
|
const lockAge = Math.abs(Date.now() - lockStat.mtimeMs);
|
|
89639
|
-
|
|
89640
|
-
|
|
90544
|
+
const lockOwner = await readLockPayload(lockPath);
|
|
90545
|
+
if (lockOwner?.pid === process.pid) {
|
|
90546
|
+
throw new Error("Sync lock is already held by current process");
|
|
90547
|
+
}
|
|
90548
|
+
const ownerAlive = lockOwner?.pid ? isProcessAlive(lockOwner.pid) : null;
|
|
90549
|
+
if (lockAge > STALE_LOCK_THRESHOLD_MS && ownerAlive !== true) {
|
|
90550
|
+
logger.warning(`Removing stale sync lock (age: ${Math.round(lockAge / 1000)}s${lockOwner?.pid ? `, pid=${lockOwner.pid}` : ""})`);
|
|
89641
90551
|
await unlink11(lockPath).catch(() => {});
|
|
89642
90552
|
continue;
|
|
89643
90553
|
}
|
|
90554
|
+
if (lockAge > STALE_LOCK_THRESHOLD_MS && ownerAlive === true) {
|
|
90555
|
+
logger.debug(`Sync lock older than threshold but owner pid=${lockOwner?.pid} still alive; waiting`);
|
|
90556
|
+
}
|
|
89644
90557
|
} catch (statError) {
|
|
90558
|
+
if (statError instanceof Error && statError.message.includes("already held by current process")) {
|
|
90559
|
+
throw statError;
|
|
90560
|
+
}
|
|
89645
90561
|
if (statError.code === "ENOENT") {
|
|
89646
90562
|
continue;
|
|
89647
90563
|
}
|
|
89648
90564
|
logger.debug(`Lock stat failed: ${statError}`);
|
|
89649
90565
|
}
|
|
89650
|
-
await new Promise((
|
|
90566
|
+
await new Promise((resolve23) => setTimeout(resolve23, 100));
|
|
89651
90567
|
continue;
|
|
89652
90568
|
}
|
|
89653
90569
|
throw err;
|
|
@@ -89666,18 +90582,20 @@ async function executeSyncMerge(ctx) {
|
|
|
89666
90582
|
const releaseLock = await acquireSyncLock(ctx.options.global);
|
|
89667
90583
|
try {
|
|
89668
90584
|
const trackedFiles = ctx.syncTrackedFiles;
|
|
89669
|
-
const upstreamDir = ctx.options.global ?
|
|
90585
|
+
const upstreamDir = ctx.options.global ? join105(ctx.extractDir, ".claude") : ctx.extractDir;
|
|
89670
90586
|
let deletions = [];
|
|
89671
90587
|
try {
|
|
89672
|
-
const sourceMetadataPath =
|
|
89673
|
-
if (await
|
|
89674
|
-
const content = await
|
|
90588
|
+
const sourceMetadataPath = join105(upstreamDir, "metadata.json");
|
|
90589
|
+
if (await import_fs_extra36.pathExists(sourceMetadataPath)) {
|
|
90590
|
+
const content = await readFile50(sourceMetadataPath, "utf-8");
|
|
89675
90591
|
const sourceMetadata = JSON.parse(content);
|
|
89676
90592
|
deletions = sourceMetadata.deletions || [];
|
|
89677
90593
|
}
|
|
89678
90594
|
} catch (error) {
|
|
89679
90595
|
logger.debug(`Failed to load source metadata for deletion filtering: ${error}`);
|
|
89680
90596
|
}
|
|
90597
|
+
const { categorizeDeletions: categorizeDeletions2, handleDeletions: handleDeletions2 } = await Promise.resolve().then(() => (init_deletion_handler(), exports_deletion_handler));
|
|
90598
|
+
const categorizedDeletions = categorizeDeletions2(deletions);
|
|
89681
90599
|
const filteredTrackedFiles = filterDeletionPaths(trackedFiles, deletions);
|
|
89682
90600
|
if (deletions.length > 0) {
|
|
89683
90601
|
const filtered = trackedFiles.length - filteredTrackedFiles.length;
|
|
@@ -89685,23 +90603,44 @@ async function executeSyncMerge(ctx) {
|
|
|
89685
90603
|
}
|
|
89686
90604
|
logger.info("Analyzing file changes...");
|
|
89687
90605
|
const plan = await SyncEngine.createSyncPlan(filteredTrackedFiles, ctx.claudeDir, upstreamDir);
|
|
90606
|
+
const forceOverwriteNonInteractive = ctx.isNonInteractive && ctx.options.forceOverwrite;
|
|
90607
|
+
const autoUpdateQueue = forceOverwriteNonInteractive ? dedupeTrackedFiles([...plan.autoUpdate, ...plan.needsReview]) : plan.autoUpdate;
|
|
90608
|
+
const reviewQueue = forceOverwriteNonInteractive ? [] : plan.needsReview;
|
|
89688
90609
|
displaySyncPlan(plan);
|
|
89689
|
-
if (
|
|
90610
|
+
if (autoUpdateQueue.length === 0 && reviewQueue.length === 0 && categorizedDeletions.immediate.length === 0 && categorizedDeletions.deferred.length === 0) {
|
|
89690
90611
|
ctx.prompts.note("All files are up to date or user-owned.", "No Changes Needed");
|
|
90612
|
+
}
|
|
90613
|
+
if (reviewQueue.length > 0 && ctx.isNonInteractive) {
|
|
90614
|
+
logger.error(`Cannot complete sync: ${reviewQueue.length} file(s) require interactive review`);
|
|
90615
|
+
ctx.prompts.note(`The following files have local modifications:
|
|
90616
|
+
${reviewQueue.slice(0, 5).map((f3) => ` • ${f3.path}`).join(`
|
|
90617
|
+
`)}${reviewQueue.length > 5 ? `
|
|
90618
|
+
... and ${reviewQueue.length - 5} more` : ""}
|
|
90619
|
+
|
|
90620
|
+
Options:
|
|
90621
|
+
1. Run 'ck init --sync' without --yes for interactive merge
|
|
90622
|
+
2. Use --force-overwrite to accept all upstream changes
|
|
90623
|
+
3. Manually resolve conflicts before syncing`, "Sync Blocked");
|
|
89691
90624
|
return { ...ctx, cancelled: true };
|
|
89692
90625
|
}
|
|
89693
|
-
|
|
89694
|
-
|
|
89695
|
-
|
|
89696
|
-
|
|
89697
|
-
|
|
90626
|
+
if (forceOverwriteNonInteractive && plan.needsReview.length > 0) {
|
|
90627
|
+
logger.info(`--force-overwrite enabled: auto-updating ${plan.needsReview.length} locally modified file(s)`);
|
|
90628
|
+
}
|
|
90629
|
+
const willModifyFiles = autoUpdateQueue.length > 0 || reviewQueue.length > 0 || categorizedDeletions.immediate.length > 0 || categorizedDeletions.deferred.length > 0;
|
|
90630
|
+
if (willModifyFiles) {
|
|
90631
|
+
const backupDir = PathResolver.getBackupDir();
|
|
90632
|
+
await createBackup(ctx.claudeDir, trackedFiles, backupDir);
|
|
90633
|
+
logger.success(`Backup created at ${import_picocolors23.default.dim(backupDir)}`);
|
|
90634
|
+
}
|
|
90635
|
+
if (autoUpdateQueue.length > 0) {
|
|
90636
|
+
logger.info(`Auto-updating ${autoUpdateQueue.length} file(s)...`);
|
|
89698
90637
|
let updateSuccess = 0;
|
|
89699
90638
|
let updateFailed = 0;
|
|
89700
|
-
for (const file of
|
|
90639
|
+
for (const file of autoUpdateQueue) {
|
|
89701
90640
|
try {
|
|
89702
90641
|
const sourcePath = await validateSyncPath(upstreamDir, file.path);
|
|
89703
90642
|
const targetPath = await validateSyncPath(ctx.claudeDir, file.path);
|
|
89704
|
-
const targetDir =
|
|
90643
|
+
const targetDir = dirname23(targetPath);
|
|
89705
90644
|
try {
|
|
89706
90645
|
await mkdir31(targetDir, { recursive: true });
|
|
89707
90646
|
} catch (mkdirError) {
|
|
@@ -89711,7 +90650,7 @@ async function executeSyncMerge(ctx) {
|
|
|
89711
90650
|
ctx.prompts.note("Your disk is full. Free up space and try again.", "Sync Failed");
|
|
89712
90651
|
return { ...ctx, cancelled: true };
|
|
89713
90652
|
}
|
|
89714
|
-
if (errCode === "EROFS" || errCode === "EACCES") {
|
|
90653
|
+
if (errCode === "EROFS" || errCode === "EACCES" || errCode === "EPERM") {
|
|
89715
90654
|
logger.warning(`Cannot create directory ${file.path}: ${errCode}`);
|
|
89716
90655
|
updateFailed++;
|
|
89717
90656
|
continue;
|
|
@@ -89729,7 +90668,7 @@ async function executeSyncMerge(ctx) {
|
|
|
89729
90668
|
ctx.prompts.note("Your disk is full. Free up space and try again.", "Sync Failed");
|
|
89730
90669
|
return { ...ctx, cancelled: true };
|
|
89731
90670
|
}
|
|
89732
|
-
if (errCode === "EACCES" || errCode === "EPERM") {
|
|
90671
|
+
if (errCode === "EACCES" || errCode === "EPERM" || errCode === "EROFS") {
|
|
89733
90672
|
logger.warning(`Permission denied: ${file.path} - check file permissions`);
|
|
89734
90673
|
updateFailed++;
|
|
89735
90674
|
} else if (errMsg.includes("Symlink") || errMsg.includes("Path")) {
|
|
@@ -89744,19 +90683,22 @@ async function executeSyncMerge(ctx) {
|
|
|
89744
90683
|
if (updateSuccess > 0) {
|
|
89745
90684
|
logger.success(`Auto-updated ${updateSuccess} file(s)${updateFailed > 0 ? ` (${updateFailed} failed)` : ""}`);
|
|
89746
90685
|
}
|
|
90686
|
+
if (updateSuccess === 0 && updateFailed > 0) {
|
|
90687
|
+
logger.warning("No files were updated due to write errors");
|
|
90688
|
+
}
|
|
89747
90689
|
}
|
|
89748
|
-
if (
|
|
89749
|
-
logger.info(`${
|
|
90690
|
+
if (reviewQueue.length > 0 && !ctx.isNonInteractive) {
|
|
90691
|
+
logger.info(`${reviewQueue.length} file(s) need interactive review...`);
|
|
89750
90692
|
let totalApplied = 0;
|
|
89751
90693
|
let totalRejected = 0;
|
|
89752
90694
|
let skippedFiles = 0;
|
|
89753
|
-
for (const file of
|
|
90695
|
+
for (const file of reviewQueue) {
|
|
89754
90696
|
let currentPath;
|
|
89755
90697
|
let upstreamPath;
|
|
89756
90698
|
try {
|
|
89757
90699
|
currentPath = await validateSyncPath(ctx.claudeDir, file.path);
|
|
89758
90700
|
upstreamPath = await validateSyncPath(upstreamDir, file.path);
|
|
89759
|
-
} catch
|
|
90701
|
+
} catch {
|
|
89760
90702
|
logger.warning(`Skipping invalid path during review: ${file.path}`);
|
|
89761
90703
|
skippedFiles++;
|
|
89762
90704
|
continue;
|
|
@@ -89783,7 +90725,7 @@ async function executeSyncMerge(ctx) {
|
|
|
89783
90725
|
const tempPath = `${currentPath}.tmp.${Date.now()}`;
|
|
89784
90726
|
try {
|
|
89785
90727
|
await writeFile30(tempPath, result.result, "utf-8");
|
|
89786
|
-
await
|
|
90728
|
+
await rename7(tempPath, currentPath);
|
|
89787
90729
|
} catch (atomicError) {
|
|
89788
90730
|
await unlink11(tempPath).catch(() => {});
|
|
89789
90731
|
throw atomicError;
|
|
@@ -89804,8 +90746,8 @@ async function executeSyncMerge(ctx) {
|
|
|
89804
90746
|
console.log("");
|
|
89805
90747
|
console.log(import_picocolors23.default.bold("Sync Summary:"));
|
|
89806
90748
|
console.log(import_picocolors23.default.dim("─".repeat(40)));
|
|
89807
|
-
if (
|
|
89808
|
-
console.log(import_picocolors23.default.green(` ✓ ${
|
|
90749
|
+
if (autoUpdateQueue.length > 0) {
|
|
90750
|
+
console.log(import_picocolors23.default.green(` ✓ ${autoUpdateQueue.length} file(s) auto-updated`));
|
|
89809
90751
|
}
|
|
89810
90752
|
if (totalApplied > 0) {
|
|
89811
90753
|
console.log(import_picocolors23.default.green(` ✓ ${totalApplied} hunk(s) applied`));
|
|
@@ -89819,23 +90761,96 @@ async function executeSyncMerge(ctx) {
|
|
|
89819
90761
|
if (plan.skipped.length > 0) {
|
|
89820
90762
|
console.log(import_picocolors23.default.dim(` ─ ${plan.skipped.length} user-owned file(s) unchanged`));
|
|
89821
90763
|
}
|
|
89822
|
-
}
|
|
89823
|
-
|
|
89824
|
-
|
|
89825
|
-
|
|
89826
|
-
|
|
89827
|
-
|
|
89828
|
-
|
|
89829
|
-
|
|
89830
|
-
|
|
89831
|
-
|
|
89832
|
-
|
|
89833
|
-
|
|
90764
|
+
}
|
|
90765
|
+
if (categorizedDeletions.immediate.length > 0) {
|
|
90766
|
+
try {
|
|
90767
|
+
const deletionResult = await handleDeletions2({ deletions: categorizedDeletions.immediate }, ctx.claudeDir);
|
|
90768
|
+
if (deletionResult.deletedPaths.length > 0) {
|
|
90769
|
+
logger.info(`Removed ${deletionResult.deletedPaths.length} deprecated file(s)`);
|
|
90770
|
+
}
|
|
90771
|
+
if (deletionResult.preservedPaths.length > 0) {
|
|
90772
|
+
logger.verbose(`Preserved ${deletionResult.preservedPaths.length} user-owned file(s)`);
|
|
90773
|
+
}
|
|
90774
|
+
} catch (error) {
|
|
90775
|
+
logger.debug(`Immediate deletion cleanup failed during sync: ${error}`);
|
|
90776
|
+
}
|
|
90777
|
+
}
|
|
90778
|
+
let pluginSupported = false;
|
|
90779
|
+
let pluginVerified = false;
|
|
90780
|
+
try {
|
|
90781
|
+
const { requireCCPluginSupport: requireCCPluginSupport2 } = await Promise.resolve().then(() => (init_cc_version_checker(), exports_cc_version_checker));
|
|
90782
|
+
await requireCCPluginSupport2();
|
|
90783
|
+
pluginSupported = true;
|
|
90784
|
+
} catch (error) {
|
|
90785
|
+
if (error instanceof CCPluginSupportError) {
|
|
90786
|
+
logger.info(`Plugin install skipped during sync: ${error.message}`);
|
|
90787
|
+
if (error.code === "cc_version_too_old") {
|
|
90788
|
+
logger.info("Upgrade Claude Code, then re-run: ck init --sync (plugin migration requires >= 1.0.33)");
|
|
90789
|
+
}
|
|
90790
|
+
if (error.code === "cc_not_found") {
|
|
90791
|
+
logger.info("Install Claude Code CLI to enable /ck:* plugin skills");
|
|
90792
|
+
}
|
|
90793
|
+
} else {
|
|
90794
|
+
logger.debug(`Plugin version check failed during sync: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
90795
|
+
}
|
|
90796
|
+
}
|
|
90797
|
+
if (pluginSupported && ctx.extractDir) {
|
|
90798
|
+
try {
|
|
90799
|
+
const { handlePluginInstall: handlePluginInstall2 } = await Promise.resolve().then(() => (init_plugin_installer(), exports_plugin_installer));
|
|
90800
|
+
const pluginResult = await handlePluginInstall2(ctx.extractDir);
|
|
90801
|
+
pluginVerified = pluginResult.verified;
|
|
90802
|
+
if (pluginResult.error) {
|
|
90803
|
+
logger.debug(`Plugin install issue: ${pluginResult.error}`);
|
|
90804
|
+
}
|
|
90805
|
+
} catch (error) {
|
|
90806
|
+
logger.debug(`Plugin install skipped: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
90807
|
+
}
|
|
90808
|
+
}
|
|
90809
|
+
if (ctx.claudeDir && ctx.kitType) {
|
|
90810
|
+
try {
|
|
90811
|
+
const { updateKitPluginState: updateKitPluginState2 } = await Promise.resolve().then(() => (init_metadata_migration(), exports_metadata_migration));
|
|
90812
|
+
await updateKitPluginState2(ctx.claudeDir, ctx.kitType, {
|
|
90813
|
+
pluginInstalled: pluginVerified,
|
|
90814
|
+
pluginInstalledAt: new Date().toISOString(),
|
|
90815
|
+
pluginVersion: ctx.selectedVersion || ctx.syncLatestVersion || "unknown"
|
|
90816
|
+
});
|
|
90817
|
+
} catch {}
|
|
90818
|
+
}
|
|
90819
|
+
if (categorizedDeletions.deferred.length > 0) {
|
|
90820
|
+
if (pluginVerified) {
|
|
90821
|
+
try {
|
|
90822
|
+
const { migrateUserSkills: migrateUserSkills2 } = await Promise.resolve().then(() => (init_skill_migration_merger(), exports_skill_migration_merger));
|
|
90823
|
+
const migration = await migrateUserSkills2(ctx.claudeDir, pluginVerified);
|
|
90824
|
+
if (!migration.canDelete) {
|
|
90825
|
+
logger.warning("Skill migration metadata unavailable during sync — preserving existing skills");
|
|
90826
|
+
} else {
|
|
90827
|
+
const preservedDirs = new Set(migration.preserved.map(normalizeSkillDir2));
|
|
90828
|
+
const safeDeletions = categorizedDeletions.deferred.filter((path14) => {
|
|
90829
|
+
const skillDir = extractSkillDirFromDeletionPath2(path14);
|
|
90830
|
+
return !skillDir || !preservedDirs.has(normalizeSkillDir2(skillDir));
|
|
90831
|
+
});
|
|
90832
|
+
if (safeDeletions.length > 0) {
|
|
90833
|
+
const deferredResult = await handleDeletions2({ deletions: safeDeletions }, ctx.claudeDir);
|
|
90834
|
+
if (deferredResult.deletedPaths.length > 0) {
|
|
90835
|
+
logger.info(`Removed ${deferredResult.deletedPaths.length} old skill file(s) during sync`);
|
|
90836
|
+
}
|
|
90837
|
+
}
|
|
90838
|
+
}
|
|
90839
|
+
} catch (error) {
|
|
90840
|
+
logger.debug(`Deferred skill cleanup failed during sync: ${error}`);
|
|
90841
|
+
}
|
|
90842
|
+
} else {
|
|
90843
|
+
logger.info("Plugin not verified during sync — keeping existing skills as fallback");
|
|
90844
|
+
}
|
|
89834
90845
|
}
|
|
89835
90846
|
ctx.prompts.outro("Config sync completed successfully");
|
|
89836
90847
|
return { ...ctx, cancelled: true };
|
|
89837
90848
|
} finally {
|
|
89838
|
-
|
|
90849
|
+
try {
|
|
90850
|
+
await releaseLock();
|
|
90851
|
+
} catch (error) {
|
|
90852
|
+
logger.debug(`Failed to release sync lock: ${error}`);
|
|
90853
|
+
}
|
|
89839
90854
|
}
|
|
89840
90855
|
}
|
|
89841
90856
|
function displaySyncPlan(plan) {
|
|
@@ -89870,9 +90885,9 @@ async function createBackup(claudeDir2, files, backupDir) {
|
|
|
89870
90885
|
for (const file of files) {
|
|
89871
90886
|
try {
|
|
89872
90887
|
const sourcePath = await validateSyncPath(claudeDir2, file.path);
|
|
89873
|
-
if (await
|
|
90888
|
+
if (await import_fs_extra36.pathExists(sourcePath)) {
|
|
89874
90889
|
const targetPath = await validateSyncPath(backupDir, file.path);
|
|
89875
|
-
const targetDir =
|
|
90890
|
+
const targetDir = dirname23(targetPath);
|
|
89876
90891
|
await mkdir31(targetDir, { recursive: true });
|
|
89877
90892
|
await copyFile8(sourcePath, targetPath);
|
|
89878
90893
|
}
|
|
@@ -89885,9 +90900,27 @@ async function createBackup(claudeDir2, files, backupDir) {
|
|
|
89885
90900
|
}
|
|
89886
90901
|
}
|
|
89887
90902
|
}
|
|
90903
|
+
function dedupeTrackedFiles(files) {
|
|
90904
|
+
const deduped = new Map;
|
|
90905
|
+
for (const file of files) {
|
|
90906
|
+
deduped.set(file.path, file);
|
|
90907
|
+
}
|
|
90908
|
+
return [...deduped.values()];
|
|
90909
|
+
}
|
|
90910
|
+
function normalizeSkillDir2(path14) {
|
|
90911
|
+
return path14.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
90912
|
+
}
|
|
90913
|
+
function extractSkillDirFromDeletionPath2(path14) {
|
|
90914
|
+
const normalized = normalizeSkillDir2(path14);
|
|
90915
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
90916
|
+
if (parts.length < 2 || parts[0] !== "skills") {
|
|
90917
|
+
return null;
|
|
90918
|
+
}
|
|
90919
|
+
return `skills/${parts[1]}`;
|
|
90920
|
+
}
|
|
89888
90921
|
// src/commands/init/phases/transform-handler.ts
|
|
89889
90922
|
init_config_manager();
|
|
89890
|
-
import { join as
|
|
90923
|
+
import { join as join109 } from "node:path";
|
|
89891
90924
|
|
|
89892
90925
|
// src/services/transformers/folder-path-transformer.ts
|
|
89893
90926
|
init_logger();
|
|
@@ -89896,40 +90929,40 @@ init_types3();
|
|
|
89896
90929
|
// src/services/transformers/folder-transform/folder-renamer.ts
|
|
89897
90930
|
init_logger();
|
|
89898
90931
|
init_types3();
|
|
89899
|
-
var
|
|
89900
|
-
import { rename as
|
|
89901
|
-
import { join as
|
|
90932
|
+
var import_fs_extra37 = __toESM(require_lib3(), 1);
|
|
90933
|
+
import { rename as rename8, rm as rm13 } from "node:fs/promises";
|
|
90934
|
+
import { join as join106, relative as relative18 } from "node:path";
|
|
89902
90935
|
async function collectDirsToRename(extractDir, folders) {
|
|
89903
90936
|
const dirsToRename = [];
|
|
89904
90937
|
if (folders.docs !== DEFAULT_FOLDERS.docs) {
|
|
89905
|
-
const docsPath =
|
|
89906
|
-
if (await
|
|
90938
|
+
const docsPath = join106(extractDir, DEFAULT_FOLDERS.docs);
|
|
90939
|
+
if (await import_fs_extra37.pathExists(docsPath)) {
|
|
89907
90940
|
dirsToRename.push({
|
|
89908
90941
|
from: docsPath,
|
|
89909
|
-
to:
|
|
90942
|
+
to: join106(extractDir, folders.docs)
|
|
89910
90943
|
});
|
|
89911
90944
|
}
|
|
89912
|
-
const claudeDocsPath =
|
|
89913
|
-
if (await
|
|
90945
|
+
const claudeDocsPath = join106(extractDir, ".claude", DEFAULT_FOLDERS.docs);
|
|
90946
|
+
if (await import_fs_extra37.pathExists(claudeDocsPath)) {
|
|
89914
90947
|
dirsToRename.push({
|
|
89915
90948
|
from: claudeDocsPath,
|
|
89916
|
-
to:
|
|
90949
|
+
to: join106(extractDir, ".claude", folders.docs)
|
|
89917
90950
|
});
|
|
89918
90951
|
}
|
|
89919
90952
|
}
|
|
89920
90953
|
if (folders.plans !== DEFAULT_FOLDERS.plans) {
|
|
89921
|
-
const plansPath =
|
|
89922
|
-
if (await
|
|
90954
|
+
const plansPath = join106(extractDir, DEFAULT_FOLDERS.plans);
|
|
90955
|
+
if (await import_fs_extra37.pathExists(plansPath)) {
|
|
89923
90956
|
dirsToRename.push({
|
|
89924
90957
|
from: plansPath,
|
|
89925
|
-
to:
|
|
90958
|
+
to: join106(extractDir, folders.plans)
|
|
89926
90959
|
});
|
|
89927
90960
|
}
|
|
89928
|
-
const claudePlansPath =
|
|
89929
|
-
if (await
|
|
90961
|
+
const claudePlansPath = join106(extractDir, ".claude", DEFAULT_FOLDERS.plans);
|
|
90962
|
+
if (await import_fs_extra37.pathExists(claudePlansPath)) {
|
|
89930
90963
|
dirsToRename.push({
|
|
89931
90964
|
from: claudePlansPath,
|
|
89932
|
-
to:
|
|
90965
|
+
to: join106(extractDir, ".claude", folders.plans)
|
|
89933
90966
|
});
|
|
89934
90967
|
}
|
|
89935
90968
|
}
|
|
@@ -89937,11 +90970,11 @@ async function collectDirsToRename(extractDir, folders) {
|
|
|
89937
90970
|
}
|
|
89938
90971
|
async function moveAcrossDevices(src, dest) {
|
|
89939
90972
|
try {
|
|
89940
|
-
await
|
|
90973
|
+
await rename8(src, dest);
|
|
89941
90974
|
} catch (e2) {
|
|
89942
90975
|
if (e2.code === "EXDEV") {
|
|
89943
90976
|
logger.debug(`Cross-device move detected, using copy+delete: ${src} -> ${dest}`);
|
|
89944
|
-
await
|
|
90977
|
+
await import_fs_extra37.copy(src, dest, { overwrite: true });
|
|
89945
90978
|
await rm13(src, { recursive: true, force: true });
|
|
89946
90979
|
} else {
|
|
89947
90980
|
throw e2;
|
|
@@ -89969,8 +91002,8 @@ async function renameFolders(dirsToRename, extractDir, options2) {
|
|
|
89969
91002
|
// src/services/transformers/folder-transform/path-replacer.ts
|
|
89970
91003
|
init_logger();
|
|
89971
91004
|
init_types3();
|
|
89972
|
-
import { readFile as
|
|
89973
|
-
import { join as
|
|
91005
|
+
import { readFile as readFile51, readdir as readdir33, writeFile as writeFile31 } from "node:fs/promises";
|
|
91006
|
+
import { join as join107, relative as relative19 } from "node:path";
|
|
89974
91007
|
var TRANSFORMABLE_FILE_PATTERNS = [
|
|
89975
91008
|
".md",
|
|
89976
91009
|
".txt",
|
|
@@ -90021,9 +91054,9 @@ function compileReplacements(replacements) {
|
|
|
90021
91054
|
async function transformFileContents(dir, compiledReplacements, options2) {
|
|
90022
91055
|
let filesChanged = 0;
|
|
90023
91056
|
let replacementsCount = 0;
|
|
90024
|
-
const entries = await
|
|
91057
|
+
const entries = await readdir33(dir, { withFileTypes: true });
|
|
90025
91058
|
for (const entry of entries) {
|
|
90026
|
-
const fullPath =
|
|
91059
|
+
const fullPath = join107(dir, entry.name);
|
|
90027
91060
|
if (entry.isDirectory()) {
|
|
90028
91061
|
if (entry.name === "node_modules" || entry.name === ".git") {
|
|
90029
91062
|
continue;
|
|
@@ -90036,7 +91069,7 @@ async function transformFileContents(dir, compiledReplacements, options2) {
|
|
|
90036
91069
|
if (!shouldTransform)
|
|
90037
91070
|
continue;
|
|
90038
91071
|
try {
|
|
90039
|
-
const content = await
|
|
91072
|
+
const content = await readFile51(fullPath, "utf-8");
|
|
90040
91073
|
let newContent = content;
|
|
90041
91074
|
let changeCount = 0;
|
|
90042
91075
|
for (const { regex: regex2, replacement } of compiledReplacements) {
|
|
@@ -90158,9 +91191,9 @@ async function transformFolderPaths(extractDir, folders, options2 = {}) {
|
|
|
90158
91191
|
|
|
90159
91192
|
// src/services/transformers/global-path-transformer.ts
|
|
90160
91193
|
init_logger();
|
|
90161
|
-
import { readFile as
|
|
91194
|
+
import { readFile as readFile52, readdir as readdir34, writeFile as writeFile32 } from "node:fs/promises";
|
|
90162
91195
|
import { platform as platform13 } from "node:os";
|
|
90163
|
-
import { extname as extname6, join as
|
|
91196
|
+
import { extname as extname6, join as join108 } from "node:path";
|
|
90164
91197
|
var IS_WINDOWS3 = platform13() === "win32";
|
|
90165
91198
|
var HOME_PREFIX = IS_WINDOWS3 ? "%USERPROFILE%" : "$HOME";
|
|
90166
91199
|
function getHomeDirPrefix() {
|
|
@@ -90268,9 +91301,9 @@ async function transformPathsForGlobalInstall(directory, options2 = {}) {
|
|
|
90268
91301
|
let filesSkipped = 0;
|
|
90269
91302
|
const skippedFiles = [];
|
|
90270
91303
|
async function processDirectory2(dir) {
|
|
90271
|
-
const entries = await
|
|
91304
|
+
const entries = await readdir34(dir, { withFileTypes: true });
|
|
90272
91305
|
for (const entry of entries) {
|
|
90273
|
-
const fullPath =
|
|
91306
|
+
const fullPath = join108(dir, entry.name);
|
|
90274
91307
|
if (entry.isDirectory()) {
|
|
90275
91308
|
if (entry.name === "node_modules" || entry.name.startsWith(".") && entry.name !== ".claude") {
|
|
90276
91309
|
continue;
|
|
@@ -90278,7 +91311,7 @@ async function transformPathsForGlobalInstall(directory, options2 = {}) {
|
|
|
90278
91311
|
await processDirectory2(fullPath);
|
|
90279
91312
|
} else if (entry.isFile() && shouldTransformFile3(entry.name)) {
|
|
90280
91313
|
try {
|
|
90281
|
-
const content = await
|
|
91314
|
+
const content = await readFile52(fullPath, "utf-8");
|
|
90282
91315
|
const { transformed, changes } = transformContent(content);
|
|
90283
91316
|
if (changes > 0) {
|
|
90284
91317
|
await writeFile32(fullPath, transformed, "utf-8");
|
|
@@ -90346,7 +91379,7 @@ async function handleTransforms(ctx) {
|
|
|
90346
91379
|
logger.debug(ctx.options.global ? "Saved folder configuration to ~/.claude/.ck.json" : "Saved folder configuration to .claude/.ck.json");
|
|
90347
91380
|
}
|
|
90348
91381
|
}
|
|
90349
|
-
const claudeDir2 = ctx.options.global ? ctx.resolvedDir :
|
|
91382
|
+
const claudeDir2 = ctx.options.global ? ctx.resolvedDir : join109(ctx.resolvedDir, ".claude");
|
|
90350
91383
|
return {
|
|
90351
91384
|
...ctx,
|
|
90352
91385
|
foldersConfig,
|
|
@@ -90540,8 +91573,8 @@ init_checksum_utils();
|
|
|
90540
91573
|
init_config_discovery();
|
|
90541
91574
|
var import_picocolors25 = __toESM(require_picocolors(), 1);
|
|
90542
91575
|
import { existsSync as existsSync51 } from "node:fs";
|
|
90543
|
-
import { readFile as
|
|
90544
|
-
import { resolve as
|
|
91576
|
+
import { readFile as readFile53, rm as rm14, unlink as unlink12 } from "node:fs/promises";
|
|
91577
|
+
import { resolve as resolve23 } from "node:path";
|
|
90545
91578
|
|
|
90546
91579
|
// src/commands/portable/conflict-resolver.ts
|
|
90547
91580
|
init_dist2();
|
|
@@ -90888,7 +91921,7 @@ function shouldExecuteAction2(action) {
|
|
|
90888
91921
|
}
|
|
90889
91922
|
async function executeDeleteAction(action, options2) {
|
|
90890
91923
|
const preservePaths = options2?.preservePaths ?? new Set;
|
|
90891
|
-
const shouldPreserveTarget = action.targetPath.length > 0 && preservePaths.has(
|
|
91924
|
+
const shouldPreserveTarget = action.targetPath.length > 0 && preservePaths.has(resolve23(action.targetPath));
|
|
90892
91925
|
try {
|
|
90893
91926
|
if (!shouldPreserveTarget && action.targetPath && existsSync51(action.targetPath)) {
|
|
90894
91927
|
await rm14(action.targetPath, { recursive: true, force: true });
|
|
@@ -91113,7 +92146,7 @@ async function migrateCommand(options2) {
|
|
|
91113
92146
|
for (const action of conflictActions) {
|
|
91114
92147
|
if (!action.diff && action.targetPath && existsSync51(action.targetPath)) {
|
|
91115
92148
|
try {
|
|
91116
|
-
const targetContent = await
|
|
92149
|
+
const targetContent = await readFile53(action.targetPath, "utf-8");
|
|
91117
92150
|
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
92151
|
if (sourceItem) {
|
|
91119
92152
|
const providerConfig = providers[action.provider];
|
|
@@ -91204,7 +92237,7 @@ async function migrateCommand(options2) {
|
|
|
91204
92237
|
allResults.push(...await installSkillDirectories(skills, skillProviders, installOpts));
|
|
91205
92238
|
}
|
|
91206
92239
|
}
|
|
91207
|
-
const writtenPaths = new Set(allResults.filter((result) => result.success && !result.skipped && result.path.length > 0).map((result) =>
|
|
92240
|
+
const writtenPaths = new Set(allResults.filter((result) => result.success && !result.skipped && result.path.length > 0).map((result) => resolve23(result.path)));
|
|
91208
92241
|
for (const deleteAction of plannedDeleteActions) {
|
|
91209
92242
|
allResults.push(await executeDeleteAction(deleteAction, {
|
|
91210
92243
|
preservePaths: writtenPaths
|
|
@@ -91320,7 +92353,7 @@ async function computeTargetStates(selectedProviders, global3) {
|
|
|
91320
92353
|
exists: true
|
|
91321
92354
|
};
|
|
91322
92355
|
try {
|
|
91323
|
-
const content = await
|
|
92356
|
+
const content = await readFile53(entry.path, "utf-8");
|
|
91324
92357
|
state.currentChecksum = computeContentChecksum(content);
|
|
91325
92358
|
} catch (error) {
|
|
91326
92359
|
logger.debug(`[migrate] Failed to read target for checksum: ${entry.path} (${String(error)})`);
|
|
@@ -91379,11 +92412,11 @@ var import_picocolors26 = __toESM(require_picocolors(), 1);
|
|
|
91379
92412
|
|
|
91380
92413
|
// src/commands/new/phases/directory-setup.ts
|
|
91381
92414
|
init_config_manager();
|
|
91382
|
-
import { resolve as
|
|
92415
|
+
import { resolve as resolve24 } from "node:path";
|
|
91383
92416
|
init_logger();
|
|
91384
92417
|
init_path_resolver();
|
|
91385
92418
|
init_types3();
|
|
91386
|
-
var
|
|
92419
|
+
var import_fs_extra38 = __toESM(require_lib3(), 1);
|
|
91387
92420
|
async function directorySetup(validOptions, prompts) {
|
|
91388
92421
|
const isNonInteractive2 = !process.stdin.isTTY || process.env.CI === "true" || process.env.NON_INTERACTIVE === "true";
|
|
91389
92422
|
const config = await ConfigManager.get();
|
|
@@ -91464,7 +92497,7 @@ async function directorySetup(validOptions, prompts) {
|
|
|
91464
92497
|
targetDir = await prompts.getDirectory(targetDir);
|
|
91465
92498
|
}
|
|
91466
92499
|
}
|
|
91467
|
-
const resolvedDir =
|
|
92500
|
+
const resolvedDir = resolve24(targetDir);
|
|
91468
92501
|
logger.info(`Target directory: ${resolvedDir}`);
|
|
91469
92502
|
if (PathResolver.isLocalSameAsGlobal(resolvedDir)) {
|
|
91470
92503
|
logger.warning("You're creating a project at HOME directory.");
|
|
@@ -91482,8 +92515,8 @@ async function directorySetup(validOptions, prompts) {
|
|
|
91482
92515
|
return null;
|
|
91483
92516
|
}
|
|
91484
92517
|
}
|
|
91485
|
-
if (await
|
|
91486
|
-
const files = await
|
|
92518
|
+
if (await import_fs_extra38.pathExists(resolvedDir)) {
|
|
92519
|
+
const files = await import_fs_extra38.readdir(resolvedDir);
|
|
91487
92520
|
const isEmpty = files.length === 0;
|
|
91488
92521
|
if (!isEmpty) {
|
|
91489
92522
|
if (isNonInteractive2) {
|
|
@@ -91521,7 +92554,7 @@ async function handleDirectorySetup(ctx) {
|
|
|
91521
92554
|
// src/commands/new/phases/project-creation.ts
|
|
91522
92555
|
init_config_manager();
|
|
91523
92556
|
init_github_client();
|
|
91524
|
-
import { join as
|
|
92557
|
+
import { join as join110 } from "node:path";
|
|
91525
92558
|
init_logger();
|
|
91526
92559
|
init_output_manager();
|
|
91527
92560
|
init_types3();
|
|
@@ -91647,7 +92680,7 @@ async function projectCreation(kit, resolvedDir, validOptions, isNonInteractive2
|
|
|
91647
92680
|
output.section("Installing");
|
|
91648
92681
|
logger.verbose("Installation target", { directory: resolvedDir });
|
|
91649
92682
|
const merger = new FileMerger;
|
|
91650
|
-
const claudeDir2 =
|
|
92683
|
+
const claudeDir2 = join110(resolvedDir, ".claude");
|
|
91651
92684
|
merger.setMultiKitContext(claudeDir2, kit);
|
|
91652
92685
|
if (validOptions.exclude && validOptions.exclude.length > 0) {
|
|
91653
92686
|
merger.addIgnorePatterns(validOptions.exclude);
|
|
@@ -91694,7 +92727,7 @@ async function handleProjectCreation(ctx) {
|
|
|
91694
92727
|
}
|
|
91695
92728
|
// src/commands/new/phases/post-setup.ts
|
|
91696
92729
|
init_projects_registry();
|
|
91697
|
-
import { join as
|
|
92730
|
+
import { join as join111 } from "node:path";
|
|
91698
92731
|
init_package_installer();
|
|
91699
92732
|
init_logger();
|
|
91700
92733
|
init_path_resolver();
|
|
@@ -91726,9 +92759,9 @@ async function postSetup(resolvedDir, validOptions, isNonInteractive2, prompts)
|
|
|
91726
92759
|
withSudo: validOptions.withSudo
|
|
91727
92760
|
});
|
|
91728
92761
|
}
|
|
91729
|
-
const claudeDir2 =
|
|
92762
|
+
const claudeDir2 = join111(resolvedDir, ".claude");
|
|
91730
92763
|
await promptSetupWizardIfNeeded({
|
|
91731
|
-
envPath:
|
|
92764
|
+
envPath: join111(claudeDir2, ".env"),
|
|
91732
92765
|
claudeDir: claudeDir2,
|
|
91733
92766
|
isGlobal: false,
|
|
91734
92767
|
isNonInteractive: isNonInteractive2,
|
|
@@ -91801,11 +92834,11 @@ init_logger();
|
|
|
91801
92834
|
init_safe_prompts();
|
|
91802
92835
|
var import_picocolors27 = __toESM(require_picocolors(), 1);
|
|
91803
92836
|
import { existsSync as existsSync52 } from "node:fs";
|
|
91804
|
-
import { resolve as
|
|
92837
|
+
import { resolve as resolve25 } from "node:path";
|
|
91805
92838
|
async function handleAdd(projectPath, options2) {
|
|
91806
92839
|
logger.debug(`Adding project: ${projectPath}, options: ${JSON.stringify(options2)}`);
|
|
91807
92840
|
intro("Add Project");
|
|
91808
|
-
const absolutePath =
|
|
92841
|
+
const absolutePath = resolve25(projectPath);
|
|
91809
92842
|
if (!existsSync52(absolutePath)) {
|
|
91810
92843
|
log.error(`Path does not exist: ${absolutePath}`);
|
|
91811
92844
|
process.exitCode = 1;
|
|
@@ -92695,7 +93728,7 @@ var import_picocolors32 = __toESM(require_picocolors(), 1);
|
|
|
92695
93728
|
// src/commands/uninstall/installation-detector.ts
|
|
92696
93729
|
init_claudekit_scanner();
|
|
92697
93730
|
init_path_resolver();
|
|
92698
|
-
var
|
|
93731
|
+
var import_fs_extra39 = __toESM(require_lib3(), 1);
|
|
92699
93732
|
function hasClaudeKitComponents(components) {
|
|
92700
93733
|
return components.agents > 0 || components.commands > 0 || components.rules > 0 || components.skills > 0;
|
|
92701
93734
|
}
|
|
@@ -92710,7 +93743,7 @@ async function detectInstallations() {
|
|
|
92710
93743
|
installations.push({
|
|
92711
93744
|
type: "local",
|
|
92712
93745
|
path: setup.project.path,
|
|
92713
|
-
exists: await
|
|
93746
|
+
exists: await import_fs_extra39.pathExists(setup.project.path),
|
|
92714
93747
|
hasMetadata,
|
|
92715
93748
|
components: setup.project.components
|
|
92716
93749
|
});
|
|
@@ -92723,7 +93756,7 @@ async function detectInstallations() {
|
|
|
92723
93756
|
installations.push({
|
|
92724
93757
|
type: "global",
|
|
92725
93758
|
path: setup.global.path,
|
|
92726
|
-
exists: await
|
|
93759
|
+
exists: await import_fs_extra39.pathExists(setup.global.path),
|
|
92727
93760
|
hasMetadata,
|
|
92728
93761
|
components: setup.global.components
|
|
92729
93762
|
});
|
|
@@ -92734,16 +93767,16 @@ async function detectInstallations() {
|
|
|
92734
93767
|
|
|
92735
93768
|
// src/commands/uninstall/removal-handler.ts
|
|
92736
93769
|
import { readdirSync as readdirSync6, rmSync as rmSync5 } from "node:fs";
|
|
92737
|
-
import { join as
|
|
93770
|
+
import { join as join113, resolve as resolve26, sep as sep5 } from "node:path";
|
|
92738
93771
|
init_logger();
|
|
92739
93772
|
init_safe_prompts();
|
|
92740
93773
|
init_safe_spinner();
|
|
92741
|
-
var
|
|
93774
|
+
var import_fs_extra40 = __toESM(require_lib3(), 1);
|
|
92742
93775
|
|
|
92743
93776
|
// src/commands/uninstall/analysis-handler.ts
|
|
92744
93777
|
init_metadata_migration();
|
|
92745
93778
|
import { readdirSync as readdirSync5, rmSync as rmSync4 } from "node:fs";
|
|
92746
|
-
import { dirname as
|
|
93779
|
+
import { dirname as dirname24, join as join112 } from "node:path";
|
|
92747
93780
|
init_logger();
|
|
92748
93781
|
init_safe_prompts();
|
|
92749
93782
|
var import_picocolors31 = __toESM(require_picocolors(), 1);
|
|
@@ -92761,7 +93794,7 @@ function classifyFileByOwnership(ownership, forceOverwrite, deleteReason) {
|
|
|
92761
93794
|
}
|
|
92762
93795
|
async function cleanupEmptyDirectories3(filePath, installationRoot) {
|
|
92763
93796
|
let cleaned = 0;
|
|
92764
|
-
let currentDir =
|
|
93797
|
+
let currentDir = dirname24(filePath);
|
|
92765
93798
|
while (currentDir !== installationRoot && currentDir.startsWith(installationRoot)) {
|
|
92766
93799
|
try {
|
|
92767
93800
|
const entries = readdirSync5(currentDir);
|
|
@@ -92769,7 +93802,7 @@ async function cleanupEmptyDirectories3(filePath, installationRoot) {
|
|
|
92769
93802
|
rmSync4(currentDir, { recursive: true });
|
|
92770
93803
|
cleaned++;
|
|
92771
93804
|
logger.debug(`Removed empty directory: ${currentDir}`);
|
|
92772
|
-
currentDir =
|
|
93805
|
+
currentDir = dirname24(currentDir);
|
|
92773
93806
|
} else {
|
|
92774
93807
|
break;
|
|
92775
93808
|
}
|
|
@@ -92791,7 +93824,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
|
|
|
92791
93824
|
if (uninstallManifest.isMultiKit && kit && metadata?.kits?.[kit]) {
|
|
92792
93825
|
const kitFiles = metadata.kits[kit].files || [];
|
|
92793
93826
|
for (const trackedFile of kitFiles) {
|
|
92794
|
-
const filePath =
|
|
93827
|
+
const filePath = join112(installation.path, trackedFile.path);
|
|
92795
93828
|
if (uninstallManifest.filesToPreserve.includes(trackedFile.path)) {
|
|
92796
93829
|
result.toPreserve.push({ path: trackedFile.path, reason: "shared with other kit" });
|
|
92797
93830
|
continue;
|
|
@@ -92821,7 +93854,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
|
|
|
92821
93854
|
return result;
|
|
92822
93855
|
}
|
|
92823
93856
|
for (const trackedFile of allTrackedFiles) {
|
|
92824
|
-
const filePath =
|
|
93857
|
+
const filePath = join112(installation.path, trackedFile.path);
|
|
92825
93858
|
const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
|
|
92826
93859
|
if (!ownershipResult.exists)
|
|
92827
93860
|
continue;
|
|
@@ -92866,7 +93899,7 @@ function displayDryRunPreview(analysis, installationType) {
|
|
|
92866
93899
|
// src/commands/uninstall/removal-handler.ts
|
|
92867
93900
|
async function isDirectory(filePath) {
|
|
92868
93901
|
try {
|
|
92869
|
-
const stats = await
|
|
93902
|
+
const stats = await import_fs_extra40.lstat(filePath);
|
|
92870
93903
|
return stats.isDirectory();
|
|
92871
93904
|
} catch {
|
|
92872
93905
|
logger.debug(`Failed to check if path is directory: ${filePath}`);
|
|
@@ -92875,16 +93908,16 @@ async function isDirectory(filePath) {
|
|
|
92875
93908
|
}
|
|
92876
93909
|
async function isPathSafeToRemove(filePath, baseDir) {
|
|
92877
93910
|
try {
|
|
92878
|
-
const resolvedPath =
|
|
92879
|
-
const resolvedBase =
|
|
93911
|
+
const resolvedPath = resolve26(filePath);
|
|
93912
|
+
const resolvedBase = resolve26(baseDir);
|
|
92880
93913
|
if (!resolvedPath.startsWith(resolvedBase + sep5) && resolvedPath !== resolvedBase) {
|
|
92881
93914
|
logger.debug(`Path outside installation directory: ${filePath}`);
|
|
92882
93915
|
return false;
|
|
92883
93916
|
}
|
|
92884
|
-
const stats = await
|
|
93917
|
+
const stats = await import_fs_extra40.lstat(filePath);
|
|
92885
93918
|
if (stats.isSymbolicLink()) {
|
|
92886
|
-
const realPath = await
|
|
92887
|
-
const resolvedReal =
|
|
93919
|
+
const realPath = await import_fs_extra40.realpath(filePath);
|
|
93920
|
+
const resolvedReal = resolve26(realPath);
|
|
92888
93921
|
if (!resolvedReal.startsWith(resolvedBase + sep5) && resolvedReal !== resolvedBase) {
|
|
92889
93922
|
logger.debug(`Symlink points outside installation directory: ${filePath} -> ${realPath}`);
|
|
92890
93923
|
return false;
|
|
@@ -92918,15 +93951,15 @@ async function removeInstallations(installations, options2) {
|
|
|
92918
93951
|
let removedCount = 0;
|
|
92919
93952
|
let cleanedDirs = 0;
|
|
92920
93953
|
for (const item of analysis.toDelete) {
|
|
92921
|
-
const filePath =
|
|
92922
|
-
if (!await
|
|
93954
|
+
const filePath = join113(installation.path, item.path);
|
|
93955
|
+
if (!await import_fs_extra40.pathExists(filePath))
|
|
92923
93956
|
continue;
|
|
92924
93957
|
if (!await isPathSafeToRemove(filePath, installation.path)) {
|
|
92925
93958
|
logger.debug(`Skipping unsafe path: ${item.path}`);
|
|
92926
93959
|
continue;
|
|
92927
93960
|
}
|
|
92928
93961
|
const isDir = await isDirectory(filePath);
|
|
92929
|
-
await
|
|
93962
|
+
await import_fs_extra40.remove(filePath);
|
|
92930
93963
|
removedCount++;
|
|
92931
93964
|
logger.debug(`Removed ${isDir ? "directory" : "file"}: ${item.path}`);
|
|
92932
93965
|
if (!isDir) {
|
|
@@ -93110,6 +94143,12 @@ ${import_picocolors32.default.yellow("User modifications will be permanently del
|
|
|
93110
94143
|
forceOverwrite: validOptions.forceOverwrite,
|
|
93111
94144
|
kit: validOptions.kit
|
|
93112
94145
|
});
|
|
94146
|
+
if (!validOptions.kit || validOptions.kit === "engineer") {
|
|
94147
|
+
try {
|
|
94148
|
+
const { handlePluginUninstall: handlePluginUninstall2 } = await Promise.resolve().then(() => (init_plugin_installer(), exports_plugin_installer));
|
|
94149
|
+
await handlePluginUninstall2();
|
|
94150
|
+
} catch {}
|
|
94151
|
+
}
|
|
93113
94152
|
const kitMsg = validOptions.kit ? ` (${validOptions.kit} kit)` : "";
|
|
93114
94153
|
prompts.outro(`ClaudeKit${kitMsg} uninstalled successfully!`);
|
|
93115
94154
|
} catch (error) {
|
|
@@ -93311,7 +94350,7 @@ init_logger();
|
|
|
93311
94350
|
init_path_resolver();
|
|
93312
94351
|
init_types3();
|
|
93313
94352
|
import { existsSync as existsSync53, readFileSync as readFileSync10 } from "node:fs";
|
|
93314
|
-
import { join as
|
|
94353
|
+
import { join as join114 } from "node:path";
|
|
93315
94354
|
var packageVersion = package_default.version;
|
|
93316
94355
|
function formatInstalledKits(metadata) {
|
|
93317
94356
|
if (!metadata.kits || Object.keys(metadata.kits).length === 0) {
|
|
@@ -93343,9 +94382,9 @@ async function displayVersion() {
|
|
|
93343
94382
|
let localKitVersion = null;
|
|
93344
94383
|
let isGlobalOnlyKit = false;
|
|
93345
94384
|
const globalKitDir = PathResolver.getGlobalKitDir();
|
|
93346
|
-
const globalMetadataPath =
|
|
94385
|
+
const globalMetadataPath = join114(globalKitDir, "metadata.json");
|
|
93347
94386
|
const prefix = PathResolver.getPathPrefix(false);
|
|
93348
|
-
const localMetadataPath = prefix ?
|
|
94387
|
+
const localMetadataPath = prefix ? join114(process.cwd(), prefix, "metadata.json") : join114(process.cwd(), "metadata.json");
|
|
93349
94388
|
const isLocalSameAsGlobal = localMetadataPath === globalMetadataPath;
|
|
93350
94389
|
if (!isLocalSameAsGlobal && existsSync53(localMetadataPath)) {
|
|
93351
94390
|
try {
|
|
@@ -93738,7 +94777,7 @@ var output2 = new OutputManager2;
|
|
|
93738
94777
|
|
|
93739
94778
|
// src/shared/temp-cleanup.ts
|
|
93740
94779
|
init_logger();
|
|
93741
|
-
var
|
|
94780
|
+
var import_fs_extra41 = __toESM(require_lib3(), 1);
|
|
93742
94781
|
import { rmSync as rmSync6 } from "node:fs";
|
|
93743
94782
|
var tempDirs2 = new Set;
|
|
93744
94783
|
async function cleanup() {
|
|
@@ -93747,7 +94786,7 @@ async function cleanup() {
|
|
|
93747
94786
|
logger.debug(`Cleaning up ${tempDirs2.size} temporary director(ies)...`);
|
|
93748
94787
|
for (const dir of tempDirs2) {
|
|
93749
94788
|
try {
|
|
93750
|
-
await
|
|
94789
|
+
await import_fs_extra41.remove(dir);
|
|
93751
94790
|
logger.debug(`Cleaned up temp directory: ${dir}`);
|
|
93752
94791
|
} catch (error) {
|
|
93753
94792
|
logger.debug(`Failed to clean temp directory ${dir}: ${error}`);
|