@hupan56/wlkj 3.1.11 → 3.1.13

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
@@ -1219,6 +1219,7 @@ function doHelp() {
1219
1219
  console.log(" (提交/同步 /wl-task 和 /wl-commit 会自动做, 不用单独 npx sync)");
1220
1220
  console.log("");
1221
1221
  console.log("=== 进阶 (管理员/特殊情况才用, 不常用) ===");
1222
+ console.log(" npx @hupan56/wlkj selftest 全套件自检 (跨环境+QoderWork工具协议+smoke+体检+MCP)");
1222
1223
  console.log(" npx @hupan56/wlkj doctor 体检 (或在 Qoder 里 /wl-init 无参数)");
1223
1224
  console.log(" npx @hupan56/wlkj migrate 老团队一次性升级到 Trellis 架构");
1224
1225
  console.log(" npx @hupan56/wlkj run <脚本> 万能跑脚本 (AI 内部用, 用户很少需要)");
@@ -1432,5 +1433,43 @@ switch (cmd) {
1432
1433
  case "提交": case "拉取最新": case "同步":
1433
1434
  case "查看状态": case "提交PRD": case "提交Spec": case "提交任务":
1434
1435
  gitOp(cmd); break;
1436
+
1437
+ // ── 自检: 一条命令跑完所有验证套件 (替代 7 步手动清单的"环境验证"部分) ──
1438
+ // 用法: npx @hupan56/wlkj selftest
1439
+ // 跑: cross-env 模拟 + QoderWork 工具协议 + smoke + doctor + mcp_doctor
1440
+ case "selftest": case "自检": {
1441
+ const { spawnSync: _ss } = require("child_process");
1442
+ const cwd = process.cwd();
1443
+ const pyCmd = detectPyCmd();
1444
+ const env = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1", PYTHONPATH: path.join(cwd, ".qoder", "scripts") };
1445
+ console.log("\n" + "=".repeat(56));
1446
+ console.log(" wlkj selftest — 全套件自检 (验证跨环境 + 第一宿主)");
1447
+ console.log("=".repeat(56));
1448
+ let totalFail = 0;
1449
+ const suites = [
1450
+ ["1. 跨环境模拟验证", ["packages/wlkj/tests/test_cross_env.py"]],
1451
+ ["2. QoderWork 工具协议 (真实 MCP JSON-RPC)", ["packages/wlkj/tests/test_qoderwork_tools.py"]],
1452
+ ["3. 命令底层脚本 smoke", ["-c", "import sys; sys.path.insert(0, r'" + path.join(cwd, ".qoder", "scripts") + "'); exec(open(r'" + path.join(cwd, ".qoder", "scripts", "validation", "test", "smoke_all_commands.py") + "').read())"]],
1453
+ ["4. 环境体检 (doctor)", [path.join(cwd, ".qoder", "scripts", "orchestration", "wlkj.py"), "doctor"]],
1454
+ ["5. MCP 实测 (mcp_doctor)", [path.join(cwd, ".qoder", "scripts", "protocol", "mcp", "mcp_doctor.py")]],
1455
+ ];
1456
+ for (const [label, args] of suites) {
1457
+ console.log("\n--- " + label + " ---");
1458
+ if (!pyCmd) { console.log(" [SKIP] Python 未装"); continue; }
1459
+ // suites 1-2 在 packages/wlkj/tests/ 下 (相对 cwd), 3-5 用绝对路径
1460
+ const isTestFile = args[0] && args[0].endsWith(".py") && !args[0].startsWith(path.sep) && !args[0].includes(":");
1461
+ const fullArgs = isTestFile ? [path.join(cwd, args[0])] : args;
1462
+ try {
1463
+ const r = _ss(pyCmd, fullArgs, { cwd, stdio: "inherit", timeout: 120000, env });
1464
+ if (r.status !== 0) { console.log(" [FAIL] exit " + r.status); totalFail++; }
1465
+ } catch (e) { console.log(" [FAIL] " + (e.message || "").slice(0, 80)); totalFail++; }
1466
+ }
1467
+ console.log("\n" + "=".repeat(56));
1468
+ console.log(totalFail === 0 ? " ✓ 全部自检通过" : " ⚠ " + totalFail + " 个套件失败, 见上方");
1469
+ console.log("=".repeat(56) + "\n");
1470
+ process.exit(totalFail === 0 ? 0 : 1);
1471
+ break;
1472
+ }
1473
+
1435
1474
  default: doHelp();
1436
1475
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hupan56/wlkj",
3
- "version": "3.1.11",
3
+ "version": "3.1.13",
4
4
  "description": "AI Product R&D Workflow - PRD/Prototype/Search/Task/Report",
5
5
  "bin": {
6
6
  "wlkj": "bin/cli.js"
@@ -134,9 +134,9 @@ def get_index_info():
134
134
  pass
135
135
 
136
136
  lines.append('## Knowledge Graph')
137
- lines.append('MCP工具 (QoderWork自动调用): search_code / get_impact / coverage_matrix / get_workflow 等11个')
138
- lines.append(' python .qoder/scripts/io/search_index.py <keyword> [--platform web|app]')
139
- lines.append(' python .qoder/scripts/io/context_pack.py <keyword> # 一次打包全部上下文')
137
+ lines.append('MCP工具 (QoderWork自动调用): search_code / get_impact / coverage_matrix / get_workflow 等17个')
138
+ lines.append(' python .qoder/scripts/orchestration/wlkj.py search <keyword> [--platform web|app]')
139
+ lines.append(' python .qoder/scripts/orchestration/wlkj.py context <keyword> # 一次打包全部上下文')
140
140
 
141
141
  # DuckDB 知识图谱状态检查
142
142
  # Read role from .developer file
@@ -184,14 +184,14 @@ def get_index_info():
184
184
  if code_newer or db_age_hours > 168: # 7天 = 168小时
185
185
  lines.append(' [WARN] 知识图谱过期! 代码有新变更.')
186
186
  lines.append(' → 管理员请运行: python .qoder/scripts/kg/kg_build.py')
187
- lines.append(' → 完成后: python .qoder/scripts/io/team_sync.py push (共享给团队)')
187
+ lines.append(' → 完成后: python .qoder/scripts/orchestration/wlkj.py sync push (共享给团队)')
188
188
  else:
189
189
  lines.append(' ✓ 知识图谱最新')
190
190
  else:
191
191
  # 非管理员: 只拉取
192
192
  if code_newer or db_age_hours > 168:
193
193
  lines.append(' [WARN] 知识图谱可能过期.')
194
- lines.append(' → 运行: python .qoder/scripts/io/team_sync.py pull (获取管理员最新构建)')
194
+ lines.append(' → 运行: python .qoder/scripts/orchestration/wlkj.py sync pull (获取管理员最新构建)')
195
195
  else:
196
196
  lines.append(' ✓ 知识图谱最新')
197
197
 
@@ -214,7 +214,7 @@ def get_index_info():
214
214
  if role in ('admin', 'dev'):
215
215
  lines.append(' → 运行: python .qoder/scripts/kg/kg_build.py')
216
216
  else:
217
- lines.append(' → 运行: python .qoder/scripts/io/team_sync.py pull')
217
+ lines.append(' → 运行: python .qoder/scripts/orchestration/wlkj.py sync pull')
218
218
 
219
219
  return NL.join(lines)
220
220
 
@@ -227,8 +227,8 @@ def get_style_info():
227
227
 
228
228
  lines = []
229
229
  lines.append('## UI Style Index')
230
- lines.append('Style search: python .qoder/scripts/io/search_index.py --style <table|form|modal>')
231
- lines.append('Field search: python .qoder/scripts/io/search_index.py --field <name>')
230
+ lines.append('Style search: python .qoder/scripts/orchestration/wlkj.py style --style <table|form|modal>')
231
+ lines.append('Field search: python .qoder/scripts/orchestration/wlkj.py search --field <name>')
232
232
 
233
233
  for proj, d in meta.get('projects', {}).items():
234
234
  vue = d.get('vue_files', 0)
@@ -335,7 +335,18 @@ def main():
335
335
  dev = get_developer()
336
336
  if dev:
337
337
  name = dev.get('name', 'unknown')
338
- role = dev.get('role', 'unknown')
338
+ # role 真源在 member.json (.developer 常无 role= 行 → 显示 unknown)
339
+ # 回退查 member.json, 再回退 'pm' (绝不显示 unknown, 误导 AI)。
340
+ role = dev.get('role')
341
+ if not role or role == 'unknown':
342
+ try:
343
+ mj = os.path.join(BASE, 'workspace', 'members', name, 'member.json')
344
+ if os.path.isfile(mj):
345
+ m = json.load(open(mj, encoding='utf-8'))
346
+ role = m.get('role') or 'pm'
347
+ except Exception:
348
+ pass
349
+ role = role or 'pm'
339
350
  parts.append('Developer: ' + name + ' (' + role + ')')
340
351
  parts.append('Workspace: workspace/members/' + name + '/')
341
352
  else: