Browse Source

feat: merge cli (#770)

feat: create app with project name
Yiwei Mao 4 months ago
parent
commit
39ea672eda

+ 79 - 0
apps/cli/.eslintrc.cjs

@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+module.exports = {
+  parser: "@typescript-eslint/parser",
+  parserOptions: {
+    requireConfigFile: false,
+    babelOptions: {
+      babelrc: false,
+      configFile: false,
+      cwd: __dirname,
+    },
+  },
+  ignorePatterns: [
+    '**/*.d.ts',
+    '**/__mocks__',
+    '**/node_modules',
+    '**/build',
+    '**/dist',
+    '**/es',
+    '**/lib',
+    '**/.codebase',
+    '**/.changeset',
+    '**/config',
+    '**/common/scripts',
+    '**/output',
+    'error-log-str.js',
+    '*.bundle.js',
+    '*.min.js',
+    '*.js.map',
+    '**/output',
+    '**/*.log',
+    '**/tsconfig.tsbuildinfo',
+    '**/vitest.config.ts',
+    'package.json',
+    '*.json',
+  ],
+  rules: {
+    'no-console': 'off',
+    'react/no-deprecated': 'off',
+    'import/prefer-default-export': 'off',
+    'lines-between-class-members': 'warn',
+    'react/jsx-no-useless-fragment': 'off',
+    'no-unused-vars': 'off',
+    'no-redeclare': 'off',
+    'no-empty-fuNction': 'off',
+    'prefer-destructurin': 'off',
+    'no-underscore-dangle': 'off',
+    'no-empty-function': 'off',
+    'no-multi-assign': 'off',
+    'arrow-body-style': 'warn',
+    'no-useless-constructor': 'off',
+    'no-param-reassign': 'off',
+    'max-classes-per-file': 'off',
+    'grouped-accessor-pairs': 'off',
+    'no-plusplus': 'off',
+    'no-restricted-syntax': 'off',
+    'react/destructuring-assignment': 'off',
+    'import/extensions': 'off',
+    'consistent-return': 'off',
+    'jsx-a11y/no-static-element-interactions': 'off',
+    'no-use-before-define': 'off',
+    'no-bitwise': 'off',
+    'no-case-declarations': 'off',
+    'react/no-array-index-key': 'off',
+    'react/require-default-props': 'off',
+    'no-dupe-class-members': 'off',
+    'react/jsx-props-no-spreading': 'off',
+    'no-console': 'off',
+    'no-shadow': 'off',
+    'class-methods-use-this': 'off',
+    'default-param-last': 'off',
+    'no-unused-vars': 'off',
+    'import/prefer-default-export': 'off',
+    'import/extensions': 'off',
+  },
+}

+ 1 - 0
apps/cli/.gitignore

@@ -0,0 +1 @@
+.download

+ 2 - 0
apps/cli/bin/index.js

@@ -0,0 +1,2 @@
+#!/usr/bin/env node
+import '../dist/index.js';

+ 40 - 0
apps/cli/package.json

@@ -0,0 +1,40 @@
+{
+  "name": "@flowgram.ai/cli",
+  "version": "0.1.8",
+  "description": "A CLI tool to create demo projects or sync materials",
+  "bin": {
+    "flowgram-cli": "./bin/index.js"
+  },
+  "type": "module",
+  "files": [
+    "bin",
+    "src",
+    "dist"
+  ],
+  "scripts": {
+    "build": "tsup src/index.ts --format esm,cjs --dts --out-dir dist",
+    "start": "node bin/index.js"
+  },
+  "dependencies": {
+    "fs-extra": "^9.1.0",
+    "commander": "^11.0.0",
+    "chalk": "^5.3.0",
+    "download": "8.0.0",
+    "tar": "7.4.3",
+    "inquirer": "^9.2.7"
+  },
+  "devDependencies": {
+    "@types/download": "8.0.5",
+    "@types/fs-extra": "11.0.4",
+    "@types/node": "^18",
+    "@types/inquirer": "9.0.7",
+    "tsup": "^8.0.1",
+    "eslint": "^8.54.0",
+    "@typescript-eslint/parser": "^6.10.0",
+    "typescript": "^5.8.3"
+  },
+  "publishConfig": {
+    "access": "public",
+    "registry": "https://registry.npmjs.org/"
+  }
+}

+ 130 - 0
apps/cli/src/create-app/index.ts

@@ -0,0 +1,130 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import path from 'path';
+
+import { execSync } from 'child_process';
+import inquirer from 'inquirer';
+import fs from 'fs-extra';
+import chalk from 'chalk';
+import download from 'download';
+import * as tar from 'tar';
+
+const args = process.argv.slice(2);
+
+const updateFlowGramVersions = (dependencies: any[], latestVersion: string) => {
+  for(const packageName in dependencies) {
+    if (packageName.startsWith('@flowgram.ai')) {
+      dependencies[packageName] = latestVersion
+    }
+  }
+}
+
+export const createApp = async (projectName?: string) => {
+  console.log(chalk.green('Welcome to @flowgram.ai/create-app CLI!'));
+  const latest = execSync('npm view @flowgram.ai/demo-fixed-layout version --tag=latest latest').toString().trim();
+
+  let folderName = ''
+
+  if (!projectName) {
+    // 询问用户选择 demo 项目
+    const { repo } = await inquirer.prompt([
+      {
+        type: 'list',
+        name: 'repo',
+        message: 'Select a demo to create:',
+        choices: [
+          { name: 'Fixed Layout Demo', value: 'demo-fixed-layout' },
+          { name: 'Free Layout Demo', value: 'demo-free-layout' },
+          { name: 'Fixed Layout Demo Simple', value: 'demo-fixed-layout-simple' },
+          { name: 'Free Layout Demo Simple', value: 'demo-free-layout-simple' },
+          { name: 'Free Layout Nextjs Demo', value: 'demo-nextjs' },
+          { name: 'Free Layout Vite Demo Simple', value: 'demo-vite' },
+          { name: 'Demo Playground for infinite canvas', value: 'demo-playground' }
+        ],
+      },
+    ]);
+
+    folderName = repo;
+  } else {
+    if (['fixed-layout', 'free-layout', 'fixed-layout-simple', 'free-layout-simple', 'playground', 'nextjs'].includes(projectName)) {
+      folderName = `demo-${projectName}`;
+    } else {
+      console.error('Invalid projectName. Please run "npx create-app" to choose demo.');
+      return;
+    }
+  }
+
+  try {
+    const targetDir = path.join(process.cwd());
+    // 下载 npm 包的 tarball
+    const downloadPackage = async () => {
+      try {
+        // 从 npm registry 下载 tarball 文件
+        const tarballBuffer = await download(`https://registry.npmjs.org/@flowgram.ai/${folderName}/-/${folderName}-${latest}.tgz`);
+
+        // 确保目标文件夹存在
+        fs.ensureDirSync(targetDir);
+
+        // 创建一个临时文件名来保存 tarball 数据
+        const tempTarballPath = path.join(process.cwd(), `${folderName}.tgz`);
+
+        // 将下载的 tarball 写入临时文件
+        fs.writeFileSync(tempTarballPath, tarballBuffer);
+
+        // 解压 tarball 文件到目标文件夹
+        await tar.x({
+          file: tempTarballPath,
+          C: targetDir,
+        });
+
+        fs.renameSync(path.join(targetDir, 'package'), path.join(targetDir, folderName))
+
+        // 删除下载的 tarball 文件
+        fs.unlinkSync(tempTarballPath);
+        return true;
+
+      } catch (error) {
+        console.error(`Error downloading or extracting package: ${error}`);
+        return false;
+      }
+    };
+    const res = await downloadPackage();
+
+    // 下载完成后,执行操作,替换 package.json 文件内部的所有 @flowgram.ai 包版本为 latest
+    const pkgJsonPath = path.join(targetDir, folderName, 'package.json');
+    const data = fs.readFileSync(pkgJsonPath, 'utf-8');
+
+    const packageLatestVersion = execSync('npm view @flowgram.ai/core version --tag=latest latest').toString().trim();
+
+    const jsonData = JSON.parse(data);
+    if (jsonData.dependencies) {
+      updateFlowGramVersions(jsonData.dependencies, packageLatestVersion);
+    }
+
+    if (jsonData.devDependencies) {
+      updateFlowGramVersions(jsonData.devDependencies, packageLatestVersion);
+    }
+
+    // 修改完成后写入
+    fs.writeFileSync(pkgJsonPath, JSON.stringify(jsonData, null, 2), 'utf-8');
+
+    if (res) {
+      // 克隆项目
+      console.log(chalk.green(`${folderName} Demo project created successfully!`));
+
+      console.log(chalk.yellow('Run the following commands to start:'));
+      console.log(chalk.cyan(`  cd ${folderName}`));
+      console.log(chalk.cyan('  npm install'));
+      console.log(chalk.cyan('  npm start'));
+    } else {
+      console.log(chalk.red('Download failed'))
+    }
+
+  } catch (error) {
+    console.error('Error downloading repo:', error);
+    return;
+  }
+}

+ 31 - 0
apps/cli/src/index.ts

@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { Command } from "commander";
+
+import { syncMaterial } from "./materials";
+import { createApp } from "./create-app";
+
+const program = new Command();
+
+program.name("flowgram-cli").version("1.0.0").description("Flowgram CLI");
+
+program
+  .command("create-app")
+  .description("Create a new flowgram project")
+  .argument('[string]', 'Project name')
+  .action(async (projectName) => {
+    await createApp(projectName);
+  });
+
+program
+  .command("materials")
+  .description("Sync materials to the project")
+  .argument('[string]', 'Material name')
+  .action(async (materialName) => {
+    await syncMaterial(materialName);
+  });
+
+program.parse(process.argv);

+ 93 - 0
apps/cli/src/materials/index.ts

@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import inquirer from "inquirer";
+import chalk from "chalk";
+
+import { copyMaterial, listAllMaterials, Material } from "./materials";
+import { loadNpm } from "../utils/npm";
+import path from "path";
+import { Project } from "../utils/project";
+
+export async function syncMaterial(materialName?: string) {
+  // materialName can be undefined
+  console.log(chalk.bgGreenBright("Welcome to @flowgram.ai form-materials!"));
+
+  const project = await Project.getSingleton();
+  project.printInfo();
+
+  if (!project.flowgramVersion) {
+    throw new Error(
+      "Please install @flowgram.ai/fixed-layout-editor or @flowgram.ai/free-layout-editor",
+    );
+  }
+
+  const formMaterialPath = await loadNpm("@flowgram.ai/form-materials");
+  const formMaterialSrc = path.join(formMaterialPath, "src");
+
+  const materials: Material[] = listAllMaterials(formMaterialSrc);
+
+  let material: Material | undefined; // material can be undefined
+
+  // Check if materialName is provided and exists in materials
+  if (materialName) {
+    const selectedMaterial = materials.find(
+      (m) => `${m.type}/${m.name}` === materialName,
+    );
+    if (selectedMaterial) {
+      material = selectedMaterial;
+      console.log(chalk.green(`Using material: ${materialName}`));
+    } else {
+      console.log(
+        chalk.yellow(
+          `Material "${materialName}" not found. Please select from the list:`,
+        ),
+      );
+    }
+  }
+
+  // If material not found or materialName not provided, prompt user to select
+  if (!material) {
+    // User select one component
+    const result = await inquirer.prompt<{
+      material: Material; // Specify type for prompt result
+    }>([
+      {
+        type: "list",
+        name: "material",
+        message: "Select one material to add:",
+        choices: [
+          ...materials.map((_material) => ({
+            name: `${_material.type}/${_material.name}`,
+            value: _material,
+          })),
+        ],
+      },
+    ]);
+    material = result.material;
+  }
+  // Ensure material is defined before proceeding
+  if (!material) {
+    console.error(chalk.red("No material selected. Exiting."));
+    process.exit(1);
+  }
+
+  // 4. Copy the materials to the project
+  console.log(
+    chalk.bold("The following materials will be added to your project"),
+  );
+  console.log(material);
+  let { packagesToInstall } = copyMaterial(material, project, formMaterialPath);
+
+  // 4. Install the dependencies
+  await project.addDependencies(packagesToInstall);
+  console.log(
+    chalk.bold("These npm dependencies is added to your package.json"),
+  );
+  packagesToInstall.forEach((_package) => {
+    console.log(`- ${_package}`);
+  })
+  console.log(chalk.bold("\nPlease run npm install to install dependencies"));
+}

+ 46 - 46
packages/materials/form-materials/bin/materials.ts → apps/cli/src/materials/materials.ts

@@ -3,13 +3,13 @@
  * SPDX-License-Identifier: MIT
  */
 
-import { fileURLToPath } from 'url';
-import path from 'path';
-import fs from 'fs';
+import { fileURLToPath } from "url";
+import path from "path";
+import fs from "fs";
 
-import { traverseRecursiveFiles } from './utils/traverse-file';
-import { replaceImport, traverseFileImports } from './utils/import';
-import { ProjectInfo } from './project'; // Import ProjectInfo
+import { traverseRecursiveFiles } from "../utils/traverse-file";
+import { replaceImport, traverseFileImports } from "../utils/import";
+import { Project } from "../utils/project"; // Import ProjectInfo
 
 const __filename = fileURLToPath(import.meta.url);
 const __dirname = path.dirname(__filename);
@@ -23,28 +23,28 @@ export interface Material {
 }
 
 const _types: string[] = [
-  'components',
-  'effects',
-  'plugins',
-  'shared',
-  'typings',
-  'validate',
-  'form-plugins',
-  'hooks',
+  "components",
+  "effects",
+  "plugins",
+  "shared",
+  "typings",
+  "validate",
+  "form-plugins",
+  "hooks",
 ];
 
-export function listAllMaterials(): Material[] {
+export function listAllMaterials(formMaterialSrc: string): Material[] {
   const _materials: Material[] = [];
 
   for (const _type of _types) {
     // 在 Node.js 中,import.meta.dirname 不可用,可使用 import.meta.url 结合 url 模块来获取目录路径
 
-    const materialsPath: string = path.join(__dirname, '..', 'src', _type);
+    const materialsPath: string = path.join(formMaterialSrc, _type);
     _materials.push(
       ...fs
         .readdirSync(materialsPath)
         .map((_path: string) => {
-          if (_path === 'index.ts') {
+          if (_path === "index.ts") {
             return null;
           }
 
@@ -54,34 +54,37 @@ export function listAllMaterials(): Material[] {
             path: path.join(materialsPath, _path),
           } as Material;
         })
-        .filter((material): material is Material => material !== null)
+        .filter((material): material is Material => material !== null),
     );
   }
 
   return _materials;
 }
 
-export const getFormMaterialDependencies = (): Record<string, string> => {
-  const packageJsonPath: string = path.join(__dirname, '..', 'package.json');
-  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
+export const getFormMaterialDependencies = (
+  formMaterialPath: string,
+): Record<string, string> => {
+  const packageJsonPath: string = path.join(formMaterialPath, "package.json");
+  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
 
   return packageJson.dependencies;
 };
 
 export const copyMaterial = (
   material: Material,
-  projectInfo: ProjectInfo
+  project: Project,
+  formMaterialPath: string,
 ): {
   packagesToInstall: string[];
 } => {
-  const formMaterialDependencies = getFormMaterialDependencies();
+  const formMaterialDependencies = getFormMaterialDependencies(formMaterialPath);
 
   const sourceDir: string = material.path;
   const materialRoot: string = path.join(
-    projectInfo.projectPath,
-    'src',
-    'form-materials',
-    `${material.type}`
+    project.projectPath,
+    "src",
+    "form-materials",
+    `${material.type}`,
   );
   const targetDir = path.join(materialRoot, material.name);
   const packagesToInstall: Set<string> = new Set();
@@ -89,37 +92,34 @@ export const copyMaterial = (
   fs.cpSync(sourceDir, targetDir, { recursive: true });
 
   for (const file of traverseRecursiveFiles(targetDir)) {
-    if (['.ts', '.tsx'].includes(file.suffix)) {
+    if ([".ts", ".tsx"].includes(file.suffix)) {
       for (const importDeclaration of traverseFileImports(file.content)) {
         const { source } = importDeclaration;
 
-        if (source.startsWith('@/')) {
+        if (source.startsWith("@/")) {
           // is inner import
-          console.log(`Replace Import from ${source} to @flowgram.ai/form-materials`);
+          console.log(
+            `Replace Import from ${source} to @flowgram.ai/form-materials`,
+          );
           file.replace((content) =>
             replaceImport(content, importDeclaration, [
-              { ...importDeclaration, source: '@flowgram.ai/form-materials' },
-            ])
+              { ...importDeclaration, source: "@flowgram.ai/form-materials" },
+            ]),
           );
-          if (projectInfo.flowgramVersion !== 'workspace:*') {
-            packagesToInstall.add(`@flowgram.ai/form-materials@${projectInfo.flowgramVersion}`);
-            continue;
-          }
-          packagesToInstall.add('@flowgram.ai/form-materials');
-        } else if (!source.startsWith('.') && !source.startsWith('react')) {
+          packagesToInstall.add(
+            `@flowgram.ai/form-materials@${project.flowgramVersion}`,
+          );
+        } else if (!source.startsWith(".") && !source.startsWith("react")) {
           // check if is in form material dependencies
           const [dep, version] =
-            Object.entries(formMaterialDependencies).find(([_key]) => source.startsWith(_key)) ||
-            [];
+            Object.entries(formMaterialDependencies).find(([_key]) =>
+              source.startsWith(_key),
+            ) || [];
           if (!dep) {
             continue;
           }
-          if (version === 'workspace:*') {
-            if (projectInfo.flowgramVersion !== 'workspace:*') {
-              packagesToInstall.add(`${dep}@${projectInfo.flowgramVersion}`);
-            } else {
-              packagesToInstall.add(dep);
-            }
+          if (dep.startsWith("@flowgram.ai/")) {
+            packagesToInstall.add(`${dep}@${project.flowgramVersion}`);
           } else {
             packagesToInstall.add(`${dep}@${version}`);
           }

+ 0 - 0
packages/materials/form-materials/bin/utils/import.ts → apps/cli/src/utils/import.ts


+ 43 - 0
apps/cli/src/utils/npm.ts

@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { execSync } from 'child_process';
+import { existsSync } from 'fs';
+import path from 'path';
+import download from 'download';
+
+export async function getLatestVersion(packageName: string): Promise<string> {
+  return execSync(`npm view ${packageName} version --tag=latest`)
+    .toString()
+    .trim();
+}
+
+export async function loadNpm(packageName: string): Promise<string> {
+  const packageLatestVersion = await getLatestVersion(packageName);
+
+  const packagePath = path.join(
+    __dirname,
+    `./.download/${packageName}-${packageLatestVersion}`,
+  );
+
+  if (existsSync(packagePath)) {
+    return packagePath;
+  }
+
+  // download else
+  try {
+    const tarballUrl = execSync(
+      `npm view ${packageName}@${packageLatestVersion} dist.tarball`,
+    )
+      .toString()
+      .trim();
+    await download(tarballUrl, packagePath, { extract: true, strip: 1 });
+    return packagePath;
+  } catch (error) {
+    console.error(`Error downloading or extracting package: ${error}`);
+    throw error;
+  }
+}
+

+ 100 - 0
apps/cli/src/utils/project.ts

@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { existsSync, readFileSync, writeFileSync } from "fs";
+import path from "path";
+import { getLatestVersion } from "./npm";
+import chalk from "chalk";
+
+interface PackageJson {
+  dependencies: { [key: string]: string };
+  devDependencies?: { [key: string]: string };
+  peerDependencies?: { [key: string]: string };
+  [key: string]: any;
+}
+
+export class Project {
+  flowgramVersion?: string;
+
+  projectPath: string;
+
+  packageJsonPath: string;
+
+  packageJson: PackageJson;
+
+  protected constructor() {}
+
+  async init() {
+    // get nearest package.json
+    let projectPath: string = process.cwd();
+
+    while (
+      projectPath !== "/" &&
+      !existsSync(path.join(projectPath, "package.json"))
+    ) {
+      projectPath = path.join(projectPath, "..");
+    }
+    if (projectPath === "/") {
+      throw new Error("Please run this command in a valid project");
+    }
+
+    this.projectPath = projectPath;
+
+    this.packageJsonPath = path.join(projectPath, "package.json");
+    this.packageJson = JSON.parse(readFileSync(this.packageJsonPath, "utf8"));
+
+    this.flowgramVersion =
+      this.packageJson.dependencies["@flowgram.ai/fixed-layout-editor"] ||
+      this.packageJson.dependencies["@flowgram.ai/free-layout-editor"] ||
+      this.packageJson.dependencies["@flowgram.ai/editor"];
+  }
+
+  async addDependency(dependency: string) {
+    let name: string;
+    let version: string;
+
+    // 处理作用域包(如 @types/react@1.0.0)
+    const lastAtIndex = dependency.lastIndexOf("@");
+
+    if (lastAtIndex <= 0) {
+      // 没有@符号 或者@在开头(如 @types/react)
+      name = dependency;
+      version = await getLatestVersion(name);
+    } else {
+      // 正常分割包名和版本
+      name = dependency.substring(0, lastAtIndex);
+      version = dependency.substring(lastAtIndex + 1);
+
+      // 如果版本部分为空,获取最新版本
+      if (!version.trim()) {
+        version = await getLatestVersion(name);
+      }
+    }
+
+    this.packageJson.dependencies[name] = version;
+    writeFileSync(
+      this.packageJsonPath,
+      JSON.stringify(this.packageJson, null, 2),
+    );
+  }
+
+  async addDependencies(dependencies: string[]) {
+    for (const dependency of dependencies) {
+      await this.addDependency(dependency);
+    }
+  }
+
+  printInfo() {
+    console.log(chalk.bold("Project Info:"));
+    console.log(chalk.black(`  - Flowgram Version: ${this.flowgramVersion}`));
+    console.log(chalk.black(`  - Project Path: ${this.projectPath}`));
+  }
+
+  static async getSingleton() {
+    const info = new Project();
+    await info.init();
+    return info;
+  }
+}

+ 0 - 0
packages/materials/form-materials/bin/utils/traverse-file.ts → apps/cli/src/utils/traverse-file.ts


+ 20 - 0
apps/cli/tsconfig.json

@@ -0,0 +1,20 @@
+{
+  "compilerOptions": {
+    "experimentalDecorators": true,
+    "target": "es2020",
+    "module": "esnext",
+    "strictPropertyInitialization": false,
+    "strict": true,
+    "esModuleInterop": true,
+    "moduleResolution": "node",
+    "skipLibCheck": true,
+    "noUnusedLocals": true,
+    "noImplicitAny": true,
+    "allowJs": true,
+    "resolveJsonModule": true,
+    "types": ["node"],
+    "jsx": "react-jsx",
+    "lib": ["es6", "dom", "es2020", "es2019.Array"]
+  },
+  "include": ["./src"],
+}

+ 10 - 0
apps/cli/tsup.config.js

@@ -0,0 +1,10 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { defineConfig } from "tsup";
+
+export default defineConfig({
+  shims: true,
+})

+ 54 - 211
common/config/rush/pnpm-lock.yaml

@@ -8,6 +8,52 @@ importers:
 
   .: {}
 
+  ../../apps/cli:
+    dependencies:
+      chalk:
+        specifier: ^5.3.0
+        version: 5.4.1
+      commander:
+        specifier: ^11.0.0
+        version: 11.1.0
+      download:
+        specifier: 8.0.0
+        version: 8.0.0
+      fs-extra:
+        specifier: ^9.1.0
+        version: 9.1.0
+      inquirer:
+        specifier: ^9.2.7
+        version: 9.3.7
+      tar:
+        specifier: 7.4.3
+        version: 7.4.3
+    devDependencies:
+      '@types/download':
+        specifier: 8.0.5
+        version: 8.0.5
+      '@types/fs-extra':
+        specifier: 11.0.4
+        version: 11.0.4
+      '@types/inquirer':
+        specifier: 9.0.7
+        version: 9.0.7
+      '@types/node':
+        specifier: ^18
+        version: 18.19.68
+      '@typescript-eslint/parser':
+        specifier: ^6.10.0
+        version: 6.21.0(eslint@8.57.1)(typescript@5.8.3)
+      eslint:
+        specifier: ^8.54.0
+        version: 8.57.1
+      tsup:
+        specifier: ^8.0.1
+        version: 8.3.5(typescript@5.8.3)
+      typescript:
+        specifier: ^5.8.3
+        version: 5.8.3
+
   ../../apps/create-app:
     dependencies:
       chalk:
@@ -1888,7 +1934,7 @@ importers:
         version: 8.57.1
       tsup:
         specifier: ^8.0.1
-        version: 8.3.5(tsx@4.19.4)(typescript@5.8.3)
+        version: 8.3.5(typescript@5.8.3)
       vitest:
         specifier: ^0.34.6
         version: 0.34.6(jsdom@22.1.0)
@@ -2148,18 +2194,9 @@ importers:
       '@flowgram.ai/json-schema':
         specifier: workspace:*
         version: link:../../variable-engine/json-schema
-      chalk:
-        specifier: ^5.3.0
-        version: 5.4.1
-      commander:
-        specifier: ^11.0.0
-        version: 11.1.0
       immer:
         specifier: ~10.1.1
         version: 10.1.1
-      inquirer:
-        specifier: ^9.2.7
-        version: 9.3.7
       lodash-es:
         specifier: ^4.17.21
         version: 4.17.21
@@ -2702,7 +2739,7 @@ importers:
         version: 5.3.11(@babel/core@7.26.10)(react-dom@18.3.1)(react-is@18.3.1)(react@18.3.1)
       tsup:
         specifier: ^8.0.1
-        version: 8.3.5(tsx@4.19.4)(typescript@5.8.3)
+        version: 8.3.5(typescript@5.8.3)
       vitest:
         specifier: ^0.34.6
         version: 0.34.6(jsdom@22.1.0)
@@ -3499,7 +3536,7 @@ importers:
         version: 5.3.11(@babel/core@7.26.10)(react-dom@18.3.1)(react-is@18.3.1)(react@18.3.1)
       tsup:
         specifier: ^8.0.1
-        version: 8.3.5(tsx@4.19.4)(typescript@5.8.3)
+        version: 8.3.5(typescript@5.8.3)
       vitest:
         specifier: ^0.34.6
         version: 0.34.6(jsdom@22.1.0)
@@ -8607,14 +8644,6 @@ packages:
     engines: {node: '>=14.0.0'}
     dev: false
 
-  /@rollup/rollup-android-arm-eabi@4.28.1:
-    resolution: {integrity: sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==}
-    cpu: [arm]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-android-arm-eabi@4.40.2:
     resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==}
     cpu: [arm]
@@ -8623,14 +8652,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-android-arm64@4.28.1:
-    resolution: {integrity: sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==}
-    cpu: [arm64]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-android-arm64@4.40.2:
     resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==}
     cpu: [arm64]
@@ -8639,14 +8660,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-darwin-arm64@4.28.1:
-    resolution: {integrity: sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-darwin-arm64@4.40.2:
     resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==}
     cpu: [arm64]
@@ -8655,14 +8668,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-darwin-x64@4.28.1:
-    resolution: {integrity: sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-darwin-x64@4.40.2:
     resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==}
     cpu: [x64]
@@ -8671,14 +8676,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-freebsd-arm64@4.28.1:
-    resolution: {integrity: sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==}
-    cpu: [arm64]
-    os: [freebsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-freebsd-arm64@4.40.2:
     resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==}
     cpu: [arm64]
@@ -8687,14 +8684,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-freebsd-x64@4.28.1:
-    resolution: {integrity: sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==}
-    cpu: [x64]
-    os: [freebsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-freebsd-x64@4.40.2:
     resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==}
     cpu: [x64]
@@ -8703,15 +8692,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-arm-gnueabihf@4.28.1:
-    resolution: {integrity: sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==}
-    cpu: [arm]
-    os: [linux]
-    libc: [glibc]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-linux-arm-gnueabihf@4.40.2:
     resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==}
     cpu: [arm]
@@ -8720,15 +8700,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-arm-musleabihf@4.28.1:
-    resolution: {integrity: sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==}
-    cpu: [arm]
-    os: [linux]
-    libc: [musl]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-linux-arm-musleabihf@4.40.2:
     resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==}
     cpu: [arm]
@@ -8737,15 +8708,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-gnu@4.28.1:
-    resolution: {integrity: sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==}
-    cpu: [arm64]
-    os: [linux]
-    libc: [glibc]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-linux-arm64-gnu@4.40.2:
     resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==}
     cpu: [arm64]
@@ -8754,15 +8716,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-musl@4.28.1:
-    resolution: {integrity: sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==}
-    cpu: [arm64]
-    os: [linux]
-    libc: [musl]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-linux-arm64-musl@4.40.2:
     resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==}
     cpu: [arm64]
@@ -8771,15 +8724,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-loongarch64-gnu@4.28.1:
-    resolution: {integrity: sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==}
-    cpu: [loong64]
-    os: [linux]
-    libc: [glibc]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-linux-loongarch64-gnu@4.40.2:
     resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==}
     cpu: [loong64]
@@ -8788,15 +8732,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-powerpc64le-gnu@4.28.1:
-    resolution: {integrity: sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==}
-    cpu: [ppc64]
-    os: [linux]
-    libc: [glibc]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-linux-powerpc64le-gnu@4.40.2:
     resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==}
     cpu: [ppc64]
@@ -8805,15 +8740,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-riscv64-gnu@4.28.1:
-    resolution: {integrity: sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==}
-    cpu: [riscv64]
-    os: [linux]
-    libc: [glibc]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-linux-riscv64-gnu@4.40.2:
     resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==}
     cpu: [riscv64]
@@ -8830,15 +8756,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-s390x-gnu@4.28.1:
-    resolution: {integrity: sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==}
-    cpu: [s390x]
-    os: [linux]
-    libc: [glibc]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-linux-s390x-gnu@4.40.2:
     resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==}
     cpu: [s390x]
@@ -8847,15 +8764,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-x64-gnu@4.28.1:
-    resolution: {integrity: sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==}
-    cpu: [x64]
-    os: [linux]
-    libc: [glibc]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-linux-x64-gnu@4.40.2:
     resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==}
     cpu: [x64]
