@hupan56/wlkj 2.7.3 → 2.7.4

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/bin/cli.js CHANGED
@@ -51,6 +51,18 @@ function py(script, args = []) {
51
51
  } catch (e) { return (e.stdout || e.message); }
52
52
  }
53
53
 
54
+ // 透传执行: 直接 spawn (不捕获输出, 实时显示, 支持长超时和交互式)
55
+ // 用于 kg_build/eval_prd/autotest 等耗时脚本, 以及需要交互的场景
56
+ function runScript(scriptName, args = [], timeoutMs = 600000) {
57
+ const p = path.join(process.cwd(), ".qoder", "scripts", scriptName);
58
+ if (!fs.existsSync(p)) { console.log("脚本不存在: " + scriptName + "。跑: npx wlkj init"); process.exit(1); }
59
+ const pyCmd = detectPyCmd();
60
+ if (!pyCmd) { console.log("Python 未安装。跑: npx @hupan56/wlkj install-env"); process.exit(1); }
61
+ const { spawnSync } = require("child_process");
62
+ const r = spawnSync(pyCmd, [p, ...args], { cwd: process.cwd(), stdio: "inherit", timeout: timeoutMs, encoding: "utf-8" });
63
+ process.exit(r.status || 0);
64
+ }
65
+
54
66
  // 带镜像兜底的 pip 安装 —— 复用 Python 的 common/pip_install.py (单一事实源)
55
67
  // 为什么不复用 Python: 上一轮给 init_doctor 做了 common/pip_install.py (失败切清华/阿里/腾讯)。
56
68
  // cli.js 的 install-env/update 是独立 JS 路径, 之前没享受镜像兜底 → 50 人里弱网用户全崩。
@@ -241,13 +253,14 @@ async function doInit(name, roleArg) {
241
253
  }
242
254
  console.log(` 引擎: ${copied} 个文件 (${hasExisting ? "更新" : "新建"})`);
243
255
 
