@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.
- package/README.md +10 -7
- package/dist/{chunk-GSMXGUYF.js → chunk-5IGMJVV5.js} +56 -38
- package/dist/chunk-5IGMJVV5.js.map +1 -0
- package/dist/{chunk-BPO34NRL.js → chunk-CFCXOKGV.js} +27 -5
- package/dist/chunk-CFCXOKGV.js.map +1 -0
- package/dist/cli-config-templates-5J5VKWWD.js +5 -0
- package/dist/{cli-config-templates-JCS7T3NU.js.map → cli-config-templates-5J5VKWWD.js.map} +1 -1
- package/dist/cli-skill-templates-JMSV2SVW.js +5 -0
- package/dist/{cli-skill-templates-IMYZPQRG.js.map → cli-skill-templates-JMSV2SVW.js.map} +1 -1
- package/dist/index.js +627 -107
- package/dist/index.js.map +1 -1
- package/dist/{src-N4NPS7AG.js → src-QNEJJ3BV.js} +261 -103
- package/dist/src-QNEJJ3BV.js.map +1 -0
- package/package.json +3 -2
- package/templates/README.md +40 -29
- package/templates/apps/admin/messages/en-US.json +5 -0
- package/templates/apps/admin/messages/zh-CN.json +5 -0
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +5 -5
- package/templates/apps/admin/src/routes/+page.svelte.hbs +32 -12
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +37 -20
- package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +33 -24
- package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +16 -13
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +16 -0
- package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +38 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-contract.ts.hbs +27 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +33 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +3 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +12 -0
- package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +18 -0
- package/templates/apps/api/apps/{{projectName}}-contract/tsconfig.json.hbs +12 -0
- package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +11 -0
- package/templates/apps/api/apps/{{projectName}}-contract/vitest.config.ts.hbs +9 -0
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +17 -0
- package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +19 -0
- package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +26 -0
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +55 -0
- package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +98 -0
- package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +21 -0
- package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +184 -0
- package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/app-procedures.ts.hbs +49 -0
- package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +18 -0
- package/templates/apps/api/apps/{{projectName}}-service/tests/service.test.ts.hbs +168 -0
- package/templates/apps/api/apps/{{projectName}}-service/tsconfig.json.hbs +16 -0
- package/templates/apps/api/apps/{{projectName}}-service/tsup.config.ts.hbs +11 -0
- package/templates/apps/api/apps/{{projectName}}-service/vitest.config.ts.hbs +9 -0
- package/templates/apps/api/e2e/api-service.spec.ts.hbs +18 -0
- package/templates/apps/api/eslint.config.js +32 -0
- package/templates/apps/api/package.json.hbs +29 -0
- package/templates/apps/api/playwright.config.ts.hbs +58 -0
- package/templates/apps/api/pnpm-workspace.yaml.hbs +14 -0
- package/templates/apps/api/tsconfig.base.json +15 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/.env.example.hbs +10 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +24 -7
- package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +18 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +16 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/en-US.json.hbs +6 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/zh-CN.json.hbs +6 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +51 -29
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +186 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.css.hbs +2 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.d.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +18 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/main.ts.hbs +14 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +1 -5
- package/templates/apps/fullstack/apps/{{projectName}}-app/tsconfig.json.hbs +10 -4
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +4 -3
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/.env.example.hbs +6 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +20 -6
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/eslint.config.js.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/index.html.hbs +15 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +33 -30
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +152 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.d.ts.hbs +1 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/main.ts.hbs +13 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/Cargo.toml.hbs +22 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/build.rs.hbs +6 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/capabilities/default.json.hbs +12 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128.png +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128@2x.png +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/32x32.png +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.icns +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.ico +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +16 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/main.rs.hbs +8 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +41 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +2 -8
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/tsconfig.json.hbs +9 -4
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +44 -21
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +15 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +15 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +25 -27
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/App.svelte.hbs +153 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.d.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +14 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +1 -5
- package/templates/apps/fullstack/apps/{{projectName}}-web/tsconfig.json.hbs +10 -4
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +3 -2
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +2 -1
- package/templates/apps/fullstack/eslint.config.js +0 -1
- package/templates/apps/fullstack/package.json.hbs +18 -17
- package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +5 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +14 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +13 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/{server-app.ts.hbs → app-server.ts.hbs} +23 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +4 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/{server-app.test.ts.hbs → app-server.test.ts.hbs} +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +9 -8
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/index.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +5 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ThemeSwitcher.svelte.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tsconfig.json.hbs +1 -1
- package/templates/apps/fullstack/playwright.config.ts.hbs +41 -4
- package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +15 -0
- package/templates/apps/h5/messages/en-US.json +3 -0
- package/templates/apps/h5/messages/zh-CN.json +3 -0
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +30 -29
- package/templates/apps/h5/src/routes/+page.svelte.hbs +17 -6
- package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +17 -6
- package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +17 -6
- package/templates/apps/mobile-app/.env.hbs +3 -0
- package/templates/apps/{android-app → mobile-app}/capacitor.config.ts.hbs +2 -2
- package/templates/apps/mobile-app/index.html.hbs +13 -0
- package/templates/apps/mobile-app/messages/en-US.json +36 -0
- package/templates/apps/mobile-app/messages/zh-CN.json +36 -0
- package/templates/apps/mobile-app/src/App.svelte.hbs +162 -0
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +54 -0
- package/templates/apps/{android-app → mobile-app}/src/lib/capacitor.ts +6 -7
- package/templates/apps/mobile-app/src/main.ts.hbs +11 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +35 -26
- package/templates/apps/website/src/routes/+page.svelte.hbs +28 -10
- package/templates/apps/website/src/routes/about/+page.svelte.hbs +11 -6
- package/templates/apps/website/src/routes/contact/+page.svelte.hbs +37 -34
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +27 -40
- package/templates/base/.gitignore.hbs +16 -0
- package/templates/base/{eslint.config.js → eslint.config.js.hbs} +4 -1
- package/templates/base/package.json.hbs +55 -31
- package/templates/base/playwright.config.ts +30 -3
- package/templates/base/pnpm-workspace.yaml.hbs +14 -0
- package/templates/base/src/app.d.ts.hbs +3 -0
- package/templates/base/src/app.html.hbs +7 -0
- package/templates/base/src/hooks.server.ts.hbs +17 -7
- package/templates/base/src/lib/server/init.ts.hbs +20 -12
- package/templates/base/static/favicon.png +0 -0
- package/templates/base/svelte.config.js.hbs +43 -3
- package/templates/base/tests/scaffold.test.ts.hbs +16 -1
- package/templates/base/tsconfig.json.hbs +50 -0
- package/templates/base/vite.config.ts.hbs +13 -3
- package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +5 -5
- package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +34 -32
- package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +46 -48
- package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +35 -31
- package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +41 -38
- package/templates/skills/AGENTS.md +60 -17
- package/templates/skills/CLAUDE.md +18 -6
- package/templates/skills/bridges/admin/AGENTS.md +66 -0
- package/templates/skills/bridges/admin/CLAUDE.md +19 -0
- package/templates/skills/bridges/admin/copilot-instructions.md +46 -0
- package/templates/skills/bridges/api/AGENTS.md +65 -0
- package/templates/skills/bridges/api/CLAUDE.md +19 -0
- package/templates/skills/bridges/api/copilot-instructions.md +46 -0
- package/templates/skills/bridges/fullstack/AGENTS.md +84 -0
- package/templates/skills/bridges/fullstack/CLAUDE.md +19 -0
- package/templates/skills/bridges/fullstack/copilot-instructions.md +54 -0
- package/templates/skills/bridges/h5/AGENTS.md +65 -0
- package/templates/skills/bridges/h5/CLAUDE.md +19 -0
- package/templates/skills/bridges/h5/copilot-instructions.md +44 -0
- package/templates/skills/bridges/mobile-app/AGENTS.md +77 -0
- package/templates/skills/bridges/mobile-app/CLAUDE.md +19 -0
- package/templates/skills/bridges/mobile-app/copilot-instructions.md +45 -0
- package/templates/skills/bridges/website/AGENTS.md +65 -0
- package/templates/skills/bridges/website/CLAUDE.md +19 -0
- package/templates/skills/bridges/website/copilot-instructions.md +44 -0
- package/templates/skills/copilot-instructions.md +29 -50
- package/templates/skills/hai-ai/SKILL.md +115 -648
- package/templates/skills/hai-ai/reference.md +197 -0
- package/templates/skills/hai-api-client/SKILL.md +3 -1
- package/templates/skills/hai-app-create/SKILL.md +2 -1
- package/templates/skills/hai-app-review/SKILL.md +2 -2
- package/templates/skills/hai-app-tests/SKILL.md +28 -3
- package/templates/skills/hai-capacitor/SKILL.md +14 -22
- package/templates/skills/hai-deploy/SKILL.md +12 -9
- package/templates/skills/hai-kit/SKILL.md +5 -3
- package/templates/skills/hai-serv/SKILL.md +2 -0
- package/templates/skills/hai-ui/SKILL.md +25 -11
- package/templates/skills/opencode.json +1 -6
- package/dist/chunk-BPO34NRL.js.map +0 -1
- package/dist/chunk-GSMXGUYF.js.map +0 -1
- package/dist/cli-config-templates-JCS7T3NU.js +0 -5
- package/dist/cli-skill-templates-IMYZPQRG.js +0 -5
- package/dist/src-N4NPS7AG.js.map +0 -1
- package/templates/apps/android-app/.env.hbs +0 -3
- package/templates/apps/android-app/messages/en-US.json +0 -24
- package/templates/apps/android-app/messages/zh-CN.json +0 -24
- package/templates/apps/android-app/src/lib/api.ts.hbs +0 -31
- package/templates/apps/android-app/src/routes/+layout.svelte.hbs +0 -42
- package/templates/apps/android-app/src/routes/+layout.ts +0 -7
- package/templates/apps/android-app/src/routes/+page.svelte.hbs +0 -27
- package/templates/apps/api/src/routes/+layout.svelte +0 -14
- package/templates/apps/api/src/routes/+page.svelte +0 -11
- package/templates/apps/api/src/routes/api/v1/health/+server.ts +0 -14
- package/templates/apps/api/src/routes/api/v1/items/+server.ts +0 -37
- package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +0 -30
- package/templates/apps/api/tests/health.test.ts.hbs +0 -26
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.html.hbs +0 -14
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/routes/+layout.svelte.hbs +0 -18
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/routes/+page.svelte.hbs +0 -162
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.html.hbs +0 -14
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/routes/+layout.svelte.hbs +0 -18
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/routes/+page.svelte.hbs +0 -162
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.html.hbs +0 -14
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/routes/+layout.svelte.hbs +0 -18
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/routes/+page.svelte.hbs +0 -162
- package/templates/apps/fullstack/pnpm-workspace.yaml +0 -7
- package/templates/base/tsconfig.json +0 -20
- /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/{theme.svelte.ts.hbs → theme-store.svelte.ts.hbs} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{{!-- 认证布局(登录/注册共用) --}}
|
|
2
|
-
<script lang=
|
|
2
|
+
<script lang='ts'>
|
|
3
3
|
/**
|
|
4
4
|
* 认证布局(登录/注册共用)
|
|
5
5
|
*/
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
children: Snippet
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
const { children }: Props = $props()
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
|
-
<div class=
|
|
16
|
-
<div class=
|
|
17
|
-
<div class=
|
|
15
|
+
<div class='min-h-screen flex items-center justify-center bg-base-200'>
|
|
16
|
+
<div class='card w-full max-w-md bg-base-100 shadow-xl'>
|
|
17
|
+
<div class='card-body'>
|
|
18
18
|
{@render children()}
|
|
19
19
|
</div>
|
|
20
20
|
</div>
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
{{!-- Admin 登录页面 --}}
|
|
2
|
-
<script lang=
|
|
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 { Alert, Button, FormField, Input } from '@h-ai/ui'
|
|
9
11
|
|
|
10
12
|
let username = $state('')
|
|
11
13
|
let password = $state('')
|
|
12
14
|
let error = $state('')
|
|
13
15
|
let loading = $state(false)
|
|
14
16
|
|
|
15
|
-
async function handleLogin() {
|
|
17
|
+
async function handleLogin(event: SubmitEvent) {
|
|
18
|
+
event.preventDefault()
|
|
16
19
|
error = ''
|
|
17
20
|
loading = true
|
|
18
21
|
try {
|
|
@@ -24,20 +27,23 @@
|
|
|
24
27
|
const data = await res.json()
|
|
25
28
|
if (data.success) {
|
|
26
29
|
window.location.href = '/admin'
|
|
27
|
-
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
28
32
|
{{#if hasI18n}}
|
|
29
33
|
error = data.error?.message || m.auth_login_failed()
|
|
30
34
|
{{else}}
|
|
31
35
|
error = data.error?.message || '登录失败'
|
|
32
36
|
{{/if}}
|
|
33
37
|
}
|
|
34
|
-
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
35
40
|
{{#if hasI18n}}
|
|
36
41
|
error = m.auth_error_network()
|
|
37
42
|
{{else}}
|
|
38
43
|
error = '网络错误'
|
|
39
44
|
{{/if}}
|
|
40
|
-
}
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
41
47
|
loading = false
|
|
42
48
|
}
|
|
43
49
|
}
|
|
@@ -52,47 +58,43 @@
|
|
|
52
58
|
</svelte:head>
|
|
53
59
|
|
|
54
60
|
{{#if hasI18n}}
|
|
55
|
-
<h2 class=
|
|
61
|
+
<h2 class='text-2xl font-bold text-center mb-6'>{m.auth_login_title()}</h2>
|
|
56
62
|
{{else}}
|
|
57
|
-
<h2 class=
|
|
63
|
+
<h2 class='text-2xl font-bold text-center mb-6'>登录</h2>
|
|
58
64
|
{{/if}}
|
|
59
65
|
|
|
60
66
|
{#if error}
|
|
61
|
-
<
|
|
62
|
-
<span>{error}</span>
|
|
63
|
-
</div>
|
|
67
|
+
<Alert variant='error' class='mb-4'>{error}</Alert>
|
|
64
68
|
{/if}
|
|
65
69
|
|
|
66
|
-
<form onsubmit={
|
|
67
|
-
<div class="form-control">
|
|
68
|
-
{{#if hasI18n}}
|
|
69
|
-
<label class="label" for="username"><span class="label-text">{m.auth_label_username()}</span></label>
|
|
70
|
-
{{else}}
|
|
71
|
-
<label class="label" for="username"><span class="label-text">用户名</span></label>
|
|
72
|
-
{{/if}}
|
|
73
|
-
<input id="username" type="text" class="input input-bordered w-full" bind:value={username} required />
|
|
74
|
-
</div>
|
|
75
|
-
<div class="form-control">
|
|
76
|
-
{{#if hasI18n}}
|
|
77
|
-
<label class="label" for="password"><span class="label-text">{m.auth_label_password()}</span></label>
|
|
78
|
-
{{else}}
|
|
79
|
-
<label class="label" for="password"><span class="label-text">密码</span></label>
|
|
80
|
-
{{/if}}
|
|
81
|
-
<input id="password" type="password" class="input input-bordered w-full" bind:value={password} required />
|
|
82
|
-
</div>
|
|
83
|
-
<button type="submit" class="btn btn-primary w-full" disabled={loading}>
|
|
70
|
+
<form onsubmit={handleLogin} class='space-y-4'>
|
|
84
71
|
{{#if hasI18n}}
|
|
72
|
+
<FormField label={m.auth_label_username()} required>
|
|
73
|
+
<Input id='username' type='text' bind:value={username} required />
|
|
74
|
+
</FormField>
|
|
75
|
+
<FormField label={m.auth_label_password()} required>
|
|
76
|
+
<Input id='password' type='password' bind:value={password} required />
|
|
77
|
+
</FormField>
|
|
78
|
+
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
85
79
|
{loading ? m.auth_login_loading() : m.auth_login_submit()}
|
|
80
|
+
</Button>
|
|
86
81
|
{{else}}
|
|
82
|
+
<FormField label='用户名' required>
|
|
83
|
+
<Input id='username' type='text' bind:value={username} required />
|
|
84
|
+
</FormField>
|
|
85
|
+
<FormField label='密码' required>
|
|
86
|
+
<Input id='password' type='password' bind:value={password} required />
|
|
87
|
+
</FormField>
|
|
88
|
+
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
87
89
|
{loading ? '登录中...' : '登录'}
|
|
90
|
+
</Button>
|
|
88
91
|
{{/if}}
|
|
89
|
-
</button>
|
|
90
92
|
</form>
|
|
91
93
|
|
|
92
|
-
<div class=
|
|
94
|
+
<div class='text-center mt-4'>
|
|
93
95
|
{{#if hasI18n}}
|
|
94
|
-
<a href=
|
|
96
|
+
<a href={resolve('/auth/register', {})} class='link link-primary text-sm'>{m.auth_login_to_register()}</a>
|
|
95
97
|
{{else}}
|
|
96
|
-
<a href=
|
|
98
|
+
<a href={resolve('/auth/register', {})} class='link link-primary text-sm'>没有账号?立即注册</a>
|
|
97
99
|
{{/if}}
|
|
98
100
|
</div>
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{{!-- Admin 注册页面 --}}
|
|
2
|
-
<script lang=
|
|
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 { Alert, Button, FormField, Input } from '@h-ai/ui'
|
|
9
11
|
|
|
10
12
|
let username = $state('')
|
|
11
13
|
let email = $state('')
|
|
@@ -14,7 +16,8 @@
|
|
|
14
16
|
let error = $state('')
|
|
15
17
|
let loading = $state(false)
|
|
16
18
|
|
|
17
|
-
async function handleRegister() {
|
|
19
|
+
async function handleRegister(event: SubmitEvent) {
|
|
20
|
+
event.preventDefault()
|
|
18
21
|
error = ''
|
|
19
22
|
if (password !== confirmPassword) {
|
|
20
23
|
{{#if hasI18n}}
|
|
@@ -34,20 +37,23 @@
|
|
|
34
37
|
const data = await res.json()
|
|
35
38
|
if (data.success) {
|
|
36
39
|
window.location.href = '/auth/login'
|
|
37
|
-
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
38
42
|
{{#if hasI18n}}
|
|
39
43
|
error = data.error?.message || m.auth_register_failed()
|
|
40
44
|
{{else}}
|
|
41
45
|
error = data.error?.message || '注册失败'
|
|
42
46
|
{{/if}}
|
|
43
47
|
}
|
|
44
|
-
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
45
50
|
{{#if hasI18n}}
|
|
46
51
|
error = m.auth_error_network()
|
|
47
52
|
{{else}}
|
|
48
53
|
error = '网络错误'
|
|
49
54
|
{{/if}}
|
|
50
|
-
}
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
51
57
|
loading = false
|
|
52
58
|
}
|
|
53
59
|
}
|
|
@@ -62,63 +68,55 @@
|
|
|
62
68
|
</svelte:head>
|
|
63
69
|
|
|
64
70
|
{{#if hasI18n}}
|
|
65
|
-
<h2 class=
|
|
71
|
+
<h2 class='text-2xl font-bold text-center mb-6'>{m.auth_register_title()}</h2>
|
|
66
72
|
{{else}}
|
|
67
|
-
<h2 class=
|
|
73
|
+
<h2 class='text-2xl font-bold text-center mb-6'>注册</h2>
|
|
68
74
|
{{/if}}
|
|
69
75
|
|
|
70
76
|
{#if error}
|
|
71
|
-
<
|
|
72
|
-
<span>{error}</span>
|
|
73
|
-
</div>
|
|
77
|
+
<Alert variant='error' class='mb-4'>{error}</Alert>
|
|
74
78
|
{/if}
|
|
75
79
|
|
|
76
|
-
<form onsubmit={
|
|
77
|
-
<div class="form-control">
|
|
78
|
-
{{#if hasI18n}}
|
|
79
|
-
<label class="label" for="username"><span class="label-text">{m.auth_label_username()}</span></label>
|
|
80
|
-
{{else}}
|
|
81
|
-
<label class="label" for="username"><span class="label-text">用户名</span></label>
|
|
82
|
-
{{/if}}
|
|
83
|
-
<input id="username" type="text" class="input input-bordered w-full" bind:value={username} required />
|
|
84
|
-
</div>
|
|
85
|
-
<div class="form-control">
|
|
86
|
-
{{#if hasI18n}}
|
|
87
|
-
<label class="label" for="email"><span class="label-text">{m.auth_label_email()}</span></label>
|
|
88
|
-
{{else}}
|
|
89
|
-
<label class="label" for="email"><span class="label-text">邮箱</span></label>
|
|
90
|
-
{{/if}}
|
|
91
|
-
<input id="email" type="email" class="input input-bordered w-full" bind:value={email} required />
|
|
92
|
-
</div>
|
|
93
|
-
<div class="form-control">
|
|
94
|
-
{{#if hasI18n}}
|
|
95
|
-
<label class="label" for="password"><span class="label-text">{m.auth_label_password()}</span></label>
|
|
96
|
-
{{else}}
|
|
97
|
-
<label class="label" for="password"><span class="label-text">密码</span></label>
|
|
98
|
-
{{/if}}
|
|
99
|
-
<input id="password" type="password" class="input input-bordered w-full" bind:value={password} required />
|
|
100
|
-
</div>
|
|
101
|
-
<div class="form-control">
|
|
102
|
-
{{#if hasI18n}}
|
|
103
|
-
<label class="label" for="confirmPassword"><span class="label-text">{m.auth_label_confirm_password()}</span></label>
|
|
104
|
-
{{else}}
|
|
105
|
-
<label class="label" for="confirmPassword"><span class="label-text">确认密码</span></label>
|
|
106
|
-
{{/if}}
|
|
107
|
-
<input id="confirmPassword" type="password" class="input input-bordered w-full" bind:value={confirmPassword} required />
|
|
108
|
-
</div>
|
|
109
|
-
<button type="submit" class="btn btn-primary w-full" disabled={loading}>
|
|
80
|
+
<form onsubmit={handleRegister} class='space-y-4'>
|
|
110
81
|
{{#if hasI18n}}
|
|
82
|
+
<FormField label={m.auth_label_username()} required>
|
|
83
|
+
<Input id='username' type='text' bind:value={username} required />
|
|
84
|
+
</FormField>
|
|
85
|
+
<FormField label={m.auth_label_email()} required>
|
|
86
|
+
<Input id='email' type='email' bind:value={email} required />
|
|
87
|
+
</FormField>
|
|
88
|
+
<FormField label={m.auth_label_password()} required>
|
|
89
|
+
<Input id='password' type='password' bind:value={password} required />
|
|
90
|
+
</FormField>
|
|
91
|
+
<FormField label={m.auth_label_confirm_password()} required>
|
|
92
|
+
<Input id='confirmPassword' type='password' bind:value={confirmPassword} required />
|
|
93
|
+
</FormField>
|
|
94
|
+
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
111
95
|
{loading ? m.auth_register_loading() : m.auth_register_submit()}
|
|
96
|
+
</Button>
|
|
112
97
|
{{else}}
|
|
98
|
+
<FormField label='用户名' required>
|
|
99
|
+
<Input id='username' type='text' bind:value={username} required />
|
|
100
|
+
</FormField>
|
|
101
|
+
<FormField label='邮箱' required>
|
|
102
|
+
<Input id='email' type='email' bind:value={email} required />
|
|
103
|
+
</FormField>
|
|
104
|
+
<FormField label='密码' required>
|
|
105
|
+
<Input id='password' type='password' bind:value={password} required />
|
|
106
|
+
</FormField>
|
|
107
|
+
<FormField label='确认密码' required>
|
|
108
|
+
<Input id='confirmPassword' type='password' bind:value={confirmPassword} required />
|
|
109
|
+
</FormField>
|
|
110
|
+
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
113
111
|
{loading ? '注册中...' : '注册'}
|
|
112
|
+
</Button>
|
|
114
113
|
{{/if}}
|
|
115
|
-
</button>
|
|
116
114
|
</form>
|
|
117
115
|
|
|
118
|
-
<div class=
|
|
116
|
+
<div class='text-center mt-4'>
|
|
119
117
|
{{#if hasI18n}}
|
|
120
|
-
<a href=
|
|
118
|
+
<a href={resolve('/auth/login', {})} class='link link-primary text-sm'>{m.auth_register_to_login()}</a>
|
|
121
119
|
{{else}}
|
|
122
|
-
<a href=
|
|
120
|
+
<a href={resolve('/auth/login', {})} class='link link-primary text-sm'>已有账号?立即登录</a>
|
|
123
121
|
{{/if}}
|
|
124
122
|
</div>
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
{{!-- H5 登录页面 --}}
|
|
2
|
-
<script lang=
|
|
2
|
+
<script lang='ts'>
|
|
3
3
|
/**
|
|
4
4
|
* H5 登录页面
|
|
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 { Alert, Button, FormField, Input } from '@h-ai/ui'
|
|
9
11
|
|
|
10
12
|
let username = $state('')
|
|
11
13
|
let password = $state('')
|
|
12
14
|
let error = $state('')
|
|
13
15
|
let loading = $state(false)
|
|
14
16
|
|
|
15
|
-
async function handleLogin() {
|
|
17
|
+
async function handleLogin(event: SubmitEvent) {
|
|
18
|
+
event.preventDefault()
|
|
16
19
|
error = ''
|
|
17
20
|
loading = true
|
|
18
21
|
try {
|
|
@@ -24,20 +27,23 @@
|
|
|
24
27
|
const data = await res.json()
|
|
25
28
|
if (data.success) {
|
|
26
29
|
window.location.href = '/'
|
|
27
|
-
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
28
32
|
{{#if hasI18n}}
|
|
29
33
|
error = data.error?.message || m.auth_login_failed()
|
|
30
34
|
{{else}}
|
|
31
35
|
error = data.error?.message || '登录失败'
|
|
32
36
|
{{/if}}
|
|
33
37
|
}
|
|
34
|
-
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
35
40
|
{{#if hasI18n}}
|
|
36
41
|
error = m.auth_error_network()
|
|
37
42
|
{{else}}
|
|
38
43
|
error = '网络错误'
|
|
39
44
|
{{/if}}
|
|
40
|
-
}
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
41
47
|
loading = false
|
|
42
48
|
}
|
|
43
49
|
}
|
|
@@ -51,48 +57,46 @@
|
|
|
51
57
|
{{/if}}
|
|
52
58
|
</svelte:head>
|
|
53
59
|
|
|
54
|
-
<div class=
|
|
60
|
+
<div class='p-4 pt-12 space-y-4'>
|
|
55
61
|
{{#if hasI18n}}
|
|
56
|
-
<h1 class=
|
|
62
|
+
<h1 class='text-2xl font-black tracking-tight text-center'>{m.auth_login_title()}</h1>
|
|
57
63
|
{{else}}
|
|
58
|
-
<h1 class=
|
|
64
|
+
<h1 class='text-2xl font-black tracking-tight text-center'>登录</h1>
|
|
59
65
|
{{/if}}
|
|
60
66
|
|
|
61
67
|
{#if error}
|
|
62
|
-
<
|
|
63
|
-
<span>{error}</span>
|
|
64
|
-
</div>
|
|
68
|
+
<Alert variant='error'>{error}</Alert>
|
|
65
69
|
{/if}
|
|
66
70
|
|
|
67
|
-
<form onsubmit={
|
|
68
|
-
<div class="form-control">
|
|
69
|
-
{{#if hasI18n}}
|
|
70
|
-
<input type="text" placeholder={m.auth_label_username()} class="input input-bordered w-full" bind:value={username} required />
|
|
71
|
-
{{else}}
|
|
72
|
-
<input type="text" placeholder="用户名" class="input input-bordered w-full" bind:value={username} required />
|
|
73
|
-
{{/if}}
|
|
74
|
-
</div>
|
|
75
|
-
<div class="form-control">
|
|
76
|
-
{{#if hasI18n}}
|
|
77
|
-
<input type="password" placeholder={m.auth_label_password()} class="input input-bordered w-full" bind:value={password} required />
|
|
78
|
-
{{else}}
|
|
79
|
-
<input type="password" placeholder="密码" class="input input-bordered w-full" bind:value={password} required />
|
|
80
|
-
{{/if}}
|
|
81
|
-
</div>
|
|
82
|
-
<button type="submit" class="btn btn-primary w-full" disabled={loading}>
|
|
71
|
+
<form onsubmit={handleLogin} class='space-y-4'>
|
|
83
72
|
{{#if hasI18n}}
|
|
73
|
+
<FormField label={m.auth_label_username()} required>
|
|
74
|
+
<Input id='username' type='text' bind:value={username} required />
|
|
75
|
+
</FormField>
|
|
76
|
+
<FormField label={m.auth_label_password()} required>
|
|
77
|
+
<Input id='password' type='password' bind:value={password} required />
|
|
78
|
+
</FormField>
|
|
79
|
+
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
84
80
|
{loading ? m.auth_login_loading() : m.auth_login_submit()}
|
|
81
|
+
</Button>
|
|
85
82
|
{{else}}
|
|
83
|
+
<FormField label='用户名' required>
|
|
84
|
+
<Input id='username' type='text' bind:value={username} required />
|
|
85
|
+
</FormField>
|
|
86
|
+
<FormField label='密码' required>
|
|
87
|
+
<Input id='password' type='password' bind:value={password} required />
|
|
88
|
+
</FormField>
|
|
89
|
+
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
86
90
|
{loading ? '登录中...' : '登录'}
|
|
91
|
+
</Button>
|
|
87
92
|
{{/if}}
|
|
88
|
-
</button>
|
|
89
93
|
</form>
|
|
90
94
|
|
|
91
|
-
<div class=
|
|
95
|
+
<div class='text-center'>
|
|
92
96
|
{{#if hasI18n}}
|
|
93
|
-
<a href=
|
|
97
|
+
<a href={resolve('/auth/register', {})} class='link link-primary text-sm'>{m.auth_login_to_register()}</a>
|
|
94
98
|
{{else}}
|
|
95
|
-
<a href=
|
|
99
|
+
<a href={resolve('/auth/register', {})} class='link link-primary text-sm'>没有账号?立即注册</a>
|
|
96
100
|
{{/if}}
|
|
97
101
|
</div>
|
|
98
102
|
</div>
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{{!-- H5 注册页面 --}}
|
|
2
|
-
<script lang=
|
|
2
|
+
<script lang='ts'>
|
|
3
3
|
/**
|
|
4
4
|
* H5 注册页面
|
|
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 { Alert, Button, FormField, Input } from '@h-ai/ui'
|
|
9
11
|
|
|
10
12
|
let username = $state('')
|
|
11
13
|
let password = $state('')
|
|
@@ -13,7 +15,8 @@
|
|
|
13
15
|
let error = $state('')
|
|
14
16
|
let loading = $state(false)
|
|
15
17
|
|
|
16
|
-
async function handleRegister() {
|
|
18
|
+
async function handleRegister(event: SubmitEvent) {
|
|
19
|
+
event.preventDefault()
|
|
17
20
|
error = ''
|
|
18
21
|
if (password !== confirmPassword) {
|
|
19
22
|
{{#if hasI18n}}
|
|
@@ -33,20 +36,23 @@
|
|
|
33
36
|
const data = await res.json()
|
|
34
37
|
if (data.success) {
|
|
35
38
|
window.location.href = '/auth/login'
|
|
36
|
-
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
37
41
|
{{#if hasI18n}}
|
|
38
42
|
error = data.error?.message || m.auth_register_failed()
|
|
39
43
|
{{else}}
|
|
40
44
|
error = data.error?.message || '注册失败'
|
|
41
45
|
{{/if}}
|
|
42
46
|
}
|
|
43
|
-
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
44
49
|
{{#if hasI18n}}
|
|
45
50
|
error = m.auth_error_network()
|
|
46
51
|
{{else}}
|
|
47
52
|
error = '网络错误'
|
|
48
53
|
{{/if}}
|
|
49
|
-
}
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
50
56
|
loading = false
|
|
51
57
|
}
|
|
52
58
|
}
|
|
@@ -60,55 +66,52 @@
|
|
|
60
66
|
{{/if}}
|
|
61
67
|
</svelte:head>
|
|
62
68
|
|
|
63
|
-
<div class=
|
|
69
|
+
<div class='p-4 pt-12 space-y-4'>
|
|
64
70
|
{{#if hasI18n}}
|
|
65
|
-
<h1 class=
|
|
71
|
+
<h1 class='text-2xl font-black tracking-tight text-center'>{m.auth_register_title()}</h1>
|
|
66
72
|
{{else}}
|
|
67
|
-
<h1 class=
|
|
73
|
+
<h1 class='text-2xl font-black tracking-tight text-center'>注册</h1>
|
|
68
74
|
{{/if}}
|
|
69
75
|
|
|
70
76
|
{#if error}
|
|
71
|
-
<
|
|
72
|
-
<span>{error}</span>
|
|
73
|
-
</div>
|
|
77
|
+
<Alert variant='error'>{error}</Alert>
|
|
74
78
|
{/if}
|
|
75
79
|
|
|
76
|
-
<form onsubmit={
|
|
77
|
-
<div class="form-control">
|
|
78
|
-
{{#if hasI18n}}
|
|
79
|
-
<input type="text" placeholder={m.auth_label_username()} class="input input-bordered w-full" bind:value={username} required />
|
|
80
|
-
{{else}}
|
|
81
|
-
<input type="text" placeholder="用户名" class="input input-bordered w-full" bind:value={username} required />
|
|
82
|
-
{{/if}}
|
|
83
|
-
</div>
|
|
84
|
-
<div class="form-control">
|
|
85
|
-
{{#if hasI18n}}
|
|
86
|
-
<input type="password" placeholder={m.auth_label_password()} class="input input-bordered w-full" bind:value={password} required />
|
|
87
|
-
{{else}}
|
|
88
|
-
<input type="password" placeholder="密码" class="input input-bordered w-full" bind:value={password} required />
|
|
89
|
-
{{/if}}
|
|
90
|
-
</div>
|
|
91
|
-
<div class="form-control">
|
|
92
|
-
{{#if hasI18n}}
|
|
93
|
-
<input type="password" placeholder={m.auth_label_confirm_password()} class="input input-bordered w-full" bind:value={confirmPassword} required />
|
|
94
|
-
{{else}}
|
|
95
|
-
<input type="password" placeholder="确认密码" class="input input-bordered w-full" bind:value={confirmPassword} required />
|
|
96
|
-
{{/if}}
|
|
97
|
-
</div>
|
|
98
|
-
<button type="submit" class="btn btn-primary w-full" disabled={loading}>
|
|
80
|
+
<form onsubmit={handleRegister} class='space-y-4'>
|
|
99
81
|
{{#if hasI18n}}
|
|
82
|
+
<FormField label={m.auth_label_username()} required>
|
|
83
|
+
<Input id='username' type='text' bind:value={username} required />
|
|
84
|
+
</FormField>
|
|
85
|
+
<FormField label={m.auth_label_password()} required>
|
|
86
|
+
<Input id='password' type='password' bind:value={password} required />
|
|
87
|
+
</FormField>
|
|
88
|
+
<FormField label={m.auth_label_confirm_password()} required>
|
|
89
|
+
<Input id='confirmPassword' type='password' bind:value={confirmPassword} required />
|
|
90
|
+
</FormField>
|
|
91
|
+
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
100
92
|
{loading ? m.auth_register_loading() : m.auth_register_submit()}
|
|
93
|
+
</Button>
|
|
101
94
|
{{else}}
|
|
95
|
+
<FormField label='用户名' required>
|
|
96
|
+
<Input id='username' type='text' bind:value={username} required />
|
|
97
|
+
</FormField>
|
|
98
|
+
<FormField label='密码' required>
|
|
99
|
+
<Input id='password' type='password' bind:value={password} required />
|
|
100
|
+
</FormField>
|
|
101
|
+
<FormField label='确认密码' required>
|
|
102
|
+
<Input id='confirmPassword' type='password' bind:value={confirmPassword} required />
|
|
103
|
+
</FormField>
|
|
104
|
+
<Button type='submit' variant='primary' loading={loading} class='w-full'>
|
|
102
105
|
{loading ? '注册中...' : '注册'}
|
|
106
|
+
</Button>
|
|
103
107
|
{{/if}}
|
|
104
|
-
</button>
|
|
105
108
|
</form>
|
|
106
109
|
|
|
107
|
-
<div class=
|
|
110
|
+
<div class='text-center'>
|
|
108
111
|
{{#if hasI18n}}
|
|
109
|
-
<a href=
|
|
112
|
+
<a href={resolve('/auth/login', {})} class='link link-primary text-sm'>{m.auth_register_to_login()}</a>
|
|
110
113
|
{{else}}
|
|
111
|
-
<a href=
|
|
114
|
+
<a href={resolve('/auth/login', {})} class='link link-primary text-sm'>已有账号?立即登录</a>
|
|
112
115
|
{{/if}}
|
|
113
116
|
</div>
|
|
114
117
|
</div>
|
|
@@ -1,27 +1,70 @@
|
|
|
1
1
|
# AGENTS.md
|
|
2
2
|
|
|
3
|
-
> 通用 AI
|
|
3
|
+
> 通用 AI 编程助手入口。优先阅读 `README.md`、现有测试与 `.agents/skills/`;本文件同时作为 `hai add` / AI 支持回填时的 generic fallback。
|
|
4
|
+
|
|
5
|
+
## 行为契约
|
|
6
|
+
|
|
7
|
+
1. 每次响应第一行写:`规模: XS|S|M|L — <一句话意图>`。
|
|
8
|
+
2. 任务规模 ≥ M 时,先回顾 `README.md`、相关测试、现有 AI 指导文件与对应 `.agents/skills/*/SKILL.md`。
|
|
9
|
+
3. 在写第一行新代码前,用 1 行回答 Q1-Q7:复用对象、可扩展点、当前真实调用点、更简方案、知识泄漏、一致性、性能/安全对比。
|
|
10
|
+
4. 任务规模 ≥ M 时,同步说明直接/间接影响:文件、接口/类型、导入、测试、文档、依赖方。
|
|
11
|
+
5. 不能把未运行或失败的门禁写成“已通过”。
|
|
12
|
+
|
|
13
|
+
## 必要性自检(M / L 任务必须输出)
|
|
14
|
+
|
|
15
|
+
- Q1:仓库里是否已有同类能力可复用?
|
|
16
|
+
- Q2:能否扩展现有文件/模块,而不是新建抽象或目录?
|
|
17
|
+
- Q3:当前真实调用点是什么,不要为了“以后可能”而设计?
|
|
18
|
+
- Q4:有没有少一层、少一个参数、少一个文件的更简单方案?
|
|
19
|
+
- Q5:这次改动会不会让使用方必须了解新的内部细节?
|
|
20
|
+
- Q6:是否与当前项目命名、目录、脚本和对外暴露形式一致?
|
|
21
|
+
- Q7:是否已经比较过更高效或更安全的做法?
|
|
22
|
+
|
|
23
|
+
## 影响分析(M / L 任务必须输出)
|
|
24
|
+
|
|
25
|
+
- 直接影响:修改 / 新建 / 删除哪些文件;哪些脚本、类型、配置或路由会变。
|
|
26
|
+
- 间接影响:哪些测试、README、i18n、示例、共享类型、依赖方需要同步更新。
|
|
4
27
|
|
|
5
28
|
## 项目概述
|
|
6
29
|
|
|
7
|
-
|
|
30
|
+
本项目由 hai-framework CLI 生成。单应用工程通常在 `src/` 下开发;多包工程会同时包含 `packages/*` 与 `apps/*`。实际结构以当前项目目录为准。
|
|
31
|
+
|
|
32
|
+
## 通用硬约束
|
|
33
|
+
|
|
34
|
+
- 先用全局检索确认是否已有实现、测试和文档;优先复用,避免为“未来可能”新增抽象。
|
|
35
|
+
- 统一模式:`module.init(config) -> use -> module.close()`;公共 `@h-ai/*` API 返回 `HaiResult<T>` 或 `Promise<HaiResult<T>>`,业务错误不要直接 `throw`。
|
|
36
|
+
- 禁止 `any`、无说明的危险类型断言、`console.log`、硬编码密钥。
|
|
37
|
+
- 用户可见文本必须走 i18n;如果项目启用了 `messages/zh-CN.json` 与 `messages/en-US.json`,修改文案时必须同步更新。
|
|
38
|
+
- 代码注释中文,日志消息英文。
|
|
39
|
+
- 如项目使用 SvelteKit,`+server.ts` 只能导出 HTTP handler 与 SvelteKit 允许的配置项;辅助函数放到 `src/lib/**`。
|
|
40
|
+
|
|
41
|
+
## 工作流程
|
|
42
|
+
|
|
43
|
+
1. 先搜索现有实现、引用点、测试和 README。
|
|
44
|
+
2. 小步修改,优先保持目录结构、公开 API 和脚本稳定。
|
|
45
|
+
3. 改动公共类型、配置、模板或共享逻辑时,同步更新所有调用方与文档。
|
|
46
|
+
4. 每次改动后按影响范围运行质量门禁并记录结果。
|
|
47
|
+
|
|
48
|
+
## 质量门禁
|
|
8
49
|
|
|
9
|
-
|
|
50
|
+
```bash
|
|
51
|
+
pnpm typecheck
|
|
52
|
+
pnpm lint
|
|
53
|
+
pnpm build
|
|
54
|
+
pnpm test
|
|
55
|
+
pnpm test:e2e
|
|
56
|
+
```
|
|
10
57
|
|
|
11
|
-
|
|
12
|
-
- **TDD 驱动开发**:先写测试(Red)→ 确认失败 → 再实现(Green)→ 确认通过 → 重构(Refactor)
|
|
13
|
-
- 禁止 `any`(使用 `unknown` + 缩窄),禁止 `console.log`(使用 `core.logger`)
|
|
14
|
-
- 用户可见文本走 i18n,代码注释中文,日志英文
|
|
15
|
-
- HaiResult 错误直接透传,不重新包装
|
|
16
|
-
- 框架模块公共 API 不抛异常,统一返回 `HaiResult<T>`;不要用 `try/catch` 处理模块返回的错误
|
|
17
|
-
- 质量门禁:`pnpm typecheck && pnpm lint && pnpm test && pnpm --filter <app> test:e2e`
|
|
58
|
+
## 完成条件
|
|
18
59
|
|
|
19
|
-
|
|
60
|
+
- typecheck / lint / build / test / test:e2e 状态明确;失败必须先修复或明确说明原因。
|
|
61
|
+
- README、i18n、测试、代码注释与实现保持一致。
|
|
62
|
+
- 最终回复说明执行过的门禁、已同步的文档 / 依赖方,以及未完成项。
|
|
20
63
|
|
|
21
|
-
|
|
64
|
+
## Skills 路由
|
|
22
65
|
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
66
|
+
- `hai-build/SKILL.md`:架构、构建和质量门禁。
|
|
67
|
+
- `hai-app-create/SKILL.md`:应用骨架、路由、页面与 API 模板。
|
|
68
|
+
- `hai-app-review/SKILL.md`:应用代码审查。
|
|
69
|
+
- `hai-app-tests/SKILL.md`:Vitest / Playwright 测试策略。
|
|
70
|
+
- 具体模块按需读取对应 `hai-*` Skill。
|