@hupan56/wlkj 3.0.0 → 3.1.2

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 (237) hide show
  1. package/bin/cli.js +1435 -1373
  2. package/package.json +1 -1
  3. package/templates/qoder/commands/optional/wl-insight.md +6 -4
  4. package/templates/qoder/commands/optional/wl-report.md +20 -39
  5. package/templates/qoder/commands/optional/wl-status.md +4 -4
  6. package/templates/qoder/commands/wl-code.md +2 -2
  7. package/templates/qoder/commands/wl-design.md +5 -5
  8. package/templates/qoder/commands/wl-init.md +3 -3
  9. package/templates/qoder/commands/wl-prd.md +26 -13
  10. package/templates/qoder/commands/wl-req.md +3 -3
  11. package/templates/qoder/commands/wl-search.md +78 -47
  12. package/templates/qoder/commands/wl-task.md +343 -40
  13. package/templates/qoder/commands/wl-test.md +10 -10
  14. package/templates/qoder/config.yaml +35 -13
  15. package/templates/qoder/hooks/post-tool-use.py +181 -0
  16. package/templates/qoder/hooks/session-start.py +82 -1
  17. package/templates/qoder/hooks/stop-eval.py +207 -0
  18. package/templates/qoder/hooks/user-prompt-submit.py +139 -0
  19. package/templates/qoder/rules/wl-pipeline.md +70 -8
  20. package/templates/qoder/scripts/README.md +78 -115
  21. package/templates/qoder/scripts/capability/__init__.py +26 -0
  22. package/templates/qoder/scripts/capability/__main__.py +72 -0
  23. package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
  24. package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
  25. package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
  26. package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
  27. package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
  28. package/templates/qoder/scripts/capability/caps/context.py +36 -0
  29. package/templates/qoder/scripts/capability/caps/cron.py +50 -0
  30. package/templates/qoder/scripts/capability/caps/identity.py +43 -0
  31. package/templates/qoder/scripts/capability/caps/memory.py +71 -0
  32. package/templates/qoder/scripts/capability/caps/notify.py +41 -0
  33. package/templates/qoder/scripts/capability/caps/present.py +48 -0
  34. package/templates/qoder/scripts/capability/caps/repo.py +31 -0
  35. package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
  36. package/templates/qoder/scripts/capability/chain.py +92 -0
  37. package/templates/qoder/scripts/capability/memory_chain.py +41 -0
  38. package/templates/qoder/scripts/capability/registry.py +246 -0
  39. package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
  40. package/templates/qoder/scripts/capability/smoke_test.py +211 -0
  41. package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
  42. package/templates/qoder/scripts/{report → deployment/setup}/__init__.py +11 -11
  43. package/templates/qoder/scripts/{setup → deployment/setup}/carriers.py +33 -26
  44. package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
  45. package/templates/qoder/scripts/{setup → deployment/setup}/init_doctor.py +40 -16
  46. package/templates/qoder/scripts/{setup → deployment/setup}/install_qoderwork.py +20 -12
  47. package/templates/qoder/scripts/{setup → deployment/setup}/platform_doctor.py +8 -2
  48. package/templates/qoder/scripts/{setup → deployment/setup}/repo_root.py +113 -112
  49. package/templates/qoder/scripts/{setup → deployment/setup}/setup.py +31 -11
  50. package/templates/qoder/scripts/{setup → deployment/setup}/setup_lanhu.py +13 -7
  51. package/templates/qoder/scripts/domain/__init__.py +0 -0
  52. package/templates/qoder/scripts/domain/design/__init__.py +0 -0
  53. package/templates/qoder/scripts/{report → domain/design}/fill_prototype.py +12 -6
  54. package/templates/qoder/scripts/{report → domain/design}/gen_design_doc.py +8 -2
  55. package/templates/qoder/scripts/{setup → domain/kg}/__init__.py +11 -11
  56. package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
  57. package/templates/qoder/scripts/{kg → domain/kg/build}/build_entity_registry.py +7 -2
  58. package/templates/qoder/scripts/{kg → domain/kg/build}/build_relations.py +7 -2
  59. package/templates/qoder/scripts/{kg → domain/kg/build}/build_style_index.py +13 -7
  60. package/templates/qoder/scripts/{kg → domain/kg/build}/build_workflows.py +7 -2
  61. package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build.py +76 -9
  62. package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build_db.py +7 -2
  63. package/templates/qoder/scripts/{kg → domain/kg/build}/kg_incremental.py +11 -6
  64. package/templates/qoder/scripts/{report → domain/kg/build}/learn_aggregate.py +9 -4
  65. package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
  66. package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
  67. package/templates/qoder/scripts/{common → domain/kg/extract}/test_extract.py +115 -115
  68. package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
  69. package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
  70. package/templates/qoder/scripts/{kg → domain/kg/graph}/kg_link_db.py +7 -2
  71. package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
  72. package/templates/qoder/scripts/{kg → domain/kg}/kg.py +263 -30
  73. package/templates/qoder/scripts/{common → domain/kg}/kg_capabilities.py +182 -182
  74. package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
  75. package/templates/qoder/scripts/{kg → domain/kg/search}/context_pack.py +30 -9
  76. package/templates/qoder/scripts/{kg → domain/kg/search}/enrich_prompt.py +10 -4
  77. package/templates/qoder/scripts/{kg → domain/kg/search}/prefetch.py +33 -8
  78. package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
  79. package/templates/qoder/scripts/{kg → domain/kg/search}/search_index.py +143 -77
  80. package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
  81. package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
  82. package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
  83. package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
  84. package/templates/qoder/scripts/{kg → domain/kg/storage}/kg_duckdb.py +38 -14
  85. package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
  86. package/templates/qoder/scripts/{report → domain/learning}/learn.py +8 -3
  87. package/templates/qoder/scripts/{mcp → domain/report}/__init__.py +11 -11
  88. package/templates/qoder/scripts/{report → domain/report}/add_session.py +9 -3
  89. package/templates/qoder/scripts/{report → domain/report}/export.py +10 -5
  90. package/templates/qoder/scripts/{report → domain/report}/report.py +9 -4
  91. package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
  92. package/templates/qoder/scripts/{report → domain/report}/role.py +10 -4
  93. package/templates/qoder/scripts/{report → domain/report}/status.py +112 -9
  94. package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
  95. package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
  96. package/templates/qoder/scripts/{report → domain/requirement}/archive_prd.py +8 -2
  97. package/templates/qoder/scripts/{report → domain/requirement}/req.py +228 -222
  98. package/templates/qoder/scripts/{kg → domain/task}/__init__.py +11 -11
  99. package/templates/qoder/scripts/{task → domain/task}/git_sync.py +40 -21
  100. package/templates/qoder/scripts/{task → domain/task}/syncgate.py +13 -8
  101. package/templates/qoder/scripts/{task → domain/task}/task.py +13 -5
  102. package/templates/qoder/scripts/{task → domain/task}/task_lifecycle.py +606 -596
  103. package/templates/qoder/scripts/{task → domain/task}/task_query.py +162 -161
  104. package/templates/qoder/scripts/{task → domain/task}/task_relations.py +425 -424
  105. package/templates/qoder/scripts/{task → domain/task}/team_sync.py +9 -4
  106. package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
  107. package/templates/qoder/scripts/foundation/__init__.py +0 -0
  108. package/templates/qoder/scripts/{common → foundation}/bootstrap.py +10 -10
  109. package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
  110. package/templates/qoder/scripts/{common → foundation/core}/cmd_registry.py +1 -1
  111. package/templates/qoder/scripts/{common → foundation/core}/config.py +1 -1
  112. package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -586
  113. package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
  114. package/templates/qoder/scripts/{common → foundation/data}/contract.py +2 -2
  115. package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
  116. package/templates/qoder/scripts/{common → foundation/data}/result.py +1 -1
  117. package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -404
  118. package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
  119. package/templates/qoder/scripts/{common → foundation/identity}/check_publish.py +103 -98
  120. package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -239
  121. package/templates/qoder/scripts/{common → foundation/identity}/guard.py +2 -2
  122. package/templates/qoder/scripts/{common → foundation/identity}/identity.py +14 -10
  123. package/templates/qoder/scripts/{common → foundation/identity}/roles.py +1 -1
  124. package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
  125. package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
  126. package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +2 -2
  127. package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
  128. package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
  129. package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
  130. package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
  131. package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
  132. package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
  133. package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
  134. package/templates/qoder/scripts/{common → foundation/protocol}/mcp_base.py +1 -1
  135. package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
  136. package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
  137. package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
  138. package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
  139. package/templates/qoder/scripts/orchestration/__init__.py +0 -0
  140. package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
  141. package/templates/qoder/scripts/protocol/__init__.py +0 -0
  142. package/templates/qoder/scripts/protocol/browser/README.md +207 -0
  143. package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
  144. package/templates/qoder/scripts/protocol/browser/config.env +9 -0
  145. package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
  146. package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
  147. package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
  148. package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
  149. package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
  150. package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
  151. package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
  152. package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
  153. package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
  154. package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
  155. package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
  156. package/templates/qoder/scripts/{mcp → protocol/mcp}/lanhu_stdio_wrapper.py +11 -5
  157. package/templates/qoder/scripts/{mcp → protocol/mcp}/mcp_doctor.py +47 -14
  158. package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
  159. package/templates/qoder/scripts/{mcp → protocol/mcp}/mysql_mcp_server.py +181 -67
  160. package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
  161. package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
  162. package/templates/qoder/scripts/protocol/transports/base.py +87 -0
  163. package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
  164. package/templates/qoder/scripts/protocol/transports/http.py +141 -0
  165. package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
  166. package/templates/qoder/scripts/run_weekly_update.bat +27 -18
  167. package/templates/qoder/scripts/run_weekly_update.sh +22 -13
  168. package/templates/qoder/scripts/validation/__init__.py +0 -0
  169. package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
  170. package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
  171. package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
  172. package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
  173. package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
  174. package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
  175. package/templates/qoder/scripts/{report → validation/metrics}/eval_prd.py +17 -10
  176. package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
  177. package/templates/qoder/scripts/validation/test/__init__.py +11 -0
  178. package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
  179. package/templates/qoder/scripts/{test → validation/test}/autotest.py +64 -4
  180. package/templates/qoder/scripts/{common → validation/test}/autotest_auth.py +1 -1
  181. package/templates/qoder/scripts/{test → validation/test}/autotest_batch.py +16 -3
  182. package/templates/qoder/scripts/{test → validation/test}/autotest_data.py +17 -12
  183. package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
  184. package/templates/qoder/scripts/{test → validation/test}/autotest_run.py +17 -3
  185. package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
  186. package/templates/qoder/scripts/{test → validation/test}/benchmark.py +12 -6
  187. package/templates/qoder/scripts/{test → validation/test}/kg_auto_login.py +8 -2
  188. package/templates/qoder/scripts/{test → validation/test}/kg_test_runner.py +7 -2
  189. package/templates/qoder/scripts/{test → validation/test}/page_probe.py +8 -3
  190. package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
  191. package/templates/qoder/settings.json +30 -0
  192. package/templates/qoder/skills/design-import/SKILL.md +22 -13
  193. package/templates/qoder/skills/design-review/SKILL.md +19 -10
  194. package/templates/qoder/skills/prd-generator/SKILL.md +22 -18
  195. package/templates/qoder/skills/prd-review/SKILL.md +6 -4
  196. package/templates/qoder/skills/prototype-generator/SKILL.md +63 -14
  197. package/templates/qoder/skills/spec-coder/SKILL.md +22 -13
  198. package/templates/qoder/skills/spec-generator/SKILL.md +22 -10
  199. package/templates/qoder/skills/test-generator/SKILL.md +7 -5
  200. package/templates/qoder/skills/wl-code/SKILL.md +23 -11
  201. package/templates/qoder/skills/wl-commit/SKILL.md +9 -8
  202. package/templates/qoder/skills/wl-design/SKILL.md +5 -3
  203. package/templates/qoder/skills/wl-init/SKILL.md +6 -6
  204. package/templates/qoder/skills/wl-insight/SKILL.md +23 -10
  205. package/templates/qoder/skills/wl-prd-full/SKILL.md +57 -6
  206. package/templates/qoder/skills/wl-prd-quick/SKILL.md +3 -3
  207. package/templates/qoder/skills/wl-prd-review/SKILL.md +16 -4
  208. package/templates/qoder/skills/wl-report/SKILL.md +25 -23
  209. package/templates/qoder/skills/wl-search/SKILL.md +149 -78
  210. package/templates/qoder/skills/wl-spec/SKILL.md +7 -5
  211. package/templates/qoder/skills/wl-status/SKILL.md +48 -16
  212. package/templates/qoder/skills/wl-task/SKILL.md +92 -10
  213. package/templates/qoder/skills/wl-test/SKILL.md +75 -29
  214. package/templates/qoder/templates/prd-full-template.md +7 -0
  215. package/templates/root/AGENTS.md +275 -236
  216. package/templates/root/requirements.txt +3 -0
  217. package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
  218. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
  219. package/templates/.qoder/.runtime/ctx-cache-5660152f1d6dd819.md +0 -23
  220. package/templates/.qoder/.runtime/ctx-cache-afdce0dac06b25b0.md +0 -23
  221. package/templates/.qoder/.runtime/search-cache-eae7644e7b122f35.txt +0 -1
  222. package/templates/.qoder/learning/eval-history.jsonl +0 -28
  223. package/templates/data/index/wiki-index.json +0 -8
  224. package/templates/qoder/nul +0 -4
  225. package/templates/qoder/scripts/kg/extract.py +0 -398
  226. package/templates/qoder/scripts/kg/kg_semantic.py +0 -156
  227. package/templates/qoder/scripts/mcp/kg_mcp_server.py +0 -606
  228. package/templates/qoder/scripts/mcp/mcp_launcher.py +0 -442
  229. package/templates/qoder/scripts/mcp/zentao_mcp_server.py +0 -384
  230. package/templates/qoder/scripts/task/__init__.py +0 -11
  231. package/templates/qoder/scripts/test/__init__.py +0 -11
  232. package/templates/qoder/scripts/wlkj.py +0 -116
  233. /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
  234. /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
  235. /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
  236. /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
  237. /package/templates/qoder/scripts/{test → validation/test}/secure-ls.js +0 -0
