cursor-kit-cli 1.1.1-beta → 1.2.0-beta

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
@@ -39,6 +39,7 @@ ck init
39
39
  - **📜 Commands** - Reusable prompt templates for common tasks
40
40
  - **📋 Rules** - Project-specific AI behavior guidelines
41
41
  - **🔄 Sync** - Keep configurations updated from the community
42
+ - **đŸ–Ĩī¸ Multi-Instance** - Run multiple Cursor accounts simultaneously (macOS)
42
43
  - **🎨 Beautiful CLI** - Delightful terminal experience
43
44
 
44
45
  ## đŸ“Ļ Commands
@@ -98,6 +99,41 @@ cursor-kit remove -t command -n my-command # Quick remove
98
99
  cursor-kit remove -f # Skip confirmation
99
100
  ```
100
101
 
102
+ ### `instance`
103
+
104
+ Manage multiple Cursor IDE instances for multi-account login. **macOS only.**
105
+
106
+ This command allows you to create separate Cursor instances, each with its own identity (bundle ID) and data directory. Perfect for users who need to work with multiple Cursor accounts simultaneously.
107
+
108
+ ```bash
109
+ cursor-kit instance # Interactive mode
110
+ cursor-kit instance -l # List existing instances
111
+ cursor-kit instance -a create -n "Cursor Work" # Create instance
112
+ cursor-kit instance -a remove -n "Cursor Work" # Remove instance
113
+ ```
114
+
115
+ **How it works:**
116
+ - Creates a copy of Cursor.app in `~/Applications/`
117
+ - Assigns a unique bundle identifier (e.g., `com.cursor.cursorwork`)
118
+ - Creates a separate data directory in `~/Library/Application Support/`
119
+ - Re-signs the app with an ad-hoc signature
120
+ - Each instance can be logged into with a different Cursor account
121
+
122
+ **Example workflow:**
123
+ ```bash
124
+ # Create an instance for work projects
125
+ cursor-kit instance -a create -n "Cursor Enterprise"
126
+
127
+ # Create another for personal use
128
+ cursor-kit instance -a create -n "Cursor Personal"
129
+
130
+ # List all your instances
131
+ cursor-kit instance --list
132
+
133
+ # Remove an instance when no longer needed
134
+ cursor-kit instance -a remove -n "Cursor Personal"
135
+ ```
136
+
101
137
  ## 📁 Directory Structure
102
138
 
103
139
  After running `cursor-kit init`, your project will have:
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # OS guard – must be macOS
5
+ if [ "$(uname)" != "Darwin" ]; then
6
+ echo "This command only works on macOS." >&2
7
+ exit 1
8
+ fi
9
+
10
+
11
+ # === Arguments ===
12
+ NEW_APP_NAME="${1:-}"
13
+
14
+ if [ -z "$NEW_APP_NAME" ]; then
15
+ echo "Usage: $0 \"New App Name (e.g. Cursor Enterprise)\"" >&2
16
+ exit 1
17
+ fi
18
+
19
+ # === Config based on name ===
20
+ ORIG_APP_NAME="Cursor"
21
+ ORIG_APP="/Applications/$ORIG_APP_NAME.app"
22
+
23
+ # slug: lowercase, alphanumeric only, used in bundle id / data dir
24
+ SLUG="$(echo "$NEW_APP_NAME" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]')"
25
+ [ -z "$SLUG" ] && SLUG="custom"
26
+
27
+ BUNDLE_ID="com.cursor.$SLUG"
28
+ TARGET_APP="$HOME/Applications/$NEW_APP_NAME.app"
29
+ DATA_DIR="$HOME/Library/Application Support/${NEW_APP_NAME// /}"
30
+
31
+ echo "New app name: $NEW_APP_NAME"
32
+ echo "Bundle identifier: $BUNDLE_ID"
33
+ echo "Target app: $TARGET_APP"
34
+ echo "Data dir: $DATA_DIR"
35
+ echo
36
+
37
+ # === Step 1: Copy original app ===
38
+ if [ ! -d "$ORIG_APP" ]; then
39
+ echo "❌ Original app not found at: $ORIG_APP" >&2
40
+ exit 1
41
+ fi
42
+
43
+ echo "📁 Creating $HOME/Applications if needed..."
44
+ mkdir -p "$HOME/Applications"
45
+
46
+ echo "đŸ“Ļ Copying $ORIG_APP -> $TARGET_APP ..."
47
+ cp -R "$ORIG_APP" "$TARGET_APP"
48
+
49
+ # === Step 3: Give each app its own identity ===
50
+ APP="$TARGET_APP"
51
+ PLIST="$APP/Contents/Info.plist"
52
+
53
+ echo "📝 Setting CFBundleIdentifier to $BUNDLE_ID in $PLIST ..."
54
+ if /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $BUNDLE_ID" "$PLIST"; then
55
+ echo "✅ CFBundleIdentifier updated."
56
+ else
57
+ echo "â„šī¸ CFBundleIdentifier not found, adding..."
58
+ /usr/libexec/PlistBuddy -c "Add :CFBundleIdentifier string $BUNDLE_ID" "$PLIST"
59
+ echo "✅ CFBundleIdentifier added."
60
+ fi
61
+
62
+ echo "🔏 Re-signing app (ad-hoc signature)..."
63
+ codesign --force --deep --sign - "$APP"
64
+
65
+ # === Step 4: Create separate data folders ===
66
+ echo "📂 Creating data dir: $DATA_DIR ..."
67
+ mkdir -p "$DATA_DIR"
68
+
69
+ # === Step 5: Launch ===
70
+ echo "🚀 Launching $NEW_APP_NAME..."
71
+ open -n "$APP" --args --user-data-dir "$DATA_DIR"
72
+
73
+ echo "✅ Done."
74
+
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # OS guard – must be macOS
5
+ if [ "$(uname)" != "Darwin" ]; then
6
+ echo "This command only works on macOS." >&2
7
+ exit 1
8
+ fi
9
+
10
+ # === Parse arguments ===
11
+ SKIP_CONFIRM=false
12
+ NEW_APP_NAME=""
13
+
14
+ while [[ $# -gt 0 ]]; do
15
+ case "$1" in
16
+ -y|--yes|--force)
17
+ SKIP_CONFIRM=true
18
+ shift
19
+ ;;
20
+ *)
21
+ NEW_APP_NAME="$1"
22
+ shift
23
+ ;;
24
+ esac
25
+ done
26
+
27
+ if [ -z "$NEW_APP_NAME" ]; then
28
+ echo "Usage: $0 [-y|--yes] \"App Name (e.g. Cursor Enterprise)\"" >&2
29
+ exit 1
30
+ fi
31
+
32
+ APP_PATH="$HOME/Applications/$NEW_APP_NAME.app"
33
+ DATA_DIR="$HOME/Library/Application Support/${NEW_APP_NAME// /}"
34
+
35
+ echo "This will remove:"
36
+ echo " App bundle: $APP_PATH"
37
+ echo " Data dir: $DATA_DIR"
38
+ echo
39
+
40
+ # Skip confirmation if --yes flag is provided
41
+ if [ "$SKIP_CONFIRM" = true ]; then
42
+ ans="y"
43
+ else
44
+ read -r -p "Are you sure? [y/N] " ans
45
+ fi
46
+
47
+ case "$ans" in
48
+ y|Y|yes|YES)
49
+ if [ -d "$APP_PATH" ]; then
50
+ echo "🗑 Removing app bundle..."
51
+ rm -rf "$APP_PATH"
52
+ else
53
+ echo "â„šī¸ App not found, skipping: $APP_PATH"
54
+ fi
55
+
56
+ if [ -d "$DATA_DIR" ]; then
57
+ echo "🗑 Removing data directory..."
58
+ rm -rf "$DATA_DIR"
59
+ else
60
+ echo "â„šī¸ Data dir not found, skipping: $DATA_DIR"
61
+ fi
62
+
63
+ echo "✅ Cleanup complete."
64
+ ;;
65
+ *)
66
+ echo "Cancelled."
67
+ exit 0
68
+ ;;
69
+ esac
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_citty6 = require("citty");
31
+ var import_citty7 = require("citty");
32
32
  var import_node_module = require("module");
33
33
 
34
34
  // src/utils/branding.ts
@@ -54,6 +54,9 @@ function printSuccess(message) {
54
54
  function printInfo(message) {
55
55
  console.log(import_picocolors.default.cyan("\u2139") + import_picocolors.default.dim(" ") + message);
56
56
  }
57
+ function printWarning(message) {
58
+ console.log(import_picocolors.default.yellow("\u26A0") + import_picocolors.default.dim(" ") + message);
59
+ }
57
60
  function printDivider() {
58
61
  console.log(import_picocolors.default.dim("\u2500".repeat(50)));
59
62
  }
@@ -63,8 +66,8 @@ function printVersion(version) {
63
66
  );
64
67
  console.log();
65
68
  }
66
- function highlight(text2) {
67
- return import_picocolors.default.cyan(text2);
69
+ function highlight(text3) {
70
+ return import_picocolors.default.cyan(text3);
68
71
  }
69
72
 
70
73
  // src/commands/init.ts
@@ -939,10 +942,268 @@ var removeCommand = (0, import_citty5.defineCommand)({
939
942
  }
940
943
  });
941
944
 
945
+ // src/commands/instance.ts
946
+ var import_citty6 = require("citty");
947
+ var p6 = __toESM(require("@clack/prompts"), 1);
948
+ var import_picocolors7 = __toESM(require("picocolors"), 1);
949
+ var import_node_child_process = require("child_process");
950
+ var import_node_path7 = require("path");
951
+ var import_node_url2 = require("url");
952
+ var import_node_fs2 = require("fs");
953
+ function getBinPath() {
954
+ const currentDir = (0, import_node_path7.dirname)((0, import_node_url2.fileURLToPath)(importMetaUrl));
955
+ const possiblePaths = [
956
+ (0, import_node_path7.join)(currentDir, "..", "..", "bin"),
957
+ (0, import_node_path7.join)(currentDir, "..", "bin")
958
+ ];
959
+ for (const binPath of possiblePaths) {
960
+ if ((0, import_node_fs2.existsSync)(binPath)) {
961
+ return binPath;
962
+ }
963
+ }
964
+ return possiblePaths[0];
965
+ }
966
+ function ensureExecutable(scriptPath) {
967
+ try {
968
+ (0, import_node_fs2.chmodSync)(scriptPath, 493);
969
+ } catch {
970
+ }
971
+ }
972
+ function getExistingInstances() {
973
+ const userAppsDir = (0, import_node_path7.join)(process.env.HOME ?? "", "Applications");
974
+ if (!(0, import_node_fs2.existsSync)(userAppsDir)) return [];
975
+ try {
976
+ const apps = (0, import_node_fs2.readdirSync)(userAppsDir);
977
+ return apps.filter((app) => app.startsWith("Cursor") && app.endsWith(".app") && app !== "Cursor.app").map((app) => ({
978
+ name: app.replace(".app", ""),
979
+ path: (0, import_node_path7.join)(userAppsDir, app)
980
+ }));
981
+ } catch {
982
+ return [];
983
+ }
984
+ }
985
+ function runScript(scriptPath, args) {
986
+ return new Promise((resolve2) => {
987
+ ensureExecutable(scriptPath);
988
+ const child = (0, import_node_child_process.spawn)(scriptPath, args, {
989
+ stdio: "inherit"
990
+ });
991
+ child.on("close", (code) => {
992
+ resolve2(code ?? 1);
993
+ });
994
+ child.on("error", () => {
995
+ resolve2(1);
996
+ });
997
+ });
998
+ }
999
+ var instanceCommand = (0, import_citty6.defineCommand)({
1000
+ meta: {
1001
+ name: "instance",
1002
+ description: "Manage Cursor IDE instances for multi-account login (macOS only)"
1003
+ },
1004
+ args: {
1005
+ action: {
1006
+ type: "string",
1007
+ alias: "a",
1008
+ description: "Action: 'create' or 'remove'"
1009
+ },
1010
+ name: {
1011
+ type: "string",
1012
+ alias: "n",
1013
+ description: "Name of the instance (e.g. 'Cursor Enterprise')"
1014
+ },
1015
+ list: {
1016
+ type: "boolean",
1017
+ alias: "l",
1018
+ description: "List existing Cursor instances",
1019
+ default: false
1020
+ }
1021
+ },
1022
+ async run({ args }) {
1023
+ p6.intro(import_picocolors7.default.bgCyan(import_picocolors7.default.black(" cursor-kit instance ")));
1024
+ if (process.platform !== "darwin") {
1025
+ console.log();
1026
+ printWarning("This command only works on macOS.");
1027
+ console.log(import_picocolors7.default.dim(" Cursor instance management requires macOS-specific features."));
1028
+ console.log();
1029
+ p6.outro(import_picocolors7.default.dim("Exiting"));
1030
+ process.exit(1);
1031
+ }
1032
+ if (args.list) {
1033
+ const instances = getExistingInstances();
1034
+ printDivider();
1035
+ console.log();
1036
+ if (instances.length === 0) {
1037
+ printInfo("No custom Cursor instances found.");
1038
+ console.log(import_picocolors7.default.dim(" Run ") + highlight("cursor-kit instance") + import_picocolors7.default.dim(" to create one."));
1039
+ } else {
1040
+ console.log(import_picocolors7.default.bold(import_picocolors7.default.cyan(" \u{1F5A5} Cursor Instances")) + import_picocolors7.default.dim(` (${instances.length})`));
1041
+ console.log();
1042
+ for (const instance of instances) {
1043
+ console.log(` ${import_picocolors7.default.green("\u25CF")} ${highlight(instance.name)}`);
1044
+ console.log(import_picocolors7.default.dim(` \u2514\u2500 ${instance.path}`));
1045
+ }
1046
+ }
1047
+ console.log();
1048
+ printDivider();
1049
+ p6.outro(import_picocolors7.default.dim(`Total: ${instances.length} instance${instances.length !== 1 ? "s" : ""}`));
1050
+ return;
1051
+ }
1052
+ const s = p6.spinner();
1053
+ s.start("Checking prerequisites...");
1054
+ const binPath = getBinPath();
1055
+ const createScript = (0, import_node_path7.join)(binPath, "cursor-new-instance");
1056
+ const removeScript = (0, import_node_path7.join)(binPath, "cursor-remove-instance");
1057
+ const scriptsExist = (0, import_node_fs2.existsSync)(createScript) && (0, import_node_fs2.existsSync)(removeScript);
1058
+ if (!scriptsExist) {
1059
+ s.stop("Prerequisites check failed");
1060
+ console.log();
1061
+ printWarning("Required scripts not found.");
1062
+ console.log(import_picocolors7.default.dim(` Expected at: ${binPath}`));
1063
+ console.log();
1064
+ p6.outro(import_picocolors7.default.red("Installation may be corrupted"));
1065
+ process.exit(1);
1066
+ }
1067
+ const originalCursor = "/Applications/Cursor.app";
1068
+ if (!(0, import_node_fs2.existsSync)(originalCursor)) {
1069
+ s.stop("Prerequisites check failed");
1070
+ console.log();
1071
+ printWarning("Cursor.app not found in /Applications");
1072
+ console.log(import_picocolors7.default.dim(" Please install Cursor IDE first."));
1073
+ console.log();
1074
+ p6.outro(import_picocolors7.default.red("Cursor IDE required"));
1075
+ process.exit(1);
1076
+ }
1077
+ s.stop("Prerequisites verified");
1078
+ const existingInstances = getExistingInstances();
1079
+ let action;
1080
+ let instanceName;
1081
+ if (args.action && ["create", "remove"].includes(args.action)) {
1082
+ action = args.action;
1083
+ } else {
1084
+ const actionResult = await p6.select({
1085
+ message: "What would you like to do?",
1086
+ options: [
1087
+ {
1088
+ value: "create",
1089
+ label: "Create new instance",
1090
+ hint: "Clone Cursor with separate identity"
1091
+ },
1092
+ {
1093
+ value: "remove",
1094
+ label: "Remove instance",
1095
+ hint: existingInstances.length > 0 ? `${existingInstances.length} instance${existingInstances.length !== 1 ? "s" : ""} available` : "No instances to remove"
1096
+ }
1097
+ ]
1098
+ });
1099
+ if (p6.isCancel(actionResult)) {
1100
+ p6.cancel("Operation cancelled");
1101
+ process.exit(0);
1102
+ }
1103
+ action = actionResult;
1104
+ }
1105
+ if (args.name) {
1106
+ instanceName = args.name;
1107
+ } else if (action === "remove" && existingInstances.length > 0) {
1108
+ const instanceResult = await p6.select({
1109
+ message: "Select instance to remove:",
1110
+ options: existingInstances.map((inst) => ({
1111
+ value: inst.name,
1112
+ label: inst.name,
1113
+ hint: inst.path
1114
+ }))
1115
+ });
1116
+ if (p6.isCancel(instanceResult)) {
1117
+ p6.cancel("Operation cancelled");
1118
+ process.exit(0);
1119
+ }
1120
+ instanceName = instanceResult;
1121
+ } else if (action === "remove" && existingInstances.length === 0) {
1122
+ console.log();
1123
+ printInfo("No custom Cursor instances found to remove.");
1124
+ console.log();
1125
+ p6.outro(import_picocolors7.default.dim("Nothing to do"));
1126
+ return;
1127
+ } else {
1128
+ const nameResult = await p6.text({
1129
+ message: "Enter a name for the new instance:",
1130
+ placeholder: "Cursor Enterprise",
1131
+ validate: (value) => {
1132
+ if (!value.trim()) return "Instance name is required";
1133
+ if (value.length < 2) return "Name must be at least 2 characters";
1134
+ const existing = existingInstances.find(
1135
+ (i) => i.name.toLowerCase() === value.toLowerCase()
1136
+ );
1137
+ if (existing) return `Instance "${value}" already exists`;
1138
+ return void 0;
1139
+ }
1140
+ });
1141
+ if (p6.isCancel(nameResult)) {
1142
+ p6.cancel("Operation cancelled");
1143
+ process.exit(0);
1144
+ }
1145
+ instanceName = nameResult;
1146
+ }
1147
+ printDivider();
1148
+ console.log();
1149
+ console.log(import_picocolors7.default.bold(import_picocolors7.default.cyan(" \u{1F4CB} Summary")));
1150
+ console.log();
1151
+ console.log(` ${import_picocolors7.default.dim("Action:")} ${action === "create" ? import_picocolors7.default.green("Create") : import_picocolors7.default.yellow("Remove")}`);
1152
+ console.log(` ${import_picocolors7.default.dim("Instance:")} ${highlight(instanceName)}`);
1153
+ if (action === "create") {
1154
+ const slug = instanceName.toLowerCase().replace(/[^a-z0-9]/g, "");
1155
+ console.log(` ${import_picocolors7.default.dim("Bundle ID:")} ${import_picocolors7.default.dim("com.cursor.")}${highlight(slug)}`);
1156
+ console.log(` ${import_picocolors7.default.dim("Location:")} ${import_picocolors7.default.dim("~/Applications/")}${highlight(instanceName + ".app")}`);
1157
+ } else {
1158
+ const targetPath = (0, import_node_path7.join)(process.env.HOME ?? "", "Applications", `${instanceName}.app`);
1159
+ console.log(` ${import_picocolors7.default.dim("Path:")} ${import_picocolors7.default.dim(targetPath)}`);
1160
+ }
1161
+ console.log();
1162
+ printDivider();
1163
+ console.log();
1164
+ const shouldContinue = await p6.confirm({
1165
+ message: action === "create" ? "Create this Cursor instance?" : "Remove this Cursor instance? This cannot be undone.",
1166
+ initialValue: action === "create"
1167
+ });
1168
+ if (p6.isCancel(shouldContinue) || !shouldContinue) {
1169
+ p6.cancel("Operation cancelled");
1170
+ process.exit(0);
1171
+ }
1172
+ console.log();
1173
+ printDivider();
1174
+ console.log();
1175
+ const scriptPath = action === "create" ? createScript : removeScript;
1176
+ const scriptArgs = action === "remove" ? ["--yes", instanceName] : [instanceName];
1177
+ const exitCode = await runScript(scriptPath, scriptArgs);
1178
+ console.log();
1179
+ printDivider();
1180
+ console.log();
1181
+ if (exitCode === 0) {
1182
+ if (action === "create") {
1183
+ printSuccess(`Instance ${highlight(instanceName)} created successfully!`);
1184
+ console.log();
1185
+ console.log(import_picocolors7.default.dim(" Next steps:"));
1186
+ console.log(import_picocolors7.default.dim(" \u2022 The new instance should launch automatically"));
1187
+ console.log(import_picocolors7.default.dim(" \u2022 Sign in with a different Cursor account"));
1188
+ console.log(import_picocolors7.default.dim(" \u2022 Find it in ~/Applications/"));
1189
+ } else {
1190
+ printSuccess(`Instance ${highlight(instanceName)} removed successfully!`);
1191
+ }
1192
+ console.log();
1193
+ p6.outro(import_picocolors7.default.green("\u2728 Done!"));
1194
+ } else {
1195
+ printWarning(`Operation completed with exit code ${exitCode}`);
1196
+ console.log();
1197
+ p6.outro(import_picocolors7.default.yellow("Check the output above for details"));
1198
+ process.exit(exitCode);
1199
+ }
1200
+ }
1201
+ });
1202
+
942
1203
  // src/cli.ts
943
1204
  var require2 = (0, import_node_module.createRequire)(importMetaUrl);
944
1205
  var pkg = require2("../package.json");
945
- var main = (0, import_citty6.defineCommand)({
1206
+ var main = (0, import_citty7.defineCommand)({
946
1207
  meta: {
947
1208
  name: "cursor-kit",
948
1209
  version: pkg.version,
@@ -957,8 +1218,9 @@ var main = (0, import_citty6.defineCommand)({
957
1218
  add: addCommand,
958
1219
  pull: pullCommand,
959
1220
  list: listCommand,
960
- remove: removeCommand
1221
+ remove: removeCommand,
1222
+ instance: instanceCommand
961
1223
  }
962
1224
  });
963
- (0, import_citty6.runMain)(main);
1225
+ (0, import_citty7.runMain)(main);
964
1226
  //# sourceMappingURL=cli.cjs.map