cursor-kit-cli 1.6.0 → 1.7.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,6 +45,7 @@ ck init
45
45
  - **🎓 Skills** - Comprehensive guides with references for specialized domains
46
46
  - **🔄 Sync** - Keep configurations updated from the community
47
47
  - **🎯 Multi-Target** - Support for Cursor IDE, GitHub Copilot, and Google AntiGravity
48
+ - **🔗 LAN Sharing** - Share configs between machines over local network
48
49
  - **🖥️ Multi-Instance** - Run multiple Cursor accounts simultaneously (macOS)
49
50
  - **⚡ Instance Aliases** - Create shell commands to quickly open projects in specific instances
50
51
  - **🎨 Beautiful CLI** - Delightful terminal experience
@@ -130,6 +131,51 @@ cursor-kit remove -f # Skip confirmation
130
131
  cursor-kit remove --target cursor -t rule -n my-rule -f # Full example
131
132
  ```
132
133
 
134
+ ### `share`
135
+
136
+ Share AI IDE configs over the local network via HTTP. Perfect for transferring your configuration to another machine without cloud services.
137
+
138
+ ```bash
139
+ cursor-kit share # Start share server (auto-detects configs)
140
+ cursor-kit share -p 9000 # Use a specific port
141
+ ```
142
+
143
+ **How it works:**
144
+
145
+ - Detects available configs (`.cursor`, `.agent`, `.github`) in current directory
146
+ - Starts an HTTP server on the local network
147
+ - Displays the `receive` command to run on the target machine
148
+ - Automatically shuts down after successful transfer
149
+
150
+ ### `receive`
151
+
152
+ Receive and extract shared AI IDE configs from a `cursor-kit share` URL.
153
+
154
+ ```bash
155
+ cursor-kit receive http://192.168.1.15:8080 # Receive from share URL
156
+ cursor-kit receive http://192.168.1.15:8080 -f # Force overwrite without prompts
157
+ ```
158
+
159
+ **Conflict handling:**
160
+
161
+ When existing configs are detected, you can choose to:
162
+ - **Overwrite** - Replace all conflicting files
163
+ - **Merge** - Keep existing files, add new ones only
164
+ - **Cancel** - Abort the operation
165
+
166
+ **Example workflow:**
167
+
168
+ ```bash
169
+ # On source machine (has the configs)
170
+ cd ~/project-with-configs
171
+ cursor-kit share
172
+ # Output shows: cursor-kit receive http://192.168.1.15:8080
173
+
174
+ # On target machine (wants to receive configs)
175
+ cd ~/new-project
176
+ cursor-kit receive http://192.168.1.15:8080
177
+ ```
178
+
133
179
  ### `instance`
134
180
 
135
181
  Manage multiple Cursor IDE instances for multi-account login. **macOS only.**
package/dist/cli.cjs CHANGED
@@ -28,7 +28,7 @@ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${_
28
28
  var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
29
29
 
30
30
  // src/cli.ts
31
- var import_citty7 = require("citty");
31
+ var import_citty9 = require("citty");
32
32
  var import_node_module = require("module");
33
33
 
34
34
  // src/utils/branding.ts
@@ -51,6 +51,9 @@ function printBanner() {
51
51
  function printSuccess(message) {
52
52
  console.log(import_picocolors.default.green("\u2713") + import_picocolors.default.dim(" ") + message);
53
53
  }
54
+ function printError(message) {
55
+ console.log(import_picocolors.default.red("\u2717") + import_picocolors.default.dim(" ") + message);
56
+ }
54
57
  function printInfo(message) {
55
58
  console.log(import_picocolors.default.cyan("\u2139") + import_picocolors.default.dim(" ") + message);
56
59
  }
@@ -2338,7 +2341,7 @@ function getStorageLocationLabel(location) {
2338
2341
  function isHomeBinInPath() {
2339
2342
  const pathEnv = process.env.PATH ?? "";
2340
2343
  const homeBin = getHomeBinDir();
2341
- return pathEnv.split(":").some((p9) => p9 === homeBin || p9 === "~/bin" || p9 === "$HOME/bin");
2344
+ return pathEnv.split(":").some((p11) => p11 === homeBin || p11 === "~/bin" || p11 === "$HOME/bin");
2342
2345
  }
2343
2346
  function getPathSetupInstructions() {
2344
2347
  const shell = process.env.SHELL ?? "/bin/zsh";
@@ -2904,10 +2907,580 @@ var instanceCommand = (0, import_citty6.defineCommand)({
2904
2907
  }
2905
2908
  });
2906
2909
 
2910
+ // src/commands/share.ts
2911
+ var import_citty7 = require("citty");
2912
+ var p9 = __toESM(require("@clack/prompts"), 1);
2913
+ var import_picocolors9 = __toESM(require("picocolors"), 1);
2914
+ var import_node_http = require("http");
2915
+ var import_node_os2 = require("os");
2916
+ var import_archiver = __toESM(require("archiver"), 1);
2917
+ var DEFAULT_PORT = 8080;
2918
+ var MAX_PORT_RETRIES = 10;
2919
+ var METADATA_FILENAME = ".cursor-kit-share.json";
2920
+ function getLocalIp() {
2921
+ const nets = (0, import_node_os2.networkInterfaces)();
2922
+ for (const name of Object.keys(nets)) {
2923
+ const netList = nets[name];
2924
+ if (!netList) continue;
2925
+ for (const net of netList) {
2926
+ if (net.family === "IPv4" && !net.internal) {
2927
+ return net.address;
2928
+ }
2929
+ }
2930
+ }
2931
+ return "127.0.0.1";
2932
+ }
2933
+ function detectAvailableConfigs(cwd) {
2934
+ const configs = [];
2935
+ const cursorDir = getCursorDir(cwd);
2936
+ if (dirExists(cursorDir)) {
2937
+ configs.push({
2938
+ type: "cursor",
2939
+ label: "Cursor",
2940
+ directory: ".cursor",
2941
+ path: cursorDir
2942
+ });
2943
+ }
2944
+ const agentDir = getAgentDir(cwd);
2945
+ if (dirExists(agentDir)) {
2946
+ configs.push({
2947
+ type: "google-antigravity",
2948
+ label: "Google AntiGravity",
2949
+ directory: ".agent",
2950
+ path: agentDir
2951
+ });
2952
+ }
2953
+ const githubDir = getGitHubDir(cwd);
2954
+ const copilotInstructionsPath = getCopilotInstructionsPath(cwd);
2955
+ const copilotInstructionsDir = getCopilotInstructionsDir(cwd);
2956
+ if (fileExists(copilotInstructionsPath) || dirExists(copilotInstructionsDir)) {
2957
+ configs.push({
2958
+ type: "github-copilot",
2959
+ label: "GitHub Copilot",
2960
+ directory: ".github",
2961
+ path: githubDir
2962
+ });
2963
+ }
2964
+ return configs;
2965
+ }
2966
+ function tryListen(server, port) {
2967
+ return new Promise((resolve2, reject) => {
2968
+ const handleError = (err) => {
2969
+ if (err.code === "EADDRINUSE") {
2970
+ reject(new Error(`Port ${port} is already in use`));
2971
+ } else {
2972
+ reject(err);
2973
+ }
2974
+ };
2975
+ server.once("error", handleError);
2976
+ server.listen(port, () => {
2977
+ server.removeListener("error", handleError);
2978
+ resolve2(port);
2979
+ });
2980
+ });
2981
+ }
2982
+ async function findAvailablePort(server, startPort) {
2983
+ for (let i = 0; i < MAX_PORT_RETRIES; i++) {
2984
+ const port = startPort + i;
2985
+ try {
2986
+ return await tryListen(server, port);
2987
+ } catch {
2988
+ if (i === MAX_PORT_RETRIES - 1) {
2989
+ throw new Error(`Could not find an available port after ${MAX_PORT_RETRIES} attempts`);
2990
+ }
2991
+ }
2992
+ }
2993
+ throw new Error("Unexpected error finding available port");
2994
+ }
2995
+ function createConfigZipStream(selectedConfigs) {
2996
+ const archive = (0, import_archiver.default)("zip", {
2997
+ zlib: { level: 6 }
2998
+ });
2999
+ archive.on("warning", (err) => {
3000
+ if (err.code !== "ENOENT") {
3001
+ console.warn(import_picocolors9.default.yellow(`Warning: ${err.message}`));
3002
+ }
3003
+ });
3004
+ archive.on("error", (err) => {
3005
+ throw err;
3006
+ });
3007
+ const metadata = {
3008
+ version: 1,
3009
+ configs: selectedConfigs.map((c) => c.type)
3010
+ };
3011
+ archive.append(JSON.stringify(metadata, null, 2), { name: METADATA_FILENAME });
3012
+ for (const config of selectedConfigs) {
3013
+ archive.directory(config.path, config.directory);
3014
+ }
3015
+ archive.finalize();
3016
+ return archive;
3017
+ }
3018
+ function handleRequest(req, res, selectedConfigs, onTransferStart, onTransferComplete, onTransferError) {
3019
+ if (req.method !== "GET" || req.url !== "/") {
3020
+ res.writeHead(404, { "Content-Type": "text/plain" });
3021
+ res.end("Not Found");
3022
+ return;
3023
+ }
3024
+ onTransferStart();
3025
+ res.writeHead(200, {
3026
+ "Content-Type": "application/zip",
3027
+ "Content-Disposition": 'attachment; filename="cursor-kit-configs.zip"',
3028
+ "Transfer-Encoding": "chunked"
3029
+ });
3030
+ const archive = createConfigZipStream(selectedConfigs);
3031
+ res.on("finish", () => {
3032
+ onTransferComplete();
3033
+ });
3034
+ res.on("close", () => {
3035
+ if (!res.writableFinished) {
3036
+ onTransferError(new Error("Client disconnected before transfer completed"));
3037
+ }
3038
+ });
3039
+ archive.on("error", (err) => {
3040
+ onTransferError(err);
3041
+ res.destroy();
3042
+ });
3043
+ archive.pipe(res);
3044
+ }
3045
+ var shareCommand = (0, import_citty7.defineCommand)({
3046
+ meta: {
3047
+ name: "share",
3048
+ description: "Share AI IDE configs (.cursor, .agent, .github) over LAN via HTTP"
3049
+ },
3050
+ args: {
3051
+ port: {
3052
+ type: "string",
3053
+ alias: "p",
3054
+ description: "Port to use for the HTTP server",
3055
+ default: String(DEFAULT_PORT)
3056
+ }
3057
+ },
3058
+ async run({ args }) {
3059
+ const cwd = process.cwd();
3060
+ const requestedPort = Number.parseInt(args.port, 10);
3061
+ if (Number.isNaN(requestedPort) || requestedPort < 1 || requestedPort > 65535) {
3062
+ p9.cancel("Invalid port number. Please specify a port between 1 and 65535.");
3063
+ process.exit(1);
3064
+ }
3065
+ p9.intro(import_picocolors9.default.bgCyan(import_picocolors9.default.black(" cursor-kit share ")));
3066
+ const availableConfigs = detectAvailableConfigs(cwd);
3067
+ if (availableConfigs.length === 0) {
3068
+ console.log();
3069
+ printError("No AI IDE configs found in the current directory.");
3070
+ printInfo("Run 'cursor-kit init' first to create configs for your AI IDE.");
3071
+ console.log();
3072
+ p9.cancel("Nothing to share");
3073
+ process.exit(1);
3074
+ }
3075
+ let selectedConfigs;
3076
+ if (availableConfigs.length === 1) {
3077
+ selectedConfigs = availableConfigs;
3078
+ printInfo(`Found: ${highlight(availableConfigs[0].label)} (${availableConfigs[0].directory})`);
3079
+ } else {
3080
+ const selection = await p9.multiselect({
3081
+ message: "Which AI IDE configs do you want to share?",
3082
+ options: availableConfigs.map((config) => ({
3083
+ value: config.type,
3084
+ label: config.label,
3085
+ hint: config.directory
3086
+ })),
3087
+ required: true
3088
+ });
3089
+ if (p9.isCancel(selection)) {
3090
+ p9.cancel("Operation cancelled");
3091
+ process.exit(0);
3092
+ }
3093
+ selectedConfigs = availableConfigs.filter(
3094
+ (c) => selection.includes(c.type)
3095
+ );
3096
+ }
3097
+ console.log();
3098
+ const s = p9.spinner();
3099
+ s.start("Starting HTTP server...");
3100
+ let server = null;
3101
+ let transferInProgress = false;
3102
+ const cleanup = () => {
3103
+ if (server) {
3104
+ server.close();
3105
+ server = null;
3106
+ }
3107
+ };
3108
+ process.on("SIGINT", () => {
3109
+ console.log();
3110
+ if (transferInProgress) {
3111
+ printError("Transfer interrupted");
3112
+ } else {
3113
+ printInfo("Server stopped");
3114
+ }
3115
+ cleanup();
3116
+ process.exit(0);
3117
+ });
3118
+ process.on("SIGTERM", () => {
3119
+ cleanup();
3120
+ process.exit(0);
3121
+ });
3122
+ try {
3123
+ server = (0, import_node_http.createServer)((req, res) => {
3124
+ handleRequest(
3125
+ req,
3126
+ res,
3127
+ selectedConfigs,
3128
+ () => {
3129
+ transferInProgress = true;
3130
+ console.log();
3131
+ printInfo("Connection established, transferring files...");
3132
+ },
3133
+ () => {
3134
+ transferInProgress = false;
3135
+ console.log();
3136
+ printSuccess("Transfer complete!");
3137
+ printInfo("Server will shut down automatically.");
3138
+ cleanup();
3139
+ process.exit(0);
3140
+ },
3141
+ (err) => {
3142
+ transferInProgress = false;
3143
+ printError(`Transfer failed: ${err.message}`);
3144
+ }
3145
+ );
3146
+ });
3147
+ const actualPort = await findAvailablePort(server, requestedPort);
3148
+ const localIp = getLocalIp();
3149
+ s.stop("HTTP server started");
3150
+ console.log();
3151
+ printDivider();
3152
+ console.log();
3153
+ printInfo("Sharing configs:");
3154
+ for (const config of selectedConfigs) {
3155
+ console.log(import_picocolors9.default.dim(` \u2514\u2500 ${import_picocolors9.default.green("\u25CF")} ${config.label} (${config.directory})`));
3156
+ }
3157
+ console.log();
3158
+ printInfo(`Local IP: ${highlight(localIp)}`);
3159
+ printInfo(`Port: ${highlight(String(actualPort))}`);
3160
+ console.log();
3161
+ printDivider();
3162
+ console.log();
3163
+ console.log(import_picocolors9.default.bold(" Run this command on the receiving machine:"));
3164
+ console.log();
3165
+ console.log(
3166
+ ` ${import_picocolors9.default.cyan("$")} ${import_picocolors9.default.bold(import_picocolors9.default.green(`cursor-kit receive http://${localIp}:${actualPort}`))}`
3167
+ );
3168
+ console.log();
3169
+ printDivider();
3170
+ console.log();
3171
+ printInfo("Waiting for connection... (Press Ctrl+C to cancel)");
3172
+ } catch (error) {
3173
+ s.stop("Failed to start server");
3174
+ cleanup();
3175
+ p9.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
3176
+ process.exit(1);
3177
+ }
3178
+ }
3179
+ });
3180
+
3181
+ // src/commands/receive.ts
3182
+ var import_citty8 = require("citty");
3183
+ var p10 = __toESM(require("@clack/prompts"), 1);
3184
+ var import_picocolors10 = __toESM(require("picocolors"), 1);
3185
+ var import_node_http2 = require("http");
3186
+ var import_node_fs4 = require("fs");
3187
+ var import_promises = require("stream/promises");
3188
+ var import_node_stream = require("stream");
3189
+ var import_unzipper = __toESM(require("unzipper"), 1);
3190
+ var import_node_path11 = require("path");
3191
+ var import_node_os3 = require("os");
3192
+ var import_node_crypto = require("crypto");
3193
+ var METADATA_FILENAME2 = ".cursor-kit-share.json";
3194
+ function isValidUrl(urlString) {
3195
+ try {
3196
+ const url = new URL(urlString);
3197
+ return url.protocol === "http:";
3198
+ } catch {
3199
+ return false;
3200
+ }
3201
+ }
3202
+ function formatBytes(bytes) {
3203
+ if (bytes === 0) return "0 B";
3204
+ const k = 1024;
3205
+ const sizes = ["B", "KB", "MB", "GB"];
3206
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
3207
+ return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
3208
+ }
3209
+ function getConfigInfo(type, cwd) {
3210
+ switch (type) {
3211
+ case "cursor":
3212
+ return {
3213
+ type,
3214
+ label: "Cursor",
3215
+ directory: ".cursor",
3216
+ targetPath: getCursorDir(cwd),
3217
+ hasConflict: dirExists(getCursorDir(cwd))
3218
+ };
3219
+ case "google-antigravity":
3220
+ return {
3221
+ type,
3222
+ label: "Google AntiGravity",
3223
+ directory: ".agent",
3224
+ targetPath: getAgentDir(cwd),
3225
+ hasConflict: dirExists(getAgentDir(cwd))
3226
+ };
3227
+ case "github-copilot":
3228
+ return {
3229
+ type,
3230
+ label: "GitHub Copilot",
3231
+ directory: ".github",
3232
+ targetPath: getGitHubDir(cwd),
3233
+ hasConflict: dirExists(getGitHubDir(cwd))
3234
+ };
3235
+ }
3236
+ }
3237
+ async function downloadToTemp(url) {
3238
+ const tempPath = (0, import_node_path11.join)((0, import_node_os3.tmpdir)(), `cursor-kit-receive-${(0, import_node_crypto.randomUUID)()}.zip`);
3239
+ return new Promise((resolve2, reject) => {
3240
+ const request = (0, import_node_http2.get)(url, (response) => {
3241
+ if (response.statusCode !== 200) {
3242
+ reject(new Error(`Server returned status ${response.statusCode}`));
3243
+ return;
3244
+ }
3245
+ const writeStream = (0, import_node_fs4.createWriteStream)(tempPath);
3246
+ const passThrough = new import_node_stream.PassThrough();
3247
+ let size = 0;
3248
+ passThrough.on("data", (chunk) => {
3249
+ size += chunk.length;
3250
+ });
3251
+ response.pipe(passThrough).pipe(writeStream);
3252
+ writeStream.on("finish", () => {
3253
+ resolve2({ tempPath, size });
3254
+ });
3255
+ writeStream.on("error", (err) => {
3256
+ reject(err);
3257
+ });
3258
+ response.on("error", (err) => {
3259
+ reject(err);
3260
+ });
3261
+ });
3262
+ request.on("error", reject);
3263
+ request.on("timeout", () => {
3264
+ request.destroy();
3265
+ reject(new Error("Connection timed out"));
3266
+ });
3267
+ request.setTimeout(3e4);
3268
+ });
3269
+ }
3270
+ async function readMetadataFromZip(zipPath) {
3271
+ return new Promise((resolve2) => {
3272
+ const readStream = (0, import_node_fs4.createReadStream)(zipPath);
3273
+ const unzipStream = readStream.pipe(import_unzipper.default.Parse());
3274
+ let found = false;
3275
+ unzipStream.on("entry", async (entry) => {
3276
+ if (entry.path === METADATA_FILENAME2 && !found) {
3277
+ found = true;
3278
+ const chunks = [];
3279
+ entry.on("data", (chunk) => chunks.push(chunk));
3280
+ entry.on("end", () => {
3281
+ try {
3282
+ const content = Buffer.concat(chunks).toString("utf-8");
3283
+ const metadata = JSON.parse(content);
3284
+ resolve2(metadata);
3285
+ } catch {
3286
+ resolve2(null);
3287
+ }
3288
+ });
3289
+ } else {
3290
+ entry.autodrain();
3291
+ }
3292
+ });
3293
+ unzipStream.on("close", () => {
3294
+ if (!found) {
3295
+ resolve2(null);
3296
+ }
3297
+ });
3298
+ unzipStream.on("error", () => {
3299
+ resolve2(null);
3300
+ });
3301
+ });
3302
+ }
3303
+ async function extractWithStrategy(zipPath, cwd, configs, strategy) {
3304
+ if (strategy === "overwrite") {
3305
+ for (const config of configs) {
3306
+ if (config.hasConflict) {
3307
+ removeFile(config.targetPath);
3308
+ }
3309
+ }
3310
+ }
3311
+ const configDirs = new Set(configs.map((c) => c.directory));
3312
+ return new Promise((resolve2, reject) => {
3313
+ const readStream = (0, import_node_fs4.createReadStream)(zipPath);
3314
+ const unzipStream = readStream.pipe(import_unzipper.default.Parse());
3315
+ const extractPromises = [];
3316
+ unzipStream.on("entry", (entry) => {
3317
+ const entryPath = entry.path;
3318
+ if (entryPath === METADATA_FILENAME2) {
3319
+ entry.autodrain();
3320
+ return;
3321
+ }
3322
+ const topLevelDir = entryPath.split("/")[0];
3323
+ if (!configDirs.has(topLevelDir)) {
3324
+ entry.autodrain();
3325
+ return;
3326
+ }
3327
+ const targetPath = (0, import_node_path11.join)(cwd, entryPath);
3328
+ if (entry.type === "Directory") {
3329
+ ensureDir(targetPath);
3330
+ entry.autodrain();
3331
+ return;
3332
+ }
3333
+ if (strategy === "merge" && fileExists(targetPath)) {
3334
+ entry.autodrain();
3335
+ return;
3336
+ }
3337
+ ensureDir((0, import_node_path11.join)(targetPath, ".."));
3338
+ const writeStream = (0, import_node_fs4.createWriteStream)(targetPath);
3339
+ const extractPromise = (0, import_promises.pipeline)(entry, writeStream);
3340
+ extractPromises.push(extractPromise);
3341
+ });
3342
+ unzipStream.on("close", async () => {
3343
+ try {
3344
+ await Promise.all(extractPromises);
3345
+ resolve2();
3346
+ } catch (err) {
3347
+ reject(err);
3348
+ }
3349
+ });
3350
+ unzipStream.on("error", reject);
3351
+ });
3352
+ }
3353
+ var receiveCommand = (0, import_citty8.defineCommand)({
3354
+ meta: {
3355
+ name: "receive",
3356
+ description: "Receive and extract shared AI IDE configs from a cursor-kit share URL"
3357
+ },
3358
+ args: {
3359
+ url: {
3360
+ type: "positional",
3361
+ description: "The share URL (e.g., http://192.168.1.15:8080)",
3362
+ required: true
3363
+ },
3364
+ force: {
3365
+ type: "boolean",
3366
+ alias: "f",
3367
+ description: "Overwrite existing configs without prompting",
3368
+ default: false
3369
+ }
3370
+ },
3371
+ async run({ args }) {
3372
+ const { url, force } = args;
3373
+ const cwd = process.cwd();
3374
+ if (!isValidUrl(url)) {
3375
+ p10.cancel("Invalid URL. Please provide a valid HTTP URL (e.g., http://192.168.1.15:8080)");
3376
+ process.exit(1);
3377
+ }
3378
+ p10.intro(import_picocolors10.default.bgCyan(import_picocolors10.default.black(" cursor-kit receive ")));
3379
+ console.log();
3380
+ printInfo(`Source: ${highlight(url)}`);
3381
+ printInfo(`Destination: ${highlight(cwd)}`);
3382
+ console.log();
3383
+ printDivider();
3384
+ console.log();
3385
+ const s = p10.spinner();
3386
+ s.start("Connecting to server...");
3387
+ let tempPath = null;
3388
+ let receivedSize = 0;
3389
+ try {
3390
+ const result = await downloadToTemp(url);
3391
+ tempPath = result.tempPath;
3392
+ receivedSize = result.size;
3393
+ s.stop("Connected and downloaded");
3394
+ printInfo(`Received: ${highlight(formatBytes(receivedSize))}`);
3395
+ console.log();
3396
+ s.start("Reading metadata...");
3397
+ const metadata = await readMetadataFromZip(tempPath);
3398
+ if (!metadata || !metadata.configs || metadata.configs.length === 0) {
3399
+ s.stop("Invalid share file");
3400
+ printError("This doesn't appear to be a valid cursor-kit share.");
3401
+ printInfo("The share file is missing required metadata.");
3402
+ p10.cancel("Invalid share format");
3403
+ removeFile(tempPath);
3404
+ process.exit(1);
3405
+ }
3406
+ s.stop("Metadata loaded");
3407
+ const configs = metadata.configs.map((type) => getConfigInfo(type, cwd));
3408
+ const conflictingConfigs = configs.filter((c) => c.hasConflict);
3409
+ printInfo("Configs in this share:");
3410
+ for (const config of configs) {
3411
+ const conflictIcon = config.hasConflict ? import_picocolors10.default.yellow("\u26A0") : import_picocolors10.default.green("\u25CF");
3412
+ const conflictHint = config.hasConflict ? import_picocolors10.default.yellow(" (exists)") : "";
3413
+ console.log(import_picocolors10.default.dim(` \u2514\u2500 ${conflictIcon} ${config.label} (${config.directory})${conflictHint}`));
3414
+ }
3415
+ console.log();
3416
+ let strategy = "overwrite";
3417
+ if (conflictingConfigs.length > 0 && !force) {
3418
+ const strategySelection = await p10.select({
3419
+ message: `${conflictingConfigs.length} existing config(s) found. How do you want to handle conflicts?`,
3420
+ options: [
3421
+ {
3422
+ value: "overwrite",
3423
+ label: "Overwrite existing files",
3424
+ hint: "Replace all conflicting files"
3425
+ },
3426
+ {
3427
+ value: "merge",
3428
+ label: "Merge (keep existing, add new only)",
3429
+ hint: "Skip files that already exist"
3430
+ },
3431
+ {
3432
+ value: "cancel",
3433
+ label: "Cancel",
3434
+ hint: "Abort the operation"
3435
+ }
3436
+ ]
3437
+ });
3438
+ if (p10.isCancel(strategySelection) || strategySelection === "cancel") {
3439
+ p10.cancel("Operation cancelled");
3440
+ removeFile(tempPath);
3441
+ process.exit(0);
3442
+ }
3443
+ strategy = strategySelection;
3444
+ }
3445
+ s.start("Extracting configs...");
3446
+ await extractWithStrategy(tempPath, cwd, configs, strategy);
3447
+ s.stop("Configs extracted");
3448
+ removeFile(tempPath);
3449
+ console.log();
3450
+ printDivider();
3451
+ console.log();
3452
+ for (const config of configs) {
3453
+ const action = config.hasConflict ? strategy === "merge" ? "merged" : "replaced" : "added";
3454
+ printSuccess(`${config.label}: ${highlight(action)}`);
3455
+ }
3456
+ console.log();
3457
+ p10.outro(import_picocolors10.default.green("\u2728 Transfer complete!"));
3458
+ } catch (error) {
3459
+ s.stop("Failed");
3460
+ if (tempPath) {
3461
+ removeFile(tempPath);
3462
+ }
3463
+ let errorMessage = error instanceof Error ? error.message : "Unknown error";
3464
+ if (errorMessage.includes("ECONNREFUSED")) {
3465
+ errorMessage = "Connection refused. Make sure the share server is running.";
3466
+ } else if (errorMessage.includes("ETIMEDOUT") || errorMessage.includes("timeout")) {
3467
+ errorMessage = "Connection timed out. Check the URL and network connection.";
3468
+ } else if (errorMessage.includes("ENOTFOUND")) {
3469
+ errorMessage = "Host not found. Check the URL and network connection.";
3470
+ } else if (errorMessage.includes("ECONNRESET") || errorMessage.includes("aborted")) {
3471
+ errorMessage = "Connection was reset. The server may have closed unexpectedly.";
3472
+ }
3473
+ printError(errorMessage);
3474
+ p10.cancel("Failed to receive configs");
3475
+ process.exit(1);
3476
+ }
3477
+ }
3478
+ });
3479
+
2907
3480
  // src/cli.ts
2908
3481
  var require2 = (0, import_node_module.createRequire)(importMetaUrl);
2909
3482
  var pkg = require2("../package.json");
2910
- var main = (0, import_citty7.defineCommand)({
3483
+ var main = (0, import_citty9.defineCommand)({
2911
3484
  meta: {
2912
3485
  name: "cursor-kit",
2913
3486
  version: pkg.version,
@@ -2923,8 +3496,10 @@ var main = (0, import_citty7.defineCommand)({
2923
3496
  pull: pullCommand,
2924
3497
  list: listCommand,
2925
3498
  remove: removeCommand,
2926
- instance: instanceCommand
3499
+ instance: instanceCommand,
3500
+ share: shareCommand,
3501
+ receive: receiveCommand
2927
3502
  }
2928
3503
  });
2929
- (0, import_citty7.runMain)(main);
3504
+ (0, import_citty9.runMain)(main);
2930
3505
  //# sourceMappingURL=cli.cjs.map