@hupan56/wlkj 2.5.0 → 2.7.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 (127) hide show
  1. package/bin/cli.js +289 -12
  2. package/package.json +1 -1
  3. package/templates/qoder/agents/insight-planning.md +67 -0
  4. package/templates/qoder/agents/insight-research.md +61 -0
  5. package/templates/qoder/agents/prd-quick.md +1 -0
  6. package/templates/qoder/agents/prd-reference.md +10 -2
  7. package/templates/qoder/commands/optional/wl-insight.md +275 -0
  8. package/templates/qoder/commands/{wl-report.md → optional/wl-report.md} +13 -5
  9. package/templates/qoder/commands/{wl-spec.md → optional/wl-spec.md} +1 -1
  10. package/templates/qoder/commands/{wl-status.md → optional/wl-status.md} +28 -2
  11. package/templates/qoder/commands/wl-code.md +10 -2
  12. package/templates/qoder/commands/wl-commit.md +1 -1
  13. package/templates/qoder/commands/wl-design-draw.md +78 -0
  14. package/templates/qoder/commands/wl-design-scan.md +108 -0
  15. package/templates/qoder/commands/wl-design-spec.md +154 -0
  16. package/templates/qoder/commands/wl-design.md +32 -0
  17. package/templates/qoder/commands/wl-init.md +24 -3
  18. package/templates/qoder/commands/wl-prd-full.md +226 -0
  19. package/templates/qoder/commands/wl-prd-quick.md +134 -0
  20. package/templates/qoder/commands/wl-prd-review.md +104 -0
  21. package/templates/qoder/commands/wl-prd.md +17 -288
  22. package/templates/qoder/commands/wl-search.md +66 -30
  23. package/templates/qoder/commands/wl-task.md +290 -59
  24. package/templates/qoder/commands/wl-test.md +92 -24
  25. package/templates/qoder/config.yaml +59 -15
  26. package/templates/qoder/hooks/inject-workflow-state.py +35 -9
  27. package/templates/qoder/hooks/session-start.py +144 -62
  28. package/templates/qoder/rules/wl-pipeline.md +216 -105
  29. package/templates/qoder/scripts/__pycache__/search_index.cpython-39.pyc +0 -0
  30. package/templates/qoder/scripts/archive_prd.py +377 -0
  31. package/templates/qoder/scripts/autotest.py +1715 -0
  32. package/templates/qoder/scripts/autotest_batch.py +224 -0
  33. package/templates/qoder/scripts/autotest_run.py +297 -0
  34. package/templates/qoder/scripts/benchmark.py +210 -209
  35. package/templates/qoder/scripts/build_style_index.py +444 -4
  36. package/templates/qoder/scripts/check_carriers.py +238 -0
  37. package/templates/qoder/scripts/check_mcp.py +298 -0
  38. package/templates/qoder/scripts/check_qoderwork_consistency.py +166 -0
  39. package/templates/qoder/scripts/common/developer.py +26 -19
  40. package/templates/qoder/scripts/common/events.py +46 -0
  41. package/templates/qoder/scripts/common/extract.py +419 -0
  42. package/templates/qoder/scripts/common/graph_traverse.py +533 -0
  43. package/templates/qoder/scripts/common/identity.py +6 -1
  44. package/templates/qoder/scripts/common/paths.py +89 -0
  45. package/templates/qoder/scripts/common/pip_install.py +144 -0
  46. package/templates/qoder/scripts/common/platform_guard.py +61 -0
  47. package/templates/qoder/scripts/common/search_engine.py +205 -205
  48. package/templates/qoder/scripts/common/terms.py +57 -0
  49. package/templates/qoder/scripts/common/ts_extract.py +536 -0
  50. package/templates/qoder/scripts/context_pack.py +73 -13
  51. package/templates/qoder/scripts/enrich_prompt.py +226 -0
  52. package/templates/qoder/scripts/eval_prd.py +318 -225
  53. package/templates/qoder/scripts/export.py +487 -487
  54. package/templates/qoder/scripts/extract_api_params.py +246 -0
  55. package/templates/qoder/scripts/extract_routes.py +54 -0
  56. package/templates/qoder/scripts/extract_routes_tree.py +78 -0
  57. package/templates/qoder/scripts/fill_prototype.py +707 -0
  58. package/templates/qoder/scripts/gen_design_doc.py +394 -0
  59. package/templates/qoder/scripts/git_sync.py +27 -15
  60. package/templates/qoder/scripts/init_doctor.py +306 -41
  61. package/templates/qoder/scripts/install_qoderwork.py +366 -9
  62. package/templates/qoder/scripts/kg.py +708 -0
  63. package/templates/qoder/scripts/kg_auto_login.py +196 -0
  64. package/templates/qoder/scripts/kg_build.py +612 -0
  65. package/templates/qoder/scripts/kg_build_db.py +327 -0
  66. package/templates/qoder/scripts/kg_duckdb.py +549 -0
  67. package/templates/qoder/scripts/kg_incremental.py +393 -0
  68. package/templates/qoder/scripts/kg_link_db.py +224 -0
  69. package/templates/qoder/scripts/kg_mcp_server.py +801 -0
  70. package/templates/qoder/scripts/kg_semantic.py +150 -0
  71. package/templates/qoder/scripts/kg_test_runner.py +241 -0
  72. package/templates/qoder/scripts/lanhu_stdio_wrapper.py +119 -0
  73. package/templates/qoder/scripts/learn.py +118 -39
  74. package/templates/qoder/scripts/learn_aggregate.py +201 -0
  75. package/templates/qoder/scripts/mcp_launcher.py +359 -0
  76. package/templates/qoder/scripts/mysql_mcp_server.py +396 -0
  77. package/templates/qoder/scripts/repo_root.py +106 -0
  78. package/templates/qoder/scripts/role.py +12 -0
  79. package/templates/qoder/scripts/run_weekly_update.bat +5 -0
  80. package/templates/qoder/scripts/run_weekly_update.sh +5 -0
  81. package/templates/qoder/scripts/search_index.py +307 -60
  82. package/templates/qoder/scripts/secure-ls.js +5640 -0
  83. package/templates/qoder/scripts/setup.py +706 -453
  84. package/templates/qoder/scripts/setup_lanhu.py +963 -0
  85. package/templates/qoder/scripts/status.py +250 -11
  86. package/templates/qoder/scripts/sync_carriers.py +259 -0
  87. package/templates/qoder/scripts/syncgate.py +5 -4
  88. package/templates/qoder/scripts/task.py +75 -0
  89. package/templates/qoder/scripts/team_sync.py +60 -4
  90. package/templates/qoder/scripts/workspace_init.py +1 -1
  91. package/templates/qoder/skills/design-import/SKILL.md +226 -0
  92. package/templates/qoder/skills/design-import/figma-workflow.md +81 -0
  93. package/templates/qoder/skills/design-review/SKILL.md +82 -25
  94. package/templates/qoder/skills/prd-generator/SKILL.md +185 -58
  95. package/templates/qoder/skills/prd-review/SKILL.md +18 -1
  96. package/templates/qoder/skills/prompt-enrich/SKILL.md +90 -0
  97. package/templates/qoder/skills/prototype-generator/SKILL.md +256 -141
  98. package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
  99. package/templates/qoder/skills/spec-coder/SKILL.md +18 -1
  100. package/templates/qoder/skills/spec-generator/SKILL.md +18 -1
  101. package/templates/qoder/skills/test-generator/SKILL.md +15 -2
  102. package/templates/qoder/skills/wl-code/SKILL.md +55 -36
  103. package/templates/qoder/skills/wl-commit/SKILL.md +89 -76
  104. package/templates/qoder/skills/wl-design/SKILL.md +55 -0
  105. package/templates/qoder/skills/wl-init/SKILL.md +76 -67
  106. package/templates/qoder/skills/wl-insight/SKILL.md +201 -81
  107. package/templates/qoder/skills/wl-prd-full/SKILL.md +69 -0
  108. package/templates/qoder/skills/wl-prd-quick/SKILL.md +49 -0
  109. package/templates/qoder/skills/wl-prd-review/SKILL.md +34 -0
  110. package/templates/qoder/skills/wl-report/SKILL.md +131 -107
  111. package/templates/qoder/skills/wl-search/SKILL.md +141 -75
  112. package/templates/qoder/skills/wl-spec/SKILL.md +49 -39
  113. package/templates/qoder/skills/wl-status/SKILL.md +83 -61
  114. package/templates/qoder/skills/wl-task/SKILL.md +132 -58
  115. package/templates/qoder/skills/wl-test/SKILL.md +406 -40
  116. package/templates/qoder/templates/prd-full-template.md +2 -0
  117. package/templates/qoder/templates/prd-quick-template.md +1 -0
  118. package/templates/qoder/templates/prototype-app.html +13 -8
  119. package/templates/qoder/templates/prototype-web.html +376 -93
  120. package/templates/root/AGENTS.md +89 -34
  121. package/templates/root/requirements.txt +21 -0
  122. package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +259 -259
  123. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +186 -186
  124. package/templates/qoder/agents/prd-planning.md +0 -56
  125. package/templates/qoder/agents/prd-research.md +0 -33
  126. package/templates/qoder/commands/wl-insight.md +0 -51
  127. package/templates/qoder/skills/wl-prd/SKILL.md +0 -89