@@ -8864,15 +8772,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-linux-x64-musl@4.28.1:
-    resolution: {integrity: sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==}
-    cpu: [x64]
-    os: [linux]
-    libc: [musl]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-linux-x64-musl@4.40.2:
     resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==}
     cpu: [x64]
@@ -8881,14 +8780,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-win32-arm64-msvc@4.28.1:
-    resolution: {integrity: sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-win32-arm64-msvc@4.40.2:
     resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==}
     cpu: [arm64]
@@ -8897,14 +8788,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-win32-ia32-msvc@4.28.1:
-    resolution: {integrity: sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-win32-ia32-msvc@4.40.2:
     resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==}
     cpu: [ia32]
@@ -8913,14 +8796,6 @@ packages:
     dev: true
     optional: true
 
-  /@rollup/rollup-win32-x64-msvc@4.28.1:
-    resolution: {integrity: sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@rollup/rollup-win32-x64-msvc@4.40.2:
     resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==}
     cpu: [x64]
@@ -10000,10 +9875,6 @@ packages:
     resolution: {integrity: sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==}
     dev: true
 
-  /@types/estree@1.0.6:
-    resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
-    dev: true
-
   /@types/estree@1.0.7:
     resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
 
@@ -13328,6 +13199,7 @@ packages:
         optional: true
     dependencies:
       picomatch: 4.0.2
