@h-ai/cli 0.1.0-alpha.19 → 0.1.0-alpha.21

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.
Files changed (219) hide show
  1. package/README.md +10 -7
  2. package/dist/{chunk-GSMXGUYF.js → chunk-5IGMJVV5.js} +56 -38
  3. package/dist/chunk-5IGMJVV5.js.map +1 -0
  4. package/dist/{chunk-BPO34NRL.js → chunk-CFCXOKGV.js} +27 -5
  5. package/dist/chunk-CFCXOKGV.js.map +1 -0
  6. package/dist/cli-config-templates-5J5VKWWD.js +5 -0
  7. package/dist/{cli-config-templates-JCS7T3NU.js.map → cli-config-templates-5J5VKWWD.js.map} +1 -1
  8. package/dist/cli-skill-templates-JMSV2SVW.js +5 -0
  9. package/dist/{cli-skill-templates-IMYZPQRG.js.map → cli-skill-templates-JMSV2SVW.js.map} +1 -1
  10. package/dist/index.js +627 -107
  11. package/dist/index.js.map +1 -1
  12. package/dist/{src-N4NPS7AG.js → src-QNEJJ3BV.js} +261 -103
  13. package/dist/src-QNEJJ3BV.js.map +1 -0
  14. package/package.json +3 -2
  15. package/templates/README.md +40 -29
  16. package/templates/apps/admin/messages/en-US.json +5 -0
  17. package/templates/apps/admin/messages/zh-CN.json +5 -0
  18. package/templates/apps/admin/src/routes/+layout.svelte.hbs +5 -5
  19. package/templates/apps/admin/src/routes/+page.svelte.hbs +32 -12
  20. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +37 -20
  21. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +33 -24
  22. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +16 -13
  23. package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +16 -0
  24. package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +38 -0
  25. package/templates/apps/api/apps/{{projectName}}-contract/src/app-contract.ts.hbs +27 -0
  26. package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +33 -0
  27. package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +3 -0
  28. package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +12 -0
  29. package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +18 -0
  30. package/templates/apps/api/apps/{{projectName}}-contract/tsconfig.json.hbs +12 -0
  31. package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +11 -0
  32. package/templates/apps/api/apps/{{projectName}}-contract/vitest.config.ts.hbs +9 -0
  33. package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +17 -0
  34. package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +19 -0
  35. package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +26 -0
  36. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +55 -0
  37. package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +98 -0
  38. package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +21 -0
  39. package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +184 -0
  40. package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/app-procedures.ts.hbs +49 -0
  41. package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +18 -0
  42. package/templates/apps/api/apps/{{projectName}}-service/tests/service.test.ts.hbs +168 -0
  43. package/templates/apps/api/apps/{{projectName}}-service/tsconfig.json.hbs +16 -0
  44. package/templates/apps/api/apps/{{projectName}}-service/tsup.config.ts.hbs +11 -0
  45. package/templates/apps/api/apps/{{projectName}}-service/vitest.config.ts.hbs +9 -0
  46. package/templates/apps/api/e2e/api-service.spec.ts.hbs +18 -0
  47. package/templates/apps/api/eslint.config.js +32 -0
  48. package/templates/apps/api/package.json.hbs +29 -0
  49. package/templates/apps/api/playwright.config.ts.hbs +58 -0
  50. package/templates/apps/api/pnpm-workspace.yaml.hbs +14 -0
  51. package/templates/apps/api/tsconfig.base.json +15 -0
  52. package/templates/apps/fullstack/apps/{{projectName}}-app/.env.example.hbs +10 -0
  53. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +24 -7
  54. package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +18 -0
  55. package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +1 -1
  56. package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +16 -0
  57. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/en-US.json.hbs +6 -0
  58. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/zh-CN.json.hbs +6 -0
  59. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +51 -29
  60. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +186 -0
  61. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.css.hbs +2 -0
  62. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.d.ts.hbs +0 -1
  63. package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +18 -0
  64. package/templates/apps/fullstack/apps/{{projectName}}-app/src/main.ts.hbs +14 -0
  65. package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +1 -5
  66. package/templates/apps/fullstack/apps/{{projectName}}-app/tsconfig.json.hbs +10 -4
  67. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +4 -3
  68. package/templates/apps/fullstack/apps/{{projectName}}-desktop/.env.example.hbs +6 -0
  69. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +20 -6
  70. package/templates/apps/fullstack/apps/{{projectName}}-desktop/eslint.config.js.hbs +1 -1
  71. package/templates/apps/fullstack/apps/{{projectName}}-desktop/index.html.hbs +15 -0
  72. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +33 -30
  73. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +152 -0
  74. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.d.ts.hbs +1 -2
  75. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/main.ts.hbs +13 -0
  76. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/Cargo.toml.hbs +22 -0
  77. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/build.rs.hbs +6 -0
  78. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/capabilities/default.json.hbs +12 -0
  79. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128.png +0 -0
  80. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128@2x.png +0 -0
  81. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/32x32.png +0 -0
  82. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.icns +0 -0
  83. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.ico +0 -0
  84. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +16 -0
  85. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/main.rs.hbs +8 -0
  86. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +41 -0
  87. package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +2 -8
  88. package/templates/apps/fullstack/apps/{{projectName}}-desktop/tsconfig.json.hbs +9 -4
  89. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +44 -21
  90. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +15 -6
  91. package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +1 -1
  92. package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +15 -0
  93. package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +25 -27
  94. package/templates/apps/fullstack/apps/{{projectName}}-web/src/App.svelte.hbs +153 -0
  95. package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.d.ts.hbs +0 -1
  96. package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +14 -0
  97. package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +1 -5
  98. package/templates/apps/fullstack/apps/{{projectName}}-web/tsconfig.json.hbs +10 -4
  99. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +3 -2
  100. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +2 -1
  101. package/templates/apps/fullstack/eslint.config.js +0 -1
  102. package/templates/apps/fullstack/package.json.hbs +18 -17
  103. package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +3 -3
  104. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +5 -2
  105. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +14 -0
  106. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +13 -0
  107. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +2 -2
  108. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/{server-app.ts.hbs → app-server.ts.hbs} +23 -3
  109. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +4 -1
  110. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/{server-app.test.ts.hbs → app-server.test.ts.hbs} +1 -1
  111. package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +9 -8
  112. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/index.ts.hbs +1 -1
  113. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +5 -1
  114. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ThemeSwitcher.svelte.hbs +1 -1
  115. package/templates/apps/fullstack/packages/{{projectName}}-shared/tsconfig.json.hbs +1 -1
  116. package/templates/apps/fullstack/playwright.config.ts.hbs +41 -4
  117. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +15 -0
  118. package/templates/apps/h5/messages/en-US.json +3 -0
  119. package/templates/apps/h5/messages/zh-CN.json +3 -0
  120. package/templates/apps/h5/src/routes/+layout.svelte.hbs +30 -29
  121. package/templates/apps/h5/src/routes/+page.svelte.hbs +17 -6
  122. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +17 -6
  123. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +17 -6
  124. package/templates/apps/mobile-app/.env.hbs +3 -0
  125. package/templates/apps/{android-app → mobile-app}/capacitor.config.ts.hbs +2 -2
  126. package/templates/apps/mobile-app/index.html.hbs +13 -0
  127. package/templates/apps/mobile-app/messages/en-US.json +36 -0
  128. package/templates/apps/mobile-app/messages/zh-CN.json +36 -0
  129. package/templates/apps/mobile-app/src/App.svelte.hbs +162 -0
  130. package/templates/apps/mobile-app/src/lib/api.ts.hbs +54 -0
  131. package/templates/apps/{android-app → mobile-app}/src/lib/capacitor.ts +6 -7
  132. package/templates/apps/mobile-app/src/main.ts.hbs +11 -0
  133. package/templates/apps/website/src/routes/+layout.svelte.hbs +35 -26
  134. package/templates/apps/website/src/routes/+page.svelte.hbs +28 -10
  135. package/templates/apps/website/src/routes/about/+page.svelte.hbs +11 -6
  136. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +37 -34
  137. package/templates/apps/website/src/routes/services/+page.svelte.hbs +27 -40
  138. package/templates/base/.gitignore.hbs +16 -0
  139. package/templates/base/{eslint.config.js → eslint.config.js.hbs} +4 -1
  140. package/templates/base/package.json.hbs +55 -31
  141. package/templates/base/playwright.config.ts +30 -3
  142. package/templates/base/pnpm-workspace.yaml.hbs +14 -0
  143. package/templates/base/src/app.d.ts.hbs +3 -0
  144. package/templates/base/src/app.html.hbs +7 -0
  145. package/templates/base/src/hooks.server.ts.hbs +17 -7
  146. package/templates/base/src/lib/server/init.ts.hbs +20 -12
  147. package/templates/base/static/favicon.png +0 -0
  148. package/templates/base/svelte.config.js.hbs +43 -3
  149. package/templates/base/tests/scaffold.test.ts.hbs +16 -1
  150. package/templates/base/tsconfig.json.hbs +50 -0
  151. package/templates/base/vite.config.ts.hbs +13 -3
  152. package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +5 -5
  153. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +34 -32
  154. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +46 -48
  155. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +35 -31
  156. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +41 -38
  157. package/templates/skills/AGENTS.md +60 -17
  158. package/templates/skills/CLAUDE.md +18 -6
  159. package/templates/skills/bridges/admin/AGENTS.md +66 -0
  160. package/templates/skills/bridges/admin/CLAUDE.md +19 -0
  161. package/templates/skills/bridges/admin/copilot-instructions.md +46 -0
  162. package/templates/skills/bridges/api/AGENTS.md +65 -0
  163. package/templates/skills/bridges/api/CLAUDE.md +19 -0
  164. package/templates/skills/bridges/api/copilot-instructions.md +46 -0
  165. package/templates/skills/bridges/fullstack/AGENTS.md +84 -0
  166. package/templates/skills/bridges/fullstack/CLAUDE.md +19 -0
  167. package/templates/skills/bridges/fullstack/copilot-instructions.md +54 -0
  168. package/templates/skills/bridges/h5/AGENTS.md +65 -0
  169. package/templates/skills/bridges/h5/CLAUDE.md +19 -0
  170. package/templates/skills/bridges/h5/copilot-instructions.md +44 -0
  171. package/templates/skills/bridges/mobile-app/AGENTS.md +77 -0
  172. package/templates/skills/bridges/mobile-app/CLAUDE.md +19 -0
  173. package/templates/skills/bridges/mobile-app/copilot-instructions.md +45 -0
  174. package/templates/skills/bridges/website/AGENTS.md +65 -0
  175. package/templates/skills/bridges/website/CLAUDE.md +19 -0
  176. package/templates/skills/bridges/website/copilot-instructions.md +44 -0
  177. package/templates/skills/copilot-instructions.md +29 -50
  178. package/templates/skills/hai-ai/SKILL.md +115 -648
  179. package/templates/skills/hai-ai/reference.md +197 -0
  180. package/templates/skills/hai-api-client/SKILL.md +3 -1
  181. package/templates/skills/hai-app-create/SKILL.md +2 -1
  182. package/templates/skills/hai-app-review/SKILL.md +2 -2
  183. package/templates/skills/hai-app-tests/SKILL.md +28 -3
  184. package/templates/skills/hai-capacitor/SKILL.md +14 -22
  185. package/templates/skills/hai-deploy/SKILL.md +12 -9
  186. package/templates/skills/hai-kit/SKILL.md +5 -3
  187. package/templates/skills/hai-serv/SKILL.md +2 -0
  188. package/templates/skills/hai-ui/SKILL.md +25 -11
  189. package/templates/skills/opencode.json +1 -6
  190. package/dist/chunk-BPO34NRL.js.map +0 -1
  191. package/dist/chunk-GSMXGUYF.js.map +0 -1
  192. package/dist/cli-config-templates-JCS7T3NU.js +0 -5
  193. package/dist/cli-skill-templates-IMYZPQRG.js +0 -5
  194. package/dist/src-N4NPS7AG.js.map +0 -1
  195. package/templates/apps/android-app/.env.hbs +0 -3
  196. package/templates/apps/android-app/messages/en-US.json +0 -24
  197. package/templates/apps/android-app/messages/zh-CN.json +0 -24
  198. package/templates/apps/android-app/src/lib/api.ts.hbs +0 -31
  199. package/templates/apps/android-app/src/routes/+layout.svelte.hbs +0 -42
  200. package/templates/apps/android-app/src/routes/+layout.ts +0 -7
  201. package/templates/apps/android-app/src/routes/+page.svelte.hbs +0 -27
  202. package/templates/apps/api/src/routes/+layout.svelte +0 -14
  203. package/templates/apps/api/src/routes/+page.svelte +0 -11
  204. package/templates/apps/api/src/routes/api/v1/health/+server.ts +0 -14
  205. package/templates/apps/api/src/routes/api/v1/items/+server.ts +0 -37
  206. package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +0 -30
  207. package/templates/apps/api/tests/health.test.ts.hbs +0 -26
  208. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.html.hbs +0 -14
  209. package/templates/apps/fullstack/apps/{{projectName}}-app/src/routes/+layout.svelte.hbs +0 -18
  210. package/templates/apps/fullstack/apps/{{projectName}}-app/src/routes/+page.svelte.hbs +0 -162
  211. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.html.hbs +0 -14
  212. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/routes/+layout.svelte.hbs +0 -18
  213. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/routes/+page.svelte.hbs +0 -162
  214. package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.html.hbs +0 -14
  215. package/templates/apps/fullstack/apps/{{projectName}}-web/src/routes/+layout.svelte.hbs +0 -18
  216. package/templates/apps/fullstack/apps/{{projectName}}-web/src/routes/+page.svelte.hbs +0 -162
  217. package/templates/apps/fullstack/pnpm-workspace.yaml +0 -7
  218. package/templates/base/tsconfig.json +0 -20
  219. /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/{theme.svelte.ts.hbs → theme-store.svelte.ts.hbs} +0 -0
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.19",
4
+ "version": "0.1.0-alpha.21",
5
5
  "description": "Hai Framework CLI for project scaffolding and code generation.",
