@hupan56/wlkj 2.6.0 → 2.7.1

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 (125) hide show
  1. package/bin/cli.js +269 -11
  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-reference.md +8 -2
  6. package/templates/qoder/commands/optional/wl-insight.md +275 -0
  7. package/templates/qoder/commands/{wl-report.md → optional/wl-report.md} +13 -5
  8. package/templates/qoder/commands/{wl-spec.md → optional/wl-spec.md} +1 -1
  9. package/templates/qoder/commands/{wl-status.md → optional/wl-status.md} +28 -2
  10. package/templates/qoder/commands/wl-code.md +10 -2
  11. package/templates/qoder/commands/wl-commit.md +1 -1
  12. package/templates/qoder/commands/wl-design-draw.md +78 -0
  13. package/templates/qoder/commands/wl-design-scan.md +108 -0
  14. package/templates/qoder/commands/wl-design-spec.md +154 -0
  15. package/templates/qoder/commands/wl-design.md +32 -0
  16. package/templates/qoder/commands/wl-init.md +24 -3
  17. package/templates/qoder/commands/wl-prd-full.md +226 -0
  18. package/templates/qoder/commands/wl-prd-quick.md +134 -0
  19. package/templates/qoder/commands/wl-prd-review.md +104 -0
  20. package/templates/qoder/commands/wl-prd.md +17 -311
  21. package/templates/qoder/commands/wl-search.md +66 -30
  22. package/templates/qoder/commands/wl-task.md +290 -59
  23. package/templates/qoder/commands/wl-test.md +92 -24
  24. package/templates/qoder/config.yaml +51 -15
  25. package/templates/qoder/hooks/inject-workflow-state.py +2 -2
  26. package/templates/qoder/hooks/session-start.py +82 -51
  27. package/templates/qoder/rules/wl-pipeline.md +216 -105
  28. package/templates/qoder/scripts/__pycache__/search_index.cpython-39.pyc +0 -0
  29. package/templates/qoder/scripts/archive_prd.py +377 -0
  30. package/templates/qoder/scripts/autotest.py +1715 -0
  31. package/templates/qoder/scripts/autotest_batch.py +224 -0
  32. package/templates/qoder/scripts/autotest_run.py +297 -0
  33. package/templates/qoder/scripts/benchmark.py +210 -209
  34. package/templates/qoder/scripts/build_style_index.py +444 -4
  35. package/templates/qoder/scripts/check_carriers.py +238 -0
  36. package/templates/qoder/scripts/check_mcp.py +298 -0
  37. package/templates/qoder/scripts/check_qoderwork_consistency.py +166 -0
  38. package/templates/qoder/scripts/common/events.py +46 -0
  39. package/templates/qoder/scripts/common/extract.py +419 -0
  40. package/templates/qoder/scripts/common/graph_traverse.py +533 -0
  41. package/templates/qoder/scripts/common/paths.py +89 -0
  42. package/templates/qoder/scripts/common/pip_install.py +144 -0
  43. package/templates/qoder/scripts/common/platform_guard.py +61 -0
  44. package/templates/qoder/scripts/common/search_engine.py +205 -205
  45. package/templates/qoder/scripts/common/terms.py +57 -0
  46. package/templates/qoder/scripts/common/ts_extract.py +536 -0
  47. package/templates/qoder/scripts/context_pack.py +73 -13
  48. package/templates/qoder/scripts/enrich_prompt.py +226 -0
  49. package/templates/qoder/scripts/eval_prd.py +318 -231
  50. package/templates/qoder/scripts/export.py +487 -487
  51. package/templates/qoder/scripts/extract_api_params.py +246 -0
  52. package/templates/qoder/scripts/extract_routes.py +54 -0
  53. package/templates/qoder/scripts/extract_routes_tree.py +78 -0
  54. package/templates/qoder/scripts/fill_prototype.py +707 -0
  55. package/templates/qoder/scripts/gen_design_doc.py +394 -0
  56. package/templates/qoder/scripts/git_sync.py +27 -15
  57. package/templates/qoder/scripts/init_doctor.py +292 -40
  58. package/templates/qoder/scripts/install_qoderwork.py +366 -9
  59. package/templates/qoder/scripts/kg.py +708 -0
  60. package/templates/qoder/scripts/kg_auto_login.py +196 -0
  61. package/templates/qoder/scripts/kg_build.py +612 -0
  62. package/templates/qoder/scripts/kg_build_db.py +327 -0
  63. package/templates/qoder/scripts/kg_duckdb.py +549 -0
  64. package/templates/qoder/scripts/kg_incremental.py +393 -0
  65. package/templates/qoder/scripts/kg_link_db.py +224 -0
  66. package/templates/qoder/scripts/kg_mcp_server.py +801 -0
  67. package/templates/qoder/scripts/kg_semantic.py +150 -0
  68. package/templates/qoder/scripts/kg_test_runner.py +241 -0
  69. package/templates/qoder/scripts/lanhu_stdio_wrapper.py +119 -0
  70. package/templates/qoder/scripts/learn.py +118 -39
  71. package/templates/qoder/scripts/learn_aggregate.py +201 -0
  72. package/templates/qoder/scripts/mcp_launcher.py +359 -0
  73. package/templates/qoder/scripts/mysql_mcp_server.py +396 -0
  74. package/templates/qoder/scripts/repo_root.py +106 -0
  75. package/templates/qoder/scripts/role.py +12 -0
  76. package/templates/qoder/scripts/run_weekly_update.bat +5 -0
  77. package/templates/qoder/scripts/run_weekly_update.sh +5 -0
  78. package/templates/qoder/scripts/search_index.py +307 -60
  79. package/templates/qoder/scripts/secure-ls.js +5640 -0
  80. package/templates/qoder/scripts/setup.py +706 -641
  81. package/templates/qoder/scripts/setup_lanhu.py +963 -0
  82. package/templates/qoder/scripts/status.py +250 -11
  83. package/templates/qoder/scripts/sync_carriers.py +259 -0
  84. package/templates/qoder/scripts/syncgate.py +2 -2
  85. package/templates/qoder/scripts/task.py +75 -0
  86. package/templates/qoder/scripts/team_sync.py +60 -4
  87. package/templates/qoder/scripts/workspace_init.py +1 -1
  88. package/templates/qoder/skills/design-import/SKILL.md +226 -0
  89. package/templates/qoder/skills/design-import/figma-workflow.md +81 -0
  90. package/templates/qoder/skills/design-review/SKILL.md +82 -25
  91. package/templates/qoder/skills/prd-generator/SKILL.md +184 -60
  92. package/templates/qoder/skills/prd-review/SKILL.md +18 -1
  93. package/templates/qoder/skills/prompt-enrich/SKILL.md +90 -0
  94. package/templates/qoder/skills/prototype-generator/SKILL.md +256 -141
  95. package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
  96. package/templates/qoder/skills/spec-coder/SKILL.md +18 -1
  97. package/templates/qoder/skills/spec-generator/SKILL.md +18 -1
  98. package/templates/qoder/skills/test-generator/SKILL.md +15 -2
  99. package/templates/qoder/skills/wl-code/SKILL.md +55 -36
  100. package/templates/qoder/skills/wl-commit/SKILL.md +89 -76
  101. package/templates/qoder/skills/wl-design/SKILL.md +55 -0
  102. package/templates/qoder/skills/wl-init/SKILL.md +76 -67
  103. package/templates/qoder/skills/wl-insight/SKILL.md +201 -81
  104. package/templates/qoder/skills/wl-prd-full/SKILL.md +69 -0
  105. package/templates/qoder/skills/wl-prd-quick/SKILL.md +49 -0
  106. package/templates/qoder/skills/wl-prd-review/SKILL.md +34 -0
  107. package/templates/qoder/skills/wl-report/SKILL.md +131 -107
  108. package/templates/qoder/skills/wl-search/SKILL.md +141 -75
  109. package/templates/qoder/skills/wl-spec/SKILL.md +49 -39
  110. package/templates/qoder/skills/wl-status/SKILL.md +83 -61
  111. package/templates/qoder/skills/wl-task/SKILL.md +132 -58
  112. package/templates/qoder/skills/wl-test/SKILL.md +406 -40
  113. package/templates/qoder/templates/prototype-app.html +13 -8
  114. package/templates/qoder/templates/prototype-web.html +376 -93
  115. package/templates/root/AGENTS.md +89 -34
  116. package/templates/root/requirements.txt +21 -0
  117. package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +259 -259
  118. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +186 -186
  119. package/templates/qoder/agents/prd-planning.md +0 -57
  120. package/templates/qoder/agents/prd-research.md +0 -34
  121. package/templates/qoder/commands/wl-insight.md +0 -51
  122. package/templates/qoder/scripts/__pycache__/init_doctor.cpython-39.pyc +0 -0
  123. package/templates/qoder/scripts/__pycache__/setup.cpython-39.pyc +0 -0
  124. package/templates/qoder/scripts/common/__pycache__/developer.cpython-39.pyc +0 -0
  125. package/templates/qoder/skills/wl-prd/SKILL.md +0 -92