@@ -10,7 +10,10 @@ QODER Pipeline - 任务生命周期管理
10
10
  - finish: 完成任务 (清除活跃任务)
11
11
  - archive: 归档任务 (移动到 archive/ 目录)
12
12
  - list: 列出活跃/归档任务
13
+ - show: 查看任务详情
14
+ - reassign: 改派任务负责人 (多角色协作, PM 建任务后转给开发)
13
15
  - add-subtask / remove-subtask: 父子任务关联
16
+ - set-due / block / unblock / gantt: 排期与依赖
14
17
 
15
18
  参考: Trellis 的 task.py 设计
16
19
 
@@ -20,6 +23,7 @@ Usage:
20
23
  python task.py current [--source]
21
24
  python task.py finish
22
25
  python task.py archive <name>
26
+ python task.py reassign <name> <new_assignee>
23
27
  python task.py list [--mine] [--status <s>]
24
28
  python task.py add-subtask <parent> <child>
25
29
  python task.py remove-subtask <parent> <child>
@@ -331,6 +335,18 @@ def cmd_finish(args: argparse.Namespace) -> int:
331
335
  if task_json_path.is_file():
332
336
  run_task_hooks("after_finish", task_json_path, repo_root)
333
337
 
338
+ # 埋点: 任务完成反馈给 learning 引擎
339
+ if _finish_ctx:
340
+ try:
341
+ from learn import record_feedback
342
+ record_feedback('task_completed', {
343
+ 'task': task_dir.name,
344
+ 'assignee': _finish_ctx.get('assignee', '?'),
345
+ 'hours': _finish_ctx.get('now_iso'),
346
+ })
347
+ except Exception:
348
+ pass # 埋点失败不阻塞任务完成
349
+
334
350
  return 0
335
351
 
336
352
 
@@ -692,6 +708,59 @@ def cmd_set_due(args: argparse.Namespace) -> int:
692
708
  return 0
693
709
 
694
710
 
