
Eliteforge Frontend Generator
- 44 installs
- Updated July 24, 2026
- cloudsen/eliteforge-skills
Scaffolds EliteForge frontend projects with deterministic naming and template rules for app, UI component, and JS SDK/lib types via onebase-cli.
About
Generates EliteForge frontend projects (app, Vue3 component monorepo, JS SDK) using the same rules as cisdigital-generator-app, with strict fe-company-product-service naming and onebase-cli command assembly. A developer uses it to scaffold EliteForge frontend projects or preview the dry-run command.
- Project name must be fe-<company>-<product>-<service>
- pnpm-only; never infer missing required parameters
Eliteforge Frontend Generator by the numbers
- 44 all-time installs (skills.sh)
- Ranked #1,342 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cloudsen/eliteforge-skills --skill eliteforge-frontend-generatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 44 |
|---|---|
| Last updated | July 24, 2026 |
| Repository | cloudsen/eliteforge-skills ↗ |
What it does
Scaffolds EliteForge frontend projects with deterministic naming and template rules for app, UI component, and JS SDK/lib types via onebase-cli.
Files
EliteForge 前端生成器
Overview
Generate frontend projects with deterministic rules from the online generator app. Use the bundled script to produce a dry-run payload/command preview or run project creation directly. Project name is mandatory: fe-<company_name>-<product_name>-<service_name>.
Package Manager Policy
- Treat
pnpmas the only package manager for generated frontend projects. - After project generation, always initialize dependencies with
pnpm iinside the generated project directory. - Do not replace
pnpm iwithnpm install,yarn, orpnpm install.
Project Naming Policy
- Always pass
project_nameto scaffold CLI asfe-<company_slug>-<product_slug>-<service_slug>. - Never drop the
fe-prefix. - Never append project-type suffixes such as
-app,-ui, or-sdk.
Mandatory Input Policy
- Treat
project_type,company_name,product_name, andservice_nameas required and user-provided only. - Never infer required fields from repository name, current directory, previous conversation values, defaults, or examples.
- If any required field is missing, stop and ask only for missing fields first.
- Do not run dry-run or generation before all required fields are explicitly provided by the user.
- If user input is ambiguous (for example "same as before" or "use default"), ask for explicit values.
Inputs
Collect the following fields.
Required (must come from the user message):
project_type:frontend_app,frontend_ui, orfrontend_sdkcompany_name: kebab-caseproduct_name: kebab-caseservice_name: kebab-case
Optional:
cli_name: defaultonebase-clioutput_dir: default current working directoryauto_slugify: defaultfalse; use only when the user explicitly asks to convert non-kebab names
Workflow
1. Collect required fields only from explicit user input. 2. If required fields are missing, ask only for the missing fields and wait for user response. 3. Validate naming fields as kebab-case. 4. If naming is invalid and auto_slugify is not explicitly requested, ask the user to provide kebab-case values. 5. Run dry-run first to show payload and command. 6. Verify dry-run command uses -p fe-<company_slug>-<product_slug>-<service_slug>. 7. Execute the create command. 8. Change to generated project directory and run pnpm i. 9. Confirm generated folder exists and initialization completed.
Read references/frontend-generation-logic.md when the user asks why a template or project name is chosen.
Commands
Dry-run:
python3 scripts/create_frontend_project.py \
--project-type frontend_app \
--company-name cisdigital \
--product-name your-product \
--service-name your-service \
--dry-runGenerate frontend_app:
python3 scripts/create_frontend_project.py \
--project-type frontend_app \
--company-name cisdigital \
--product-name your-product \
--service-name your-service
cd fe-cisdigital-your-product-your-service
pnpm iGenerate frontend_ui:
python3 scripts/create_frontend_project.py \
--project-type frontend_ui \
--company-name cisdigital \
--product-name your-product \
--service-name your-component
cd fe-cisdigital-your-product-your-component
pnpm iGenerate frontend_sdk:
python3 scripts/create_frontend_project.py \
--project-type frontend_sdk \
--company-name cisdigital \
--product-name your-product \
--service-name your-jsdk
cd fe-cisdigital-your-product-your-jsdk
pnpm iUse custom CLI or output directory:
python3 scripts/create_frontend_project.py \
--project-type frontend_app \
--company-name cisdigital \
--product-name your-product \
--service-name your-service \
--cli-name onebase-cli \
--output-dir /path/to/workspace
cd /path/to/workspace/fe-cisdigital-your-product-your-service
pnpm iConvert non-kebab input (only when user explicitly requests conversion):
python3 scripts/create_frontend_project.py \
--project-type frontend_app \
--company-name "CIS Digital" \
--product-name "My Product" \
--service-name "Portal UI" \
--auto-slugify \
--dry-runNotes
frontend_project_typeexists in the form data but does not affect template selection in current generator logic.- The script blocks generation if target project directory already exists.
- Always initialize generated projects with
pnpm i. - Generated project directory name is exactly
fe-<company_slug>-<product_slug>-<service_slug>. - Keep output concise: return generated directory, create command, and
pnpm iresult. - Never guess or fill missing required fields on behalf of the user.
interface:
display_name: "EliteForge 前端生成器"
short_description: "按线上规则生成 EliteForge 前端应用、组件库和 SDK 工程(必填参数显式提供,生成后执行 pnpm i)"
default_prompt: "使用 $eliteforge-frontend-generator 生成 EliteForge 前端工程;必须先让用户明确提供 project_type/company_name/product_name/service_name,禁止推断缺失必填参数。脚手架命令的 project_name 必须是 fe-<company>-<product>-<service>,不得丢失 fe- 前缀或追加 -app/-ui/-sdk 后缀。项目生成后进入该目录执行 pnpm i 完成初始化。"
Frontend Generation Logic (Source-Aligned)
This document summarizes frontend generation logic implemented in the online generator project:
cisdigital-generator-app/generator/services.pycisdigital-generator-app/generator/biz_dependencies_forms.pycisdigital-generator-app/generator/models.py
Type Mapping
Frontend project type to template mapping:
frontend_app->fe-cisdigital-vite-app-templatefrontend_ui->fe-cisdigital-monorepo-templatefrontend_sdk->fe-cisdigital-ts-lib-template
Command Template
Frontend command is assembled as:
onebase-cli create -t <template_name> -p <project_name>- CLI name defaults to
onebase-cliand can be overridden. project_nameformat isfe-<company_slug>-<product_slug>-<service_slug>.- Do not append project-type suffixes (
-app,-ui,-sdk) toproject_name.
Naming Rules
Form-level validation (ProjectGenerationForm) requires kebab-case for:
company_nameproduct_nameservice_name
Payload-level normalization uses slugify:
- lower-case input
- replace non
[a-zA-Z0-9._-]with- - collapse repeated
- - trim leading/trailing
-
Payload Fields Used by Frontend Flow
Key fields generated for frontend flow:
template_nameproject_namefrontend_bundleoutput_foldergenerated_folderarchive_basenameobject_namecompany_slugproduct_slugservice_slug
Form Field Note
frontend_project_type currently exists and is optional. It is preserved in payload context but does not affect template selection.
#!/usr/bin/env python3
"""
Create OneBase frontend projects using cisdigital-generator-app frontend logic.
"""
from __future__ import annotations
import argparse
import json
import re
import subprocess
import sys
from pathlib import Path
from typing import Dict, List
KEBAB_CASE_PATTERN = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
FRONTEND_TEMPLATES: Dict[str, str] = {
"frontend_app": "fe-cisdigital-vite-app-template",
"frontend_ui": "fe-cisdigital-monorepo-template",
"frontend_sdk": "fe-cisdigital-ts-lib-template",
}
PROJECT_TYPE_ALIASES = {
"app": "frontend_app",
"ui": "frontend_ui",
"sdk": "frontend_sdk",
}
def _slugify(value: str, default: str = "service") -> str:
slug = re.sub(r"[^a-zA-Z0-9._-]+", "-", value.strip().lower())
slug = re.sub(r"-+", "-", slug).strip("-")
return slug or default
def _validate_kebab(value: str, field_name: str) -> None:
if not KEBAB_CASE_PATTERN.fullmatch(value.strip()):
raise ValueError(
f"{field_name} must be kebab-case: lowercase letters, numbers, and hyphens only."
)
def _resolve_project_type(project_type: str) -> str:
resolved = PROJECT_TYPE_ALIASES.get(project_type, project_type)
if resolved not in FRONTEND_TEMPLATES:
valid = ", ".join(sorted(FRONTEND_TEMPLATES.keys()))
raise ValueError(f"Unsupported project_type '{project_type}'. Expected one of: {valid}")
return resolved
def build_payload(
project_type: str,
company_name: str,
product_name: str,
service_name: str,
cli_name: str = "onebase-cli",
frontend_project_type: str | None = None,
) -> Dict[str, str]:
resolved_type = _resolve_project_type(project_type)
template_name = FRONTEND_TEMPLATES[resolved_type]
company_slug = _slugify(company_name, "company")
product_slug = _slugify(product_name, "product")
service_slug = _slugify(service_name, "service")
project_name = f"fe-{company_slug}-{product_slug}-{service_slug}"
path_prefix = f"{company_slug}/{product_slug}"
payload: Dict[str, str] = {
"project_type": resolved_type,
"company_name": company_name,
"product_name": product_name,
"service_name": service_name,
"package_manager": "pnpm",
"cli_name": cli_name,
"template_name": template_name,
"project_name": project_name,
"frontend_bundle": f"{project_name}.zip",
"output_folder": project_name,
"generated_folder": project_name,
"archive_basename": project_name,
"object_name": f"{path_prefix}/{project_name}.zip",
"company_slug": company_slug,
"product_slug": product_slug,
"service_slug": service_slug,
}
if frontend_project_type:
payload["frontend_project_type"] = frontend_project_type
return payload
def build_command(payload: Dict[str, str]) -> List[str]:
return [
payload.get("cli_name", "onebase-cli"),
"create",
"-t",
payload["template_name"],
"-p",
payload["project_name"],
]
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Create OneBase frontend project using generator-app rules."
)
parser.add_argument(
"--project-type",
required=True,
help="frontend_app | frontend_ui | frontend_sdk (aliases: app/ui/sdk)",
)
parser.add_argument("--company-name", required=True)
parser.add_argument("--product-name", required=True)
parser.add_argument("--service-name", required=True)
parser.add_argument("--frontend-project-type")
parser.add_argument("--cli-name", default="onebase-cli")
parser.add_argument("--output-dir", default=".")
parser.add_argument(
"--auto-slugify",
action="store_true",
help="Allow non-kebab input and convert to slug in payload/project_name.",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Print payload and command, do not execute.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
if not args.auto_slugify:
_validate_kebab(args.company_name, "company_name")
_validate_kebab(args.product_name, "product_name")
_validate_kebab(args.service_name, "service_name")
payload = build_payload(
project_type=args.project_type,
company_name=args.company_name,
product_name=args.product_name,
service_name=args.service_name,
cli_name=args.cli_name,
frontend_project_type=args.frontend_project_type,
)
command = build_command(payload)
output_dir = Path(args.output_dir).expanduser().resolve()
if not output_dir.exists():
print(f"[ERROR] output_dir does not exist: {output_dir}", file=sys.stderr)
return 2
if not output_dir.is_dir():
print(f"[ERROR] output_dir is not a directory: {output_dir}", file=sys.stderr)
return 2
result = {
"payload": payload,
"command": command,
"cwd": str(output_dir),
}
if args.dry_run:
print(json.dumps(result, indent=2, ensure_ascii=False))
return 0
target_dir = output_dir / payload["project_name"]
if target_dir.exists():
print(
f"[ERROR] target project directory already exists: {target_dir}",
file=sys.stderr,
)
return 2
process = subprocess.run(
command,
cwd=str(output_dir),
capture_output=True,
text=True,
)
if process.stdout:
print(process.stdout, end="")
if process.stderr:
print(process.stderr, end="", file=sys.stderr)
if process.returncode != 0:
print(
f"[ERROR] command failed ({process.returncode}): {' '.join(command)}",
file=sys.stderr,
)
return process.returncode
if not target_dir.exists():
print(
f"[ERROR] command succeeded but project directory missing: {target_dir}",
file=sys.stderr,
)
return 3
print(json.dumps({"project_path": str(target_dir), "command": command}, ensure_ascii=False))
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except ValueError as exc:
print(f"[ERROR] {exc}", file=sys.stderr)
raise SystemExit(2)