@dev-workflow/skill 0.1.0

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 (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -0
  3. package/bin/cli.mjs +9 -0
  4. package/package.json +39 -0
  5. package/skills/artifact/README.md +23 -0
  6. package/skills/execution/devFlow/SKILL.md +341 -0
  7. package/skills/execution/devFlow/agents/openai.yaml +7 -0
  8. package/skills/execution/devFlow/domains/backend/references/api-tech.md +173 -0
  9. package/skills/execution/devFlow/domains/backend/scripts/check_api_tech_doc.mjs +170 -0
  10. package/skills/execution/devFlow/domains/backend/scripts/check_api_tech_doc.test.mjs +89 -0
  11. package/skills/execution/devFlow/domains/backend/templates/api-tech.md +164 -0
  12. package/skills/execution/devFlow/domains/frontend/references/contract-check.md +270 -0
  13. package/skills/execution/devFlow/domains/frontend/references/foundation-freeze.md +92 -0
  14. package/skills/execution/devFlow/domains/frontend/references/lightweight-flow.md +55 -0
  15. package/skills/execution/devFlow/domains/frontend/references/page-build.md +268 -0
  16. package/skills/execution/devFlow/domains/frontend/references/page-tech.md +414 -0
  17. package/skills/execution/devFlow/domains/frontend/references/product-design-specs/field-common.md +69 -0
  18. package/skills/execution/devFlow/domains/frontend/references/product-design-specs/fields-complex.md +105 -0
  19. package/skills/execution/devFlow/domains/frontend/references/product-design-specs/fields-id-code.md +44 -0
  20. package/skills/execution/devFlow/domains/frontend/references/product-design-specs/fields-number-date.md +106 -0
  21. package/skills/execution/devFlow/domains/frontend/references/product-design-specs/fields-relation.md +144 -0
  22. package/skills/execution/devFlow/domains/frontend/references/product-design-specs/fields-selectors.md +102 -0
  23. package/skills/execution/devFlow/domains/frontend/references/product-design-specs/fields-text.md +130 -0
  24. package/skills/execution/devFlow/domains/frontend/references/product-design-specs/index.md +42 -0
  25. package/skills/execution/devFlow/domains/frontend/references/product-design-specs/interaction.md +61 -0
  26. package/skills/execution/devFlow/domains/frontend/scripts/check_page_tech_doc.mjs +82 -0
  27. package/skills/execution/devFlow/domains/frontend/scripts/contract_check_static.mjs +145 -0
  28. package/skills/execution/devFlow/domains/frontend/scripts/generate_foundation_summary.mjs +224 -0
  29. package/skills/execution/devFlow/domains/frontend/templates/contract-report.md +66 -0
  30. package/skills/execution/devFlow/domains/frontend/templates/mini-plan-l0.md +7 -0
  31. package/skills/execution/devFlow/domains/frontend/templates/mini-plan-l1.md +9 -0
  32. package/skills/execution/devFlow/domains/frontend/templates/page-build/component.tsx.tpl +8 -0
  33. package/skills/execution/devFlow/domains/frontend/templates/page-build/constants.ts.tpl +5 -0
  34. package/skills/execution/devFlow/domains/frontend/templates/page-build/index.ts.tpl +1 -0
  35. package/skills/execution/devFlow/domains/frontend/templates/page-build/route.tsx.tpl +11 -0
  36. package/skills/execution/devFlow/domains/frontend/templates/page-build/service.ts.tpl +17 -0
  37. package/skills/execution/devFlow/domains/frontend/templates/page-build/types.ts.tpl +11 -0
  38. package/skills/execution/devFlow/domains/frontend/templates/page-tech.md +103 -0
  39. package/skills/execution/devFlow/domains/requirement/references/prd-review.md +129 -0
  40. package/skills/execution/devFlow/domains/requirement/templates/prd-review.md +57 -0
  41. package/skills/execution/figmaSync/SKILL.md +349 -0
  42. package/skills/execution/figmaSync/chapters/apply.md +177 -0
  43. package/skills/execution/figmaSync/chapters/plan.md +359 -0
  44. package/skills/execution/figmaSync/chapters/prepare.md +47 -0
  45. package/skills/execution/figmaSync/examples/native-css.md +129 -0
  46. package/skills/execution/figmaSync/examples/plan-doc-sample.md +39 -0
  47. package/skills/execution/figmaSync/scripts/_shared/session-log.mjs +82 -0
  48. package/skills/execution/figmaSync/scripts/_shared/token-resolver.mjs +132 -0
  49. package/skills/execution/figmaSync/scripts/_shared/token-source.mjs +145 -0
  50. package/skills/execution/figmaSync/scripts/figma-sync-report.mjs +448 -0
  51. package/skills/execution/figmaSync/scripts/icon-inventory.mjs +165 -0
  52. package/skills/execution/figmaSync/scripts/lookup-var.mjs +184 -0
  53. package/skills/execution/figmaSync/scripts/match-token.mjs +210 -0
  54. package/skills/execution/figmaSync/scripts/prepare-check.mjs +290 -0
  55. package/skills/execution/figmaSync/scripts/verify-plan.mjs +458 -0
  56. package/skills/execution/figmaSync/snapshots/README.md +13 -0
  57. package/skills/execution/figmaSync/snapshots/last-sync.md +1538 -0
  58. package/skills/execution/figmaSync/templates/PLAN.md.tpl +227 -0
  59. package/skills/review/consistency-checker/SKILL.md +112 -0
  60. package/skills/review/consistency-checker/rules/README.md +16 -0
  61. package/src/cli/agents.mjs +16 -0
  62. package/src/cli/copy.mjs +95 -0
  63. package/src/cli/install.mjs +131 -0
  64. package/src/cli/prompts.mjs +66 -0
  65. package/tools/lark/README.md +78 -0
  66. package/tools/lark/references/lark-doc.md +156 -0
  67. package/tools/lark/references/lark-read.md +235 -0
  68. package/tools/lark/references/prepare.md +147 -0
  69. package/tools/lark/scripts/lark_api.mjs +404 -0
  70. package/tools/lark/scripts/lark_api.test.mjs +63 -0
  71. package/tools/lark/scripts/lark_check_permissions.mjs +52 -0
  72. package/tools/lark/scripts/lark_publish_doc.mjs +299 -0
  73. package/tools/lark/scripts/lark_read_docx.mjs +328 -0
  74. package/tools/lark/scripts/markdown_to_lark_blocks.mjs +397 -0
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs'
3
+ import path from 'node:path'
4
+
5
+ function parseArgs(argv) {
6
+ const args = {
7
+ routeDir: null,
8
+ service: null,
9
+ types: null,
10
+ constants: null,
11
+ json: false
12
+ }
13
+ for (const arg of argv.slice(2)) {
14
+ if (arg.startsWith('--route-dir=')) args.routeDir = arg.slice('--route-dir='.length)
15
+ else if (arg.startsWith('--service=')) args.service = arg.slice('--service='.length)
16
+ else if (arg.startsWith('--types=')) args.types = arg.slice('--types='.length)
17
+ else if (arg.startsWith('--constants=')) args.constants = arg.slice('--constants='.length)
18
+ else if (arg === '--json') args.json = true
19
+ }
20
+ return args
21
+ }
22
+
23
+ function exists(filePath) {
24
+ return Boolean(filePath && fs.existsSync(filePath))
25
+ }
26
+
27
+ function isKebabCase(value) {
28
+ return /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(value)
29
+ }
30
+
31
+ function isPascalCase(value) {
32
+ return /^[A-Z][A-Za-z0-9]*\.tsx$/.test(value)
33
+ }
34
+
35
+ function exportsPascalComponent(filePath) {
36
+ const source = fs.readFileSync(filePath, 'utf-8')
37
+ return (
38
+ /export\s+function\s+[A-Z][A-Za-z0-9]*\s*\(/.test(source) ||
39
+ /export\s+const\s+[A-Z][A-Za-z0-9]*\s*=/.test(source)
40
+ )
41
+ }
42
+
43
+ function collectFiles(dir) {
44
+ if (!fs.existsSync(dir)) return []
45
+ const out = []
46
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
47
+ const full = path.join(dir, entry.name)
48
+ if (entry.isDirectory()) out.push(...collectFiles(full))
49
+ else out.push(full)
50
+ }
51
+ return out
52
+ }
53
+
54
+ async function checkRouteTreeModified() {
55
+ const gitDir = '.git'
56
+ if (!fs.existsSync(gitDir)) return { status: 'unknown', modified: false }
57
+ try {
58
+ const { execSync } = await import('node:child_process')
59
+ const output = execSync('git status --porcelain -- src/routeTree.gen.ts', { encoding: 'utf-8' })
60
+ return { status: 'ok', modified: output.trim().length > 0 }
61
+ } catch {
62
+ return { status: 'unknown', modified: false }
63
+ }
64
+ }
65
+
66
+ async function main() {
67
+ const args = parseArgs(process.argv)
68
+ const checks = []
69
+
70
+ if (!args.routeDir) {
71
+ checks.push({ name: 'routeDir', ok: false, message: 'missing --route-dir' })
72
+ } else {
73
+ checks.push({
74
+ name: 'routeDirExists',
75
+ ok: fs.existsSync(args.routeDir),
76
+ message: args.routeDir
77
+ })
78
+ const rel = path.relative('src/routes', args.routeDir)
79
+ const segments = rel.split(path.sep).filter(Boolean)
80
+ checks.push({
81
+ name: 'routeDirNaming',
82
+ ok: !rel.startsWith('..') && segments.every(isKebabCase),
83
+ message: rel
84
+ })
85
+ checks.push({
86
+ name: 'routeFileExists',
87
+ ok: exists(path.join(args.routeDir, 'route.tsx')),
88
+ message: path.join(args.routeDir, 'route.tsx')
89
+ })
90
+
91
+ const componentFiles = collectFiles(args.routeDir).filter(
92
+ (file) =>
93
+ file.endsWith('.tsx') &&
94
+ path.basename(file) !== 'route.tsx' &&
95
+ path.basename(file) !== 'index.tsx'
96
+ )
97
+ const componentExports = componentFiles.filter(exportsPascalComponent)
98
+ const badComponents = componentExports.filter((file) => !isPascalCase(path.basename(file)))
99
+ checks.push({
100
+ name: 'componentFileNaming',
101
+ ok: badComponents.length === 0,
102
+ message:
103
+ badComponents.length === 0
104
+ ? `${componentExports.length} component files checked`
105
+ : badComponents
106
+ })
107
+ }
108
+
109
+ const typesPath = args.types ?? (args.routeDir ? path.join(args.routeDir, 'types.ts') : null)
110
+ const constantsPath =
111
+ args.constants ?? (args.routeDir ? path.join(args.routeDir, 'constants.ts') : null)
112
+ checks.push({ name: 'typesExists', ok: exists(typesPath), message: typesPath })
113
+ checks.push({ name: 'constantsExists', ok: exists(constantsPath), message: constantsPath })
114
+ if (args.service)
115
+ checks.push({ name: 'serviceExists', ok: exists(args.service), message: args.service })
116
+
117
+ const routeTree = await checkRouteTreeModified()
118
+ checks.push({
119
+ name: 'routeTreeNotModified',
120
+ ok: !routeTree.modified,
121
+ message: routeTree.status === 'unknown' ? 'git status unavailable' : 'src/routeTree.gen.ts'
122
+ })
123
+
124
+ const result = {
125
+ ok: checks.every((check) => check.ok),
126
+ checks
127
+ }
128
+
129
+ if (args.json) {
130
+ console.log(JSON.stringify(result, null, 2))
131
+ return
132
+ }
133
+
134
+ for (const check of checks) {
135
+ console.log(
136
+ `${check.ok ? 'PASS' : 'FAIL'} ${check.name}: ${Array.isArray(check.message) ? check.message.join(', ') : check.message}`
137
+ )
138
+ }
139
+ process.exit(result.ok ? 0 : 1)
140
+ }
141
+
142
+ main().catch((error) => {
143
+ console.error(error.message)
144
+ process.exit(1)
145
+ })
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs'
3
+ import path from 'node:path'
4
+
5
+ const AUTO_HEADER = '<!-- AUTO-GENERATED. Do not edit. Regenerate from source files. -->'
6
+
7
+ function parseArgs(argv) {
8
+ const args = { routeDir: null, service: null, out: null, check: false }
9
+ for (const arg of argv.slice(2)) {
10
+ if (arg.startsWith('--route-dir=')) args.routeDir = arg.slice('--route-dir='.length)
11
+ else if (arg.startsWith('--service=')) args.service = arg.slice('--service='.length)
12
+ else if (arg.startsWith('--out=')) args.out = arg.slice('--out='.length)
13
+ else if (arg === '--check') args.check = true
14
+ }
15
+ return args
16
+ }
17
+
18
+ function collectFiles(dir) {
19
+ if (!fs.existsSync(dir)) return []
20
+ const out = []
21
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
22
+ const full = path.join(dir, entry.name)
23
+ if (entry.isDirectory()) out.push(...collectFiles(full))
24
+ else out.push(full)
25
+ }
26
+ return out.sort()
27
+ }
28
+
29
+ function read(file) {
30
+ return fs.existsSync(file) ? fs.readFileSync(file, 'utf-8') : ''
31
+ }
32
+
33
+ function rel(file) {
34
+ return file ? path.relative(process.cwd(), file) : ''
35
+ }
36
+
37
+ function extractComponentProps(file) {
38
+ const source = read(file)
39
+ const out = []
40
+ const interfaceRe = /export\s+interface\s+([A-Z][A-Za-z0-9]*Props)\s*{([\s\S]*?)\n}/g
41
+ let m
42
+ while ((m = interfaceRe.exec(source)) !== null) {
43
+ out.push(`${m[1]} { ${m[2].replace(/\s+/g, ' ').trim()} }`)
44
+ }
45
+ const typeRe = /export\s+type\s+([A-Z][A-Za-z0-9]*Props)\s*=\s*([^;\n]+)/g
46
+ while ((m = typeRe.exec(source)) !== null) out.push(`${m[1]} = ${m[2].trim()}`)
47
+ return out
48
+ }
49
+
50
+ function extractExports(source, keyword) {
51
+ const re = new RegExp(`export\\s+${keyword}\\s+([A-Za-z0-9_]+)`, 'g')
52
+ const out = []
53
+ let m
54
+ while ((m = re.exec(source)) !== null) out.push(m[1])
55
+ return out
56
+ }
57
+
58
+ function extractServiceFunctions(file) {
59
+ const source = read(file)
60
+ const out = []
61
+ const re = /export\s+(?:async\s+)?function\s+([A-Za-z0-9_]+)\s*\(([^)]*)\)/g
62
+ let m
63
+ while ((m = re.exec(source)) !== null) out.push(`${m[1]}(${m[2].replace(/\s+/g, ' ').trim()})`)
64
+ return out
65
+ }
66
+
67
+ function renderList(items) {
68
+ return items.length > 0 ? items.map((item) => `- ${item}`).join('\n') : '- 未发现'
69
+ }
70
+
71
+ function renderTable(rows, empty) {
72
+ return rows.length > 0 ? rows.join('\n') : empty
73
+ }
74
+
75
+ function buildSummary({ routeDir, service }) {
76
+ const files = collectFiles(routeDir)
77
+ const routeFile = path.join(routeDir, 'route.tsx')
78
+ const componentFiles = files.filter(
79
+ (file) =>
80
+ file.endsWith('.tsx') &&
81
+ path.basename(file) !== 'route.tsx' &&
82
+ path.basename(file) !== 'index.tsx'
83
+ )
84
+ const typesFile = path.join(routeDir, 'types.ts')
85
+ const constantsFile = path.join(routeDir, 'constants.ts')
86
+ const serviceFile = service && fs.existsSync(service) ? service : null
87
+
88
+ const componentRows = componentFiles.map((file) => {
89
+ const props = extractComponentProps(file)
90
+ return `| ${path.basename(file, '.tsx')} | ${rel(file)} | ${props.length > 0 ? props.join('<br/>') : '-'} | ${props.length > 0 ? 'OK' : 'NO_PROPS_EXPORT'} |`
91
+ })
92
+ const serviceRows = serviceFile
93
+ ? extractServiceFunctions(serviceFile).map(
94
+ (signature) => `| ${signature.split('(')[0]} | ${rel(serviceFile)} | \`${signature}\` |`
95
+ )
96
+ : []
97
+ const typeRows = fs.existsSync(typesFile)
98
+ ? [
99
+ ...extractExports(read(typesFile), 'interface'),
100
+ ...extractExports(read(typesFile), 'type')
101
+ ].map((name) => `| ${name} | ${rel(typesFile)} |`)
102
+ : []
103
+ const constRows = fs.existsSync(constantsFile)
104
+ ? extractExports(read(constantsFile), 'const').map(
105
+ (name) => `| ${name} | ${rel(constantsFile)} |`
106
+ )
107
+ : []
108
+
109
+ const pending = []
110
+ if (!fs.existsSync(routeFile)) pending.push('route.tsx 未发现')
111
+ if (!fs.existsSync(typesFile)) pending.push('types.ts 未发现')
112
+ if (!fs.existsSync(constantsFile)) pending.push('constants.ts 未发现')
113
+ if (!serviceFile) pending.push('service 文件未指定或未发现')
114
+
115
+ return `${AUTO_HEADER}
116
+
117
+ # foundation-summary:${path.basename(routeDir)}
118
+
119
+ ## 1. 生成信息
120
+
121
+ - routeDir:${rel(routeDir)}
122
+ - generatedAt:${new Date().toISOString().replace(/\.\d{3}Z$/, 'Z')}
123
+ - generator:.agent/skills/devFlow/domains/frontend/scripts/generate_foundation_summary.mjs
124
+
125
+ ## 2. 文件事实
126
+
127
+ ### route
128
+
129
+ ${fs.existsSync(routeFile) ? `- ${rel(routeFile)}` : '- 未发现'}
130
+
131
+ ### components
132
+
133
+ ${renderList(componentFiles.map(rel))}
134
+
135
+ ### service
136
+
137
+ ${serviceFile ? `- ${rel(serviceFile)}` : '- 未发现'}
138
+
139
+ ### types
140
+
141
+ ${fs.existsSync(typesFile) ? `- ${rel(typesFile)}` : '- 未发现'}
142
+
143
+ ### constants
144
+
145
+ ${fs.existsSync(constantsFile) ? `- ${rel(constantsFile)}` : '- 未发现'}
146
+
147
+ ## 3. 组件 props
148
+
149
+ | 组件 | 文件 | Props | 解析状态 |
150
+ |---|---|---|---|
151
+ ${renderTable(componentRows, '| - | - | - | 未发现组件 |')}
152
+
153
+ ## 4. service 函数签名
154
+
155
+ | 函数 | 文件 | 签名 |
156
+ |---|---|---|
157
+ ${renderTable(serviceRows, '| - | - | 未发现 service 函数 |')}
158
+
159
+ ## 5. types 导出
160
+
161
+ | 类型 | 文件 |
162
+ |---|---|
163
+ ${renderTable(typeRows, '| - | 未发现类型导出 |')}
164
+
165
+ ## 6. constants 导出
166
+
167
+ | 常量 | 文件 |
168
+ |---|---|
169
+ ${renderTable(constRows, '| - | 未发现常量导出 |')}
170
+
171
+ ## 7. figmaSync 允许修改范围
172
+
173
+ - 布局。
174
+ - CSS。
175
+ - Apex UI 组件选型和 props。
176
+ - 局部展示结构。
177
+ - PLAN.md 和 figma-plan.css。
178
+
179
+ ## 8. figmaSync 禁止修改范围
180
+
181
+ - 路由路径。
182
+ - service 契约。
183
+ - API 请求基础封装。
184
+ - 类型语义。
185
+ - 页面核心状态模型。
186
+ - 业务组件边界。
187
+ - 权限逻辑。
188
+ - Wujie bridge。
189
+ - src/routeTree.gen.ts。
190
+
191
+ ## 9. 待确认项
192
+
193
+ ${renderList(pending)}
194
+ `
195
+ }
196
+
197
+ function main() {
198
+ const args = parseArgs(process.argv)
199
+ if (!args.routeDir || !fs.existsSync(args.routeDir)) {
200
+ console.error('[foundation-summary] missing or invalid --route-dir')
201
+ process.exit(1)
202
+ }
203
+ const out = args.out ?? path.join(args.routeDir, 'foundation-summary.md')
204
+ const next = buildSummary({ routeDir: args.routeDir, service: args.service })
205
+ const prev = fs.existsSync(out) ? fs.readFileSync(out, 'utf-8') : null
206
+ if (args.check) {
207
+ if (normalizeForCompare(prev) !== normalizeForCompare(next)) {
208
+ console.error(`[foundation-summary] out of date: ${out}`)
209
+ process.exit(1)
210
+ }
211
+ console.log(`[foundation-summary] up to date: ${out}`)
212
+ return
213
+ }
214
+ fs.writeFileSync(out, next)
215
+ console.log(`[foundation-summary] wrote ${out}`)
216
+ if (prev && prev !== next)
217
+ console.log('[foundation-summary] changed from previous snapshot; ask Human to confirm impact.')
218
+ }
219
+
220
+ main()
221
+
222
+ function normalizeForCompare(text) {
223
+ return String(text ?? '').replace(/generatedAt:.+/g, 'generatedAt:<ignored>')
224
+ }
@@ -0,0 +1,66 @@
1
+ # contract-report:{页面名称}
2
+
3
+ ## 1. 总结论
4
+
5
+ - 结论:通过 / 不通过 / 阻塞
6
+ - 是否允许进入 page-build:是 / 否
7
+ - 结论说明:
8
+
9
+ ## 2. 输入材料
10
+
11
+ | 材料 | 来源 | 是否已读取 | 关键证据 |
12
+ | --------------------- | ---- | ---------- | -------- |
13
+ | 页面方案 | | | |
14
+ | API 文档 / Apifox MCP | | | |
15
+ | 当前项目结构 | | | |
16
+ | 相似页面 | | | |
17
+
18
+ ## 3. 检查范围
19
+
20
+ - 目标 route:
21
+ - 目标页面目录:
22
+ - 目标 service:
23
+ - 目标组件:
24
+ - 不检查范围:
25
+
26
+ ## 4. Checklist
27
+
28
+ | 检查项 | 结论 | 证据 | 问题 | 是否阻塞 |
29
+ | -------------- | ---- | ---- | ---- | -------- |
30
+ | 路由路径 | | | | |
31
+ | 目录和文件命名 | | | | |
32
+ | API 契约 | | | | |
33
+ | 页面状态 | | | | |
34
+ | 组件映射 | | | | |
35
+ | 待确认项 | | | | |
36
+
37
+ ## 5. API 契约明细
38
+
39
+ | 接口 | 请求路径 | Method | 入参 | 响应 | 调用组件 | 触发交互 | 刷新范围 | 异常反馈 | 证据 | 结论 |
40
+ | ---- | -------- | ------ | ---- | ---- | -------- | -------- | -------- | -------- | ---- | ---- |
41
+
42
+ ## 6. 页面状态明细
43
+
44
+ | 状态 | 来源 | 变化条件 | 消费组件 | 证据 | 结论 |
45
+ | ---- | ---- | -------- | -------- | ---- | ---- |
46
+
47
+ ## 7. 组件映射明细
48
+
49
+ | 组件 | 页面功能 | 输入 | 输出 / 事件 | 证据 | 结论 |
50
+ | ---- | -------- | ---- | ----------- | ---- | ---- |
51
+
52
+ ## 8. 阻塞项
53
+
54
+ | 阻塞项 | 影响 | 需要补充的信息 | 建议回到章节 |
55
+ | ------ | ---- | -------------- | ------------ |
56
+
57
+ ## 9. 非阻塞待确认项
58
+
59
+ | 待确认项 | 影响 | 后续处理 |
60
+ | -------- | ---- | -------- |
61
+
62
+ ## 10. page-build 准入结论
63
+
64
+ - 准入结论:
65
+ - 可进入 page-build 的文件范围:
66
+ - 不允许 page-build 处理的内容:
@@ -0,0 +1,7 @@
1
+ # L0 Mini Plan:{任务名称}
2
+
3
+ - 档位:L0
4
+ - 要改什么:
5
+ - 涉及文件:
6
+ - 不改什么:
7
+ - 验证方式:
@@ -0,0 +1,9 @@
1
+ # L1 Mini Plan:{任务名称}
2
+
3
+ - 档位:L1
4
+ - 上下文:
5
+ - 要改什么:
6
+ - 涉及文件:
7
+ - 不改什么:
8
+ - 风险:
9
+ - 验证方式:
@@ -0,0 +1,8 @@
1
+ export function {{componentName}}() {
2
+ return (
3
+ <section>
4
+ {/* TODO(figmaSync): implement final layout and Apex UI component selection after design sync. */}
5
+ {/* TODO(api): connect confirmed service functions and page states. */}
6
+ </section>
7
+ )
8
+ }
@@ -0,0 +1,5 @@
1
+ export const {{queryKeyConstName}} = {{queryKeyPrefix}} as const
2
+
3
+ export const {{initialStateConstName}} = {
4
+ // TODO(api): confirm initial query params and UI state.
5
+ } as const
@@ -0,0 +1 @@
1
+ export { Route } from './route'
@@ -0,0 +1,11 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+
3
+ import { {{pageComponentName}}Shell } from './components/{{pageComponentName}}Shell'
4
+
5
+ export const Route = createFileRoute('{{routePath}}')({
6
+ component: {{pageComponentName}}
7
+ })
8
+
9
+ function {{pageComponentName}}() {
10
+ return <{{pageComponentName}}Shell />
11
+ }
@@ -0,0 +1,17 @@
1
+ import request, { type RequestOptions } from '@/services/request'
2
+
3
+ export interface {{listParamsName}} {
4
+ // TODO(api): confirm request params.
5
+ }
6
+
7
+ export interface {{listResultName}} {
8
+ // TODO(api): confirm response shape.
9
+ }
10
+
11
+ const {{apiPathsConstName}} = {
12
+ list: 'TODO(api): confirm endpoint path'
13
+ } as const
14
+
15
+ export function {{listFunctionName}}(params: {{listParamsName}}, options?: RequestOptions) {
16
+ return request.post<{{listResultName}}>({{apiPathsConstName}}.list, params, options)
17
+ }
@@ -0,0 +1,11 @@
1
+ import type { {{listParamsName}}, {{listResultName}} } from '{{serviceModulePath}}'
2
+
3
+ export interface {{pageStateName}} {
4
+ params: {{listParamsName}}
5
+ }
6
+
7
+ export type {{pageListResultName}} = {{listResultName}}
8
+
9
+ export interface {{pageViewModelName}} {
10
+ // TODO(api): map confirmed response fields to page view model.
11
+ }
@@ -0,0 +1,103 @@
1
+ # 页面开发技术方案:{页面名称}
2
+
3
+ ## 1. 背景与目标
4
+
5
+ ## 2. 页面范围
6
+
7
+ ### 2.1 页面入口
8
+
9
+ ### 2.2 页面结构
10
+
11
+ ### 2.3 页面状态
12
+
13
+ ### 2.4 非目标范围
14
+
15
+ ## 3. 需求拆解
16
+
17
+ ### 3.1 功能与接口映射
18
+
19
+ | 功能 | PRD 交互项 | 接口 | 入参 | 响应数据 | 页面展示 / 行为 | 状态变化 | 成功后刷新 | 异常反馈 |
20
+ | ---- | ---------- | ---- | ---- | -------- | --------------- | -------- | ---------- | -------- |
21
+
22
+ ### 3.2 用户流程
23
+
24
+ ### 3.3 关键交互
25
+
26
+ | PRD 交互项 | 触发条件 | 前端行为 | 接口或状态影响 | 用户反馈 | 待确认 |
27
+ | ---------- | -------- | -------- | -------------- | -------- | ------ |
28
+
29
+ ## 4. 数据与接口
30
+
31
+ ### 4.1 依赖数据
32
+
33
+ ### 4.2 接口补充说明
34
+
35
+ ### 4.3 请求参数
36
+
37
+ ### 4.4 响应数据
38
+
39
+ ### 4.5 异常处理
40
+
41
+ ## 5. 前端实现方案
42
+
43
+ ### 5.1 目录结构与产物路径
44
+
45
+ ### 5.2 可复用资源清单
46
+
47
+ ### 5.3 UI 库组件清单
48
+
49
+ ### 5.4 组件拆分
50
+
51
+ ### 5.5 状态设计
52
+
53
+ ### 5.6 数据流
54
+
55
+ ```mermaid
56
+ flowchart LR
57
+ A["用户操作"] --> B["页面状态"]
58
+ B --> C["接口请求"]
59
+ C --> D["页面展示"]
60
+ ```
61
+
62
+ ### 5.7 提交表单与校验(如涉及)
63
+
64
+ ## 6. UI 与体验约束
65
+
66
+ ### 6.1 设计稿信息
67
+
68
+ ### 6.2 响应式要求
69
+
70
+ ### 6.3 Loading 状态
71
+
72
+ ### 6.4 空状态
73
+
74
+ ### 6.5 错误反馈
75
+
76
+ ## 7. 非功能性设计
77
+
78
+ ### 7.1 性能设计
79
+
80
+ ### 7.2 权限与安全
81
+
82
+ ### 7.3 可访问性
83
+
84
+ ### 7.4 埋点与监控
85
+
86
+ ### 7.5 可维护性
87
+
88
+ ### 7.6 可测试性
89
+
90
+ ## 8. 边界场景
91
+
92
+ ## 9. 完成标准
93
+
94
+ ## 10. 风险与待确认项
95
+
96
+ ### 10.1 风险项
97
+
98
+ #### PRD / 接口 / 仓库 / 规范冲突项
99
+
100
+ | 冲突项 | PRD 描述 | 接口或仓库现状 | 影响 | 建议确认对象 |
101
+ | ------ | -------- | -------------- | ---- | ------------ |
102
+
103
+ ### 10.2 待确认问题