@hupan56/wlkj 2.7.2 → 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 +162 -32
- package/package.json +1 -1
- package/templates/qoder/commands/wl-design.md +113 -20
- package/templates/qoder/commands/wl-prd.md +89 -17
- package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/git_sync.py +5 -1
- package/templates/qoder/scripts/platform_doctor.py +259 -0
- package/templates/qoder/commands/wl-design-draw.md +0 -78
- package/templates/qoder/commands/wl-design-scan.md +0 -108
- package/templates/qoder/commands/wl-design-spec.md +0 -154
- package/templates/qoder/commands/wl-prd-full.md +0 -226
- package/templates/qoder/commands/wl-prd-quick.md +0 -134
- package/templates/qoder/commands/wl-prd-review.md +0 -104
- package/templates/qoder/scripts/__pycache__/search_index.cpython-39.pyc +0 -0
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
|
-
|
|
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("
|
|
680
|
-
console.log("
|
|
681
|
-
console.log("
|
|
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
|
|
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("
|
|
688
|
-
console.log("
|
|
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("
|
|
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
|
-
|
|
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,32 +1,125 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wl-design
|
|
3
|
-
description: "
|
|
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
|
-
>
|
|
14
|
-
>
|
|
15
|
-
> - **draw**(画)→ 产出界面(新页面/改页面/竞品参考/全新交互)
|
|
16
|
-
> - **spec**(收)→ 设计交付(录入设计稿 + 评审原型)
|
|
13
|
+
> **设计师在蓝湖画 UI,这个命令负责把蓝湖稿接入工作流。**
|
|
14
|
+
> 模块契约:`.qoder/contracts/design.md`
|
|
17
15
|
|
|
18
|
-
##
|
|
16
|
+
## 🚦 路由(看第一个词)
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
| 用户说 | 模式 | 干啥 |
|
|
19
|
+
|--------|------|------|
|
|
20
|
+
| "预览""出预览""蓝湖链接""看看设计稿" 或 含 lanhuapp.com | **预览** | 蓝湖稿 → HTML 预览 |
|
|
21
|
+
| "扫描""看系统""有哪些页面""什么风格" | **扫描** | 看现有系统页面/组件/风格 |
|
|
22
|
+
| "评审""检查""合规""颜色对不对" | **评审** | 检查 HTML 预览是否用了系统真源 |
|
|
23
|
+
| 其他 / 不确定 | 先问:预览蓝湖稿、扫描系统、还是评审原型? |
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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: "
|
|
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 -
|
|
9
|
+
# /wl-prd - 需求工序站
|
|
8
10
|
|
|
9
|
-
|
|
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
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
18
|
+
| 用户说 | 模式 | 走哪个 |
|
|
19
|
+
|--------|------|--------|
|
|
20
|
+
| "完整""深度""正经需求" 或 描述含"新模块/新业务/新流程" | **完整** | prd-full-template(13 章)|
|
|
21
|
+
| "快速""小改动""加个""改下" 或 加字段/按钮/文案/导出 | **快速** | prd-quick-template(6 章)|
|
|
22
|
+
| "评审""检查""质量""合格吗" | **评审** | 7 项 checklist + EVA 评分 |
|
|
23
|
+
| 判断不了 | **问一句**:"是完整需求(新功能)还是小改动(加字段/按钮)?" |
|
|
26
24
|
|
|
27
|
-
|
|
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
|
-
|
|
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'],
|