@h-ai/cli 0.1.0-alpha.58 → 0.1.0-alpha.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +34 -19
- package/README.ja.md +34 -19
- package/README.md +34 -19
- package/dist/{chunk-F4IBPVZ5.js → chunk-WWG3U5ZD.js} +20 -2
- package/dist/chunk-WWG3U5ZD.js.map +1 -0
- package/dist/{cli-skill-templates-45CK4RQ2.js → cli-skill-templates-2KDUACT6.js} +3 -3
- package/dist/{cli-skill-templates-45CK4RQ2.js.map → cli-skill-templates-2KDUACT6.js.map} +1 -1
- package/dist/index.js +90 -69
- package/dist/index.js.map +1 -1
- package/package.json +9 -7
- package/templates/base/package.json.hbs +1 -1
- package/templates/skills/hai-app-create/SKILL.md +2 -0
- package/templates/skills/hai-app-create/development.md +15 -0
- package/templates/skills/hai-deploy/SKILL.md +2 -0
- package/templates/skills/hai-framework-sync/SKILL.md +4 -0
- package/dist/chunk-F4IBPVZ5.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-ai/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.59",
|
|
5
5
|
"description": "Hai Framework CLI for project scaffolding and code generation.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"keywords": [
|
|
@@ -30,9 +30,10 @@
|
|
|
30
30
|
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@h-ai/core": "0.1.0-alpha.
|
|
33
|
+
"@h-ai/core": "0.1.0-alpha.59",
|
|
34
34
|
"cac": "^7.0.1",
|
|
35
35
|
"chalk": "^6.0.1",
|
|
36
|
+
"fast-wrap-ansi": "^0.2.2",
|
|
36
37
|
"fs-extra": "^11.4.1",
|
|
37
38
|
"handlebars": "^4.7.10",
|
|
38
39
|
"ora": "^9.4.1",
|
|
@@ -40,14 +41,15 @@
|
|
|
40
41
|
"yaml": "^2.9.1"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"@h-ai/cache": "0.1.0-alpha.
|
|
44
|
-
"@h-ai/deploy": "0.1.0-alpha.
|
|
45
|
-
"@h-ai/reach": "0.1.0-alpha.
|
|
46
|
-
"@h-ai/reldb": "0.1.0-alpha.
|
|
47
|
-
"@h-ai/storage": "0.1.0-alpha.
|
|
44
|
+
"@h-ai/cache": "0.1.0-alpha.59",
|
|
45
|
+
"@h-ai/deploy": "0.1.0-alpha.59",
|
|
46
|
+
"@h-ai/reach": "0.1.0-alpha.59",
|
|
47
|
+
"@h-ai/reldb": "0.1.0-alpha.59",
|
|
48
|
+
"@h-ai/storage": "0.1.0-alpha.59",
|
|
48
49
|
"@types/fs-extra": "^11.0.4",
|
|
49
50
|
"@types/node": "^26.6.5",
|
|
50
51
|
"@types/prompts": "^2.4.9",
|
|
52
|
+
"fast-string-width": "^3.0.2",
|
|
51
53
|
"rimraf": "^6.1.3",
|
|
52
54
|
"tsup": "^8.5.1",
|
|
53
55
|
"typescript": "^6.0.3",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"engines": { "node": "^22.22.2 || ^24.15.0 || >=26.0.0" },
|
|
3
3
|
"name": "{{projectName}}",
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.59",
|
|
5
5
|
"private": true,
|
|
6
6
|
"packageManager": "pnpm@12.11.2",
|
|
7
7
|
"description": "{{#if_eq appType 'admin'}}Enterprise management system built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'website'}}Enterprise website built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'h5'}}Mobile H5 app built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'api'}}REST API service built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'mobile-app'}}Mobile app built with hai Agent Framework{{/if_eq}}",
|
|
@@ -17,6 +17,8 @@ description: "在 hai 应用中新增、修改或修复功能,按需求复用
|
|
|
17
17
|
|
|
18
18
|
## 使用路径
|
|
19
19
|
|
|
20
|
+
需要新建项目、添加模块、补齐配置或生成代码时,先按 [CLI 安装与命令](development.md#cli-安装与命令) 全局安装 `@h-ai/cli`,再执行 `hai` 命令。
|
|
21
|
+
|
|
20
22
|
1. 读取 [development.md](development.md):先按真实需求匹配框架能力、已安装版本与应用架构,再确定改动。适用能力包括同一模块已有 feature、配置和扩展点,不以导入包作为复用完成的证据。
|
|
21
23
|
2. 读取所选模块的 skill 与实际公开类型,复用现有服务和组件。新增依赖或直接使用第三方 SDK 前说明已有能力的具体缺口;无需安装无关模块。
|
|
22
24
|
3. 新增或改变行为时用有意义的目标用例验证,已有回归保持通过;按下方参考实现,使用 [hai-app-review](../hai-app-review/SKILL.md) 检查改动涉及的安全与质量边界。
|
|
@@ -9,6 +9,21 @@
|
|
|
9
9
|
- 从所属 workspace 的依赖与 lockfile 确认 `@h-ai/*` 实际解析版本和 npm/local 模式。模板 skill 提供导航,**当前安装包的 `package.json.exports`、公开 `.d.ts` 与随包文档决定可用 API**;本地框架源码只在版本匹配或显式联调时作实现证据。
|
|
10
10
|
- 若 skill 与安装版本不符,以公开类型和实际行为核验。先使用已支持能力;任务确需新版本时列明版本变更、调用方与验证范围。不能直接复制未发布源码的 API,也不能为绕过差异添加虚构兼容层。
|
|
11
11
|
|
|
12
|
+
## CLI 安装与命令
|
|
13
|
+
|
|
14
|
+
CLI 的 npm 包名是 `@h-ai/cli`,安装后提供 `hai` 命令。使用脚手架前先全局安装并确认版本;PowerShell 中使用 `pnpm.cmd` 执行 pnpm 命令。
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add -g @h-ai/cli
|
|
18
|
+
hai --version
|
|
19
|
+
hai --help
|
|
20
|
+
|
|
21
|
+
# 新建项目
|
|
22
|
+
hai create my-app
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
已有项目先进入项目根目录,再按需要执行 `hai add <module>`、`hai init` 或 `hai generate <type> <name>`。已安装 CLI 时,相同安装命令可更新到当前发布版本;需要指定版本时使用 `@h-ai/cli@<version>`。命令和模板以实际安装的 CLI 为准,不能把未发布的框架源码能力当作已安装包能力。
|
|
26
|
+
|
|
12
27
|
## 将需求映射到现有能力
|
|
13
28
|
|
|
14
29
|
应用元信息只有一份配置来源:名称、版本、环境、默认语言与日志设置来自 `_core.yml`,使用 `core.config` 与 `core.logger`;Node 启动时不通过固定 `logging` 覆盖 YAML。SvelteKit 服务端 layout 和 fullstack `app.info` 仅公开必要元信息,不能下发整个 core 或模块配置。独立 Vite mobile-app 在 Node 构建阶段经 core 加载 YAML/环境覆盖并注入公开字段,配置变化需重新构建。环境覆盖使用 `HAI_CORE_NAME`、`HAI_CORE_VERSION`、`HAI_CORE_ENV`、`HAI_CORE_LOGGING_LEVEL` 等既有规则;容器明确配置生产环境,不能用另一个状态来源代替 `core.env`。
|
|
@@ -218,6 +218,8 @@ if (result.success) {
|
|
|
218
218
|
|
|
219
219
|
## CLI 命令
|
|
220
220
|
|
|
221
|
+
通过 CLI 部署前,先按 [CLI 安装与命令](../hai-app-create/development.md#cli-安装与命令) 全局安装 `@h-ai/cli`,确认 `hai --version` 可运行后再执行下列命令。在 Node.js 脚本中直接使用 `@h-ai/deploy` API 时无需安装 CLI。
|
|
222
|
+
|
|
221
223
|
```bash
|
|
222
224
|
# 部署当前目录
|
|
223
225
|
hai deploy
|
|
@@ -40,6 +40,8 @@ description: "同步 hai-framework 通用 skills、AI 入口与应用依赖,
|
|
|
40
40
|
|
|
41
41
|
先读取当前应用入口与 package.json/lockfile,记录实际 npm/local 模式及解析版本。同步 skill 不能证明新 API 已安装或可用:以目标依赖的公开 exports/types 验证示例与配置,未发布框架能力仅用于已授权本地联调。
|
|
42
42
|
|
|
43
|
+
CLI 生成的 `@h-ai/*` 依赖固定为 CLI 对应版本,pnpm catalog 也使用精确版本。统一升级所有框架包后检查 lockfile 与已安装 exports。
|
|
44
|
+
|
|
43
45
|
| 变更类型 | 首选位置 |
|
|
44
46
|
| --- | --- |
|
|
45
47
|
| 通用 hai 模块用法 | `hai-framework/packages/cli/templates/skills/hai-*/SKILL.md` |
|
|
@@ -53,6 +55,8 @@ description: "同步 hai-framework 通用 skills、AI 入口与应用依赖,
|
|
|
53
55
|
|
|
54
56
|
### 3. 同步到应用仓库
|
|
55
57
|
|
|
58
|
+
通过已发布 CLI 补齐缺失文件时,先按 [CLI 安装与命令](../hai-app-create/development.md#cli-安装与命令) 安装或更新 `@h-ai/cli`,再从应用根目录运行 `hai init`。它只补齐缺失内容,不覆盖已有 AI 规则和 skills;已有文件仍需对照模板合并。未发布的模板修正需从框架源目录同步,不能假定已安装 CLI 包含这些修正。
|
|
59
|
+
|
|
56
60
|
把源头完整 skill 目录(含 reference.md 等被引用资源)复制到应用仓库 `.agents/skills/<skill>/`,并同步入口引用:
|
|
57
61
|
|
|
58
62
|
- `AGENTS.md`
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../messages/en-US.json","../messages/zh-CN.json","../src/cli-i18n.ts","../src/commands/cli-skill-templates.ts"],"names":[],"mappings":"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","file":"chunk-F4IBPVZ5.js","sourcesContent":["{\n \"cli_invalidGenerateName\": \"Use a name starting with a letter, with only letters, digits, hyphens or underscores\",\n \"cli_generateOutsideWorkspace\": \"Generated files must remain inside the workspace, including symbolic link targets\",\n \"cli_generateExists\": \"File already exists: {path}. Use --force to overwrite it\",\n \"cli_deploy_module_missing\": \"Deploy module not found. Install @h-ai/deploy first:\",\n \"cli_deploy_load_credentials\": \"Loading credentials...\",\n \"cli_deploy_credentials_failed\": \"Failed to load credentials: {message}\",\n \"cli_deploy_credentials_loaded\": \"Loaded {count} credentials\",\n \"cli_deploy_load_config\": \"Loading deploy config...\",\n \"cli_deploy_config_missing\": \"Deploy config not found: {path}\",\n \"cli_deploy_config_hint\": \"Run: hai add deploy to generate config template\",\n \"cli_deploy_config_loaded\": \"Deploy config loaded\",\n \"cli_deploy_scanning\": \"Scanning application...\",\n \"cli_deploy_scan_failed\": \"Scan failed: {message}\",\n \"cli_deploy_scanned\": \"Scanned: {name} (SvelteKit: {sveltekit}, Services: {services})\",\n \"cli_deploy_none\": \"none\",\n \"cli_deploy_initializing\": \"Initializing deploy module...\",\n \"cli_deploy_init_failed\": \"Init failed: {message}\",\n \"cli_deploy_initialized\": \"Deploy module initialized\",\n \"cli_deploy_deploying\": \"Deploying application...\",\n \"cli_deploy_failed\": \"Deploy failed: {message}\",\n \"cli_deploy_success\": \"Deployment successful!\",\n \"cli_deploy_url\": \" URL: {url}\",\n \"cli_deploy_id\": \" ID: {id}\",\n \"cli_deploy_env\": \" Env: {names}\",\n \"cli_deploy_command_failed\": \"Deploy command failed\",\n \"cli_adminPasswordSaved\": \" Administrator: admin; the random initial password is saved as HAI_ADMIN_DEFAULT_PASSWORD in .env\"\n}\n","{\n \"cli_invalidGenerateName\": \"名称必须以字母开头,且仅包含字母、数字、短横线或下划线\",\n \"cli_generateOutsideWorkspace\": \"生成文件必须位于工作目录内,符号链接目标也不得越界\",\n \"cli_generateExists\": \"文件已存在:{path}。使用 --force 才可覆盖\",\n \"cli_deploy_module_missing\": \"未找到部署模块,请先安装 @h-ai/deploy:\",\n \"cli_deploy_load_credentials\": \"正在加载凭证…\",\n \"cli_deploy_credentials_failed\": \"加载凭证失败:{message}\",\n \"cli_deploy_credentials_loaded\": \"已加载 {count} 项凭证\",\n \"cli_deploy_load_config\": \"正在加载部署配置…\",\n \"cli_deploy_config_missing\": \"未找到部署配置:{path}\",\n \"cli_deploy_config_hint\": \"运行 hai add deploy 生成部署配置模板\",\n \"cli_deploy_config_loaded\": \"部署配置已加载\",\n \"cli_deploy_scanning\": \"正在扫描应用…\",\n \"cli_deploy_scan_failed\": \"扫描失败:{message}\",\n \"cli_deploy_scanned\": \"已扫描:{name}(SvelteKit:{sveltekit},服务:{services})\",\n \"cli_deploy_none\": \"无\",\n \"cli_deploy_initializing\": \"正在初始化部署模块…\",\n \"cli_deploy_init_failed\": \"初始化失败:{message}\",\n \"cli_deploy_initialized\": \"部署模块已初始化\",\n \"cli_deploy_deploying\": \"正在部署应用…\",\n \"cli_deploy_failed\": \"部署失败:{message}\",\n \"cli_deploy_success\": \"部署成功!\",\n \"cli_deploy_url\": \" 地址:{url}\",\n \"cli_deploy_id\": \" 标识:{id}\",\n \"cli_deploy_env\": \" 环境变量:{names}\",\n \"cli_deploy_command_failed\": \"部署命令失败\",\n \"cli_adminPasswordSaved\": \" 管理员:admin;随机初始密码已保存到 .env 的 HAI_ADMIN_DEFAULT_PASSWORD\"\n}\n","/** CLI 用户可见消息;通过 core 的全局 locale 选择语言。 */\nimport { core } from '@h-ai/core'\nimport enUS from '../messages/en-US.json'\nimport zhCN from '../messages/zh-CN.json'\n\nexport const cliM = core.i18n.createMessageGetter<keyof typeof zhCN>({ 'en-US': enUS, 'zh-CN': zhCN })\n","/**\n * @h-ai/cli — Skill 模板生成\n *\n * 将 Skill 模板文件复制到用户项目中,供 AI 编程助手使用。\n * @module cli-skill-templates\n */\n\nimport type { AppType } from '../cli-types.js'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport fse from 'fs-extra'\nimport Handlebars from 'handlebars'\nimport { cliM } from '../cli-i18n.js'\n\n/**\n * 模块名到 Skill 目录名的映射\n */\nconst MODULE_SKILL_MAP: Record<string, string> = {\n 'db': 'hai-reldb',\n 'reldb': 'hai-reldb',\n 'cache': 'hai-cache',\n 'iam': 'hai-iam',\n 'storage': 'hai-storage',\n 'ai': 'hai-ai',\n 'crypto': 'hai-crypto',\n 'payment': 'hai-payment',\n 'vecdb': 'hai-vecdb',\n 'datapipe': 'hai-datapipe',\n 'reach': 'hai-reach',\n 'scheduler': 'hai-scheduler',\n 'audit': 'hai-audit',\n 'deploy': 'hai-deploy',\n 'kit': 'hai-kit',\n 'ui': 'hai-ui',\n 'api-client': 'hai-api-client',\n 'capacitor': 'hai-capacitor',\n}\n\n/**\n * 各 appType 互斥的 Skill。\n *\n * 生成样板工程时,默认复制 templates/skills 下所有 hai-* Skill,\n * 仅排除与当前样板技术栈明显冲突的少数 Skill。\n */\nconst SKILL_EXCLUSIONS_BY_APP_TYPE: Partial<Record<AppType, string[]>> = {\n 'admin': ['hai-serv', 'hai-api-contract', 'hai-api-client', 'hai-capacitor'],\n 'website': ['hai-serv', 'hai-api-contract', 'hai-api-client', 'hai-capacitor'],\n 'h5': ['hai-serv', 'hai-api-contract', 'hai-api-client', 'hai-capacitor'],\n 'api': ['hai-ui', 'hai-kit', 'hai-capacitor'],\n 'mobile-app': ['hai-core', 'hai-kit', 'hai-serv', 'hai-api-contract'],\n 'fullstack': ['hai-kit'],\n}\n\nconst WORKFLOW_SKILLS = [\n 'hai-build',\n 'hai-app-create',\n 'hai-app-review',\n 'hai-app-tests',\n]\n\nconst PACKAGE_SKILL_MAP: Record<string, string> = {\n '@h-ai/api-contract': 'hai-api-contract',\n '@h-ai/api-client': 'hai-api-client',\n '@h-ai/core': 'hai-core',\n '@h-ai/serv': 'hai-serv',\n '@h-ai/kit': 'hai-kit',\n '@h-ai/ui': 'hai-ui',\n '@h-ai/reldb': 'hai-reldb',\n '@h-ai/cache': 'hai-cache',\n '@h-ai/iam': 'hai-iam',\n '@h-ai/storage': 'hai-storage',\n '@h-ai/ai': 'hai-ai',\n '@h-ai/crypto': 'hai-crypto',\n '@h-ai/payment': 'hai-payment',\n '@h-ai/vecdb': 'hai-vecdb',\n '@h-ai/datapipe': 'hai-datapipe',\n '@h-ai/reach': 'hai-reach',\n '@h-ai/scheduler': 'hai-scheduler',\n '@h-ai/audit': 'hai-audit',\n '@h-ai/deploy': 'hai-deploy',\n '@h-ai/capacitor': 'hai-capacitor',\n}\n\nconst SKILL_TARGET_DIRS = ['.agents/skills'] as const\n\nconst BRIDGE_FILES = [\n {\n source: 'copilot-instructions.md',\n destination: '.github/copilot-instructions.md',\n },\n {\n source: 'CLAUDE.md',\n destination: 'CLAUDE.md',\n },\n {\n source: 'AGENTS.md',\n destination: 'AGENTS.md',\n },\n {\n source: 'opencode.json',\n destination: 'opencode.json',\n },\n] as const\n\nconst BRIDGE_PROFILE_REGEX = /^[a-z0-9-]+$/\n\n/** 从已有模板标识与实际包布局确认桥接类型,不按项目名称猜测技术栈。 */\nasync function detectBridgeContext(projectPath: string): Promise<{ appType?: AppType, projectName?: string }> {\n const packagePath = path.join(projectPath, 'package.json')\n if (!(await fse.pathExists(packagePath))) {\n return {}\n }\n\n let metadata: unknown\n try {\n metadata = await fse.readJson(packagePath)\n }\n catch {\n return {}\n }\n if (!metadata || typeof metadata !== 'object' || !('name' in metadata)\n || typeof metadata.name !== 'string' || !BRIDGE_PROFILE_REGEX.test(metadata.name)) {\n return {}\n }\n\n const projectName = metadata.name\n for (const [appType, directory, suffixes] of [\n ['fullstack', 'packages', ['contract', 'serv', 'shared']],\n ['api', 'apps', ['contract', 'service']],\n ] as const) {\n const manifests = await Promise.all(suffixes.map(suffix => fse.pathExists(\n path.join(projectPath, directory, `${projectName}-${suffix}`, 'package.json'),\n )))\n if (manifests.every(Boolean)) {\n return { appType, projectName }\n }\n }\n\n if ('keywords' in metadata && Array.isArray(metadata.keywords) && metadata.keywords.includes('hai')) {\n for (const appType of ['admin', 'website', 'h5', 'mobile-app'] as const) {\n if (metadata.keywords.includes(appType)) {\n return { appType, projectName }\n }\n }\n }\n return { projectName }\n}\n\nasync function listTemplateSkillNames(templatesDir: string): Promise<string[]> {\n const entries = await fse.readdir(templatesDir, { withFileTypes: true })\n const skillNames: string[] = []\n\n for (const entry of entries) {\n if (!entry.isDirectory() || !entry.name.startsWith('hai-')) {\n continue\n }\n\n const skillFilePath = path.join(templatesDir, entry.name, 'SKILL.md')\n if (await fse.pathExists(skillFilePath)) {\n skillNames.push(entry.name)\n }\n }\n\n return skillNames.sort((a, b) => a.localeCompare(b))\n}\n\nexport function resolveCompatibleSkillNames(\n skillNames: string[],\n appType?: AppType,\n): string[] {\n const excludedSkills = new Set(appType ? SKILL_EXCLUSIONS_BY_APP_TYPE[appType] ?? [] : [])\n return skillNames.filter(skillName => !excludedSkills.has(skillName))\n}\n\n/**\n * 获取 templates/skills/ 目录的绝对路径\n *\n * 兼容两种运行环境:\n * - 构建后:dist/cli-skill-templates.js → ../templates/skills\n * - 测试时:src/commands/cli-skill-templates.ts → ../../templates/skills\n */\nfunction getSkillTemplatesDir(): string {\n const currentDir = path.dirname(fileURLToPath(import.meta.url))\n const distPath = path.resolve(currentDir, '..', 'templates', 'skills')\n if (fse.pathExistsSync(distPath)) {\n return distPath\n }\n return path.resolve(currentDir, '..', '..', 'templates', 'skills')\n}\n\n/**\n * 复制单个 Skill 目录到项目\n *\n * @param templatesDir - Skill 模板根目录\n * @param skillName - Skill 目录名(如 'hai-reldb')\n * @param projectPath - 用户项目根目录\n */\nasync function copySkill(\n templatesDir: string,\n skillName: string,\n projectPath: string,\n targetDirs: readonly string[] = SKILL_TARGET_DIRS,\n overwrite = true,\n): Promise<string[]> {\n const src = path.join(templatesDir, skillName)\n\n if (!(await fse.pathExists(src))) {\n return []\n }\n\n const copiedFiles: string[] = []\n const resources = await fse.readdir(src, { recursive: true, withFileTypes: true })\n const relativeFiles = resources.filter(resource => resource.isFile())\n .map(resource => path.relative(src, path.join(resource.parentPath, resource.name)))\n\n for (const targetDir of targetDirs) {\n const dest = path.join(projectPath, targetDir, skillName)\n await assertProjectOutputPath(projectPath, dest)\n if (!overwrite) {\n // 已有入口不能使新参考资源被跳过;逐文件补缺,同时保留用户修改。\n const missingFiles: string[] = []\n for (const relativeFile of relativeFiles) {\n if (!(await fse.pathExists(path.join(dest, relativeFile)))) {\n missingFiles.push(relativeFile)\n }\n }\n if (missingFiles.length === 0) {\n continue\n }\n for (const relativeFile of missingFiles)\n await assertProjectOutputPath(projectPath, path.join(dest, relativeFile))\n await fse.copy(src, dest, { overwrite: false })\n copiedFiles.push(...missingFiles.map(relativeFile => `${targetDir}/${skillName}/${relativeFile.replaceAll(path.sep, '/')}`))\n }\n else {\n for (const relativeFile of relativeFiles)\n await assertProjectOutputPath(projectPath, path.join(dest, relativeFile))\n await fse.copy(src, dest, { overwrite: true })\n copiedFiles.push(`${targetDir}/${skillName}/SKILL.md`)\n }\n }\n\n return copiedFiles\n}\n\n/** 写入前解析已有目录/文件链接,所有 AI 输出均限制在当前项目内。 */\nasync function assertProjectOutputPath(projectPath: string, destination: string): Promise<void> {\n let ancestor = destination\n while (!(await fse.pathExists(ancestor))) {\n const parent = path.dirname(ancestor)\n if (parent === ancestor)\n throw new Error(cliM('cli_generateOutsideWorkspace'))\n ancestor = parent\n }\n const relativeAncestor = path.relative(await fse.realpath(projectPath), await fse.realpath(ancestor))\n if (relativeAncestor === '..' || relativeAncestor.startsWith(`..${path.sep}`) || path.isAbsolute(relativeAncestor))\n throw new Error(cliM('cli_generateOutsideWorkspace'))\n}\n\nasync function copySkills(\n templatesDir: string,\n skillNames: string[],\n projectPath: string,\n targetDirs: readonly string[] = SKILL_TARGET_DIRS,\n overwrite = true,\n): Promise<string[]> {\n const copiedFiles: string[] = []\n\n for (const skillName of new Set(skillNames)) {\n copiedFiles.push(...await copySkill(templatesDir, skillName, projectPath, targetDirs, overwrite))\n }\n\n return copiedFiles\n}\n\n/**\n * 复制桥接文件到项目\n *\n * @param templatesDir - Skill 模板根目录\n * @param projectPath - 用户项目根目录\n */\nasync function copyBridgeFiles(\n templatesDir: string,\n projectPath: string,\n overwrite = true,\n appType?: string,\n projectName?: string,\n): Promise<string[]> {\n const copiedFiles: string[] = []\n\n for (const file of BRIDGE_FILES) {\n const src = await resolveBridgeSource(templatesDir, file.source, appType, projectName !== undefined)\n const dest = path.join(projectPath, file.destination)\n\n if (!src) {\n continue\n }\n\n if (!overwrite && await fse.pathExists(dest)) {\n continue\n }\n\n await assertProjectOutputPath(projectPath, dest)\n await fse.ensureDir(path.dirname(dest))\n if (src.endsWith('.hbs')) {\n const template = await fse.readFile(src, 'utf8')\n const rendered = Handlebars.compile(template, { noEscape: true })({ projectName })\n await fse.writeFile(dest, rendered, 'utf8')\n }\n else {\n await fse.copy(src, dest, { overwrite })\n }\n copiedFiles.push(file.destination)\n }\n\n return copiedFiles\n}\n\nasync function resolveBridgeSource(\n templatesDir: string,\n source: string,\n appType?: string,\n allowTemplate = false,\n): Promise<string | null> {\n const profile = appType && BRIDGE_PROFILE_REGEX.test(appType) ? appType : 'generic'\n const candidates = [\n ...(allowTemplate ? [path.join(templatesDir, 'bridges', profile, `${source}.hbs`)] : []),\n path.join(templatesDir, 'bridges', profile, source),\n path.join(templatesDir, 'bridges', 'generic', source),\n path.join(templatesDir, source),\n ]\n\n for (const candidate of candidates) {\n if (await fse.pathExists(candidate)) {\n return candidate\n }\n }\n\n return null\n}\n\n/**\n * 为新项目生成完整的 Skill 文件。\n *\n * 默认复制 templates/skills 下所有 hai-* Skill,再按 appType 排除互斥项。\n *\n * @param projectPath - 用户项目根目录\n * @param _features - 预留参数;保留签名兼容 createProject 调用方\n * @param appType - 应用类型(api 类型不复制 UI Skill)\n */\nexport async function generateSkillFiles(\n projectPath: string,\n _features: string[],\n appType?: string,\n projectName?: string,\n): Promise<string[]> {\n const templatesDir = getSkillTemplatesDir()\n\n if (!(await fse.pathExists(templatesDir))) {\n return []\n }\n\n const copiedFiles: string[] = []\n\n const templateSkillNames = await listTemplateSkillNames(templatesDir)\n const compatibleSkillNames = resolveCompatibleSkillNames(templateSkillNames, appType as AppType | undefined)\n\n copiedFiles.push(...await copySkills(templatesDir, compatibleSkillNames, projectPath))\n\n // 复制桥接文件\n copiedFiles.push(...await copyBridgeFiles(templatesDir, projectPath, true, appType, projectName))\n\n return copiedFiles\n}\n\n/**\n * 为已有项目添加单个模块的 Skill 文件\n *\n * 同时补全普通开发所需工作流及缺失参考资源,保留已有文件。\n *\n * @param projectPath - 用户项目根目录\n * @param moduleId - 模块标识(如 'db'、'iam')\n * @returns 复制的文件路径,若模块无对应 Skill 则返回 null\n */\nexport async function generateModuleSkillFile(\n projectPath: string,\n moduleId: string,\n): Promise<string[] | null> {\n const skillName = MODULE_SKILL_MAP[moduleId]\n if (!skillName) {\n return null\n }\n\n const copiedFiles = await generateProjectAiSupport(projectPath, [skillName], {\n overwriteSkills: false,\n overwriteBridgeFiles: false,\n })\n return copiedFiles.length > 0 ? copiedFiles : null\n}\n\nexport function resolveSkillNamesForPackages(packageNames: string[]): string[] {\n const skillNames = [...WORKFLOW_SKILLS]\n\n for (const packageName of packageNames) {\n const skillName = PACKAGE_SKILL_MAP[packageName]\n if (skillName) {\n skillNames.push(skillName)\n }\n }\n\n return [...new Set(skillNames)]\n}\n\nexport async function generateProjectAiSupport(\n projectPath: string,\n skillNames: string[],\n options: {\n overwriteSkills?: boolean\n overwriteBridgeFiles?: boolean\n appType?: string\n projectName?: string\n } = {},\n): Promise<string[]> {\n const templatesDir = getSkillTemplatesDir()\n\n if (!(await fse.pathExists(templatesDir))) {\n return []\n }\n\n const {\n overwriteSkills = true,\n overwriteBridgeFiles = true,\n } = options\n\n const detected = await detectBridgeContext(projectPath)\n const appType = options.appType ?? detected.appType\n const projectName = options.projectName ?? detected.projectName\n // 已确认的样板补全整个兼容目录;其它项目只补工作流与明确请求的模块。\n const selectedSkillNames = appType\n ? [...resolveCompatibleSkillNames(await listTemplateSkillNames(templatesDir), appType as AppType), ...WORKFLOW_SKILLS, ...skillNames]\n : [...WORKFLOW_SKILLS, ...skillNames]\n const copiedFiles: string[] = []\n copiedFiles.push(...await copySkills(templatesDir, selectedSkillNames, projectPath, SKILL_TARGET_DIRS, overwriteSkills))\n copiedFiles.push(...await copyBridgeFiles(templatesDir, projectPath, overwriteBridgeFiles, appType, projectName))\n\n return copiedFiles\n}\n"]}
|