@h-ai/cli 0.1.0-alpha.55 → 0.1.0-alpha.57
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 +205 -0
- package/README.ja.md +205 -0
- package/README.md +16 -0
- package/dist/chunk-MJGUBOAV.js +391 -0
- package/dist/chunk-MJGUBOAV.js.map +1 -0
- package/dist/{chunk-FVDXJYMO.js → chunk-O3ANVICQ.js} +31 -2
- package/dist/chunk-O3ANVICQ.js.map +1 -0
- package/dist/cli-config-templates-OWBEGBZQ.js +5 -0
- package/dist/{cli-config-templates-WGN2I23J.js.map → cli-config-templates-OWBEGBZQ.js.map} +1 -1
- package/dist/{cli-skill-templates-ZIWYMS4Y.js → cli-skill-templates-ZYXES3NT.js} +3 -3
- package/dist/{cli-skill-templates-ZIWYMS4Y.js.map → cli-skill-templates-ZYXES3NT.js.map} +1 -1
- package/dist/index.js +35 -72
- package/dist/index.js.map +1 -1
- package/dist/{src-6ZBWDBUB.js → src-TBIET7YU.js} +908 -127
- package/dist/src-TBIET7YU.js.map +1 -0
- package/package.json +7 -7
- package/templates/apps/api/.gitignore.hbs +13 -0
- package/templates/apps/api/.npmrc.hbs +2 -0
- package/templates/apps/api/e2e/api-service.spec.ts.hbs +1 -1
- package/templates/apps/api/{eslint.config.js → eslint.config.js.hbs} +6 -8
- package/templates/apps/api/package.json.hbs +1 -1
- package/templates/apps/fullstack/.gitignore.hbs +16 -0
- package/templates/apps/fullstack/.npmrc.hbs +2 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +5 -8
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/eslint.config.js.hbs +5 -8
- package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +5 -8
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +1 -1
- package/templates/apps/fullstack/{eslint.config.js → eslint.config.js.hbs} +6 -8
- package/templates/apps/fullstack/package.json.hbs +1 -1
- package/templates/base/.gitignore.hbs +3 -0
- package/templates/base/.npmrc.hbs +15 -0
- package/templates/base/eslint-rules.js +45 -0
- package/templates/base/eslint.config.js.hbs +6 -8
- package/templates/base/package.json.hbs +1 -1
- package/templates/skills/AGENTS.md +9 -8
- package/templates/skills/CLAUDE.md +4 -0
- package/templates/skills/bridges/admin/AGENTS.md +8 -0
- package/templates/skills/bridges/admin/CLAUDE.md +4 -0
- package/templates/skills/bridges/admin/copilot-instructions.md +2 -0
- package/templates/skills/bridges/api/AGENTS.md +8 -1
- package/templates/skills/bridges/api/CLAUDE.md +4 -0
- package/templates/skills/bridges/api/copilot-instructions.md +2 -0
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +8 -2
- package/templates/skills/bridges/fullstack/CLAUDE.md.hbs +4 -0
- package/templates/skills/bridges/fullstack/copilot-instructions.md.hbs +2 -0
- package/templates/skills/bridges/h5/AGENTS.md +8 -0
- package/templates/skills/bridges/h5/CLAUDE.md +4 -0
- package/templates/skills/bridges/h5/copilot-instructions.md +2 -0
- package/templates/skills/bridges/mobile-app/AGENTS.md +8 -0
- package/templates/skills/bridges/mobile-app/CLAUDE.md +4 -0
- package/templates/skills/bridges/mobile-app/copilot-instructions.md +2 -0
- package/templates/skills/bridges/website/AGENTS.md +8 -0
- package/templates/skills/bridges/website/CLAUDE.md +4 -0
- package/templates/skills/bridges/website/copilot-instructions.md +2 -0
- package/templates/skills/claude-skill.md.hbs +7 -0
- package/templates/skills/copilot-instructions.md +2 -0
- package/templates/skills/hai-api-client/SKILL.md +2 -0
- package/templates/skills/hai-app-create/SKILL.md +9 -7
- package/templates/skills/hai-app-create/development.md +66 -0
- package/templates/skills/hai-app-create/reference.md +44 -32
- package/templates/skills/hai-app-review/SKILL.md +50 -144
- package/templates/skills/hai-app-tests/SKILL.md +3 -1
- package/templates/skills/hai-app-tests/reference.md +47 -27
- package/templates/skills/hai-build/SKILL.md +5 -4
- package/templates/skills/hai-build/reference.md +52 -32
- package/templates/skills/hai-ci/SKILL.md +8 -15
- package/templates/skills/hai-deploy/SKILL.md +63 -7
- package/templates/skills/hai-framework-sync/SKILL.md +8 -1
- package/templates/skills/hai-pr-review/SKILL.md +5 -4
- package/templates/skills/hai-reldb/SKILL.md +1 -0
- package/templates/skills/hai-reldb/reference.md +3 -0
- package/templates/skills/hai-scheduler/SKILL.md +3 -0
- package/templates/skills/hai-serv/SKILL.md +1 -0
- package/templates/skills/hai-serv/reference.md +16 -2
- package/templates/skills/hai-storage/SKILL.md +3 -1
- package/templates/skills/hai-ui/reference.md +1 -1
- package/dist/chunk-FVDXJYMO.js.map +0 -1
- package/dist/chunk-WUYLYCW2.js +0 -237
- package/dist/chunk-WUYLYCW2.js.map +0 -1
- package/dist/cli-config-templates-WGN2I23J.js +0 -5
- package/dist/src-6ZBWDBUB.js.map +0 -1
|
@@ -38,6 +38,8 @@ description: "同步 hai-framework 通用 skills、AI 入口与应用依赖,
|
|
|
38
38
|
|
|
39
39
|
### 1. 判断来源
|
|
40
40
|
|
|
41
|
+
先读取当前应用入口与 package.json/lockfile,记录实际 npm/local 模式及解析版本。同步 skill 不能证明新 API 已安装或可用:以目标依赖的公开 exports/types 验证示例与配置,未发布框架能力仅用于已授权本地联调。
|
|
42
|
+
|
|
41
43
|
| 变更类型 | 首选位置 |
|
|
42
44
|
| --- | --- |
|
|
43
45
|
| 通用 hai 模块用法 | `hai-framework/packages/cli/templates/skills/hai-*/SKILL.md` |
|
|
@@ -56,13 +58,16 @@ description: "同步 hai-framework 通用 skills、AI 入口与应用依赖,
|
|
|
56
58
|
- `AGENTS.md`
|
|
57
59
|
- `.github/copilot-instructions.md`
|
|
58
60
|
- `CLAUDE.md`(如存在)
|
|
61
|
+
- `.claude/skills/<skill>/SKILL.md` 的原生发现入口;描述同步共享技能,正文和参考仍从 `.agents/skills/` 读取,保留已声明的本地覆盖
|
|
59
62
|
- `opencode.json`(如结构变化)
|
|
60
63
|
|
|
64
|
+
新增/修改/修复任务入口必须能到达 `hai-app-create` 与其 `development.md`;审查入口能到达 `hai-app-review`。按目标架构/依赖交付所需模块技能,不把 SvelteKit 指令套到 API/Vite 项目。复制前比对应用私有覆盖,合并通用修正并保留差异,不能直接覆盖业务规则。
|
|
65
|
+
|
|
61
66
|
### 4. 验证
|
|
62
67
|
|
|
63
68
|
按变更类型选择验证方式:
|
|
64
69
|
|
|
65
|
-
- 仅模板 / skill 变更:检查 diff
|
|
70
|
+
- 仅模板 / skill 变更:检查 diff、目标中实际存在的入口/相邻资源及其相对引用,确认没有框架仓库私有路径/命令;本地能力导航对可选 skill 先检查存在,不把目录缺失当成能力缺失。
|
|
66
71
|
- 同步到应用仓库:按 `hai-ci` 定义的质量门禁运行。
|
|
67
72
|
- 涉及 framework runtime 时,先确认应用 package.json 已定义下列脚本;没有时按 README 的本地联调方式操作:
|
|
68
73
|
|
|
@@ -75,6 +80,8 @@ pnpm framework:watch <package>
|
|
|
75
80
|
## 漂移检查清单
|
|
76
81
|
|
|
77
82
|
- [ ] framework 模板和应用 `.agents/skills` 对同名 skill 内容一致,除非 PR 明确说明本地覆盖
|
|
83
|
+
- [ ] 普通功能修改/修复也路由到能力匹配与安全/质量自查,不仅“创建项目”或手工调用 skill 才生效
|
|
84
|
+
- [ ] 技能示例与目标实际依赖版本、架构、公开 API 一致;没有把模板更新当成 runtime 升级
|
|
78
85
|
- [ ] `AGENTS.md` 的 skill 列表包含新增/重命名 skill
|
|
79
86
|
- [ ] Copilot/Claude/OpenCode 入口能找到新增 skill
|
|
80
87
|
- [ ] CI 默认不依赖 `/home/.../hai-framework` 本地路径
|
|
@@ -27,7 +27,7 @@ description: "审查 PR 范围、风险、验证证据和合并准备度。"
|
|
|
27
27
|
- 设计 AI PR Review 方案
|
|
28
28
|
- 选择 Copilot、CodeRabbit、reviewdog、DangerJS、Claude Code Action、Codex Action 等工具
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
代码级框架复用、安全与风格审查交给 [hai-app-review](../hai-app-review/SKILL.md),测试用例设计交给 `hai-app-tests`,workflow 触发/权限/secret 细节交给 `hai-ci`。
|
|
31
31
|
|
|
32
32
|
## 审查分层
|
|
33
33
|
|
|
@@ -41,7 +41,8 @@ description: "审查 PR 范围、风险、验证证据和合并准备度。"
|
|
|
41
41
|
|
|
42
42
|
- scope 是否清晰且与 issue 对齐
|
|
43
43
|
- 测试是否覆盖成功路径、失败路径、边界条件
|
|
44
|
-
-
|
|
44
|
+
- 本次需求是否复用适用框架能力/feature/配置,自定义或新依赖是否有已安装版本的缺口依据(见 [development.md](../hai-app-create/development.md))
|
|
45
|
+
- 是否符合实际返回契约、i18n、日志、分层、当前 lint 风格,以及输入/越权/失败等适用行为验收;不能以导入包数量或 CI 全绿替代代码与安全审查
|
|
45
46
|
- 是否引入密钥、权限扩大、workflow 风险或两仓漂移
|
|
46
47
|
|
|
47
48
|
### 第 3 层:AI Review(默认 advisory)
|
|
@@ -91,7 +92,7 @@ PR 描述必须说明:
|
|
|
91
92
|
- [ ] PR 模板完整填写
|
|
92
93
|
- [ ] CI required checks 全绿
|
|
93
94
|
- [ ] 新行为有测试,失败路径有覆盖
|
|
94
|
-
- [ ]
|
|
95
|
+
- [ ] 框架能力选择、代码风格与适用安全边界已由 `hai-app-review` 覆盖,有行为证据或明确未验证项
|
|
95
96
|
- [ ] 文档 / skill / README 与代码同步
|
|
96
97
|
- [ ] workflow 权限或 secrets 变化已由 `hai-ci` 审查
|
|
97
98
|
- [ ] AI-assisted 内容已由人类复核
|
|
@@ -99,5 +100,5 @@ PR 描述必须说明:
|
|
|
99
100
|
## 相关 Skills
|
|
100
101
|
|
|
101
102
|
- `hai-ci` — CI/CD 与 workflow 安全
|
|
102
|
-
- `hai-
|
|
103
|
+
- `hai-app-review` — 应用代码质量与框架复用审查
|
|
103
104
|
- `hai-framework-sync` — 两仓同步审查
|
|
@@ -18,6 +18,7 @@ description: "使用 @h-ai/reldb 访问 SQLite/PostgreSQL/MySQL,编写 SQL、C
|
|
|
18
18
|
## 使用路径
|
|
19
19
|
|
|
20
20
|
- `_db.yml` 对应 core.config 的 db;先校验 ReldbConfigSchema,再 init 并检查 success。
|
|
21
|
+
- `reldb.crud.table` / `tx.crud.table` 的 `dbType` 必填并与实际后端一致;`BaseReldbCrudRepository` 自动读取已初始化的配置。
|
|
21
22
|
- 事务每步检查结果,避免把失败当普通返回值提交;同一事务操作须共享 tx。
|
|
22
23
|
- SQL 值参数化;where/orderBy/json 列名不是任意用户输入。分页使用 PaginatedResult。
|
|
23
24
|
|
|
@@ -173,6 +173,8 @@ const page = await reldb.sql.queryPage<User>({
|
|
|
173
173
|
|
|
174
174
|
通过配置创建单表 CRUD 仓库,免写 SQL:
|
|
175
175
|
|
|
176
|
+
`CrudConfig.dbType` 必填,必须与实际初始化的数据库后端一致(含环境变量覆盖后的类型)。以下为 SQLite 示例;切换到 PostgreSQL/MySQL 时同步修改。`BaseReldbCrudRepository` 会从已初始化的 `reldb.config.type` 获取类型,无需手动传入。
|
|
177
|
+
|
|
176
178
|
```typescript
|
|
177
179
|
const userCrud = reldb.crud.table<{ id: number, name: string, email: string }>({
|
|
178
180
|
table: 'users',
|
|
@@ -180,6 +182,7 @@ const userCrud = reldb.crud.table<{ id: number, name: string, email: string }>({
|
|
|
180
182
|
select: ['id', 'name', 'email'], // 查询列,默认 '*'
|
|
181
183
|
createColumns: ['name', 'email'], // 允许插入的列
|
|
182
184
|
updateColumns: ['name', 'email'], // 允许更新的列
|
|
185
|
+
dbType: 'sqlite', // 必填,与本例初始化后端一致
|
|
183
186
|
})
|
|
184
187
|
```
|
|
185
188
|
|
|
@@ -9,6 +9,8 @@ description: "使用 @h-ai/scheduler 注册 cron/API/JS/Hook 任务、手工触
|
|
|
9
9
|
|
|
10
10
|
持久化动态任务每个 `tickInterval`(默认 1000ms)在运行节点刷新;API 操作及获锁后的每次尝试重读定义,读取失败或任务已禁用/删除/改期时中断执行。`tasks` 仅表示最近成功刷新快照,已开始的外部副作用不会回滚。跨节点任务使用 `register()` 与共享数据库、Redis;`init({ tasks })` 是节点本地配置,memory cache 不支持跨进程防重。
|
|
11
11
|
|
|
12
|
+
`refreshTasks()` 主动刷新而不执行,同一快照的并发读取合并;保存、更新和删除落库成功后才发布内存投影。清空可选策略使用 `retry: null` / `handler: null`。Hook 业务异常为 `failed` 并重试,缺处理器、取消或 `SchedulerTaskSkippedError` 为 `interrupted`;一次性任务只在成功后删除。`event.context.occurrenceId` 在一次发生的重试期间不变,宿主用其绑定持久幂等回执;跨周期长任务使用宿主持久租约。配置被替换时旧发生停止重试,成功清理前再次核对定义。
|
|
13
|
+
|
|
12
14
|
关闭必须 `await scheduler.close()` 后再关闭数据库和缓存:关闭会拒绝新任务、取消 HTTP/Worker/重试并等待执行链退出。生命周期回调通过事件 `signal` 协作取消,必须自行停止外部副作用,禁止在 Hook 阻塞主线程。旧任务取消后不重试、不删除新实例同 ID 的任务。
|
|
13
15
|
|
|
14
16
|
JS 任务每次调用独立 Worker,默认 30000 毫秒期限包含线程启动;超时后等待线程终止才返回失败或重试,同步死循环不会占用主事件循环。上下文和结果须支持结构化克隆。Worker/vm 不构成安全沙箱,仍只接受受信任的服务端代码。
|
|
@@ -140,6 +142,7 @@ const logs = await scheduler.getLogs({
|
|
|
140
142
|
| `init` | `(config?) => Promise<HaiResult<void>>` | 初始化调度器 |
|
|
141
143
|
| `register` | `(task) => Promise<HaiResult<void>>` | 注册统一任务模型 |
|
|
142
144
|
| `updateTask` | `(taskId, updates) => Promise<HaiResult<void>>` | 更新 cron / params / handler |
|
|
145
|
+
| `refreshTasks` | `() => Promise<HaiResult<void>>` | 主动刷新共享定义,不执行任务 |
|
|
143
146
|
| `register` 扩展字段 | `description / deleteAfterRun / retry` | 支持任务描述、一次性任务、失败重试策略 |
|
|
144
147
|
| `trigger` | `(taskId, { source? }) => Promise<HaiResult<TaskExecutionLog>>` | 手工触发并记录来源 |
|
|
145
148
|
| `getLogs` | `(options?) => Promise<HaiResult<PaginatedResult<TaskExecutionLog>>>` | 支持按 trigger + startedAfter/startedBefore 过滤日志 |
|
|
@@ -19,6 +19,7 @@ description: "使用 @h-ai/serv 装配 contract/procedures、认证守卫、Open
|
|
|
19
19
|
|
|
20
20
|
- 先确定 Node listen 还是 Fetch adapter,再从共享 contract 装配 procedures;不要直接暴露 Hono。
|
|
21
21
|
- 认证绑定 route 的 auth/permission/role;启动配置错误与正常请求领域错误分别处理。
|
|
22
|
+
- 普通 procedure 的失败 HaiResult 默认通过 HTTP 200 返回,必须检查 success 与 error.code;error.httpStatus 是领域错误元数据。contract 输入校验失败为 HTTP 400,HTTP middleware、刷新 cookie 等响应另按自身契约处理,不能混为同一种状态映射。
|
|
22
23
|
- 浏览器 Cookie、CORS、apiPrefix、transport 须与 api-client 同步;语音 ticket 单独核验。
|
|
23
24
|
|
|
24
25
|
## 按需参考
|
|
@@ -374,7 +374,7 @@ await apiClient.init({
|
|
|
374
374
|
3. `iam.session.verifyToken`(推荐:传入 `iam` 后自动启用)
|
|
375
375
|
4. 默认 `parseRequestContext`:仅解析请求元数据,`session` 为 `undefined`
|
|
376
376
|
|
|
377
|
-
> ⚠️ `verifyToken` 会在每次请求调用(不缓存);verifyToken 失败或抛错统一收敛为 `session=undefined`,由 route 的 `.auth()` / `.permission()` / `.role()`
|
|
377
|
+
> ⚠️ `verifyToken` 会在每次请求调用(不缓存);verifyToken 失败或抛错统一收敛为 `session=undefined`,由 route 的 `.auth()` / `.permission()` / `.role()` 返回失败 HaiResult,`error.httpStatus=401`;普通 procedure 的 HTTP 响应默认仍为 200。
|
|
378
378
|
|
|
379
379
|
### 链式 procedure guard
|
|
380
380
|
|
|
@@ -403,7 +403,7 @@ const procedures = serv
|
|
|
403
403
|
.build()
|
|
404
404
|
```
|
|
405
405
|
|
|
406
|
-
- `.permission()` / `.role()` 隐含 `.auth()`;未登录返回 401
|
|
406
|
+
- `.permission()` / `.role()` 隐含 `.auth()`;未登录返回 `error.httpStatus=401` 的失败 HaiResult,授权不足为 403。普通 procedure 的 HTTP 响应默认仍为 200,必须检查 `success` 与 `error.code`。
|
|
407
407
|
- 同一路由声明多个 permission/role 时必须全部满足。
|
|
408
408
|
- handler 的未处理异常由 router 统一转换成 INTERNAL_ERROR HaiResult。
|
|
409
409
|
- 重复/未知路径会被拒绝;遗漏 contract procedure 时 `.build()` 在类型上不可用,运行时也会校验。
|
|
@@ -522,6 +522,20 @@ HTTP 审计、限流、CORS、租户头校验等跨 route 能力继续放在
|
|
|
522
522
|
|
|
523
523
|
### 正常业务请求中的错误与 i18n
|
|
524
524
|
|
|
525
|
+
先区分 HTTP 状态与 HaiResult 中的 `error.httpStatus`;普通 procedure 返回的领域错误元数据不会自动改写 HTTP 状态,当前默认装配如下(显式自定义响应状态或 middleware 按实际入口契约验证):
|
|
526
|
+
|
|
527
|
+
| 失败来源 | HTTP 响应 | 结果判定 |
|
|
528
|
+
| --- | --- | --- |
|
|
529
|
+
| procedure `.auth()` / `.permission()` / `.role()` | 200 | `success=false`;`error.httpStatus` 为 401 / 403 |
|
|
530
|
+
| handler 返回 `err(...)` 或下游模块失败 HaiResult | 200 | 检查 `success`、`error.code`,例如数据库查询错误的 `error.httpStatus=500` |
|
|
531
|
+
| handler 未处理异常被 route 实现器捕获 | 200 | INTERNAL_ERROR 失败 HaiResult,`error.httpStatus=500` |
|
|
532
|
+
| oRPC contract 输入 schema 校验失败(handler 之前) | 400 | 本地化失败体,`error.httpStatus=400` 与字段错误 |
|
|
533
|
+
| handler 内 `serv.validateInputOrFail(...)` 失败 | 200 | 失败 HaiResult,`error.httpStatus=400` 与字段错误 |
|
|
534
|
+
|
|
535
|
+
HTTP middleware、自定义 Response、refresh-cookie、OpenAPI/RPC 入口权限和 transport 协议错误分别按自身响应契约处理,可返回真实 HTTP 4xx/5xx。`@h-ai/api-client` 的自动刷新只由真实 HTTP 401 触发,不会因 HTTP 200 失败包络中的 `error.httpStatus=401` 自动刷新。
|
|
536
|
+
|
|
537
|
+
SvelteKit 的 `kit.response.fromError` 根据 `error.httpStatus` 生成 HTTP Response;`kit.response.fromResult` 使用显式错误码映射,未命中默认 400。这些响应转换规则不能套用到 serv 普通 procedure。
|
|
538
|
+
|
|
525
539
|
按三层处理:
|
|
526
540
|
|
|
527
541
|
1. **oRPC contract 输入校验**:进入 handler 前自动发生;`serv.createApp()` 会把默认英文 Zod 错误改写为本地化 `errors[]`。
|
|
@@ -25,7 +25,7 @@ description: "使用 @h-ai/storage 访问本地/S3 文件、目录和预签名
|
|
|
25
25
|
|
|
26
26
|
- 文件上传与下载
|
|
27
27
|
- 预签名 URL 生成(客户端直传)
|
|
28
|
-
- S3 兼容存储对接(AWS S3、MinIO、阿里云 OSS 等)
|
|
28
|
+
- S3 兼容存储对接(AWS S3、MinIO、RustFS、阿里云 OSS 等)
|
|
29
29
|
- 本地开发文件存储
|
|
30
30
|
- 目录前缀清理与批量文件删除
|
|
31
31
|
|
|
@@ -57,6 +57,8 @@ operationLog:
|
|
|
57
57
|
约定环境变量优先于 YAML,例如 `HAI_STORAGE_ROOT`、
|
|
58
58
|
`HAI_STORAGE_ACCESSKEYID`、`HAI_STORAGE_SECRETACCESSKEY`。
|
|
59
59
|
|
|
60
|
+
MinIO、RustFS 使用同一个 S3 Provider:配置 `type: s3`、S3 API `endpoint`(通常为 9000 端口)和 `forcePathStyle: true`,并预先创建 `bucket`;不要新增 `type: minio` 或 `type: rustfs`。
|
|
61
|
+
|
|
60
62
|
`operationLog` 在 Local/S3 Provider 的真实 file/dir/presign 操作处输出日志,不要在 `storage-main.ts` 或调用方重复包装。文件内容不会写入日志,上传数据只记录字节长度;`maxLength` 用于截断序列化后的参数,`level` 默认 `debug`。
|
|
61
63
|
|
|
62
64
|
### 2. 初始化与关闭
|
|
@@ -103,7 +103,7 @@ npm install -D @iconify/tailwind4 @iconify-json/tabler
|
|
|
103
103
|
|
|
104
104
|
```js
|
|
105
105
|
import { autoImportHaiUi } from '@h-ai/ui/auto-import'
|
|
106
|
-
import adapter from '@sveltejs/adapter-
|
|
106
|
+
import adapter from '@sveltejs/adapter-node'
|
|
107
107
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
|
108
108
|
|
|
109
109
|
/** @type {import('@sveltejs/kit').Config} */
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/cli-config-templates.ts"],"names":[],"mappings":"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","file":"chunk-FVDXJYMO.js","sourcesContent":["/**\n * @h-ai/cli — 配置文件模板\n *\n * 为各模块生成默认配置文件内容。\n * @module cli-config-templates\n */\n\nimport type { AiModuleConfig, CacheModuleConfig, CoreModuleConfig, DbModuleConfig, FeatureId, IamModuleConfig, ModuleConfigs, StorageModuleConfig } from '../cli-types.js'\n\n/**\n * 生成模块配置文件内容\n *\n * @param moduleKey - 模块标识(core/serv/db/cache/iam/storage/ai)\n * @param configs - 用户自定义配置值\n * @returns YAML 格式的配置内容\n */\nexport function generateConfigFile(moduleKey: string, configs?: ModuleConfigs): string {\n switch (moduleKey) {\n case 'core':\n return generateCoreConfig(configs?.core)\n case 'serv':\n return generateServConfig()\n case 'db':\n return generateDbConfig(configs?.db)\n case 'cache':\n return generateCacheConfig(configs?.cache)\n case 'iam':\n return generateIamConfig(configs?.iam)\n case 'storage':\n return generateStorageConfig(configs?.storage)\n case 'ai':\n return generateAiConfig(configs?.ai)\n case 'deploy':\n return generateDeployConfig()\n case 'vecdb':\n return generateVecdbConfig()\n case 'reach':\n return generateReachConfig()\n case 'scheduler':\n return generateSchedulerConfig()\n case 'audit':\n return generateAuditConfig()\n case 'payment':\n return generatePaymentConfig()\n default:\n return `# ${moduleKey} 配置\\n`\n }\n}\n\nfunction generateCoreConfig(cfg?: CoreModuleConfig): string {\n const name = cfg?.name ?? 'my-app'\n const locale = cfg?.defaultLocale ?? 'zh-CN'\n const supportedLocales = locale === 'en-US'\n ? ` - en-US\\n - zh-CN`\n : ` - zh-CN\\n - en-US`\n\n return `# =============================================================================\n# 应用配置\n# =============================================================================\n\nname: ${name}\nversion: 0.1.0\nenv: development\ndebug: false\n\ndefaultLocale: ${locale}\nsupportedLocales:\n${supportedLocales}\n`\n}\n\nfunction generateServConfig(): string {\n return `# =============================================================================\n# Service / HTTP 入口配置\n# =============================================================================\n\nhttp:\n apiPrefix: /api/v1\n openapi:\n path: /openapi.json\n docs:\n path: /docs\n health:\n path: /health\n readyPath: /ready\n rpc: false\n\n# 开发期默认关闭 transport;启用 @h-ai/crypto 后可按需改为对象配置\ntransport: false\n`\n}\n\nfunction generateDbConfig(cfg?: DbModuleConfig): string {\n const dbType = cfg?.type ?? 'sqlite'\n const database = cfg?.database ?? (dbType === 'sqlite' ? './data/app.db' : 'hai')\n const host = cfg?.host ?? 'localhost'\n const port = cfg?.port ?? (dbType === 'postgresql' ? 5432 : 3306)\n\n if (dbType === 'sqlite') {\n return `# =============================================================================\n# 数据库配置\n# =============================================================================\n\n# 数据库类型: sqlite | postgresql | mysql\ntype: sqlite\n\n# SQLite 数据库路径\ndatabase: ${database}\n\n# PostgreSQL/MySQL 配置(可选)\n# host: localhost\n# port: 5432\n# user: postgres\n# password: ''\n`\n }\n\n const user = dbType === 'postgresql' ? 'postgres' : 'root'\n\n return `# =============================================================================\n# 数据库配置\n# =============================================================================\n\n# 数据库类型: sqlite | postgresql | mysql\ntype: ${dbType}\n\n# 数据库名称\ndatabase: ${database}\n\n# 连接配置\nhost: ${host}\nport: ${port}\nuser: ${user}\npassword: ''\n`\n}\n\nfunction generateCacheConfig(cfg?: CacheModuleConfig): string {\n const cacheType = cfg?.type ?? 'memory'\n\n if (cacheType === 'memory') {\n return `# =============================================================================\n# 缓存配置\n# =============================================================================\n\n# 缓存类型: memory | redis\ntype: memory\n\n# Redis 配置(当 type 为 redis 时使用)\n# url: redis://localhost:6379/0\n# host: localhost\n# port: 6379\n# password: ''\n# keyPrefix: 'hai:'\n`\n }\n\n const host = cfg?.host ?? 'localhost'\n const port = cfg?.port ?? 6379\n\n return `# =============================================================================\n# 缓存配置\n# =============================================================================\n\n# 缓存类型: memory | redis\ntype: redis\n\n# Redis 配置\nhost: ${host}\nport: ${port}\npassword: ''\nkeyPrefix: 'hai:'\n`\n}\n\nfunction generateIamConfig(cfg?: IamModuleConfig): string {\n const loginPassword = cfg?.loginPassword ?? true\n const loginOtp = cfg?.loginOtp ?? false\n\n return `# =============================================================================\n# IAM 配置\n# =============================================================================\n\n# 登录配置\nlogin:\n password: ${loginPassword}\n otp: ${loginOtp}\n ldap: false\n\n# 密码策略\npassword:\n minLength: 8\n maxLength: 128\n requireUppercase: false\n requireLowercase: false\n requireNumber: false\n requireSpecialChar: false\n\n# 会话配置\nsession:\n maxAge: 86400 # 会话超时(秒),默认 24 小时\n sliding: true # 滑动窗口(每次请求刷新过期时间)\n singleDevice: false # 单设备登录\n refreshTokenMaxAge: 604800 # refreshToken 过期(秒),默认 7 天\n\n# RBAC 配置\nrbac:\n defaultRole: user\n\n# 是否自动创建默认数据\nseedDefaultData: true\n`\n}\n\nfunction generateStorageConfig(cfg?: StorageModuleConfig): string {\n if ((cfg?.type ?? 'local') === 'local') {\n return `# 存储配置;HAI_STORAGE 可整体替换为 S3 配置。\ntype: local\nroot: ${cfg?.localPath ?? './data/uploads'}\n`\n }\n return `# S3 存储配置\ntype: s3\nbucket: ''\nregion: us-east-1\naccessKeyId: ''\nsecretAccessKey: ''\nendpoint: https://s3.amazonaws.com\nforcePathStyle: false\n`\n}\n\nfunction generateAiConfig(cfg?: AiModuleConfig): string {\n const model = cfg?.model ?? 'gpt-4o-mini'\n\n // AIConfigSchema 结构:llm(必填)+ 可选子模块\n // LLMConfigSchema 支持顶层 apiKey/model,以及多模型 models 数组\n return `# =============================================================================\n# AI 配置\n# =============================================================================\n\n# LLM 配置(必填)\nllm:\n # API Key(约定环境变量 HAI_AI_LLM_APIKEY 优先)\n apiKey: ''\n # Base URL(约定环境变量 HAI_AI_LLM_BASEURL 优先)\n # baseUrl: https://api.openai.com/v1\n # 默认模型\n model: ${model}\n # 全局最大 Token 数(默认 4096)\n # maxTokens: 4096\n # 采样温度(0-2,默认 0.7)\n # temperature: 0.7\n # 请求超时(毫秒,默认 60000)\n # timeout: 60000\n\n # 多模型配置(可选,用于场景路由)\n # models:\n # - id: fast\n # model: gpt-4o-mini\n # - id: smart\n # model: gpt-4o\n # apiKey: ''\n\n # 场景模型映射(可选)\n # scenarios:\n # default: fast\n # analysis: smart\n`\n}\n\n/**\n * 根据选中的功能模块生成 .env.example 内容\n *\n * @param features - 选中的功能模块列表\n * @param configs - 用户自定义配置值\n * @returns .env.example 文件内容\n */\nexport function generateEnvExample(features: FeatureId[], configs?: ModuleConfigs): string {\n const sections: string[] = []\n\n // 文件头\n sections.push(`# =============================================================================\n# Environment Variables\n# =============================================================================\n#\n# Copy this file to .env and fill in actual values.\n# Config files use literal defaults; matching HAI variables override YAML leaves.\n#\n# Naming convention: HAI_<config name>_<YAML path>\n# =============================================================================`)\n\n // 应用基础(始终生成)\n sections.push(`\n# =============================================================================\n# Application\n# =============================================================================\n# Environment: development | production | test\nHAI_CORE_ENV=development\n# Enable debug mode\nHAI_CORE_DEBUG=false`)\n\n // 数据库\n if (features.includes('db')) {\n const dbType = configs?.db?.type ?? 'sqlite'\n const dbDatabase = configs?.db?.database ?? (dbType === 'sqlite' ? './data/app.db' : 'hai')\n const isSqlite = dbType === 'sqlite'\n sections.push(`\n# =============================================================================\n# Database (@h-ai/reldb)\n# =============================================================================\n# Database type: sqlite | postgresql | mysql\nHAI_DB_TYPE=${dbType}\n# SQLite: file path; PostgreSQL/MySQL: database name\nHAI_DB_DATABASE=${dbDatabase}${isSqlite\n ? `\n# PostgreSQL/MySQL connection (uncomment when not using sqlite)\n# HAI_DB_HOST=localhost\n# HAI_DB_PORT=5432\n# HAI_DB_USER=postgres\n# HAI_DB_PASSWORD=`\n : `\n# Connection\nHAI_DB_HOST=${configs?.db?.host ?? 'localhost'}\nHAI_DB_PORT=${configs?.db?.port ?? (dbType === 'postgresql' ? 5432 : 3306)}\nHAI_DB_USER=${dbType === 'postgresql' ? 'postgres' : 'root'}\nHAI_DB_PASSWORD=`}`)\n }\n\n // IAM / Session\n if (features.includes('iam')) {\n sections.push(`\n# =============================================================================\n# IAM (@h-ai/iam)\n# =============================================================================\n# HAI_IAM_PASSWORD_MINLENGTH=8\n# HAI_IAM_SESSION_MAXAGE=86400\n# HAI_IAM_SESSION_REFRESHTOKENMAXAGE=604800\n# HAI_KIT_COOKIE_KEY= # 32-char hex for SM4-CBC cookie encryption\n`)\n }\n\n // 缓存\n if (features.includes('cache')) {\n const cacheType = configs?.cache?.type ?? 'memory'\n if (cacheType === 'redis') {\n sections.push(`\n# =============================================================================\n# Cache (@h-ai/cache)\n# =============================================================================\n# Cache type: memory | redis\nHAI_CACHE_TYPE=redis\n# Redis connection\nHAI_CACHE_HOST=${configs?.cache?.host ?? 'localhost'}\nHAI_CACHE_PORT=${configs?.cache?.port ?? 6379}\nHAI_CACHE_PASSWORD=\n# HAI_CACHE_URL=redis://localhost:6379/0\n# HAI_CACHE_DB=0\n# HAI_CACHE_CONNECT_TIMEOUT=10000\n# HAI_CACHE_COMMAND_TIMEOUT=5000\n# HAI_CACHE_KEY_PREFIX=hai:`)\n }\n else {\n sections.push(`\n# =============================================================================\n# Cache (@h-ai/cache)\n# =============================================================================\n# Cache type: memory | redis\nHAI_CACHE_TYPE=memory\n# Redis connection (uncomment when type=redis)\n# HAI_CACHE_URL=redis://localhost:6379/0\n# HAI_CACHE_HOST=localhost\n# HAI_CACHE_PORT=6379\n# HAI_CACHE_PASSWORD=\n# HAI_CACHE_DB=0\n# HAI_CACHE_CONNECT_TIMEOUT=10000\n# HAI_CACHE_COMMAND_TIMEOUT=5000\n# HAI_CACHE_KEY_PREFIX=hai:`)\n }\n }\n\n // 存储\n if (features.includes('storage')) {\n const storageType = configs?.storage?.type ?? 'local'\n if (storageType === 's3') {\n sections.push(`\n# =============================================================================\n# Storage (@h-ai/storage)\n# =============================================================================\n# Default storage provider: local | s3\nHAI_STORAGE_TYPE=s3\n# S3 / S3-compatible\nHAI_STORAGE_BUCKET=\nHAI_STORAGE_REGION=us-east-1\nHAI_STORAGE_ACCESSKEYID=\nHAI_STORAGE_SECRETACCESSKEY=\n# HAI_STORAGE_ENDPOINT= # Custom endpoint for MinIO / Aliyun OSS etc.\n# HAI_STORAGE_FORCEPATHSTYLE=false\n# Local storage (uncomment when type=local)\n# HAI_STORAGE_ROOT=./data/uploads`)\n }\n else {\n sections.push(`\n# =============================================================================\n# Storage (@h-ai/storage)\n# =============================================================================\n# Default storage provider: local | s3\nHAI_STORAGE_TYPE=local\n# Local storage root path\nHAI_STORAGE_ROOT=${configs?.storage?.localPath ?? './data/uploads'}\n# S3 / S3-compatible (uncomment when type=s3)\n# HAI_STORAGE_BUCKET=\n# HAI_STORAGE_REGION=us-east-1\n# HAI_STORAGE_ACCESSKEYID=\n# HAI_STORAGE_SECRETACCESSKEY=\n# HAI_STORAGE_ENDPOINT= # Custom endpoint for MinIO / Aliyun OSS etc.\n# HAI_STORAGE_FORCEPATHSTYLE=false`)\n }\n }\n\n // AI\n if (features.includes('ai')) {\n const provider = configs?.ai?.defaultProvider ?? 'openai'\n if (provider === 'anthropic') {\n sections.push(`\n# =============================================================================\n# AI (@h-ai/ai)\n# =============================================================================\n# OpenAI-compatible API Key (Anthropic models via OpenAI-compatible proxy)\nHAI_AI_LLM_APIKEY=\n# HAI_AI_LLM_BASEURL= # Custom base URL\n# Optional compatibility fallback\n# OPENAI_API_KEY=`)\n }\n else if (provider === 'openai') {\n sections.push(`\n# =============================================================================\n# AI (@h-ai/ai)\n# =============================================================================\n# OpenAI / OpenAI-compatible\nHAI_AI_LLM_APIKEY=\n# HAI_AI_LLM_BASEURL= # Custom base URL (e.g. local proxy, Azure)\n# Optional compatibility fallback\n# OPENAI_API_KEY=`)\n }\n else {\n sections.push(`\n# =============================================================================\n# AI (@h-ai/ai)\n# =============================================================================\n# OpenAI-compatible provider\nHAI_AI_LLM_APIKEY=\n# HAI_AI_LLM_BASEURL= # Custom base URL\n# Optional compatibility fallback\n# OPENAI_API_KEY=`)\n }\n }\n\n // VecDB\n if (features.includes('vecdb')) {\n sections.push(`\n# =============================================================================\n# Vector Database (@h-ai/vecdb)\n# =============================================================================\n# Backend type: lancedb | pgvector | qdrant\nHAI_VECDB_TYPE=lancedb\n# LanceDB data path\nHAI_VECDB_PATH=./data/vecdb\n# Qdrant (uncomment when type=qdrant)\n# HAI_VECDB_URL=http://localhost:6333\n# HAI_VECDB_APIKEY=\n`)\n }\n\n // Reach\n if (features.includes('reach')) {\n sections.push(`\n# =============================================================================\n# Reach (@h-ai/reach)\n# =============================================================================\n# SMTP (uncomment when enabling email provider)\n# HAI_REACH_SMTP_HOST=smtp.example.com\n# HAI_REACH_SMTP_PORT=465\n# HAI_REACH_SMTP_USER=\n# HAI_REACH_SMTP_PASS=\n# HAI_REACH_SMTP_FROM=noreply@example.com\n`)\n }\n\n // Payment\n if (features.includes('payment')) {\n sections.push(`\n# =============================================================================\n# Payment (@h-ai/payment)\n# =============================================================================\n# WeChat Pay\n# HAI_PAYMENT_WECHAT_MCHID=\n# HAI_PAYMENT_WECHAT_APIV3KEY=\n# HAI_PAYMENT_WECHAT_SERIALNO=\n# HAI_PAYMENT_WECHAT_PRIVATEKEY=\n# HAI_PAYMENT_WECHAT_APPID=\n# Alipay\n# HAI_PAYMENT_ALIPAY_APPID=\n# HAI_PAYMENT_ALIPAY_PRIVATEKEY=\n# HAI_PAYMENT_ALIPAY_ALIPAYPUBLICKEY=\n# Stripe\n# HAI_PAYMENT_STRIPE_SECRETKEY=\n# HAI_PAYMENT_STRIPE_WEBHOOKSECRET=\n`)\n }\n\n // 测试(始终生成)\n sections.push(`\n# =============================================================================\n# Testing\n# =============================================================================\n# E2E test mode (relaxes rate limiting etc.)\n# HAI_E2E=1\n`)\n\n return sections.join('\\n')\n}\n\nfunction generateDeployConfig(): string {\n return `# =============================================================================\n# 部署配置 (@h-ai/deploy)\n# =============================================================================\n# 部署平台凭证通过 ~/.hai/credentials.yml 管理\n# 环境变量引用格式: \\${VAR_NAME:default_value}\n\n# 部署平台\nprovider:\n type: vercel\n token: \\${HAI_DEPLOY_VERCEL_TOKEN}\n\n# 基础设施服务(按需开启)\nservices:\n # PostgreSQL 数据库 (Neon)\n # db:\n # provisioner: neon\n # apiKey: \\${HAI_DEPLOY_NEON_API_KEY}\n\n # Redis 缓存 (Upstash)\n # cache:\n # provisioner: upstash\n # email: \\${HAI_DEPLOY_UPSTASH_EMAIL}\n # apiKey: \\${HAI_DEPLOY_UPSTASH_API_KEY}\n\n # S3 存储 (Cloudflare R2)\n # storage:\n # provisioner: cloudflare-r2\n # accountId: \\${HAI_DEPLOY_CF_ACCOUNT_ID}\n # apiToken: \\${HAI_DEPLOY_CF_API_TOKEN}\n # accessKeyId: \\${HAI_DEPLOY_R2_ACCESS_KEY_ID}\n # secretAccessKey: \\${HAI_DEPLOY_R2_SECRET_ACCESS_KEY}\n\n # 邮件 (Resend)\n # email:\n # provisioner: resend\n # apiKey: \\${HAI_DEPLOY_RESEND_API_KEY}\n # from: \\${HAI_DEPLOY_RESEND_FROM}\n\n # 短信 (阿里云)\n # sms:\n # provisioner: aliyun\n # accessKeyId: \\${HAI_DEPLOY_ALIYUN_ACCESS_KEY_ID}\n # accessKeySecret: \\${HAI_DEPLOY_ALIYUN_ACCESS_KEY_SECRET}\n # signName: \\${HAI_DEPLOY_ALIYUN_SIGN_NAME}\n`\n}\n\nfunction generateVecdbConfig(): string {\n return `# =============================================================================\n# 向量数据库配置 (@h-ai/vecdb)\n# =============================================================================\n\n# 数据库类型: lancedb | pgvector | qdrant\ntype: lancedb\n\n# LanceDB 数据目录\npath: ./data/vecdb\n\n# 距离度量: cosine | euclidean | dot\n# metric: cosine\n\n# pgvector 配置(当 type 为 pgvector 时使用)\n# type: pgvector\n# host: localhost\n# port: 5432\n# database: hai\n# user: postgres\n# password: ''\n# indexType: hnsw\n# tablePrefix: vec_\n\n# Qdrant 配置(当 type 为 qdrant 时使用)\n# type: qdrant\n# url: http://localhost:6333\n# apiKey: ''\n`\n}\n\nfunction generateReachConfig(): string {\n return `# =============================================================================\n# 消息推送配置 (@h-ai/reach)\n# =============================================================================\n\nproviders:\n # 控制台输出(开发/测试用)\n - name: console_dev\n type: console\n\n # SMTP 邮件(按需开启)\n # - name: email\n # type: smtp\n # host: \\${HAI_REACH_SMTP_HOST:smtp.example.com}\n # port: \\${HAI_REACH_SMTP_PORT:465}\n # secure: true\n # user: \\${HAI_REACH_SMTP_USER:}\n # pass: \\${HAI_REACH_SMTP_PASS:}\n # from: \\${HAI_REACH_SMTP_FROM:noreply@example.com}\n\n # 阿里云短信(按需开启)\n # - name: sms\n # type: aliyun-sms\n # accessKeyId: \\${HAI_REACH_SMS_ACCESS_KEY}\n # accessKeySecret: \\${HAI_REACH_SMS_SECRET_KEY}\n # signName: MyApp\n\n# 模板(可选)\n# templates:\n# - name: verification_code\n# provider: email\n# subject: \"验证码: {code}\"\n# body: \"您的验证码是 {code},有效期 {minutes} 分钟。\"\n\n# 免打扰(可选)\n# dnd:\n# enabled: true\n# strategy: delay\n# start: \"22:00\"\n# end: \"08:00\"\n`\n}\n\nfunction generateSchedulerConfig(): string {\n return `# =============================================================================\n# 任务调度配置 (@h-ai/scheduler)\n# =============================================================================\n\n# 是否启用数据库日志(需要 @h-ai/reldb)\nenableDb: true\n\n# 执行日志表名\ntableName: hai_scheduler_logs\n\n# 任务定义表名\ntaskTableName: hai_scheduler_tasks\n\n# 分布式锁表名\nlockTableName: hai_scheduler_locks\n\n# 检查间隔(毫秒)\ntickInterval: 1000\n\n# 锁过期时间(毫秒)\nlockExpireMs: 300000\n`\n}\n\nfunction generateAuditConfig(): string {\n return `# =============================================================================\n# 审计日志配置 (@h-ai/audit)\n# =============================================================================\n\n# 审计日志表名\ntableName: hai_audit_logs\n\n# 用户表名(用于 JOIN 查询)\nuserTable: hai_iam_users\n\n# 用户表主键列名\nuserIdColumn: id\n\n# 用户名列名\nuserNameColumn: username\n`\n}\n\nfunction generatePaymentConfig(): string {\n return `# =============================================================================\n# 支付配置 (@h-ai/payment)\n# =============================================================================\n\n# 微信支付(按需开启)\n# wechat:\n# mchId: ''\n# apiV3Key: ''\n# serialNo: ''\n# privateKey: ''\n# appId: ''\n\n# 支付宝(按需开启)\n# alipay:\n# appId: ''\n# privateKey: ''\n# alipayPublicKey: ''\n# signType: RSA2\n# sandbox: false\n\n# Stripe(按需开启)\n# stripe:\n# secretKey: ''\n# webhookSecret: ''\n`\n}\n"]}
|
package/dist/chunk-WUYLYCW2.js
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import fse from 'fs-extra';
|
|
5
|
-
import Handlebars from 'handlebars';
|
|
6
|
-
|
|
7
|
-
var MODULE_SKILL_MAP = {
|
|
8
|
-
"db": "hai-reldb",
|
|
9
|
-
"reldb": "hai-reldb",
|
|
10
|
-
"cache": "hai-cache",
|
|
11
|
-
"iam": "hai-iam",
|
|
12
|
-
"storage": "hai-storage",
|
|
13
|
-
"ai": "hai-ai",
|
|
14
|
-
"crypto": "hai-crypto",
|
|
15
|
-
"payment": "hai-payment",
|
|
16
|
-
"vecdb": "hai-vecdb",
|
|
17
|
-
"datapipe": "hai-datapipe",
|
|
18
|
-
"reach": "hai-reach",
|
|
19
|
-
"scheduler": "hai-scheduler",
|
|
20
|
-
"audit": "hai-audit",
|
|
21
|
-
"deploy": "hai-deploy",
|
|
22
|
-
"kit": "hai-kit",
|
|
23
|
-
"ui": "hai-ui",
|
|
24
|
-
"api-client": "hai-api-client",
|
|
25
|
-
"capacitor": "hai-capacitor"
|
|
26
|
-
};
|
|
27
|
-
var SKILL_EXCLUSIONS_BY_APP_TYPE = {
|
|
28
|
-
"admin": ["hai-serv", "hai-api-contract", "hai-api-client", "hai-capacitor"],
|
|
29
|
-
"website": ["hai-serv", "hai-api-contract", "hai-api-client", "hai-capacitor"],
|
|
30
|
-
"h5": ["hai-serv", "hai-api-contract", "hai-api-client", "hai-capacitor"],
|
|
31
|
-
"api": ["hai-ui", "hai-kit", "hai-capacitor"],
|
|
32
|
-
"mobile-app": ["hai-core", "hai-kit", "hai-serv", "hai-api-contract"],
|
|
33
|
-
"fullstack": ["hai-kit"]
|
|
34
|
-
};
|
|
35
|
-
var WORKFLOW_SKILLS = [
|
|
36
|
-
"hai-build",
|
|
37
|
-
"hai-app-create",
|
|
38
|
-
"hai-app-review",
|
|
39
|
-
"hai-app-tests"
|
|
40
|
-
];
|
|
41
|
-
var PACKAGE_SKILL_MAP = {
|
|
42
|
-
"@h-ai/api-contract": "hai-api-contract",
|
|
43
|
-
"@h-ai/api-client": "hai-api-client",
|
|
44
|
-
"@h-ai/core": "hai-core",
|
|
45
|
-
"@h-ai/serv": "hai-serv",
|
|
46
|
-
"@h-ai/kit": "hai-kit",
|
|
47
|
-
"@h-ai/ui": "hai-ui",
|
|
48
|
-
"@h-ai/reldb": "hai-reldb",
|
|
49
|
-
"@h-ai/cache": "hai-cache",
|
|
50
|
-
"@h-ai/iam": "hai-iam",
|
|
51
|
-
"@h-ai/storage": "hai-storage",
|
|
52
|
-
"@h-ai/ai": "hai-ai",
|
|
53
|
-
"@h-ai/crypto": "hai-crypto",
|
|
54
|
-
"@h-ai/payment": "hai-payment",
|
|
55
|
-
"@h-ai/vecdb": "hai-vecdb",
|
|
56
|
-
"@h-ai/datapipe": "hai-datapipe",
|
|
57
|
-
"@h-ai/reach": "hai-reach",
|
|
58
|
-
"@h-ai/scheduler": "hai-scheduler",
|
|
59
|
-
"@h-ai/audit": "hai-audit",
|
|
60
|
-
"@h-ai/deploy": "hai-deploy",
|
|
61
|
-
"@h-ai/capacitor": "hai-capacitor"
|
|
62
|
-
};
|
|
63
|
-
var SKILL_TARGET_DIRS = [".agents/skills"];
|
|
64
|
-
var BRIDGE_FILES = [
|
|
65
|
-
{
|
|
66
|
-
source: "copilot-instructions.md",
|
|
67
|
-
destination: ".github/copilot-instructions.md"
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
source: "CLAUDE.md",
|
|
71
|
-
destination: "CLAUDE.md"
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
source: "AGENTS.md",
|
|
75
|
-
destination: "AGENTS.md"
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
source: "opencode.json",
|
|
79
|
-
destination: "opencode.json"
|
|
80
|
-
}
|
|
81
|
-
];
|
|
82
|
-
var BRIDGE_PROFILE_REGEX = /^[a-z0-9-]+$/;
|
|
83
|
-
async function listTemplateSkillNames(templatesDir) {
|
|
84
|
-
const entries = await fse.readdir(templatesDir, { withFileTypes: true });
|
|
85
|
-
const skillNames = [];
|
|
86
|
-
for (const entry of entries) {
|
|
87
|
-
if (!entry.isDirectory() || !entry.name.startsWith("hai-")) {
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
const skillFilePath = path.join(templatesDir, entry.name, "SKILL.md");
|
|
91
|
-
if (await fse.pathExists(skillFilePath)) {
|
|
92
|
-
skillNames.push(entry.name);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return skillNames.sort((a, b) => a.localeCompare(b));
|
|
96
|
-
}
|
|
97
|
-
function resolveCompatibleSkillNames(skillNames, appType) {
|
|
98
|
-
const excludedSkills = new Set(appType ? SKILL_EXCLUSIONS_BY_APP_TYPE[appType] ?? [] : []);
|
|
99
|
-
return skillNames.filter((skillName) => !excludedSkills.has(skillName));
|
|
100
|
-
}
|
|
101
|
-
function getSkillTemplatesDir() {
|
|
102
|
-
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
103
|
-
const distPath = path.resolve(currentDir, "..", "templates", "skills");
|
|
104
|
-
if (fse.pathExistsSync(distPath)) {
|
|
105
|
-
return distPath;
|
|
106
|
-
}
|
|
107
|
-
return path.resolve(currentDir, "..", "..", "templates", "skills");
|
|
108
|
-
}
|
|
109
|
-
async function copySkill(templatesDir, skillName, projectPath, targetDirs = SKILL_TARGET_DIRS, overwrite = true) {
|
|
110
|
-
const src = path.join(templatesDir, skillName);
|
|
111
|
-
if (!await fse.pathExists(src)) {
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
-
const copiedFiles = [];
|
|
115
|
-
for (const targetDir of targetDirs) {
|
|
116
|
-
const dest = path.join(projectPath, targetDir, skillName);
|
|
117
|
-
const skillFilePath = path.join(dest, "SKILL.md");
|
|
118
|
-
if (!overwrite && await fse.pathExists(skillFilePath)) {
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
await fse.copy(src, dest, { overwrite: true });
|
|
122
|
-
copiedFiles.push(`${targetDir}/${skillName}/SKILL.md`);
|
|
123
|
-
}
|
|
124
|
-
return copiedFiles;
|
|
125
|
-
}
|
|
126
|
-
async function copySkills(templatesDir, skillNames, projectPath, targetDirs = SKILL_TARGET_DIRS, overwrite = true) {
|
|
127
|
-
const copiedFiles = [];
|
|
128
|
-
for (const skillName of new Set(skillNames)) {
|
|
129
|
-
copiedFiles.push(...await copySkill(templatesDir, skillName, projectPath, targetDirs, overwrite));
|
|
130
|
-
}
|
|
131
|
-
return copiedFiles;
|
|
132
|
-
}
|
|
133
|
-
async function copyBridgeFiles(templatesDir, projectPath, overwrite = true, appType, projectName) {
|
|
134
|
-
const copiedFiles = [];
|
|
135
|
-
for (const file of BRIDGE_FILES) {
|
|
136
|
-
const src = await resolveBridgeSource(templatesDir, file.source, appType, projectName !== void 0);
|
|
137
|
-
const dest = path.join(projectPath, file.destination);
|
|
138
|
-
if (!src) {
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
if (!overwrite && await fse.pathExists(dest)) {
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
await fse.ensureDir(path.dirname(dest));
|
|
145
|
-
if (src.endsWith(".hbs")) {
|
|
146
|
-
const template = await fse.readFile(src, "utf8");
|
|
147
|
-
const rendered = Handlebars.compile(template, { noEscape: true })({ projectName });
|
|
148
|
-
await fse.writeFile(dest, rendered, "utf8");
|
|
149
|
-
} else {
|
|
150
|
-
await fse.copy(src, dest, { overwrite });
|
|
151
|
-
}
|
|
152
|
-
copiedFiles.push(file.destination);
|
|
153
|
-
}
|
|
154
|
-
return copiedFiles;
|
|
155
|
-
}
|
|
156
|
-
async function resolveBridgeSource(templatesDir, source, appType, allowTemplate = false) {
|
|
157
|
-
const profile = appType && BRIDGE_PROFILE_REGEX.test(appType) ? appType : "generic";
|
|
158
|
-
const candidates = [
|
|
159
|
-
...allowTemplate ? [path.join(templatesDir, "bridges", profile, `${source}.hbs`)] : [],
|
|
160
|
-
path.join(templatesDir, "bridges", profile, source),
|
|
161
|
-
path.join(templatesDir, "bridges", "generic", source),
|
|
162
|
-
path.join(templatesDir, source)
|
|
163
|
-
];
|
|
164
|
-
for (const candidate of candidates) {
|
|
165
|
-
if (await fse.pathExists(candidate)) {
|
|
166
|
-
return candidate;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
async function generateSkillFiles(projectPath, _features, appType, projectName) {
|
|
172
|
-
const templatesDir = getSkillTemplatesDir();
|
|
173
|
-
if (!await fse.pathExists(templatesDir)) {
|
|
174
|
-
return [];
|
|
175
|
-
}
|
|
176
|
-
const copiedFiles = [];
|
|
177
|
-
const templateSkillNames = await listTemplateSkillNames(templatesDir);
|
|
178
|
-
const compatibleSkillNames = resolveCompatibleSkillNames(templateSkillNames, appType);
|
|
179
|
-
copiedFiles.push(...await copySkills(templatesDir, compatibleSkillNames, projectPath));
|
|
180
|
-
copiedFiles.push(...await copyBridgeFiles(templatesDir, projectPath, true, appType, projectName));
|
|
181
|
-
return copiedFiles;
|
|
182
|
-
}
|
|
183
|
-
async function generateModuleSkillFile(projectPath, moduleId) {
|
|
184
|
-
const skillName = MODULE_SKILL_MAP[moduleId];
|
|
185
|
-
if (!skillName) {
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
188
|
-
const templatesDir = getSkillTemplatesDir();
|
|
189
|
-
if (!await fse.pathExists(templatesDir)) {
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
192
|
-
const missingTargetDirs = [];
|
|
193
|
-
for (const targetDir of SKILL_TARGET_DIRS) {
|
|
194
|
-
const skillFilePath = path.join(projectPath, targetDir, skillName, "SKILL.md");
|
|
195
|
-
if (!await fse.pathExists(skillFilePath)) {
|
|
196
|
-
missingTargetDirs.push(targetDir);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
const copiedBridgeFiles = await copyBridgeFiles(templatesDir, projectPath, false);
|
|
200
|
-
if (missingTargetDirs.length === 0) {
|
|
201
|
-
return copiedBridgeFiles.length > 0 ? copiedBridgeFiles : null;
|
|
202
|
-
}
|
|
203
|
-
const copiedSkillFiles = await copySkill(templatesDir, skillName, projectPath, missingTargetDirs);
|
|
204
|
-
const copiedFiles = [...copiedBridgeFiles, ...copiedSkillFiles];
|
|
205
|
-
if (copiedFiles.length === 0) {
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
return copiedFiles;
|
|
209
|
-
}
|
|
210
|
-
function resolveSkillNamesForPackages(packageNames) {
|
|
211
|
-
const skillNames = [...WORKFLOW_SKILLS];
|
|
212
|
-
for (const packageName of packageNames) {
|
|
213
|
-
const skillName = PACKAGE_SKILL_MAP[packageName];
|
|
214
|
-
if (skillName) {
|
|
215
|
-
skillNames.push(skillName);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return [...new Set(skillNames)];
|
|
219
|
-
}
|
|
220
|
-
async function generateProjectAiSupport(projectPath, skillNames, options = {}) {
|
|
221
|
-
const templatesDir = getSkillTemplatesDir();
|
|
222
|
-
if (!await fse.pathExists(templatesDir)) {
|
|
223
|
-
return [];
|
|
224
|
-
}
|
|
225
|
-
const {
|
|
226
|
-
overwriteSkills = true,
|
|
227
|
-
overwriteBridgeFiles = true
|
|
228
|
-
} = options;
|
|
229
|
-
const copiedFiles = [];
|
|
230
|
-
copiedFiles.push(...await copySkills(templatesDir, skillNames, projectPath, SKILL_TARGET_DIRS, overwriteSkills));
|
|
231
|
-
copiedFiles.push(...await copyBridgeFiles(templatesDir, projectPath, overwriteBridgeFiles, options.appType));
|
|
232
|
-
return copiedFiles;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
export { generateModuleSkillFile, generateProjectAiSupport, generateSkillFiles, resolveCompatibleSkillNames, resolveSkillNamesForPackages };
|
|
236
|
-
//# sourceMappingURL=chunk-WUYLYCW2.js.map
|
|
237
|
-
//# sourceMappingURL=chunk-WUYLYCW2.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/cli-skill-templates.ts"],"names":[],"mappings":"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","file":"chunk-WUYLYCW2.js","sourcesContent":["/**\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'\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\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\n for (const targetDir of targetDirs) {\n const dest = path.join(projectPath, targetDir, skillName)\n const skillFilePath = path.join(dest, 'SKILL.md')\n\n if (!overwrite && await fse.pathExists(skillFilePath)) {\n continue\n }\n\n await fse.copy(src, dest, { overwrite: true })\n copiedFiles.push(`${targetDir}/${skillName}/SKILL.md`)\n }\n\n return copiedFiles\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 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 * 在 `hai add <module>` 时调用,仅复制对应模块的 Skill 文件。\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 templatesDir = getSkillTemplatesDir()\n if (!(await fse.pathExists(templatesDir))) {\n return null\n }\n\n const missingTargetDirs: string[] = []\n\n for (const targetDir of SKILL_TARGET_DIRS) {\n const skillFilePath = path.join(projectPath, targetDir, skillName, 'SKILL.md')\n if (!(await fse.pathExists(skillFilePath))) {\n missingTargetDirs.push(targetDir)\n }\n }\n\n const copiedBridgeFiles = await copyBridgeFiles(templatesDir, projectPath, false)\n if (missingTargetDirs.length === 0) {\n return copiedBridgeFiles.length > 0 ? copiedBridgeFiles : null\n }\n\n const copiedSkillFiles = await copySkill(templatesDir, skillName, projectPath, missingTargetDirs)\n const copiedFiles = [...copiedBridgeFiles, ...copiedSkillFiles]\n\n if (copiedFiles.length === 0) {\n return null\n }\n\n return copiedFiles\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 } = {},\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 copiedFiles: string[] = []\n copiedFiles.push(...await copySkills(templatesDir, skillNames, projectPath, SKILL_TARGET_DIRS, overwriteSkills))\n copiedFiles.push(...await copyBridgeFiles(templatesDir, projectPath, overwriteBridgeFiles, options.appType))\n\n return copiedFiles\n}\n"]}
|