@hupan56/wlkj 2.6.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 (125) hide show
  1. package/bin/cli.js +261 -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
@@ -24,6 +24,8 @@ Usage:
24
24
  7. PRD 模板 (docx 源 + 蒸馏 md, docx 更新会提示重新蒸馏)
25
25
  8. 周五自动构建 (检测 + 给出当前系统的设置命令)
26
26
  9. QoderWork 桌面端技能 (可选, 检查 ~/.qoderwork/skills/ 的 junction)
27
+ 10. 个人测试数据 (可选, 登录类 /wl-test 用)
28
+ 11. 蓝湖 MCP (可选, 设计师直读蓝湖设计稿)
27
29
 
28
30
  Exit: 0 = 全部健康, 1 = 有问题未修复 (--fix 可修的会自动修)
29
31
  """
@@ -111,6 +113,81 @@ def check_basics():
111
113
  say(WARN, 'git 未安装 — 团队同步/源码克隆将禁用, 本地 PRD/搜索/任务/报告仍可用')
112
114
 
113
115
 
116
+ # ============================================================
117
+ # 1.5 Python 依赖 (pip 包) — 新增: 不装则图谱/MCP/测试全崩
118
+ # ============================================================
119
+
120
+ # (import名, pip包名, 用途, 是否核心)
121
+ PIP_DEPS = [
122
+ ('duckdb', 'duckdb', '知识图谱存储', True),
123
+ ('pymysql', 'pymysql', 'MySQL 只读 MCP', True),
124
+ ('yaml', 'PyYAML', 'config/learn 配置解析', True),
125
+ ('requests', 'requests', 'API 调用/通知', True),
126
+ ('tree_sitter', 'tree-sitter', '代码图谱构建(管理员)', False),
127
+ ('tree_sitter_typescript', 'tree-sitter-typescript', 'Vue/TS 解析(管理员)', False),
128
+ ('playwright', 'playwright', '浏览器测试(QA)', False),
129
+ ('ddddocr', 'ddddocr', '验证码识别(测试)', False),
130
+ ('cryptography', 'cryptography', '飞书集成加密(可选)', False),
131
+ ]
132
+
133
+
134
+ def check_dependencies(fix=False):
135
+ """检查 pip 依赖装没装。--fix 时用镜像兜底安装 (失败切国内源重试)。"""
136
+ print('\n--- 1.5 Python 依赖 ---')
137
+ missing_core = []
138
+ missing_optional = []
139
+ for mod, pkg, usage, is_core in PIP_DEPS:
140
+ try:
141
+ __import__(mod)
142
+ say(OK, '%s (%s)' % (pkg, usage))
143
+ except ImportError:
144
+ if is_core:
145
+ missing_core.append(pkg)
146
+ problem('%s 未装 (%s)' % (pkg, usage),
147
+ 'pip install %s' % pkg)
148
+ else:
149
+ missing_optional.append(pkg)
150
+ say(WARN, '%s 未装 (%s, 按需装)' % (pkg, usage))
151
+
152
+ if fix and (missing_core or missing_optional):
153
+ req = os.path.join(BASE, 'requirements.txt')
154
+ if os.path.isfile(req):
155
+ # 用镜像兜底安装: 默认源失败 → 自动切清华/阿里/腾讯
156
+ from common.pip_install import install_with_fallback
157
+ print(' → 安装: pip install -r requirements.txt (失败自动切国内镜像)')
158
+ result = install_with_fallback(['-r', req])
159
+ if result['ok']:
160
+ src = result['source']
161
+ say(FIX, '依赖安装成功' + ('' if src == 'default' else ' (镜像源: %s)' % src))
162
+ # 装完后重新验证 (旧版装完不复查, 会把装失败误报为已装)
163
+ missing_core = []
164
+ for mod, pkg, usage, is_core in PIP_DEPS:
165
+ if is_core:
166
+ try:
167
+ __import__(mod)
168
+ except ImportError:
169
+ missing_core.append(pkg)
170
+ else:
171
+ print(' [MISS] ' + result['hint'])
172
+ # 重新验证, 看哪些真的还缺
173
+ missing_core = []
174
+ for mod, pkg, usage, is_core in PIP_DEPS:
175
+ if is_core:
176
+ try:
177
+ __import__(mod)
178
+ except ImportError:
179
+ missing_core.append(pkg)
180
+ else:
181
+ print(' requirements.txt 不存在, 手动装: pip install ' +
182
+ ' '.join(missing_core + missing_optional))
183
+
184
+ if missing_core:
185
+ print('\n ⚠️ 核心依赖缺失会导致知识图谱/MCP/配置全部不可用!')
186
+ print(' 一键修复: python .qoder/scripts/init_doctor.py --fix')
187
+ print(' (装包慢/失败: pip config set global.index-url '
188
+ 'https://pypi.tuna.tsinghua.edu.cn/simple)')
189
+
190
+
114
191
  # ============================================================
115
192
  # 2. 开发者身份
116
193
  # ============================================================
@@ -271,18 +348,35 @@ def check_source_repos(fix, config):
271
348
  else:
272
349
  problem('{} 源码缺失'.format(name), '加 --fix 自动克隆 ' + url)
273
350
 
351
+ # 校验 platforms 映射的项目名是否真实存在于 data/code/ (最常见的静默失败源:
352
+ # 管理员改了 git_sync.projects 的 URL 却忘了改 platforms.project -> 所有
353
+ # --platform 查询静默返回 0, 用户到生成原型时才发现啥都没有)
354
+ platforms = config.get('platforms') or {}
355
+ cloned = [d for d in os.listdir(CODE_DIR)] if os.path.isdir(CODE_DIR) else []
356
+ for pkey, pinfo in platforms.items():
357
+ proj_name = (pinfo or {}).get('project')
358
+ if not proj_name:
359
+ continue
360
+ if proj_name not in cloned:
361
+ problem(
362
+ 'platforms.{}.project = {!r} 但 data/code/ 下无此目录'.format(pkey, proj_name),
363
+ 'config.yaml platforms.{}.project 改成实际项目目录名 (现有: {})'.format(
364
+ pkey, ', '.join(cloned) or '空'))
365
+ else:
366
+ say(OK, 'platform {} -> {} 目录存在'.format(pkey, proj_name))
367
+
274
368
 
275
369
  # ============================================================
276
370
  # 5. 知识图谱新鲜度 (增量的核心逻辑)
277
371
  # ============================================================
278
372
 
279
373
  def index_age_days():
280
- """索引年龄: .index-meta.jsonlast_sync, 退化用文件 mtime"""
281
- meta_path = os.path.join(INDEX_DIR, '.index-meta.json')
282
- ki_path = os.path.join(INDEX_DIR, 'keyword-index.json')
283
- if not os.path.isfile(ki_path):
284
- return None # 索引不存在
285
- ts = None
374
+ """索引年龄: kg.duckdbmtime (DuckDB 统一存储后不再依赖 JSON)"""
375
+ kg_path = os.path.join(INDEX_DIR, 'kg.duckdb')
376
+ if not os.path.isfile(kg_path):
377
+ return None
378
+ import time as _time
379
+ return int((_time.time() - os.path.getmtime(kg_path)) / 86400)
286
380
  if os.path.isfile(meta_path):
287
381
  try:
288
382
  import json
@@ -296,22 +390,61 @@ def index_age_days():
296
390
  return (datetime.now() - ts).total_seconds() / 86400
297
391
 
298
392
 
393
+ def _has_source_code():
394
+ """data/code/ 下是否有真实项目 (非空目录)。"""
395
+ if not os.path.isdir(CODE_DIR):
396
+ return False
397
+ return any(d for d in os.listdir(CODE_DIR) if not d.startswith('.'))
398
+
399
+
400
+ def print_degraded_status():
401
+ """无源码/无索引时的降级声明 —— 让新同事第一时间知道工作流什么能用。
402
+ 在 main() 体检汇总后调用, 只在 data/code/ 为空时打印。
403
+ """
404
+ if _has_source_code():
405
+ return # 有源码, 不需要降级声明
406
+
407
+ print('\n' + '=' * 56)
408
+ print(' ⚠ 当前没有源码 (data/code/ 为空), 工作流处于降级状态')
409
+ print('=' * 56)
410
+ print(' 以下功能需要源码, 当前不可用:')
411
+ print(' /wl-search 搜代码 (无源码 = 搜不到)')
412
+ print(' /wl-prd-full 完整 PRD 的字段/API 锚定 (会跳过, 原型可用)')
413
+ print(' 知识图谱 MCP search_code/api 等工具 (启动但返回空)')
414
+ print()
415
+ print(' 以下功能不依赖源码, 当前可用:')
416
+ print(' /wl-prd-quick 零星需求 (纯文案/小改动)')
417
+ print(' /wl-task 任务管理')
418
+ print(' /wl-report 日报周报')
419
+ print(' /wl-commit 代码提交 (提交的是工作流本身的改动)')
420
+ print()
421
+ print(' 如何补上源码 (任选其一):')
422
+ print(' A. 配团队源码仓库: 打开 .qoder/config.yaml 的 git_sync.projects,')
423
+ print(' 填 url, 然后 wlkj init 会自动 clone')
424
+ print(' B. 手动放代码: 把项目代码放到 data/code/<项目名>/ 下')
425
+ print(' C. 拿团队的图谱: team_sync.py pull (管理员已建好的 kg.duckdb)')
426
+ print('=' * 56)
427
+
428
+
299
429
  def check_index(fix):
300
430
  print('\n--- 5. 知识图谱 (data/index/) ---')
301
431
  age = index_age_days()
302
432
  if age is None:
303
433
  # 完全缺失 -> 需要全量 (唯一允许"重头再来"的情形)
304
- if fix:
305
- if os.path.isdir(CODE_DIR) and os.listdir(CODE_DIR):
434
+ if _has_source_code():
435
+ # 有源码 可以建索引
436
+ if fix:
306
437
  print(' 索引缺失, 全量构建 (一次性, 之后都是增量) ...')
307
438
  if run_script('git_sync.py', '--index-only'):
308
439
  actions.append('全量构建知识图谱')
309
440
  else:
310
441
  problem('索引构建失败, 查看上方报错')
311
442
  else:
312
- problem('索引缺失且无源码', '先解决第 4 项再重跑')
443
+ problem('知识图谱不存在', ' --fix 全量构建 (仅首次)')
313
444
  else:
314
- problem('知识图谱不存在', '加 --fix 全量构建 (仅首次)')
445
+ # 无源码 建不了索引, 醒目告警 (不能只说"先解决第4项", 太含糊)
446
+ problem('索引缺失且无源码 (data/code/ 为空)',
447
+ '无法构建知识图谱。看下方"降级状态"说明, 配源码后重跑 init')
315
448
  return
316
449
  if age <= FRESH_DAYS:
317
450
  say(OK, '知识图谱 {:.1f} 天前更新过, 新鲜 (阈值 {} 天), 跳过'.format(age, FRESH_DAYS))
@@ -332,37 +465,57 @@ def check_index(fix):
332
465
  # ============================================================
333
466
 
334
467
  def check_style(fix):
335
- print('\n--- 6. 风格约束 ---')
336
- required = {
337
- 'vben-style-reference.json': 'Web 设计 Token (Vben HSL)',
338
- 'chart-style-reference.json': '看板/大屏风格先例',
339
- 'icon-reference.json': '真实图标库 (antd SVG + Vant, 原型禁 emoji)',
340
- 'style-index.json': 'UI 模式索引 (--style/--field 数据源)',
341
- 'style-meta.json': '风格摘要 (会话注入)',
468
+ print('\n--- 6. 知识图谱 + 设计参考 ---')
469
+
470
+ # 手工参考文件 (必须存在, 提交在 git )
471
+ ref_files = {
472
+ 'ref-vben-style.json': 'Web 设计 Token (Vben HSL)',
473
+ 'ref-chart-style.json': '看板/大屏风格先例',
474
+ 'ref-icon.json': '真实图标库 (antd SVG + Vant, 原型禁 emoji)',
342
475
  }
343
- missing_buildable = False
344
- for fname, desc in required.items():
345
- if os.path.isfile(os.path.join(INDEX_DIR, fname)):
476
+ for fname, desc in ref_files.items():
477
+ fpath = os.path.join(INDEX_DIR, fname)
478
+ if os.path.isfile(fpath):
346
479
  say(OK, '{} - {}'.format(fname, desc))
347
- elif fname in ('style-index.json', 'style-meta.json'):
348
- missing_buildable = True
349
- if not fix:
350
- problem(fname + ' 缺失', '加 --fix 自动构建')
351
- else:
352
- problem(fname + ' 缺失', '该文件提交在 git 中, 先解决第 3 项团队同步')
353
- tpls = ['prototype-web.html', 'prototype-app.html']
354
- for t in tpls:
355
- if os.path.isfile(os.path.join(TPL_DIR, t)):
356
- say(OK, '原型模板 ' + t)
357
480
  else:
358
- problem('原型模板缺失: ' + t)
359
- if missing_buildable and fix:
360
- if os.path.isdir(CODE_DIR) and os.listdir(CODE_DIR):
361
- print(' 构建风格索引 ...')
362
- if run_script('build_style_index.py'):
363
- actions.append('构建风格索引')
364
- else:
365
- problem('风格索引缺源码无法构建', '先解决第 4 项')
481
+ problem(fname + ' 缺失', '该文件提交在 git 中, 先解决团队同步 (team_sync pull)')
482
+
483
+ # kg.duckdb 知识图谱 (统一存储)
484
+ kg_path = os.path.join(INDEX_DIR, 'kg.duckdb')
485
+ role = ''
486
+ try:
487
+ dev_file = os.path.join(BASE, '.qoder', '.developer')
488
+ if os.path.isfile(dev_file):
489
+ for _line in open(dev_file, encoding='utf-8'):
490
+ if _line.strip().startswith('role='):
491
+ role = _line.split('=', 1)[1].strip()
492
+ except Exception:
493
+ pass
494
+
495
+ if os.path.isfile(kg_path):
496
+ say(OK, 'kg.duckdb 知识图谱 (%d KB)' % (os.path.getsize(kg_path) // 1024))
497
+ # 过期检测
498
+ import time as _t
499
+ age_h = (_t.time() - os.path.getmtime(kg_path)) / 3600
500
+ if age_h > 168:
501
+ if role in ('admin', 'dev'):
502
+ print(' [WARN] kg.duckdb 已 %.0f 小时未更新, 建议重建' % age_h)
503
+ if fix:
504
+ print(' 重建知识图谱 (kg_build.py) ...')
505
+ if run_script('kg_build.py'):
506
+ actions.append('知识图谱重建')
507
+ else:
508
+ print(' [WARN] kg.duckdb 可能过期, 建议 team_sync pull')
509
+ elif role in ('admin', 'dev') and os.path.isdir(CODE_DIR) and os.listdir(CODE_DIR):
510
+ if fix:
511
+ print(' 构建知识图谱 (kg_build.py, 首次2-4分钟) ...')
512
+ if run_script('kg_build.py'):
513
+ actions.append('知识图谱构建 (kg.duckdb)')
514
+ print(' 提示: 运行 kg_semantic.py --check 标注模块中文名')
515
+ elif role not in ('admin', 'dev'):
516
+ problem('kg.duckdb 缺失', '非管理员: team_sync.py pull 获取管理员构建的知识图谱')
517
+ else:
518
+ problem('kg.duckdb 缺源码无法构建', '先解决源码 clone')
366
519
 
367
520
 
368
521
  # ============================================================
@@ -493,6 +646,95 @@ def check_qoderwork():
493
646
  print(' 安装: python .qoder/scripts/install_qoderwork.py')
494
647
 
495
648
 
649
+ # ============================================================
650
+ # 10. 浏览器测试 · 个人测试数据文档 (可选, 测试时才需要)
651
+ # ============================================================
652
+
653
+ def check_autotest_data():
654
+ """检查个人测试数据文档是否就绪 (账号/密码, 登录类测试要用)。
655
+
656
+ 这是个人文档 (members/{dev}/autotest-data.yaml), 已 gitignored 不共享。
657
+ 缺了不阻塞普通工作流, 只是 /wl-test 登录类用例时会卡在"需要测试数据"。
658
+ """
659
+ print('\n--- 10. 浏览器测试 · 个人测试数据 (可选) ---')
660
+ dev = get_developer()
661
+ if not dev:
662
+ return # 没初始化开发者, 前面 check_developer 已报过
663
+ data_doc = os.path.join(BASE, 'workspace', 'members', dev, 'autotest-data.yaml')
664
+ if os.path.isfile(data_doc):
665
+ say(OK, '个人测试数据文档就绪: workspace/members/%s/autotest-data.yaml' % dev)
666
+ print(' /wl-test 登录类用例会自动从中取账号密码 (已 gitignored, 不共享)')
667
+ return
668
+ say(WARN, '尚未准备个人测试数据文档 (登录类 /wl-test 会问你要账号密码)')
669
+ print(' 生成模板: python .qoder/scripts/autotest.py init-data')
670
+ print(' 然后打开 workspace/members/%s/autotest-data.yaml 填测试账号' % dev)
671
+ print(' (个人文档, 已 gitignored, 不会 push 给团队)')
672
+ print(' 不填也能用: 每次 /wl-test 时临时 --data 传或对话里给)')
673
+
674
+
675
+ # ============================================================
676
+ # 11. 蓝湖 MCP (可选增强, 不阻塞)
677
+ # ============================================================
678
+
679
+ def check_lanhu():
680
+ """检查蓝湖 MCP 是否就绪 (第三方 HTTP server, 设计师直读蓝湖设计稿)。
681
+
682
+ 三态检查:
683
+ - mcp.json 有 lanhu 条目 + 服务在跑 → OK
684
+ - mcp.json 有 lanhu 但服务没起 → WARN (用时再起)
685
+ - 完全没配 → MISS (团队不用蓝湖可忽略; 用的话跑 setup.py)
686
+ 蓝湖是 opt-in 增强, 任何状态都不算 issue (不进未解决列表, 不阻塞)。
687
+ """
688
+ print('\n--- 11. 蓝湖 MCP (可选增强) ---')
689
+ import json
690
+ import socket
691
+
692
+ home = os.path.expanduser('~')
693
+ mcp_file = os.path.join(home, '.qoderwork', 'mcp.json')
694
+ if not os.path.isfile(mcp_file):
695
+ say(WARN, '未配置蓝湖 MCP (团队不用蓝湖可忽略)')
696
+ print(' 用的话跑: python .qoder/scripts/setup.py 或 setup_lanhu.py')
697
+ return
698
+
699
+ try:
700
+ cfg = json.loads(open(mcp_file, encoding='utf-8').read())
701
+ servers = cfg.get('mcpServers', {})
702
+ except (ValueError, OSError):
703
+ say(WARN, 'mcp.json 解析失败, 跳过蓝湖检查')
704
+ return
705
+
706
+ if 'lanhu' not in servers:
707
+ say(WARN, '未配置蓝湖 MCP (团队不用蓝湖可忽略)')
708
+ print(' 用的话跑: python .qoder/scripts/setup.py 或 setup_lanhu.py')
709
+ return
710
+
711
+ entry = servers['lanhu']
712
+ args = entry.get('args', []) if isinstance(entry, dict) else []
713
+ is_launcher = (isinstance(entry, dict) and 'command' in entry
714
+ and any('mcp_launcher.py' in str(a) for a in args))
715
+ is_stdio = isinstance(entry, dict) and 'command' in entry and 'url' not in entry
716
+
717
+ if is_launcher:
718
+ # launcher 模式 (最佳): QoderWork 自动开关, 不写死路径
719
+ launcher = os.path.join(os.path.expanduser('~'), '.qoderwork', 'mcp_launcher.py')
720
+ say(OK, '蓝湖 MCP 已注册 (STDIO launcher, QoderWork 自动开关)')
721
+ if os.path.isfile(launcher):
722
+ print(' 开 QoderWork 自动起, 关自动停, 无需手动 start')
723
+ print(' launcher 按当前 .developer 角色读 cookie (角色隔离)')
724
+ else:
725
+ say(WARN, 'launcher 缺失: ' + launcher)
726
+ print(' 重跑: python .qoder/scripts/install_qoderwork.py')
727
+ elif is_stdio:
728
+ # 旧 STDIO (写死 venv 路径), 建议升级 launcher
729
+ say(WARN, '蓝湖 MCP 是旧 STDIO 格式 (command 写死路径)')
730
+ print(' 建议升级 launcher 模式 (跨机器通用): python .qoder/scripts/install_qoderwork.py')
731
+ else:
732
+ # 旧 HTTP 模式
733
+ url = entry.get('url', '?')
734
+ say(WARN, '蓝湖 MCP 是旧 HTTP 模式: ' + url[:50])
735
+ print(' 建议升级 STDIO launcher (QoderWork 自动开关): python .qoder/scripts/setup_lanhu.py')
736
+
737
+
496
738
  # ============================================================
497
739
  # Main
498
740
  # ============================================================
@@ -511,6 +753,7 @@ def main():
511
753
 
512
754
  config = load_config()
513
755
  check_basics()
756
+ check_dependencies(fix)
514
757
  check_developer(fix, name, role)
515
758
  check_team_sync(fix)
516
759
  check_source_repos(fix, config)
@@ -519,19 +762,28 @@ def main():
519
762
  check_prd_templates()
520
763
  check_weekly(fix)
521
764
  check_qoderwork()
765
+ check_autotest_data()
766
+ check_lanhu()
522
767
 
523
768
  print('\n' + '=' * 56)
524
769
  if actions:
525
770
  print('本次修复: ')
526
771
  for a in actions:
527
772
  print(' + ' + a)
773
+
774
+ # 无源码时的降级声明 (不算 issue, 但必须让用户知道当前能用什么)
775
+ print_degraded_status()
776
+
528
777
  if issues:
529
- print('未解决问题 ({}):'.format(len(issues)))
778
+ print('\n未解决问题 ({}):'.format(len(issues)))
530
779
  for i in issues:
531
780
  print(' - ' + i)
532
781
  print('=' * 56)
533
782
  return 1
534
- print('环境健康, 可以开始工作: /wl-prd <需求描述>')
783
+ if _has_source_code():
784
+ print('环境健康, 可以开始工作: /wl-prd-full <需求描述>')
785
+ else:
786
+ print('工作流核心已就绪 (见上方降级说明补源码)。')
535
787
  print('=' * 56)
536
788
  return 0
537
789