From 8a1fb0af7761903d8dc779bcf394fda376012eab Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Mar 2026 17:16:25 +0000 Subject: [PATCH 01/12] feat: make update notification more prominent with bordered box and agent hints Move the upgrade notification to appear after command execution (just before outro) so it's the last substantive output. Display it in a unicode-bordered box using log.warn for visual prominence. Add [Agent Hints] section so AI agents can easily parse the upgrade suggestion. https://claude.ai/code/session_01GDFqeAimhxEVBWJNLEB96Z --- src/cli/utils/runCommand.ts | 9 ++++-- src/cli/utils/upgradeNotification.ts | 47 +++++++++++++++++++++++++--- tests/cli/version-check.spec.ts | 21 +++++++++++++ 3 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/cli/utils/runCommand.ts b/src/cli/utils/runCommand.ts index fa6be349..18445584 100644 --- a/src/cli/utils/runCommand.ts +++ b/src/cli/utils/runCommand.ts @@ -3,7 +3,10 @@ import { login } from "@/cli/commands/auth/login-flow.js"; import type { CLIContext } from "@/cli/types.js"; import { printBanner } from "@/cli/utils/banner.js"; import { theme } from "@/cli/utils/theme.js"; -import { printUpgradeNotificationIfAvailable } from "@/cli/utils/upgradeNotification.js"; +import { + printUpgradeNotification, + startUpgradeCheck, +} from "@/cli/utils/upgradeNotification.js"; import { isLoggedIn, readAuth } from "@/core/auth/index.js"; import { isCLIError } from "@/core/errors.js"; import { initAppConfig } from "@/core/project/index.js"; @@ -76,7 +79,7 @@ export async function runCommand( } else { intro(theme.colors.base44OrangeBackground(" Base 44 ")); } - await printUpgradeNotificationIfAvailable(); + const upgradeCheckPromise = startUpgradeCheck(); try { // Check authentication if required @@ -105,6 +108,7 @@ export async function runCommand( } const result = await commandFn(); + await printUpgradeNotification(upgradeCheckPromise); outro(result.outroMessage || ""); if (result.stdout) { @@ -112,6 +116,7 @@ export async function runCommand( } } catch (error) { displayError(error); + await printUpgradeNotification(upgradeCheckPromise); const errorContext = context.errorReporter.getErrorContext(); outro(theme.format.errorContext(errorContext)); diff --git a/src/cli/utils/upgradeNotification.ts b/src/cli/utils/upgradeNotification.ts index eaf3f173..04bc6060 100644 --- a/src/cli/utils/upgradeNotification.ts +++ b/src/cli/utils/upgradeNotification.ts @@ -1,20 +1,57 @@ import { log } from "@clack/prompts"; +import stripAnsi from "strip-ansi"; import { theme } from "@/cli/utils/theme.js"; import type { UpgradeInfo } from "@/cli/utils/version-check.js"; import { checkForUpgrade } from "@/cli/utils/version-check.js"; -function formatUpgradeMessage(info: UpgradeInfo): string { +const UPGRADE_COMMAND = "npm install -g base44@latest"; + +function formatUpgradeBox(info: UpgradeInfo): string { const { shinyOrange } = theme.colors; const { bold } = theme.styles; - return `${shinyOrange("Update available!")} ${shinyOrange(`${info.currentVersion} → ${info.latestVersion}`)} ${shinyOrange("Run:")} ${bold(shinyOrange("npm install -g base44@latest"))}`; + const lines = [ + shinyOrange( + `Update available! ${info.currentVersion} → ${bold(info.latestVersion)}`, + ), + shinyOrange(`Run: ${bold(UPGRADE_COMMAND)}`), + "", + "[Agent Hints]", + ` Run: ${UPGRADE_COMMAND}`, + ]; + + const maxVisualWidth = Math.max(...lines.map((l) => stripAnsi(l).length)); + const pad = (line: string) => { + const visual = stripAnsi(line).length; + return `${line}${" ".repeat(maxVisualWidth - visual)}`; + }; + + const top = `┌${"─".repeat(maxVisualWidth + 2)}┐`; + const bottom = `└${"─".repeat(maxVisualWidth + 2)}┘`; + const body = lines.map((l) => `│ ${pad(l)} │`).join("\n"); + + return `${top}\n${body}\n${bottom}`; +} + +/** + * Starts the upgrade check in the background. Returns a promise that + * resolves to upgrade info (or null). Never rejects. + */ +export function startUpgradeCheck(): Promise { + return checkForUpgrade().catch(() => null); } -export async function printUpgradeNotificationIfAvailable(): Promise { +/** + * Awaits the upgrade check and prints a bordered notification if an + * upgrade is available. Should be called just before `outro()`. + */ +export async function printUpgradeNotification( + upgradeCheckPromise: Promise, +): Promise { try { - const upgradeInfo = await checkForUpgrade(); + const upgradeInfo = await upgradeCheckPromise; if (upgradeInfo) { - log.message(formatUpgradeMessage(upgradeInfo)); + log.warn(formatUpgradeBox(upgradeInfo)); } } catch { // Silently ignore errors diff --git a/tests/cli/version-check.spec.ts b/tests/cli/version-check.spec.ts index 66cb8708..64c22c4d 100644 --- a/tests/cli/version-check.spec.ts +++ b/tests/cli/version-check.spec.ts @@ -26,6 +26,27 @@ describe("upgrade notification", () => { t.expectResult(result).toNotContain("Update available!"); }); + it("displays upgrade notification in a bordered box", async () => { + t.givenLatestVersion("1.0.0"); + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + + const result = await t.run("whoami"); + + t.expectResult(result).toSucceed(); + t.expectResult(result).toContain("┌"); + t.expectResult(result).toContain("└"); + }); + + it("includes agent hints in upgrade notification", async () => { + t.givenLatestVersion("1.0.0"); + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + + const result = await t.run("whoami"); + + t.expectResult(result).toSucceed(); + t.expectResult(result).toContain("[Agent Hints]"); + }); + it("does not display notification when check is not overridden", async () => { // Opt into real npm version check (default is null which skips it) t.givenLatestVersion(undefined); From bdeea5c9f9aad057b2cd2e79b376f411b0aa1fee Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 5 Mar 2026 10:19:35 +0000 Subject: [PATCH 02/12] fix: remove redundant agent hints from upgrade notification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The command is already displayed in the human-readable lines — the [Agent Hints] block just duplicated it unnecessarily. https://claude.ai/code/session_01GDFqeAimhxEVBWJNLEB96Z --- src/cli/utils/upgradeNotification.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/cli/utils/upgradeNotification.ts b/src/cli/utils/upgradeNotification.ts index 04bc6060..2dd1b5f0 100644 --- a/src/cli/utils/upgradeNotification.ts +++ b/src/cli/utils/upgradeNotification.ts @@ -15,9 +15,6 @@ function formatUpgradeBox(info: UpgradeInfo): string { `Update available! ${info.currentVersion} → ${bold(info.latestVersion)}`, ), shinyOrange(`Run: ${bold(UPGRADE_COMMAND)}`), - "", - "[Agent Hints]", - ` Run: ${UPGRADE_COMMAND}`, ]; const maxVisualWidth = Math.max(...lines.map((l) => stripAnsi(l).length)); From 26c5375770f1c15ebbe4192964ac1931eb56640b Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Mar 2026 08:12:54 +0000 Subject: [PATCH 03/12] refactor: use @clack/prompts box() for upgrade notification Replace hand-rolled Unicode box-drawing with the native box() function from @clack/prompts, as requested in PR review. https://claude.ai/code/session_01GDFqeAimhxEVBWJNLEB96Z --- src/cli/utils/upgradeNotification.ts | 42 ++++++++++------------------ 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/src/cli/utils/upgradeNotification.ts b/src/cli/utils/upgradeNotification.ts index 2dd1b5f0..f2259abe 100644 --- a/src/cli/utils/upgradeNotification.ts +++ b/src/cli/utils/upgradeNotification.ts @@ -1,35 +1,10 @@ -import { log } from "@clack/prompts"; -import stripAnsi from "strip-ansi"; +import { box } from "@clack/prompts"; import { theme } from "@/cli/utils/theme.js"; import type { UpgradeInfo } from "@/cli/utils/version-check.js"; import { checkForUpgrade } from "@/cli/utils/version-check.js"; const UPGRADE_COMMAND = "npm install -g base44@latest"; -function formatUpgradeBox(info: UpgradeInfo): string { - const { shinyOrange } = theme.colors; - const { bold } = theme.styles; - - const lines = [ - shinyOrange( - `Update available! ${info.currentVersion} → ${bold(info.latestVersion)}`, - ), - shinyOrange(`Run: ${bold(UPGRADE_COMMAND)}`), - ]; - - const maxVisualWidth = Math.max(...lines.map((l) => stripAnsi(l).length)); - const pad = (line: string) => { - const visual = stripAnsi(line).length; - return `${line}${" ".repeat(maxVisualWidth - visual)}`; - }; - - const top = `┌${"─".repeat(maxVisualWidth + 2)}┐`; - const bottom = `└${"─".repeat(maxVisualWidth + 2)}┘`; - const body = lines.map((l) => `│ ${pad(l)} │`).join("\n"); - - return `${top}\n${body}\n${bottom}`; -} - /** * Starts the upgrade check in the background. Returns a promise that * resolves to upgrade info (or null). Never rejects. @@ -48,7 +23,20 @@ export async function printUpgradeNotification( try { const upgradeInfo = await upgradeCheckPromise; if (upgradeInfo) { - log.warn(formatUpgradeBox(upgradeInfo)); + const { shinyOrange } = theme.colors; + const { bold } = theme.styles; + + const message = [ + shinyOrange( + `Update available! ${upgradeInfo.currentVersion} → ${bold(upgradeInfo.latestVersion)}`, + ), + shinyOrange(`Run: ${bold(UPGRADE_COMMAND)}`), + "", + "[Agent Hints]", + ` Run: ${UPGRADE_COMMAND}`, + ].join("\n"); + + box(message); } } catch { // Silently ignore errors From 26a09f80680ce721a9c35359a1c0ea0949563fb4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Mar 2026 08:18:40 +0000 Subject: [PATCH 04/12] remove added comments from upgradeNotification https://claude.ai/code/session_01GDFqeAimhxEVBWJNLEB96Z --- src/cli/utils/upgradeNotification.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/cli/utils/upgradeNotification.ts b/src/cli/utils/upgradeNotification.ts index f2259abe..5dbe82e5 100644 --- a/src/cli/utils/upgradeNotification.ts +++ b/src/cli/utils/upgradeNotification.ts @@ -5,18 +5,10 @@ import { checkForUpgrade } from "@/cli/utils/version-check.js"; const UPGRADE_COMMAND = "npm install -g base44@latest"; -/** - * Starts the upgrade check in the background. Returns a promise that - * resolves to upgrade info (or null). Never rejects. - */ export function startUpgradeCheck(): Promise { return checkForUpgrade().catch(() => null); } -/** - * Awaits the upgrade check and prints a bordered notification if an - * upgrade is available. Should be called just before `outro()`. - */ export async function printUpgradeNotification( upgradeCheckPromise: Promise, ): Promise { @@ -38,7 +30,5 @@ export async function printUpgradeNotification( box(message); } - } catch { - // Silently ignore errors - } + } catch {} } From 090770748d36c113ced5332d6a614a22f9bae396 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Mar 2026 08:24:24 +0000 Subject: [PATCH 05/12] remove agent hints from upgrade notification https://claude.ai/code/session_01GDFqeAimhxEVBWJNLEB96Z --- src/cli/utils/upgradeNotification.ts | 3 --- tests/cli/version-check.spec.ts | 10 ---------- 2 files changed, 13 deletions(-) diff --git a/src/cli/utils/upgradeNotification.ts b/src/cli/utils/upgradeNotification.ts index 5dbe82e5..19465307 100644 --- a/src/cli/utils/upgradeNotification.ts +++ b/src/cli/utils/upgradeNotification.ts @@ -23,9 +23,6 @@ export async function printUpgradeNotification( `Update available! ${upgradeInfo.currentVersion} → ${bold(upgradeInfo.latestVersion)}`, ), shinyOrange(`Run: ${bold(UPGRADE_COMMAND)}`), - "", - "[Agent Hints]", - ` Run: ${UPGRADE_COMMAND}`, ].join("\n"); box(message); diff --git a/tests/cli/version-check.spec.ts b/tests/cli/version-check.spec.ts index 64c22c4d..d07a819a 100644 --- a/tests/cli/version-check.spec.ts +++ b/tests/cli/version-check.spec.ts @@ -37,16 +37,6 @@ describe("upgrade notification", () => { t.expectResult(result).toContain("└"); }); - it("includes agent hints in upgrade notification", async () => { - t.givenLatestVersion("1.0.0"); - await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); - - const result = await t.run("whoami"); - - t.expectResult(result).toSucceed(); - t.expectResult(result).toContain("[Agent Hints]"); - }); - it("does not display notification when check is not overridden", async () => { // Opt into real npm version check (default is null which skips it) t.givenLatestVersion(undefined); From a10298abeb62eca08de1df4792d66ad87d6bfd3f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Mar 2026 08:40:57 +0000 Subject: [PATCH 06/12] remove bordered box implementation detail test https://claude.ai/code/session_01GDFqeAimhxEVBWJNLEB96Z --- tests/cli/version-check.spec.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/cli/version-check.spec.ts b/tests/cli/version-check.spec.ts index d07a819a..66cb8708 100644 --- a/tests/cli/version-check.spec.ts +++ b/tests/cli/version-check.spec.ts @@ -26,17 +26,6 @@ describe("upgrade notification", () => { t.expectResult(result).toNotContain("Update available!"); }); - it("displays upgrade notification in a bordered box", async () => { - t.givenLatestVersion("1.0.0"); - await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); - - const result = await t.run("whoami"); - - t.expectResult(result).toSucceed(); - t.expectResult(result).toContain("┌"); - t.expectResult(result).toContain("└"); - }); - it("does not display notification when check is not overridden", async () => { // Opt into real npm version check (default is null which skips it) t.givenLatestVersion(undefined); From f25e1aa16113f791792c14a9052ee8913825e5d6 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Mar 2026 08:44:33 +0000 Subject: [PATCH 07/12] refactor: extract formatUpgradeMessage helper https://claude.ai/code/session_01GDFqeAimhxEVBWJNLEB96Z --- src/cli/utils/upgradeNotification.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/cli/utils/upgradeNotification.ts b/src/cli/utils/upgradeNotification.ts index 19465307..0a99ae77 100644 --- a/src/cli/utils/upgradeNotification.ts +++ b/src/cli/utils/upgradeNotification.ts @@ -9,23 +9,25 @@ export function startUpgradeCheck(): Promise { return checkForUpgrade().catch(() => null); } +function formatUpgradeMessage(info: UpgradeInfo): string { + const { shinyOrange } = theme.colors; + const { bold } = theme.styles; + + return [ + shinyOrange( + `Update available! ${info.currentVersion} → ${bold(info.latestVersion)}`, + ), + shinyOrange(`Run: ${bold(UPGRADE_COMMAND)}`), + ].join("\n"); +} + export async function printUpgradeNotification( upgradeCheckPromise: Promise, ): Promise { try { const upgradeInfo = await upgradeCheckPromise; if (upgradeInfo) { - const { shinyOrange } = theme.colors; - const { bold } = theme.styles; - - const message = [ - shinyOrange( - `Update available! ${upgradeInfo.currentVersion} → ${bold(upgradeInfo.latestVersion)}`, - ), - shinyOrange(`Run: ${bold(UPGRADE_COMMAND)}`), - ].join("\n"); - - box(message); + box(formatUpgradeMessage(upgradeInfo)); } } catch {} } From cc26596734062f49979310c3d11adbf89cda8140 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Mar 2026 08:47:57 +0000 Subject: [PATCH 08/12] skip upgrade notification on command errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep error output clean — only show upgrade notification on success. https://claude.ai/code/session_01GDFqeAimhxEVBWJNLEB96Z --- src/cli/utils/runCommand.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cli/utils/runCommand.ts b/src/cli/utils/runCommand.ts index 18445584..c5529bc8 100644 --- a/src/cli/utils/runCommand.ts +++ b/src/cli/utils/runCommand.ts @@ -116,7 +116,6 @@ export async function runCommand( } } catch (error) { displayError(error); - await printUpgradeNotification(upgradeCheckPromise); const errorContext = context.errorReporter.getErrorContext(); outro(theme.format.errorContext(errorContext)); From a95f6233665b26380b43df3f494a8cf96371c03e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Mar 2026 08:53:35 +0000 Subject: [PATCH 09/12] use single-line format for upgrade message Match original formatUpgradeMessage style. https://claude.ai/code/session_01GDFqeAimhxEVBWJNLEB96Z --- src/cli/utils/upgradeNotification.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/cli/utils/upgradeNotification.ts b/src/cli/utils/upgradeNotification.ts index 0a99ae77..459da005 100644 --- a/src/cli/utils/upgradeNotification.ts +++ b/src/cli/utils/upgradeNotification.ts @@ -13,12 +13,9 @@ function formatUpgradeMessage(info: UpgradeInfo): string { const { shinyOrange } = theme.colors; const { bold } = theme.styles; - return [ - shinyOrange( - `Update available! ${info.currentVersion} → ${bold(info.latestVersion)}`, - ), - shinyOrange(`Run: ${bold(UPGRADE_COMMAND)}`), - ].join("\n"); + return shinyOrange( + `Update available! ${info.currentVersion} → ${bold(info.latestVersion)} Run: ${bold(UPGRADE_COMMAND)}`, + ); } export async function printUpgradeNotification( From c45cfff875cede891381d3ddc479f55f17ae4751 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Mar 2026 09:01:05 +0000 Subject: [PATCH 10/12] split upgrade message into two lines for readability https://claude.ai/code/session_01GDFqeAimhxEVBWJNLEB96Z --- src/cli/utils/upgradeNotification.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cli/utils/upgradeNotification.ts b/src/cli/utils/upgradeNotification.ts index 459da005..0a99ae77 100644 --- a/src/cli/utils/upgradeNotification.ts +++ b/src/cli/utils/upgradeNotification.ts @@ -13,9 +13,12 @@ function formatUpgradeMessage(info: UpgradeInfo): string { const { shinyOrange } = theme.colors; const { bold } = theme.styles; - return shinyOrange( - `Update available! ${info.currentVersion} → ${bold(info.latestVersion)} Run: ${bold(UPGRADE_COMMAND)}`, - ); + return [ + shinyOrange( + `Update available! ${info.currentVersion} → ${bold(info.latestVersion)}`, + ), + shinyOrange(`Run: ${bold(UPGRADE_COMMAND)}`), + ].join("\n"); } export async function printUpgradeNotification( From ced7311cbea3908c5ca50fba8a1ddffac6427f34 Mon Sep 17 00:00:00 2001 From: Kfir Strikovsky Date: Wed, 11 Mar 2026 19:06:09 +0200 Subject: [PATCH 11/12] show nicer upgrade messages --- packages/cli/bin/binary-entry.ts | 2 +- packages/cli/src/cli/index.ts | 9 ++++- packages/cli/src/cli/types.ts | 3 ++ packages/cli/src/cli/utils/runCommand.ts | 3 +- .../cli/src/cli/utils/upgradeNotification.ts | 38 ++++++++++++++++--- 5 files changed, 46 insertions(+), 9 deletions(-) diff --git a/packages/cli/bin/binary-entry.ts b/packages/cli/bin/binary-entry.ts index eee5717f..773c596b 100644 --- a/packages/cli/bin/binary-entry.ts +++ b/packages/cli/bin/binary-entry.ts @@ -37,4 +37,4 @@ if (!process.stdin.isTTY || !process.stdout.isTTY) { process.env.CI = "true"; } -await runCLI(); +await runCLI({ distribution: "binary" }); diff --git a/packages/cli/src/cli/index.ts b/packages/cli/src/cli/index.ts index 8821542a..65fa89b2 100644 --- a/packages/cli/src/cli/index.ts +++ b/packages/cli/src/cli/index.ts @@ -6,11 +6,15 @@ import { readAuth } from "@/core/auth/index.js"; import { CLIExitError } from "./errors.js"; import { ErrorReporter } from "./telemetry/error-reporter.js"; import { addCommandInfoToErrorReporter } from "./telemetry/index.js"; -import type { CLIContext } from "./types.js"; +import type { CLIContext, Distribution } from "./types.js"; const __dirname = dirname(fileURLToPath(import.meta.url)); -async function runCLI(): Promise { +interface RunCLIOptions { + distribution?: Distribution; +} + +async function runCLI(options?: RunCLIOptions): Promise { ensureNpmAssets(join(__dirname, "../assets")); // Create error reporter - single instance for the CLI session @@ -24,6 +28,7 @@ async function runCLI(): Promise { const context: CLIContext = { errorReporter, isNonInteractive, + distribution: options?.distribution ?? "npm", }; // Create program with injected context diff --git a/packages/cli/src/cli/types.ts b/packages/cli/src/cli/types.ts index 124ac9de..b72d30f6 100644 --- a/packages/cli/src/cli/types.ts +++ b/packages/cli/src/cli/types.ts @@ -1,6 +1,9 @@ import type { ErrorReporter } from "./telemetry/error-reporter.js"; +export type Distribution = "npm" | "binary"; + export interface CLIContext { errorReporter: ErrorReporter; isNonInteractive: boolean; + distribution: Distribution; } diff --git a/packages/cli/src/cli/utils/runCommand.ts b/packages/cli/src/cli/utils/runCommand.ts index c5529bc8..99f4dc1f 100644 --- a/packages/cli/src/cli/utils/runCommand.ts +++ b/packages/cli/src/cli/utils/runCommand.ts @@ -108,7 +108,8 @@ export async function runCommand( } const result = await commandFn(); - await printUpgradeNotification(upgradeCheckPromise); + await printUpgradeNotification(upgradeCheckPromise, context.distribution); + outro(result.outroMessage || ""); if (result.stdout) { diff --git a/packages/cli/src/cli/utils/upgradeNotification.ts b/packages/cli/src/cli/utils/upgradeNotification.ts index 0a99ae77..912a31ab 100644 --- a/packages/cli/src/cli/utils/upgradeNotification.ts +++ b/packages/cli/src/cli/utils/upgradeNotification.ts @@ -1,33 +1,61 @@ -import { box } from "@clack/prompts"; +import { note } from "@clack/prompts"; +import type { Distribution } from "@/cli/types.js"; import { theme } from "@/cli/utils/theme.js"; import type { UpgradeInfo } from "@/cli/utils/version-check.js"; import { checkForUpgrade } from "@/cli/utils/version-check.js"; -const UPGRADE_COMMAND = "npm install -g base44@latest"; +type InstallMethod = "npm" | "brew" | "binary"; + +function detectInstallMethod(distribution: Distribution): InstallMethod { + if (distribution !== "binary") { + return "npm"; + } + const execPath = process.execPath.toLowerCase(); + if (execPath.includes("/homebrew/") || execPath.includes("/cellar/")) { + return "brew"; + } + return "binary"; +} + +function getUpgradeInstruction(method: InstallMethod): string { + switch (method) { + case "npm": + return "Run: npm install -g base44@latest"; + case "brew": + return "Run: brew upgrade base44"; + case "binary": + return "Download Base44 CLI from: https://github.com/base44/cli/releases/latest"; + } +} export function startUpgradeCheck(): Promise { return checkForUpgrade().catch(() => null); } -function formatUpgradeMessage(info: UpgradeInfo): string { +function formatUpgradeMessage( + info: UpgradeInfo, + distribution: Distribution, +): string { const { shinyOrange } = theme.colors; const { bold } = theme.styles; + const instruction = getUpgradeInstruction(detectInstallMethod(distribution)); return [ shinyOrange( `Update available! ${info.currentVersion} → ${bold(info.latestVersion)}`, ), - shinyOrange(`Run: ${bold(UPGRADE_COMMAND)}`), + shinyOrange(instruction), ].join("\n"); } export async function printUpgradeNotification( upgradeCheckPromise: Promise, + distribution: Distribution, ): Promise { try { const upgradeInfo = await upgradeCheckPromise; if (upgradeInfo) { - box(formatUpgradeMessage(upgradeInfo)); + note(formatUpgradeMessage(upgradeInfo, distribution)); } } catch {} } From 9884c4725f82d7afd2a7e1e3f2f710b5178338a2 Mon Sep 17 00:00:00 2001 From: Kfir Strikovsky Date: Wed, 11 Mar 2026 19:13:37 +0200 Subject: [PATCH 12/12] fix test --- packages/cli/tests/cli/version-check.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cli/tests/cli/version-check.spec.ts b/packages/cli/tests/cli/version-check.spec.ts index 66cb8708..e9f826f5 100644 --- a/packages/cli/tests/cli/version-check.spec.ts +++ b/packages/cli/tests/cli/version-check.spec.ts @@ -13,7 +13,6 @@ describe("upgrade notification", () => { t.expectResult(result).toSucceed(); t.expectResult(result).toContain("Update available!"); t.expectResult(result).toContain("1.0.0"); - t.expectResult(result).toContain("npm install -g base44@latest"); }); it("does not display notification when version is current", async () => {