package/bin/cli.js CHANGED
@@ -24,14 +24,89 @@ const LOCAL_STATE_FILES = new Set([
24
24
  ".developer", ".current-task", ".engine-version",
25
25
  ]);
26
26
 
27
+ // 探测可用的 Python 命令 (python 或 python3)。
28
+ // macOS 系统通常只有 python3, 硬编码 python 会让 Mac 全员失败。
29
+ // 探测结果缓存到全局, 避免每次调用都 fork 进程。
30
+ let _PY_CMD_CACHE = null;
31
+ function detectPyCmd() {
32
+ if (_PY_CMD_CACHE) return _PY_CMD_CACHE;
33
+ for (const c of ["python", "python3"]) {
34
+ try {
35
+ execSync(`${c} --version`, { encoding: "utf-8", timeout: 5000, stdio: "pipe" });
36
+ _PY_CMD_CACHE = c;
37
+ return c;
38
+ } catch { /* try next */ }
39
+ }
40
+ _PY_CMD_CACHE = null;
41
+ return null;
42
+ }
43
+
27
44
  function py(script, args = []) {
28
45
  const p = path.join(process.cwd(), ".qoder", "scripts", script);
29
46
  if (!fs.existsSync(p)) { console.log("请先运行: npx wlkj init"); process.exit(1); }
47
+ const pyCmd = detectPyCmd();
48
+ if (!pyCmd) { console.log("Python 未安装, 无法运行脚本。跑: npx @hupan56/wlkj install-env"); return ""; }
30
49
  try {
31
- return execSync(`python "${p}" ${args.map(a => `"${a}"`).join(" ")}`, { cwd: process.cwd(), encoding: "utf-8", timeout: 15000 });
50
+ return execSync(`${pyCmd} "${p}" ${args.map(a => `"${a}"`).join(" ")}`, { cwd: process.cwd(), encoding: "utf-8", timeout: 15000 });
32
51
  } catch (e) { return (e.stdout || e.message); }
33
52
  }
