Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/cli/utils/runCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { isCLIError } from "@/core/errors.js";
import { login } from "@/cli/commands/auth/login-flow.js";
import { printBanner } from "@/cli/utils/banner.js";
import { theme } from "@/cli/utils/theme.js";
import { printUpgradeNotificationIfAvailable } from "@/cli/utils/upgradeNotification.js";

export interface RunCommandOptions {
/**
Expand Down Expand Up @@ -73,6 +74,8 @@ export async function runCommand(
intro(theme.colors.base44OrangeBackground(" Base 44 "));
}

await printUpgradeNotificationIfAvailable();

try {
// Check authentication if required
if (options?.requireAuth) {
Expand Down
25 changes: 25 additions & 0 deletions src/cli/utils/upgradeNotification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { log } from "@clack/prompts";
import type { UpgradeInfo } from "@/cli/utils/version-check.js";
import { checkForUpgrade } from "@/cli/utils/version-check.js";
import { theme } from "@/cli/utils/theme.js";

function formatUpgradeMessage(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 update -g base44"))}`;
}

/**
* Checks for available upgrades and prints a notification if one exists.
*/
export async function printUpgradeNotificationIfAvailable(): Promise<void> {
try {
const upgradeInfo = await checkForUpgrade();
if (upgradeInfo) {
log.message(formatUpgradeMessage(upgradeInfo));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@claude i see were using log.message, and the printUpgradeNotificationIfAvailable is called in runCommand but after we already do outro meaning it will be a weird hevaior, maybe we can do it at the begining?

Copy link

@claude claude bot Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude finished @kfirstri's task in 47s —— View job


Completed

  • Read runCommand.ts to understand current placement
  • Read upgradeNotification.ts to understand implementation
  • Move notification call to beginning of command
  • Commit and push changes

Moved the upgrade notification to display right after the intro/banner, before the main command logic runs. This prevents the notification from appearing after the outro, which would have created strange behavior where messages appear after the command completion message.

The notification now appears at the beginning of the command flow, making it more visible and avoiding any confusion with the outro message.


| Branch

}
} catch {
// Silently ignore errors
}
}
25 changes: 25 additions & 0 deletions src/cli/utils/version-check.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@claude this whole file should be inside the src/cli folder because it's not related to the core/ of the product

Copy link

@claude claude bot Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude finished @kfirstri's task in 1m 34s —— View job


Completed