244
- // === 2. 根文件 (AGENTS.md, 新手指南.md) ===
245
- ["root/AGENTS.md", "root/新手指南.md"].forEach(f => {
256
+ // === 2. 根文件 (AGENTS.md, 新手指南.md, requirements.txt) ===
257
+ ["root/AGENTS.md", "root/新手指南.md", "root/requirements.txt"].forEach(f => {
246
258
  const src = path.join(T, f);
247
259
  if (fs.existsSync(src)) {
248
260
  const dstName = path.basename(f);
249
261
  const dst = path.join(cwd, dstName);
250
- if (!fs.existsSync(dst)) {
262
+ // requirements.txt 始终覆盖(版本可能更新), 其它不存在才拷
263
+ if (dstName === "requirements.txt" || !fs.existsSync(dst)) {
251
264
  fs.copyFileSync(src, dst);
252
265
  }
253
266
  }
@@ -520,14 +533,29 @@ function doUpdate() {
520
533
  console.log(` config.yaml / settings.json: 已保护(未改动)`);
521
534
  }
522
535
 
523
- // === 2. 根文件(文档类, 安全覆盖)===
524
- ["root/AGENTS.md", "root/新手指南.md"].forEach(f => {
536
+ // === 2. 根文件(文档 + requirements.txt, 始终覆盖保证最新)===
537
+ ["root/AGENTS.md", "root/新手指南.md", "root/requirements.txt"].forEach(f => {
525
538
  const src = path.join(T, f);
526
539
  if (fs.existsSync(src)) {
527
540
  fs.copyFileSync(src, path.join(cwd, path.basename(f)));
528
541
  }
529
542
  });
530
- console.log(` 根文档: 已更新`);
543
+ console.log(` 根文档 + requirements.txt: 已更新`);
544
+
545
+ // === 2.5. git pull 拉最新团队数据 (kg.duckdb/kg_db.duckdb/PRD等在git里) ===
546
+ try {
547
+ const { execSync: _es } = require("child_process");
548
+ const pullR = _es("git pull --no-rebase 2>&1", { cwd, encoding: "utf-8", timeout: 30000 });
549
+ if (pullR.includes("Already up to date") || pullR.includes("已经是最新")) {
550
+ console.log(` git: 已是最新`);
551
+ } else if (pullR.includes("CONFLICT") || pullR.includes("conflict")) {
552
+ console.log(` git: ⚠️ 有冲突, 跳过pull(不影响本地工作)`);
553
+ } else {
554
+ console.log(` git: 已拉取最新`);
555
+ }
556
+ } catch (e) {
557
+ console.log(` git: 跳过(${(e.message || "").slice(0, 50)})`);
558
+ }
531
559
 
532
560
  // === 3. 刷新 QoderWork commands(强制覆盖已存在的)===
533
561
  runQoderWorkInstall(cwd, true);
@@ -537,6 +565,7 @@ function doUpdate() {
537
565
 
538
566
  // === 5. 清理旧版废弃文件 ===
539
567
  const OBSOLETE_FILES = [
568
+ // 旧脚本(已替代)
540
569
  ".qoder/workflow.md", // 被 rules/wl-pipeline.md 替代
541
570
  ".qoder/scripts/code_index.py", // 被 git_sync.py 替代
542
571
  ".qoder/scripts/notify.py", // 被 common/feishu.py 替代
@@ -546,6 +575,35 @@ function doUpdate() {
546
575
  ".qoder/templates/qoder/scripts/code_index.py",
547
576
  ".qoder/templates/qoder/scripts/notify.py",
548
577
  ".qoder/templates/qoder/scripts/team.py",
578
+ // 旧命令(已合并进 /wl-prd 和 /wl-design)
579
+ ".qoder/commands/wl-prd-full.md",
580
+ ".qoder/commands/wl-prd-quick.md",
581
+ ".qoder/commands/wl-prd-review.md",
582
+ ".qoder/commands/wl-design-draw.md",
583
+ ".qoder/commands/wl-design-scan.md",
584
+ ".qoder/commands/wl-design-spec.md",
585
+ // 旧版索引JSON(已迁移到DuckDB, 残留在老用户的data/index/)
586
+ "data/index/keyword-index.json",
587
+ "data/index/api-index.json",
588
+ "data/index/prd-index.json",
589
+ "data/index/module-map.json",
590
+ "data/index/prd-code-map.json",
591
+ "data/index/callgraph.json",
592
+ "data/index/style-index.json",
593
+ "data/index/style-meta.json",
594
+ "data/index/vben-style-reference.json",
595
+ "data/index/chart-style-reference.json",
596
+ "data/index/icon-reference.json",
597
+ "data/index/wiki-index.json",
598
+ // 旧运行时缓存(自动重建)
599
+ "data/index/.file-keys.json",
600
+ "data/index/.inverted-cache.json",
601
+ "data/index/.api-table-cache.json",
602
+ // 旧learning空壳(未实现的功能占位)
603
+ ".qoder/learning/patterns/code-patterns.md",
604
+ ".qoder/learning/patterns/prd-patterns.md",
605
+ ".qoder/learning/patterns/review-fixes.md",
606
+ // 用户级旧命令(已合并的, 清理~/.qoderwork/commands/)
549
607
  ];
550
608
  let cleaned = 0;
551
609
  for (const f of OBSOLETE_FILES) {
@@ -563,6 +621,30 @@ function doUpdate() {
563
621
  }
564
622
  }
565
623
  // 清理 .new 临时文件 (上次升级生成的配置对比文件)
624
+ try {
625
+ for (const f of fs.readdirSync(path.join(cwd, ".qoder"))) {
626
+ if (f.endsWith(".new")) {
627
+ fs.unlinkSync(path.join(cwd, ".qoder", f));
628
+ cleaned++;
629
+ }
630
+ }
631
+ } catch { /* */ }
632
+
633
+ // 清理用户级旧命令 (已合并的, ~/.qoderwork/commands/)
634
+ const home = process.env.USERPROFILE || require("os").homedir();
635
+ const userCmdDir = path.join(home, ".qoderwork", "commands");
636
+ const OBSOLETE_USER_CMDS = [
637
+ "wl-prd-full.md", "wl-prd-quick.md", "wl-prd-review.md",
638
+ "wl-design-draw.md", "wl-design-scan.md", "wl-design-spec.md",
639
+ ];
640
+ for (const f of OBSOLETE_USER_CMDS) {
641
+ const fp = path.join(userCmdDir, f);
642
+ if (fs.existsSync(fp)) {
643
+ try { fs.unlinkSync(fp); console.log(` [清理] 用户级旧命令: ${f}`); cleaned++; }
644
+ catch { /* */ }
645
+ }
646
+ }
647
+ // 清理 .new 临时文件 (上次升级生成的配置对比文件)
566
648
  try {
567
649
  for (const f of fs.readdirSync(path.join(cwd, ".qoder"))) {
568
650
  if (f.endsWith(".new")) {
@@ -619,6 +701,45 @@ function doUpdate() {
619
701
  }
620
702
  } catch (e) { /* 刷新失败不阻塞升级 */ }
621
703
 
704
+ // === 8. 自检: 关键文件到底齐不齐 ===
705
+ console.log(`\n--- 自检 (关键文件验证) ---`);
706
+ const checks = [
707
+ ["requirements.txt", path.join(cwd, "requirements.txt"), "依赖清单(没它pip装不了)"],
708
+ ["duckdb(pip)", null, "知识图谱核心依赖"],
709
+ ["kg.duckdb", path.join(cwd, "data", "index", "kg.duckdb"), "代码图谱(MCP需要)"],
710
+ ["kg_db.duckdb", path.join(cwd, "data", "index", "kg_db.duckdb"), "数据库图谱(table_impact需要)"],
711
+ [".qoder/commands/", path.join(cwd, ".qoder", "commands"), "斜杠命令"],
712
+ ];
713
+ let allOk = true;
714
+ for (const [label, p, desc] of checks) {
715
+ if (p === null) {
716
+ // duckdb 用 python 检测
717
+ try {
718
+ execSync(`${pyCmd || "python"} -c "import duckdb"`, { stdio: "pipe", timeout: 5000 });
719
+ console.log(` [✓] ${label} - ${desc}`);
720
+ } catch {
721
+ console.log(` [✗] ${label} 缺失! ${desc}`);
722
+ console.log(` → 修复: pip install duckdb -i https://pypi.tuna.tsinghua.edu.cn/simple`);
723
+ allOk = false;
724
+ }
725
+ } else if (fs.existsSync(p)) {
726
+ console.log(` [✓] ${label}`);
727
+ } else {
728
+ console.log(` [✗] ${label} 缺失! ${desc}`);
729
+ if (label.includes("duckdb")) {
730
+ console.log(` → 修复: npx @hupan56/wlkj run kg_build.py --rebuild`);
731
+ } else if (label.includes("commands")) {
732
+ console.log(` → 修复: npx @hupan56/wlkj update`);
733
+ }
734
+ allOk = false;
735
+ }
736
+ }
737
+ if (!allOk) {
738
+ console.log(`\n ⚠️ 有缺失项, 按上面的提示修复后重跑 update。`);
739
+ } else {
740
+ console.log(` 全部关键文件就绪 ✓`);
741
+ }
742
+
622
743
  console.log(`\n${"=".repeat(50)}`);
623
744
  console.log(` 升级完成! (v${PKG_VERSION})`);
624
745
  console.log(`${"=".repeat(50)}`);
@@ -663,35 +784,24 @@ function doHelp() {
663
784
  console.log("");
664
785
  console.log(`wlkj - AI 产品研发工作流 (v${PKG_VERSION})`);
665
786
  console.log("");
666
- console.log("=== 环境安装 (什么都没装时先跑这个) ===");
667
- console.log(" npx @hupan56/wlkj install-env 检测+自动装 Node/Python/git");
668
- console.log(" npx @hupan56/wlkj install-env --check 只检测不装");
669
- console.log(" npx @hupan56/wlkj install-env --location D:\\wldev 指定安装目录");
670
- console.log(" npx @hupan56/wlkj add-path D:\\wldev\\nodejs 把目录加到系统 PATH");
671
- console.log("");
672
- console.log("=== 一键安装 (装好环境后) ===");
673
- console.log(" npx @hupan56/wlkj init [你的名字] 安装完整引擎 + 自动初始化");
674
- console.log("");
675
- console.log("=== 升级 (已装用户, 新版本发布后) ===");
676
- console.log(" npx @hupan56/wlkj update 刷新引擎, 保护你的配置");
787
+ console.log("=== 5 个核心命令 (记住这 5 个就够) ===");
677
788
  console.log("");
678
- console.log("=== 安装后怎么用 ===");
679
- console.log(" Qoder (IDE/Quest/QoderWork) 里:");
680
- console.log(" 说中文: '写个 XX 的需求' '查一下 XX 代码' '建个任务'");
681
- console.log(" 或斜杠: /wl-prd /wl-search /wl-task /wl-status /wl-report");
789
+ console.log(" npx @hupan56/wlkj install-env ① 装环境 (Node/Python/git/pip依赖)");
790
+ console.log(" npx @hupan56/wlkj init [名字] ② 装引擎 + 初始化身份");
791
+ console.log(" npx @hupan56/wlkj update ③ 升级 (刷新引擎+补依赖+刷MCP)");
792
+ console.log(" npx @hupan56/wlkj doctor ④ 体检 (环境/依赖/图谱健康度)");
793
+ console.log(" npx @hupan56/wlkj sync ⑤ 推送产出 (PRD/任务/原型)");
682
794
  console.log("");
683
- console.log("=== 状态 ===");
684
- console.log(" npx @hupan56/wlkj status 查看当前状态 + 引擎版本");
795
+ console.log("=== 万能跑 (想跑任何脚本) ===");
796
+ console.log(" npx @hupan56/wlkj run <脚本名> [参数]");
797
+ console.log(" 例: npx wlkj run kg_build.py --rebuild");
798
+ console.log(" npx wlkj run eval_prd.py xxx.md");
685
799
  console.log("");
686
- console.log("=== 环境修复 ===");
687
- console.log(" python .qoder/scripts/init_doctor.py --fix 自动修复环境");
688
- console.log(" python .qoder/scripts/setup.py 重新初始化");
800
+ console.log("=== Qoder 里怎么用 (日常就在这里干) ===");
801
+ console.log(" 说中文: '写个XX的需求' '查XX代码' '建任务' '测一下XX'");
802
+ console.log(" 或斜杠: /wl-prd /wl-design /wl-task /wl-code /wl-test /wl-commit");
689
803
  console.log("");
690
- console.log("=== Git (中文命令) ===");
691
- console.log(" npx @hupan56/wlkj 提交PRD 提交 PRD");
692
- console.log(" npx @hupan56/wlkj 提交任务 提交任务");
693
- console.log(" npx @hupan56/wlkj 提交 全部提交并推送");
694
- console.log(" npx @hupan56/wlkj 拉取最新 / 同步 git pull");
804
+ console.log(" 看不到命令? 新建对话 / 重启 QoderWork");
695
805
  console.log("");
696
806
  }
697
807
 
@@ -874,7 +984,27 @@ switch (cmd) {
874
984
  case "status": doStatus(); break;
875
985
  case "session": process.stdout.write(py("add_session.py", rest)); break;
876
986
  case "help": case "--help": case "-h": doHelp(); break;
877
- case "提交": case "推送": case "拉取最新": case "同步":
987
+
988
+ // ── 核心 5 个: 装/升/体检/同步/万能跑 ──
989
+ case "doctor": case "体检":
990
+ runScript("init_doctor.py", rest); break;
991
+ case "sync": case "push": case "commit":
992
+ runScript("team_sync.py", ["push", ...rest.filter(a => a !== "push")]); break;
993
+ case "pull":
994
+ runScript("team_sync.py", ["pull"]); break;
995
+
996
+ // ── 万能透传: npx wlkj run <脚本名> [参数...] ──
997
+ // 任何 .qoder/scripts/ 下的脚本都能跑, 不用记命令名
998
+ // 例: npx wlkj run kg_build.py --rebuild
999
+ // npx wlkj run eval_prd.py xxx.md
1000
+ // npx wlkj run autotest.py quick
1001
+ case "run": {
1002
+ if (!rest[0]) { console.log("用法: npx wlkj run <脚本名> [参数...]\n例: npx wlkj run kg_build.py --rebuild"); break; }
1003
+ runScript(rest[0], rest.slice(1));
1004
+ break;
1005
+ }
1006
+
1007
+ case "提交": case "拉取最新": case "同步":
878
1008
  case "查看状态": case "提交PRD": case "提交Spec": case "提交任务":
879
1009
  gitOp(cmd); break;
880
1010
  default: doHelp();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hupan56/wlkj",
3
- "version": "2.7.3",
3
+ "version": "2.7.4",
4
4
  "description": "AI Product R&D Workflow - PRD/Prototype/Search/Task/Report",
5
5
  "bin": {
6
6
  "wlkj": "bin/cli.js"
@@ -1,32 +1,125 @@
1
1
  ---
2
2
  name: wl-design
3
- description: "设计工序入口: import 录入设计稿→spec.json(默认) / generate 按设计spec出原型 / review 评审原型。"
4
- argument-hint: "<需求描述>"
3
+ description: "设计工序: 预览(蓝湖稿→HTML) / 扫描(看系统现状) / 评审(检查合规)。设计师在蓝湖画完,一条链接出预览。"
4
+ argument-hint: "[预览|扫描|评审] <蓝湖链接或功能名>"
5
5
  auto-approve: true
6
- allowed-tools: [Read, Bash, mcp__qoder-knowledge-graph, mcp__lanhu]
6
+ allowed-tools: [Read, Glob, Grep, Bash, Write, Edit, mcp__qoder-knowledge-graph, mcp__lanhu, mcp__qoder-mysql]
7
7
  ---
8
8
 
9
- # /wl-design - 设计工序站(总入口)
9
+ # /wl-design - 设计工序站
10
10
 
11
11
  User input: $ARGUMENTS
12
12
 
13
- > 三个子命令,覆盖 UI 工程师的全部工作:
14
- > - **scan**(看)→ 理解系统现状(功能模块/操作流程/设计风格/测试覆盖)
15
- > - **draw**(画)→ 产出界面(新页面/改页面/竞品参考/全新交互)
16
- > - **spec**(收)→ 设计交付(录入设计稿 + 评审原型)
13
+ > **设计师在蓝湖画 UI,这个命令负责把蓝湖稿接入工作流。**
14
+ > 模块契约:`.qoder/contracts/design.md`
17
15
 
18
- ## 不带子命令时
16
+ ## 🚦 路由(看第一个词)
19
17
 
20
- 如果用户直接 `/wl-design <描述>`(不带 -draw/-scan/-spec 后缀),AI 从描述判断:
18
+ | 用户说 | 模式 | 干啥 |
19
+ |--------|------|------|
20
+ | "预览""出预览""蓝湖链接""看看设计稿" 或 含 lanhuapp.com | **预览** | 蓝湖稿 → HTML 预览 |
21
+ | "扫描""看系统""有哪些页面""什么风格" | **扫描** | 看现有系统页面/组件/风格 |
22
+ | "评审""检查""合规""颜色对不对" | **评审** | 检查 HTML 预览是否用了系统真源 |
23
+ | 其他 / 不确定 | 先问:预览蓝湖稿、扫描系统、还是评审原型? |
21
24
 
22
- | 描述特征 | 推荐 |
23
- |---------|------|
24
- | "出个""画个""做个""改个" | `/wl-design-draw` |
25
- | "看看""有哪些""什么风格""影响什么" | → `/wl-design-scan` |
26
- | "录入""评审""检查""Figma" | `/wl-design-spec` |
27
- | 判断不了 | 问一句:"你要画原型(draw)、看系统现状(scan)、还是录入/评审(spec)?" |
25
+ ---
26
+
27
+ ## 预览:蓝湖稿HTML(核心能力)
28
+
29
+ **场景**:设计师在蓝湖画完 UI,发链接给你,AI 一键转成 HTML 预览给产品确认交互。
30
+
31
+ ### 流程(2 步,走 lanhu MCP)
32
+
33
+ **Step 1:列设计稿**
34
+ ```
35
+ mcp__lanhu__lanhu_get_designs(url="<蓝湖链接>")
36
+ ```
37
+ - 蓝湖链接含 `pid`(项目 ID),如 `https://lanhuapp.com/web/#/item/project/stage?tid=xxx&pid=xxx`
38
+ - 返回设计稿列表(名称 + 序号)
39
+
40
+ **Step 2:分析出 HTML**
41
+ ```
42
+ mcp__lanhu__lanhu_get_ai_analyze_design_result(url="<蓝湖链接>", design_names="<名称或序号或all>")
43
+ ```
44
+ - 返回 **HTML+CSS 代码**(工具原文:"GET VISUAL CONTENT + HTML CODE")
45
+ - AI 把返回的 HTML 存成文件:`workspace/members/{developer}/drafts/prototype-{feature}.html`
46
+
47
+ ### 完成后提示
48
+ ```
49
+ ✅ HTML 预览已生成: workspace/members/{dev}/drafts/prototype-{feature}.html
50
+ 来源: 蓝湖设计稿 → lanhu MCP → HTML+CSS
51
+ 打开文件可直接预览交互。
52
+ ```
53
+
54
+ ### 注意
55
+ - 蓝湖链接必须有 `pid`(项目 ID),分享链接先用 `lanhu_resolve_invite_link` 解析
56
+ - 如果蓝湖 cookie 过期 → 提示"蓝湖登录态过期,重新配置:setup_lanhu.py"
57
+ - 如果是 Axure 原型(非 UI 设计稿)→ 用 `lanhu_get_ai_analyze_page_result`(分析交互流程)
58
+
59
+ ---
60
+
61
+ ## 扫描:看系统现状(画之前用)
62
+
63
+ **场景**:画 UI 前想看现有系统有哪些页面、用什么风格、有哪些组件可复用。
64
+
65
+ ### 流程(1 次调用取全)
66
+ ```
67
+ mcp__qoder-knowledge-graph__context_pack(keyword='<功能名>', platform='web')
68
+ ```
69
+ 返回 8 段:相关代码 + 同类页面 + 字段 + API + 风格 token + 组件 + 数据库表结构。
70
+
71
+ **重点关注**:
72
+ - 第 2 段(同类页面示例)→ 看现有页面长什么样
73
+ - 第 6 段(风格真源)→ 颜色/字号/间距 token
74
+ - `get_design_system(platform='web')` → 拿 Vben Admin 设计系统详情
75
+
76
+ ### 完成后提示
77
+ ```
78
+ 📋 系统现状扫描完成:
79
+ - 现有页面: X 个同类页面(列出)
80
+ - 设计系统: 主色 #xxx, 组件 Space/Modal/Select...
81
+ - 可复用组件: 列出 top 5
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 评审:检查合规
87
+
88
+ **场景**:HTML 预览生成后,检查颜色/图标/间距是否用了系统真源(不是瞎编的)。
89
+
90
+ ### 检查项
91
+ - [ ] 颜色来自 `data/index/ref-vben-style.json`(不是随便填的色值)
92
+ - [ ] 图标来自 `data/index/ref-icon.json`(Web=Ant Design SVG / APP=Vant,**禁 emoji**)
93
+ - [ ] 间距/字号符合设计系统 token
94
+ - [ ] 组件用法和现有页面一致
95
+
96
+ ### 流程
97
+ ```
98
+ 读 HTML 预览文件 → 比对 get_design_system(platform='web') → 输出 PASS 或问题清单
99
+ ```
100
+
101
+ 输出:`✅ PASS` 或 `🔴/🟡 问题清单`(按严重程度分级)。
102
+
103
+ ---
104
+
105
+ ## 平台必问
106
+
107
+ 如果用户没说平台,先问:
108
+ ```
109
+ 这个设计是针对哪个平台?
110
+ 1. Web 管理端 (fywl-ui) - Ant Design Vue + Vben
111
+ 2. APP 移动端 (Carmg-H5) - Vant
112
+ 请选择 (1/2):
113
+ ```
114
+ ```
115
+ (两端都要也行,但分别处理)
116
+ ```
117
+
118
+ ---
119
+
120
+ ## 埋点(评审完成后)
28
121
 
29
- > 但推荐用户直接用带后缀的命令(更准确):
30
- > `/wl-design-draw 营业外合同`
31
- > `/wl-design-scan 资产管理`
32
- > `/wl-design-spec 录入`
122
+ 评审输出后,记录到 learning:
123
+ ```bash
124
+ python "$R/.qoder/scripts/learn.py" record review_done "{\"target\":\"$feature\",\"result\":\"$pass_or_issues\"}"
125
+ ```
@@ -1,27 +1,99 @@
1
1
  ---
2
2
  name: wl-prd
3
- description: "[已废弃] 旧的单命令已拆分。请用 /wl-prd-full /wl-prd-quick /wl-prd-review"
3
+ description: "需求: 完整(13章,新功能) / 快速(6章,小改动) / 评审(检查质量)。默认根据描述自动判断。"
4
+ argument-hint: "[完整|快速|评审] [需求描述] 可选: 参考:<insight报告路径>"
4
5
  auto-approve: true
6
+ allowed-tools: [Read, Glob, Grep, Bash, Write, Edit, mcp__qoder-knowledge-graph, mcp__qoder-mysql]
5
7
  ---
6
8
 
7
- # /wl-prd - 已废弃(拆分为 3 个独立命令)
9
+ # /wl-prd - 需求工序站
8
10
 
9
- > ⚠️ **本命令已废弃。** `/wl-prd` 的单命令 + 参数路由(`快速`/`深度`/`探讨`)已拆成 3 个独立命令,
10
- > **只准 `/wl-prd-xx` 形式触发**。请改用:
11
+ User input: $ARGUMENTS
11
12
 
12
- | 你要做什么 | 用哪个命令 |
13
- |-----------|-----------|
14
- | 写正经需求(新模块/新业务/新流程),完整 13 章 PRD + 原型 | **`/wl-prd-full`** |
15
- | 小改动(加字段/加按钮/改文案),极速 Mini-PRD | **`/wl-prd-quick`** |
16
- | 评审已有 PRD 的质量 | **`/wl-prd-review`** |
13
+ > **写需求 / 评审需求,一个命令搞定。**
14
+ > 模块契约:`.qoder/contracts/prd.md`
17
15
 
18
- ## 旧用法对照
16
+ ## 🚦 路由(看第一个词或描述特征)
19
17
 
20
- | 旧用法(已废弃) | 新用法 |
21
- |----------------|--------|
22
- | `/wl-prd 快速:<需求>` | `/wl-prd-quick <需求>` |
23
- | `/wl-prd <需求>`(默认) | `/wl-prd-full <需求>` |
24
- | `/wl-prd 参考:<报告>`(衔接 insight) | `/wl-prd-full 参考:<报告>` |
25
- | `/wl-prd 深度` / `/wl-prd 探讨` | `/wl-prd-full`(探索类已归 `/wl-insight`) |
18
+ | 用户说 | 模式 | 走哪个 |
19
+ |--------|------|--------|
20
+ | "完整""深度""正经需求" 描述含"新模块/新业务/新流程" | **完整** | prd-full-template(13 章)|
21
+ | "快速""小改动""加个""改下" 加字段/按钮/文案/导出 | **快速** | prd-quick-template(6 章)|
22
+ | "评审""检查""质量""合格吗" | **评审** | 7 checklist + EVA 评分 |
23
+ | 判断不了 | **问一句**:"是完整需求(新功能)还是小改动(加字段/按钮)?" |
26
24
 
27
- > 旧参数路由模式不再支持。输入 `/wl-prd-` 可看到 3 个新命令。
25
+ ### 默认规则
26
+ - 没说模式 + 描述像新功能 → 默认**完整**
27
+ - 没说模式 + 描述像小改动 → 默认**快速**
28
+ - 给了 PRD 文件路径 → **评审**
29
+
30
+ ---
31
+
32
+ ## 完整模式(13 章 PRD)
33
+
34
+ **新模块/新业务/新流程用这个。** 支持衔接 insight:`参考:<报告路径>`
35
+
36
+ **铁律:平台必问。** 先问:
37
+ ```
38
+ 这个需求是针对哪个平台?
39
+ 1. Web 管理端 (fywl-ui)
40
+ 2. APP 移动端 (Carmg-H5)
41
+ 3. 两端都要
42
+ 请选择 (1/2/3):
43
+ ```
44
+
45
+ ### 取上下文(1 次调用取全 8 段)
46
+ ```bash
47
+ python "$R/.qoder/scripts/context_pack.py" <业务词> --platform <web|app>
48
+ ```
49
+ - 第 4 段 = 历史 PRD(防重复)
50
+ - 第 5 段 = 相关 API
51
+ - 第 8 段 = 数据库表结构+真实字段
52
+ - 涉及枚举字段时额外调 `mcp__qoder-mysql__query_distinct`
53
+
54
+ ### 产出
55
+ - PRD: `workspace/members/{dev}/drafts/REQ-{YYYY}-{NNN}-{标题}.md`(13 章)
56
+ - 原型: `workspace/members/{dev}/drafts/prototype-{feature}.html`
57
+ - 第一行必须带 `@contract` 头(platform/req-id/mode/acceptance)
58
+
59
+ ### 质量门禁
60
+ ```bash
61
+ python "$R/.qoder/scripts/eval_prd.py" <prd.md> [原型.html]
62
+ ```
63
+ ≥ 80% 才 PASS。完整模板见 `.qoder/templates/prd-full-template.md`。
64
+
65
+ ---
66
+
67
+ ## 快速模式(6 章 Mini-PRD)
68
+
69
+ **加字段/加按钮/改文案/加导出等零星需求专用。** 极速,不走 EVA。
70
+
71
+ **平台必问**(同上)。
72
+
73
+ ### 产出
74
+ - Mini-PRD: `workspace/members/{dev}/drafts/REQ-{YYYY}-{NNN}-{标题}.md`(6 章)
75
+ - 6 章:功能入口 / 需求背景 / 需求说明 / 影响范围 / 验收标准 / 不在本次范围
76
+ - 模板见 `.qoder/templates/prd-quick-template.md`
77
+
78
+ ---
79
+
80
+ ## 评审模式(7 项 checklist + EVA)
81
+
82
+ **检查 PRD 完整性与质量。**
83
+
84
+ ### 流程
85
+ 1. 不给文件 → 列出最近的 PRD 让用户选
86
+ 2. 7 项 checklist 逐项检查:
87
+ - 验收标准是否可测
88
+ - 字段是否引用了真实数据库字段
89
+ - 影响范围是否评估
90
+ - 非功能性需求是否覆盖
91
+ - ...
92
+ 3. 跑 EVA 评分:`eval_prd.py <prd.md>`
93
+ 4. 输出:总分 + 7 项 ✓/✗ + 改进建议
94
+
95
+ ---
96
+
97
+ ## 埋点
98
+
99
+ PRD 生成/评审后自动记录到 learning(eval_prd.py 已内置)。
@@ -199,7 +199,11 @@ def collect_prds():
199
199
  is_updated = not is_new and collected[key].get('mtime', 0) < prd['mtime']
200
200
 
201
201
  if is_new or is_updated:
202
- dst = os.path.join(PRD_DIR, prd['file'])
202
+ # 按年月聚合: data/docs/prd/{YYYY-MM}/REQ-xxx.md
203
+ prd_year_month = datetime.now().strftime('%Y-%m')
204
+ prd_subdir = os.path.join(PRD_DIR, prd_year_month)
205
+ os.makedirs(prd_subdir, exist_ok=True)
206
+ dst = os.path.join(prd_subdir, prd['file'])
203
207
  shutil.copy2(prd['path'], dst)
204
208
  collected[key] = {
205
209
  'user': prd['user'],
@@ -1,78 +0,0 @@
1
- ---
2
- name: wl-design-draw
3
- description: "画界面/原型。说你要什么,AI 画出来。新页面、改页面、参考竞品都行。"
4
- argument-hint: "<你要画什么>"
5
- auto-approve: true
6
- allowed-tools: [Read, Glob, Grep, Bash, Write, Edit, mcp__qoder-knowledge-graph, mcp__lanhu]
7
- ---
8
-
9
- # /wl-design-draw - 画界面
10
-
11
- User input: $ARGUMENTS
12
-
13
- > 用户说"画什么",AI 画出来。就这么简单。
14
-
15
- ## 🔧 环境自检(QoderWork 桌面端 vs Qoder IDE/CLI)
16
-
17
- **先确定仓库根 R**(QoderWork 桌面端工作目录不是仓库根,相对路径会失效):
18
- ```bash
19
- R=$(python ~/.qoderwork/repo_root.py 2>/dev/null) || R=.
20
- ```
21
- > 后续脚本统一用 `python "$R/.qoder/scripts/xxx.py"`。
22
-
23
- ## Step 1: 平台(问了就停)
24
-
25
- ```
26
- 这个界面是针对哪个平台?
27
- 1. Web 管理端 (fywl-ui)
28
- 2. APP 移动端 (Carmg-H5)
29
- 3. 两端都要
30
- 请选择 (1/2/3):
31
- ```
32
-
33
- ## Step 2: 如果输入是大板块,先缩小到具体页面
34
-
35
- "资产"有 38 个页面,不能直接画。调 `mcp__qoder-knowledge-graph__feature_overview(feature='资产')`,
36
- 列出页面问用户选哪个(**问了就停**)。
37
-
38
- 具体功能(如"营业外合同筛选")跳过这步。
39
-
40
- ## Step 3: 画
41
-
42
- **先拿设计约束 + 真实数据(不许跳过,不许手写 HTML):**
43
-
44
- ```
45
- mcp__qoder-knowledge-graph__get_design_system(platform='web') → 颜色/布局/按钮/组件
46
- mcp__qoder-knowledge-graph__fill_prototype(keyword='营业外合同', platform='web') → 80% 草稿
47
- ```
48
-
49
- FALLBACK(无 MCP 时):
50
- ```bash
51
- python "$R/.qoder/scripts/gen_design_doc.py"
52
- python "$R/.qoder/scripts/fill_prototype.py" 营业外合同 --platform web
53
- ```
54
-
55
- **拿到草稿后微调:**
56
- - 按钮用真实文案(数据清单里的,不是编的"新增/编辑")
57
- - 补交互细节(弹窗/Tab/折叠)
58
- - 画完整状态(有数据 + 空状态 + 加载中)
59
- - **颜色/宽度/间距不许改**(来自真实系统,AI 猜的一定不对)
60
-
61
- ## 铁律
62
-
63
- 1. **必须先调 get_design_system + fill_prototype**,不许跳过直接手写
64
- 2. **若设计师录入了蓝湖 spec,fill_prototype 会自动注入**——先确认 `data/style/{关键词}-design-spec.json` 是否存在。若有,fill_prototype 会把蓝湖的真实 `design_tokens`(如 `rgba(255,115,10,1)`)原样注入原型 `:root`,**这一步全自动,不用手动调蓝湖**。验证:看输出原型 `:root` 里有没有 `/* design-import spec tokens (优先级最高) */`
65
- 3. **颜色只用真源**(蓝湖 spec 注入值 > get_design_system 的 HSL,不是 #1890ff)
66
- 4. **按钮用真实文案**(entity-registry 的,不是编的)
67
- 5. **布局参数不猜**(蓝湖 spec 的 width 或 layout_fingerprint 的 160px)
68
- 6. **图标禁 emoji**(Web 用 Ant Design SVG,APP 用 Vant 字体图标)
69
- 7. **画完整状态**(不只画"有数据",还要画空状态/加载中/错误)
70
-
71
- > 蓝湖与画图的关系:**蓝湖在录入环节(/wl-design-spec)发力**,读出的 CSS 值存进 spec.json;
72
- > 画图时 fill_prototype 自动消费 spec.json,不需要 draw 再调蓝湖。蓝湖没起/没录入?不影响 draw,
73
- > fill_prototype 降级用 layout_fingerprint + 代码风格,照样出原型(只是不如蓝湖精确)。
74
-
75
- ## 存储与 Figma
76
-
77
- 存到 `workspace/members/{developer}/drafts/prototype-{feature}.html`。
78
- 设计师可在 Figma 用 html.to.design 插件导入精修。