6
6
  "license": "Apache-2.0",
7
7
  "keywords": [
@@ -34,7 +34,7 @@
34
34
  "ora": "^9.1.0",
35
35
  "prompts": "^2.4.2",
36
36
  "yaml": "^2.7.0",
37
- "@h-ai/core": "0.1.0-alpha.19"
37
+ "@h-ai/core": "0.1.0-alpha.21"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/fs-extra": "^11.0.4",
@@ -52,6 +52,7 @@
52
52
  "lint": "eslint .",
53
53
  "lint:fix": "eslint . --fix",
54
54
  "test": "vitest run",
55
+ "test:e2e": "pnpm test:scaffold-gates",
55
56
  "test:scaffold-gates": "pnpm build && node tests/run-scaffold-gates.mjs",
56
57
  "test:watch": "vitest",
57
58
  "test:coverage": "vitest run --coverage",
@@ -9,13 +9,13 @@ templates/
9
9
  ├── base/ # 所有应用类型共用的骨架文件
10
10
  │ ├── package.json.hbs # 动态:按 features 注入依赖
11
11
  │ ├── vite.config.ts.hbs # 动态:按 hasI18n / hasUi 注入插件
12
- │ ├── tsconfig.json
13
- │ ├── svelte.config.js
14
- │ ├── .gitignore
12
+ │ ├── tsconfig.json.hbs # 动态:SvelteKit / 纯 Svelte 5 分支
13
+ │ ├── svelte.config.js.hbs # 动态:SvelteKit / 纯 Svelte 5 分支
14
+ │ ├── .gitignore.hbs # 动态:纯 Svelte 5 应用不输出 .svelte-kit
15
15
  │ ├── playwright.config.ts
16
16
  │ └── src/
17
- │ ├── app.html.hbs # 动态:i18n 时输出 %lang% 占位符
18
- │ ├── app.d.ts
17
+ │ ├── app.html.hbs # 动态:SvelteKit 应用输出 app shell
18
+ │ ├── app.d.ts.hbs # 动态:SvelteKit 应用输出 App namespace
19
19
  │ ├── app.css
20
20
  │ ├── hooks.server.ts.hbs # 动态:按 features / hasI18n 组装 handle 序列
21
21
  │ └── lib/server/
@@ -33,6 +33,10 @@ templates/
33
33
  │ │ └── src/routes/
34
34
  │ ├── api/ # 纯 API 服务(无 UI / 无 i18n)
35
35
  │ │ └── src/routes/
36
+ │ ├── mobile-app/ # Svelte 5 + Vite + Capacitor 移动应用
37
+ │ │ ├── index.html.hbs
38
+ │ │ ├── messages/
39
+ │ │ └── src/App.svelte.hbs
36
40
  │ └── fullstack/ # 前后端分离多包工程(contract / serv / web / app / desktop / miniapp 占位)
37
41
  │ ├── packages/ # 共享 contract 与后端 serv 包
38
42
  │ ├── apps/ # 多前端目标,按用户选择条件生成
@@ -52,17 +56,20 @@ templates/
52
56
  │ └── project.inlang/
53
57
  │ └── settings.json # Paraglide 项目配置
54
58
  │
55
- └── skills/ # AI Skill 单一来源模板(分发到 .agents/skills/)
59
+ └── skills/ # AI Skill 与各 appType 桥接指引
60
+ └── bridges/ # admin / api / website / h5 / mobile-app / fullstack 专属 AGENTS/Copilot/Claude
56
61
  ```
57
62
 
58
63
  ## AI Skill 模板(Single Source of Truth)
59
64
 
60
65
  `templates/skills/` 是 CLI 生成共享 AI 上下文时的单一来源,同时用于 `hai create` 与 `hai add`。
61
66
 
62
- - 每个 Skill 目录会复制到 `.agents/skills/<skill>/`
63
- - `copilot-instructions.md` 会复制到 `.github/copilot-instructions.md`,并引导 Copilot/其他助手读取 `.agents/skills/`
64
- - `AGENTS.md`、`CLAUDE.md`、`opencode.json` 会复制到项目根目录
65
- - `opencode.json` 使用 OpenCode 的 `instructions` 与 `skills.paths` 配置,将项目级指令与 Skill 目录显式指向 `AGENTS.md` 和 `.agents/skills/`
67
+ - `hai create` 会把 `templates/skills/` 下所有 `hai-*` Skill 目录复制到 `.agents/skills/<skill>/`,再按 appType 排除互斥项(例如 fullstack 不复制 `hai-kit`;admin / website / h5 不复制 `hai-serv`、`hai-api-contract`、`hai-api-client`、`hai-capacitor`;mobile-app 不复制 `hai-core`、`hai-kit`、`hai-serv`、`hai-api-contract`)
68
+ - `bridges/<appType>/copilot-instructions.md` 会复制到 `.github/copilot-instructions.md`;没有专属文件时回退到通用桥接文件
69
+ - `bridges/<appType>/AGENTS.md`、`bridges/<appType>/CLAUDE.md` 会复制到项目根目录,确保不同样板工程使用自己的 AI 指引
70
+ - `opencode.json` 会复制到项目根目录
71
+ - `templates/skills/AGENTS.md`、`CLAUDE.md`、`copilot-instructions.md` 与 `opencode.json` 不是多余副本:它们是 generic fallback,主要服务于缺少 appType 上下文时的桥接复制与 `hai add` 回填 AI 支持
72
+ - `opencode.json` 使用 OpenCode 的 `instructions` 配置复用 `.github/copilot-instructions.md`;`.agents/skills/` 由 OpenCode 原生发现,不再写入过期的 `skills.paths`
66
73
  - `CLAUDE.md` 作为 Claude Code 的原生项目指引入口,并通过 `@AGENTS.md` 复用共享规范;`.agents/skills/` 仍是共享参考目录,而非 Claude Code 的原生 project skills 目录
67
74
  - `hai add` 只补齐缺失的 AI 支持文件,不覆盖用户已自定义的桥接文件,也不会自动删除遗留的 `.github/skills/`
68
75
 
@@ -77,23 +84,27 @@ templates/
77
84
 
78
85
  所有 `.hbs` 文件可使用以下变量:
79
86
 
80
- | 变量 | 类型 | 说明 |
81
- | ---------------------- | --------- | -------------------------------------------- |
82
- | `{{projectName}}` | `string` | 项目名称(如 `my-app`) |
83
- | `{{appType}}` | `string` | 应用类型:`admin` / `website` / `h5` / `api` |
84
- | `{{hasUi}}` | `boolean` | 是否包含 UI(非 `api` 类型为 `true`) |
85
- | `{{hasI18n}}` | `boolean` | 是否启用 i18n(非 `api` 类型为 `true`) |
86
- | `{{defaultLocale}}` | `string` | 默认语言,如 `zh-CN` |
87
- | `{{packageManager}}` | `string` | 包管理器:`pnpm` / `npm` / `yarn` |
88
- | `{{fullstack.*}}` | `object` | fullstack 类型专用上下文 |
89
- | `{{features.iam}}` | `boolean` | 是否选中 iam feature |
90
- | `{{features.db}}` | `boolean` | 是否选中 db feature |
91
- | `{{features.cache}}` | `boolean` | 是否选中 cache feature |
92
- | `{{features.crypto}}` | `boolean` | 是否选中 crypto feature |
93
- | `{{features.storage}}` | `boolean` | 是否选中 storage feature |
94
- | `{{features.ai}}` | `boolean` | 是否选中 ai feature |
95
-
96
- `fullstack.*` 包含包名、前端选择、依赖版本和 `contractExportName`,用于渲染 contract / serv / 多端 UI 的模板。
87
+ | 变量 | 类型 | 说明 |
88
+ | ---------------------- | --------- | ----------------------------------------- |
89
+ | `{{projectName}}` | `string` | 项目名称(如 `my-app`) |
90
+ | `{{appType}}` | `string` | 应用类型 |
91
+ | `{{hasUi}}` | `boolean` | 是否包含 UI(非 `api` 类型为 `true`) |
92
+ | `{{hasI18n}}` | `boolean` | 是否启用 i18n(非 `api` 类型为 `true`) |
93
+ | `{{isSvelteOnlyApp}}` | `boolean` | 是否为不依赖 SvelteKit 的纯 Svelte 5 应用 |
94
+ | `{{isCapacitorApp}}` | `boolean` | 是否为 Capacitor 移动应用 |
95
+ | `{{defaultLocale}}` | `string` | 默认语言,如 `zh-CN` |
96
+ | `{{packageManager}}` | `string` | 包管理器:`pnpm` / `npm` / `yarn` |
97
+ | `{{fullstack.*}}` | `object` | fullstack 类型专用上下文 |
98
+ | `{{features.iam}}` | `boolean` | 是否选中 iam feature |
99
+ | `{{features.db}}` | `boolean` | 是否选中 db feature |
100
+ | `{{features.cache}}` | `boolean` | 是否选中 cache feature |
101
+ | `{{features.crypto}}` | `boolean` | 是否选中 crypto feature |
102
+ | `{{features.storage}}` | `boolean` | 是否选中 storage feature |
103
+ | `{{features.ai}}` | `boolean` | 是否选中 ai feature |
104
+
105
+ `appType` 支持 `admin` / `website` / `h5` / `api` / `mobile-app` / `fullstack`。
106
+
107
+ `fullstack.*` 包含包名、前端选择、依赖版本、`contractExportName` 和原生壳 `nativeAppIdSegment`,用于渲染 contract / serv / 多端 UI / Capacitor / Tauri 模板。
97
108
 
98
109
  ### 常用条件写法
99
110
 
@@ -116,8 +127,8 @@ import { iam } from '@h-ai/iam'
116
127
  模板引擎执行 6 个步骤:
117
128
 
118
129
  1. **复制 `base/` 静态文件** → 项目根
119
- 2. **复制 `apps/{appType}/` 路由** → 项目根(`messages/` 仅 `hasI18n` 时复制)
120
- 3. **叠加 feature 静态路由** → `src/routes/`
130
+ 2. **复制 `apps/{appType}/` 专属文件** → 项目根(`messages/` 仅 `hasI18n` 时复制)
131
+ 3. **叠加 feature 静态路由** → `src/routes/`(纯 Svelte 5 应用跳过 SvelteKit 路由)
121
132
  4. **渲染所有 `.hbs` 文件** — `base/` + `apps/{appType}/` 输出到项目根;`features/*/routes*/` 输出到 `src/routes/`
122
133
  5. **复制 `i18n/` 脚手架** → 项目根(仅 `hasI18n` 时)
123
134
  6. **确保 `static/` 目录存在**
@@ -8,10 +8,15 @@
8
8
  "nav_admin": "Enter Admin",
9
9
  "page_home_title": "Admin Console",
10
10
  "page_home_subtitle": "Built with hai Agent Framework",
11
+ "page_home_badge": "Powered by hai",
11
12
  "page_dashboard_title": "Dashboard",
13
+ "page_dashboard_subtitle": "Overview of system operations",
12
14
  "page_dashboard_total_users": "Total Users",
13
15
  "page_dashboard_today_active": "Today Active",
16
+ "page_dashboard_revenue": "Monthly Revenue",
17
+ "page_dashboard_orders": "Pending Orders",
14
18
  "page_settings_title": "System Settings",
19
+ "page_settings_subtitle": "Manage system configuration",
15
20
  "page_settings_basic": "Basic Configuration",
16
21
  "page_settings_desc": "Manage system settings here.",
17
22
  "auth_login_title": "Login",
@@ -8,10 +8,15 @@
8
8
  "nav_admin": "进入后台",
9
9
  "page_home_title": "管理后台",
10
10
  "page_home_subtitle": "基于 hai Agent Framework 构建",
11
+ "page_home_badge": "由 hai 驱动",
11
12
  "page_dashboard_title": "仪表盘",
13
+ "page_dashboard_subtitle": "系统运营数据总览",
12
14
  "page_dashboard_total_users": "总用户",
13
15
  "page_dashboard_today_active": "今日活跃",
16
+ "page_dashboard_revenue": "本月营收",
17
+ "page_dashboard_orders": "待处理订单",
14
18
  "page_settings_title": "系统设置",
19
+ "page_settings_subtitle": "管理系统配置项",
15
20
  "page_settings_basic": "基本配置",
16
21
  "page_settings_desc": "在此管理系统设置。",
17
22
  "auth_login_title": "登录",
@@ -1,21 +1,21 @@
1
1
  {{!-- 管理后台根布局 --}}
2
- <script lang="ts">
2
+ <script lang='ts'>
3
3
  /**
4
4
  * 管理后台根布局
5
5
  */
6
6
  import type { Snippet } from 'svelte'
7
- import '../app.css'
8
7
  {{#if hasI18n}}
9
8
  import { browser } from '$app/environment'
10
- import { setGlobalLocale } from '@h-ai/ui'
11
9
  import { getLocale } from '$lib/paraglide/runtime.js'
10
+ import { setGlobalLocale } from '@h-ai/ui'
12
11
  {{/if}}
12
+ import '../app.css'
13
13
 
14
14
  interface Props {
15
15
  children: Snippet
16
16
  }
17
17
 
18
- let { children }: Props = $props()
18
+ const { children }: Props = $props()
19
19
  {{#if hasI18n}}
20
20
 
21
21
  if (browser) {
@@ -25,6 +25,6 @@
25
25
  {{/if}}
26
26
  </script>
27
27
 
28
- <div class="min-h-screen bg-base-200">
28
+ <div class='min-h-screen bg-base-200'>
29
29
  {@render children()}
30
30
  </div>
@@ -1,11 +1,13 @@
1
1
  {{!-- 管理后台首页 --}}
2
- <script lang="ts">
2
+ <script lang='ts'>
3
3
  /**
4
4
  * 管理后台首页 — 入口导航
5
5
  */
6
+ import { resolve } from '$app/paths'
6
7
  {{#if hasI18n}}
7
8
  import * as m from '$lib/paraglide/messages'
8
9
  {{/if}}
10
+ import { Badge, Card } from '@h-ai/ui'
9
11
  </script>
10
12
 
11
13
  <svelte:head>
@@ -16,20 +18,38 @@
16
18
  {{/if}}
17
19
  </svelte:head>
18
20
 
19
- <main class="min-h-screen flex items-center justify-center">
20
- <div class="text-center">
21
+ <main class='min-h-screen flex items-center justify-center p-6'>
22
+ <Card shadow='lg' padding='xl' class='w-full max-w-xl bg-base-100 text-center'>
21
23
  {{#if hasI18n}}
22
- <h1 class="text-4xl font-bold mb-4">{m.app_title()}</h1>
23
- <p class="text-gray-600 mb-8">{m.page_home_subtitle()}</p>
24
- <div class="flex gap-4 justify-center">
25
- <a href="/admin" class="btn btn-primary">{m.nav_admin()}</a>
24
+ <div class='flex justify-center'>
25
+ <Badge variant='primary'>
26
+ <span class='icon-[tabler--sparkles] size-4'></span>
27
+ {m.page_home_badge()}
28
+ </Badge>
29
+ </div>
30
+ <h1 class='mt-4 text-4xl font-black tracking-tight'>{m.app_title()}</h1>
31
+ <p class='mt-3 text-base-content/60'>{m.page_home_subtitle()}</p>
32
+ <div class='mt-8 flex flex-wrap gap-3 justify-center'>
33
+ <a href={resolve('/admin', {})} class='btn btn-primary'>
34
+ <span class='icon-[tabler--layout-dashboard] size-5'></span>
35
+ {m.nav_admin()}
36
+ </a>
26
37
  </div>
27
38
  {{else}}
28
- <h1 class="text-4xl font-bold mb-4">管理后台</h1>
29
- <p class="text-gray-600 mb-8">基于 hai Agent Framework 构建</p>
30
- <div class="flex gap-4 justify-center">
31
- <a href="/admin" class="btn btn-primary">进入后台</a>
39
+ <div class='flex justify-center'>
40
+ <Badge variant='primary'>
41
+ <span class='icon-[tabler--sparkles] size-4'></span>
42
+ 由 hai 驱动
43
+ </Badge>
44
+ </div>
45
+ <h1 class='mt-4 text-4xl font-black tracking-tight'>管理后台</h1>
46
+ <p class='mt-3 text-base-content/60'>基于 hai Agent Framework 构建</p>
47
+ <div class='mt-8 flex flex-wrap gap-3 justify-center'>
48
+ <a href={resolve('/admin', {})} class='btn btn-primary'>
49
+ <span class='icon-[tabler--layout-dashboard] size-5'></span>
50
+ 进入后台
51
+ </a>
32
52
  </div>
33
53
  {{/if}}
34
- </div>
54
+ </Card>
35
55
  </main>
@@ -1,9 +1,11 @@
1
1
  {{!-- 后台管理布局(侧边栏 + 内容区) --}}
2
- <script lang="ts">
2
+ <script lang='ts'>
3
3
  /**
4
4
  * 后台管理布局(侧边栏 + 内容区)
5
5
  */
6
6
  import type { Snippet } from 'svelte'
7
+ import { resolve } from '$app/paths'
8
+ import { page } from '$app/state'
7
9
  {{#if hasI18n}}
8
10
  import * as m from '$lib/paraglide/messages'
9
11
  {{/if}}
@@ -12,33 +14,48 @@
12
14
  children: Snippet
13
15
  }
14
16
 
15
- let { children }: Props = $props()
17
+ const { children }: Props = $props()
18
+
19
+ const navItems = $derived([
20
+ {{#if hasI18n}}
21
+ { path: '/admin', label: m.nav_dashboard(), icon: 'icon-[tabler--layout-dashboard]' },
22
+ { path: '/admin/settings', label: m.nav_settings(), icon: 'icon-[tabler--settings]' },
23
+ {{else}}
24
+ { path: '/admin', label: '仪表盘', icon: 'icon-[tabler--layout-dashboard]' },
25
+ { path: '/admin/settings', label: '设置', icon: 'icon-[tabler--settings]' },
26
+ {{/if}}
27
+ ] as const)
16
28
  </script>
17
29
 
18
- <div class="drawer lg:drawer-open">
19
- <input id="admin-drawer" type="checkbox" class="drawer-toggle" />
20
- <div class="drawer-content p-6">
21
- <label for="admin-drawer" class="btn btn-ghost drawer-button lg:hidden mb-4">
22
- ☰
30
+ <div class='drawer lg:drawer-open'>
31
+ <input id='admin-drawer' type='checkbox' class='drawer-toggle' />
32
+ <div class='drawer-content p-6'>
33
+ <label for='admin-drawer' class='btn btn-ghost btn-sm drawer-button lg:hidden mb-4'>
34
+ <span class='icon-[tabler--menu-2] size-5'></span>
23
35
  </label>
24
36
  {@render children()}
25
37
  </div>
26
- <div class="drawer-side">
27
- <label for="admin-drawer" class="drawer-overlay"></label>
28
- <nav class="menu p-4 w-64 min-h-full bg-base-100 text-base-content">
38
+ <div class='drawer-side'>
39
+ <label for='admin-drawer' class='drawer-overlay' aria-label='close sidebar'></label>
40
+ <nav class='menu p-4 w-64 min-h-full gap-1 bg-base-100 text-base-content border-r border-base-300/60'>
41
+ <div class='flex items-center gap-2 px-2 mb-6 text-xl font-bold'>
42
+ <span class='icon-[tabler--cube] size-6 text-primary'></span>
29
43
  {{#if hasI18n}}
30
- <div class="text-xl font-bold mb-6 px-2">{m.app_title()}</div>
31
- <ul>
32
- <li><a href="/admin">{m.nav_dashboard()}</a></li>
33
- <li><a href="/admin/settings">{m.nav_settings()}</a></li>
34
- </ul>
44
+ {m.app_title()}
35
45
  {{else}}
36
- <div class="text-xl font-bold mb-6 px-2">管理后台</div>
37
- <ul>
38
- <li><a href="/admin">仪表盘</a></li>
39
- <li><a href="/admin/settings">设置</a></li>
40
- </ul>
46
+ 管理后台
41
47
  {{/if}}
48
+ </div>
49
+ <ul class='gap-1'>
50
+ {#each navItems as item (item.path)}
51
+ <li>
52
+ <a href={resolve(item.path, {})} class:menu-active={page.url.pathname === item.path}>
53
+ <span class='{item.icon} size-5'></span>
54
+ {item.label}
55
+ </a>
56
+ </li>
57
+ {/each}
58
+ </ul>
42
59
  </nav>
43
60
  </div>
44
61
  </div>
@@ -1,10 +1,27 @@
1
1
  {{!-- 仪表盘页面 --}}
2
- <script lang="ts">
2
+ <script lang='ts'>
3
3
  /**
4
4
  * 仪表盘页面
5
5
  */
6
6
  {{#if hasI18n}}
7
7
  import * as m from '$lib/paraglide/messages'
8
+ {{/if}}
9
+ import { Card, PageHeader } from '@h-ai/ui'
10
+ {{#if hasI18n}}
11
+
12
+ const stats = $derived([
13
+ { label: m.page_dashboard_total_users(), value: '0', icon: 'icon-[tabler--users]', accent: 'text-primary' },
14
+ { label: m.page_dashboard_today_active(), value: '0', icon: 'icon-[tabler--activity]', accent: 'text-success' },
15
+ { label: m.page_dashboard_revenue(), value: '¥0', icon: 'icon-[tabler--coin]', accent: 'text-warning' },
16
+ { label: m.page_dashboard_orders(), value: '0', icon: 'icon-[tabler--shopping-cart]', accent: 'text-info' },
17
+ ])
18
+ {{else}}
19
+ const stats = [
20
+ { label: '总用户', value: '0', icon: 'icon-[tabler--users]', accent: 'text-primary' },
21
+ { label: '今日活跃', value: '0', icon: 'icon-[tabler--activity]', accent: 'text-success' },
22
+ { label: '本月营收', value: '¥0', icon: 'icon-[tabler--coin]', accent: 'text-warning' },
23
+ { label: '待处理订单', value: '0', icon: 'icon-[tabler--shopping-cart]', accent: 'text-info' },
24
+ ]
8
25
  {{/if}}
9
26
  </script>
10
27
 
@@ -17,29 +34,21 @@
17
34
  </svelte:head>
18
35
 
19
36
  {{#if hasI18n}}
20
- <h1 class="text-2xl font-bold mb-6">{m.page_dashboard_title()}</h1>
21
-
22
- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
23
- <div class="stat bg-base-100 rounded-box shadow">
24
- <div class="stat-title">{m.page_dashboard_total_users()}</div>
25
- <div class="stat-value">0</div>
26
- </div>
27
- <div class="stat bg-base-100 rounded-box shadow">
28
- <div class="stat-title">{m.page_dashboard_today_active()}</div>
29
- <div class="stat-value">0</div>
30
- </div>
31
- </div>
37
+ <PageHeader title={m.page_dashboard_title()} description={m.page_dashboard_subtitle()} />
32
38
  {{else}}
33
- <h1 class="text-2xl font-bold mb-6">仪表盘</h1>
39
+ <PageHeader title='仪表盘' description='系统运营数据总览' />
40
+ {{/if}}
34
41
 
35
- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
36
- <div class="stat bg-base-100 rounded-box shadow">
37
- <div class="stat-title">总用户</div>
38
- <div class="stat-value">0</div>
39
- </div>
40
- <div class="stat bg-base-100 rounded-box shadow">
41
- <div class="stat-title">今日活跃</div>
42
- <div class="stat-value">0</div>
43
- </div>
42
+ <div class='grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4'>
43
+ {#each stats as stat (stat.label)}
44
+ <Card shadow='sm' class='bg-base-100'>
45
+ <div class='flex items-center justify-between'>
46
+ <div>
47
+ <div class='text-sm text-base-content/55'>{stat.label}</div>
48
+ <div class='mt-1 text-3xl font-bold tracking-tight'>{stat.value}</div>
49
+ </div>
50
+ <span class='{stat.icon} {stat.accent} size-9'></span>
51
+ </div>
52
+ </Card>
53
+ {/each}
44
54
  </div>
45
- {{/if}}
@@ -1,11 +1,12 @@
1
1
  {{!-- 设置页面 --}}
2
- <script lang="ts">
2
+ <script lang='ts'>
3
3
  /**
4
4
  * 设置页面
5
5
  */
6
6
  {{#if hasI18n}}
7
7
  import * as m from '$lib/paraglide/messages'
8
8
  {{/if}}
9
+ import { Card, PageHeader } from '@h-ai/ui'
9
10
  </script>
10
11
 
11
12
  <svelte:head>
@@ -17,21 +18,23 @@
17
18
  </svelte:head>
18
19
 
19
20
  {{#if hasI18n}}
20
- <h1 class="text-2xl font-bold mb-6">{m.page_settings_title()}</h1>
21
+ <PageHeader title={m.page_settings_title()} description={m.page_settings_subtitle()} />
21
22
 
22
- <div class="card bg-base-100 shadow">
23
- <div class="card-body">
24
- <h2 class="card-title">{m.page_settings_basic()}</h2>
25
- <p>{m.page_settings_desc()}</p>
23
+ <Card shadow='sm' class='bg-base-100'>
24
+ <div class='flex items-center gap-2'>
25
+ <span class='icon-[tabler--adjustments] size-5 text-primary'></span>
26
+ <h2 class='text-base font-semibold'>{m.page_settings_basic()}</h2>
26
27
  </div>
27
- </div>
28
+ <p class='mt-2 text-sm text-base-content/60'>{m.page_settings_desc()}</p>
29
+ </Card>
28
30
  {{else}}
29
- <h1 class="text-2xl font-bold mb-6">系统设置</h1>
31
+ <PageHeader title='系统设置' description='管理系统配置项' />
30
32
 
31
- <div class="card bg-base-100 shadow">
32
- <div class="card-body">
33
- <h2 class="card-title">基本配置</h2>
34
- <p>在此管理系统设置。</p>
33
+ <Card shadow='sm' class='bg-base-100'>
34
+ <div class='flex items-center gap-2'>
35
+ <span class='icon-[tabler--adjustments] size-5 text-primary'></span>
36
+ <h2 class='text-base font-semibold'>基本配置</h2>
35
37
  </div>
36
- </div>
38
+ <p class='mt-2 text-sm text-base-content/60'>在此管理系统设置。</p>
39
+ </Card>
37
40
  {{/if}}
@@ -0,0 +1,16 @@
1
+ # {{api.contractPackageName}}
2
+
3
+ {{projectName}} API service 的共享 contract 包。
4
+
5
+ ## 包含内容
6
+
7
+ - `app-contract.ts`:应用级路由定义
8
+ - `app-schemas.ts`:输入输出 Zod schema
9
+ - `{{projectName}}-contract.ts`:工程级 contract 聚合入口
10
+
11
+ ## 常用命令
12
+
13
+ ```bash
14
+ pnpm --filter {{api.contractPackageName}} build
15
+ pnpm --filter {{api.contractPackageName}} test
16
+ ```
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "{{api.contractPackageName}}",
3
+ "type": "module",
4
+ "version": "0.1.0",
5
+ "private": true,
6
+ "license": "MIT",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "typecheck": "tsc --noEmit",
22
+ "lint": "eslint .",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "clean": "rimraf dist .turbo"
26
+ },
27
+ "dependencies": {
28
+ "@h-ai/api-contract": "{{haiPackageSpecifier}}",
29
+ "zod": "{{api.packageSpecifiers.zod}}"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "{{api.packageSpecifiers.nodeTypes}}",
33
+ "rimraf": "{{api.packageSpecifiers.rimraf}}",
34
+ "tsup": "{{api.packageSpecifiers.tsup}}",
35
+ "typescript": "{{api.packageSpecifiers.typescript}}",
36
+ "vitest": "{{api.packageSpecifiers.vitest}}"
37
+ }
38
+ }
@@ -0,0 +1,27 @@
1
+ import { apiContract } from '@h-ai/api-contract'
2
+ import { AppEchoInputSchema, AppEchoOutputSchema, AppInfoOutputSchema } from './app-schemas.js'
3
+
4
+ /** 应用自有 app contract。 */
5
+ export const appContract = {
6
+ info: apiContract
7
+ .route({
8
+ method: 'POST',
9
+ path: '/app/info',
10
+ operationId: 'app.info',
11
+ summary: 'Get service info',
12
+ tags: ['app'],
13
+ })
14
+ .output(AppInfoOutputSchema),
15
+ echo: apiContract
16
+ .route({
17
+ method: 'POST',
18
+ path: '/app/echo',
19
+ operationId: 'app.echo',
20
+ summary: '{{#if features.iam}}Echo a message (auth required){{else}}Echo a message{{/if}}',
21
+ tags: ['app'],
22
+ })
23
+ .input(AppEchoInputSchema)
24
+ .output(AppEchoOutputSchema),
25
+ }
26
+
27
+ export type AppContract = typeof appContract
@@ -0,0 +1,33 @@
1
+ import { apiContract } from '@h-ai/api-contract'
2
+ import { z } from 'zod'
3
+
4
+ /** 服务元信息响应数据。 */
5
+ export const AppInfoOutputDataSchema = z.object({
6
+ name: z.string(),
7
+ version: z.string(),
8
+ uptimeMs: z.number().int().nonnegative(),
9
+ transportEnabled: z.boolean(),
10
+ })
11
+
12
+ /** app.info 标准 HaiResult 输出。 */
13
+ export const AppInfoOutputSchema = apiContract.haiResultSchema(AppInfoOutputDataSchema)
14
+
15
+ /** 输入:单条文本消息(演示用,限制最大长度防止误用)。 */
16
+ export const AppEchoInputSchema = z.object({
17
+ message: z.string().min(1).max(2000),
18
+ })
19
+
20
+ /** 输出:原样回显 + 调用者快照。 */
21
+ export const AppEchoOutputDataSchema = z.object({
22
+ message: z.string(),
23
+ requestId: z.string(),
24
+ timestamp: z.string(),
25
+ userId: z.string().optional(),
26
+ })
27
+
28
+ /** app.echo 标准 HaiResult 输出。 */
29
+ export const AppEchoOutputSchema = apiContract.haiResultSchema(AppEchoOutputDataSchema)
30
+
31
+ export type AppInfoOutputData = z.infer<typeof AppInfoOutputDataSchema>
32
+ export type AppEchoInput = z.infer<typeof AppEchoInputSchema>
33
+ export type AppEchoOutputData = z.infer<typeof AppEchoOutputDataSchema>
@@ -0,0 +1,3 @@
1
+ export * from './app-contract.js'
2
+ export * from './app-schemas.js'
3
+ export * from './{{projectName}}-contract.js'
@@ -0,0 +1,12 @@
1
+ import { apiContract } from '@h-ai/api-contract'
2
+ import { appContract } from './app-contract.js'
3
+
4
+ /** 工程级应用 contract。 */
5
+ export const {{api.contractExportName}} = apiContract.create({
6
+ {{#if features.iam}} iam: apiContract.iam,
7
+ {{/if}}{{#if features.storage}} storage: apiContract.storage,
8
+ {{/if}}{{#if features.ai}} ai: apiContract.ai,
9
+ {{/if}} app: appContract,
10
+ })
11
+
12
+ export type ApiWorkspaceContract = typeof {{api.contractExportName}}
@@ -0,0 +1,18 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { AppEchoInputSchema, {{api.contractExportName}} } from '../src/index.js'
3
+
4
+ describe('{{api.contractPackageName}}', () => {
5
+ it('includes generated contract routes', () => {
6
+ expect({{api.contractExportName}}.app.info).toBeDefined()
7
+ expect({{api.contractExportName}}.app.echo).toBeDefined()
8
+ {{#if features.iam}} expect({{api.contractExportName}}.iam.auth.login).toBeDefined()
9
+ {{/if}}{{#if features.storage}} expect({{api.contractExportName}}.storage.presignedUrls.createUpload).toBeDefined()
10
+ {{/if}}{{#if features.ai}} expect({{api.contractExportName}}.ai.chats.createCompletion).toBeDefined()
11
+ {{/if}} })
12
+
13
+ it('validates app.echo input length', () => {
14
+ expect(AppEchoInputSchema.safeParse({ message: 'hello' }).success).toBe(true)
15
+ expect(AppEchoInputSchema.safeParse({ message: '' }).success).toBe(false)
16
+ expect(AppEchoInputSchema.safeParse({ message: 'x'.repeat(2001) }).success).toBe(false)
17
+ })
18
+ })