UNPKG

gensx

Version:
270 lines 13.7 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { mkdir, readdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; import process from "node:process"; import { fileURLToPath } from "node:url"; import { Box, Text, useApp } from "ink"; import SelectInput from "ink-select-input"; import Spinner from "ink-spinner"; import TextInput from "ink-text-input"; import { useCallback, useEffect, useState } from "react"; import { ErrorMessage } from "../components/ErrorMessage.js"; import { LoadingSpinner } from "../components/LoadingSpinner.js"; import { readConfig } from "../utils/config.js"; import { exec } from "../utils/exec.js"; import { saveProjectConfig } from "../utils/project-config.js"; import { login } from "./login.js"; const TEMPLATE_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), process.env.DENO_BINARY ? "src/templates/projects" : "../templates/projects"); export function NewProjectUI({ projectPath, options }) { const { exit } = useApp(); const [phase, setPhase] = useState("initial"); const [error, setError] = useState(null); const [description, setDescription] = useState(""); const [_selectedAssistants, setSelectedAssistants] = useState([]); const [hasCopiedFiles, setHasCopiedFiles] = useState(false); const [hasInstalledDeps, setHasInstalledDeps] = useState(false); const [isInstallingAssistants, setIsInstallingAssistants] = useState(false); const handleError = useCallback((err) => { const message = err instanceof Error ? err.message : String(err); setError(message); setPhase("error"); setTimeout(() => { exit(); }, 100); }, [exit]); const handleDescriptionSubmit = useCallback((value) => { try { const trimmed = value.trim(); setDescription(trimmed || "My GenSX Project"); setPhase("copyFiles"); } catch (err) { handleError(err); } }, [handleError]); const handleAssistantSelect = useCallback((assistants) => { setSelectedAssistants(assistants); setIsInstallingAssistants(true); // Filter out "all" and "none" from the assistants array const packagesToInstall = assistants.filter((pkg) => pkg !== "all" && pkg !== "none"); // Run each assistant installation command using npx const installPromises = packagesToInstall.map((assistantPackage) => exec(`npx ${assistantPackage}`, { cwd: projectPath }).catch(handleError)); Promise.all(installPromises) .then(() => { setIsInstallingAssistants(false); setPhase("done"); }) .catch(handleError); }, [handleError, projectPath]); useEffect(() => { async function initialize() { try { // Check if user has completed first-time setup const { state } = await readConfig(); if (!state.hasCompletedFirstTimeSetup && !options.skipLogin) { setPhase("login"); return; } // If description is provided in options, skip the createProject phase if (options.description) { setDescription(options.description); setPhase("copyFiles"); } else { setPhase("createProject"); } } catch (err) { handleError(err); } } void initialize(); }, [handleError, options.skipLogin, options.description]); useEffect(() => { async function copyFiles() { if (phase === "copyFiles" && !hasCopiedFiles) { try { await copyTemplateFiles("ts", projectPath); // Save gensx.yaml config const projectName = path.basename(projectPath); await saveProjectConfig({ projectName, description: description || "My GenSX Project", }, projectPath); setHasCopiedFiles(true); setPhase("installDeps"); } catch (err) { handleError(err); } } } void copyFiles(); }, [phase, projectPath, handleError, hasCopiedFiles, description]); useEffect(() => { async function installDependencies() { if (phase === "installDeps" && !hasInstalledDeps) { try { const template = await loadTemplate("ts"); if (template.dependencies.length > 0) { await exec(`npm install ${template.dependencies.join(" ")}`, { cwd: projectPath, }); } if (template.devDependencies.length > 0) { await exec(`npm install -D ${template.devDependencies.join(" ")}`, { cwd: projectPath, }); } setHasInstalledDeps(true); if (options.skipIdeRules) { setPhase("done"); setTimeout(() => { exit(); }, 100); } else { setPhase("selectAssistants"); } } catch (err) { handleError(err); setTimeout(() => { exit(); }, 100); } } } void installDependencies(); }, [phase, handleError, hasInstalledDeps, options.skipIdeRules]); if (error) { return _jsx(ErrorMessage, { message: error }); } if (phase === "login") { return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: _jsx(Text, { color: "yellow", children: "Welcome to GenSX! Let's get you set up first." }) }), _jsx(LoginUI, { onComplete: () => { setPhase("createProject"); } })] })); } if (phase === "createProject") { return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "\u279C" }), " Enter a project description (or press enter to skip):"] }), _jsx(TextInput, { value: description, onChange: setDescription, onSubmit: handleDescriptionSubmit })] })); } if (phase === "copyFiles") { return _jsx(LoadingSpinner, { message: "Creating project..." }); } if (phase === "installDeps") { return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "green", bold: true, children: "\u2714" }), " ", "Created project"] }), _jsx(LoadingSpinner, { message: "Installing dependencies..." })] })); } if (phase === "selectAssistants") { return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "green", bold: true, children: "\u2714" }), " ", "Created project"] }), _jsxs(Text, { children: [_jsx(Text, { color: "green", bold: true, children: "\u2714" }), " ", "Installed dependencies"] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "blue", children: "\u279C" }), " Select AI assistants to integrate:"] }), isInstallingAssistants ? (_jsx(LoadingSpinner, { message: "Installing AI assistant integrations..." })) : (_jsx(AiAssistantSelector, { onSelect: handleAssistantSelect }))] })] })); } if (phase === "done") { return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "green", bold: true, children: "\u2714" }), " ", "Created project"] }), _jsxs(Text, { children: [_jsx(Text, { color: "green", bold: true, children: "\u2714" }), " ", "Installed dependencies"] }), _jsxs(Text, { children: [_jsx(Text, { color: "green", bold: true, children: "\u2714" }), " ", "Installed ", _selectedAssistants.length, " AI assistant integration", _selectedAssistants.length !== 1 ? "s" : ""] }), _jsx(Box, { children: _jsxs(Text, { children: [_jsx(Text, { color: "green", bold: true, children: "\u2714" }), " ", "Successfully created GenSX project in", " ", _jsx(Text, { color: "cyan", children: projectPath })] }) }), _jsxs(Box, { marginTop: 2, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "NEXT STEPS:" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "1. Open the project directory:" }), projectPath !== "." && (_jsxs(Text, { children: [" ", _jsxs(Text, { color: "cyan", children: ["cd ", projectPath] })] }))] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "2. Choose what you want to do:" }), _jsxs(Box, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "DEPLOY THE PROJECT" }), _jsx(Text, { children: _jsx(Text, { color: "cyan", children: "OPENAI_API_KEY=your_api_key npm run deploy" }) })] }), _jsxs(Box, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "RUN LOCALLY" }), _jsx(Text, { children: _jsx(Text, { color: "cyan", children: "OPENAI_API_KEY=your_api_key npm run dev" }) })] }), _jsxs(Box, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "START API SERVER" }), _jsx(Text, { children: _jsx(Text, { color: "cyan", children: "OPENAI_API_KEY=your_api_key npm run start" }) })] })] }), _jsx(Box, { marginTop: 2, children: _jsxs(Text, { children: ["Open ", _jsx(Text, { color: "cyan", children: "src/workflows.tsx" }), " to start building your workflows."] }) })] })] })); } return null; } function LoginUI({ onComplete }) { const { skipped } = login(); useEffect(() => { if (skipped) { onComplete(); } }, [skipped, onComplete]); return (_jsx(Box, { children: _jsxs(Text, { children: [_jsx(Spinner, {}), " Logging in..."] }) })); } function AiAssistantSelector({ onSelect }) { const aiAssistantOptions = [ { name: "claude", value: "@gensx/claude-md", message: "Claude AI", hint: "Adds CLAUDE.md for Anthropic Claude integration", }, { name: "cursor", value: "@gensx/cursor-rules", message: "Cursor", hint: "Adds Cursor IDE integration rules", }, { name: "cline", value: "@gensx/cline-rules", message: "Cline", hint: "Adds Cline VS Code extension integration rules", }, { name: "windsurf", value: "@gensx/windsurf-rules", message: "Windsurf", hint: "Adds Windsurf Cascade AI integration rules", }, { name: "all", value: "all", message: "All", hint: "Adds all AI assistants", }, { name: "none", value: "none", message: "None", hint: "No AI assistants will be installed", }, ]; const items = aiAssistantOptions.map((option) => ({ label: `${option.message} (${option.hint})`, value: option.value, })); return (_jsx(SelectInput, { items: items, onSelect: (item) => { const selection = item.value; if (selection === "none") { onSelect([]); } else if (selection === "all") { // For "all", get all the actual package values const allPackages = aiAssistantOptions .filter((opt) => opt.value !== "all" && opt.value !== "none") .map((opt) => opt.value); onSelect(allPackages); } else { onSelect([selection]); } } })); } async function loadTemplate(_templateName) { const templatePath = path.join(TEMPLATE_DIR, "typescript"); const templateConfigPath = path.join(templatePath, "template.json"); try { const configContent = await readFile(templateConfigPath, "utf-8"); const template = JSON.parse(configContent); return template; } catch { throw new Error(`Template "typescript" not found or invalid.`); } } async function copyTemplateFiles(_templateName, targetPath) { const templatePath = path.join(TEMPLATE_DIR, "typescript"); async function copyDir(currentPath, targetBase) { const entries = await readdir(currentPath, { withFileTypes: true }); for (const entry of entries) { const sourcePath = path.join(currentPath, entry.name); const targetFilePath = path .join(targetBase, path.relative(templatePath, sourcePath)) .replace(/\.template$/, ""); if (entry.name === "template.json") continue; if (entry.isDirectory()) { await mkdir(targetFilePath, { recursive: true }); await copyDir(sourcePath, targetBase); } else { const content = await readFile(sourcePath, "utf-8"); await mkdir(path.dirname(targetFilePath), { recursive: true }); await writeFile(targetFilePath, content); } } } await copyDir(templatePath, targetPath); } //# sourceMappingURL=new.js.map