@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.
- package/bin/cli.js +261 -11
- package/package.json +1 -1
- package/templates/qoder/agents/insight-planning.md +67 -0
- package/templates/qoder/agents/insight-research.md +61 -0
- package/templates/qoder/agents/prd-reference.md +8 -2
- package/templates/qoder/commands/optional/wl-insight.md +275 -0
- package/templates/qoder/commands/{wl-report.md → optional/wl-report.md} +13 -5
- package/templates/qoder/commands/{wl-spec.md → optional/wl-spec.md} +1 -1
- package/templates/qoder/commands/{wl-status.md → optional/wl-status.md} +28 -2
- package/templates/qoder/commands/wl-code.md +10 -2
- package/templates/qoder/commands/wl-commit.md +1 -1
- package/templates/qoder/commands/wl-design-draw.md +78 -0
- package/templates/qoder/commands/wl-design-scan.md +108 -0
- package/templates/qoder/commands/wl-design-spec.md +154 -0
- package/templates/qoder/commands/wl-design.md +32 -0
- package/templates/qoder/commands/wl-init.md +24 -3
- package/templates/qoder/commands/wl-prd-full.md +226 -0
- package/templates/qoder/commands/wl-prd-quick.md +134 -0
- package/templates/qoder/commands/wl-prd-review.md +104 -0
- package/templates/qoder/commands/wl-prd.md +17 -311
- package/templates/qoder/commands/wl-search.md +66 -30
- package/templates/qoder/commands/wl-task.md +290 -59
- package/templates/qoder/commands/wl-test.md +92 -24
- package/templates/qoder/config.yaml +51 -15
- package/templates/qoder/hooks/inject-workflow-state.py +2 -2
- package/templates/qoder/hooks/session-start.py +82 -51
- package/templates/qoder/rules/wl-pipeline.md +216 -105
- package/templates/qoder/scripts/__pycache__/search_index.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/archive_prd.py +377 -0
- package/templates/qoder/scripts/autotest.py +1715 -0
- package/templates/qoder/scripts/autotest_batch.py +224 -0
- package/templates/qoder/scripts/autotest_run.py +297 -0
- package/templates/qoder/scripts/benchmark.py +210 -209
- package/templates/qoder/scripts/build_style_index.py +444 -4
- package/templates/qoder/scripts/check_carriers.py +238 -0
- package/templates/qoder/scripts/check_mcp.py +298 -0
- package/templates/qoder/scripts/check_qoderwork_consistency.py +166 -0
- package/templates/qoder/scripts/common/events.py +46 -0
- package/templates/qoder/scripts/common/extract.py +419 -0
- package/templates/qoder/scripts/common/graph_traverse.py +533 -0
- package/templates/qoder/scripts/common/paths.py +89 -0
- package/templates/qoder/scripts/common/pip_install.py +144 -0
- package/templates/qoder/scripts/common/platform_guard.py +61 -0
- package/templates/qoder/scripts/common/search_engine.py +205 -205
- package/templates/qoder/scripts/common/terms.py +57 -0
- package/templates/qoder/scripts/common/ts_extract.py +536 -0
- package/templates/qoder/scripts/context_pack.py +73 -13
- package/templates/qoder/scripts/enrich_prompt.py +226 -0
- package/templates/qoder/scripts/eval_prd.py +318 -231
- package/templates/qoder/scripts/export.py +487 -487
- package/templates/qoder/scripts/extract_api_params.py +246 -0
- package/templates/qoder/scripts/extract_routes.py +54 -0
- package/templates/qoder/scripts/extract_routes_tree.py +78 -0
- package/templates/qoder/scripts/fill_prototype.py +707 -0
- package/templates/qoder/scripts/gen_design_doc.py +394 -0
- package/templates/qoder/scripts/git_sync.py +27 -15
- package/templates/qoder/scripts/init_doctor.py +292 -40
- package/templates/qoder/scripts/install_qoderwork.py +366 -9
- package/templates/qoder/scripts/kg.py +708 -0
- package/templates/qoder/scripts/kg_auto_login.py +196 -0
- package/templates/qoder/scripts/kg_build.py +612 -0
- package/templates/qoder/scripts/kg_build_db.py +327 -0
- package/templates/qoder/scripts/kg_duckdb.py +549 -0
- package/templates/qoder/scripts/kg_incremental.py +393 -0
- package/templates/qoder/scripts/kg_link_db.py +224 -0
- package/templates/qoder/scripts/kg_mcp_server.py +801 -0
- package/templates/qoder/scripts/kg_semantic.py +150 -0
- package/templates/qoder/scripts/kg_test_runner.py +241 -0
- package/templates/qoder/scripts/lanhu_stdio_wrapper.py +119 -0
- package/templates/qoder/scripts/learn.py +118 -39
- package/templates/qoder/scripts/learn_aggregate.py +201 -0
- package/templates/qoder/scripts/mcp_launcher.py +359 -0
- package/templates/qoder/scripts/mysql_mcp_server.py +396 -0
- package/templates/qoder/scripts/repo_root.py +106 -0
- package/templates/qoder/scripts/role.py +12 -0
- package/templates/qoder/scripts/run_weekly_update.bat +5 -0
- package/templates/qoder/scripts/run_weekly_update.sh +5 -0
- package/templates/qoder/scripts/search_index.py +307 -60
- package/templates/qoder/scripts/secure-ls.js +5640 -0
- package/templates/qoder/scripts/setup.py +706 -641
- package/templates/qoder/scripts/setup_lanhu.py +963 -0
- package/templates/qoder/scripts/status.py +250 -11
- package/templates/qoder/scripts/sync_carriers.py +259 -0
- package/templates/qoder/scripts/syncgate.py +2 -2
- package/templates/qoder/scripts/task.py +75 -0
- package/templates/qoder/scripts/team_sync.py +60 -4
- package/templates/qoder/scripts/workspace_init.py +1 -1
- package/templates/qoder/skills/design-import/SKILL.md +226 -0
- package/templates/qoder/skills/design-import/figma-workflow.md +81 -0
- package/templates/qoder/skills/design-review/SKILL.md +82 -25
- package/templates/qoder/skills/prd-generator/SKILL.md +184 -60
- package/templates/qoder/skills/prd-review/SKILL.md +18 -1
- package/templates/qoder/skills/prompt-enrich/SKILL.md +90 -0
- package/templates/qoder/skills/prototype-generator/SKILL.md +256 -141
- package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
- package/templates/qoder/skills/spec-coder/SKILL.md +18 -1
- package/templates/qoder/skills/spec-generator/SKILL.md +18 -1
- package/templates/qoder/skills/test-generator/SKILL.md +15 -2
- package/templates/qoder/skills/wl-code/SKILL.md +55 -36
- package/templates/qoder/skills/wl-commit/SKILL.md +89 -76
- package/templates/qoder/skills/wl-design/SKILL.md +55 -0
- package/templates/qoder/skills/wl-init/SKILL.md +76 -67
- package/templates/qoder/skills/wl-insight/SKILL.md +201 -81
- package/templates/qoder/skills/wl-prd-full/SKILL.md +69 -0
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +49 -0
- package/templates/qoder/skills/wl-prd-review/SKILL.md +34 -0
- package/templates/qoder/skills/wl-report/SKILL.md +131 -107
- package/templates/qoder/skills/wl-search/SKILL.md +141 -75
- package/templates/qoder/skills/wl-spec/SKILL.md +49 -39
- package/templates/qoder/skills/wl-status/SKILL.md +83 -61
- package/templates/qoder/skills/wl-task/SKILL.md +132 -58
- package/templates/qoder/skills/wl-test/SKILL.md +406 -40
- package/templates/qoder/templates/prototype-app.html +13 -8
- package/templates/qoder/templates/prototype-web.html +376 -93
- package/templates/root/AGENTS.md +89 -34
- package/templates/root/requirements.txt +21 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +259 -259
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +186 -186
- package/templates/qoder/agents/prd-planning.md +0 -57
- package/templates/qoder/agents/prd-research.md +0 -34
- package/templates/qoder/commands/wl-insight.md +0 -51
- package/templates/qoder/scripts/__pycache__/init_doctor.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/setup.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/common/__pycache__/developer.cpython-39.pyc +0 -0
- 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
|
-
"""索引年龄:
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
374
|
+
"""索引年龄: 用 kg.duckdb 的 mtime (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
|
|
305
|
-
|
|
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('
|
|
443
|
+
problem('知识图谱不存在', '加 --fix 全量构建 (仅首次)')
|
|
313
444
|
else:
|
|
314
|
-
|
|
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
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
'
|
|
340
|
-
'style
|
|
341
|
-
'
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
if os.path.isfile(
|
|
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('
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
-
|
|
783
|
+
if _has_source_code():
|
|
784
|
+
print('环境健康, 可以开始工作: /wl-prd-full <需求描述>')
|
|
785
|
+
else:
|
|
786
|
+
print('工作流核心已就绪 (见上方降级说明补源码)。')
|
|
535
787
|
print('=' * 56)
|
|
536
788
|
return 0
|
|
537
789
|
|