waitspin 0.1.13 → 0.1.14

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/cli.js CHANGED
@@ -84,8 +84,44 @@ const QODER_HOOK_TIMEOUT_SECONDS = 15;
84
84
  const QODER_HOOK_STATUS_MESSAGE = "WaitSpin sponsor check";
85
85
  const QODER_HOOK_EVENTS = ["UserPromptSubmit", "Stop"];
86
86
  const QODER_ACCEPTANCE_HINT = "Run a real Qoder TUI prompt and keep the sponsored system message visible for at least 5 seconds. WaitSpin schedules a delayed visibility check after display; Stop or next-prompt hooks refresh the same managed state.";
87
+ const WAITSPIN_EDITOR_EXTENSION_ID = "waitspin.waitspin-vscode";
88
+ const EDITOR_PUBLISHER_TARGET = "status-bar-fallback";
87
89
  const extensionTargets = {
88
- vscode: "vscode",
90
+ vscode: {
91
+ label: "VS Code",
92
+ mode: "bundled",
93
+ publisherTarget: EDITOR_PUBLISHER_TARGET,
94
+ },
95
+ cursor: {
96
+ label: "Cursor",
97
+ mode: "editor-cli",
98
+ publisherTarget: EDITOR_PUBLISHER_TARGET,
99
+ registryLabel: "VS Code Marketplace",
100
+ registryUrl: "https://marketplace.visualstudio.com/items?itemName=waitspin.waitspin-vscode",
101
+ binaryEnv: "WAITSPIN_CURSOR_EDITOR_BIN",
102
+ defaultBinary: "cursor",
103
+ windowsDefaultBinaries: ["cursor"],
104
+ windowsRelativeBinaries: [["Programs", "cursor", "Cursor.exe"]],
105
+ productMarker: "cursor",
106
+ executableBasenames: ["cursor", "cursor-editor"],
107
+ macosAppBinary: "/Applications/Cursor.app/Contents/Resources/app/bin/cursor",
108
+ macosUserAppBinary: path.join(os.homedir(), "Applications", "Cursor.app", "Contents", "Resources", "app", "bin", "cursor"),
109
+ },
110
+ devin: {
111
+ label: "Devin Desktop",
112
+ mode: "editor-cli",
113
+ publisherTarget: EDITOR_PUBLISHER_TARGET,
114
+ registryLabel: "Open VSX",
115
+ registryUrl: "https://open-vsx.org/extension/waitspin/waitspin-vscode",
116
+ binaryEnv: "WAITSPIN_DEVIN_DESKTOP_BIN",
117
+ defaultBinary: "devin-desktop",
118
+ windowsDefaultBinaries: ["devin", "devin-desktop"],
119
+ windowsRelativeBinaries: [["devin", "bin", "devin.exe"]],
120
+ productMarker: "devin",
121
+ executableBasenames: ["devin", "devin-desktop"],
122
+ macosAppBinary: "/Applications/Devin.app/Contents/Resources/app/bin/devin-desktop",
123
+ macosUserAppBinary: path.join(os.homedir(), "Applications", "Devin.app", "Contents", "Resources", "app", "bin", "devin-desktop"),
124
+ },
89
125
  };