@@ -1,442 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # ⚠️ 本文件会被复制到 ~/.qoderwork/mcp_launcher.py, 在用户主目录运行!
4
- # 不能依赖仓库内的 bootstrap/common (那里没有), 必须自包含路径定位。
5
- import os, sys
6
-
7
- try:
8
- sys.stderr.reconfigure(encoding='utf-8', errors='replace')
9
- except (AttributeError, TypeError, OSError):
10
- pass
11
-
12
-
13
- """
14
- mcp_launcher.py - 统一 MCP 启动器(跨 Win/Mac/Linux,消除写死路径)
15
-
16
- 被谁调用:
17
- QoderWork 按 ~/.qoderwork/mcp.json 的 command 拉起本脚本。
18
- mcp.json 的 args = [<本脚本路径>, <MCP名>]
19
- 例: {"command": "py", "args": ["C:/Users/HP/.qoderwork/mcp_launcher.py", "kg"]}
20
-
21
- 为什么需要它:
22
- 之前 mcp.json 写死绝对路径 (E:\\...\\python.exe + 仓库路径 + venv路径),
23
- 换电脑/换系统/别人 clone 全废。本 launcher 运行时自定位仓库根 + 找对的 python,
24
- mcp.json 只需写 python 命令名 (py/python3) + 本脚本路径 (在 ~/.qoderwork/)。
25
-
26
- 做什么:
27
- 1. 解析 argv[1] = MCP 名 (kg / mysql / lanhu)
28
- 2. find_repo_root(): 环境变量 QODER_REPO > ~/.qoderwork/.repo-root 文件 > 向上找 .qoder/
29
- 3. 按 MCP 名选 python + server 脚本:
30
- - kg: sys.executable, <repo>/.qoder/scripts/kg_mcp_server.py
31
- - mysql: sys.executable, <repo>/.qoder/scripts/mysql_mcp_server.py (env 从 .mcp-mysql-env.json 读)
32
- - lanhu: <repo>/external/lanhu-mcp/.venv python (3.10+), 复用 lanhu_stdio_wrapper 逻辑 (读角色 cookie)
33
- 4. os.execv 启动 (接管进程, QoderWork 管生死)
34
-
35
- 跨平台:
36
- find_repo_root 用 os.path, venv python 用 _venv_python (win Scripts/mac bin)。
37
- 本脚本被 python3/py/python 跑都行 (它只做定位+execv, 不依赖特定版本)。
38
- """
39
-
40
- import json
41
- import os
42
- import sys
43
- from pathlib import Path
44
-
45
- try:
46
- sys.stderr.reconfigure(encoding='utf-8', errors='replace')
47
- except (AttributeError, TypeError, OSError):
48
- pass
49
-
50
-
51
- def _home():
52
- """跨平台 home 目录。"""
53
- return Path(os.environ.get('USERPROFILE') or os.path.expanduser('~'))
54
-
55
-
56
- def _log(msg):
57
- """调试日志到 stderr (MCP stdio 用 stdout, 不能污染)。"""
58
- sys.stderr.write('[mcp-launcher] ' + msg + '\n')
59
-
60
-
61
- def _launch_disabled_mcp(name, reason):
62
- """没装/没配时, 启动一个空壳 MCP (只回 initialize + tools/list 空)。
63
- 让 QoderWork 显示绿(已连接)但无工具, 而不是黄(报错)。
64
- reason 写进 serverInfo 让用户知道为什么没工具。"""
65
- import json as _j
66
- _log(f'{name} MCP 以"未启用"模式启动: {reason}')
67
- # 简单的 stdio 循环, 只响应 initialize 和 tools/list
68
- while True:
69
- try:
70
- line = sys.stdin.buffer.readline()
71
- except (EOFError, OSError):
72
- break
73
- if not line:
74
- break
75
- try:
76
- req = _j.loads(line.decode('utf-8'))
77
- except Exception:
78
- continue
79
- method = req.get('method', '')
80
- rid = req.get('id')
81
- if method == 'initialize':
82
- sys.stdout.write(_j.dumps({
83
- "jsonrpc": "2.0", "id": rid,
84
- "result": {
85
- "protocolVersion": "2024-11-05",
86
- "capabilities": {"tools": {}},
87
- "serverInfo": {"name": name, "version": "0.0.0-disabled",
88
- "reason": reason}
89
- }
90
- }) + '\n')
91
- sys.stdout.flush()
92
- elif method == 'tools/list':
93
- sys.stdout.write(_j.dumps({
94
- "jsonrpc": "2.0", "id": rid,
95
- "result": {"tools": []}
96
- }) + '\n')
97
- sys.stdout.flush()
98
- elif method == 'notifications/initialized':
99
- pass
100
- elif rid is not None:
101
- sys.stdout.write(_j.dumps({
102
- "jsonrpc": "2.0", "id": rid,
103
- "error": {"code": -32601, "message": reason}
104
- }) + '\n')
105
- sys.stdout.flush()
106
-
107
-
108
- def _find_python_with_deps(required_module='duckdb'):
109
- """找一个装了指定依赖的 Python。优先级:
110
- 1. sys.executable (当前 Python, 先测它有没有)
111
- 2. 常见 Python 路径 (anaconda/python3等) 逐个测
112
- 返回 python 路径(str) 或 None。
113
- 解决"py -3 选的 Python 没装 duckdb"问题。
114
- """
115
- # 1. 当前 Python 有没有
116
- try:
117
- __import__(required_module)
118
- return sys.executable
119
- except ImportError:
120
- pass
121
- # 2. 找其它 Python
122
- import shutil
123
- candidates = []
124
- for name in ('python', 'python3', 'py'):
125
- p = shutil.which(name)
126
- if p and p != sys.executable:
127
- candidates.append(p)
128
- # 常见安装路径 (Windows)
129
- if sys.platform == 'win32':
130
- for p in [
131
- os.path.join(os.environ.get('USERPROFILE', ''), 'Anaconda3', 'python.exe'),
132
- os.path.join(os.environ.get('USERPROFILE', ''), 'anaconda3', 'python.exe'),
133
- os.path.join(os.environ.get('USERPROFILE', ''), 'miniconda3', 'python.exe'),
134
- r'C:\ProgramData\Anaconda3\python.exe',
135
- ]:
136
- if os.path.isfile(p) and p not in candidates:
137
- candidates.append(p)
138
- else:
139
- # macOS / Linux: 补 conda 路径候选 (治"Mac+conda 必爆"隐患)
140
- # 场景: Mac 同事把 duckdb 装进 conda base, 而 which python3 指向系统 python
141
- # → 之前探测不到 conda python, KG MCP 静默降级
142
- home = os.path.expanduser('~')
143
- for p in [
144
- '/opt/anaconda3/bin/python',
145
- '/opt/miniconda3/bin/python',
146
- '/usr/local/anaconda3/bin/python',
147
- os.path.join(home, 'anaconda3', 'bin', 'python'),
148
- os.path.join(home, 'miniconda3', 'bin', 'python'),
149
- os.path.join(home, 'opt', 'anaconda3', 'bin', 'python'),
150
- os.path.join(home, 'opt', 'miniconda3', 'bin', 'python'),
151
- '/opt/homebrew/bin/python3', # Apple Silicon Homebrew
152
- '/usr/local/bin/python3', # Intel Mac Homebrew
153
- ]:
154
- if os.path.isfile(p) and p not in candidates:
155
- candidates.append(p)
156
- for py in candidates:
157
- try:
158
- r = __import__('subprocess').run(
159
- [py, '-c', 'import %s' % required_module],
160
- capture_output=True, timeout=5)
161
- if r.returncode == 0:
162
- _log(f'找到装了 {required_module} 的 Python: {py}')
163
- return py
164
- except Exception:
165
- continue
166
- return None
167
-
168
-
169
- def find_repo_root():
170
- """定位 QODER 仓库根。优先级:
171
- 1. 环境变量 QODER_REPO (用户显式设, 最可靠)
172
- 2. ~/.qoderwork/.repo-root 文件 (install 时写)
173
- 3. 向上找 .qoder/scripts/ (cwd 在仓库内, IDE/CLI 场景)
174
- 4. 本脚本若在 <repo>/.qoder/scripts/ 下 → 上三级 (开发态)
175
- 返回 Path 或 None。
176
- """
177
- # 1. 环境变量
178
- env_repo = os.environ.get('QODER_REPO')
179
- if env_repo and (Path(env_repo) / '.qoder' / 'scripts').is_dir():
180
- return Path(env_repo)
181
-
182
- # 2. ~/.qoderwork/.repo-root 文件 (install 写的, 主路径)
183
- anchor = _home() / '.qoderwork' / '.repo-root'
184
- if anchor.is_file():
185
- try:
186
- line = anchor.read_text(encoding='utf-8').strip()
187
- if line and (Path(line) / '.qoder' / 'scripts').is_dir():
188
- return Path(line)
189
- except OSError:
190
- pass
191
-
192
- # 3. 向上找 .qoder/scripts/
193
- here = Path(os.getcwd()).resolve()
194
- for _ in range(8):
195
- if (here / '.qoder' / 'scripts').is_dir():
196
- return here
197
- parent = here.parent
198
- if parent == here:
199
- break
200
- here = parent
201
-
202
- # 4. 本脚本位置 (开发态: 在 <repo>/.qoder/scripts/mcp_launcher.py)
203
- try:
204
- s = Path(__file__).resolve()
205
- root = s.parent.parent.parent # scripts/ -> .qoder/ -> repo
206
- if (root / '.qoder' / 'scripts').is_dir():
207
- return root
208
- except Exception:
209
- pass
210
-
211
- return None
212
-
213
-
214
- def _venv_python(venv):
215
- """venv 内 python 路径 (跨平台: win Scripts/python.exe, mac bin/python)。"""
216
- if sys.platform == 'win32':
217
- return venv / 'Scripts' / 'python.exe'
218
- return venv / 'bin' / 'python'
219
-
220
-
221
- def _read_current_developer(repo):
222
- """读 .qoder/.developer 当前角色。"""
223
- try:
224
- sys.path.insert(0, str(repo / '.qoder' / 'scripts'))
225
- from common.paths import get_developer
226
- return get_developer(repo)
227
- except Exception:
228
- return None
229
-
230
-
231
- def _read_lanhu_cookie(repo, dev):
232
- """读角色的蓝湖 cookie。"""
233
- try:
234
- sys.path.insert(0, str(repo / '.qoder' / 'scripts'))
235
- from setup_lanhu import _read_env_cookie, lanhu_env_file, COOKIE_PLACEHOLDER
236
- ck = _read_env_cookie(dev)
237
- return ck if (ck and ck != COOKIE_PLACEHOLDER) else None
238
- except Exception:
239
- return None
240
-
241
-
242
- def launch_kg(repo):
243
- """启动知识图谱 MCP。找装了 duckdb 的 Python (不是盲目用 sys.executable)。"""
244
- server = repo / '.qoder' / 'scripts' / 'mcp' / 'kg_mcp_server.py'
245
- if not server.is_file():
246
- _log(f'kg server 不存在: {server}')
247
- _launch_disabled_mcp('qoder-knowledge-graph',
248
- 'kg_mcp_server.py 缺失。跑 npx @hupan56/wlkj update')
249
- return
250
- py = _find_python_with_deps('duckdb')
251
- if not py:
252
- _log('⚠️ 找不到装了 duckdb 的 Python! 跑: pip install duckdb')
253
- _log(' 或: npx @hupan56/wlkj install-env (自动装依赖)')
254
- _launch_disabled_mcp('qoder-knowledge-graph',
255
- 'duckdb 未装。跑 npx @hupan56/wlkj install-env (自动装依赖)')
256
- return
257
- _log(f'启动 kg (python={py})')
258
- os.execv(py, [py, str(server)])
259
-
260
-
261
- def launch_mysql(repo):
262
- """启动 MySQL MCP。连接信息优先从 config.yaml 读 (团队共享, git 里有)。
263
- 回退: ~/.qoderwork/.mcp-mysql-env.json (旧版个人文件)。"""
264
- server = repo / '.qoder' / 'scripts' / 'mcp' / 'mysql_mcp_server.py' # v3.0: mcp子包
265
- if not server.is_file():
266
- _log(f'mysql server 不存在: {server}')
267
- _launch_disabled_mcp('qoder-mysql',
268
- 'mysql_mcp_server.py 缺失。跑 npx @hupan56/wlkj update')
269
- return
270
- # 优先: 从 config.yaml 读 (团队共享, clone 就有)
271
- cfg_path = repo / '.qoder' / 'config.yaml'
272
- mysql_cfg = {}
273
- if cfg_path.is_file():
274
- try:
275
- import yaml
276
- d = yaml.safe_load(cfg_path.read_text(encoding='utf-8')) or {}
277
- mysql_cfg = d.get('mysql', {}) or {}
278
- except Exception:
279
- pass
280
- if mysql_cfg:
281
- for k in ('host', 'port', 'user', 'password'):
282
- v = mysql_cfg.get(k)
283
- if v:
284
- os.environ['MYSQL_' + k.upper()] = str(v)
285
- if mysql_cfg.get('env_name'):
286
- os.environ['MYSQL_ENV_NAME'] = str(mysql_cfg['env_name'])
287
- _log(f'mysql env 从 config.yaml 注入 ({len(mysql_cfg)} 项)')
288
- else:
289
- # 回退: 旧版个人 env 文件 (~/.qoderwork/.mcp-mysql-env.json)
290
- env_file = _home() / '.qoderwork' / '.mcp-mysql-env.json'
291
- if env_file.is_file():
292
- try:
293
- cfg = json.loads(env_file.read_text(encoding='utf-8'))
294
- for k, v in cfg.items():
295
- if v:
296
- os.environ[k] = str(v)
297
- _log(f'mysql env 从个人文件注入 ({len(cfg)} 项)')
298
- except (json.JSONDecodeError, OSError):
299
- pass
300
- else:
301
- _log('⚠️ mysql 配置缺失: config.yaml 无 mysql 段, 且 ~/.qoderwork/.mcp-mysql-env.json 不存在')
302
- _log(f'启动 mysql')
303
- py = _find_python_with_deps('pymysql')
304
- if not py:
305
- _log('⚠️ 找不到装了 pymysql 的 Python! 跑: pip install pymysql')
306
- _launch_disabled_mcp('qoder-mysql',
307
- 'pymysql 未装。跑 pip install pymysql 或 npx @hupan56/wlkj install-env')
308
- return
309
- _log(f' python={py}')
310
- os.execv(py, [py, str(server)])
311
-
312
-
313
- ZENTAO_URL_DEFAULT = 'http://10.89.2.91/zentao'
314
-
315
-
316
- def launch_zentao(repo):
317
- """启动禅道 MCP。连接信息从 config.yaml 读(团队共享)。"""
318
- server = repo / '.qoder' / 'scripts' / 'mcp' / 'zentao_mcp_server.py' # v3.0: mcp子包
319
- if not server.is_file():
320
- _log(f'zentao server 不存在: {server}')
321
- _launch_disabled_mcp('qoder-zentao', 'zentao_mcp_server.py 缺失')
322
- return
323
- # 从 config.yaml 读禅道配置
324
- cfg_path = repo / '.qoder' / 'config.yaml'
325
- if cfg_path.is_file():
326
- try:
327
- import yaml
328
- d = yaml.safe_load(cfg_path.read_text(encoding='utf-8')) or {}
329
- zt = d.get('zentao', {}) or {}
330
- if zt:
331
- os.environ['ZENTAO_URL'] = str(zt.get('url', ZENTAO_URL_DEFAULT))
332
- os.environ['ZENTAO_USER'] = str(zt.get('user', ''))
333
- os.environ['ZENTAO_PASSWORD'] = str(zt.get('password', ''))
334
- _log(f'zentao env 从 config.yaml 注入')
335
- except Exception:
336
- pass
337
- _log(f'启动 zentao')
338
- py = _find_python_with_deps('requests')
339
- if not py:
340
- _log('⚠️ 找不到装了 requests 的 Python!')
341
- _launch_disabled_mcp('qoder-zentao', 'requests 未装。pip install requests')
342
- return
343
- _log(f' python={py}')
344
- os.execv(py, [py, str(server)])
345
-
346
-
347
- def launch_lanhu(repo):
348
- """启动蓝湖 MCP。用 venv 的 3.10+ python (fastmcp 要求), 读角色 cookie。
349
- 没装 lanhu venv 或没配 cookie 时, 输出友好提示并启动一个空壳 MCP
350
- (让 QoderWork 显示"未启用"而不是黄/报错)。"""
351
- venv = repo / 'external' / 'lanhu-mcp' / '.venv'
352
- server = repo / 'external' / 'lanhu-mcp' / 'lanhu_mcp_server.py'
353
-
354
- # 检查 venv
355
- if not venv.is_dir():
356
- _log('lanhu venv 未安装 — MCP 将以"未启用"模式启动')
357
- _launch_disabled_mcp('lanhu',
358
- '蓝湖 MCP 未安装。跑 python .qoder/scripts/setup_lanhu.py 安装')
359
- return
360
- py = _venv_python(venv)
361
- if not py.is_file():
362
- _log(f'lanhu venv python 不存在: {py}')
363
- _launch_disabled_mcp('lanhu', '蓝湖 MCP venv 异常, 重装: setup_lanhu.py')
364
- return
365
- if not server.is_file():
366
- _log(f'lanhu server 不存在: {server}')
367
- _launch_disabled_mcp('lanhu', '蓝湖 MCP server 文件缺失')
368
- return
369
-
370
- # 读角色 cookie
371
- dev = _read_current_developer(repo)
372
- if not dev:
373
- _log('未初始化开发者 (.qoder/.developer), 跑 /wl-init')
374
- _launch_disabled_mcp('lanhu', '未初始化开发者, 跑 /wl-init')
375
- return
376
- cookie = _read_lanhu_cookie(repo, dev)
377
- if not cookie:
378
- _log(f'角色 {dev} 的蓝湖 cookie 未配置 (占位/缺失)')
379
- _log(f' 编辑 workspace/members/{dev}/.secrets/lanhu.env')
380
- _launch_disabled_mcp('lanhu',
381
- f'蓝湖 cookie 未配置: workspace/members/{dev}/.secrets/lanhu.env')
382
- return
383
-
384
- # 注入 env (角色隔离: cookie + 用户名都是当前角色的)
385
- os.environ['LANHU_COOKIE'] = cookie
386
- os.environ['MCP_TRANSPORT'] = 'stdio'
387
- os.environ['LANHU_USER_NAME'] = dev or 'Developer'
388
- os.environ['LANHU_USER_ROLE'] = 'Developer'
389
- _log(f'启动 lanhu (角色={dev}, python={py})')
390
- os.execv(str(py), [str(py), str(server)])
391
-
392
-
393
- LAUNCHERS = {
394
- 'kg': launch_kg,
395
- 'knowledge-graph': launch_kg,
396
- 'mysql': launch_mysql,
397
- 'zentao': launch_zentao,
398
- 'lanhu': launch_lanhu,
399
- }
400
-
401
-
402
- def main():
403
- # ⚠️ 关键: 任何失败都走 _launch_disabled_mcp(), 绝不 sys.exit()。
404
- # sys.exit(1) 会让进程退出 → QoderWork 显示黄色(连接错误), 用户以为坏了。
405
- # _launch_disabled_mcp() 启动一个干净 stdio 循环响应 initialize →
406
- # QoderWork 显示绿色(已连接, 0工具), 用户知道是"未启用"而非"坏了"。
407
- if len(sys.argv) < 2:
408
- _log('用法: mcp_launcher.py <kg|mysql|zentao|lanhu>')
409
- _log(f' argv={sys.argv}')
410
- _launch_disabled_mcp('qoder-mcp',
411
- '启动参数缺失: mcp_launcher.py <kg|mysql|zentao|lanhu>')
412
- return
413
- name = sys.argv[1].lower()
414
-
415
- repo = find_repo_root()
416
- if not repo:
417
- _log('无法定位 QODER 仓库根。')
418
- _log(' 方式1: 设环境变量 QODER_REPO=<仓库根>')
419
- _log(' 方式2: 重跑 npx @hupan56/wlkj update (刷新 .repo-root)')
420
- _log(' 方式3: 在仓库目录内运行')
421
- _launch_disabled_mcp(name,
422
- '未定位到 QODER 仓库根。跑 npx @hupan56/wlkj update')
423
- return
424
- _log(f'仓库根: {repo}')
425
-
426
- fn = LAUNCHERS.get(name)
427
- if not fn:
428
- _log(f'未知 MCP 名: {name} (支持: {list(LAUNCHERS)})')
429
- _launch_disabled_mcp(name,
430
- '未知 MCP 名: %s (支持: %s)' % (name, ', '.join(LAUNCHERS)))
431
- return
432
- try:
433
- fn(repo)
434
- except Exception as e:
435
- # 启动函数抛异常也不能让进程死 (死=黄, 捕获=绿)
436
- _log(f'启动 {name} 时异常: {e}')
437
- _launch_disabled_mcp(name,
438
- '%s MCP 启动异常: %s。跑 npx @hupan56/wlkj update' % (name, e))
439
-
440
-
441
- if __name__ == '__main__':
442
- main()