@h-ai/cli 0.1.0-alpha.53 → 0.1.0-alpha.55
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.md +18 -0
- package/dist/{chunk-U4PA632G.js → chunk-FVDXJYMO.js} +34 -67
- package/dist/chunk-FVDXJYMO.js.map +1 -0
- package/dist/cli-config-templates-WGN2I23J.js +5 -0
- package/dist/{cli-config-templates-GSXKLGU5.js.map → cli-config-templates-WGN2I23J.js.map} +1 -1
- package/dist/index.js +174 -105
- package/dist/index.js.map +1 -1
- package/dist/{src-QNEJJ3BV.js → src-6ZBWDBUB.js} +127 -87
- package/dist/{src-QNEJJ3BV.js.map → src-6ZBWDBUB.js.map} +1 -1
- package/package.json +13 -4
- package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +1 -0
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -0
- package/templates/apps/api/package.json.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +1 -0
- package/templates/apps/fullstack/package.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +1 -0
- package/templates/base/eslint.config.js.hbs +1 -0
- package/templates/base/package.json.hbs +3 -1
- package/templates/skills/AGENTS.md +8 -22
- package/templates/skills/CLAUDE.md +3 -16
- package/templates/skills/bridges/admin/AGENTS.md +3 -16
- package/templates/skills/bridges/admin/CLAUDE.md +3 -11
- package/templates/skills/bridges/admin/copilot-instructions.md +2 -39
- package/templates/skills/bridges/api/AGENTS.md +5 -18
- package/templates/skills/bridges/api/CLAUDE.md +3 -11
- package/templates/skills/bridges/api/copilot-instructions.md +3 -38
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +24 -59
- package/templates/skills/bridges/fullstack/CLAUDE.md.hbs +1 -15
- package/templates/skills/bridges/fullstack/copilot-instructions.md.hbs +5 -45
- package/templates/skills/bridges/h5/AGENTS.md +4 -17
- package/templates/skills/bridges/h5/CLAUDE.md +3 -11
- package/templates/skills/bridges/h5/copilot-instructions.md +2 -37
- package/templates/skills/bridges/mobile-app/AGENTS.md +5 -18
- package/templates/skills/bridges/mobile-app/CLAUDE.md +3 -11
- package/templates/skills/bridges/mobile-app/copilot-instructions.md +2 -38
- package/templates/skills/bridges/website/AGENTS.md +4 -17
- package/templates/skills/bridges/website/CLAUDE.md +3 -11
- package/templates/skills/bridges/website/copilot-instructions.md +2 -37
- package/templates/skills/copilot-instructions.md +2 -36
- package/templates/skills/hai-ai/SKILL.md +35 -389
- package/templates/skills/hai-ai/guide.md +383 -0
- package/templates/skills/hai-ai/reference.md +6 -2
- package/templates/skills/hai-api-client/SKILL.md +6 -6
- package/templates/skills/hai-api-contract/SKILL.md +10 -25
- package/templates/skills/hai-app-create/SKILL.md +29 -388
- package/templates/skills/hai-app-create/reference.md +350 -0
- package/templates/skills/hai-app-review/SKILL.md +15 -23
- package/templates/skills/hai-app-tests/SKILL.md +29 -494
- package/templates/skills/hai-app-tests/reference.md +442 -0
- package/templates/skills/hai-audit/SKILL.md +6 -6
- package/templates/skills/hai-build/SKILL.md +19 -330
- package/templates/skills/hai-build/reference.md +326 -0
- package/templates/skills/hai-cache/SKILL.md +28 -31
- package/templates/skills/hai-capacitor/SKILL.md +19 -364
- package/templates/skills/hai-capacitor/reference.md +352 -0
- package/templates/skills/hai-ci/SKILL.md +17 -20
- package/templates/skills/hai-core/SKILL.md +25 -564
- package/templates/skills/hai-core/reference.md +533 -0
- package/templates/skills/hai-crypto/SKILL.md +8 -20
- package/templates/skills/hai-datapipe/SKILL.md +9 -23
- package/templates/skills/hai-deploy/SKILL.md +18 -24
- package/templates/skills/hai-framework-sync/SKILL.md +8 -20
- package/templates/skills/hai-iam/SKILL.md +27 -365
- package/templates/skills/hai-iam/reference.md +343 -0
- package/templates/skills/hai-kit/SKILL.md +6 -6
- package/templates/skills/hai-payment/SKILL.md +6 -26
- package/templates/skills/hai-pr-review/SKILL.md +6 -22
- package/templates/skills/hai-reach/SKILL.md +19 -317
- package/templates/skills/hai-reach/reference.md +303 -0
- package/templates/skills/hai-reldb/SKILL.md +19 -472
- package/templates/skills/hai-reldb/reference.md +452 -0
- package/templates/skills/hai-scheduler/SKILL.md +18 -6
- package/templates/skills/hai-serv/SKILL.md +27 -759
- package/templates/skills/hai-serv/reference.md +736 -0
- package/templates/skills/hai-storage/SKILL.md +7 -25
- package/templates/skills/hai-ui/SKILL.md +29 -621
- package/templates/skills/hai-ui/reference.md +598 -0
- package/templates/skills/hai-vecdb/SKILL.md +18 -303
- package/templates/skills/hai-vecdb/reference.md +291 -0
- package/dist/chunk-U4PA632G.js.map +0 -1
- package/dist/cli-config-templates-GSXKLGU5.js +0 -5
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# hai-app-create — 详细用法
|
|
2
|
+
|
|
3
|
+
从 [SKILL.md](SKILL.md) 选择主题后读取对应小节。代码片段展示 API 用法;省略的业务变量、schema、依赖与 return 上下文需由应用补齐。
|
|
4
|
+
|
|
5
|
+
> 面向 AI 助手的应用功能创建指南。**必须遵循 TDD**:先写测试(Red)→ 再实现(Green)→ 再重构(Refactor)。
|
|
6
|
+
> 若目标是纯 API service workspace,优先围绕 `apps/*-contract`、`apps/*-service` 与测试组织改动,而不是套用页面/路由脚手架。
|
|
7
|
+
|
|
8
|
+
## 适用场景
|
|
9
|
+
|
|
10
|
+
- 新增页面(路由)
|
|
11
|
+
- 新增 API 端点
|
|
12
|
+
- 新增服务层模块(`$lib/server/services/`)
|
|
13
|
+
- 新增数据库表 / 数据模型
|
|
14
|
+
- 新增业务组件
|
|
15
|
+
|
|
16
|
+
## TDD 开发流程(强制)
|
|
17
|
+
|
|
18
|
+
创建任何新功能时,必须按以下顺序执行:
|
|
19
|
+
|
|
20
|
+
### Step 1:需求分析与测试设计
|
|
21
|
+
|
|
22
|
+
1. 明确功能的输入、输出、边界条件、错误场景
|
|
23
|
+
2. 确定需要创建的文件(服务、路由、Schema、组件等)
|
|
24
|
+
3. 设计测试用例清单(参照 `hai-app-tests` 技能的覆盖范围要求)
|
|
25
|
+
|
|
26
|
+
### Step 2:编写测试(Red 阶段)
|
|
27
|
+
|
|
28
|
+
1. **创建单元测试**:在 `tests/` 下为服务层、Schema 编写 Vitest 测试
|
|
29
|
+
2. **创建 E2E 测试**:在 `e2e/` 下为 API 端点、页面交互编写 Playwright 测试
|
|
30
|
+
3. **创建空的实现文件**:仅包含函数签名和类型定义,不写实际逻辑(确保 import 不报错)
|
|
31
|
+
4. **运行测试确认新增目标用例因预期行为缺失而失败(既有回归保持通过)**:
|
|
32
|
+
```bash
|
|
33
|
+
pnpm --filter <app-name> test # 新增目标用例应 FAIL,既有回归应 PASS
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Step 3:编写实现(Green 阶段)
|
|
37
|
+
|
|
38
|
+
按照下文的「创建指南」编写实际功能代码,每完成一个功能点立即运行测试确认通过。
|
|
39
|
+
|
|
40
|
+
### Step 4:重构与验证(Refactor 阶段)
|
|
41
|
+
|
|
42
|
+
1. 参照 `hai-app-review` 审查代码规范
|
|
43
|
+
2. 运行质量门禁:`pnpm typecheck && pnpm lint && pnpm test`
|
|
44
|
+
3. 运行 E2E:`pnpm --filter <app-name> test:e2e`
|
|
45
|
+
|
|
46
|
+
## 目录结构约定
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
src/
|
|
50
|
+
hooks.server.ts # `handle` hook 入口
|
|
51
|
+
app.css # 全局样式(TailwindCSS 4)
|
|
52
|
+
lib/
|
|
53
|
+
server/
|
|
54
|
+
init.ts # 应用初始化(模块 init 顺序)
|
|
55
|
+
services/ # 业务服务层(Server-only)
|
|
56
|
+
index.ts # 服务聚合导出
|
|
57
|
+
user.ts # 示例:用户服务
|
|
58
|
+
schemas/ # 请求校验 Schema(Zod)
|
|
59
|
+
components/ # 业务组件(非 @h-ai/ui 通用组件)
|
|
60
|
+
stores/ # 客户端 Store(Svelte 5 Runes)
|
|
61
|
+
paraglide/ # i18n 生成文件(禁止手动修改)
|
|
62
|
+
routes/
|
|
63
|
+
+layout.svelte # 根布局
|
|
64
|
+
+page.svelte # 首页
|
|
65
|
+
(auth)/ # 认证分组路由
|
|
66
|
+
login/+page.svelte
|
|
67
|
+
admin/ # 管理区域
|
|
68
|
+
users/
|
|
69
|
+
+page.svelte # 列表页
|
|
70
|
+
+page.server.ts # 列表数据加载
|
|
71
|
+
[id]/
|
|
72
|
+
+page.svelte # 详情页
|
|
73
|
+
+page.server.ts
|
|
74
|
+
api/ # API 端点
|
|
75
|
+
auth/+server.ts
|
|
76
|
+
health/+server.ts
|
|
77
|
+
iam/
|
|
78
|
+
users/+server.ts
|
|
79
|
+
messages/ # i18n 消息文件
|
|
80
|
+
en-US.json
|
|
81
|
+
zh-CN.json
|
|
82
|
+
config/ # 模块配置文件
|
|
83
|
+
_core.yml
|
|
84
|
+
_db.yml
|
|
85
|
+
_cache.yml
|
|
86
|
+
_iam.yml
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 创建页面
|
|
90
|
+
|
|
91
|
+
### 页面路由规则
|
|
92
|
+
|
|
93
|
+
使用 SvelteKit 文件路由,参考 `src/routes/` 约定:
|
|
94
|
+
|
|
95
|
+
| 类型 | 路径示例 | 说明 |
|
|
96
|
+
| -------- | ---------------------------------- | -------------------- |
|
|
97
|
+
| 静态页面 | `routes/about/+page.svelte` | 常规页面 |
|
|
98
|
+
| 动态路由 | `routes/users/[id]/+page.svelte` | URL 参数 |
|
|
99
|
+
| 分组路由 | `routes/(auth)/login/+page.svelte` | 共享布局,不影响 URL |
|
|
100
|
+
| API 端点 | `routes/api/users/+server.ts` | RESTful API |
|
|
101
|
+
|
|
102
|
+
### 页面文件模板
|
|
103
|
+
|
|
104
|
+
```svelte
|
|
105
|
+
<!-- +page.svelte -->
|
|
106
|
+
<script lang="ts">
|
|
107
|
+
// Svelte 5 Runes 语法
|
|
108
|
+
let { data } = $props()
|
|
109
|
+
let loading = $state(false)
|
|
110
|
+
|
|
111
|
+
async function handleAction() {
|
|
112
|
+
loading = true
|
|
113
|
+
try {
|
|
114
|
+
// 业务逻辑
|
|
115
|
+
} finally {
|
|
116
|
+
loading = false
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<div class="container mx-auto p-4">
|
|
122
|
+
<!-- 使用 @h-ai/ui 组件 -->
|
|
123
|
+
</div>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 数据加载(+page.server.ts)
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import type { PageServerLoad } from './$types'
|
|
130
|
+
import { error, redirect } from '@sveltejs/kit'
|
|
131
|
+
|
|
132
|
+
export const load: PageServerLoad = async ({ locals }) => {
|
|
133
|
+
if (!locals.session)
|
|
134
|
+
redirect(303, '/auth/login')
|
|
135
|
+
const result = await someService.list()
|
|
136
|
+
if (!result.success)
|
|
137
|
+
error(500, result.error.message)
|
|
138
|
+
return { items: result.data }
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## 创建 API 端点
|
|
143
|
+
|
|
144
|
+
### RESTful 端点模板
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// src/routes/api/[resource]/+server.ts
|
|
148
|
+
import type { RequestHandler } from './$types'
|
|
149
|
+
import { kit } from '@h-ai/kit'
|
|
150
|
+
import { z } from 'zod'
|
|
151
|
+
|
|
152
|
+
const CreateSchema = z.object({
|
|
153
|
+
name: z.string().min(1).max(100),
|
|
154
|
+
description: z.string().optional(),
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
// GET - 列表
|
|
158
|
+
export const GET = kit.handler(async ({ locals }) => {
|
|
159
|
+
kit.guard.require(locals.session, 'resource:read')
|
|
160
|
+
|
|
161
|
+
const result = await service.list()
|
|
162
|
+
if (!result.success)
|
|
163
|
+
return kit.response.error(result.error.code, result.error.message)
|
|
164
|
+
return kit.response.ok(result.data)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
// POST - 创建
|
|
168
|
+
export const POST = kit.handler(async ({ request, locals }) => {
|
|
169
|
+
kit.guard.require(locals.session, 'resource:create')
|
|
170
|
+
|
|
171
|
+
const data = await kit.validate.body(request, CreateSchema)
|
|
172
|
+
|
|
173
|
+
const result = await service.create(data)
|
|
174
|
+
if (!result.success)
|
|
175
|
+
return kit.response.error(result.error.code, result.error.message)
|
|
176
|
+
return kit.response.created(result.data)
|
|
177
|
+
})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 要点
|
|
181
|
+
|
|
182
|
+
- 所有端点**必须**用 `kit.handler(async ({ locals, request, ... }) => { ... })` 包裹,由 handler 统一处理 throw 的 Response
|
|
183
|
+
- 权限守卫使用 `kit.guard.require(locals.session, 'xxx:yyy')`,它本身就是 throw 模式(未通过时 throw 403 Response),**无需**检查返回值
|
|
184
|
+
- 输入校验使用 `await kit.validate.body(request, Schema)`,校验失败时 throw 400 Response,**无需**手动判断 valid
|
|
185
|
+
- 返回统一使用 `kit.response.*`
|
|
186
|
+
- HaiResult 错误直接透传,不重新包装
|
|
187
|
+
- HaiResult 型业务操作先检查 result.success;流和显式抛错 API 按各自契约处理
|
|
188
|
+
|
|
189
|
+
## 创建服务层
|
|
190
|
+
|
|
191
|
+
服务层位于 `$lib/server/services/`,处理业务逻辑。
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import type { HaiResult, PaginatedResult } from '@h-ai/core'
|
|
195
|
+
import { core, ok } from '@h-ai/core'
|
|
196
|
+
import { reldb } from '@h-ai/reldb'
|
|
197
|
+
|
|
198
|
+
// Article/CreateArticleInput/ListParams 为应用类型;已完成建表。
|
|
199
|
+
const articles = reldb.crud.table<Article>({
|
|
200
|
+
table: 'articles',
|
|
201
|
+
idColumn: 'id',
|
|
202
|
+
dbType: 'sqlite', // 与本例初始化后端一致;切换数据库时同步修改
|
|
203
|
+
select: ['id', 'title', 'created_at AS createdAt'],
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
export async function createArticle(input: CreateArticleInput): Promise<HaiResult<Article>> {
|
|
207
|
+
const article = { ...input, id: core.id.generate(), createdAt: new Date().toISOString() }
|
|
208
|
+
// DB 列映射由应用定义;create 返回 ExecuteResult,不是 Article。
|
|
209
|
+
const result = await articles.create({ id: article.id, title: article.title, created_at: article.createdAt })
|
|
210
|
+
if (!result.success) return result
|
|
211
|
+
return ok(article)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export async function listArticles(params: ListParams): Promise<HaiResult<PaginatedResult<Article>>> {
|
|
215
|
+
return articles.findPage({ pagination: params })
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 服务层规则
|
|
220
|
+
|
|
221
|
+
- 统一返回 `HaiResult<T>`
|
|
222
|
+
- 写操作需 `debug`(进入)+ `info`(成功)日志
|
|
223
|
+
- 读操作仅 `debug` 日志
|
|
224
|
+
- 禁止 `console.log`,使用 `core.logger`
|
|
225
|
+
- 服务层不处理 HTTP 响应格式,只返回 HaiResult
|
|
226
|
+
|
|
227
|
+
## 创建数据模型
|
|
228
|
+
|
|
229
|
+
### Schema 定义(SQL DDL)
|
|
230
|
+
|
|
231
|
+
数据库表 Schema 定义在 `$lib/server/init.ts` 中:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
const BUSINESS_SCHEMA = `
|
|
235
|
+
CREATE TABLE IF NOT EXISTS articles (
|
|
236
|
+
id TEXT PRIMARY KEY,
|
|
237
|
+
title TEXT NOT NULL,
|
|
238
|
+
content TEXT,
|
|
239
|
+
author_id TEXT NOT NULL,
|
|
240
|
+
status TEXT DEFAULT 'draft',
|
|
241
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
242
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
CREATE INDEX IF NOT EXISTS idx_articles_author_id ON articles(author_id);
|
|
246
|
+
CREATE INDEX IF NOT EXISTS idx_articles_status ON articles(status);
|
|
247
|
+
`
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 类型定义
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// 在服务文件或独立类型文件中定义
|
|
254
|
+
interface Article {
|
|
255
|
+
id: string
|
|
256
|
+
title: string
|
|
257
|
+
content: string | null
|
|
258
|
+
authorId: string
|
|
259
|
+
status: 'draft' | 'published' | 'archived'
|
|
260
|
+
createdAt: string
|
|
261
|
+
updatedAt: string
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
interface CreateArticleInput {
|
|
265
|
+
title: string
|
|
266
|
+
content?: string
|
|
267
|
+
authorId: string
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## 创建业务组件
|
|
272
|
+
|
|
273
|
+
业务组件放在 `$lib/components/`,通用 UI 使用 `@h-ai/ui`。
|
|
274
|
+
|
|
275
|
+
```svelte
|
|
276
|
+
<!-- src/lib/components/ArticleCard.svelte -->
|
|
277
|
+
<script lang="ts">
|
|
278
|
+
import type { Article } from '$lib/server/services/article'
|
|
279
|
+
|
|
280
|
+
let { article, onEdit }: { article: Article, onEdit?: () => void } = $props()
|
|
281
|
+
</script>
|
|
282
|
+
|
|
283
|
+
<div class="card bg-base-100 shadow">
|
|
284
|
+
<div class="card-body">
|
|
285
|
+
<h2 class="card-title">{article.title}</h2>
|
|
286
|
+
<p>{article.content ?? ''}</p>
|
|
287
|
+
{#if onEdit}
|
|
288
|
+
<div class="card-actions justify-end">
|
|
289
|
+
<button class="btn btn-primary" onclick={onEdit}>编辑</button>
|
|
290
|
+
</div>
|
|
291
|
+
{/if}
|
|
292
|
+
</div>
|
|
293
|
+
</div>
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### 组件规则
|
|
297
|
+
|
|
298
|
+
- 使用 Svelte 5 Runes 语法(`$props()`、`$state()`、`$derived()`、`$effect()`)
|
|
299
|
+
- 事件回调通过 props 传入(非 `createEventDispatcher`)
|
|
300
|
+
- 样式使用 TailwindCSS 4 + DaisyUI 5 class
|
|
301
|
+
- 通用 UI 组件优先使用 `@h-ai/ui`,不重复实现
|
|
302
|
+
- i18n 文本使用 `$lib/paraglide/messages.js` 中的 key
|
|
303
|
+
|
|
304
|
+
## 初始化顺序
|
|
305
|
+
|
|
306
|
+
在 `$lib/server/init.ts` 中管理模块初始化顺序:
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
core.init() → reldb.init() → cache.init() → iam.init() → createBusinessTables()
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
新增模块时需在此文件中按依赖顺序添加初始化调用。
|
|
313
|
+
|
|
314
|
+
## 容器交付
|
|
315
|
+
|
|
316
|
+
- 保持脚手架根目录 `Dockerfile`、`.dockerignore` 与 `package.json` 中 `docker:build*` / `podman:build*` 命令同步。
|
|
317
|
+
- Podman 多阶段构建保持 `--format docker --jobs=1`;前者保留 `HEALTHCHECK`,后者避免部分 Podman 5.8/Buildah 组合在并行 stage 构建时触发内部 semaphore panic。
|
|
318
|
+
- 先复制 lockfile 与 workspace 配置执行 `pnpm fetch`,再复制源码并离线安装、构建;避免源码变化重复下载依赖,同时兼容新增 workspace。
|
|
319
|
+
- SvelteKit 服务使用 Node adapter;纯 Vite 前端使用带 SPA fallback 的静态镜像;fullstack 的 Service 与 Web 分别交付,Service 直接监听且不增加转发进程。
|
|
320
|
+
- Web 的 `PUBLIC_*` 部署配置在容器启动时生成静态运行时配置,不作为 build arg 烘焙,确保同一镜像可跨环境部署。
|
|
321
|
+
- 配置文件随镜像提供,并允许只读挂载覆盖;密钥只通过环境变量或 `--env-file` 注入。
|
|
322
|
+
- 普通构建不携带 `data/`;只有用户明确运行 `docker:build:data` / `podman:build:data` 时才通过 named build context 嵌入 SQLite/本地存储初始快照。
|
|
323
|
+
- 数据库和本地存储的持续写入必须落到 volume,不能依赖镜像可写层。
|
|
324
|
+
- 完成前实际启动镜像,验证健康端点、非 root 用户、环境变量覆盖、配置挂载和预置数据读取。
|
|
325
|
+
|
|
326
|
+
## 检查清单
|
|
327
|
+
|
|
328
|
+
- [ ] **TDD 流程已执行**:先写测试 → 确认失败 → 再实现 → 确认通过
|
|
329
|
+
- [ ] 单元测试已编写(服务层、Schema)
|
|
330
|
+
- [ ] E2E 测试已编写(API 端点、页面交互)
|
|
331
|
+
- [ ] 页面使用 Svelte 5 Runes 语法
|
|
332
|
+
- [ ] API 端点有权限守卫与输入校验
|
|
333
|
+
- [ ] 服务层统一返回 `HaiResult<T>`
|
|
334
|
+
- [ ] 日志级别正确(读 debug / 写 debug+info / 失败 warn|error)
|
|
335
|
+
- [ ] 禁止 `console.log`、`any`、硬编码密钥
|
|
336
|
+
- [ ] 用户可见文本走 i18n
|
|
337
|
+
- [ ] Docker / Podman 镜像可启动,配置/环境变量/数据目录行为已验证
|
|
338
|
+
- [ ] 新增表有索引定义
|
|
339
|
+
- [ ] 组件优先使用 `@h-ai/ui`
|
|
340
|
+
- [ ] `pnpm typecheck && pnpm lint && pnpm test` 通过
|
|
341
|
+
|
|
342
|
+
## 相关 Skills
|
|
343
|
+
|
|
344
|
+
- `hai-build`:项目架构与模块初始化顺序
|
|
345
|
+
- `hai-app-tests`:TDD 测试规范(Red 阶段详细指引)
|
|
346
|
+
- `hai-app-review`:代码审查规范(Refactor 阶段参照)
|
|
347
|
+
- `hai-kit`:SvelteKit 集成的完整 API
|
|
348
|
+
- `hai-ui`:UI 组件库使用
|
|
349
|
+
- `hai-reldb`:数据库操作详细 API
|
|
350
|
+
- `hai-iam`:认证与权限管理
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-app-review
|
|
3
|
-
description:
|
|
3
|
+
description: "审查 hai 应用的路由、权限、i18n、服务层与交互体验。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-app-review — 应用代码审查规范
|
|
@@ -9,16 +9,14 @@ description: 对应用代码进行审查:路由安全 → 认证授权 → i18
|
|
|
9
9
|
|
|
10
10
|
| 项目 | 契约 |
|
|
11
11
|
| --- | --- |
|
|
12
|
-
| 能力 |
|
|
13
|
-
| 适用场景 |
|
|
14
|
-
| 输入 |
|
|
15
|
-
| 输出 |
|
|
16
|
-
| 限制 |
|
|
12
|
+
| 能力 | 审查 hai 应用的路由、权限、i18n、服务层与交互体验 |
|
|
13
|
+
| 适用场景 | 用户要求应用代码/体验审查或修复评估 |
|
|
14
|
+
| 输入 | 用户指定范围、代码/差异、运行证据与验收要求 |
|
|
15
|
+
| 输出 | 按优先级列出的证据、影响和修正建议 |
|
|
16
|
+
| 限制 | 只读审查不自动改代码;未读取的实现不下结论,不把偏好当缺陷。实现修复需用户已授权。 |
|
|
17
17
|
|
|
18
18
|
> 面向 AI 助手的应用代码审查指南。用于审查使用 hai-framework 构建的 SvelteKit 应用、纯 Svelte/Vite 客户端与 API service workspace。本技能也是 TDD Refactor 阶段的执行入口。
|
|
19
19
|
|
|
20
|
-
---
|
|
21
|
-
|
|
22
20
|
## 适用场景
|
|
23
21
|
|
|
24
22
|
- 审查应用代码是否符合 hai-framework 规范
|
|
@@ -26,8 +24,6 @@ description: 对应用代码进行审查:路由安全 → 认证授权 → i18
|
|
|
26
24
|
- 规范化现有代码(分层、命名、日志、i18n)
|
|
27
25
|
- 安全审查(权限、输入校验、密钥管理)
|
|
28
26
|
|
|
29
|
-
---
|
|
30
|
-
|
|
31
27
|
## 工作准则
|
|
32
28
|
|
|
33
29
|
1. **先搜索再改动**:用全局检索确认引用关系,避免遗漏更新。
|
|
@@ -35,8 +31,6 @@ description: 对应用代码进行审查:路由安全 → 认证授权 → i18
|
|
|
35
31
|
3. **成套更新**:代码 / 测试 / 文档同步。
|
|
36
32
|
4. **重构后测试必须仍通过**:任何重构都不能破坏已有测试。
|
|
37
33
|
|
|
38
|
-
---
|
|
39
|
-
|
|
40
34
|
## 审查清单
|
|
41
35
|
|
|
42
36
|
### TDD 合规性
|
|
@@ -49,8 +43,6 @@ description: 对应用代码进行审查:路由安全 → 认证授权 → i18
|
|
|
49
43
|
- [ ] `pnpm test` 单元测试全部通过
|
|
50
44
|
- [ ] `pnpm test:e2e` E2E 测试全部通过
|
|
51
45
|
|
|
52
|
-
---
|
|
53
|
-
|
|
54
46
|
## 审查清单
|
|
55
47
|
|
|
56
48
|
### 架构分层
|
|
@@ -63,9 +55,11 @@ description: 对应用代码进行审查:路由安全 → 认证授权 → i18
|
|
|
63
55
|
|
|
64
56
|
### HaiResult 处理
|
|
65
57
|
|
|
66
|
-
- [ ]
|
|
58
|
+
- [ ] 投递等业务状态按 `sent` 等字段判断,失败保留用户输入;HTTP/HaiResult 成功不代替业务完成
|
|
59
|
+
|
|
60
|
+
- [ ] 返回 HaiResult 的调用先检查 `result.success`
|
|
67
61
|
- [ ] 上游 HaiResult 错误直接透传,不重新包装
|
|
68
|
-
- [ ] 不使用 `try/catch`
|
|
62
|
+
- [ ] 不使用 `try/catch` 来处理框架模块返回的错误(HaiResult 型 API 的业务错误在结果中;流/显式抛错 API 另按契约处理)
|
|
69
63
|
|
|
70
64
|
```typescript
|
|
71
65
|
// ❌ 重新包装
|
|
@@ -82,7 +76,7 @@ if (!result.success)
|
|
|
82
76
|
|
|
83
77
|
### 权限与安全
|
|
84
78
|
|
|
85
|
-
- [ ]
|
|
79
|
+
- [ ] 受保护端点/load 有与实际框架匹配的守卫;公开路由显式允许匿名,不给 API service 强加 kit.guard
|
|
86
80
|
- [ ] 所有用户输入通过 Zod Schema 校验(`kit.validate.*`)
|
|
87
81
|
- [ ] 无硬编码 API Key / 密钥
|
|
88
82
|
- [ ] 日志不输出密码、token 明文
|
|
@@ -150,8 +144,6 @@ if (!result.success)
|
|
|
150
144
|
- [ ] 初始化在 `$lib/server/init.ts` 统一管理
|
|
151
145
|
- [ ] 配置文件路径遵循 `config/_<module>.yml` 约定
|
|
152
146
|
|
|
153
|
-
---
|
|
154
|
-
|
|
155
147
|
## 质量门禁
|
|
156
148
|
|
|
157
149
|
审查完成后按顺序执行:
|
|
@@ -163,8 +155,6 @@ if (!result.success)
|
|
|
163
155
|
|
|
164
156
|
优先使用 `pnpm --filter <app-name>` 指定应用。
|
|
165
157
|
|
|
166
|
-
---
|
|
167
|
-
|
|
168
158
|
## 输出要求
|
|
169
159
|
|
|
170
160
|
- 列出改动文件与原因
|
|
@@ -172,10 +162,12 @@ if (!result.success)
|
|
|
172
162
|
- 明确测试(单元 + E2E)与 lint 结果
|
|
173
163
|
- 确认重构后所有测试仍然通过
|
|
174
164
|
|
|
175
|
-
---
|
|
176
|
-
|
|
177
165
|
## 相关 Skills
|
|
178
166
|
|
|
179
167
|
- `hai-build`:项目架构总览与 TDD 工作流导航
|
|
180
168
|
- `hai-app-create`:TDD 驱动的应用功能创建规范
|
|
181
169
|
- `hai-app-tests`:TDD 测试规范(Red 阶段详细指引)
|
|
170
|
+
|
|
171
|
+
管理端数据读取在 HTTP 应用边界保留失败:查询异常返回 503 和可刷新错误页,分页任一页或权限关联失败均中止加载,禁止回退为空列表、零统计或空权限。未知权限代码返回 400,权限读取失败时不得继续写入。底层模块仍使用 HaiResult。
|
|
172
|
+
|
|
173
|
+
失败与降级提示也必须国际化:官网 API 按请求 locale 生成 AI 降级/邮件送达状态;未送达不能称为已接收。CLI 部署状态使用 cliM,Gallery 加密能力限制使用双语消息。中文与英文分别验证未配置、失败和成功路径,无需调用付费服务。
|