+    dev: false
 
   /fdir@6.4.4(picomatch@4.0.2):
     resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==}
@@ -17829,35 +17701,6 @@ packages:
       fsevents: 2.3.3
     dev: true
 
-  /rollup@4.28.1:
-    resolution: {integrity: sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==}
-    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
-    hasBin: true
-    dependencies:
-      '@types/estree': 1.0.6
-    optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.28.1
-      '@rollup/rollup-android-arm64': 4.28.1
-      '@rollup/rollup-darwin-arm64': 4.28.1
-      '@rollup/rollup-darwin-x64': 4.28.1
-      '@rollup/rollup-freebsd-arm64': 4.28.1
-      '@rollup/rollup-freebsd-x64': 4.28.1
-      '@rollup/rollup-linux-arm-gnueabihf': 4.28.1
-      '@rollup/rollup-linux-arm-musleabihf': 4.28.1
-      '@rollup/rollup-linux-arm64-gnu': 4.28.1
-      '@rollup/rollup-linux-arm64-musl': 4.28.1
-      '@rollup/rollup-linux-loongarch64-gnu': 4.28.1
-      '@rollup/rollup-linux-powerpc64le-gnu': 4.28.1
-      '@rollup/rollup-linux-riscv64-gnu': 4.28.1
-      '@rollup/rollup-linux-s390x-gnu': 4.28.1
-      '@rollup/rollup-linux-x64-gnu': 4.28.1
-      '@rollup/rollup-linux-x64-musl': 4.28.1
-      '@rollup/rollup-win32-arm64-msvc': 4.28.1
-      '@rollup/rollup-win32-ia32-msvc': 4.28.1
-      '@rollup/rollup-win32-x64-msvc': 4.28.1
-      fsevents: 2.3.3
-    dev: true
-
   /rollup@4.40.2:
     resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@@ -18840,6 +18683,7 @@ packages:
     dependencies:
       fdir: 6.4.2(picomatch@4.0.2)
       picomatch: 4.0.2
