@standardagents/code 0.5.0 → 0.6.0

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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import os6 from 'os';
2
+ import os6, { homedir } from 'os';
3
3
  import fs4 from 'fs';
4
4
  import path3 from 'path';
5
5
  import readline2 from 'readline/promises';
@@ -8,6 +8,7 @@ import { stdout, stdin } from 'process';
8
8
  import fsp from 'fs/promises';
9
9
  import crypto from 'crypto';
10
10
  import readline from 'readline';
11
+ import { fileURLToPath } from 'url';
11
12
 
12
13
  // src/api.ts
13
14
  function classifyConnectError(err, endpoint) {
@@ -2105,6 +2106,19 @@ var SUBAGENT_COLORS = [
2105
2106
  ];
2106
2107
  var COMPACTION_COLOR = "\x1B[38;5;208m";
2107
2108
  var COMPACTION_AGENT = "compaction_agent";
2109
+ function isWideCodePoint(cp) {
2110
+ return cp >= 4352 && cp <= 4447 || // Hangul Jamo
2111
+ cp >= 11904 && cp <= 42191 || // CJK radicals … Yi
2112
+ cp >= 44032 && cp <= 55203 || // Hangul syllables
2113
+ cp >= 63744 && cp <= 64255 || // CJK compatibility ideographs
2114
+ cp >= 65072 && cp <= 65103 || // CJK compatibility forms
2115
+ cp >= 65280 && cp <= 65376 || // fullwidth forms
2116
+ cp >= 65504 && cp <= 65510 || cp >= 127744 && cp <= 129791 || // emoji
2117
+ cp >= 131072;
2118
+ }
2119
+ function sanitizeHudRow(s) {
2120
+ return s.replace(/\x1b(?!\[)/g, "").replace(/[\x00-\x1a\x1c-\x1f\x7f]/g, " ");
2121
+ }
2108
2122
  var Tui = class _Tui {
2109
2123
  constructor(level = 1) {
2110
2124
  this.level = level;
@@ -2741,7 +2755,50 @@ var Tui = class _Tui {
2741
2755
  return `${q}${bg}${this.levelColor()}\u276F${C.reset} `;
2742
2756
  }
2743
2757
  visibleWidth(s) {
2744
- return s.replace(/\x1b\[[0-9;]*m/g, "").length;
2758
+ let w = 0;
2759
+ for (const ch of s.replace(/\x1b\[[0-9;]*m/g, "")) {
2760
+ w += isWideCodePoint(ch.codePointAt(0)) ? 2 : 1;
2761
+ }
2762
+ return w;
2763
+ }
2764
+ /**
2765
+ * Truncate a styled string to at most `max` VISIBLE columns, copying ANSI
2766
+ * escape sequences through without counting them and appending a reset if it
2767
+ * cut mid-style. The redraw's whole height math assumes every HUD row is
2768
+ * exactly ONE physical terminal row — true only while each row's visible
2769
+ * width is strictly less than `cols`. A row of width == cols hits the
2770
+ * terminal's auto-wrap boundary: many terminals push the cursor to the next
2771
+ * line, so the following CR/LF yields an EXTRA physical row we didn't count,
2772
+ * and the region marches down the screen leaving a trail on every tick. Every
2773
+ * HUD row is clamped through here to keep that invariant no matter what any
2774
+ * individual line builder produces.
2775
+ */
2776
+ clampVisible(s, max) {
2777
+ let out = "";
2778
+ let width = 0;
2779
+ let i = 0;
2780
+ let truncated = false;
2781
+ while (i < s.length) {
2782
+ if (s[i] === "\x1B" && s[i + 1] === "[") {
2783
+ let j = i + 2;
2784
+ while (j < s.length && !/[a-zA-Z]/.test(s[j])) j++;
2785
+ out += s.slice(i, j + 1);
2786
+ i = j + 1;
2787
+ continue;
2788
+ }
2789
+ const cp = s.codePointAt(i);
2790
+ const chLen = cp > 65535 ? 2 : 1;
2791
+ const chWidth = isWideCodePoint(cp) ? 2 : 1;
2792
+ if (width + chWidth > max) {
2793
+ truncated = true;
2794
+ break;
2795
+ }
2796
+ out += s.slice(i, i + chLen);
2797
+ width += chWidth;
2798
+ i += chLen;
2799
+ }
2800
+ if (truncated) out += C.reset;
2801
+ return out;
2745
2802
  }
2746
2803
  /**
2747
2804
  * The slash-palette block rendered BELOW the input. While the palette is open
@@ -2754,7 +2811,7 @@ var Tui = class _Tui {
2754
2811
  */
2755
2812
  paletteBlockLines(cols2) {
2756
2813
  if (!this.paletteOpen()) return [];
2757
- const rows = this.paletteLines(cols2);
2814
+ const rows = this.paletteLines(cols2).map((r) => this.clampVisible(r, Math.max(1, cols2 - 1)));
2758
2815
  const reserved = Math.max(this.commands.length, rows.length);
2759
2816
  while (rows.length < reserved) rows.push("");
2760
2817
  return rows;
@@ -2823,11 +2880,13 @@ var Tui = class _Tui {
2823
2880
  if (!this.started || this.takeoverHandler) return;
2824
2881
  const cols2 = process.stdout.columns || 80;
2825
2882
  this.moveToRegionTop();
2826
- process.stdout.write("\x1B[J");
2827
2883
  const hudWidths = [];
2884
+ const hudRows = [];
2885
+ const rowCap = Math.max(1, cols2 - 1);
2828
2886
  const writeHudRow = (line) => {
2829
- hudWidths.push(this.visibleWidth(line));
2830
- process.stdout.write(line + "\r\n");
2887
+ const row = this.clampVisible(sanitizeHudRow(line), rowCap);
2888
+ hudWidths.push(this.visibleWidth(row));
2889
+ hudRows.push(row);
2831
2890
  };
2832
2891
  const previewLines = this.streamPreviewLines(cols2);
2833
2892
  for (const line of previewLines) writeHudRow(line);
@@ -2858,8 +2917,8 @@ var Tui = class _Tui {
2858
2917
  const prefix = this.promptPrefix();
2859
2918
  const pw = this.visibleWidth(prefix);
2860
2919
  const lines = this.inputBuffer.split("\n");
2861
- process.stdout.write(prefix + lines[0]);
2862
- for (let i = 1; i < lines.length; i++) process.stdout.write("\r\n" + lines[i]);
2920
+ const tail = [prefix + lines[0]];
2921
+ for (let i = 1; i < lines.length; i++) tail.push("\r\n" + lines[i]);
2863
2922
  const rowsOf = (len, lead) => Math.max(1, Math.ceil((lead + len) / cols2));
2864
2923
  const lineRows = lines.map((l, i) => rowsOf(l.length, i === 0 ? pw : 0));
2865
2924
  const inputRows = lineRows.reduce((a, b) => a + b, 0);
@@ -2874,18 +2933,18 @@ var Tui = class _Tui {
2874
2933
  for (let i = 0; i < caretLine; i++) caretRow += lineRows[i];
2875
2934
  const caretCol = caretCell % cols2;
2876
2935
  const paletteBlock = this.paletteBlockLines(cols2);
2877
- for (const line of paletteBlock) process.stdout.write("\r\n" + line);
2936
+ for (const line of paletteBlock) tail.push("\r\n" + line);
2878
2937
  if (paletteBlock.length > 0) {
2879
- process.stdout.write("\r");
2938
+ tail.push("\r");
2880
2939
  const up = inputRows - 1 + paletteBlock.length - caretRow;
2881
- if (up > 0) process.stdout.write(`\x1B[${up}A`);
2882
- if (caretCol > 0) process.stdout.write(`\x1B[${caretCol}C`);
2940
+ if (up > 0) tail.push(`\x1B[${up}A`);
2941
+ if (caretCol > 0) tail.push(`\x1B[${caretCol}C`);
2883
2942
  this.lastCursorRow = aboveRows + caretRow;
2884
2943
  } else if (this.cursorPos < this.inputBuffer.length) {
2885
- process.stdout.write("\r");
2944
+ tail.push("\r");
2886
2945
  const up = inputRows - 1 - caretRow;
2887
- if (up > 0) process.stdout.write(`\x1B[${up}A`);
2888
- if (caretCol > 0) process.stdout.write(`\x1B[${caretCol}C`);
2946
+ if (up > 0) tail.push(`\x1B[${up}A`);
2947
+ if (caretCol > 0) tail.push(`\x1B[${caretCol}C`);
2889
2948
  this.lastCursorRow = aboveRows + caretRow;
2890
2949
  } else {
2891
2950
  this.lastCursorRow = aboveRows + (inputRows - 1);
@@ -2893,6 +2952,8 @@ var Tui = class _Tui {
2893
2952
  this.drawnHudWidths = hudWidths;
2894
2953
  this.lastDrawnCols = cols2;
2895
2954
  this.bottomDrawn = true;
2955
+ const out = "\x1B[J" + hudRows.join("\r\n") + "\r\n" + tail.join("");
2956
+ process.stdout.write(out);
2896
2957
  }
2897
2958
  clearBottom() {
2898
2959
  if (!this.bottomDrawn) return;
@@ -3206,7 +3267,7 @@ var Tui = class _Tui {
3206
3267
  * resets whenever the label changes.
3207
3268
  */
3208
3269
  setStep(label, outTokens) {
3209
- const next = label && label.trim() ? label.trim() : null;
3270
+ const next = label && label.trim() ? label.replace(/\s+/g, " ").trim() : null;
3210
3271
  if (next !== this.step) {
3211
3272
  this.step = next;
3212
3273
  this.stepStart = Date.now();
@@ -4032,6 +4093,172 @@ function saveDefaultEndpoint(endpoint) {
4032
4093
  } catch {
4033
4094
  }
4034
4095
  }
4096
+ var PKG_NAME = "@standardagents/code";
4097
+ var REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(PKG_NAME)}`;
4098
+ var CACHE_REL_DIR = ".config/standardagents-cli";
4099
+ var CACHE_FILE = "update-check.json";
4100
+ var STATE_FILE = "auto-update.json";
4101
+ var CACHE_TTL_MS = 1e3 * 60 * 60 * 24;
4102
+ var CHECK_TIMEOUT_MS = 4e3;
4103
+ var IN_FLIGHT_TTL_MS = 15 * 60 * 1e3;
4104
+ function cacheDir() {
4105
+ return path3.join(homedir(), CACHE_REL_DIR);
4106
+ }
4107
+ function cachePath() {
4108
+ return path3.join(cacheDir(), CACHE_FILE);
4109
+ }
4110
+ function readCache() {
4111
+ try {
4112
+ const raw = fs4.readFileSync(cachePath(), "utf-8");
4113
+ return JSON.parse(raw);
4114
+ } catch {
4115
+ return null;
4116
+ }
4117
+ }
4118
+ function writeCache(latest) {
4119
+ try {
4120
+ const dir = cacheDir();
4121
+ if (!fs4.existsSync(dir)) fs4.mkdirSync(dir, { recursive: true });
4122
+ fs4.writeFileSync(cachePath(), JSON.stringify({ latest, timestamp: Date.now() }));
4123
+ } catch {
4124
+ }
4125
+ }
4126
+ function readAutoUpdateState(dir = cacheDir()) {
4127
+ try {
4128
+ const raw = fs4.readFileSync(path3.join(dir, STATE_FILE), "utf-8");
4129
+ const state = JSON.parse(raw);
4130
+ return typeof state?.version === "string" ? state : null;
4131
+ } catch {
4132
+ return null;
4133
+ }
4134
+ }
4135
+ function writeAutoUpdateState(state, dir = cacheDir()) {
4136
+ try {
4137
+ if (!fs4.existsSync(dir)) fs4.mkdirSync(dir, { recursive: true });
4138
+ fs4.writeFileSync(path3.join(dir, STATE_FILE), JSON.stringify(state));
4139
+ } catch {
4140
+ }
4141
+ }
4142
+ function clearAutoUpdateState(dir = cacheDir()) {
4143
+ try {
4144
+ fs4.unlinkSync(path3.join(dir, STATE_FILE));
4145
+ } catch {
4146
+ }
4147
+ }
4148
+ function detectPackageManager(selfPath = fileURLToPath(import.meta.url), env = process.env) {
4149
+ const norm = selfPath.split(/[\\/]/).join("/");
4150
+ if (!norm.includes("node_modules/@standardagents/code")) return null;
4151
+ const pnpmHome = (env.PNPM_HOME || "").split(/[\\/]/).join("/");
4152
+ if (pnpmHome && norm.startsWith(pnpmHome)) return "pnpm";
4153
+ if (norm.includes("/.pnpm/") || /\/pnpm\/global\//.test(norm)) return "pnpm";
4154
+ if (norm.includes("/.bun/")) return "bun";
4155
+ if (norm.includes("/.config/yarn/") || norm.includes("/.yarn/")) return "yarn";
4156
+ return "npm";
4157
+ }
4158
+ function updateCommand(pm) {
4159
+ switch (pm) {
4160
+ case "pnpm":
4161
+ return { cmd: "pnpm", args: ["add", "-g", `${PKG_NAME}@latest`], display: `pnpm add -g ${PKG_NAME}@latest` };
4162
+ case "yarn":
4163
+ return { cmd: "yarn", args: ["global", "add", `${PKG_NAME}@latest`], display: `yarn global add ${PKG_NAME}@latest` };
4164
+ case "bun":
4165
+ return { cmd: "bun", args: ["add", "-g", `${PKG_NAME}@latest`], display: `bun add -g ${PKG_NAME}@latest` };
4166
+ default:
4167
+ return { cmd: "npm", args: ["i", "-g", `${PKG_NAME}@latest`], display: `npm i -g ${PKG_NAME}@latest` };
4168
+ }
4169
+ }
4170
+ function decideAutoUpdate(info, opts) {
4171
+ const env = opts.env ?? process.env;
4172
+ if (env.STANDARD_CODE_NO_AUTO_UPDATE) return "disabled";
4173
+ if (!opts.pm) return "dev_checkout";
4174
+ const state = opts.state;
4175
+ if (state && state.version === info.latest) {
4176
+ if (state.exitCode === null) {
4177
+ const now = opts.now ?? Date.now();
4178
+ return now - state.startedAt < IN_FLIGHT_TTL_MS ? "in_flight" : "start";
4179
+ }
4180
+ return state.exitCode === 0 ? "path_shadowed" : "already_failed";
4181
+ }
4182
+ return "start";
4183
+ }
4184
+ function startBackgroundUpdate(latest, pm, dir = cacheDir()) {
4185
+ const startedAt = Date.now();
4186
+ writeAutoUpdateState({ version: latest, startedAt, exitCode: null }, dir);
4187
+ const stateFile = path3.join(dir, STATE_FILE);
4188
+ const { cmd, args } = updateCommand(pm);
4189
+ const script = `const cp=require('child_process');const fs=require('fs');const r=cp.spawnSync(${JSON.stringify(cmd)},${JSON.stringify(args)},{shell:process.platform==='win32',encoding:'utf8'});const out=((r.stdout||'')+(r.stderr||'')).slice(-2000);fs.writeFileSync(${JSON.stringify(stateFile)},JSON.stringify({version:${JSON.stringify(latest)},startedAt:${startedAt},exitCode:r.status==null?-1:r.status,finishedAt:Date.now(),output:out}));`;
4190
+ try {
4191
+ const child = spawn(process.execPath, ["-e", script], { detached: true, stdio: "ignore" });
4192
+ child.unref();
4193
+ return true;
4194
+ } catch {
4195
+ writeAutoUpdateState({ version: latest, startedAt, exitCode: -1, finishedAt: Date.now() }, dir);
4196
+ return false;
4197
+ }
4198
+ }
4199
+ function consumeAppliedUpdate(currentVersion, dir = cacheDir()) {
4200
+ const state = readAutoUpdateState(dir);
4201
+ if (!state || state.version !== currentVersion) return null;
4202
+ clearAutoUpdateState(dir);
4203
+ return state.exitCode === 0 ? state : null;
4204
+ }
4205
+ async function fetchLatest(currentVersion) {
4206
+ const controller = new AbortController();
4207
+ const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
4208
+ try {
4209
+ const res = await fetch(REGISTRY_URL, {
4210
+ signal: controller.signal,
4211
+ headers: {
4212
+ Accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*",
4213
+ "User-Agent": `${PKG_NAME}/${currentVersion}`
4214
+ }
4215
+ });
4216
+ if (!res.ok) return null;
4217
+ const data = await res.json();
4218
+ return data["dist-tags"]?.latest ?? null;
4219
+ } catch {
4220
+ return null;
4221
+ } finally {
4222
+ clearTimeout(timeout);
4223
+ }
4224
+ }
4225
+ async function checkForUpdate(currentVersion) {
4226
+ if (!currentVersion) return null;
4227
+ if (process.env.NO_UPDATE_NOTIFIER || process.env.CI) return null;
4228
+ const cached = readCache();
4229
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
4230
+ if (cached.latest !== currentVersion) {
4231
+ return { current: currentVersion, latest: cached.latest };
4232
+ }
4233
+ return null;
4234
+ }
4235
+ const latest = await fetchLatest(currentVersion);
4236
+ if (!latest) return null;
4237
+ writeCache(latest);
4238
+ return latest !== currentVersion ? { current: currentVersion, latest } : null;
4239
+ }
4240
+ async function forceCheckForUpdate(currentVersion) {
4241
+ if (!currentVersion) return null;
4242
+ if (process.env.NO_UPDATE_NOTIFIER || process.env.CI) return null;
4243
+ const latest = await fetchLatest(currentVersion);
4244
+ if (!latest) return null;
4245
+ writeCache(latest);
4246
+ return latest !== currentVersion ? { current: currentVersion, latest } : null;
4247
+ }
4248
+ function runUpdate(pm) {
4249
+ return new Promise((resolve) => {
4250
+ const { cmd, args } = updateCommand(pm);
4251
+ const child = spawn(cmd, args, {
4252
+ shell: process.platform === "win32",
4253
+ stdio: ["ignore", "pipe", "pipe"]
4254
+ });
4255
+ let out = "";
4256
+ child.stdout?.on("data", (d) => out += d);
4257
+ child.stderr?.on("data", (d) => out += d);
4258
+ child.on("close", (code) => resolve({ ok: code === 0, output: out }));
4259
+ child.on("error", (err) => resolve({ ok: false, output: String(err) }));
4260
+ });
4261
+ }
4035
4262
 
4036
4263
  // src/index.ts
4037
4264
  var AGENT_ID = "standard_code_agent";
@@ -4316,6 +4543,44 @@ ${c.dim}Press Control-C again to exit${c.reset}
4316
4543
 
4317
4544
  `);
4318
4545
  }
4546
+ const version = readVersion();
4547
+ let updateAvailable = null;
4548
+ {
4549
+ const loading = startLoader("Checking for updates");
4550
+ updateAvailable = await checkForUpdate(version);
4551
+ loading.stop();
4552
+ const applied = consumeAppliedUpdate(version);
4553
+ if (applied) {
4554
+ stdout.write(` ${c.green}\u2713${c.reset} ${c.dim}Standard Code updated to v${version}.${c.reset}
4555
+
4556
+ `);
4557
+ }
4558
+ if (updateAvailable) {
4559
+ const pm = detectPackageManager();
4560
+ const decision = decideAutoUpdate(updateAvailable, { state: readAutoUpdateState(), pm });
4561
+ if (decision === "start" && pm && startBackgroundUpdate(updateAvailable.latest, pm)) {
4562
+ stdout.write(
4563
+ ` ${c.teal}\u27F3${c.reset} ${c.dim}Standard Code ${c.reset}${c.bold}v${updateAvailable.latest}${c.reset}${c.dim} is installing in the background \u2014 it applies on your next launch.${c.reset}
4564
+
4565
+ `
4566
+ );
4567
+ } else if (decision === "in_flight") {
4568
+ stdout.write(
4569
+ ` ${c.teal}\u27F3${c.reset} ${c.dim}Standard Code v${updateAvailable.latest} is still installing in the background.${c.reset}
4570
+
4571
+ `
4572
+ );
4573
+ } else {
4574
+ const display = updateCommand(pm ?? "npm").display;
4575
+ stdout.write(
4576
+ ` ${c.teal}\u25C7${c.reset} ${c.dim}Update available:${c.reset} ${c.dim}v${updateAvailable.current}${c.reset} \u2192 ${c.bold}v${updateAvailable.latest}${c.reset}
4577
+ ${c.dim}Run ${c.reset}${c.bold}${display}${c.reset}${c.dim} to update${c.reset}
4578
+
4579
+ `
4580
+ );
4581
+ }
4582
+ }
4583
+ }
4319
4584
  const stored = getCredential(endpoint);
4320
4585
  let api = stored ? new ApiClient(endpoint, stored.access_token) : null;
4321
4586
  let storedCheck = null;
@@ -4675,7 +4940,8 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
4675
4940
  for (const s of subs) {
4676
4941
  const status = (s.status || "").trim();
4677
4942
  if (status === "idle" || status === "terminated") continue;
4678
- const detail = status && status !== "running" ? ` \u2014 ${status.slice(0, 80)}` : "";
4943
+ const oneLineStatus = status.replace(/\s+/g, " ");
4944
+ const detail = oneLineStatus && oneLineStatus !== "running" ? ` \u2014 ${oneLineStatus.slice(0, 80)}` : "";
4679
4945
  activeSubagents.set(s.id, {
4680
4946
  label: `${subagentLabel(s, agentTitles)}${detail}`,
4681
4947
  agentName: s.agent_name ?? void 0
@@ -4871,6 +5137,7 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
4871
5137
  },
4872
5138
  { name: "background", label: "Background processes", hint: "list / stop", run: () => runProcessMenu(tui, bgMgr) },
4873
5139
  { name: "keybindings", label: "Keyboard shortcuts", run: () => showKeybindings(tui) },
5140
+ { name: "update", label: "Check for updates", hint: "check for a newer version", run: () => runUpdateCommand(tui) },
4874
5141
  { name: "logout", label: "Sign out", hint: "delete the saved token & quit", run: () => logout() },
4875
5142
  { name: "quit", label: "Quit", run: () => quit() }
4876
5143
  ]);
@@ -5127,6 +5394,41 @@ function showKeybindings(tui) {
5127
5394
  tui.print(`${c.gray} \u2190${c.reset} from the start of the input: select the [\u2699 n bg] badge (enter opens it)`);
5128
5395
  tui.print(`${c.gray} ctrl-c${c.reset} quit`);
5129
5396
  }
5397
+ async function runUpdateCommand(tui) {
5398
+ const version = readVersion();
5399
+ const result = await forceCheckForUpdate(version);
5400
+ if (!result) {
5401
+ tui.print(`${c.green}\u2713${c.reset} ${c.gray}@standardagents/code${c.reset} is up to date (v${version})`);
5402
+ return;
5403
+ }
5404
+ const { latest } = result;
5405
+ tui.print(`
5406
+ ${c.yellow}\u27F3${c.reset} Update available: ${c.gray}v${version}${c.reset} \u2192 ${c.green}v${latest}${c.reset}`);
5407
+ const pm = detectPackageManager();
5408
+ if (!pm) {
5409
+ tui.print(` ${c.gray}This is a source checkout \u2014 pull the repo to update.${c.reset}`);
5410
+ return;
5411
+ }
5412
+ const { display } = updateCommand(pm);
5413
+ const choice = await tui.select(`Update now with \`${display}\`?`, [
5414
+ { label: "Yes, update now", value: "yes" },
5415
+ { label: "No, skip", value: "no" }
5416
+ ]);
5417
+ if (choice === "yes") {
5418
+ tui.print(` ${c.gray}Running ${display}\u2026${c.reset}`);
5419
+ const { ok, output: pmOutput } = await runUpdate(pm);
5420
+ if (ok) {
5421
+ tui.print(` ${c.green}\u2713${c.reset} Updated to v${latest}. Restart to use the new version.`);
5422
+ } else {
5423
+ tui.print(` ${c.red}\u2717${c.reset} Update failed:`);
5424
+ for (const line of pmOutput.trim().split("\n").slice(-6)) {
5425
+ tui.print(` ${c.dim}${line}${c.reset}`);
5426
+ }
5427
+ }
5428
+ } else {
5429
+ tui.print(` ${c.gray}Skipped. Run /update later.${c.reset}`);
5430
+ }
5431
+ }
5130
5432
  async function runProcessMenu(tui, bg) {
5131
5433
  const procs = await bg.list();
5132
5434
  if (!procs.length) {