90
126
  export function usageText() {
91
127
  return ([
@@ -100,9 +136,9 @@ export function usageText() {
100
136
  " waitspin wallet ledger [--limit N] [--json] [--base-url URL] [--api-key KEY]",
101
137
  " waitspin wallet payout --dry-run [--json] [--base-url URL] [--api-key KEY]",
102
138
  " waitspin wallet payout --confirm-test-transfer [--json] [--base-url URL] [--api-key KEY]",
103
- " waitspin extension install [--target vscode] [--json] [--base-url URL] [--api-key KEY] [--dry-run]",
104
- " waitspin extension status [--target vscode] [--json]",
105
- " waitspin extension uninstall [--target vscode] [--json] [--dry-run]",
139
+ " waitspin extension install [--target vscode|cursor|devin] [--json] [--base-url URL] [--api-key KEY] [--dry-run]",
140
+ " waitspin extension status [--target vscode|cursor|devin] [--json]",
141
+ " waitspin extension uninstall [--target vscode|cursor|devin] [--json] [--dry-run]",
106
142
  " waitspin install --all [--json] [--api-key KEY] [--compose-existing] [--dry-run]",
107
143
  " waitspin status --all [--json] [--demo]",
108
144
  " waitspin claude-code install [--json] [--api-key KEY] [--compose-existing] [--dry-run]",
@@ -730,7 +766,377 @@ export function generateInstallId() {
730
766
  return `wins_${randomUUID().replace(/-/g, "")}`;
731
767
  }
732
768
  export function publisherTargetForExtension(target) {
733
- return target === "vscode" ? "status-bar-fallback" : target;
769
+ return extensionTargets[target].publisherTarget;
770
+ }
771
+ function isEditorCliExtensionTarget(target) {
772
+ return extensionTargets[target].mode === "editor-cli";
773
+ }
774
+ const EDITOR_PROCESS_ENV_KEYS = [
775
+ "APPDATA",
776
+ "COLORTERM",
777
+ "COMSPEC",
778
+ "HOME",
779
+ "LANG",
780
+ "LC_ALL",
781
+ "LC_CTYPE",
782
+ "LOCALAPPDATA",
783
+ "LOGNAME",
784
+ "NODE_EXTRA_CA_CERTS",
785
+ "NO_PROXY",
786
+ "PATH",
787
+ "PATHEXT",
788
+ "SHELL",
789
+ "SSL_CERT_DIR",
790
+ "SSL_CERT_FILE",
791
+ "SystemRoot",
792
+ "TEMP",
793
+ "TERM",
794
+ "TMP",
795
+ "TMPDIR",
796
+ "USER",
797
+ "USERPROFILE",
798
+ "XDG_CACHE_HOME",
799
+ "XDG_CONFIG_HOME",
800
+ "XDG_DATA_HOME",
801
+ ];
802
+ function editorProxyEnvValue(value) {
803
+ if (!value)
804
+ return undefined;
805
+ try {
806
+ const url = new URL(value);
807
+ if (!["http:", "https:"].includes(url.protocol) ||
808
+ url.username ||
809
+ url.password ||
810
+ url.search ||
811
+ url.hash) {
812
+ return undefined;
813
+ }
814
+ return value;
815
+ }
816
+ catch {
817
+ return undefined;
818
+ }
819
+ }
820
+ function editorProcessEnv() {
821
+ const env = { NODE_ENV: process.env.NODE_ENV };
822
+ for (const key of EDITOR_PROCESS_ENV_KEYS) {
823
+ const value = process.env[key];
824
+ if (value !== undefined)
825
+ env[key] = value;
826
+ }
827
+ for (const key of ["HTTP_PROXY", "HTTPS_PROXY"]) {
828
+ const value = editorProxyEnvValue(process.env[key]);
829
+ if (value !== undefined)
830
+ env[key] = value;
831
+ }
832
+ return env;
833
+ }
834
+ function editorExecOptions(timeout) {
835
+ return { encoding: "utf8", timeout, env: editorProcessEnv() };
836
+ }
837
+ function uniqueNonEmpty(values) {
838
+ return Array.from(new Set(values.map((value) => value?.trim()).filter(Boolean)));
839
+ }
840
+ function editorCliCandidates(target) {
841
+ const descriptor = extensionTargets[target];
842
+ const localAppData = process.env.LOCALAPPDATA?.trim();
843
+ const windowsCandidates = process.platform === "win32"
844
+ ? [
845
+ ...descriptor.windowsDefaultBinaries,
846
+ ...(localAppData
847
+ ? descriptor.windowsRelativeBinaries.map((segments) => path.win32.join(localAppData, ...segments))
848
+ : []),
849
+ ]
850
+ : [];
851
+ return uniqueNonEmpty([
852
+ process.env[descriptor.binaryEnv],
853
+ ...(process.platform === "win32"
854
+ ? windowsCandidates
855
+ : [descriptor.defaultBinary]),
856
+ ...(process.platform === "darwin"
857
+ ? [descriptor.macosAppBinary, descriptor.macosUserAppBinary]
858
+ : []),
859
+ ]);
860
+ }
861
+ function isWindowsEditorCommandScript(binary) {
862
+ return process.platform === "win32" && /\.(?:cmd|bat)$/i.test(binary);
863
+ }
864
+ function assertSafeWindowsEditorCommandScript(binary) {
865
+ if (/["\r\n\u0000%&|<>^!]/.test(binary)) {
866
+ throw new Error("Refusing unsafe Windows editor command shim path.");
867
+ }
868
+ }
869
+ async function resolveWindowsEditorCommand(binary) {
870
+ if (process.platform !== "win32")
871
+ return binary;
872
+ if (path.win32.isAbsolute(binary) ||
873
+ binary.includes("\\") ||
874
+ binary.includes("/") ||
875
+ path.win32.extname(binary)) {
876
+ return binary;
877
+ }
878
+ const result = await execFileText("where.exe", [binary], editorExecOptions(5_000)).catch(() => null);
879
+ if (!result)
880
+ return binary;
881
+ return (result.stdout
882
+ .split(/\r?\n/)
883
+ .map((line) => line.trim())
884
+ .find((line) => path.win32.isAbsolute(line) &&
885
+ /\.(?:exe|com|cmd|bat)$/i.test(path.win32.extname(line))) || binary);
886
+ }
887
+ async function editorCommandForBinary(binary) {
888
+ const resolvedBinary = await resolveWindowsEditorCommand(binary);
889
+ if (!isWindowsEditorCommandScript(resolvedBinary)) {
890
+ return {
891
+ binary: resolvedBinary,
892
+ executable: resolvedBinary,
893
+ argumentPrefix: [],
894
+ };
895
+ }
896
+ assertSafeWindowsEditorCommandScript(resolvedBinary);
897
+ return {
898
+ binary: resolvedBinary,
899
+ executable: process.env.ComSpec || "cmd.exe",
900
+ argumentPrefix: [
901
+ "/d",
902
+ "/v:off",
903
+ "/s",
904
+ "/c",
905
+ "call",
906
+ resolvedBinary,
907
+ ],
908
+ };
909
+ }
910
+ function execEditorCommand(editor, args, timeout) {
911
+ return execFileText(editor.executable, [...editor.argumentPrefix, ...args], editorExecOptions(timeout));
912
+ }
913
+ function editorCliMatchesProduct(target, binary, version, help) {
914
+ const descriptor = extensionTargets[target];
915
+ const identityText = `${version}\n${help}`.toLowerCase();
916
+ if (identityText.includes(descriptor.productMarker))
917
+ return true;
918
+ const knownOtherProduct = ["cursor", "devin", "visual studio code"].some((marker) => marker !== descriptor.productMarker && identityText.includes(marker));
919
+ if (knownOtherProduct)
920
+ return false;
921
+ const basename = path.basename(binary).toLowerCase().replace(/\.(exe|cmd|bat)$/, "");
922
+ return descriptor.executableBasenames.includes(basename);
923
+ }
924
+ async function resolveEditorCli(target) {
925
+ const descriptor = extensionTargets[target];
926
+ let lastError;
927
+ for (const binary of editorCliCandidates(target)) {
928
+ try {
929
+ const editor = await editorCommandForBinary(binary);
930
+ const result = await execEditorCommand(editor, ["--version"], 5_000);
931
+ const helpResult = await execEditorCommand(editor, ["--help"], 5_000);
932
+ const help = `${helpResult.stdout}\n${helpResult.stderr}`;
933
+ if (![
934
+ "--install-extension",
935
+ "--list-extensions",
936
+ "--uninstall-extension",
937
+ ].every((option) => help.includes(option))) {
938
+ lastError = new Error(`${binary} does not expose editor extension management.`);
939
+ continue;
940
+ }
941
+ const version = `${result.stdout || result.stderr || descriptor.label}`.trim();
942
+ if (!editorCliMatchesProduct(target, editor.binary, version, help)) {
943
+ lastError = new Error(`${editor.binary} does not identify as ${descriptor.label}.`);
944
+ continue;
945
+ }
946
+ return {
947
+ ...editor,
948
+ version,
949
+ };
950
+ }
951
+ catch (error) {
952
+ lastError = error;
953
+ }
954
+ }
955
+ throw new Error(`${descriptor.label} was not detected. Install ${descriptor.label}, add ${process.platform === "win32" ? descriptor.windowsDefaultBinaries[0] : descriptor.defaultBinary} to PATH, or set ${descriptor.binaryEnv} to the editor executable path.`, { cause: lastError });
956
+ }
957
+ function parseEditorExtensionStatus(output) {
958
+ for (const rawLine of output.split(/\r?\n/)) {
959
+ const line = rawLine.trim();
960
+ const match = /^waitspin\.waitspin-vscode(?:@([^\s]+))?$/i.exec(line);
961
+ if (match) {
962
+ return { installed: true, version: match[1] || null };
963
+ }
964
+ }
965
+ return { installed: false, version: null };
966
+ }
967
+ async function readEditorExtensionStatus(editor) {
968
+ const result = await execEditorCommand(editor, ["--list-extensions", "--show-versions"], 10_000);
969
+ return parseEditorExtensionStatus(`${result.stdout}\n${result.stderr}`);
970
+ }
971
+ function editorActivationCommand(target) {
972
+ return `WaitSpin: Connect and earn inside ${extensionTargets[target].label}`;
973
+ }
974
+ async function runEditorExtensionInstall(target, flags) {
975
+ const descriptor = extensionTargets[target];
976
+ const editor = await resolveEditorCli(target);
977
+ const before = await readEditorExtensionStatus(editor);
978
+ const summary = {
979
+ ok: true,
980
+ target,
981
+ mode: EDITOR_PUBLISHER_TARGET,
982
+ detected: true,
983
+ editor: descriptor.label,
984
+ editor_version: editor.version,
985
+ editor_binary: editor.binary,
986
+ extension: WAITSPIN_EDITOR_EXTENSION_ID,
987
+ version: before.version,
988
+ installed: before.installed,
989
+ registry: descriptor.registryLabel,
990
+ registry_url: descriptor.registryUrl,
991
+ publisher_target: descriptor.publisherTarget,
992
+ publisher_registration_managed_by: "editor-extension",
993
+ activation_required: true,
994
+ next: { command: editorActivationCommand(target) },
995
+ next_command: editorActivationCommand(target),
996
+ };
997
+ if (booleanFlag(flags, "dry-run")) {
998
+ const dryRunPayload = {
999
+ ...summary,
1000
+ dry_run: true,
1001
+ planned_argv: [
1002
+ editor.binary,
1003
+ "--install-extension",
1004
+ WAITSPIN_EDITOR_EXTENSION_ID,
1005
+ "--force",
1006
+ ],
1007
+ };
1008
+ printCliOutput(flags, dryRunPayload, formatTargetInstallResult(dryRunPayload));
1009
+ return;
1010
+ }
1011
+ await execEditorCommand(editor, ["--install-extension", WAITSPIN_EDITOR_EXTENSION_ID, "--force"], 30_000);
1012
+ const after = await readEditorExtensionStatus(editor);
1013
+ if (!after.installed) {
1014
+ throw new Error(`${descriptor.label} did not report ${WAITSPIN_EDITOR_EXTENSION_ID} after installation.`);
1015
+ }
1016
+ const output = {
1017
+ ...summary,
1018
+ installed: true,
1019
+ version: after.version,
1020
+ };
1021
+ printCliOutput(flags, output, formatTargetInstallResult(output));
1022
+ }
1023
+ async function runEditorExtensionStatus(target, flags) {
1024
+ const descriptor = extensionTargets[target];
1025
+ let editor;
1026
+ try {
1027
+ editor = await resolveEditorCli(target);
1028
+ }
1029
+ catch (error) {
1030
+ const output = {
1031
+ ok: true,
1032
+ target,
1033
+ mode: EDITOR_PUBLISHER_TARGET,
1034
+ detected: false,
1035
+ installed: false,
1036
+ version: null,
1037
+ publisher_target: descriptor.publisherTarget,
1038
+ publisher_registration_managed_by: "editor-extension",
1039
+ activation_required: false,
1040
+ detection_error: redactCliSecretText(error instanceof Error ? error.message : String(error)),
1041
+ next: {
1042
+ command: `waitspin extension install --target ${target}`,
1043
+ },
1044
+ next_command: `waitspin extension install --target ${target}`,
1045
+ };
1046
+ printCliOutput(flags, output, formatTargetStatusResult(output));
1047
+ return;
1048
+ }
1049
+ const status = await readEditorExtensionStatus(editor);
1050
+ const output = {
1051
+ ok: true,
1052
+ target,
1053
+ mode: EDITOR_PUBLISHER_TARGET,
1054
+ detected: true,
1055
+ editor: descriptor.label,
1056
+ editor_version: editor.version,
1057
+ editor_binary: editor.binary,
1058
+ extension: WAITSPIN_EDITOR_EXTENSION_ID,
1059
+ version: status.version,
1060
+ installed: status.installed,
1061
+ publisher_target: descriptor.publisherTarget,
1062
+ publisher_registration_managed_by: "editor-extension",
1063
+ activation_required: status.installed,
1064
+ next: status.installed
1065
+ ? { command: editorActivationCommand(target) }
1066
+ : {
1067
+ command: `waitspin extension install --target ${target}`,
1068
+ },
1069
+ next_command: status.installed
1070
+ ? editorActivationCommand(target)
1071
+ : `waitspin extension install --target ${target}`,
1072
+ };
1073
+ printCliOutput(flags, output, formatTargetStatusResult(output));
1074
+ }
1075
+ async function runEditorExtensionUninstall(target, flags) {
1076
+ const descriptor = extensionTargets[target];
1077
+ let editor;
1078
+ try {
1079
+ editor = await resolveEditorCli(target);
1080
+ }
1081
+ catch (error) {
1082
+ const output = {
1083
+ ok: true,
1084
+ target,
1085
+ detected: false,
1086
+ installed: false,
1087
+ uninstalled: false,
1088
+ dry_run: booleanFlag(flags, "dry-run"),
1089
+ publisher_target: descriptor.publisherTarget,
1090
+ detection_error: redactCliSecretText(error instanceof Error ? error.message : String(error)),
1091
+ };
1092
+ printCliOutput(flags, output, formatTargetUninstallResult(output));
1093
+ return;
1094
+ }
1095
+ const before = await readEditorExtensionStatus(editor);
1096
+ const summary = {
1097
+ ok: true,
1098
+ target,
1099
+ detected: true,
1100
+ installed: before.installed,
1101
+ version: before.version,
1102
+ extension: WAITSPIN_EDITOR_EXTENSION_ID,
1103
+ publisher_target: descriptor.publisherTarget,
1104
+ };
1105
+ if (booleanFlag(flags, "dry-run")) {
1106
+ const output = {
1107
+ ...summary,
1108
+ dry_run: true,
1109
+ planned_argv: before.installed
1110
+ ? [
1111
+ editor.binary,
1112
+ "--uninstall-extension",
1113
+ WAITSPIN_EDITOR_EXTENSION_ID,
1114
+ ]
1115
+ : null,
1116
+ would_remove_extension: before.installed
1117
+ ? WAITSPIN_EDITOR_EXTENSION_ID
1118
+ : null,
1119
+ };
1120
+ printCliOutput(flags, output, formatTargetUninstallResult(output));
1121
+ return;
1122
+ }
1123
+ if (!before.installed) {
1124
+ const output = { ...summary, uninstalled: false };
1125
+ printCliOutput(flags, output, formatTargetUninstallResult(output));
1126
+ return;
1127
+ }
1128
+ await execEditorCommand(editor, ["--uninstall-extension", WAITSPIN_EDITOR_EXTENSION_ID], 30_000);
1129
+ const after = await readEditorExtensionStatus(editor);
1130
+ if (after.installed) {
1131
+ throw new Error(`${descriptor.label} still reports ${WAITSPIN_EDITOR_EXTENSION_ID} after uninstall.`);
1132
+ }
1133
+ const output = {
1134
+ ...summary,
1135
+ installed: false,
1136
+ uninstalled: true,
1137
+ removed_extension: WAITSPIN_EDITOR_EXTENSION_ID,
1138
+ };
1139
+ printCliOutput(flags, output, formatTargetUninstallResult(output));
734
1140
  }
735
1141
  function extensionInstallDir(_target) {
736
1142
  return path.join(os.homedir(), ".vscode", "extensions");
@@ -755,8 +1161,8 @@ function extensionMarkerPath(target) {
755
1161
  }
756
1162
  function resolveExtensionTarget(flags) {
757
1163
  const target = optionalFlag(flags, "target") || "vscode";
758
- if (!(target in extensionTargets)) {
759
- throw new Error(`Unsupported extension target: ${target}. Public WaitSpin installs currently support --target vscode only.`);
1164
+ if (!Object.prototype.hasOwnProperty.call(extensionTargets, target)) {
1165
+ throw new Error(`Unsupported extension target: ${target}. Public WaitSpin installs support --target vscode, cursor, or devin.`);
760
1166
  }
761
1167
  return target;
762
1168
  }
@@ -986,6 +1392,10 @@ async function validateVscodeExtensionRuntimeSource(sourceDir) {
986
1392
  }
987
1393
  export async function runExtensionInstall(flags) {
988
1394
  const target = resolveExtensionTarget(flags);
1395
+ if (isEditorCliExtensionTarget(target)) {
1396
+ await runEditorExtensionInstall(target, flags);
1397
+ return;
1398
+ }
989
1399
  const baseUrl = resolveCredentialedBaseUrl(flags);
990
1400
  const publisherTarget = publisherTargetForExtension(target);
991
1401
  const extensionDir = await resolveExtensionDir(flags);
@@ -1094,6 +1504,10 @@ function managedInstalledPathStatus(marker) {
1094
1504
  }
1095
1505
  export async function runExtensionStatus(flags) {
1096
1506
  const target = resolveExtensionTarget(flags);
1507
+ if (isEditorCliExtensionTarget(target)) {
1508
+ await runEditorExtensionStatus(target, flags);
1509
+ return;
1510
+ }
1097
1511
  const statePath = installStatePath(target);
1098
1512
  const markerPath = extensionMarkerPath(target);
1099
1513
  const [state, marker] = await Promise.all([
@@ -1127,6 +1541,10 @@ export async function runExtensionStatus(flags) {
1127
1541
  }
1128
1542
  export async function runExtensionUninstall(flags) {
1129
1543
  const target = resolveExtensionTarget(flags);
1544
+ if (isEditorCliExtensionTarget(target)) {
1545
+ await runEditorExtensionUninstall(target, flags);
1546
+ return;
1547
+ }
1130
1548
  const statePath = installStatePath(target);
1131
1549
  const markerPath = extensionMarkerPath(target);
1132
1550
  const marker = await loadInstallMarker(markerPath);
@@ -5981,9 +6399,9 @@ function jsonFlags(flags) {
5981
6399
  next.set("json", ["true"]);
5982
6400
  return next;
5983
6401
  }
5984
- function extensionAllFlags(flags) {
6402
+ function extensionAllFlags(flags, target) {
5985
6403
  const next = cloneFlags(flags);
5986
- next.set("target", ["vscode"]);
6404
+ next.set("target", [target]);
5987
6405
  return next;
5988
6406
  }
5989
6407
  function executableFromEnv(envName, defaultBin) {
@@ -6005,6 +6423,10 @@ async function executableVersion(envName, defaultBin, label) {
6005
6423
  async function preflightVscode(flags) {
6006
6424
  return resolveExtensionDir(flags);
6007
6425
  }
6426
+ async function preflightEditorExtension(target) {
6427
+ const editor = await resolveEditorCli(target);
6428
+ return `${extensionTargets[target].label} ${editor.version} (${editor.binary})`;
6429
+ }
6008
6430
  async function preflightClaudeCode() {
6009
6431
  return assertSupportedClaudeCodeVersion();
6010
6432
  }
@@ -6043,8 +6465,24 @@ function allInstallTargets(flags) {
6043
6465
  command: "waitspin extension install --target vscode",
6044
6466
  statusCommand: "waitspin extension status --target vscode",
6045
6467
  preflight: preflightVscode,
6046
- install: (flags) => runExtensionInstall(extensionAllFlags(flags)),
6047
- status: (flags) => runExtensionStatus(extensionAllFlags(flags)),
6468
+ install: (flags) => runExtensionInstall(extensionAllFlags(flags, "vscode")),
6469
+ status: (flags) => runExtensionStatus(extensionAllFlags(flags, "vscode")),
6470
+ },
6471
+ {
6472
+ target: "cursor",
6473
+ command: "waitspin extension install --target cursor",
6474
+ statusCommand: "waitspin extension status --target cursor",
6475
+ preflight: () => preflightEditorExtension("cursor"),
6476
+ install: (flags) => runExtensionInstall(extensionAllFlags(flags, "cursor")),
6477
+ status: (flags) => runExtensionStatus(extensionAllFlags(flags, "cursor")),
6478
+ },
6479
+ {
6480
+ target: "devin",
6481
+ command: "waitspin extension install --target devin",
6482
+ statusCommand: "waitspin extension status --target devin",
6483
+ preflight: () => preflightEditorExtension("devin"),
6484
+ install: (flags) => runExtensionInstall(extensionAllFlags(flags, "devin")),
6485
+ status: (flags) => runExtensionStatus(extensionAllFlags(flags, "devin")),
6048
6486
  },
6049
6487
  {
6050
6488
  target: CLAUDE_CODE_PUBLISHER_TARGET,