+    dev: false
 
   /tinyglobby@0.2.13:
     resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==}
@@ -18855,7 +18699,6 @@ packages:
     dependencies:
       fdir: 6.4.4(picomatch@4.0.2)
       picomatch: 4.0.2
-    dev: false
 
   /tinypool@0.7.0:
     resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==}
@@ -19067,7 +18910,7 @@ packages:
       source-map: 0.8.0-beta.0
       sucrase: 3.35.0
       tinyexec: 0.3.1
-      tinyglobby: 0.2.13
+      tinyglobby: 0.2.14
       tree-kill: 1.2.2
       typescript: 5.8.3
     transitivePeerDependencies:
@@ -19106,11 +18949,11 @@ packages:
       picocolors: 1.1.1
       postcss-load-config: 6.0.1(tsx@4.19.4)
       resolve-from: 5.0.0
-      rollup: 4.28.1
+      rollup: 4.40.2
       source-map: 0.8.0-beta.0
       sucrase: 3.35.0
       tinyexec: 0.3.1
-      tinyglobby: 0.2.10
+      tinyglobby: 0.2.14
       tree-kill: 1.2.2
       typescript: 5.8.3
     transitivePeerDependencies:

+ 0 - 80
packages/materials/form-materials/bin/index.ts

@@ -1,80 +0,0 @@
-#!/usr/bin/env -S npx tsx@latest
-
-import inquirer from 'inquirer';
-import { Command } from 'commander';
-import chalk from 'chalk';
-
-import { getProjectInfo, installDependencies, ProjectInfo } from './project.js';
-import { copyMaterial, listAllMaterials, Material } from './materials.js';
-
-const program = new Command();
-
-program
-  .version('1.0.1')
-  .description('Add official materials to your project')
-  .argument('[materialName]', 'Optional material name to skip selection (format: type/name)')
-  .action(async (materialName?: string) => {
-    // materialName can be undefined
-    console.log(chalk.bgGreenBright('Welcome to @flowgram.ai/form-materials CLI!'));
-
-    const projectInfo: ProjectInfo = getProjectInfo();
-
-    console.log(chalk.bold('Project Info:'));
-    console.log(chalk.black(`  - Flowgram Version: ${projectInfo.flowgramVersion}`));
-    console.log(chalk.black(`  - Project Path: ${projectInfo.projectPath}`));
-
-    const materials: Material[] = listAllMaterials();
-
-    let material: Material | undefined; // material can be undefined
-
-    // Check if materialName is provided and exists in materials
-    if (materialName) {
-      const selectedMaterial = materials.find((m) => `${m.type}/${m.name}` === materialName);
-      if (selectedMaterial) {
-        material = selectedMaterial;
-        console.log(chalk.green(`Using material: ${materialName}`));
-      } else {
-        console.log(
-          chalk.yellow(`Material "${materialName}" not found. Please select from the list:`)
-        );
-      }
-    }
-
-    // If material not found or materialName not provided, prompt user to select
-    if (!material) {
-      // User select one component
-      const result = await inquirer.prompt<{
-        material: Material; // Specify type for prompt result
-      }>([
-        {
-          type: 'list',
-          name: 'material',
-          message: 'Select one material to add:',
-          choices: [
-            ...materials.map((_material) => ({
-              name: `${_material.type}/${_material.name}`,
-              value: _material,
-            })),
-          ],
-        },
-      ]);
-      material = result.material;
-    }
-    // Ensure material is defined before proceeding
-    if (!material) {
-      console.error(chalk.red('No material selected. Exiting.'));
-      process.exit(1);
-    }
-
-    // 4. Copy the materials to the project
-    console.log(chalk.bold('The following materials will be added to your project'));
-    console.log(material);
-    let { packagesToInstall } = copyMaterial(material, projectInfo);
-
-    // 4. Install the dependencies
-    console.log(chalk.bold('These npm dependencies will be added to your project'));
-    console.log(packagesToInstall);
-    installDependencies(packagesToInstall, projectInfo);
-  });
-
-program.parse(process.argv);