711
+ def cmd_reassign(args: argparse.Namespace) -> int:
712
+ """改派任务负责人。用法: task.py reassign <task> <new_assignee>
713
+
714
+ 场景: PM 建任务后改派给开发(多角色协作必需)。
715
+ ACL: 只有 creator/admin 能改派(防恶意抢占/甩锅)。
716
+ 保留改派历史(assignee_history), 便于追溯。
717
+ """
718
+ repo_root = get_repo_root()
719
+ full_path = resolve_task_dir(args.task, repo_root)
720
+ if not full_path.is_dir():
721
+ print(f"Error: Task not found: {args.task}", file=sys.stderr)
722
+ return 1
723
+ # ACL: 只有 creator/admin 能改派 (assignee 自己不能转手甩锅)
724
+ try:
725
+ assert_can_modify_task(full_path, "reassign", repo_root)
726
+ except PermissionError as e:
727
+ print(str(e), file=sys.stderr)
728
+ return 4
729
+
730
+ new_assignee = (args.new_assignee or "").strip()
731
+ if not new_assignee:
732
+ print("Error: new_assignee 不能为空", file=sys.stderr)
733
+ return 1
734
+
735
+ changed = {"from": None, "to": new_assignee}
736
+
737
+ def _reassign_mutator(data):
738
+ old = data.get("assignee")
739
+ changed["from"] = old
740
+ data["assignee"] = new_assignee
741
+ data["updated_at"] = datetime.now().isoformat()
742
+ # 记录改派历史 (追溯用)
743
+ hist = data.get("assignee_history") or []
744
+ hist.append({
745
+ "from": old,
746
+ "to": new_assignee,
747
+ "by": get_developer(repo_root),
748
+ "at": datetime.now().isoformat(),
749
+ })
750
+ data["assignee_history"] = hist
751
+
752
+ if not modify_task_json(full_path, _reassign_mutator):
753
+ print("Error: Missing task.json", file=sys.stderr)
754
+ return 1
755
+
756
+ if changed["from"] == new_assignee:
757
+ print(f"Note: assignee 已是 {new_assignee}, 无变化 ({full_path.name})")
758
+ else:
759
+ print(f"Reassigned: {changed['from']} -> {new_assignee} ({full_path.name})")
760
+ print(f" 改派人: {get_developer(repo_root)} (已记录到 assignee_history)")
761
+ return 0
762
+
763
+
695
764
  def cmd_block(args: argparse.Namespace) -> int:
