@mcpcloud/cli 0.3.0 → 0.4.0-next-20260611161108

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.
Files changed (2) hide show
  1. package/dist/index.js +562 -176
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5113,6 +5113,10 @@ function stateFile(cwd) {
5113
5113
  function serverDir(cwd) {
5114
5114
  return join4(devRoot(cwd), "server");
5115
5115
  }
5116
+ function gitCloneDir(cwd, owner, name) {
5117
+ const safe = `${owner}__${name}`.replace(/[^a-zA-Z0-9._-]/g, "-");
5118
+ return join4(devRoot(cwd), "git", safe);
5119
+ }
5116
5120
  function envFile(cwd) {
5117
5121
  return join4(devRoot(cwd), "env.json");
5118
5122
  }
@@ -5152,6 +5156,7 @@ function ensureGitignore(cwd) {
5152
5156
  writeFileSync2(ignoreFile, [
5153
5157
  "# generated by `mcp dev`",
5154
5158
  "server/",
5159
+ "git/",
5155
5160
  "env.json",
5156
5161
  "agent-backups/",
5157
5162
  "inspector/",
@@ -8097,7 +8102,7 @@ function registerConfigCommands(program2) {
8097
8102
  }
8098
8103
 
8099
8104
  // src/commands/dev.ts
8100
- import { existsSync as existsSync31 } from "node:fs";
8105
+ import { existsSync as existsSync32 } from "node:fs";
8101
8106
  import { isAbsolute as isAbsolute2, relative as relative7, resolve as resolve6 } from "node:path";
8102
8107
 
8103
8108
  // src/lib/mcp-invoke.ts
@@ -9556,6 +9561,24 @@ var Q = class extends m {
9556
9561
  }
9557
9562
  }
9558
9563
  };
9564
+
9565
+ class X extends m {
9566
+ get cursor() {
9567
+ return this.value ? 0 : 1;
9568
+ }
9569
+ get _value() {
9570
+ return this.cursor === 0;
9571
+ }
9572
+ constructor(t) {
9573
+ super(t, false), this.value = !!t.initialValue, this.on("userInput", () => {
9574
+ this.value = this._value;
9575
+ }), this.on("confirm", (s) => {
9576
+ this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = s, this.state = "submit", this.close();
9577
+ }), this.on("cursor", () => {
9578
+ this.value = !this.value;
9579
+ });
9580
+ }
9581
+ }
9559
9582
  var Z = { Y: { type: "year", len: 4 }, M: { type: "month", len: 2 }, D: { type: "day", len: 2 } };
9560
9583
  function P(r) {
9561
9584
  return [...r].map((t) => Z[t]);
@@ -9992,6 +10015,33 @@ var F2 = ({ cursor: t, options: i, style: s, output: r = process.stdout, maxItem
9992
10015
  S2.push(G2);
9993
10016
  return h2 && S2.push(l), S2;
9994
10017
  };
10018
+ var ue = (t) => {
10019
+ const i = t.active ?? "Yes", s = t.inactive ?? "No";
10020
+ return new X({ active: i, inactive: s, signal: t.signal, input: t.input, output: t.output, initialValue: t.initialValue ?? true, render() {
10021
+ const r = t.withGuide ?? h.withGuide, u = `${P2(this.state)} `, n = r ? `${e("gray", $2)} ` : "", a = W(t.output, t.message, n, u), c2 = `${r ? `${e("gray", $2)}
10022
+ ` : ""}${a}
10023
+ `, o = this.value ? i : s;
10024
+ switch (this.state) {
10025
+ case "submit": {
10026
+ const l = r ? `${e("gray", $2)} ` : "";
10027
+ return `${c2}${l}${e("dim", o)}`;
10028
+ }
10029
+ case "cancel": {
10030
+ const l = r ? `${e("gray", $2)} ` : "";
10031
+ return `${c2}${l}${e(["strikethrough", "dim"], o)}${r ? `
10032
+ ${e("gray", $2)}` : ""}`;
10033
+ }
10034
+ default: {
10035
+ const l = r ? `${e("cyan", $2)} ` : "", d = r ? e("cyan", x2) : "";
10036
+ return `${c2}${l}${this.value ? `${e("green", z2)} ${i}` : `${e("dim", U2)} ${e("dim", i)}`}${t.vertical ? r ? `
10037
+ ${e("cyan", $2)} ` : `
10038
+ ` : ` ${e("dim", "/")} `}${this.value ? `${e("dim", U2)} ${e("dim", s)}` : `${e("green", z2)} ${s}`}
10039
+ ${d}
10040
+ `;
10041
+ }
10042
+ }
10043
+ } }).prompt();
10044
+ };
9995
10045
  var me = (t = "", i) => {
9996
10046
  const s = i?.output ?? process.stdout, r = i?.withGuide ?? h.withGuide ? `${e("gray", x2)} ` : "";
9997
10047
  s.write(`${r}${e("red", t)}
@@ -10092,6 +10142,18 @@ async function resolveDevTarget(deps) {
10092
10142
  }
10093
10143
  };
10094
10144
  }
10145
+ async function promptResumeOrSwitch(args) {
10146
+ const label = args.serverName ? `${args.serverName} ${c.dim(`(${args.serverId})`)}` : args.serverId;
10147
+ const resume = await ue({
10148
+ message: `Resume ${label}?`,
10149
+ active: "Resume",
10150
+ inactive: "Pick a different server",
10151
+ initialValue: true
10152
+ });
10153
+ if (q(resume))
10154
+ return "cancel";
10155
+ return resume ? "resume" : "switch";
10156
+ }
10095
10157
  function canRunInteractive() {
10096
10158
  if (isCiMode())
10097
10159
  return false;
@@ -10177,6 +10239,20 @@ async function resolveServerProject(args) {
10177
10239
  version: data.server.version
10178
10240
  };
10179
10241
  }
10242
+ async function resolveServerGitLink(args) {
10243
+ const data = await api.get("/api/v1/server/git-link", {
10244
+ organizationId: args.organizationId,
10245
+ serverId: args.serverId
10246
+ });
10247
+ return data.gitLink ?? null;
10248
+ }
10249
+ async function resolveServerGitPullStatus(args) {
10250
+ const data = await api.get("/api/v1/server/git-sync-status", {
10251
+ organizationId: args.organizationId,
10252
+ serverId: args.serverId
10253
+ });
10254
+ return data.pull ?? null;
10255
+ }
10180
10256
  async function fetchBundle(args) {
10181
10257
  requireApiKey();
10182
10258
  const url = new URL("/api/v1/artifacts/download", getBaseUrl());
@@ -10274,6 +10350,49 @@ function pruneStaleFiles(rootDir, currentDir, kept) {
10274
10350
  return removed;
10275
10351
  }
10276
10352
 
10353
+ // src/lib/dev/git-pull-poll.ts
10354
+ var DEFAULT_INTERVAL_MS2 = 6000;
10355
+ function reducePollState(baseline, status) {
10356
+ if (!status)
10357
+ return { baseline, nudge: false };
10358
+ const sha = status.lastPulledGitSha;
10359
+ if (baseline === undefined)
10360
+ return { baseline: sha, nudge: false };
10361
+ if (sha && sha !== baseline)
10362
+ return { baseline: sha, nudge: true };
10363
+ return { baseline, nudge: false };
10364
+ }
10365
+ function startGitPullPoll(args) {
10366
+ const intervalMs = args.intervalMs ?? DEFAULT_INTERVAL_MS2;
10367
+ let baseline;
10368
+ let stopped = false;
10369
+ let timer = null;
10370
+ const tick = async () => {
10371
+ if (stopped)
10372
+ return;
10373
+ try {
10374
+ const status = await args.fetchStatus();
10375
+ const next = reducePollState(baseline, status);
10376
+ baseline = next.baseline;
10377
+ if (next.nudge && status)
10378
+ args.onChange(status);
10379
+ } catch (err) {
10380
+ args.onError?.(err);
10381
+ } finally {
10382
+ if (!stopped)
10383
+ timer = setTimeout(() => void tick(), intervalMs);
10384
+ }
10385
+ };
10386
+ tick();
10387
+ return {
10388
+ close() {
10389
+ stopped = true;
10390
+ if (timer)
10391
+ clearTimeout(timer);
10392
+ }
10393
+ };
10394
+ }
10395
+
10277
10396
  // src/lib/dev/editor.ts
10278
10397
  import { spawn as spawn4 } from "node:child_process";
10279
10398
  import { existsSync as existsSync17, statSync as statSync4 } from "node:fs";
@@ -10972,16 +11091,126 @@ async function installDependencies(options) {
10972
11091
  }
10973
11092
 
10974
11093
  // src/lib/dev/prepare.ts
10975
- import { existsSync as existsSync24, writeFileSync as writeFileSync9 } from "node:fs";
10976
- import { join as join19, relative as relative6 } from "node:path";
11094
+ import { existsSync as existsSync25, writeFileSync as writeFileSync10 } from "node:fs";
11095
+ import { join as join20, relative as relative6 } from "node:path";
11096
+
11097
+ // src/lib/dev/git-clone.ts
11098
+ import { spawnSync } from "node:child_process";
11099
+ import {
11100
+ existsSync as existsSync22,
11101
+ mkdirSync as mkdirSync9,
11102
+ readFileSync as readFileSync14,
11103
+ writeFileSync as writeFileSync7
11104
+ } from "node:fs";
11105
+ import { dirname as dirname5, join as join17 } from "node:path";
11106
+
11107
+ class GitNotAvailableError extends Error {
11108
+ constructor() {
11109
+ super("git is not installed or not on your PATH. Install Git to use Git-native dev mode.");
11110
+ this.name = "GitNotAvailableError";
11111
+ }
11112
+ }
11113
+
11114
+ class GitCloneError extends Error {
11115
+ constructor(message) {
11116
+ super(message);
11117
+ this.name = "GitCloneError";
11118
+ }
11119
+ }
11120
+ var GIT_LOCAL_EXCLUDES = [
11121
+ "_mcpsh_*",
11122
+ "node_modules/",
11123
+ "bun.lock"
11124
+ ];
11125
+ var EXCLUDE_HEADER = "# added by `mcp dev` (Git-native mode)";
11126
+ function gitCloneArgs(httpsCloneUrl, branch, repoDir) {
11127
+ return [
11128
+ "clone",
11129
+ "--branch",
11130
+ branch,
11131
+ "--single-branch",
11132
+ httpsCloneUrl,
11133
+ repoDir
11134
+ ];
11135
+ }
11136
+ function gitFetchArgs(branch) {
11137
+ return ["fetch", "--quiet", "origin", branch];
11138
+ }
11139
+ function mergeLocalExcludes(existing) {
11140
+ const lines = existing.split(`
11141
+ `);
11142
+ const present = new Set(lines.map((l) => l.trim()));
11143
+ const missing = GIT_LOCAL_EXCLUDES.filter((pattern) => !present.has(pattern));
11144
+ if (missing.length === 0)
11145
+ return null;
11146
+ const prefix = existing.length > 0 && !existing.endsWith(`
11147
+ `) ? `
11148
+ ` : "";
11149
+ const block = present.has(EXCLUDE_HEADER) ? missing.join(`
11150
+ `) : [EXCLUDE_HEADER, ...missing].join(`
11151
+ `);
11152
+ return `${existing}${prefix}${block}
11153
+ `;
11154
+ }
11155
+ function runGit(args, cwd) {
11156
+ const res = spawnSync("git", args, {
11157
+ cwd,
11158
+ encoding: "utf-8",
11159
+ stdio: ["inherit", "pipe", "pipe"]
11160
+ });
11161
+ if (res.error && res.error.code === "ENOENT") {
11162
+ throw new GitNotAvailableError;
11163
+ }
11164
+ return {
11165
+ status: res.status ?? 1,
11166
+ stdout: res.stdout ?? "",
11167
+ stderr: res.stderr ?? ""
11168
+ };
11169
+ }
11170
+ function isGitAvailable() {
11171
+ try {
11172
+ return runGit(["--version"]).status === 0;
11173
+ } catch {
11174
+ return false;
11175
+ }
11176
+ }
11177
+ function applyLocalExcludes(repoDir) {
11178
+ const excludePath = join17(repoDir, ".git", "info", "exclude");
11179
+ const existing = existsSync22(excludePath) ? readFileSync14(excludePath, "utf-8") : "";
11180
+ const next = mergeLocalExcludes(existing);
11181
+ if (next === null)
11182
+ return;
11183
+ if (!existsSync22(dirname5(excludePath))) {
11184
+ mkdirSync9(dirname5(excludePath), { recursive: true });
11185
+ }
11186
+ writeFileSync7(excludePath, next, "utf-8");
11187
+ }
11188
+ function ensureClone(opts) {
11189
+ if (!isGitAvailable()) {
11190
+ throw new GitNotAvailableError;
11191
+ }
11192
+ if (existsSync22(join17(opts.repoDir, ".git"))) {
11193
+ opts.onStep?.("Refreshing existing clone (git fetch)…");
11194
+ runGit(gitFetchArgs(opts.branch), opts.repoDir);
11195
+ applyLocalExcludes(opts.repoDir);
11196
+ return { cloned: false };
11197
+ }
11198
+ opts.onStep?.(`Cloning ${opts.httpsCloneUrl} (branch ${opts.branch})…`);
11199
+ const res = runGit(gitCloneArgs(opts.httpsCloneUrl, opts.branch, opts.repoDir));
11200
+ if (res.status !== 0) {
11201
+ throw new GitCloneError(`git clone failed (exit ${res.status}). ${res.stderr.trim() || "Check that you have access to the repository with your local git credentials."}`);
11202
+ }
11203
+ applyLocalExcludes(opts.repoDir);
11204
+ return { cloned: true };
11205
+ }
10977
11206
 
10978
11207
  // src/lib/dev/host-runtime.ts
10979
- import { existsSync as existsSync23, mkdirSync as mkdirSync10, writeFileSync as writeFileSync8 } from "node:fs";
10980
- import { dirname as dirname5, join as join18 } from "node:path";
11208
+ import { existsSync as existsSync24, mkdirSync as mkdirSync11, writeFileSync as writeFileSync9 } from "node:fs";
11209
+ import { dirname as dirname6, join as join19 } from "node:path";
10981
11210
 
10982
11211
  // src/lib/dev/inspector-module.ts
10983
- import { existsSync as existsSync22, mkdirSync as mkdirSync9, writeFileSync as writeFileSync7 } from "node:fs";
10984
- import { join as join17 } from "node:path";
11212
+ import { existsSync as existsSync23, mkdirSync as mkdirSync10, writeFileSync as writeFileSync8 } from "node:fs";
11213
+ import { join as join18 } from "node:path";
10985
11214
 
10986
11215
  // src/lib/dev/inspector-assets.ts
10987
11216
  var INSPECTOR_HTML = `<!doctype html>
@@ -11482,7 +11711,7 @@ function requireReact_production() {
11482
11711
  react_production.useTransition = function() {
11483
11712
  return ReactSharedInternals.H.useTransition();
11484
11713
  };
11485
- react_production.version = "19.2.6";
11714
+ react_production.version = "19.2.7";
11486
11715
  return react_production;
11487
11716
  }
11488
11717
  var hasRequiredReact;
@@ -11909,7 +12138,7 @@ function requireReactDom_production() {
11909
12138
  reactDom_production.useFormStatus = function() {
11910
12139
  return ReactSharedInternals.H.useHostTransitionStatus();
11911
12140
  };
11912
- reactDom_production.version = "19.2.6";
12141
+ reactDom_production.version = "19.2.7";
11913
12142
  return reactDom_production;
11914
12143
  }
11915
12144
  var hasRequiredReactDom;
@@ -23353,12 +23582,12 @@ function requireReactDomClient_production() {
23353
23582
  }
23354
23583
  };
23355
23584
  var isomorphicReactPackageVersion$jscomp$inline_1840 = React.version;
23356
- if ("19.2.6" !== isomorphicReactPackageVersion$jscomp$inline_1840)
23585
+ if ("19.2.7" !== isomorphicReactPackageVersion$jscomp$inline_1840)
23357
23586
  throw Error(
23358
23587
  formatProdErrorMessage(
23359
23588
  527,
23360
23589
  isomorphicReactPackageVersion$jscomp$inline_1840,
23361
- "19.2.6"
23590
+ "19.2.7"
23362
23591
  )
23363
23592
  );
23364
23593
  ReactDOMSharedInternals.findDOMNode = function(componentOrElement) {
@@ -23376,10 +23605,10 @@ function requireReactDomClient_production() {
23376
23605
  };
23377
23606
  var internals$jscomp$inline_2347 = {
23378
23607
  bundleType: 0,
23379
- version: "19.2.6",
23608
+ version: "19.2.7",
23380
23609
  rendererPackageName: "react-dom",
23381
23610
  currentDispatcherRef: ReactSharedInternals,
23382
- reconcilerVersion: "19.2.6"
23611
+ reconcilerVersion: "19.2.7"
23383
23612
  };
23384
23613
  if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
23385
23614
  var hook$jscomp$inline_2348 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -23446,7 +23675,7 @@ function requireReactDomClient_production() {
23446
23675
  listenToAllSupportedEvents(container);
23447
23676
  return new ReactDOMHydrationRoot(initialChildren);
23448
23677
  };
23449
- reactDomClient_production.version = "19.2.6";
23678
+ reactDomClient_production.version = "19.2.7";
23450
23679
  return reactDomClient_production;
23451
23680
  }
23452
23681
  var hasRequiredClient;
@@ -26116,10 +26345,10 @@ function buildInspectorModule() {
26116
26345
  `);
26117
26346
  }
26118
26347
  function writeInspectorModule(serverDir2) {
26119
- if (!existsSync22(serverDir2))
26120
- mkdirSync9(serverDir2, { recursive: true });
26121
- const target = join17(serverDir2, INSPECTOR_FILE_NAME);
26122
- writeFileSync7(target, buildInspectorModule(), "utf-8");
26348
+ if (!existsSync23(serverDir2))
26349
+ mkdirSync10(serverDir2, { recursive: true });
26350
+ const target = join18(serverDir2, INSPECTOR_FILE_NAME);
26351
+ writeFileSync8(target, buildInspectorModule(), "utf-8");
26123
26352
  return target;
26124
26353
  }
26125
26354
 
@@ -26298,11 +26527,11 @@ function buildHostScript(options) {
26298
26527
  `);
26299
26528
  }
26300
26529
  function writeHostScript(serverDir2, options) {
26301
- if (!existsSync23(serverDir2))
26302
- mkdirSync10(serverDir2, { recursive: true });
26303
- const target = join18(serverDir2, HOST_FILE_NAME);
26304
- mkdirSync10(dirname5(target), { recursive: true });
26305
- writeFileSync8(target, buildHostScript(options), "utf-8");
26530
+ if (!existsSync24(serverDir2))
26531
+ mkdirSync11(serverDir2, { recursive: true });
26532
+ const target = join19(serverDir2, HOST_FILE_NAME);
26533
+ mkdirSync11(dirname6(target), { recursive: true });
26534
+ writeFileSync9(target, buildHostScript(options), "utf-8");
26306
26535
  if (options.inspector) {
26307
26536
  writeInspectorModule(serverDir2);
26308
26537
  }
@@ -26314,12 +26543,50 @@ var ENTRY_CANDIDATES = ["src/worker.ts", "src/server.ts", "src/index.ts"];
26314
26543
  async function prepareDev(opts) {
26315
26544
  const cwd = opts.cwd;
26316
26545
  ensureGitignore(cwd);
26317
- const dest = serverDir(cwd);
26546
+ let dest = serverDir(cwd);
26318
26547
  const env = envFile(cwd);
26319
26548
  const backups = backupsDir(cwd);
26320
26549
  const cached2 = readState(cwd);
26321
26550
  let projectId = cached2?.projectId;
26322
- if (!opts.offline) {
26551
+ let serverName = cached2?.serverName;
26552
+ let gitNative = false;
26553
+ let repoDir;
26554
+ const gitLink = opts.offline ? null : await resolveServerGitLink({
26555
+ serverId: opts.serverId,
26556
+ organizationId: opts.organizationId
26557
+ }).catch(() => null);
26558
+ if (gitLink) {
26559
+ gitNative = true;
26560
+ projectId = projectId ?? gitLink.projectId;
26561
+ serverName = `${gitLink.owner}/${gitLink.name}`;
26562
+ repoDir = gitCloneDir(cwd, gitLink.owner, gitLink.name);
26563
+ dest = gitLink.pathPrefix ? join20(repoDir, gitLink.pathPrefix) : repoDir;
26564
+ printStep(`Git-linked server — cloning ${gitLink.owner}/${gitLink.name}@${gitLink.branch}…`);
26565
+ try {
26566
+ const result = ensureClone({
26567
+ repoDir,
26568
+ httpsCloneUrl: gitLink.httpsCloneUrl,
26569
+ branch: gitLink.branch,
26570
+ onStep: (m2) => printStep(m2)
26571
+ });
26572
+ printSuccess(result.cloned ? `Cloned to ${relative6(cwd, repoDir)}` : `Using existing clone at ${relative6(cwd, repoDir)}`);
26573
+ } catch (err) {
26574
+ if (err instanceof GitNotAvailableError) {
26575
+ printError(err.message);
26576
+ } else if (err instanceof GitCloneError) {
26577
+ printError(err.message);
26578
+ } else {
26579
+ printError(`Git clone failed: ${err instanceof Error ? err.message : String(err)}`);
26580
+ }
26581
+ process.exit(1);
26582
+ }
26583
+ if (!existsSync25(env)) {
26584
+ writeFileSync10(env, `{}
26585
+ `, "utf-8");
26586
+ printInfo(` → seeded ${relative6(cwd, env)} (empty; fill secrets here)`);
26587
+ }
26588
+ printInfo(` ${"Open the clone in your editor; commit + push to sync (config under mcpcloud/ is read-only)."}`);
26589
+ } else if (!opts.offline) {
26323
26590
  if (!projectId) {
26324
26591
  printStep(`Looking up server ${opts.serverId}…`);
26325
26592
  const lookup = await resolveServerProject({
@@ -26327,6 +26594,7 @@ async function prepareDev(opts) {
26327
26594
  organizationId: opts.organizationId
26328
26595
  });
26329
26596
  projectId = lookup.projectId;
26597
+ serverName = lookup.serverName;
26330
26598
  printInfo(` → ${lookup.serverName} (project ${projectId})`);
26331
26599
  }
26332
26600
  printStep("Fetching bundle…");
@@ -26337,8 +26605,8 @@ async function prepareDev(opts) {
26337
26605
  });
26338
26606
  const result = materializeBundle(dest, bundle, { prune: true });
26339
26607
  printInfo(` → wrote ${result.filesWritten} files (pruned ${result.filesRemoved})`);
26340
- if (!existsSync24(env)) {
26341
- writeFileSync9(env, `{}
26608
+ if (!existsSync25(env)) {
26609
+ writeFileSync10(env, `{}
26342
26610
  `, "utf-8");
26343
26611
  printInfo(` → seeded ${relative6(cwd, env)} (empty; fill secrets here)`);
26344
26612
  }
@@ -26355,7 +26623,7 @@ async function prepareDev(opts) {
26355
26623
  });
26356
26624
  printInfo(` → wrote ${toolsResult.filesWritten} editable tool file${toolsResult.filesWritten === 1 ? "" : "s"}${toolsResult.filesRemoved > 0 ? ` (pruned ${toolsResult.filesRemoved})` : ""}`);
26357
26625
  } else {
26358
- if (!existsSync24(dest)) {
26626
+ if (!existsSync25(dest)) {
26359
26627
  printError("No cached bundle found. Run once without --offline first.");
26360
26628
  process.exit(1);
26361
26629
  }
@@ -26364,7 +26632,7 @@ async function prepareDev(opts) {
26364
26632
  process.exit(1);
26365
26633
  }
26366
26634
  }
26367
- const entryRel = ENTRY_CANDIDATES.find((c2) => existsSync24(join19(dest, c2)));
26635
+ const entryRel = ENTRY_CANDIDATES.find((c2) => existsSync25(join20(dest, c2)));
26368
26636
  if (!entryRel) {
26369
26637
  printError("Could not locate an entry file (expected src/worker.ts).");
26370
26638
  process.exit(1);
@@ -26385,7 +26653,8 @@ async function prepareDev(opts) {
26385
26653
  bundleVersion: 0,
26386
26654
  lastSyncedAt: Date.now(),
26387
26655
  transports: opts.transports,
26388
- port: opts.port
26656
+ port: opts.port,
26657
+ ...serverName !== undefined ? { serverName } : {}
26389
26658
  };
26390
26659
  writeState(cwd, state);
26391
26660
  const storePath = inspectorDir(cwd);
@@ -26403,7 +26672,9 @@ async function prepareDev(opts) {
26403
26672
  destDir: dest,
26404
26673
  envPath: env,
26405
26674
  backupsPath: backups,
26406
- state
26675
+ state,
26676
+ gitNative,
26677
+ ...repoDir !== undefined ? { repoDir } : {}
26407
26678
  };
26408
26679
  }
26409
26680
 
@@ -26519,73 +26790,73 @@ function createBurstGuard() {
26519
26790
  }
26520
26791
 
26521
26792
  // src/lib/dev/agent-connectors/claude-code.ts
26522
- import { existsSync as existsSync26, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "node:fs";
26793
+ import { existsSync as existsSync27, unlinkSync as unlinkSync2, writeFileSync as writeFileSync12 } from "node:fs";
26523
26794
  import { homedir as homedir4 } from "node:os";
26524
- import { join as join21 } from "node:path";
26795
+ import { join as join22 } from "node:path";
26525
26796
 
26526
26797
  // src/lib/dev/agent-connectors/json-config-utils.ts
26527
- import { existsSync as existsSync25, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "node:fs";
26528
- import { dirname as dirname6, join as join20, basename } from "node:path";
26798
+ import { existsSync as existsSync26, mkdirSync as mkdirSync12, readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "node:fs";
26799
+ import { dirname as dirname7, join as join21, basename } from "node:path";
26529
26800
  function readJsonFile(path) {
26530
- if (!existsSync25(path))
26801
+ if (!existsSync26(path))
26531
26802
  return { ok: true, value: {} };
26532
26803
  try {
26533
- const raw = readFileSync14(path, "utf-8");
26804
+ const raw = readFileSync15(path, "utf-8");
26534
26805
  if (!raw.trim())
26535
26806
  return { ok: true, value: {} };
26536
26807
  return { ok: true, value: JSON.parse(raw) };
26537
26808
  } catch {
26538
26809
  try {
26539
- return { ok: false, raw: readFileSync14(path, "utf-8") };
26810
+ return { ok: false, raw: readFileSync15(path, "utf-8") };
26540
26811
  } catch {
26541
26812
  return { ok: false };
26542
26813
  }
26543
26814
  }
26544
26815
  }
26545
26816
  function atomicWriteJson(path, value) {
26546
- mkdirSync11(dirname6(path), { recursive: true });
26817
+ mkdirSync12(dirname7(path), { recursive: true });
26547
26818
  const tmp = `${path}.mcpsh-${process.pid}-${Date.now()}.tmp`;
26548
- writeFileSync10(tmp, JSON.stringify(value, null, 2) + `
26819
+ writeFileSync11(tmp, JSON.stringify(value, null, 2) + `
26549
26820
  `, "utf-8");
26550
26821
  const { renameSync: renameSync2 } = __require("node:fs");
26551
26822
  renameSync2(tmp, path);
26552
26823
  }
26553
26824
  function backupConfig(args) {
26554
- if (!existsSync25(args.configPath)) {
26825
+ if (!existsSync26(args.configPath)) {
26555
26826
  return { backupPath: null, existed: false };
26556
26827
  }
26557
- mkdirSync11(args.backupsDir, { recursive: true });
26828
+ mkdirSync12(args.backupsDir, { recursive: true });
26558
26829
  const filename = `${args.agentId}__${basename(args.configPath)}.bak`;
26559
- const backupPath = join20(args.backupsDir, filename);
26560
- if (!existsSync25(backupPath)) {
26561
- const raw = readFileSync14(args.configPath, "utf-8");
26562
- writeFileSync10(backupPath, raw, "utf-8");
26830
+ const backupPath = join21(args.backupsDir, filename);
26831
+ if (!existsSync26(backupPath)) {
26832
+ const raw = readFileSync15(args.configPath, "utf-8");
26833
+ writeFileSync11(backupPath, raw, "utf-8");
26563
26834
  }
26564
26835
  return { backupPath, existed: true };
26565
26836
  }
26566
26837
  function restoreFromBackup(args) {
26567
26838
  const filename = `${args.agentId}__${basename(args.configPath)}.bak`;
26568
- const backupPath = join20(args.backupsDir, filename);
26569
- if (!existsSync25(backupPath)) {
26839
+ const backupPath = join21(args.backupsDir, filename);
26840
+ if (!existsSync26(backupPath)) {
26570
26841
  return { restored: false };
26571
26842
  }
26572
- const raw = readFileSync14(backupPath, "utf-8");
26573
- mkdirSync11(dirname6(args.configPath), { recursive: true });
26574
- writeFileSync10(args.configPath, raw, "utf-8");
26843
+ const raw = readFileSync15(backupPath, "utf-8");
26844
+ mkdirSync12(dirname7(args.configPath), { recursive: true });
26845
+ writeFileSync11(args.configPath, raw, "utf-8");
26575
26846
  return { restored: true };
26576
26847
  }
26577
26848
 
26578
26849
  // src/lib/dev/agent-connectors/claude-code.ts
26579
26850
  function configPath() {
26580
- return join21(homedir4(), ".claude.json");
26851
+ return join22(homedir4(), ".claude.json");
26581
26852
  }
26582
26853
  function legacyConfigPath() {
26583
- return join21(homedir4(), ".claude", "mcp.json");
26854
+ return join22(homedir4(), ".claude", "mcp.json");
26584
26855
  }
26585
26856
  function resolveConfigPath() {
26586
- if (existsSync26(configPath()))
26857
+ if (existsSync27(configPath()))
26587
26858
  return configPath();
26588
- if (existsSync26(legacyConfigPath()))
26859
+ if (existsSync27(legacyConfigPath()))
26589
26860
  return legacyConfigPath();
26590
26861
  return configPath();
26591
26862
  }
@@ -26595,8 +26866,8 @@ var claudeCodeConnector = {
26595
26866
  hotkey: "c",
26596
26867
  describeLocation: () => resolveConfigPath(),
26597
26868
  async detect() {
26598
- const claudeDir = join21(homedir4(), ".claude");
26599
- if (existsSync26(configPath()) || existsSync26(legacyConfigPath()) || existsSync26(claudeDir)) {
26869
+ const claudeDir = join22(homedir4(), ".claude");
26870
+ if (existsSync27(configPath()) || existsSync27(legacyConfigPath()) || existsSync27(claudeDir)) {
26600
26871
  return { installed: true, note: "Found Claude Code config" };
26601
26872
  }
26602
26873
  return { installed: false };
@@ -26634,7 +26905,7 @@ var claudeCodeConnector = {
26634
26905
  backupsDir: args.backupsDir,
26635
26906
  agentId: this.id
26636
26907
  });
26637
- if (!restored.restored && existsSync26(path)) {
26908
+ if (!restored.restored && existsSync27(path)) {
26638
26909
  const after = readJsonFile(path);
26639
26910
  if (after.ok && after.value && typeof after.value === "object") {
26640
26911
  const root = after.value;
@@ -26644,7 +26915,7 @@ var claudeCodeConnector = {
26644
26915
  try {
26645
26916
  unlinkSync2(path);
26646
26917
  } catch {
26647
- writeFileSync11(path, `{}
26918
+ writeFileSync12(path, `{}
26648
26919
  `, "utf-8");
26649
26920
  }
26650
26921
  }
@@ -26656,16 +26927,16 @@ var claudeCodeConnector = {
26656
26927
 
26657
26928
  // src/lib/dev/agent-connectors/codex.ts
26658
26929
  import {
26659
- existsSync as existsSync27,
26660
- mkdirSync as mkdirSync12,
26661
- readFileSync as readFileSync15,
26930
+ existsSync as existsSync28,
26931
+ mkdirSync as mkdirSync13,
26932
+ readFileSync as readFileSync16,
26662
26933
  unlinkSync as unlinkSync3,
26663
- writeFileSync as writeFileSync12
26934
+ writeFileSync as writeFileSync13
26664
26935
  } from "node:fs";
26665
26936
  import { homedir as homedir5 } from "node:os";
26666
- import { dirname as dirname7, join as join22 } from "node:path";
26937
+ import { dirname as dirname8, join as join23 } from "node:path";
26667
26938
  function configPath2() {
26668
- return join22(homedir5(), ".codex", "config.toml");
26939
+ return join23(homedir5(), ".codex", "config.toml");
26669
26940
  }
26670
26941
  var SECTION_PREFIX = "mcp_servers.";
26671
26942
  function buildSection(name, url) {
@@ -26703,7 +26974,7 @@ var codexConnector = {
26703
26974
  hotkey: "x",
26704
26975
  describeLocation: () => configPath2(),
26705
26976
  async detect() {
26706
- if (existsSync27(join22(homedir5(), ".codex")) || existsSync27(configPath2())) {
26977
+ if (existsSync28(join23(homedir5(), ".codex")) || existsSync28(configPath2())) {
26707
26978
  return { installed: true, note: "Found ~/.codex/" };
26708
26979
  }
26709
26980
  return { installed: false };
@@ -26716,8 +26987,8 @@ var codexConnector = {
26716
26987
  agentId: this.id
26717
26988
  });
26718
26989
  let existing = "";
26719
- if (existsSync27(path)) {
26720
- existing = readFileSync15(path, "utf-8");
26990
+ if (existsSync28(path)) {
26991
+ existing = readFileSync16(path, "utf-8");
26721
26992
  }
26722
26993
  const conflict = findSectionRange(existing, args.name) !== null;
26723
26994
  let next = existing;
@@ -26732,18 +27003,18 @@ var codexConnector = {
26732
27003
  next += `
26733
27004
  `;
26734
27005
  next += buildSection(args.name, args.url);
26735
- mkdirSync12(dirname7(path), { recursive: true });
26736
- writeFileSync12(path, next, "utf-8");
27006
+ mkdirSync13(dirname8(path), { recursive: true });
27007
+ writeFileSync13(path, next, "utf-8");
26737
27008
  return { added: !conflict, conflict };
26738
27009
  },
26739
27010
  async remove(args) {
26740
27011
  const path = configPath2();
26741
- if (existsSync27(path)) {
26742
- const existing = readFileSync15(path, "utf-8");
27012
+ if (existsSync28(path)) {
27013
+ const existing = readFileSync16(path, "utf-8");
26743
27014
  const range = findSectionRange(existing, args.name);
26744
27015
  if (range) {
26745
27016
  const next = existing.slice(0, range.start) + existing.slice(range.end);
26746
- writeFileSync12(path, next, "utf-8");
27017
+ writeFileSync13(path, next, "utf-8");
26747
27018
  }
26748
27019
  }
26749
27020
  const restored = restoreFromBackup({
@@ -26751,8 +27022,8 @@ var codexConnector = {
26751
27022
  backupsDir: args.backupsDir,
26752
27023
  agentId: this.id
26753
27024
  });
26754
- if (!restored.restored && existsSync27(path)) {
26755
- const after = readFileSync15(path, "utf-8");
27025
+ if (!restored.restored && existsSync28(path)) {
27026
+ const after = readFileSync16(path, "utf-8");
26756
27027
  if (!after.trim()) {
26757
27028
  try {
26758
27029
  unlinkSync3(path);
@@ -26764,11 +27035,11 @@ var codexConnector = {
26764
27035
  };
26765
27036
 
26766
27037
  // src/lib/dev/agent-connectors/continue.ts
26767
- import { existsSync as existsSync28, unlinkSync as unlinkSync4, writeFileSync as writeFileSync13 } from "node:fs";
27038
+ import { existsSync as existsSync29, unlinkSync as unlinkSync4, writeFileSync as writeFileSync14 } from "node:fs";
26768
27039
  import { homedir as homedir6 } from "node:os";
26769
- import { join as join23 } from "node:path";
27040
+ import { join as join24 } from "node:path";
26770
27041
  function configPath3() {
26771
- return join23(homedir6(), ".continue", "config.json");
27042
+ return join24(homedir6(), ".continue", "config.json");
26772
27043
  }
26773
27044
  function isContinueServerEntry(value) {
26774
27045
  return Boolean(value && typeof value === "object" && typeof value.name === "string");
@@ -26779,8 +27050,8 @@ var continueConnector = {
26779
27050
  hotkey: "n",
26780
27051
  describeLocation: () => configPath3(),
26781
27052
  async detect() {
26782
- const dir = join23(homedir6(), ".continue");
26783
- if (existsSync28(dir) || existsSync28(configPath3())) {
27053
+ const dir = join24(homedir6(), ".continue");
27054
+ if (existsSync29(dir) || existsSync29(configPath3())) {
26784
27055
  return { installed: true, note: "Found ~/.continue/" };
26785
27056
  }
26786
27057
  return { installed: false };
@@ -26818,7 +27089,7 @@ var continueConnector = {
26818
27089
  backupsDir: args.backupsDir,
26819
27090
  agentId: this.id
26820
27091
  });
26821
- if (!restored.restored && existsSync28(path)) {
27092
+ if (!restored.restored && existsSync29(path)) {
26822
27093
  const after = readJsonFile(path);
26823
27094
  if (after.ok && after.value && typeof after.value === "object") {
26824
27095
  const root = after.value;
@@ -26828,7 +27099,7 @@ var continueConnector = {
26828
27099
  try {
26829
27100
  unlinkSync4(path);
26830
27101
  } catch {
26831
- writeFileSync13(path, `{}
27102
+ writeFileSync14(path, `{}
26832
27103
  `, "utf-8");
26833
27104
  }
26834
27105
  }
@@ -26839,11 +27110,11 @@ var continueConnector = {
26839
27110
  };
26840
27111
 
26841
27112
  // src/lib/dev/agent-connectors/cursor.ts
26842
- import { existsSync as existsSync29, unlinkSync as unlinkSync5, writeFileSync as writeFileSync14 } from "node:fs";
27113
+ import { existsSync as existsSync30, unlinkSync as unlinkSync5, writeFileSync as writeFileSync15 } from "node:fs";
26843
27114
  import { homedir as homedir7 } from "node:os";
26844
- import { join as join24 } from "node:path";
27115
+ import { join as join25 } from "node:path";
26845
27116
  function globalConfigPath() {
26846
- return join24(homedir7(), ".cursor", "mcp.json");
27117
+ return join25(homedir7(), ".cursor", "mcp.json");
26847
27118
  }
26848
27119
  var cursorConnector = {
26849
27120
  id: "cursor",
@@ -26851,9 +27122,9 @@ var cursorConnector = {
26851
27122
  hotkey: "u",
26852
27123
  describeLocation: () => globalConfigPath(),
26853
27124
  async detect() {
26854
- const cursorDir = join24(homedir7(), ".cursor");
26855
- const macAppSupport = join24(homedir7(), "Library", "Application Support", "Cursor");
26856
- if (existsSync29(cursorDir) || existsSync29(macAppSupport)) {
27125
+ const cursorDir = join25(homedir7(), ".cursor");
27126
+ const macAppSupport = join25(homedir7(), "Library", "Application Support", "Cursor");
27127
+ if (existsSync30(cursorDir) || existsSync30(macAppSupport)) {
26857
27128
  return { installed: true, note: "Found Cursor config dir" };
26858
27129
  }
26859
27130
  return { installed: false };
@@ -26891,7 +27162,7 @@ var cursorConnector = {
26891
27162
  backupsDir: args.backupsDir,
26892
27163
  agentId: this.id
26893
27164
  });
26894
- if (!restored.restored && existsSync29(path)) {
27165
+ if (!restored.restored && existsSync30(path)) {
26895
27166
  const after = readJsonFile(path);
26896
27167
  if (after.ok && after.value && typeof after.value === "object") {
26897
27168
  const root = after.value;
@@ -26901,7 +27172,7 @@ var cursorConnector = {
26901
27172
  try {
26902
27173
  unlinkSync5(path);
26903
27174
  } catch {
26904
- writeFileSync14(path, `{}
27175
+ writeFileSync15(path, `{}
26905
27176
  `, "utf-8");
26906
27177
  }
26907
27178
  }
@@ -26912,30 +27183,30 @@ var cursorConnector = {
26912
27183
  };
26913
27184
 
26914
27185
  // src/lib/dev/agent-connectors/vscode-copilot.ts
26915
- import { existsSync as existsSync30, unlinkSync as unlinkSync6, writeFileSync as writeFileSync15 } from "node:fs";
27186
+ import { existsSync as existsSync31, unlinkSync as unlinkSync6, writeFileSync as writeFileSync16 } from "node:fs";
26916
27187
  import { homedir as homedir8, platform as platform4 } from "node:os";
26917
- import { join as join25, resolve as resolve5 } from "node:path";
27188
+ import { join as join26, resolve as resolve5 } from "node:path";
26918
27189
  function userLevelConfigPath() {
26919
27190
  const home = homedir8();
26920
27191
  const p2 = platform4();
26921
27192
  if (p2 === "darwin")
26922
- return join25(home, "Library", "Application Support", "Code", "User", "mcp.json");
27193
+ return join26(home, "Library", "Application Support", "Code", "User", "mcp.json");
26923
27194
  if (p2 === "win32") {
26924
- const appData = process.env["APPDATA"] ?? join25(home, "AppData", "Roaming");
26925
- return join25(appData, "Code", "User", "mcp.json");
27195
+ const appData = process.env["APPDATA"] ?? join26(home, "AppData", "Roaming");
27196
+ return join26(appData, "Code", "User", "mcp.json");
26926
27197
  }
26927
- const xdg = process.env["XDG_CONFIG_HOME"] ?? join25(home, ".config");
26928
- return join25(xdg, "Code", "User", "mcp.json");
27198
+ const xdg = process.env["XDG_CONFIG_HOME"] ?? join26(home, ".config");
27199
+ return join26(xdg, "Code", "User", "mcp.json");
26929
27200
  }
26930
27201
  function isHomeDir(cwd) {
26931
27202
  return resolve5(cwd) === resolve5(homedir8());
26932
27203
  }
26933
27204
  function hasWorkspaceVscode(cwd) {
26934
- return existsSync30(join25(cwd, ".vscode"));
27205
+ return existsSync31(join26(cwd, ".vscode"));
26935
27206
  }
26936
27207
  function configPath4(cwd) {
26937
27208
  if (!isHomeDir(cwd) && hasWorkspaceVscode(cwd)) {
26938
- return join25(cwd, ".vscode", "mcp.json");
27209
+ return join26(cwd, ".vscode", "mcp.json");
26939
27210
  }
26940
27211
  return userLevelConfigPath();
26941
27212
  }
@@ -26949,7 +27220,7 @@ var vscodeCopilotConnector = {
26949
27220
  if (!isHomeDir(here) && hasWorkspaceVscode(here)) {
26950
27221
  return { installed: true, note: "Found .vscode/ in current dir" };
26951
27222
  }
26952
- if (existsSync30(userLevelConfigPath().replace(/mcp\.json$/, ""))) {
27223
+ if (existsSync31(userLevelConfigPath().replace(/mcp\.json$/, ""))) {
26953
27224
  return { installed: true, note: "Found VS Code user profile" };
26954
27225
  }
26955
27226
  return { installed: false };
@@ -26987,7 +27258,7 @@ var vscodeCopilotConnector = {
26987
27258
  backupsDir: args.backupsDir,
26988
27259
  agentId: this.id
26989
27260
  });
26990
- if (!restored.restored && existsSync30(path)) {
27261
+ if (!restored.restored && existsSync31(path)) {
26991
27262
  const after = readJsonFile(path);
26992
27263
  if (after.ok && after.value && typeof after.value === "object") {
26993
27264
  const root = after.value;
@@ -26997,7 +27268,7 @@ var vscodeCopilotConnector = {
26997
27268
  try {
26998
27269
  unlinkSync6(path);
26999
27270
  } catch {
27000
- writeFileSync15(path, `{}
27271
+ writeFileSync16(path, `{}
27001
27272
  `, "utf-8");
27002
27273
  }
27003
27274
  }
@@ -27149,6 +27420,16 @@ function buildConnectionName(serverId) {
27149
27420
  }
27150
27421
  function registerDevCommand(program2) {
27151
27422
  const dev = program2.command("dev").description("Run a deployed MCP server locally with hot reload + agent auto-wire").option("--server <serverId>", "Server ID (default: from .mcpcloud/state.json)").option("--org <organizationId>", "Organization ID (overrides default)").option("--port <number>", "Local port (default 8787)").option("--transport <list>", "Comma-separated: http,sse,stdio (default http,sse)").option("--stdio", "Shorthand for --transport stdio").option("--offline", "Use cached bundle, skip Convex sync").option("--no-watch", "Disable file watcher").option("--no-connect", "Disable agent auto-wire prompts").option("--auto-connect <agent>", "Auto-wire the named agent (claude-code, cursor, codex, vscode-copilot, continue)").option("--no-inspector", "Disable the inspector UI (default: enabled at /__inspect)").option("--spec <path>", "Watch this OpenAPI spec file and regenerate the bundle on change").option("--no-auto-regen", "Show spec drift but do not auto-apply (manual file edit still works)").option("--regen-grace <seconds>", "Grace period before auto-applying spec changes (default 5)").option("--enrich-on-change", "Preserve per-tool enrichment when the spec changes").option("--open <choice>", "Open an editor on startup: yes | always | no | never | ask").option("--open-target <choice>", "What to open: tools (.mcpcloud/tools/<server>/) | bundle (read-only .mcpcloud/server/) | all (both). When omitted on first run, mcp dev prompts.").option("--editor <command>", "Editor binary for this invocation (e.g. cursor, code, windsurf). Overrides saved editorCommand and the multi-editor picker.").option("--verbose", "Verbose logs").action(runAction(runDev));
27423
+ dev.addHelpText("after", `
27424
+ Git-native mode (automatic):
27425
+ If the chosen server is linked to a GitHub repo, mcp dev clones it under
27426
+ .mcpcloud/git/ and runs against the working copy — no flag needed. Edit in
27427
+ your own editor, then commit + push: the webhook reconciles src/** back into
27428
+ MCPCloud. The CLI's tool/handler PATCH watchers are off in this mode (native
27429
+ git is the version control). Config under mcpcloud/ is a read-only mirror —
27430
+ change it in the dashboard. Deploy when ready with \`mcp deploy\` or the UI.
27431
+ An unlinked server uses the default flow (download bundle, edit via dashboard).
27432
+ `);
27152
27433
  dev.command("list").description("List running mcp dev sessions on this machine").action(runAction(runDevList));
27153
27434
  dev.command("kill <pid>").description('Stop a running mcp dev session by PID (or "all" to stop every session)').action(runAction(runDevKill));
27154
27435
  dev.command("init").description("Interactively pick an organization + server and write .mcpcloud/state.json (forces the picker even if state already exists)").action(runAction(runDevInit));
@@ -27178,7 +27459,8 @@ async function runDevInit() {
27178
27459
  bundleVersion: 0,
27179
27460
  lastSyncedAt: 0,
27180
27461
  transports: ["http", "sse"],
27181
- port: 8787
27462
+ port: 8787,
27463
+ serverName: result.target.serverName
27182
27464
  });
27183
27465
  printSuccess(`Saved .mcpcloud/state.json — run ${c.bold("mcp dev")} to start.`);
27184
27466
  }
@@ -27234,6 +27516,21 @@ async function runDev(opts) {
27234
27516
  const cached2 = readState(cwd);
27235
27517
  let serverId = opts.server ?? cached2?.serverId;
27236
27518
  let organizationId = opts.org ?? cached2?.organizationId ?? undefined;
27519
+ if (!opts.server && cached2?.serverId && canRunInteractive()) {
27520
+ const choice = await promptResumeOrSwitch({
27521
+ serverId: cached2.serverId,
27522
+ ...cached2.serverName !== undefined ? { serverName: cached2.serverName } : {}
27523
+ });
27524
+ if (choice === "cancel")
27525
+ process.exit(1);
27526
+ if (choice === "switch") {
27527
+ const result = await runInteractiveBootstrap();
27528
+ if (result.kind !== "resolved")
27529
+ process.exit(1);
27530
+ serverId = result.target.serverId;
27531
+ organizationId = result.target.organizationId;
27532
+ }
27533
+ }
27237
27534
  if (!serverId) {
27238
27535
  if (!canRunInteractive()) {
27239
27536
  printError("No server selected. Pass --server <serverId>, run `mcp dev init` to pick one interactively, or run from a directory with .mcpcloud/state.json.");
@@ -27274,7 +27571,7 @@ async function runDev(opts) {
27274
27571
  let specPath = null;
27275
27572
  if (opts.spec) {
27276
27573
  const resolved = resolveSpecPath(cwd, opts.spec);
27277
- if (!existsSync31(resolved)) {
27574
+ if (!existsSync32(resolved)) {
27278
27575
  printError(`Spec file not found: ${resolved}`);
27279
27576
  process.exit(1);
27280
27577
  }
@@ -27303,7 +27600,8 @@ async function runDev(opts) {
27303
27600
  enrichOnChange,
27304
27601
  openChoice,
27305
27602
  openTargetOverride,
27306
- editorOverride
27603
+ editorOverride,
27604
+ gitNative: prepared.gitNative
27307
27605
  });
27308
27606
  }
27309
27607
  async function emitStaleHandlerOverrideWarning(args) {
@@ -27338,6 +27636,21 @@ async function runWithRuntime(args) {
27338
27636
  }
27339
27637
  });
27340
27638
  runtime = launch();
27639
+ let fullShutdownReady = false;
27640
+ const killRuntimeNow = () => {
27641
+ try {
27642
+ runtime?.child.kill("SIGKILL");
27643
+ } catch {}
27644
+ };
27645
+ process.once("exit", killRuntimeNow);
27646
+ const earlySignal = (signal) => {
27647
+ if (fullShutdownReady)
27648
+ return;
27649
+ killRuntimeNow();
27650
+ process.exit(signal === "SIGINT" ? 130 : 143);
27651
+ };
27652
+ process.on("SIGINT", () => earlySignal("SIGINT"));
27653
+ process.on("SIGTERM", () => earlySignal("SIGTERM"));
27341
27654
  const baseUrl = `http://127.0.0.1:${args.port}`;
27342
27655
  const mcpUrl = `${baseUrl}/mcp`;
27343
27656
  const connectionName = buildConnectionName(args.serverId);
@@ -27354,7 +27667,14 @@ async function runWithRuntime(args) {
27354
27667
  });
27355
27668
  wired.push(...selected);
27356
27669
  }
27357
- if (args.transports[0] !== "stdio") {
27670
+ if (args.transports[0] !== "stdio" && args.gitNative) {
27671
+ await maybeOpenEditor({
27672
+ dir: args.prepared.repoDir ?? args.prepared.destDir,
27673
+ target: "bundle",
27674
+ override: args.openChoice,
27675
+ editorOverride: args.editorOverride
27676
+ });
27677
+ } else if (args.transports[0] !== "stdio") {
27358
27678
  const savedTarget = readConfig().editorOpenTarget;
27359
27679
  let target = resolveOpenTarget({
27360
27680
  override: args.openTargetOverride,
@@ -27405,11 +27725,16 @@ async function runWithRuntime(args) {
27405
27725
  const watcher = args.watch ? watchDir({
27406
27726
  rootDir: args.prepared.destDir,
27407
27727
  debounceMs: 250,
27408
- ignore: ["node_modules", "_mcpsh_host.mjs", ".git"],
27728
+ ignore: [
27729
+ "node_modules",
27730
+ "_mcpsh_host.mjs",
27731
+ "_mcpsh_inspector.mjs",
27732
+ ".git"
27733
+ ],
27409
27734
  onChange: async (path) => {
27410
27735
  if (shuttingDown)
27411
27736
  return;
27412
- if (shouldWarnAboutBundleEdit({
27737
+ if (!args.gitNative && shouldWarnAboutBundleEdit({
27413
27738
  changedPath: path,
27414
27739
  alreadyWarned: bundleEditWarned,
27415
27740
  suppressed: suppressBundleWarn,
@@ -27427,14 +27752,14 @@ async function runWithRuntime(args) {
27427
27752
  runtime = launch();
27428
27753
  }
27429
27754
  }) : null;
27430
- const toolsWatcher = args.watch ? startToolsWatcher({
27755
+ const toolsWatcher = args.watch && !args.gitNative ? startToolsWatcher({
27431
27756
  cwd: args.cwd,
27432
27757
  serverId: args.serverId,
27433
27758
  organizationId: args.organizationId,
27434
27759
  projectId: args.prepared.state.projectId,
27435
27760
  devBaseUrl: args.inspector ? baseUrl : undefined
27436
27761
  }) : null;
27437
- const handlersWatcher = args.watch ? startHandlersWatcher({
27762
+ const handlersWatcher = args.watch && !args.gitNative ? startHandlersWatcher({
27438
27763
  cwd: args.cwd,
27439
27764
  serverId: args.serverId,
27440
27765
  organizationId: args.organizationId,
@@ -27443,7 +27768,7 @@ async function runWithRuntime(args) {
27443
27768
  let specWatcher = null;
27444
27769
  let pendingApply = null;
27445
27770
  let regenInFlight = false;
27446
- if (args.specPath) {
27771
+ if (args.specPath && !args.gitNative) {
27447
27772
  const initial = readSpecFile(args.specPath);
27448
27773
  let lastAppliedHash = initial.hash;
27449
27774
  specWatcher = watchSpecFile({
@@ -27524,20 +27849,60 @@ async function runWithRuntime(args) {
27524
27849
  printInfo(` ${c.dim("Inspector: ")} ${c.cyan(`${baseUrl}/__inspect`)}`);
27525
27850
  }
27526
27851
  printInfo(` ${c.dim(`Edit files in ${relative7(args.cwd, args.prepared.destDir)}/ to hot-reload.`)}`);
27852
+ if (args.gitNative && args.prepared.repoDir) {
27853
+ printInfo(` ${c.dim(`Git-native: edit + commit + push in ${relative7(args.cwd, args.prepared.repoDir)}/ — the webhook reconciles src/**.`)}`);
27854
+ printInfo(` ${c.dim("Config under mcpcloud/ is read-only; change it in the dashboard. Deploy when ready (mcp deploy).")}`);
27855
+ }
27527
27856
  if (toolsWatcher) {
27528
27857
  printInfo(` ${c.dim(`Edit tool metadata in .mcpcloud/tools/${args.serverId}/ (changes push to cloud on save).`)}`);
27529
27858
  }
27530
27859
  if (handlersWatcher) {
27531
27860
  printInfo(` ${c.dim(`Edit tool handlers in .mcpcloud/server/src/tools/<slug>.ts (changes push to cloud on save).`)}`);
27532
27861
  }
27533
- if (args.specPath) {
27862
+ if (specWatcher) {
27534
27863
  printInfo(` ${c.dim(`Spec watch: ${relative7(args.cwd, args.specPath)}`)}`);
27535
27864
  }
27536
- await emitStaleHandlerOverrideWarning({
27537
- organizationId: args.organizationId,
27538
- projectId: args.prepared.state.projectId,
27539
- serverId: args.serverId
27540
- });
27865
+ const gitPullPoller = args.gitNative ? startGitPullPoll({
27866
+ fetchStatus: () => resolveServerGitPullStatus({
27867
+ serverId: args.serverId,
27868
+ organizationId: args.organizationId
27869
+ }),
27870
+ onChange: (status) => {
27871
+ if (shuttingDown)
27872
+ return;
27873
+ const sha = status.lastPulledGitSha ? status.lastPulledGitSha.slice(0, 7) : "HEAD";
27874
+ if (status.rejectedFileCount > 0) {
27875
+ printWarn(`Push ${sha}: MCPCloud rejected ${status.rejectedFileCount} file${status.rejectedFileCount === 1 ? "" : "s"} (unsafe handler code).`);
27876
+ if (status.error)
27877
+ printInfo(` ${c.dim(status.error)}`);
27878
+ printInfo(` ${c.dim("Fix the flagged file(s) and push again.")}`);
27879
+ return;
27880
+ }
27881
+ if (status.status === "error") {
27882
+ printWarn(`Push ${sha}: MCPCloud could not reconcile — ${status.error ?? "unknown error"}.`);
27883
+ return;
27884
+ }
27885
+ if (status.status !== "pulled") {
27886
+ printInfo(`Push ${sha}: ${status.hadConfigDrift ? "touched the read-only mcpcloud/ config — ignored." : "touched only generated files outside src/** — nothing synced."}`);
27887
+ printInfo(` ${c.dim("Only src/ round-trips; edit there to sync. README / mcpcloud/ are generated.")}`);
27888
+ return;
27889
+ }
27890
+ const parts = [`${status.pulledFileCount} updated`];
27891
+ if (status.deletedFileCount > 0) {
27892
+ parts.push(`${status.deletedFileCount} deleted`);
27893
+ }
27894
+ printSuccess(`Push ${sha} reconciled (${parts.join(", ")}) — changes ready for deployment.`);
27895
+ printInfo(` ${c.dim("Deploy with")} ${c.bold("mcp deploy")} ${c.dim("or the dashboard — no preview needed, this session is your local server.")}`);
27896
+ },
27897
+ onError: () => {}
27898
+ }) : null;
27899
+ if (!args.gitNative) {
27900
+ await emitStaleHandlerOverrideWarning({
27901
+ organizationId: args.organizationId,
27902
+ projectId: args.prepared.state.projectId,
27903
+ serverId: args.serverId
27904
+ });
27905
+ }
27541
27906
  printInfo(` ${c.dim("Ctrl+C to stop.")}`);
27542
27907
  if (isJsonMode()) {
27543
27908
  printJson({
@@ -27576,6 +27941,9 @@ async function runWithRuntime(args) {
27576
27941
  try {
27577
27942
  specWatcher?.close();
27578
27943
  } catch {}
27944
+ try {
27945
+ gitPullPoller?.close();
27946
+ } catch {}
27579
27947
  if (pendingApply) {
27580
27948
  clearTimeout(pendingApply);
27581
27949
  pendingApply = null;
@@ -27595,6 +27963,7 @@ async function runWithRuntime(args) {
27595
27963
  printSuccess("Bye.");
27596
27964
  process.exit(0);
27597
27965
  };
27966
+ fullShutdownReady = true;
27598
27967
  process.on("SIGINT", () => void shutdown("SIGINT"));
27599
27968
  process.on("SIGTERM", () => void shutdown("SIGTERM"));
27600
27969
  shortcuts = startShortcuts({
@@ -28018,13 +28387,13 @@ async function confirmRollback(deploymentId) {
28018
28387
  }
28019
28388
 
28020
28389
  // src/commands/doctor.ts
28021
- import { existsSync as existsSync33, statSync as statSync6, readFileSync as readFileSync17 } from "node:fs";
28390
+ import { existsSync as existsSync34, statSync as statSync6, readFileSync as readFileSync18 } from "node:fs";
28022
28391
  import { homedir as homedir9, platform as platform6 } from "node:os";
28023
- import { join as join27, delimiter as delimiter3 } from "node:path";
28392
+ import { join as join28, delimiter as delimiter3 } from "node:path";
28024
28393
 
28025
28394
  // src/lib/version-check.ts
28026
- import { existsSync as existsSync32, mkdirSync as mkdirSync13, readFileSync as readFileSync16, writeFileSync as writeFileSync16 } from "node:fs";
28027
- import { join as join26 } from "node:path";
28395
+ import { existsSync as existsSync33, mkdirSync as mkdirSync14, readFileSync as readFileSync17, writeFileSync as writeFileSync17 } from "node:fs";
28396
+ import { join as join27 } from "node:path";
28028
28397
  var CACHE_TTL_MS = 24 * 60 * 60 * 1000;
28029
28398
  var FETCH_TIMEOUT_MS2 = 2000;
28030
28399
  var REGISTRY_URL = "https://registry.npmjs.org/@mcpcloud/cli/latest";
@@ -28032,13 +28401,13 @@ function cacheDir() {
28032
28401
  return configDir();
28033
28402
  }
28034
28403
  function cacheFile() {
28035
- return join26(cacheDir(), "version-check.json");
28404
+ return join27(cacheDir(), "version-check.json");
28036
28405
  }
28037
28406
  function readCache() {
28038
- if (!existsSync32(cacheFile()))
28407
+ if (!existsSync33(cacheFile()))
28039
28408
  return null;
28040
28409
  try {
28041
- const raw = JSON.parse(readFileSync16(cacheFile(), "utf-8"));
28410
+ const raw = JSON.parse(readFileSync17(cacheFile(), "utf-8"));
28042
28411
  if (typeof raw.latest !== "string" || typeof raw.fetchedAt !== "number")
28043
28412
  return null;
28044
28413
  return { latest: raw.latest, fetchedAt: raw.fetchedAt };
@@ -28048,9 +28417,9 @@ function readCache() {
28048
28417
  }
28049
28418
  function writeCache(entry) {
28050
28419
  try {
28051
- if (!existsSync32(cacheDir()))
28052
- mkdirSync13(cacheDir(), { recursive: true, mode: 448 });
28053
- writeFileSync16(cacheFile(), JSON.stringify(entry, null, 2), { mode: 384 });
28420
+ if (!existsSync33(cacheDir()))
28421
+ mkdirSync14(cacheDir(), { recursive: true, mode: 448 });
28422
+ writeFileSync17(cacheFile(), JSON.stringify(entry, null, 2), { mode: 384 });
28054
28423
  } catch {}
28055
28424
  }
28056
28425
  function compareVersions(a, b) {
@@ -28161,8 +28530,8 @@ function checkNode() {
28161
28530
  }
28162
28531
  function checkConfigFile() {
28163
28532
  const t0 = Date.now();
28164
- const path = join27(homedir9(), ".mcpcloud", "config.json");
28165
- if (!existsSync33(path)) {
28533
+ const path = join28(homedir9(), ".mcpcloud", "config.json");
28534
+ if (!existsSync34(path)) {
28166
28535
  return {
28167
28536
  name: "Config file",
28168
28537
  status: "warn",
@@ -28184,7 +28553,7 @@ function checkConfigFile() {
28184
28553
  } catch {}
28185
28554
  }
28186
28555
  try {
28187
- JSON.parse(readFileSync17(path, "utf-8"));
28556
+ JSON.parse(readFileSync18(path, "utf-8"));
28188
28557
  } catch (err) {
28189
28558
  return {
28190
28559
  name: "Config file",
@@ -28319,8 +28688,8 @@ function checkClaudeCli() {
28319
28688
  for (const dir of PATH.split(delimiter3)) {
28320
28689
  if (!dir)
28321
28690
  continue;
28322
- const candidate = join27(dir, exe);
28323
- if (existsSync33(candidate)) {
28691
+ const candidate = join28(dir, exe);
28692
+ if (existsSync34(candidate)) {
28324
28693
  return {
28325
28694
  name: "claude CLI",
28326
28695
  status: "pass",
@@ -29261,38 +29630,38 @@ function registerOAuthCommands(program2) {
29261
29630
  // src/lib/plugins.ts
29262
29631
  import { spawn as spawn7 } from "node:child_process";
29263
29632
  import {
29264
- existsSync as existsSync34,
29265
- mkdirSync as mkdirSync14,
29633
+ existsSync as existsSync35,
29634
+ mkdirSync as mkdirSync15,
29266
29635
  readdirSync as readdirSync4,
29267
- readFileSync as readFileSync18,
29636
+ readFileSync as readFileSync19,
29268
29637
  rmSync as rmSync3,
29269
29638
  statSync as statSync7
29270
29639
  } from "node:fs";
29271
- import { join as join28 } from "node:path";
29640
+ import { join as join29 } from "node:path";
29272
29641
  import { pathToFileURL } from "node:url";
29273
29642
  function pluginsDir() {
29274
- return join28(configDir(), "plugins");
29643
+ return join29(configDir(), "plugins");
29275
29644
  }
29276
29645
  function ensureDir2() {
29277
29646
  const dir = pluginsDir();
29278
- if (!existsSync34(dir))
29279
- mkdirSync14(dir, { recursive: true, mode: 448 });
29647
+ if (!existsSync35(dir))
29648
+ mkdirSync15(dir, { recursive: true, mode: 448 });
29280
29649
  }
29281
29650
  function readManifestFromPackageDir(packageDir) {
29282
- const pkgPath = join28(packageDir, "package.json");
29283
- if (!existsSync34(pkgPath))
29651
+ const pkgPath = join29(packageDir, "package.json");
29652
+ if (!existsSync35(pkgPath))
29284
29653
  return null;
29285
29654
  let pkg;
29286
29655
  try {
29287
- pkg = JSON.parse(readFileSync18(pkgPath, "utf-8"));
29656
+ pkg = JSON.parse(readFileSync19(pkgPath, "utf-8"));
29288
29657
  } catch {
29289
29658
  return null;
29290
29659
  }
29291
29660
  if (!pkg.name)
29292
29661
  return null;
29293
29662
  const relEntry = pkg.mcpsh?.register ?? pkg.main ?? "index.js";
29294
- const entry = join28(packageDir, relEntry);
29295
- if (!existsSync34(entry))
29663
+ const entry = join29(packageDir, relEntry);
29664
+ if (!existsSync35(entry))
29296
29665
  return null;
29297
29666
  return {
29298
29667
  name: pkg.name,
@@ -29303,11 +29672,11 @@ function readManifestFromPackageDir(packageDir) {
29303
29672
  }
29304
29673
  function listInstalledPlugins() {
29305
29674
  const dir = pluginsDir();
29306
- if (!existsSync34(dir))
29675
+ if (!existsSync35(dir))
29307
29676
  return [];
29308
29677
  const out = [];
29309
29678
  for (const entry of readdirSync4(dir)) {
29310
- const full = join28(dir, entry);
29679
+ const full = join29(dir, entry);
29311
29680
  let s;
29312
29681
  try {
29313
29682
  s = statSync7(full);
@@ -29318,7 +29687,7 @@ function listInstalledPlugins() {
29318
29687
  continue;
29319
29688
  if (entry.startsWith("@")) {
29320
29689
  for (const child of readdirSync4(full)) {
29321
- const m2 = readManifestFromPackageDir(join28(full, child));
29690
+ const m2 = readManifestFromPackageDir(join29(full, child));
29322
29691
  if (m2)
29323
29692
  out.push(m2);
29324
29693
  }
@@ -29351,26 +29720,43 @@ async function loadPlugins(program2) {
29351
29720
  }
29352
29721
  }
29353
29722
  }
29354
- function runShell(command, cwd) {
29723
+ var PACKAGE_SPEC_PATTERN = /^(@[a-z0-9~-][\w.~-]*\/)?[a-z0-9~-][\w.~-]*(@[-\w.^~><=*+|]+)?$/i;
29724
+ function runNpm(args, cwd) {
29355
29725
  return new Promise((resolve7) => {
29356
- const child = spawn7(command, { shell: true, cwd, stdio: "inherit" });
29726
+ const isWindows = process.platform === "win32";
29727
+ const child = spawn7(isWindows ? "npm.cmd" : "npm", args, {
29728
+ shell: isWindows,
29729
+ cwd,
29730
+ stdio: "inherit"
29731
+ });
29357
29732
  child.on("close", (code) => resolve7(code ?? 1));
29358
29733
  child.on("error", () => resolve7(1));
29359
29734
  });
29360
29735
  }
29361
29736
  async function installPlugin(packageSpec) {
29737
+ if (!PACKAGE_SPEC_PATTERN.test(packageSpec)) {
29738
+ throw new Error(`"${packageSpec}" is not a plain name@version npm package spec. URL, git, and path specs are not supported for plugins.`);
29739
+ }
29362
29740
  ensureDir2();
29363
29741
  const dir = pluginsDir();
29364
- const code = await runShell(`npm install --prefix "${dir}" --no-save --silent ${JSON.stringify(packageSpec)}`, dir);
29742
+ const code = await runNpm([
29743
+ "install",
29744
+ "--prefix",
29745
+ dir,
29746
+ "--no-save",
29747
+ "--silent",
29748
+ "--ignore-scripts",
29749
+ packageSpec
29750
+ ], dir);
29365
29751
  if (code !== 0) {
29366
29752
  throw new Error(`npm install ${packageSpec} exited with code ${code}.`);
29367
29753
  }
29368
- const nm = join28(dir, "node_modules");
29369
- if (!existsSync34(nm)) {
29754
+ const nm = join29(dir, "node_modules");
29755
+ if (!existsSync35(nm)) {
29370
29756
  throw new Error(`npm install ran but produced no node_modules under ${dir}.`);
29371
29757
  }
29372
29758
  const baseName = packageSpec.replace(/@[^@/]+$/, "");
29373
- const candidatePath = baseName.startsWith("@") ? join28(nm, baseName.split("/")[0], baseName.split("/")[1] ?? "") : join28(nm, baseName);
29759
+ const candidatePath = baseName.startsWith("@") ? join29(nm, baseName.split("/")[0], baseName.split("/")[1] ?? "") : join29(nm, baseName);
29374
29760
  const manifest = readManifestFromPackageDir(candidatePath);
29375
29761
  if (!manifest) {
29376
29762
  throw new Error(`Installed but could not read plugin manifest at ${candidatePath}.`);
@@ -29378,26 +29764,26 @@ async function installPlugin(packageSpec) {
29378
29764
  return { name: manifest.name, entry: manifest.entry };
29379
29765
  }
29380
29766
  function removePlugin(name) {
29381
- const nm = join28(pluginsDir(), "node_modules");
29382
- if (!existsSync34(nm))
29767
+ const nm = join29(pluginsDir(), "node_modules");
29768
+ if (!existsSync35(nm))
29383
29769
  return false;
29384
- const target = name.startsWith("@") ? join28(nm, name.split("/")[0], name.split("/")[1] ?? "") : join28(nm, name);
29385
- if (!existsSync34(target))
29770
+ const target = name.startsWith("@") ? join29(nm, name.split("/")[0], name.split("/")[1] ?? "") : join29(nm, name);
29771
+ if (!existsSync35(target))
29386
29772
  return false;
29387
29773
  rmSync3(target, { recursive: true, force: true });
29388
29774
  return true;
29389
29775
  }
29390
29776
  function listInstalledPluginsCombined() {
29391
29777
  const direct = listInstalledPlugins();
29392
- const nm = join28(pluginsDir(), "node_modules");
29393
- if (!existsSync34(nm))
29778
+ const nm = join29(pluginsDir(), "node_modules");
29779
+ if (!existsSync35(nm))
29394
29780
  return direct;
29395
29781
  const seen = new Set(direct.map((p2) => p2.name));
29396
29782
  const out = [...direct];
29397
29783
  for (const entry of readdirSync4(nm)) {
29398
29784
  if (entry === ".bin" || entry === ".package-lock.json")
29399
29785
  continue;
29400
- const full = join28(nm, entry);
29786
+ const full = join29(nm, entry);
29401
29787
  let s;
29402
29788
  try {
29403
29789
  s = statSync7(full);
@@ -29408,7 +29794,7 @@ function listInstalledPluginsCombined() {
29408
29794
  continue;
29409
29795
  if (entry.startsWith("@")) {
29410
29796
  for (const child of readdirSync4(full)) {
29411
- const m2 = readManifestFromPackageDir(join28(full, child));
29797
+ const m2 = readManifestFromPackageDir(join29(full, child));
29412
29798
  if (m2 && !seen.has(m2.name)) {
29413
29799
  seen.add(m2.name);
29414
29800
  out.push(m2);
@@ -30994,12 +31380,12 @@ function displayValueFor(field, value) {
30994
31380
 
30995
31381
  // src/lib/tui/state-store.ts
30996
31382
  import {
30997
- existsSync as existsSync35,
30998
- mkdirSync as mkdirSync15,
30999
- readFileSync as readFileSync19,
31000
- writeFileSync as writeFileSync17
31383
+ existsSync as existsSync36,
31384
+ mkdirSync as mkdirSync16,
31385
+ readFileSync as readFileSync20,
31386
+ writeFileSync as writeFileSync18
31001
31387
  } from "node:fs";
31002
- import { join as join29 } from "node:path";
31388
+ import { join as join30 } from "node:path";
31003
31389
  var ALL_TABS = [
31004
31390
  "servers",
31005
31391
  "projects",
@@ -31009,7 +31395,7 @@ var ALL_TABS = [
31009
31395
  "devSessions"
31010
31396
  ];
31011
31397
  function tuiStateFile() {
31012
- return join29(configDir(), "tui-state.json");
31398
+ return join30(configDir(), "tui-state.json");
31013
31399
  }
31014
31400
  function isTab(v2) {
31015
31401
  return typeof v2 === "string" && ALL_TABS.includes(v2);
@@ -31074,11 +31460,11 @@ function sanitizeTuiState(input) {
31074
31460
  }
31075
31461
  function readTuiState() {
31076
31462
  const path = tuiStateFile();
31077
- if (!existsSync35(path))
31463
+ if (!existsSync36(path))
31078
31464
  return {};
31079
31465
  let parsed;
31080
31466
  try {
31081
- parsed = JSON.parse(readFileSync19(path, "utf-8"));
31467
+ parsed = JSON.parse(readFileSync20(path, "utf-8"));
31082
31468
  } catch {
31083
31469
  return {};
31084
31470
  }
@@ -31086,10 +31472,10 @@ function readTuiState() {
31086
31472
  }
31087
31473
  function writeTuiState(state) {
31088
31474
  try {
31089
- if (!existsSync35(configDir())) {
31090
- mkdirSync15(configDir(), { recursive: true, mode: 448 });
31475
+ if (!existsSync36(configDir())) {
31476
+ mkdirSync16(configDir(), { recursive: true, mode: 448 });
31091
31477
  }
31092
- writeFileSync17(tuiStateFile(), JSON.stringify(state, null, 2), {
31478
+ writeFileSync18(tuiStateFile(), JSON.stringify(state, null, 2), {
31093
31479
  encoding: "utf-8",
31094
31480
  mode: 384
31095
31481
  });
@@ -33713,7 +34099,7 @@ function registerUiCommand(program2) {
33713
34099
 
33714
34100
  // src/commands/update.ts
33715
34101
  import { spawn as spawn9 } from "node:child_process";
33716
- import { dirname as dirname8 } from "node:path";
34102
+ import { dirname as dirname9 } from "node:path";
33717
34103
  import { fileURLToPath as fileURLToPath2 } from "node:url";
33718
34104
  function detectInstallContext() {
33719
34105
  const override = process.env["MCPSH_PACKAGE_MANAGER"]?.trim().toLowerCase();
@@ -33726,7 +34112,7 @@ function detectInstallContext() {
33726
34112
  }
33727
34113
  const here = (() => {
33728
34114
  try {
33729
- return dirname8(fileURLToPath2(import.meta.url));
34115
+ return dirname9(fileURLToPath2(import.meta.url));
33730
34116
  } catch {
33731
34117
  return process.argv[1] ?? "";
33732
34118
  }
@@ -33773,7 +34159,7 @@ function buildCommand(manager) {
33773
34159
  return "npm install -g @mcpcloud/cli@latest";
33774
34160
  }
33775
34161
  }
33776
- function runShell2(command) {
34162
+ function runShell(command) {
33777
34163
  return new Promise((resolve7) => {
33778
34164
  const child = spawn9(command, { shell: true, stdio: "inherit" });
33779
34165
  child.on("close", (code) => resolve7(code ?? 1));
@@ -33821,7 +34207,7 @@ function registerUpdateCommand(program2) {
33821
34207
  const ctx = detectInstallContext();
33822
34208
  printStep(`Running: ${c.cyan(ctx.command)}`);
33823
34209
  printInfo(c.dim(` (${ctx.rationale}; override with MCPSH_PACKAGE_MANAGER)`));
33824
- const code = await runShell2(ctx.command);
34210
+ const code = await runShell(ctx.command);
33825
34211
  if (code !== 0) {
33826
34212
  if (isJsonMode())
33827
34213
  printJson({