+ 0 - 95
packages/materials/form-materials/bin/project.ts

@@ -1,95 +0,0 @@
-/**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
-
-import path from 'path';
-import fs from 'fs';
-import { execSync } from 'child_process';
-
-// Added type definitions
-interface PackageJson {
-  dependencies: { [key: string]: string };
-  devDependencies?: { [key: string]: string };
-  peerDependencies?: { [key: string]: string };
-  [key: string]: any;
-}
-
-export interface ProjectInfo {
-  projectPath: string;
-  packageJsonPath: string;
-  packageJson: PackageJson;
-  flowgramVersion: string;
-}
-
-export function getProjectInfo(): ProjectInfo {
-  // get nearest package.json
-  let projectPath: string = process.cwd();
-
-  while (projectPath !== '/' && !fs.existsSync(path.join(projectPath, 'package.json'))) {
-    projectPath = path.join(projectPath, '..');
-  }
-
-  if (projectPath === '/') {
-    throw new Error('Please run this command in a valid project');
-  }
-
-  const packageJsonPath: string = path.join(projectPath, 'package.json');
-  const packageJson: PackageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
-
-  // fixed layout or free layout
-  const flowgramVersion: string | undefined =
-    packageJson.dependencies['@flowgram.ai/fixed-layout-editor'] ||
-    packageJson.dependencies['@flowgram.ai/free-layout-editor'] ||
-    packageJson.dependencies['@flowgram.ai/editor'];
-
-  if (!flowgramVersion) {
-    throw new Error(
-      'Please install @flowgram.ai/fixed-layout-editor or @flowgram.ai/free-layout-editor'
-    );
-  }
-
-  return {
-    projectPath,
-    packageJsonPath,
-    packageJson,
-    flowgramVersion, // TypeScript will ensure this is string due to the check above
-  };
-}
-
-export function findRushJson(startPath: string): string | null {
-  let currentPath: string = startPath;
-  while (currentPath !== '/' && !fs.existsSync(path.join(currentPath, 'rush.json'))) {
-    currentPath = path.join(currentPath, '..');
-  }
-  if (fs.existsSync(path.join(currentPath, 'rush.json'))) {
-    return path.join(currentPath, 'rush.json');
-  }
-  return null;
-}
-
-export function installDependencies(packages: string[], projectInfo: ProjectInfo): void {
-  if (fs.existsSync(path.join(projectInfo.projectPath, 'yarn.lock'))) {
-    console.log(`yarn add ${packages.join(' ')}`);
-    execSync(`yarn add ${packages.join(' ')}`, { stdio: 'inherit' });
-    return;
-  }
-
-  if (fs.existsSync(path.join(projectInfo.projectPath, 'pnpm-lock.yaml'))) {
-    console.log(`pnpm add ${packages.join(' ')}`);
-    execSync(`pnpm add ${packages.join(' ')}`, { stdio: 'inherit' });
-    return;
-  }
-
-  //  rush monorepo
-  if (findRushJson(projectInfo.projectPath)) {
-    console.log(`rush add ${packages.map((pkg) => `--package ${pkg}`).join(' ')}`);
-    execSync(`rush add ${packages.map((pkg) => `--package ${pkg}`).join(' ')}`, {
-      stdio: 'inherit',
-    });
-    return;
-  }
-
-  console.log(`npm install ${packages.join(' ')}`);
-  execSync(`npm install ${packages.join(' ')}`, { stdio: 'inherit' });
-}

+ 6 - 0
packages/materials/form-materials/bin/run.sh

@@ -0,0 +1,6 @@
+#!/bin/sh
+#  Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+#  SPDX-License-Identifier: MIT
+
+
+npx @flowgram.ai/cli@latest materials $1

+ 1 - 4
packages/materials/form-materials/package.json

@@ -13,7 +13,7 @@
   "module": "./dist/esm/index.js",
   "types": "./dist/index.d.ts",
   "bin": {
-    "flowgram-form-materials": "./bin/index.ts"
+    "flowgram-form-materials": "./bin/run.sh"
   },
   "files": [
     "dist",
@@ -39,9 +39,6 @@
     "@flowgram.ai/json-schema": "workspace:*",
     "lodash-es": "^4.17.21",
     "nanoid": "^4.0.2",
-    "commander": "^11.0.0",
-    "chalk": "^5.3.0",
-    "inquirer": "^9.2.7",
     "immer": "~10.1.1",
     "@coze-editor/editor": "0.1.0-alpha.5a549c",
     "@codemirror/view": "~6.38.0",

+ 8 - 0
rush.json

@@ -445,6 +445,14 @@
                 "cli"
             ]
         },
+        {
+            "packageName": "@flowgram.ai/cli",
+            "projectFolder": "apps/cli",
+            "versionPolicyName": "publishPolicy",
+            "tags": [
+                "cli"
+            ]
+        },
         // 官网
         {
             "packageName": "@flowgram.ai/docs",