-
Notifications
You must be signed in to change notification settings - Fork 32
feat: atmn precheck with interactive install prompt #65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/atmn-v2.1
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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(<InstallPrompt />); | ||
| await waitUntilExit(); | ||
| } | ||
|
|
||
| program.parse(); | ||
| } | ||
|
Comment on lines
+455
to
+462
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Precheck runs before flags
Prompt To Fix With AIThis is a comment left during a code review.
Path: atmn/src/cli.tsx
Line: 455:462
Comment:
**Precheck runs before flags**
`main()` runs the interactive install precheck before `program.parse()`, so global flags like `--headless` (which you set in the `preAction` hook) are not applied yet. This means `atmn --headless ...` will still see `process.stdout.isTTY` as true and can trigger the Ink `InstallPrompt`, breaking CI/agent usage where `--headless` is expected to suppress interactivity.
How can I resolve this? If you propose a fix, please make it concise. |
||
|
|
||
| main(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<InstallState>("choosing"); | ||
| const [error, setError] = useState<string | null>(null); | ||
|
|
||
| const items: SelectMenuItem<PackageManager>[] = [ | ||
| { label: "npm", value: "npm" }, | ||
| { label: "pnpm", value: "pnpm" }, | ||
| { label: "bun", value: "bun" }, | ||
| ]; | ||
|
|
||
| const handleSelect = useCallback( | ||
| (item: SelectMenuItem<PackageManager>) => { | ||
| setState("installing"); | ||
|
|
||
| const commands: Record<PackageManager, string> = { | ||
| 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) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: The error handler sets state but never calls Prompt for AI agents |
||
| setError( | ||
| err instanceof Error ? err.message : "Installation failed", | ||
| ); | ||
| } | ||
|
Comment on lines
+34
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CLI can hang on failure When Prompt To Fix With AIThis is a comment left during a code review.
Path: atmn/src/views/react/install/InstallPrompt.tsx
Line: 34:42
Comment:
**CLI can hang on failure**
When `execSync(...)` throws, you set `error` but never call `exit()`, so the Ink renderer in `cli.tsx` will wait indefinitely on `waitUntilExit()` with no way to continue unless the user manually terminates the process. The error UI renders, but the process doesn’t exit or return control to `program.parse()`.
How can I resolve this? If you propose a fix, please make it concise. |
||
| }, | ||
| [exit], | ||
| ); | ||
|
|
||
| if (error) { | ||
| return ( | ||
| <Box flexDirection="column" paddingLeft={1}> | ||
| <StatusLine status="error" message={`Failed to install atmn: ${error}`} /> | ||
| <Text dimColor> | ||
| Install manually: npm install atmn / pnpm add atmn / bun add atmn | ||
| </Text> | ||
| </Box> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <Box flexDirection="column" paddingLeft={1}> | ||
| <Box marginBottom={1}> | ||
| <Text> | ||
| <Text color="yellow">!</Text> The{" "} | ||
| <Text color="magenta" bold> | ||
| atmn | ||
| </Text>{" "} | ||
| package is not in your dependencies. | ||
| </Text> | ||
| </Box> | ||
| <Text dimColor> | ||
| It's needed so autumn.config.ts can import types and builders. | ||
| </Text> | ||
|
|
||
| {state === "choosing" && ( | ||
| <Box flexDirection="column" marginTop={1}> | ||
| <Text>Install with:</Text> | ||
| <Box marginTop={1}> | ||
| <SelectMenu items={items} onSelect={handleSelect} /> | ||
| </Box> | ||
| </Box> | ||
| )} | ||
|
|
||
| {state === "installing" && ( | ||
| <Box marginTop={1}> | ||
| <StatusLine status="loading" message="Installing atmn..." /> | ||
| </Box> | ||
| )} | ||
|
|
||
| {state === "success" && ( | ||
| <Box marginTop={1}> | ||
| <StatusLine status="success" message="atmn installed" /> | ||
| </Box> | ||
| )} | ||
| </Box> | ||
| ); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: The interactive precheck runs before
program.parse(), so CLI flags like--headlesshaven't been processed yet. This breaks CI/agent workflows where--headlessis expected to suppress interactivity. Consider parsing a minimal flag check first, or checking for--headlessinprocess.argvbefore the precheck.Prompt for AI agents