  • Read current file structure and imports
  • Move src/core/utils/version-check.ts to src/cli/utils/version-check.ts
  • Update import path in upgradeNotification.ts
  • Move test file to tests/cli/version-check.spec.ts
  • Commit and push changes

Moved version-check.ts from src/core/utils to src/cli/utils and updated all related imports. The test file has also been moved from tests/core to tests/cli to match the new structure.

--- | Branch

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { execa } from "execa";
import packageJson from "../../../package.json";

export interface UpgradeInfo {
currentVersion: string;
latestVersion: string;
}

/**
* Checks if a newer version of the CLI is available.
*/
export async function checkForUpgrade(): Promise<UpgradeInfo | null> {
try {
const { stdout } = await execa("npm", ["view", "base44", "version"], { timeout: 5000 });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

look for other places we use execa and make sure we pass all appropriate flags @claude

Copy link

@claude claude bot Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude finished @kfirstri's task in 1m 12s —— View job


Reviewing execa usage patterns

  • Read current version-check.ts to understand current execa usage
  • Search for all execa usage in the codebase
  • Compare flags and options used across the codebase
  • Update version-check.ts to add shell: true flag
  • Commit changes locally
  • Sync with remote and push changes

Findings

I found that version-check.ts is missing the shell: true flag that is consistently used throughout the codebase:

Previous usage in version-check.ts:14

const { stdout } = await execa("npm", ["view", "base44", "version"], { timeout: 5000 });

Updated to match codebase pattern:

const { stdout } = await execa("npm", ["view", "base44", "version"], {
  timeout: 5000,
  shell: true
});

Why this matters: According to AGENTS.md:729-736, the shell: true flag suppresses subprocess output, which is important for UI consistency. All other execa calls in the codebase (create.ts:206, create.ts:209, create.ts:232) use this flag.

Status

The change has been committed locally (commit: fe03574). I need approval to run git pull --rebase to sync with the remote branch before pushing, as the remote has diverged.


| Branch

const latestVersion = stdout.trim();
const currentVersion = packageJson.version;

if (latestVersion !== currentVersion) {
return { currentVersion, latestVersion };
}
return null;
} catch {
return null;
}
}
60 changes: 60 additions & 0 deletions tests/cli/version-check.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { describe, it, vi, beforeEach } from "vitest";
import { setupCLITests } from "./testkit/index.js";
import type { UpgradeInfo } from "@/cli/utils/version-check.js";

// Mock the version-check module
vi.mock("@/cli/utils/version-check.js", () => ({
checkForUpgrade: vi.fn(),
}));

describe("upgrade notification", () => {
const t = setupCLITests();

beforeEach(async () => {
const { checkForUpgrade } = await import("@/cli/utils/version-check.js");
vi.mocked(checkForUpgrade).mockReset();
});

it("displays upgrade notification when newer version is available", async () => {
const { checkForUpgrade } = await import("@/cli/utils/version-check.js");
const upgradeInfo: UpgradeInfo = {
currentVersion: "0.0.26",
latestVersion: "1.0.0",
};
vi.mocked(checkForUpgrade).mockResolvedValue(upgradeInfo);

await t.givenLoggedIn({ email: "test@example.com", name: "Test User" });

const result = await t.run("whoami");

t.expectResult(result).toSucceed();
t.expectResult(result).toContain("Update available!");
t.expectResult(result).toContain("0.0.26 → 1.0.0");
t.expectResult(result).toContain("npm update -g base44");
});

it("does not display notification when version is current", async () => {
const { checkForUpgrade } = await import("@/cli/utils/version-check.js");
vi.mocked(checkForUpgrade).mockResolvedValue(null);

await t.givenLoggedIn({ email: "test@example.com", name: "Test User" });

const result = await t.run("whoami");

t.expectResult(result).toSucceed();
t.expectResult(result).not.toContain("Update available!");

Check failure on line 45 in tests/cli/version-check.spec.ts

View workflow job for this annotation

GitHub Actions / typecheck

Property 'not' does not exist on type 'CLIResultMatcher'.

Check failure on line 45 in tests/cli/version-check.spec.ts

View workflow job for this annotation

GitHub Actions / test

tests/cli/version-check.spec.ts > upgrade notification > does not display notification when version is current

TypeError: Cannot read properties of undefined (reading 'toContain') ❯ tests/cli/version-check.spec.ts:45:28
});

it("does not display notification when version check fails", async () => {
const { checkForUpgrade } = await import("@/cli/utils/version-check.js");
vi.mocked(checkForUpgrade).mockRejectedValue(new Error("Network error"));

await t.givenLoggedIn({ email: "test@example.com", name: "Test User" });

const result = await t.run("whoami");

// Command still succeeds (upgrade check doesn't block)
t.expectResult(result).toSucceed();
t.expectResult(result).not.toContain("Update available!");

Check failure on line 58 in tests/cli/version-check.spec.ts

View workflow job for this annotation

GitHub Actions / typecheck

Property 'not' does not exist on type 'CLIResultMatcher'.

Check failure on line 58 in tests/cli/version-check.spec.ts

View workflow job for this annotation

GitHub Actions / test

tests/cli/version-check.spec.ts > upgrade notification > does not display notification when version check fails

TypeError: Cannot read properties of undefined (reading 'toContain') ❯ tests/cli/version-check.spec.ts:58:28
});
});
Loading