34
53
 
54
+ // 带镜像兜底的 pip 安装 —— 复用 Python 的 common/pip_install.py (单一事实源)
55
+ // 为什么不复用 Python: 上一轮给 init_doctor 做了 common/pip_install.py (失败切清华/阿里/腾讯)。
56
+ // cli.js 的 install-env/update 是独立 JS 路径, 之前没享受镜像兜底 → 50 人里弱网用户全崩。
57
+ // 这里调用同一个 Python 工具, 保证两路径行为一致。
58
+ //
59
+ // 优先: 引擎已装 → 调 common.pip_install (有镜像兜底)
60
+ // 回退: 引擎没装 → 裸 pip install (install-env 首次场景, 无 Python 工具可用)
61
+ //
62
+ // 实现注意 (修 2.7.0 的真 bug): 不能用字符串拼接构造 Python -c 代码 ——
63
+ // Windows 路径含反斜杠, 拼进 Python 字符串后 \Y \n 等成了非法转义 + 引号丢失,
64
+ // 导致 SyntaxError → 镜像兜底静默失败 → 回退裸 pip → 弱网用户全崩。
65
+ // 改用环境变量传参 (WLKJ_REQ_PATH), Python 侧 os.environ 读, 彻底避开转义地狱。
66
+ function installPipDeps(pyCmd, reqPath) {
67
+ const scriptsDir = path.join(process.cwd(), ".qoder", "scripts");
68
+ const hasTool = fs.existsSync(path.join(scriptsDir, "common", "pip_install.py"));
69
+ if (hasTool) {
70
+ // 用环境变量传 requirements 路径, Python 侧 os.environ 读 (跨平台无转义问题)
71
+ const prog = [
72
+ "import sys, os, json",
73
+ "sys.path.insert(0, os.environ['WLKJ_SCRIPTS_DIR'])",
74
+ "from common.pip_install import install_with_fallback",
75
+ "r = install_with_fallback(['-r', os.environ['WLKJ_REQ_PATH']])",
76
+ "print('WLKJ_RESULT=' + json.dumps(r, ensure_ascii=False))",
77
+ ].join("; ");
78
+ try {
79
+ const out = execSync(`${pyCmd} -c "${prog}"`, {
80
+ encoding: "utf-8", timeout: 600000,
81
+ env: { ...process.env, WLKJ_SCRIPTS_DIR: scriptsDir, WLKJ_REQ_PATH: reqPath },
82
+ });
83
+ // 找 WLKJ_RESULT= 那行 (避免被 pip 输出干扰)
84
+ const resultLine = out.split("\n").find(l => l.startsWith("WLKJ_RESULT="));
85
+ if (!resultLine) throw new Error("Python 工具无结果输出");
86
+ const result = JSON.parse(resultLine.slice("WLKJ_RESULT=".length));
87
+ if (result.ok) {
88
+ console.log(` [OK] Python 依赖已装` + (result.source && result.source !== "default" ? ` (镜像源: ${result.source})` : ""));
89
+ } else {
90
+ console.log(` [WARN] pip 安装失败 (尝试了所有镜像)`);
91
+ console.log(` ${result.hint || ""}`.replace(/\n/g, "\n "));
92
+ }
93
+ return result.ok;
94
+ } catch (e) {
95
+ console.log(` [WARN] 调用镜像工具失败, 回退裸 pip: ${(e.message || "").slice(0, 80)}`);
96
+ }
97
+ }
98
+ // 回退: 裸 pip install (引擎未装, 或工具调用失败)
99
+ try {
100
+ execSync(`${pyCmd} -m pip install -r "${reqPath}"`, { stdio: "inherit", timeout: 300000 });
101
+ console.log(" [OK] Python 依赖已装");
102
+ return true;
103
+ } catch (e) {
104
+ console.log(` [WARN] pip 安装失败: ${(e.message || "").slice(0, 80)}`);
105
+ console.log(" 国内网络建议配镜像: pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple");
106
+ return false;
107
+ }
108
+ }
109
+
35
110
  function cp(src, dest) {
36
111
  const s = path.join(T, src), d = path.join(process.cwd(), dest);
37
112
  if (!fs.existsSync(s)) return;
@@ -58,11 +133,99 @@ function gitOp(op) {
58
133
  catch (e) { console.log(e.stdout || e.message); }
59
134
  }
60
135
 
61
- function doInit(name) {
136
+ // 快速环境检测 (init 前置): 缺 Python 致命退出, 缺 git 警告不阻塞
137
+ // 目的: 杜绝"裸机没 Python 时 init 静默失败仍宣布成功"的假成功
138
+ function checkEnvQuick() {
139
+ console.log("--- 环境检测 ---");
140
+ // Node 一定有 (npx 在跑), 输出 version 确认
141
+ console.log(` Node.js: [OK] ${process.version}`);
142
+
143
+ // Python: python 或 python3 任一可用即可
144
+ let pyCmd = null, pyVer = null;
145
+ for (const c of ["python", "python3"]) {
146
+ try {
147
+ const v = execSync(`${c} --version`, { encoding: "utf-8", timeout: 5000, stdio: "pipe" }).trim();
148
+ pyCmd = c; pyVer = v; break;
149
+ } catch { /* try next */ }
150
+ }
151
+ if (!pyCmd) {
152
+ console.log(" Python: [缺失] (引擎必需)");
153
+ console.log("\n ⚠ 没装 Python, 无法继续。请先装环境:");
154
+ console.log(" npx @hupan56/wlkj install-env");
155
+ console.log(" (自动装 Node/Python/git + pip 依赖; 装完重开终端再 init)\n");
156
+ return { ok: false, pyCmd: null };
157
+ }
158
+ console.log(` Python: [OK] ${pyVer} (${pyCmd})`);
159
+
160
+ // git: 可选, 缺了只警告 (与 init_doctor 口径一致)
161
+ try {
162
+ const gv = execSync("git --version", { encoding: "utf-8", timeout: 5000, stdio: "pipe" }).trim();
163
+ console.log(` git: [OK] ${gv}`);
164
+ } catch {
165
+ console.log(" git: [缺失] (可选, 团队同步/源码克隆将禁用, 本地 PRD/搜索/任务仍可用)");
166
+ console.log(" 想启用: npx @hupan56/wlkj install-env");
167
+ }
168
+ console.log("");
169
+ return { ok: true, pyCmd };
170
+ }
171
+
172
+ // 交互式问角色 (50 人团队角色各异, 不能写死 pm)
173
+ // 支持的角色 (与 config.yaml / kg.py ROLE_SECTIONS 对齐)
174
+ const ROLES = [
175
+ { key: "pm", label: "产品经理 (PM) — 写需求/PRD/任务" },
176
+ { key: "design", label: "设计师 (UI) — 录入设计稿/出原型" },
177
+ { key: "dev", label: "开发 (前端/后端) — 写代码/提交/测试" },
178
+ { key: "test", label: "测试 (QA) — 跑测试用例/验证" },
179
+ { key: "admin", label: "管理员 — 管理/维护工作流" },
180
+ ];
181
+
182
+ function askRole() {
183
+ // 非交互环境 (CI/AI 调用) 默认 pm
184
+ if (!process.stdin.isTTY) {
185
+ console.log(" (非交互环境, 默认角色: pm)");
186
+ return "pm";
187
+ }
188
+ console.log("\n 你的角色是? (决定命令视角和建议)");
189
+ ROLES.forEach((r, i) => console.log(` ${i + 1}. ${r.label}`));
190
+ const readline = require("readline").createInterface({ input: process.stdin, output: process.stdout });
191
+ return new Promise((resolve) => {
192
+ readline.question("\n 选择 (1-5, 默认1): ", (ans) => {
193
+ readline.close();
194
+ const idx = parseInt(ans, 10) - 1;
195
+ const role = (idx >= 0 && idx < ROLES.length) ? ROLES[idx].key : "pm";
196
+ console.log(` 角色: ${role}\n`);
197
+ resolve(role);
198
+ });
199
+ });
200
+ }
201
+
202
+ // 校验角色参数合法性 (大小写无关: PM/pm/Dev/dev 都认)
203
+ function normalizeRole(r) {
204
+ if (!r) return null;
205
+ const low = String(r).toLowerCase();
206
+ return ROLES.some(x => x.key === low) ? low : null;
207
+ }
208
+
209
+ async function doInit(name, roleArg) {
62
210
  const cwd = process.cwd();
63
211
  const hasExisting = fs.existsSync(path.join(cwd, ".qoder", "scripts", "setup.py"));
64
212
  console.log(`\nwlkj init -> ${cwd}${hasExisting ? " (已存在, 增量更新)" : ""}\n`);
65
213
 
214
+ // === 0. 环境前置检测 (避免裸机无 Python 时假成功) ===
215
+ const env = checkEnvQuick();
216
+ if (!env.ok) process.exit(1);
217
+ const { pyCmd } = env;
218
+
219
+ // === 0.5. 确定角色 (参数 > 交互式, 不再写死 pm)
220
+ // 50 人团队角色各异: 产品/UI/前后端/测试/管理, 每个角色的命令视角不同。
221
+ // 写死 pm = 设计师和开发拿不到匹配的上下文 (见 wl-prd-full 的 --role 适配)。
222
+ let role = normalizeRole(roleArg);
223
+ if (!role) {
224
+ role = await askRole();
225
+ } else {
226
+ console.log(` 角色: ${role} (来自参数)\n`);
227
+ }
228
+
66
229
  // === 1. 拷贝完整引擎 (镜像 .qoder/ 结构) ===
67
230
  // 源: templates/qoder/* 目标: .qoder/*
68
231
  // 注意: init 用 "update" 模式的保护逻辑更安全 —— 新装时目标文件不存在,
@@ -124,7 +287,7 @@ function doInit(name) {
124
287
  console.log(`\n--- 自动初始化 ---`);
125
288
  const setupPath = path.join(cwd, ".qoder", "scripts", "setup.py");
126
289
  if (fs.existsSync(setupPath)) {
127
- const setupArgs = [name, "pm", "--skip-cron", "--skip-qoderwork"].filter(Boolean);
290
+ const setupArgs = [name, role, "--skip-cron", "--skip-qoderwork"].filter(Boolean);
128
291
  try {
129
292
  const cmd = `python "${setupPath}" ${setupArgs.map(a => `"${a}"`).join(" ")}`;
130
293
  console.log(` 运行: setup.py ${setupArgs.join(" ")}`);
@@ -133,8 +296,38 @@ function doInit(name) {
133
296
  console.log(` setup 部分失败 (不阻塞): ${(e.message || "").slice(0, 100)}`);
134
297
  console.log(` 可重新运行: npx @hupan56/wlkj init ${name || "<你的名字>"}`);
135
298
  }
136
- } else {
137
- console.log(` setup.py 未找到, 跳过自动初始化`);
299
+ } else {
300
+ console.log(` setup.py 未找到, 跳过自动初始化`);
301
+ }
302
+
303
+ // === 6.5. 自动配 QoderWork MCP (新机关键: 否则 mcp.json 不生成, 4 个 MCP 全连不上) ===
304
+ // 复刻 doUpdate 第 7 步的逻辑: 拷 launcher + install_qoderwork --mcp-only
305
+ // setup.py 带了 --skip-qoderwork (offer_qoderwork 是交互式+装skill, 不适合 npx),
306
+ // 这里用 --mcp-only (纯文件操作, 无交互) 把 mcp.json 配好
307
+ if (pyCmd) {
308
+ console.log(`\n--- QoderWork MCP 自动配置 ---`);
309
+ const home = process.env.USERPROFILE || require("os").homedir();
310
+ const launcherSrc = path.join(cwd, ".qoder", "scripts", "mcp_launcher.py");
311
+ const launcherDst = path.join(home, ".qoderwork", "mcp_launcher.py");
312
+ try {
313
+ if (fs.existsSync(launcherSrc)) {
314
+ fs.mkdirSync(path.dirname(launcherDst), { recursive: true });
315
+ fs.copyFileSync(launcherSrc, launcherDst);
316
+ console.log(` [OK] launcher 已装: ~/.qoderwork/mcp_launcher.py`);
317
+ }
318
+ const instScript = path.join(cwd, ".qoder", "scripts", "install_qoderwork.py");
319
+ if (fs.existsSync(instScript)) {
320
+ try {
321
+ execSync(`${pyCmd} "${instScript}" --mcp-only`, { stdio: "inherit", timeout: 20000 });
322
+ console.log(` [OK] mcp.json 已生成 (kg/mysql/lanhu/playwright, launcher 模式)`);
323
+ } catch (e) {
324
+ console.log(` [WARN] MCP 配置未完全成功 (不阻塞): ${(e.message || "").slice(0, 80)}`);
325
+ console.log(` 可手动补: ${pyCmd} .qoder/scripts/install_qoderwork.py`);
326
+ }
327
+ }
328
+ } catch (e) {
329
+ console.log(` [WARN] MCP 配置跳过: ${(e.message || "").slice(0, 80)}`);
330
+ }
138
331
  }
139
332
 
140
333
  // === 7. 写版本戳 ===
@@ -146,6 +339,9 @@ function doInit(name) {
146
339
  console.log(`\n 现在可以开始了:`);
147
340
  console.log(` 在 Qoder 里说 "写个 XX 的需求"`);
148
341
  console.log(` 或输 / 看命令列表 (/wl-prd /wl-search /wl-task)`);
342
+ console.log(`\n ✓ QoderWork MCP 已自动配好 (kg/mysql/lanhu/playwright)`);
343
+ console.log(` 用 QoderWork 桌面端想装技能(软链到 ~/.qoderwork/skills/)?`);
344
+ console.log(` python .qoder/scripts/install_qoderwork.py`);
149
345
  console.log(`\n 看不到命令? 新建对话 / 重启 QoderWork`);
150
346
  console.log(` 环境问题? python .qoder/scripts/init_doctor.py --fix\n`);
151
347
  }
@@ -248,12 +444,23 @@ function doStatus() {
248
444
  console.log("");
249
445
  const dev = path.join(process.cwd(), ".qoder", ".developer");
250
446
  if (fs.existsSync(dev)) {
251
- const line = fs.readFileSync(dev, "utf-8").split("\n")[0];
252
- console.log("dev: " + line.split("=")[1]?.trim());
253
- } else { console.log("dev: - (run: npx wlkj init <name>)"); }
254
-
447
+ // 解析 .developer 的 name= role= 行
448
+ const lines = fs.readFileSync(dev, "utf-8").split("\n");
449
+ const nameLine = lines.find(l => l.startsWith("name="));
450
+ const roleLine = lines.find(l => l.startsWith("role="));
451
+ const name = nameLine ? nameLine.split("=")[1]?.trim() : "?";
452
+ const role = roleLine ? roleLine.split("=")[1]?.trim() : "pm";
453
+ console.log(`dev: ${name} (角色: ${role})`);
454
+ if (!roleLine) {
455
+ console.log(` ⚠ 角色未设置 (默认 pm)。重跑: npx @hupan56/wlkj init ${name} <pm|design|dev|test|admin>`);
456
+ }
457
+ } else {
458
+ console.log("dev: - (未初始化。跑: npx @hupan56/wlkj init <名字> <角色>)");
459
+ console.log(" 角色: 产品(pm)/设计(design)/开发(dev)/测试(test)/管理(admin)");
460
+ }
461
+
255
462
  process.stdout.write(py("task.py", ["current", "--source"]));
256
-
463
+
257
464
  const wsSpecs = path.join(process.cwd(), "workspace", "specs", "prd");
258
465
  if (fs.existsSync(wsSpecs)) {
259
466
  const n = fs.readdirSync(wsSpecs).filter(f => f.endsWith(".md")).length;
@@ -380,6 +587,38 @@ function doUpdate() {
380
587
  console.log(` [清理] 废弃文件 ${cleaned} 个 + 过期缓存 ${cacheCleaned} 个`);
381
588
  }
382
589
 
590
+ // === 6. 补装 pip 依赖 (老用户升级时补齐, 走镜像兜底) ===
591
+ console.log(`\n--- Python 依赖补装 (失败自动切镜像) ---`);
592
+ const reqFile = path.join(cwd, "requirements.txt");
593
+ const pyCmd = detectPyCmd();
594
+ if (pyCmd && fs.existsSync(reqFile)) {
595
+ installPipDeps(pyCmd, reqFile);
596
+ } else if (!pyCmd) {
597
+ console.log(` [跳过] Python 未装`);
598
+ } else {
599
+ console.log(` [跳过] requirements.txt 不存在`);
600
+ }
601
+
602
+ // === 7. 刷新 launcher + mcp.json (老用户的可能是旧版/写死路径) ===
603
+ try {
604
+ const home = process.env.USERPROFILE || require("os").homedir();
605
+ const launcherSrc = path.join(cwd, ".qoder", "scripts", "mcp_launcher.py");
606
+ const launcherDst = path.join(home, ".qoderwork", "mcp_launcher.py");
607
+ if (fs.existsSync(launcherSrc)) {
608
+ fs.mkdirSync(path.dirname(launcherDst), { recursive: true });
609
+ fs.copyFileSync(launcherSrc, launcherDst);
610
+ console.log(` [OK] launcher 已刷新`);
611
+ }
612
+ // 刷新 mcp.json 为 launcher 模式 (install_qoderwork.py 的 rewrite 逻辑)
613
+ const instScript = path.join(cwd, ".qoder", "scripts", "install_qoderwork.py");
614
+ if (pyCmd && fs.existsSync(instScript)) {
615
+ try {
616
+ execSync(`${pyCmd} "${instScript}" --mcp-only`, { stdio: "pipe", timeout: 15000 });
617
+ console.log(` [OK] mcp.json 已刷新为动态路径`);
618
+ } catch { /* --mcp-only 可能不支持, 静默跳过 */ }
619
+ }
620
+ } catch (e) { /* 刷新失败不阻塞升级 */ }
621
+
383
622
  console.log(`\n${"=".repeat(50)}`);
384
623
  console.log(` 升级完成! (v${PKG_VERSION})`);
385
624
  console.log(`${"=".repeat(50)}`);
@@ -562,6 +801,25 @@ function doInstallEnv(checkOnly = false, location = null) {
562
801
  console.log(` 或用命令: npx @hupan56/wlkj add-path "D:\\wldev\\nodejs"`);
563
802
  }
564
803
 
804
+ // Python pip 依赖 (知识图谱/MCP/测试需要)
805
+ console.log("\n--- Python 依赖 ---");
806
+ const pyCmd = check("python") ? "python" : (check("python3") ? "python3" : null);
807
+ if (pyCmd) {
808
+ // 找 requirements.txt: 引擎已安装则用 .qoder/scripts/../requirements.txt
809
+ const reqPaths = [
810
+ path.join(process.cwd(), "requirements.txt"),
811
+ path.join(process.cwd(), ".qoder", "..", "requirements.txt"),
812
+ ].filter(p => fs.existsSync(p));
813
+ if (reqPaths.length > 0) {
814
+ console.log(` 安装 pip 依赖: ${path.basename(reqPaths[0])} (失败自动切国内镜像)`);
815
+ installPipDeps(pyCmd, reqPaths[0]);
816
+ } else {
817
+ console.log(" [跳过] 未找到 requirements.txt (先 npx @hupan56/wlkj init 安装引擎)");
818
+ }
819
+ } else {
820
+ console.log(" [跳过] Python 未装, 无法装 pip 依赖");
821
+ }
822
+
565
823
  console.log("\n=== 结果 ===");
566
824
  if (need.length === 0) {
567
825
  console.log(" 环境就绪! 下一步: npx @hupan56/wlkj init");
@@ -598,7 +856,7 @@ const [,, cmd, ...rest] = process.argv;
598
856
  const mapped = rest.map(a => a === "-p" ? "--priority" : a);
599
857
 
600
858
  switch (cmd) {
601
- case "init": doInit(rest[0]); break;
859
+ case "init": doInit(rest[0], rest[1]); break;
602
860
  case "update": case "upgrade": doUpdate(); break;
603
861
  case "install-env": {
604
862
  const checkOnly = rest.includes("--check");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hupan56/wlkj",
3
- "version": "2.6.0",
3
+ "version": "2.7.1",
4
4
  "description": "AI Product R&D Workflow - PRD/Prototype/Search/Task/Report",
5
5
  "bin": {
6
6
  "wlkj": "bin/cli.js"
@@ -0,0 +1,67 @@
1
+ # Insight Planning Agent(未来规划 · Gap 分析 + Roadmap)
2
+
3
+ > 本 agent 服务于 `/wl-insight plan` 类型。完整规则见 `.qoder/commands/optional/wl-insight.md`。
4
+ > 来源:从 prd-planning.md 抽拆迁而来——Phase 1(现状分析)已归并给 prd-reference,
5
+ > 本 agent 只保留 Phase 2-4(理想态调研 + Gap + MVP + roadmap)。
6
+
7
+ ## Required: Load Context First (Pull-Based)
8
+ Read the dispatch prompt — it MUST contain: topic, current developer name,
9
+ and either (a) a reference to a recent insight research report, or
10
+ (b) a user-described ideal state. If neither, STOP and report back:
11
+ "planning needs an ideal state — either a research report or user description".
12
+ For current state, run: `python .qoder/scripts/context_pack.py <关键词> --platform <web|app>`
13
+
14
+ ## Recursion Guard
15
+ You are the insight-planning sub-agent. Do NOT spawn another insight-planning agent.
16
+
17
+ ## 🔒 语言锁
18
+ **全部输出必须简体中文。**(技术专有名词 OST/Gap/MVP 可保留英文缩写)
19
+
20
+ ## Your Role
21
+ 做 Gap 分析 + 未来规划,产出方案候选(OST 的 Solution 层)。
22
+ 现状分析**不在本 agent 职责内**——现状由 context_pack 提供,或由 prd-reference 承接。
23
+
24
+ ## Phase 1: 取现状(输入,不是本 agent 的活)
25
+ - 从 dispatch prompt 或 context_pack 结果读现状摘要
26
+ - 若现状信息不足 → 回报 caller"现状锚定不全,建议先跑 context_pack 或先做 discover"
27
+
28
+ ## Phase 2: 理想态(如果有 research 报告)
29
+ 1. 读最近一份 `workspace/members/{dev}/drafts/insight-research-*.md`
30
+ 2. 提取调研结论作为理想态基准
31
+ 3. 若无 research 报告 → 用 dispatch prompt 里用户描述的理想态
32
+
33
+ ## Phase 3: Gap 分析(核心)
34
+ 对比现状 vs 理想态:
35
+
36
+ | 维度 | 现状 | 理想态 | Gap | 业务价值(1-4) | 可行性(1-4) | 分 | 证据来源 |
37
+ |------|------|--------|-----|--------------|-------------|-----|---------|
38
+
39
+ - 业务价值:1=锦上添花 / 2=提效 / 3=解锁核心场景 / 4=不做会丢市场
40
+ - 可行性:看现状改动量、有没有先例可复用
41
+ - 分 = 价值 × 可行性,按分排序
42
+
43
+ ## Phase 4: Plans(OST 第 3 层 Solution)
44
+ ### MVP(本期)
45
+ - 取高价值高可行性的 Gap
46
+ - **当前人力能做完的才放进 MVP**(不贪多)
47
+ - 每条标理由(为什么是它进 MVP)
48
+
49
+ ### Roadmap(后续版本)
50
+ - 按版本分组(V{N+1} / V{N+2}...)
51
+ - 标依赖关系(A 依赖 B 先做)
52
+ - 长期/实验性的标"待验证"
53
+
54
+ ## Phase 5: 与用户确认(最重要)
55
+ 把 Gap 表 + MVP + roadmap **一次性编号列清**,问:
56
+ 1. Gap 分析准确吗?
57
+ 2. MVP 范围合理吗?
58
+ 3. Roadmap 优先级对吗?
59
+ 调整基于反馈——最多 2 轮,然后用用户最新答案定稿(不无限循环)。
60
+
61
+ ## Output
62
+ 1. 存规划报告:`workspace/members/{dev}/drafts/insight-plan-{topic}-{YYYY-MM-DD}.md`
63
+ 2. 报告末尾给出口提示:"要落地哪个?说'转 PRD'我把它作为现状输入,进 /wl-prd-full 参考模式落地。"
64
+
65
+ > ⚠️ **本 agent 不生成 PRD,不分配 REQ-ID,不画原型。** 那是 /wl-prd-full 的活。
66
+ > mode 标记的 planning/brainstorm 已不在 PRD 侧使用——insight 报告**不带 mode 标记**,
67
+ > 它不是 PRD;转 PRD 时由 prd 以 `<!-- mode: reference -->` 承接。
@@ -0,0 +1,61 @@
1
+ # Insight Research Agent(外部调研 · 多视角脑暴)
2
+
3
+ > 本 agent 服务于 `/wl-insight research` 类型。完整规则见 `.qoder/commands/optional/wl-insight.md`。
4
+ > 来源:整体从 prd-research.md 搬迁而来(脑暴 + web 搜索能力归 insight)。
5
+
6
+ ## Required: Load Context First (Pull-Based)
7
+ This platform does NOT auto-inject context. Before doing anything else:
8
+ 1. Read the dispatch prompt to get: assigned perspective, topic,
9
+ current state summary (from context_pack), developer name
10
+ 2. If perspective is missing from dispatch prompt, STOP and report back:
11
+ "perspective not specified - caller must assign one of: 竞品对标/行业趋势/技术方案/用户场景"
12
+
13
+ ## Recursion Guard
14
+ You are the insight-research sub-agent.
15
+ Do NOT spawn another insight-research agent.
16
+
17
+ ## 🔒 语言锁
18
+ **全部输出必须简体中文。**(技术专有名词可保留英文)
19
+
20
+ ## Your Role
21
+ 你是 N 个调研 agent 中的一个,负责**一个特定视角**的外部调研。
22
+ 你的视角在 dispatch prompt 里指定。你的产出是**中间产物**——
23
+ caller(/wl-insight research)会把所有 agent 的报告合并成一份调研报告,
24
+ 然后删除中间产物。
25
+
26
+ ## Workflow
27
+ 1. 从 dispatch prompt 读你的视角:竞品对标 / 行业趋势 / 技术方案 / 用户场景 之一
28
+ 2. 读 dispatch prompt 里的**本项目现状摘要**(让调研有针对性,不泛泛而谈)
29
+ 3. 用 WebSearch 搜 3-5 个权威源(官方文档 / 行业报告 / 技术博客 / 公开案例)
30
+ 4. 从你的视角分析,重点找:**外部做得好而我们没做的**(Gap 信号)
31
+ 5. 写到 `workspace/members/{dev}/drafts/research-agent-{N}-{视角}.md`
32
+
33
+ > ⚠️ **不要凭训练记忆编数据**。搜不到就标"【待验证】",不要编竞品功能/数据。
34
+ > ⚠️ 每个发现必须附**来源链接**。
35
+
36
+ ## Output Format
37
+ ```markdown
38
+ # 调研报告(中间产物):{视角}
39
+
40
+ ## 视角
41
+ {你负责的视角}
42
+
43
+ ## 本项目现状(来自 dispatch)
44
+ {一句话摘要,让读者知道对标基准}
45
+
46
+ ## 关键发现
47
+ 1. {发现} —— 来源:{链接}
48
+ - 对本项目的启发:{...}
49
+ - Gap 信号:{外部做得好而我们没做的}
50
+ 2. ...
51
+
52
+ ## 风险
53
+ - {采纳这个做法的风险/前提条件}
54
+
55
+ ## 机会建议
56
+ - {值得跟进的机会点}
57
+ ```
58
+
59
+ > ⚠️ **本 agent 不生成 PRD。** 你是 research 类型的子 agent,产出中间产物。
60
+ > 合并后的调研报告也不带 mode 标记(它不是 PRD)。
61
+ > caller 合并后会删除所有 `research-agent-*.md` 中间产物。
@@ -5,7 +5,7 @@ This platform does NOT auto-inject task context. Before anything else:
5
5
  1. Read the dispatch prompt — it MUST contain: platform (Web/APP/Both),
6
6
  user requirement, current developer name
7
7
  2. If platform is missing from the dispatch prompt, STOP and report back:
8
- "platform not specified - caller must ask user first (see /wl-prd Step 0)"
8
+ "platform not specified - caller must ask user first (see /wl-prd-full Step 0)"
9
9
  3. If task context needed: python .qoder/scripts/task.py current --source
10
10
 
11
11
  ## Recursion Guard
@@ -14,16 +14,22 @@ Do NOT spawn another prd-reference agent.
14
14
 
15
15
  ## Your Role
16
16
  Generate PRD + prototype by referencing existing project data.
17
+ **同时承担"现状分析"职责**(原 prd-planning 的 Phase 1 已并入此处):
18
+ 不仅定位文件,还要总结"现状怎么做的、有什么已知限制"——
19
+ 这是 PRD 基于现状做轻反思的输入,也是 insight 探索的现状锚点来源。
17
20
 
18
21
  ## Workflow
19
22
  1. Search the index (platform-aware):
20
23
  python .qoder/scripts/search_index.py <keyword> --platform <web|app>
21
24
  python .qoder/scripts/search_index.py --style <type> --platform <web|app>
22
25
  python .qoder/scripts/search_index.py --prd <keyword>
26
+ (首选 context_pack.py 一次取全,search_index 仅作单项下钻兜底)
23
27
  2. Read related historical PRDs from data/docs/prd/
24
28
  3. Read data/docs/drafts/ for business drafts
25
29
  4. Read top 2-3 similar Vue files from the TARGET project to extract style
26
- 5. Present ALL findings as ONE numbered list and ask for confirmation
30
+ 5. **现状分析(新增职责)**:总结现状怎么实现、涉及哪些页面/字段/接口、
31
+ 有什么已知限制/技术债——这段是 PRD"产品现状"章节和轻反思的依据
32
+ 6. Present ALL findings as ONE numbered list and ask for confirmation
27
33
  in a single round (batch, not one-by-one). Follow up only on
28
34
  genuinely ambiguous points.
29
35
  6. After confirmation, generate: