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 +46 -0
- package/dist/cli.cjs +580 -5
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +579 -4
- package/dist/cli.js.map +1 -1
- package/package.json +6 -2
- package/templates/skills/swiftui-design/SKILL.md +39 -0
- package/templates/skills/swiftui-design/references/ios.md +0 -0
- package/templates/skills/swiftui-design/references/macos.md +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { defineCommand as
|
|
4
|
+
import { defineCommand as defineCommand9, runMain } from "citty";
|
|
5
5
|
import { createRequire } from "module";
|
|
6
6
|
|
|
7
7
|
// src/utils/branding.ts
|
|
@@ -24,6 +24,9 @@ function printBanner() {
|
|
|
24
24
|
function printSuccess(message) {
|
|
25
25
|
console.log(pc.green("\u2713") + pc.dim(" ") + message);
|
|
26
26
|
}
|
|
27
|
+
function printError(message) {
|
|
28
|
+
console.log(pc.red("\u2717") + pc.dim(" ") + message);
|
|
29
|
+
}
|
|
27
30
|
function printInfo(message) {
|
|
28
31
|
console.log(pc.cyan("\u2139") + pc.dim(" ") + message);
|
|
29
32
|
}
|
|
@@ -2319,7 +2322,7 @@ function getStorageLocationLabel(location) {
|
|
|
2319
2322
|
function isHomeBinInPath() {
|
|
2320
2323
|
const pathEnv = process.env.PATH ?? "";
|
|
2321
2324
|
const homeBin = getHomeBinDir();
|
|
2322
|
-
return pathEnv.split(":").some((
|
|
2325
|
+
return pathEnv.split(":").some((p11) => p11 === homeBin || p11 === "~/bin" || p11 === "$HOME/bin");
|
|
2323
2326
|
}
|
|
2324
2327
|
function getPathSetupInstructions() {
|
|
2325
2328
|
const shell = process.env.SHELL ?? "/bin/zsh";
|
|
@@ -2885,10 +2888,580 @@ var instanceCommand = defineCommand6({
|
|
|
2885
2888
|
}
|
|
2886
2889
|
});
|
|
2887
2890
|
|
|
2891
|
+
// src/commands/share.ts
|
|
2892
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
2893
|
+
import * as p9 from "@clack/prompts";
|
|
2894
|
+
import pc9 from "picocolors";
|
|
2895
|
+
import { createServer } from "http";
|
|
2896
|
+
import { networkInterfaces } from "os";
|
|
2897
|
+
import archiver from "archiver";
|
|
2898
|
+
var DEFAULT_PORT = 8080;
|
|
2899
|
+
var MAX_PORT_RETRIES = 10;
|
|
2900
|
+
var METADATA_FILENAME = ".cursor-kit-share.json";
|
|
2901
|
+
function getLocalIp() {
|
|
2902
|
+
const nets = networkInterfaces();
|
|
2903
|
+
for (const name of Object.keys(nets)) {
|
|
2904
|
+
const netList = nets[name];
|
|
2905
|
+
if (!netList) continue;
|
|
2906
|
+
for (const net of netList) {
|
|
2907
|
+
if (net.family === "IPv4" && !net.internal) {
|
|
2908
|
+
return net.address;
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
return "127.0.0.1";
|
|
2913
|
+
}
|
|
2914
|
+
function detectAvailableConfigs(cwd) {
|
|
2915
|
+
const configs = [];
|
|
2916
|
+
const cursorDir = getCursorDir(cwd);
|
|
2917
|
+
if (dirExists(cursorDir)) {
|
|
2918
|
+
configs.push({
|
|
2919
|
+
type: "cursor",
|
|
2920
|
+
label: "Cursor",
|
|
2921
|
+
directory: ".cursor",
|
|
2922
|
+
path: cursorDir
|
|
2923
|
+
});
|
|
2924
|
+
}
|
|
2925
|
+
const agentDir = getAgentDir(cwd);
|
|
2926
|
+
if (dirExists(agentDir)) {
|
|
2927
|
+
configs.push({
|
|
2928
|
+
type: "google-antigravity",
|
|
2929
|
+
label: "Google AntiGravity",
|
|
2930
|
+
directory: ".agent",
|
|
2931
|
+
path: agentDir
|
|
2932
|
+
});
|
|
2933
|
+
}
|
|
2934
|
+
const githubDir = getGitHubDir(cwd);
|
|
2935
|
+
const copilotInstructionsPath = getCopilotInstructionsPath(cwd);
|
|
2936
|
+
const copilotInstructionsDir = getCopilotInstructionsDir(cwd);
|
|
2937
|
+
if (fileExists(copilotInstructionsPath) || dirExists(copilotInstructionsDir)) {
|
|
2938
|
+
configs.push({
|
|
2939
|
+
type: "github-copilot",
|
|
2940
|
+
label: "GitHub Copilot",
|
|
2941
|
+
directory: ".github",
|
|
2942
|
+
path: githubDir
|
|
2943
|
+
});
|
|
2944
|
+
}
|
|
2945
|
+
return configs;
|
|
2946
|
+
}
|
|
2947
|
+
function tryListen(server, port) {
|
|
2948
|
+
return new Promise((resolve2, reject) => {
|
|
2949
|
+
const handleError = (err) => {
|
|
2950
|
+
if (err.code === "EADDRINUSE") {
|
|
2951
|
+
reject(new Error(`Port ${port} is already in use`));
|
|
2952
|
+
} else {
|
|
2953
|
+
reject(err);
|
|
2954
|
+
}
|
|
2955
|
+
};
|
|
2956
|
+
server.once("error", handleError);
|
|
2957
|
+
server.listen(port, () => {
|
|
2958
|
+
server.removeListener("error", handleError);
|
|
2959
|
+
resolve2(port);
|
|
2960
|
+
});
|
|
2961
|
+
});
|
|
2962
|
+
}
|
|
2963
|
+
async function findAvailablePort(server, startPort) {
|
|
2964
|
+
for (let i = 0; i < MAX_PORT_RETRIES; i++) {
|
|
2965
|
+
const port = startPort + i;
|
|
2966
|
+
try {
|
|
2967
|
+
return await tryListen(server, port);
|
|
2968
|
+
} catch {
|
|
2969
|
+
if (i === MAX_PORT_RETRIES - 1) {
|
|
2970
|
+
throw new Error(`Could not find an available port after ${MAX_PORT_RETRIES} attempts`);
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
throw new Error("Unexpected error finding available port");
|
|
2975
|
+
}
|
|
2976
|
+
function createConfigZipStream(selectedConfigs) {
|
|
2977
|
+
const archive = archiver("zip", {
|
|
2978
|
+
zlib: { level: 6 }
|
|
2979
|
+
});
|
|
2980
|
+
archive.on("warning", (err) => {
|
|
2981
|
+
if (err.code !== "ENOENT") {
|
|
2982
|
+
console.warn(pc9.yellow(`Warning: ${err.message}`));
|
|
2983
|
+
}
|
|
2984
|
+
});
|
|
2985
|
+
archive.on("error", (err) => {
|
|
2986
|
+
throw err;
|
|
2987
|
+
});
|
|
2988
|
+
const metadata = {
|
|
2989
|
+
version: 1,
|
|
2990
|
+
configs: selectedConfigs.map((c) => c.type)
|
|
2991
|
+
};
|
|
2992
|
+
archive.append(JSON.stringify(metadata, null, 2), { name: METADATA_FILENAME });
|
|
2993
|
+
for (const config of selectedConfigs) {
|
|
2994
|
+
archive.directory(config.path, config.directory);
|
|
2995
|
+
}
|
|
2996
|
+
archive.finalize();
|
|
2997
|
+
return archive;
|
|
2998
|
+
}
|
|
2999
|
+
function handleRequest(req, res, selectedConfigs, onTransferStart, onTransferComplete, onTransferError) {
|
|
3000
|
+
if (req.method !== "GET" || req.url !== "/") {
|
|
3001
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
3002
|
+
res.end("Not Found");
|
|
3003
|
+
return;
|
|
3004
|
+
}
|
|
3005
|
+
onTransferStart();
|
|
3006
|
+
res.writeHead(200, {
|
|
3007
|
+
"Content-Type": "application/zip",
|
|
3008
|
+
"Content-Disposition": 'attachment; filename="cursor-kit-configs.zip"',
|
|
3009
|
+
"Transfer-Encoding": "chunked"
|
|
3010
|
+
});
|
|
3011
|
+
const archive = createConfigZipStream(selectedConfigs);
|
|
3012
|
+
res.on("finish", () => {
|
|
3013
|
+
onTransferComplete();
|
|
3014
|
+
});
|
|
3015
|
+
res.on("close", () => {
|
|
3016
|
+
if (!res.writableFinished) {
|
|
3017
|
+
onTransferError(new Error("Client disconnected before transfer completed"));
|
|
3018
|
+
}
|
|
3019
|
+
});
|
|
3020
|
+
archive.on("error", (err) => {
|
|
3021
|
+
onTransferError(err);
|
|
3022
|
+
res.destroy();
|
|
3023
|
+
});
|
|
3024
|
+
archive.pipe(res);
|
|
3025
|
+
}
|
|
3026
|
+
var shareCommand = defineCommand7({
|
|
3027
|
+
meta: {
|
|
3028
|
+
name: "share",
|
|
3029
|
+
description: "Share AI IDE configs (.cursor, .agent, .github) over LAN via HTTP"
|
|
3030
|
+
},
|
|
3031
|
+
args: {
|
|
3032
|
+
port: {
|
|
3033
|
+
type: "string",
|
|
3034
|
+
alias: "p",
|
|
3035
|
+
description: "Port to use for the HTTP server",
|
|
3036
|
+
default: String(DEFAULT_PORT)
|
|
3037
|
+
}
|
|
3038
|
+
},
|
|
3039
|
+
async run({ args }) {
|
|
3040
|
+
const cwd = process.cwd();
|
|
3041
|
+
const requestedPort = Number.parseInt(args.port, 10);
|
|
3042
|
+
if (Number.isNaN(requestedPort) || requestedPort < 1 || requestedPort > 65535) {
|
|
3043
|
+
p9.cancel("Invalid port number. Please specify a port between 1 and 65535.");
|
|
3044
|
+
process.exit(1);
|
|
3045
|
+
}
|
|
3046
|
+
p9.intro(pc9.bgCyan(pc9.black(" cursor-kit share ")));
|
|
3047
|
+
const availableConfigs = detectAvailableConfigs(cwd);
|
|
3048
|
+
if (availableConfigs.length === 0) {
|
|
3049
|
+
console.log();
|
|
3050
|
+
printError("No AI IDE configs found in the current directory.");
|
|
3051
|
+
printInfo("Run 'cursor-kit init' first to create configs for your AI IDE.");
|
|
3052
|
+
console.log();
|
|
3053
|
+
p9.cancel("Nothing to share");
|
|
3054
|
+
process.exit(1);
|
|
3055
|
+
}
|
|
3056
|
+
let selectedConfigs;
|
|
3057
|
+
if (availableConfigs.length === 1) {
|
|
3058
|
+
selectedConfigs = availableConfigs;
|
|
3059
|
+
printInfo(`Found: ${highlight(availableConfigs[0].label)} (${availableConfigs[0].directory})`);
|
|
3060
|
+
} else {
|
|
3061
|
+
const selection = await p9.multiselect({
|
|
3062
|
+
message: "Which AI IDE configs do you want to share?",
|
|
3063
|
+
options: availableConfigs.map((config) => ({
|
|
3064
|
+
value: config.type,
|
|
3065
|
+
label: config.label,
|
|
3066
|
+
hint: config.directory
|
|
3067
|
+
})),
|
|
3068
|
+
required: true
|
|
3069
|
+
});
|
|
3070
|
+
if (p9.isCancel(selection)) {
|
|
3071
|
+
p9.cancel("Operation cancelled");
|
|
3072
|
+
process.exit(0);
|
|
3073
|
+
}
|
|
3074
|
+
selectedConfigs = availableConfigs.filter(
|
|
3075
|
+
(c) => selection.includes(c.type)
|
|
3076
|
+
);
|
|
3077
|
+
}
|
|
3078
|
+
console.log();
|
|
3079
|
+
const s = p9.spinner();
|
|
3080
|
+
s.start("Starting HTTP server...");
|
|
3081
|
+
let server = null;
|
|
3082
|
+
let transferInProgress = false;
|
|
3083
|
+
const cleanup = () => {
|
|
3084
|
+
if (server) {
|
|
3085
|
+
server.close();
|
|
3086
|
+
server = null;
|
|
3087
|
+
}
|
|
3088
|
+
};
|
|
3089
|
+
process.on("SIGINT", () => {
|
|
3090
|
+
console.log();
|
|
3091
|
+
if (transferInProgress) {
|
|
3092
|
+
printError("Transfer interrupted");
|
|
3093
|
+
} else {
|
|
3094
|
+
printInfo("Server stopped");
|
|
3095
|
+
}
|
|
3096
|
+
cleanup();
|
|
3097
|
+
process.exit(0);
|
|
3098
|
+
});
|
|
3099
|
+
process.on("SIGTERM", () => {
|
|
3100
|
+
cleanup();
|
|
3101
|
+
process.exit(0);
|
|
3102
|
+
});
|
|
3103
|
+
try {
|
|
3104
|
+
server = createServer((req, res) => {
|
|
3105
|
+
handleRequest(
|
|
3106
|
+
req,
|
|
3107
|
+
res,
|
|
3108
|
+
selectedConfigs,
|
|
3109
|
+
() => {
|
|
3110
|
+
transferInProgress = true;
|
|
3111
|
+
console.log();
|
|
3112
|
+
printInfo("Connection established, transferring files...");
|
|
3113
|
+
},
|
|
3114
|
+
() => {
|
|
3115
|
+
transferInProgress = false;
|
|
3116
|
+
console.log();
|
|
3117
|
+
printSuccess("Transfer complete!");
|
|
3118
|
+
printInfo("Server will shut down automatically.");
|
|
3119
|
+
cleanup();
|
|
3120
|
+
process.exit(0);
|
|
3121
|
+
},
|
|
3122
|
+
(err) => {
|
|
3123
|
+
transferInProgress = false;
|
|
3124
|
+
printError(`Transfer failed: ${err.message}`);
|
|
3125
|
+
}
|
|
3126
|
+
);
|
|
3127
|
+
});
|
|
3128
|
+
const actualPort = await findAvailablePort(server, requestedPort);
|
|
3129
|
+
const localIp = getLocalIp();
|
|
3130
|
+
s.stop("HTTP server started");
|
|
3131
|
+
console.log();
|
|
3132
|
+
printDivider();
|
|
3133
|
+
console.log();
|
|
3134
|
+
printInfo("Sharing configs:");
|
|
3135
|
+
for (const config of selectedConfigs) {
|
|
3136
|
+
console.log(pc9.dim(` \u2514\u2500 ${pc9.green("\u25CF")} ${config.label} (${config.directory})`));
|
|
3137
|
+
}
|
|
3138
|
+
console.log();
|
|
3139
|
+
printInfo(`Local IP: ${highlight(localIp)}`);
|
|
3140
|
+
printInfo(`Port: ${highlight(String(actualPort))}`);
|
|
3141
|
+
console.log();
|
|
3142
|
+
printDivider();
|
|
3143
|
+
console.log();
|
|
3144
|
+
console.log(pc9.bold(" Run this command on the receiving machine:"));
|
|
3145
|
+
console.log();
|
|
3146
|
+
console.log(
|
|
3147
|
+
` ${pc9.cyan("$")} ${pc9.bold(pc9.green(`cursor-kit receive http://${localIp}:${actualPort}`))}`
|
|
3148
|
+
);
|
|
3149
|
+
console.log();
|
|
3150
|
+
printDivider();
|
|
3151
|
+
console.log();
|
|
3152
|
+
printInfo("Waiting for connection... (Press Ctrl+C to cancel)");
|
|
3153
|
+
} catch (error) {
|
|
3154
|
+
s.stop("Failed to start server");
|
|
3155
|
+
cleanup();
|
|
3156
|
+
p9.cancel(`Error: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3157
|
+
process.exit(1);
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
});
|
|
3161
|
+
|
|
3162
|
+
// src/commands/receive.ts
|
|
3163
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
3164
|
+
import * as p10 from "@clack/prompts";
|
|
3165
|
+
import pc10 from "picocolors";
|
|
3166
|
+
import { get as httpGet } from "http";
|
|
3167
|
+
import { createWriteStream, createReadStream } from "fs";
|
|
3168
|
+
import { pipeline } from "stream/promises";
|
|
3169
|
+
import { PassThrough } from "stream";
|
|
3170
|
+
import unzipper from "unzipper";
|
|
3171
|
+
import { join as join11 } from "path";
|
|
3172
|
+
import { tmpdir } from "os";
|
|
3173
|
+
import { randomUUID } from "crypto";
|
|
3174
|
+
var METADATA_FILENAME2 = ".cursor-kit-share.json";
|
|
3175
|
+
function isValidUrl(urlString) {
|
|
3176
|
+
try {
|
|
3177
|
+
const url = new URL(urlString);
|
|
3178
|
+
return url.protocol === "http:";
|
|
3179
|
+
} catch {
|
|
3180
|
+
return false;
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
function formatBytes(bytes) {
|
|
3184
|
+
if (bytes === 0) return "0 B";
|
|
3185
|
+
const k = 1024;
|
|
3186
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
3187
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
3188
|
+
return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
|
|
3189
|
+
}
|
|
3190
|
+
function getConfigInfo(type, cwd) {
|
|
3191
|
+
switch (type) {
|
|
3192
|
+
case "cursor":
|
|
3193
|
+
return {
|
|
3194
|
+
type,
|
|
3195
|
+
label: "Cursor",
|
|
3196
|
+
directory: ".cursor",
|
|
3197
|
+
targetPath: getCursorDir(cwd),
|
|
3198
|
+
hasConflict: dirExists(getCursorDir(cwd))
|
|
3199
|
+
};
|
|
3200
|
+
case "google-antigravity":
|
|
3201
|
+
return {
|
|
3202
|
+
type,
|
|
3203
|
+
label: "Google AntiGravity",
|
|
3204
|
+
directory: ".agent",
|
|
3205
|
+
targetPath: getAgentDir(cwd),
|
|
3206
|
+
hasConflict: dirExists(getAgentDir(cwd))
|
|
3207
|
+
};
|
|
3208
|
+
case "github-copilot":
|
|
3209
|
+
return {
|
|
3210
|
+
type,
|
|
3211
|
+
label: "GitHub Copilot",
|
|
3212
|
+
directory: ".github",
|
|
3213
|
+
targetPath: getGitHubDir(cwd),
|
|
3214
|
+
hasConflict: dirExists(getGitHubDir(cwd))
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
async function downloadToTemp(url) {
|
|
3219
|
+
const tempPath = join11(tmpdir(), `cursor-kit-receive-${randomUUID()}.zip`);
|
|
3220
|
+
return new Promise((resolve2, reject) => {
|
|
3221
|
+
const request = httpGet(url, (response) => {
|
|
3222
|
+
if (response.statusCode !== 200) {
|
|
3223
|
+
reject(new Error(`Server returned status ${response.statusCode}`));
|
|
3224
|
+
return;
|
|
3225
|
+
}
|
|
3226
|
+
const writeStream = createWriteStream(tempPath);
|
|
3227
|
+
const passThrough = new PassThrough();
|
|
3228
|
+
let size = 0;
|
|
3229
|
+
passThrough.on("data", (chunk) => {
|
|
3230
|
+
size += chunk.length;
|
|
3231
|
+
});
|
|
3232
|
+
response.pipe(passThrough).pipe(writeStream);
|
|
3233
|
+
writeStream.on("finish", () => {
|
|
3234
|
+
resolve2({ tempPath, size });
|
|
3235
|
+
});
|
|
3236
|
+
writeStream.on("error", (err) => {
|
|
3237
|
+
reject(err);
|
|
3238
|
+
});
|
|
3239
|
+
response.on("error", (err) => {
|
|
3240
|
+
reject(err);
|
|
3241
|
+
});
|
|
3242
|
+
});
|
|
3243
|
+
request.on("error", reject);
|
|
3244
|
+
request.on("timeout", () => {
|
|
3245
|
+
request.destroy();
|
|
3246
|
+
reject(new Error("Connection timed out"));
|
|
3247
|
+
});
|
|
3248
|
+
request.setTimeout(3e4);
|
|
3249
|
+
});
|
|
3250
|
+
}
|
|
3251
|
+
async function readMetadataFromZip(zipPath) {
|
|
3252
|
+
return new Promise((resolve2) => {
|
|
3253
|
+
const readStream = createReadStream(zipPath);
|
|
3254
|
+
const unzipStream = readStream.pipe(unzipper.Parse());
|
|
3255
|
+
let found = false;
|
|
3256
|
+
unzipStream.on("entry", async (entry) => {
|
|
3257
|
+
if (entry.path === METADATA_FILENAME2 && !found) {
|
|
3258
|
+
found = true;
|
|
3259
|
+
const chunks = [];
|
|
3260
|
+
entry.on("data", (chunk) => chunks.push(chunk));
|
|
3261
|
+
entry.on("end", () => {
|
|
3262
|
+
try {
|
|
3263
|
+
const content = Buffer.concat(chunks).toString("utf-8");
|
|
3264
|
+
const metadata = JSON.parse(content);
|
|
3265
|
+
resolve2(metadata);
|
|
3266
|
+
} catch {
|
|
3267
|
+
resolve2(null);
|
|
3268
|
+
}
|
|
3269
|
+
});
|
|
3270
|
+
} else {
|
|
3271
|
+
entry.autodrain();
|
|
3272
|
+
}
|
|
3273
|
+
});
|
|
3274
|
+
unzipStream.on("close", () => {
|
|
3275
|
+
if (!found) {
|
|
3276
|
+
resolve2(null);
|
|
3277
|
+
}
|
|
3278
|
+
});
|
|
3279
|
+
unzipStream.on("error", () => {
|
|
3280
|
+
resolve2(null);
|
|
3281
|
+
});
|
|
3282
|
+
});
|
|
3283
|
+
}
|
|
3284
|
+
async function extractWithStrategy(zipPath, cwd, configs, strategy) {
|
|
3285
|
+
if (strategy === "overwrite") {
|
|
3286
|
+
for (const config of configs) {
|
|
3287
|
+
if (config.hasConflict) {
|
|
3288
|
+
removeFile(config.targetPath);
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
const configDirs = new Set(configs.map((c) => c.directory));
|
|
3293
|
+
return new Promise((resolve2, reject) => {
|
|
3294
|
+
const readStream = createReadStream(zipPath);
|
|
3295
|
+
const unzipStream = readStream.pipe(unzipper.Parse());
|
|
3296
|
+
const extractPromises = [];
|
|
3297
|
+
unzipStream.on("entry", (entry) => {
|
|
3298
|
+
const entryPath = entry.path;
|
|
3299
|
+
if (entryPath === METADATA_FILENAME2) {
|
|
3300
|
+
entry.autodrain();
|
|
3301
|
+
return;
|
|
3302
|
+
}
|
|
3303
|
+
const topLevelDir = entryPath.split("/")[0];
|
|
3304
|
+
if (!configDirs.has(topLevelDir)) {
|
|
3305
|
+
entry.autodrain();
|
|
3306
|
+
return;
|
|
3307
|
+
}
|
|
3308
|
+
const targetPath = join11(cwd, entryPath);
|
|
3309
|
+
if (entry.type === "Directory") {
|
|
3310
|
+
ensureDir(targetPath);
|
|
3311
|
+
entry.autodrain();
|
|
3312
|
+
return;
|
|
3313
|
+
}
|
|
3314
|
+
if (strategy === "merge" && fileExists(targetPath)) {
|
|
3315
|
+
entry.autodrain();
|
|
3316
|
+
return;
|
|
3317
|
+
}
|
|
3318
|
+
ensureDir(join11(targetPath, ".."));
|
|
3319
|
+
const writeStream = createWriteStream(targetPath);
|
|
3320
|
+
const extractPromise = pipeline(entry, writeStream);
|
|
3321
|
+
extractPromises.push(extractPromise);
|
|
3322
|
+
});
|
|
3323
|
+
unzipStream.on("close", async () => {
|
|
3324
|
+
try {
|
|
3325
|
+
await Promise.all(extractPromises);
|
|
3326
|
+
resolve2();
|
|
3327
|
+
} catch (err) {
|
|
3328
|
+
reject(err);
|
|
3329
|
+
}
|
|
3330
|
+
});
|
|
3331
|
+
unzipStream.on("error", reject);
|
|
3332
|
+
});
|
|
3333
|
+
}
|
|
3334
|
+
var receiveCommand = defineCommand8({
|
|
3335
|
+
meta: {
|
|
3336
|
+
name: "receive",
|
|
3337
|
+
description: "Receive and extract shared AI IDE configs from a cursor-kit share URL"
|
|
3338
|
+
},
|
|
3339
|
+
args: {
|
|
3340
|
+
url: {
|
|
3341
|
+
type: "positional",
|
|
3342
|
+
description: "The share URL (e.g., http://192.168.1.15:8080)",
|
|
3343
|
+
required: true
|
|
3344
|
+
},
|
|
3345
|
+
force: {
|
|
3346
|
+
type: "boolean",
|
|
3347
|
+
alias: "f",
|
|
3348
|
+
description: "Overwrite existing configs without prompting",
|
|
3349
|
+
default: false
|
|
3350
|
+
}
|
|
3351
|
+
},
|
|
3352
|
+
async run({ args }) {
|
|
3353
|
+
const { url, force } = args;
|
|
3354
|
+
const cwd = process.cwd();
|
|
3355
|
+
if (!isValidUrl(url)) {
|
|
3356
|
+
p10.cancel("Invalid URL. Please provide a valid HTTP URL (e.g., http://192.168.1.15:8080)");
|
|
3357
|
+
process.exit(1);
|
|
3358
|
+
}
|
|
3359
|
+
p10.intro(pc10.bgCyan(pc10.black(" cursor-kit receive ")));
|
|
3360
|
+
console.log();
|
|
3361
|
+
printInfo(`Source: ${highlight(url)}`);
|
|
3362
|
+
printInfo(`Destination: ${highlight(cwd)}`);
|
|
3363
|
+
console.log();
|
|
3364
|
+
printDivider();
|
|
3365
|
+
console.log();
|
|
3366
|
+
const s = p10.spinner();
|
|
3367
|
+
s.start("Connecting to server...");
|
|
3368
|
+
let tempPath = null;
|
|
3369
|
+
let receivedSize = 0;
|
|
3370
|
+
try {
|
|
3371
|
+
const result = await downloadToTemp(url);
|
|
3372
|
+
tempPath = result.tempPath;
|
|
3373
|
+
receivedSize = result.size;
|
|
3374
|
+
s.stop("Connected and downloaded");
|
|
3375
|
+
printInfo(`Received: ${highlight(formatBytes(receivedSize))}`);
|
|
3376
|
+
console.log();
|
|
3377
|
+
s.start("Reading metadata...");
|
|
3378
|
+
const metadata = await readMetadataFromZip(tempPath);
|
|
3379
|
+
if (!metadata || !metadata.configs || metadata.configs.length === 0) {
|
|
3380
|
+
s.stop("Invalid share file");
|
|
3381
|
+
printError("This doesn't appear to be a valid cursor-kit share.");
|
|
3382
|
+
printInfo("The share file is missing required metadata.");
|
|
3383
|
+
p10.cancel("Invalid share format");
|
|
3384
|
+
removeFile(tempPath);
|
|
3385
|
+
process.exit(1);
|
|
3386
|
+
}
|
|
3387
|
+
s.stop("Metadata loaded");
|
|
3388
|
+
const configs = metadata.configs.map((type) => getConfigInfo(type, cwd));
|
|
3389
|
+
const conflictingConfigs = configs.filter((c) => c.hasConflict);
|
|
3390
|
+
printInfo("Configs in this share:");
|
|
3391
|
+
for (const config of configs) {
|
|
3392
|
+
const conflictIcon = config.hasConflict ? pc10.yellow("\u26A0") : pc10.green("\u25CF");
|
|
3393
|
+
const conflictHint = config.hasConflict ? pc10.yellow(" (exists)") : "";
|
|
3394
|
+
console.log(pc10.dim(` \u2514\u2500 ${conflictIcon} ${config.label} (${config.directory})${conflictHint}`));
|
|
3395
|
+
}
|
|
3396
|
+
console.log();
|
|
3397
|
+
let strategy = "overwrite";
|
|
3398
|
+
if (conflictingConfigs.length > 0 && !force) {
|
|
3399
|
+
const strategySelection = await p10.select({
|
|
3400
|
+
message: `${conflictingConfigs.length} existing config(s) found. How do you want to handle conflicts?`,
|
|
3401
|
+
options: [
|
|
3402
|
+
{
|
|
3403
|
+
value: "overwrite",
|
|
3404
|
+
label: "Overwrite existing files",
|
|
3405
|
+
hint: "Replace all conflicting files"
|
|
3406
|
+
},
|
|
3407
|
+
{
|
|
3408
|
+
value: "merge",
|
|
3409
|
+
label: "Merge (keep existing, add new only)",
|
|
3410
|
+
hint: "Skip files that already exist"
|
|
3411
|
+
},
|
|
3412
|
+
{
|
|
3413
|
+
value: "cancel",
|
|
3414
|
+
label: "Cancel",
|
|
3415
|
+
hint: "Abort the operation"
|
|
3416
|
+
}
|
|
3417
|
+
]
|
|
3418
|
+
});
|
|
3419
|
+
if (p10.isCancel(strategySelection) || strategySelection === "cancel") {
|
|
3420
|
+
p10.cancel("Operation cancelled");
|
|
3421
|
+
removeFile(tempPath);
|
|
3422
|
+
process.exit(0);
|
|
3423
|
+
}
|
|
3424
|
+
strategy = strategySelection;
|
|
3425
|
+
}
|
|
3426
|
+
s.start("Extracting configs...");
|
|
3427
|
+
await extractWithStrategy(tempPath, cwd, configs, strategy);
|
|
3428
|
+
s.stop("Configs extracted");
|
|
3429
|
+
removeFile(tempPath);
|
|
3430
|
+
console.log();
|
|
3431
|
+
printDivider();
|
|
3432
|
+
console.log();
|
|
3433
|
+
for (const config of configs) {
|
|
3434
|
+
const action = config.hasConflict ? strategy === "merge" ? "merged" : "replaced" : "added";
|
|
3435
|
+
printSuccess(`${config.label}: ${highlight(action)}`);
|
|
3436
|
+
}
|
|
3437
|
+
console.log();
|
|
3438
|
+
p10.outro(pc10.green("\u2728 Transfer complete!"));
|
|
3439
|
+
} catch (error) {
|
|
3440
|
+
s.stop("Failed");
|
|
3441
|
+
if (tempPath) {
|
|
3442
|
+
removeFile(tempPath);
|
|
3443
|
+
}
|
|
3444
|
+
let errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
3445
|
+
if (errorMessage.includes("ECONNREFUSED")) {
|
|
3446
|
+
errorMessage = "Connection refused. Make sure the share server is running.";
|
|
3447
|
+
} else if (errorMessage.includes("ETIMEDOUT") || errorMessage.includes("timeout")) {
|
|
3448
|
+
errorMessage = "Connection timed out. Check the URL and network connection.";
|
|
3449
|
+
} else if (errorMessage.includes("ENOTFOUND")) {
|
|
3450
|
+
errorMessage = "Host not found. Check the URL and network connection.";
|
|
3451
|
+
} else if (errorMessage.includes("ECONNRESET") || errorMessage.includes("aborted")) {
|
|
3452
|
+
errorMessage = "Connection was reset. The server may have closed unexpectedly.";
|
|
3453
|
+
}
|
|
3454
|
+
printError(errorMessage);
|
|
3455
|
+
p10.cancel("Failed to receive configs");
|
|
3456
|
+
process.exit(1);
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
});
|
|
3460
|
+
|
|
2888
3461
|
// src/cli.ts
|
|
2889
3462
|
var require2 = createRequire(import.meta.url);
|
|
2890
3463
|
var pkg = require2("../package.json");
|
|
2891
|
-
var main =
|
|
3464
|
+
var main = defineCommand9({
|
|
2892
3465
|
meta: {
|
|
2893
3466
|
name: "cursor-kit",
|
|
2894
3467
|
version: pkg.version,
|
|
@@ -2904,7 +3477,9 @@ var main = defineCommand7({
|
|
|
2904
3477
|
pull: pullCommand,
|
|
2905
3478
|
list: listCommand,
|
|
2906
3479
|
remove: removeCommand,
|
|
2907
|
-
instance: instanceCommand
|
|
3480
|
+
instance: instanceCommand,
|
|
3481
|
+
share: shareCommand,
|
|
3482
|
+
receive: receiveCommand
|
|
2908
3483
|
}
|
|
2909
3484
|
});
|
|
2910
3485
|
runMain(main);
|