From ec8132d7669ec6d68fc1d6e5a531893d91d9befa Mon Sep 17 00:00:00 2001 From: amianthus <49116958+SirTenzin@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:29:13 -0800 Subject: [PATCH] feat: atmn precheck with interactive install prompt Add package.json precheck before CLI runs. If atmn is not installed and the terminal is interactive, display a React Ink prompt allowing users to choose between npm, pnpm, or bun for installation. This prevents type errors in autumn.config.ts from missing atmn package. Co-Authored-By: Claude Haiku 4.5 --- atmn/src/cli.tsx | 13 ++- atmn/src/lib/precheck.ts | 23 +++++ .../src/views/react/install/InstallPrompt.tsx | 95 +++++++++++++++++++ 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 atmn/src/lib/precheck.ts create mode 100644 atmn/src/views/react/install/InstallPrompt.tsx diff --git a/atmn/src/cli.tsx b/atmn/src/cli.tsx index 47f289a..2da3b7f 100644 --- a/atmn/src/cli.tsx +++ b/atmn/src/cli.tsx @@ -10,10 +10,12 @@ import { pull as newPull } from "./commands/pull/pull.js"; // New pull implement import { FRONTEND_URL } from "./constants.js"; import { fetchOrganizationMe } from "./lib/api/endpoints/index.js"; import { isProd, setCliContext } from "./lib/env/cliContext.js"; +import { checkAtmnInstalled } from "./lib/precheck.js"; import { readFromEnv } from "./lib/utils.js"; // Import Ink views import { QueryProvider } from "./views/react/components/providers/QueryProvider.js"; import { InitFlow } from "./views/react/init/InitFlow.js"; +import { InstallPrompt } from "./views/react/install/InstallPrompt.js"; import { PullView } from "./views/react/pull/Pull.js"; import { APP_VERSION } from "./lib/version.js"; @@ -450,4 +452,13 @@ const originalEmit = process.emitWarning as any; return originalEmit(warning, ...args); }; -program.parse(); +async function main() { + if (!checkAtmnInstalled() && process.stdout.isTTY) { + const { waitUntilExit } = render(); + await waitUntilExit(); + } + + program.parse(); +} + +main(); diff --git a/atmn/src/lib/precheck.ts b/atmn/src/lib/precheck.ts new file mode 100644 index 0000000..26ff270 --- /dev/null +++ b/atmn/src/lib/precheck.ts @@ -0,0 +1,23 @@ +import fs from "fs"; +import path from "path"; + +/** + * Check if `atmn` is listed in the project's package.json + * (dependencies or devDependencies). This ensures autumn.config.ts + * can import from "atmn" without type errors. + */ +export function checkAtmnInstalled(): boolean { + try { + const packageJsonPath = path.join(process.cwd(), "package.json"); + if (!fs.existsSync(packageJsonPath)) { + return false; + } + + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")); + return !!( + packageJson.dependencies?.atmn || packageJson.devDependencies?.atmn + ); + } catch { + return false; + } +} diff --git a/atmn/src/views/react/install/InstallPrompt.tsx b/atmn/src/views/react/install/InstallPrompt.tsx new file mode 100644 index 0000000..b772ad2 --- /dev/null +++ b/atmn/src/views/react/install/InstallPrompt.tsx @@ -0,0 +1,95 @@ +import { execSync } from "child_process"; +import { Box, Text, useApp } from "ink"; +import { useCallback, useState } from "react"; +import { + SelectMenu, + type SelectMenuItem, + StatusLine, +} from "../components/index.js"; + +type PackageManager = "npm" | "pnpm" | "bun"; +type InstallState = "choosing" | "installing" | "success" | "skipped"; + +export function InstallPrompt() { + const { exit } = useApp(); + const [state, setState] = useState("choosing"); + const [error, setError] = useState(null); + + const items: SelectMenuItem[] = [ + { label: "npm", value: "npm" }, + { label: "pnpm", value: "pnpm" }, + { label: "bun", value: "bun" }, + ]; + + const handleSelect = useCallback( + (item: SelectMenuItem) => { + setState("installing"); + + const commands: Record = { + npm: "npm install atmn", + pnpm: "pnpm add atmn", + bun: "bun add atmn", + }; + + try { + execSync(commands[item.value], { stdio: "inherit" }); + setState("success"); + setTimeout(() => exit(), 500); + } catch (err) { + setError( + err instanceof Error ? err.message : "Installation failed", + ); + } + }, + [exit], + ); + + if (error) { + return ( + + + + Install manually: npm install atmn / pnpm add atmn / bun add atmn + + + ); + } + + return ( + + + + ! The{" "} + + atmn + {" "} + package is not in your dependencies. + + + + It's needed so autumn.config.ts can import types and builders. + + + {state === "choosing" && ( + + Install with: + + + + + )} + + {state === "installing" && ( + + + + )} + + {state === "success" && ( + + + + )} + + ); +}