696
765
  """标记任务被另一任务阻塞。自动维护反向 blocks 关系。
697
766
  用法: task.py block <task> <blocked-by-task>
@@ -979,6 +1048,12 @@ def main() -> int:
979
1048
  p_due.add_argument("due_date", help="Due date YYYY-MM-DD")
980
1049
  p_due.set_defaults(func=cmd_set_due)
981
1050
 
1051
+ # reassign (改派任务负责人, 多角色协作必需)
1052
+ p_reassign = subparsers.add_parser("reassign", help="Reassign task to another developer")
1053
+ p_reassign.add_argument("task", help="Task name")
1054
+ p_reassign.add_argument("new_assignee", help="New assignee (developer name)")
1055
+ p_reassign.set_defaults(func=cmd_reassign)
1056
+
982
1057
  # block (B2)
983
1058
  p_block = subparsers.add_parser("block", help="Mark task blocked by another")
984
1059
  p_block.add_argument("task", help="Task that is blocked")
@@ -168,6 +168,41 @@ def report_conflict(stderr):
168
168
  return 3
169
169
 
170
170
 
171
+ def _get_kg_built_at():
172
+ """读 kg.duckdb 的 built_at 时间戳 (版本号)。"""
173
+ kg_path = os.path.join(BASE, 'data', 'index', 'kg.duckdb')
174
+ if not os.path.isfile(kg_path):
175
+ return 0
176
+ try:
177
+ import duckdb
178
+ con = duckdb.connect(kg_path, read_only=True)
179
+ row = con.execute("SELECT value FROM build_meta WHERE key='built_at'").fetchone()
180
+ con.close()
181
+ return int(row[0]) if row else 0
182
+ except Exception:
183
+ # DuckDB 不可用, 用文件 mtime
184
+ return int(os.path.getmtime(kg_path))
185
+
186
+
187
+ def _auto_resolve_kg_conflict():
188
+ """kg.duckdb 二进制冲突自动解决: 保留 built_at 更新的版本。
189
+
190
+ git pull 后如果 kg.duckdb 有冲突 (二进制不能 merge), git 会标记为 conflict。
191
+ 这里自动选 built_at 更大的 (更新的) 版本, 用户无感。
192
+ """
193
+ kg_path = os.path.join(BASE, 'data', 'index', 'kg.duckdb')
194
+ if not os.path.isfile(kg_path):
195
+ return
196
+
197
+ # 检查是否有 kg.duckdb 的 merge conflict
198
+ r = git('diff', '--name-only', '--diff-filter=U')
199
+ if r.returncode == 0 and 'kg.duckdb' in r.stdout:
200
+ # 有冲突 — 我们直接接受当前的 (pull 下来的) 版本
201
+ # 因为 pull --rebase 会把远程版本放在工作区
202
+ git('add', 'data/index/kg.duckdb')
203
+ print('[kg-sync] kg.duckdb 冲突已自动解决 (使用远程最新版本)')
204
+
205
+
171
206
  def do_pull(quiet=False):
172
207
  """拉取团队最新。安全: autostash 保护未提交改动。"""
173
208
  if rebase_in_progress():
@@ -192,6 +227,9 @@ def do_pull(quiet=False):
192
227
  print('继续离线工作, 产出不会丢失, 下次同步会自动补推。')
193
228
  return 1
194
229
 
230
+ # kg.duckdb 冲突自动解决: 如果 pull 带来了更新的 kg.duckdb, 自动用它
231
+ _auto_resolve_kg_conflict()
232
+
195
233
  touch_pull_marker()
196
234
  out = (r.stdout or '').strip()
197
235
  if not quiet:
@@ -384,13 +422,22 @@ def _do_push_locked(message, dev, skip_eval, skip_secret):
384
422
  for attempt in range(1, MAX_PUSH_RETRY + 1):
385
423
  r = git('pull', '--rebase', '--autostash', 'origin', branch)
386
424
  if r.returncode != 0:
387
- if 'CONFLICT' in (r.stdout + r.stderr) or rebase_in_progress():
425
+ # kg.duckdb 二进制冲突: 自动解决 (用户无感)
426
+ _auto_resolve_kg_conflict()
427
+ if rebase_in_progress():
428
+ # 冲突解决后继续 rebase
429
+ r2 = git('rebase', '--continue')
430
+ if r2.returncode == 0:
431
+ pass # 继续 push
432
+ elif 'CONFLICT' in (r2.stdout + r2.stderr):
433
+ return report_conflict(r2.stderr or r2.stdout)
434
+ else:
435
+ continue
436
+ elif 'CONFLICT' in (r.stdout + r.stderr):
388
437
  return report_conflict(r.stderr or r.stdout)
389
- # 非冲突的 pull 失败 (网络/auth/fetch 拒绝): 不往下 push (仓库可能处于
390
- # 半 rebase 状态), 重试本循环
438
+ # 非冲突的 pull 失败
391
439
  print('Pull failed (attempt {}): {}'.format(attempt, (r.stderr or r.stdout).strip()[:150]))
392
440
  if rebase_in_progress():
393
- # 兜底: 万一 pull 留下了未完成的 rebase, abort 掉防仓库损坏
394
441
  git('rebase', '--abort')
395
442
  continue
396
443
 
@@ -407,6 +454,15 @@ def _do_push_locked(message, dev, skip_eval, skip_secret):
407
454
  print('[sync] PRD 归档跳过 (不阻塞): ' + str(e)[:80])
408
455
  # D2: 检测 PRD 发布并推飞书通知
409
456
  _notify_prd_publications(staged_files)
457
+ # 埋点: 提交完成反馈给 learning 引擎
458
+ try:
459
+ from learn import record_feedback
460
+ record_feedback('commit_done', {
461
+ 'files': len(staged_files),
462
+ 'scope': [s for s in SYNC_SCOPES[:3]],
463
+ })
464
+ except Exception:
465
+ pass # 埋点失败不阻塞同步
410
466
  return 0
411
467
  if attempt < MAX_PUSH_RETRY:
412
468
  print('Push rejected (someone pushed first?), retrying...')
@@ -92,7 +92,7 @@ def main():
92
92
 
93
93
  name = sys.argv[1]
94
94
  if init_workspace(name):
95
- print("\nNext: say 'help me generate PRD' in Codex to start pipeline")
95
+ print("\nNext: say 'help me generate PRD' in Qoder to start pipeline")
96
96
  sys.exit(0)
97
97
  else:
98
98
  sys.exit(1)
@@ -0,0 +1,226 @@
1
+ ---
2
+ name: design-import
3
+ description: "把设计师的 Figma/Axure 设计稿录入工作流,生成设计规范 spec.json 并存入知识图谱。Import designer's Figma/Axure design into the pipeline as a style spec. 用户说'录入设计稿''Figma稿录入''把这个设计录入'时触发。"
4
+ trigger: "user says '录入设计稿', 'Figma 稿录入', '把这个设计录入', or wants to feed their design into the AI workflow"
5
+ ---
6
+
7
+
8
+ ## 🔧 仓库根定位(QoderWork 桌面端 vs Qoder IDE/CLI)
9
+
10
+ **后续脚本里的 `$R` 代表仓库根**,先确定它(QoderWork 桌面端工作目录不是仓库根,相对路径会失效):
11
+ ```bash
12
+ R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
13
+ ```
14
+ > `repo_root.py` 从 `~/.qoderwork/mcp.json` 反推仓库根;失败回退 `.`(IDE/CLI 工作目录即仓库根)。找不到时先跑 `python .qoder/scripts/install_qoderwork.py`。
15
+
16
+ # Design Import Skill(设计师专用)
17
+
18
+ > 🚫 **边界铁律:蓝湖工具(`mcp__lanhu__*`)只能在本 skill(即 `/wl-design-spec 录入`)内部调用。**
19
+ > 用户直接说"查蓝湖""读一下这个蓝湖链接"而没走录入命令时,**不要裸调蓝湖**——先确认意图,
20
+ > 引导到 `/wl-design-spec 录入 <蓝湖链接>`。工作流是非侵入式的,所有能力必须经命令/工序站触发,
21
+ > 不能脱离流程裸调工具(否则不可控、不可审计)。
22
+
23
+ 把设计师在 Figma/Axure 里出的设计稿,转成工作流能消化的**设计规范 spec.json**,
24
+ 存到 `data/style/`,进入知识图谱。从此同需求的 AI 原型优先锚定这份 spec,
25
+ 不再跟设计师的稿打架。
26
+
27
+ ## ⚙️ 自取上下文(QoderWork 无 hook 注入,必须自读)
28
+
29
+ - `.qoder/.developer` — 当前设计师名(产出归属)
30
+ - `.qoder/.current-task` — 若存在,spec 命名带上任务关键词
31
+ - 平台必须明确(Web/APP/Both);如未指定,先问
32
+
33
+ **已录入清单**(设计师问"我录过哪些"时):
34
+ ```bash
35
+ ls "$R/data/style/"*-design-spec.json 2>/dev/null # 列所有 spec
36
+ ```
37
+ 对每个文件读 `requirement`/`platform`/`source` 字段汇总给设计师,让他知道哪些录过、哪端录了。
38
+
39
+ ## Step 0: 确认平台
40
+
41
+ 跟 /wl-prd-full 一样,先问:Web 管理端 / APP 移动端 / 两端都要?
42
+ **问了就停,等设计师回答。**
43
+
44
+ ## Step 1: 收集设计信息
45
+
46
+ 设计师用以下任一方式提供设计信息(按精度从高到低排):
47
+
48
+ **方式 A(最推荐):蓝湖链接直读**
49
+
50
+ 设计师发蓝湖链接(`https://lanhuapp.com/web/#/item/...`),AI 用蓝湖 MCP 精确提取颜色/尺寸/字体/切图,比截图口述精确 10 倍。**走以下 4 步,每步失败就降级到方式 B(绝不报错、绝不阻塞):**
51
+
52
+ **A-1. 探测蓝湖可用性(失败就静默降级)**
53
+ ```
54
+ 调 mcp__lanhu__get_designs(url=<链接>)
55
+ ```
56
+ - 返回设计图列表 → 继续 A-2
57
+ - 报"连接失败"/工具不存在/418(cookie 失效)→ **立刻降级**:告诉用户"蓝湖没起/cookie 失效,改用截图口述",然后走方式 B,**不许卡住**
58
+
59
+ > 蓝湖是增强不是必需。没有蓝湖,工作流照样完整跑(方式 B/C/D 都不依赖蓝湖)。
60
+
61
+ **A-2. 选设计图(图多时帮用户缩小范围)**
62
+ ```
63
+ 调 mcp__lanhu__get_designs(url=<链接>) → 返回 {designs:[{name, width, height, ...}], sectors:[...], total_designs}
64
+ ```
65
+ - **图少(≤15 张)**:直接列全部图名,问"录入哪张?"(**问了就停**)
66
+ - **图多(>15 张)**:别一次倒几百张,先帮缩小:
67
+ 1. 若有 `sectors`(分组):先列分组名+图数,问"哪个分组?",再列该组图
68
+ 2. 没分组:问设计师"你要录的功能叫什么?",按图名关键词过滤后列
69
+ 3. 过滤掉明显占位名(如重复的"命名"),只列有意义的
70
+ - 选定后记下图名,进 A-3。**可一次选多张**(`design_names` 支持数组)。
71
+
72
+ **A-3. 读标注 + 切图**
73
+ ```
74
+ 调 mcp__lanhu__get_ai_analyze_design_result(url=<链接>, design_names=[<选中的图名>])
75
+ → 返回: HTML/CSS 标注 (rgba 值、width、padding、font...) + 还原指引
76
+ 调 mcp__lanhu__get_design_slices(url=<链接>, design_name=<图名>, include_metadata=true)
77
+ → 返回: {slices:[{name, size, download_url, svg_url, scale_urls:{1x,2x,3x}}]}
78
+ ```
79
+ - CSS 标注 → 填 `design_tokens`/`layout`/`components`
80
+ - **切图**:每个 slice 的 `svg_url` 是设计师切的**真实图标矢量**。挑出图标类(size 小、name 含"图标/icon/箭头"等),
81
+ 填进 spec.json 的 `icons` 字段(见 Step 2)。这是原型图标的最佳真源(优于 ref-icon.json 通用图标)。
82
+ 返回的 CSS 标注是**设计稿的权威真值**,优先级高于代码风格、高于 PDF 规范。
83
+
84
+ **A-4. AI 语义映射进 spec.json(关键:落到实处)**
85
+ 蓝湖返回的是一堆 CSS 属性,AI 读懂后**语义判断**填进 spec.json 各字段:
86
+
87
+ | 蓝湖返回 | 填进 spec.json 哪里 | 怎么判断 |
88
+ |---------|-------------------|---------|
89
+ | 出现最多的背景色/按钮色 (如 `rgba(255,115,10,1)`) | `design_tokens["--primary-color"]` | 统计频率,按钮/导航反复用的色 = 主色 |
90
+ | 容器 width (如 `200px`) | `design_tokens["--sidebar-width"]` | web 端左侧固定宽容器 = 侧边栏 |
91
+ | 行高 padding font-size | `design_tokens["--table-row-height"]` 等 | 表格行的 height |
92
+ | 布局结构 | `layout.description` / `layout.sidebar` | 看是左+右(web) 还是单列(app) |
93
+ | 卡片/表格/表单/按钮组 | `components[]` | 按视觉块列 |
94
+
95
+ **铁律:CSS 值原样填,禁止改格式。** `rgba(255,115,10,1)` 不要写成 `#FF730A`,`200px` 不要四舍五入。蓝湖给什么就填什么(这是设计稿的真值,AI 改了就不准了)。
96
+
97
+ > 蓝湖 MCP 是 STDIO 模式:开 QoderWork 自动起、关自动停,**无需手动 start**。
98
+ > cookie 按角色隔离在 `workspace/members/{当前用户}/.secrets/lanhu.env`(不进 git)——
99
+ > wrapper(lanhu_stdio_wrapper.py)读当前 `.developer` 角色的 cookie 拉起服务,UI 角色有改稿权限、PM/开发只读。
100
+ > 没配的话跑 `python "$R/.qoder/scripts/setup_lanhu.py"`;换角色改 `.developer` + 配新角色 cookie 后重启 QoderWork。
101
+
102
+ **方式 B:截图 + 口述**
103
+ 设计师发一张 Figma/Axure 截图,口述关键设计决策:
104
+ - "主色用 #1677ff,背景用 #f5f5f5"
105
+ - "侧边栏宽 200px,一级菜单点击展开二级"
106
+ - "表格行高 48px,斑马纹"
107
+
108
+ **方式 C:导出标注**
109
+ 设计师从 Figma 导出 CSS / 标注 PDF / tokens JSON,AI 直接读。
110
+
111
+ **方式 D:参照现有系统页面改**
112
+ 设计师说"类似 XX 页面,但侧边栏改成手风琴式"——AI 读那个页面代码,提取基础 spec,再叠加设计师的改动。
113
+
114
+ ## Step 2: 生成 spec.json
115
+
116
+ 把设计信息结构化成 spec.json,格式对齐 `data/index/vben-style-reference.json`:
117
+
118
+ ```json
119
+ {
120
+ "source": "Figma (设计师: {designer})",
121
+ "imported_at": "2026-06-16",
122
+ "platform": "web",
123
+ "requirement": "{需求名}",
124
+ "design_tokens": {
125
+ "--primary-color": "#1677ff",
126
+ "--bg-color": "#f5f5f5",
127
+ "--sidebar-width": "200px",
128
+ "--table-row-height": "48px"
129
+ },
130
+ "layout": {
131
+ "description": "左侧侧边栏 + 右侧内容区;一级菜单点击展开二级手风琴",
132
+ "sidebar": "width: 200px, collapsible, accordion mode",
133
+ "content": "padding: 16px, background: #f5f5f5"
134
+ },
135
+ "components": [
136
+ {"name": "侧边栏菜单", "spec": "三级折叠,一级固定,二级手风琴展开"},
137
+ {"name": "数据表格", "spec": "VxeGrid 风格,行高 48px,斑马纹"}
138
+ ],
139
+ "notes": "设计师强调:侧边栏必须有图标,不能用纯文字",
140
+ "icons": [
141
+ {"name": "返回箭头", "svg_url": "https://lanhu-oss-.../xxx.svg", "size": "7x14", "usage": "顶部返回按钮"},
142
+ {"name": "提交图标", "svg_url": "https://lanhu-oss-.../yyy.svg", "size": "20x20", "usage": "提交按钮"}
143
+ ],
144
+ "lanhu_source": {
145
+ "url": "https://lanhuapp.com/web/#/item/project/stage?pid=...",
146
+ "image_names": ["问题反馈-详情"],
147
+ "slice_count": 12,
148
+ "imported_at": "2026-06-18"
149
+ }
150
+ }
151
+ ```
152
+
153
+ > ⚠️ **`requirement` 字段是落地关键**:fill_prototype.py 靠它匹配关键词
154
+ > (`load_design_spec` 扫描 `data/style/*-design-spec.json`,`requirement` 包含查询词就命中)。
155
+ > 所以 `requirement` 必须跟未来 `/wl-design-draw` 的关键词一致(如"问题反馈""营业外合同")。
156
+ > 蓝湖来源时填 `source: "蓝湖直读 (设计师: XX)"`,截图口述填 `source: "Figma (设计师: XX)"`,
157
+ > 下游 fill_prototype 不管来源只认值。
158
+ > **`lanhu_source` 可选**:仅蓝湖来源时填,记录链接/图名/切图数,可追溯设计稿出处。
159
+
160
+ ## Step 3: 存储到知识图谱
161
+
162
+ - 存到 `data/style/{需求名}-{平台}-design-spec.json`(平台 = web/app,避免同功能多端冲突)
163
+ - 例:`待办-web-design-spec.json`、`待办-app-design-spec.json`
164
+ - 同需求同平台重新录入 → 覆盖(更新);不同平台 → 各自独立文件
165
+ - **优先级声明**:这份 spec 的优先级 > 代码风格 > PDF 规范
166
+ (因为它是最新、最明确的设计决策)
167
+ - **`requirement` 字段填法**(解决"设计师不知道 PM 搜什么词"):
168
+ 用**功能名 + 同义词**,别只填图名。如图名"设置-我的待办",requirement 填 `"待办 我的待办 设置待办"`
169
+ (空格分隔多个可能的关键词),这样 PM 搜"待办"或"我的待办"都能命中。
170
+
171
+ > 🎯 **落地链路(已验证通)**:spec.json 落盘后,下次 `/wl-design-draw <同关键词>` 时,
172
+ > `fill_prototype.py` 的 `load_design_spec()` 会自动命中这份 spec(靠 `requirement` 字段匹配),
173
+ > 把 `design_tokens` **原样注入**原型 HTML 的 `:root` CSS(最高优先级,覆盖模板默认色)。
174
+ > 即:蓝湖读到的 `rgba(255,115,10,1)` 会真实出现在原型里,不是 AI 看一眼就忘。
175
+ > 验证方法:看输出原型 `:root` 里有没有 `/* design-import spec tokens (优先级最高) */` 注释及下面的值。
176
+
177
+ ## Step 3.5: 录入埋点
178
+
179
+ spec.json 落盘后埋点,供 /wl-status 统计设计录入活跃度:
180
+ ```bash
181
+ python "$R/.qoder/scripts/learn.py" record design_import "{\"requirement\": \"<需求名>\", \"platform\": \"<web|app>\", \"source\": \"<蓝湖|截图|导出>\"}"
182
+ ```
183
+ > 失败静默忽略。
184
+
185
+ ## Step 4: 确认与通知
186
+ 输出给设计师:
187
+ ```
188
+ ✅ 设计规范已录入: data/style/{需求名}-design-spec.json
189
+ - 平台: Web 管理端
190
+ - 来源: 蓝湖直读 (设计师: XX) ← 或 截图口述
191
+ - 主色: rgba(255,115,10,1)
192
+ - 布局: 左侧边栏 200px + 手风琴二级菜单
193
+ - 组件: 侧边栏菜单、数据表格
194
+
195
+ ✅ 已自动接入原型链路:下次 PM/任何人用同一关键词出原型时,
196
+ fill_prototype.py 会自动发现并优先用这份 spec(优先级高于代码风格),
197
+ 主色/侧边栏宽度/布局全部按你的设计来(design_tokens 注入原型 :root)。
198
+
199
+
200
+ 设计师可以随时说"录入设计稿"更新它。
201
+ ```
202
+
203
+ ## 铁律
204
+
205
+ - **有蓝湖 MCP 时优先用它直读**:设计师发蓝湖链接,AI 调 `mcp__lanhu__*` 提取精确参数,
206
+ 不要让设计师截图口述(截图口述是蓝湖不可用时的降级方案)
207
+ - **绝不编造 token**:设计师没说的值,留空或标"待确认",不要自己猜
208
+ - **图标必须来自真源**:即使设计师用了 emoji 示意,录入时也要替换成系统真源
209
+ (Web: data/index/ref-icon.json 的 Ant Design SVG)
210
+ - **spec 一旦录入,同需求 prototype 必须锚定它**:AI 出原型前先查 data/style/ 有没有 spec
211
+
212
+ ## 🆕 录入前可参考知识图谱新数据
213
+
214
+ 设计师录入 spec 前,可以先问 AI 系统现状,让 spec 更贴合系统:
215
+
216
+ - **"这个功能模块现在有哪些页面?"** → AI 调 `mcp__qoder-knowledge-graph__feature_overview(feature='资产管理')`
217
+ 返回该模块所有页面 + API + 按钮,设计师知道改哪些、加哪些
218
+ - **"这个模块的业务流程是什么?"** → AI 调 `mcp__qoder-knowledge-graph__get_workflow(module='资产')`
219
+ 返回操作链(查询→新增→审批→...),spec 里的交互流程跟系统一致
220
+ - **"系统里这个模块的按钮都叫什么?"** → 看 DESIGN.md 的 "Real Button Texts" 段
221
+ 或 entity-registry.json,spec 里的按钮文案跟系统统一
222
+ - **"这个模块用的是什么布局模式?"** → 看 DESIGN.md 的 layout_fingerprint
223
+ spec 里的侧边栏宽度/布局模式跟系统一致(如 160px / mixed-nav)
224
+
225
+ > 这些数据让设计师的 spec 不是"凭空设计",而是"基于系统现状的增量改进"——
226
+ > 录入的 spec 天然就跟系统风格一致,AI 出原型时不会打架。
@@ -0,0 +1,81 @@
1
+ # Figma 协作指南(设计师专用)
2
+
3
+ > 工作流出的 HTML 原型 → Figma 精修 → 录回 spec.json 闭环
4
+
5
+ ## 为什么这条路可行
6
+
7
+ 工作流用 `fill_prototype.py` 出的 HTML 原型有以下特征,**非常适合 html.to.design 插件转换**:
8
+
9
+ - CSS 用标准 CSS 变量(`:root { --primary: ... }`),插件能正确解析成 Figma Variables
10
+ - 布局用 Flexbox/Grid,转换后图层结构清晰
11
+ - 颜色是 Vben 真实 HSL token(不是 #1890ff),导入后直接是正确配色
12
+ - 图标是内联 SVG(Ant Design),导入后是可编辑矢量
13
+
14
+ ## 完整流程(4 步)
15
+
16
+ ### Step 1: PM 出原型(工作流侧)
17
+
18
+ PM 在 QoderWork 说"出个 XX 的原型",工作流输出:
19
+
20
+ ```
21
+ workspace/members/{pm}/drafts/prototype-{需求名}-web.html
22
+ ```
23
+
24
+ 设计师从 PM 那拿到这个 HTML 文件。
25
+
26
+ ### Step 2: 导入 Figma(设计师侧)
27
+
28
+ 1. 打开 Figma,新建一个 Frame
29
+ 2. 菜单:**Plugins → html.to.design**(by ‹div›RIOTS)
30
+ - 没装的话:Figma Community 搜 "html.to.design" 安装(免费)
31
+ 3. 插件面板里选 **File** 标签(不是 URL 标签)
32
+ 4. 把 HTML 文件**拖进拖放区**(或点选文件)
33
+ 5. 点 **Import**,等几秒
34
+
35
+ **结果**:HTML 变成完全可编辑的 Figma 图层——每个元素是独立的 Frame/Text/Rectangle,颜色是正确的 Vben 配色。
36
+
37
+ ### Step 3: 设计师精修(Figma 侧)
38
+
39
+ 设计师在 Figma 里做只有人能做的精修:
40
+
41
+ - 调间距/对齐(AI 的间距可能不精确)
42
+ - 优化交互细节(hover 态、动画、过渡)
43
+ - 换更合适的图标/插图
44
+ - 调整信息层级和视觉重点
45
+ - 加设计师特有的"手感"
46
+
47
+ **注意**:颜色 token(`--primary` 等)尽量别改——它们是系统统一的,改了前端实现时会对不上。
48
+
49
+ ### Step 4: 录回工作流(闭环)
50
+
51
+ 设计师精修完,在 QoderWork 里说:
52
+
53
+ > "录入设计稿"
54
+
55
+ 工作流把 Figma 最终稿的关键决策(布局/配色/组件)录成 `data/style/{需求名}-design-spec.json`。
56
+
57
+ 从此:
58
+ - 下次同需求出原型,工作流**优先用这份 spec**
59
+ - 前端拿到的 HTML 原型**匹配设计师的 Figma 终稿**
60
+ - 设计师的劳动成果**沉淀进知识图谱**,不浪费
61
+
62
+ ## 常见问题
63
+
64
+ **Q: 导入后图层太碎怎么办?**
65
+ A: html.to.design 会按 HTML DOM 结构生成图层。fill_prototype 出的 HTML 结构已经很干净(侧边栏/搜索栏/表格各自独立),不会太碎。如果某个区域图层太多,选中后用 Figma 的 "Flatten" 合并。
66
+
67
+ **Q: 表格数据是假的怎么办?**
68
+ A: 对的,原型里的表格行是示例数据("XX示例1")。这是故意的——原型验证的是布局和交互,不是数据。真实数据前端会从 API 取。
69
+
70
+ **Q: 图标导入后变位图了?**
71
+ A: 不会。工作流用的图标是内联 SVG(不是图片标签),html.to.design 会保留为矢量。如果个别图标显示异常,从 `data/index/icon-reference.json` 找原始 SVG 手动替换。
72
+
73
+ **Q: APP 端原型也能导入吗?**
74
+ A: 能。`prototype-{需求名}-app.html` 同样是标准 HTML,导入方式一样。APP 原型用的是 Vant 风格(移动端组件库)。
75
+
76
+ ## html.to.design 插件安装
77
+
78
+ - Figma Community 搜索:`html.to.design`
79
+ - 或直接访问:https://www.figma.com/community/plugin/1159123024924461424
80
+ - 免费版每次导入有限制,团队版无限
81
+ - 也有 Chrome 扩展版(适合截取在线页面)
@@ -1,25 +1,82 @@
1
- ---
2
- name: design-review
3
- description: "评审设计交付物的完整性。Review design artifacts for completeness. 用户说'评审设计''设计稿看一下''检查交互稿'时触发。"
4
- trigger: "user invokes /review design, asks to check design, or designer submits design artifacts"
5
- ---
6
-
7
- # Design Review Skill
8
-
9
- ## ⚙️ 自取上下文(Quest / QoderWork 无 hook 注入,必须自读)
10
-
11
- - `.qoder/.developer` — 当前评审人
12
- - 设计文件路径由用户指定,或扫 `workspace/members/{dev}/drafts/prototype-*.html`
13
-
14
- ## 检查项 (Checklist)
15
- - [ ] 组件规格完整(components.json)
16
- - [ ] 交互流程覆盖所有状态(interaction-flow.md)
17
- - [ ] 技术约束已标注
18
- - [ ] 响应式/设计 Token 已定义
19
- - [ ] 符合 PRD 需求
20
- - [ ] **图标来自真源(data/index/icon-reference.json),无 emoji**(硬性)
21
- - [ ] 颜色来自真源(Web: vben-style-reference.json;APP: Vant 变量)
22
-
23
- ## 输出
24
-
25
- 报告:PASS 列出需修复的问题清单
1
+ ---
2
+ name: design-review
3
+ description: "评审设计交付物的完整性。Review design artifacts for completeness. 用户说'评审设计''设计稿看一下''检查交互稿'时触发。"
4
+ trigger: "user invokes /review design, asks to check design, or designer submits design artifacts"
5
+ ---
6
+
7
+
8
+ ## 🔧 仓库根定位(QoderWork 桌面端 vs Qoder IDE/CLI)
9
+
10
+ **后续脚本里的 `$R` 代表仓库根**,先确定它(QoderWork 桌面端工作目录不是仓库根,相对路径会失效):
11
+ ```bash
12
+ R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
13
+ ```
14
+ > `repo_root.py` 从 `~/.qoderwork/mcp.json` 反推仓库根;失败回退 `.`(IDE/CLI 工作目录即仓库根)。找不到时先跑 `python .qoder/scripts/install_qoderwork.py`。
15
+
16
+ # Design Review Skill(设计师 + PM 都可用)
17
+
18
+ > 🚫 **边界铁律:蓝湖工具(`mcp__lanhu__*`)只能在本 skill(即 `/wl-design-spec 评审`)内部调用。**
19
+ > 用户直接说"用蓝湖对比一下"而没走评审命令时,引导到 `/wl-design-spec 评审`。非侵入式,不裸调。
20
+
21
+ ## ⚙️ 自取上下文(Quest / QoderWork hook 注入,必须自读)
22
+
23
+ - `.qoder/.developer` — 当前评审人
24
+ - 设计文件路径由用户指定,或扫 `workspace/members/{dev}/drafts/prototype-*.html`
25
+ - `data/style/{需求名}-design-spec.json` 存在,作为评审基准(优先级最高)
26
+
27
+ ## 两种评审模式
28
+
29
+ ### 模式 A:评审 AI 原型(设计师审 AI 的活)
30
+
31
+ 设计师在 QoderWork 说"评审原型",AI 拉出 `drafts/prototype-*.html`:
32
+
33
+ 1. 逐项检查(见下方 Checklist)
34
+ 2. **对照设计师 spec**:若 data/style/ 有 spec.json,检查原型是否匹配
35
+ 3. 输出问题清单(按严重度排序),设计师确认后 PM 说"修订原型"
36
+
37
+ ### 模式 B:评审设计师交付(PM 审设计师的活)
38
+
39
+ PM 说"评审设计稿",AI 检查设计师的 spec.json 或原型:
40
+
41
+ 1. 是否跟系统现有风格一致(对照 vben-style-reference.json / Vant 变量)
42
+ 2. 图标/颜色是否来自真源
43
+ 3. 是否覆盖 PRD 的所有界面需求
44
+
45
+ ## 检查项 (Checklist)
46
+
47
+ - [ ] 组件规格完整(components.json 或 spec.json)
48
+ - [ ] 交互流程覆盖所有状态(interaction-flow.md)
49
+ - [ ] 技术约束已标注
50
+ - [ ] 响应式/设计 Token 已定义
51
+ - [ ] 符合 PRD 需求
52
+ - [ ] **图标来自真源(data/index/ref-icon.json),无 emoji**(硬性)
53
+ - [ ] 颜色来自真源(Web: ref-vben-style.json;APP: Vant 变量)
54
+ - [ ] **若设计师 spec 存在:原型是否匹配 spec 的布局/配色/组件**(硬性)
55
+ - [ ] 侧边栏/导航/表格/表单等关键区域是否跟系统其他页面一致
56
+ - [ ] **🆕 按钮文案是否用了系统真实文案**(对照 DESIGN.md 的 "Real Button Texts" 段或 entity-registry,别用编的"新增/编辑")
57
+ - [ ] **🆕 布局是否跟同功能模块的标杆页面一致**(对照 DESIGN.md 的 "Feature Modules" 段)
58
+ - [ ] **🆕 侧边栏宽度是否 = layout_fingerprint 的值**(fywl-ui 是 160px,不是 200px)
59
+ - [ ] **🆕 若 spec 有 `lanhu_source`:原型配色是否忠于蓝湖原图**(调 `mcp__lanhu__get_ai_analyze_design_result` 拉蓝湖 CSS,逐项比原型 `:root` 的值是否一致;不一致=还原度问题)
60
+ - [ ] **🆕 若 spec 有 `icons`:原型图标是否用了蓝湖切图**(别退回 emoji 或通用图标)
61
+
62
+ **评审时可调 MCP 工具辅助:**
63
+ - `mcp__qoder-knowledge-graph__feature_overview(feature='XX')` → 看这个功能模块的完整画像,对比原型是否遗漏页面/按钮
64
+ - `mcp__qoder-knowledge-graph__get_design_system(platform='web')` → 拿到真实 token/按钮文案/模块标杆做对照
65
+ - **`mcp__qoder-knowledge-graph__get_workflow(module='XX')`** → 查业务操作链(查询→新增→审批→...),检查原型是否覆盖了完整交互流程(而不只是静态页面)
66
+ - **`mcp__coverage_matrix()`** → 查这个功能有没有测试覆盖,如果原型要改的页面有测试,提醒"改动后需回归测试"
67
+ - **`mcp__lanhu__get_ai_analyze_design_result`** → spec 有 lanhu_source 时,拉蓝湖原图 CSS 做还原度比对(蓝湖不可用则跳过此项,不阻塞)
68
+
69
+ ## 输出
70
+
71
+ 报告:PASS 或 列出需修复的问题清单(按 🔴严重/🟡建议 分级)。
72
+
73
+ ## 埋点(评审完成后 AI 必须做)
74
+
75
+ 评审输出报告后,**立即调用 learning 引擎记录本次评审**(让团队学习哪些设计问题最常见):
76
+
77
+ ```bash
78
+ python "$R/.qoder/scripts/learn.py" record review_done "{\"target\":\"{被评审的原型或spec}\",\"result\":\"PASS或问题数\",\"issues\":[\"严重问题1\",\"建议1\"]}"
79
+ ```
80
+
81
+ > 埋点失败不阻塞评审。数据进个人 journal,管理员聚合到 kg.duckdb 后,可统计"最常出现的设计问题",反哺 design-import 防再犯。
82
+ 若不通过,附上修复建议和参考文件路径,PM 确认后 AI 修订原型。