@hupan56/wlkj 2.5.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 (127) hide show
  1. package/bin/cli.js +289 -12
  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-quick.md +1 -0
  6. package/templates/qoder/agents/prd-reference.md +10 -2
  7. package/templates/qoder/commands/optional/wl-insight.md +275 -0
  8. package/templates/qoder/commands/{wl-report.md → optional/wl-report.md} +13 -5
  9. package/templates/qoder/commands/{wl-spec.md → optional/wl-spec.md} +1 -1
  10. package/templates/qoder/commands/{wl-status.md → optional/wl-status.md} +28 -2
  11. package/templates/qoder/commands/wl-code.md +10 -2
  12. package/templates/qoder/commands/wl-commit.md +1 -1
  13. package/templates/qoder/commands/wl-design-draw.md +78 -0
  14. package/templates/qoder/commands/wl-design-scan.md +108 -0
  15. package/templates/qoder/commands/wl-design-spec.md +154 -0
  16. package/templates/qoder/commands/wl-design.md +32 -0
  17. package/templates/qoder/commands/wl-init.md +24 -3
  18. package/templates/qoder/commands/wl-prd-full.md +226 -0
  19. package/templates/qoder/commands/wl-prd-quick.md +134 -0
  20. package/templates/qoder/commands/wl-prd-review.md +104 -0
  21. package/templates/qoder/commands/wl-prd.md +17 -288
  22. package/templates/qoder/commands/wl-search.md +66 -30
  23. package/templates/qoder/commands/wl-task.md +290 -59
  24. package/templates/qoder/commands/wl-test.md +92 -24
  25. package/templates/qoder/config.yaml +59 -15
  26. package/templates/qoder/hooks/inject-workflow-state.py +35 -9
  27. package/templates/qoder/hooks/session-start.py +144 -62
  28. package/templates/qoder/rules/wl-pipeline.md +216 -105
  29. package/templates/qoder/scripts/__pycache__/search_index.cpython-39.pyc +0 -0
  30. package/templates/qoder/scripts/archive_prd.py +377 -0
  31. package/templates/qoder/scripts/autotest.py +1715 -0
  32. package/templates/qoder/scripts/autotest_batch.py +224 -0
  33. package/templates/qoder/scripts/autotest_run.py +297 -0
  34. package/templates/qoder/scripts/benchmark.py +210 -209
  35. package/templates/qoder/scripts/build_style_index.py +444 -4
  36. package/templates/qoder/scripts/check_carriers.py +238 -0
  37. package/templates/qoder/scripts/check_mcp.py +298 -0
  38. package/templates/qoder/scripts/check_qoderwork_consistency.py +166 -0
  39. package/templates/qoder/scripts/common/developer.py +26 -19
  40. package/templates/qoder/scripts/common/events.py +46 -0
  41. package/templates/qoder/scripts/common/extract.py +419 -0
  42. package/templates/qoder/scripts/common/graph_traverse.py +533 -0
  43. package/templates/qoder/scripts/common/identity.py +6 -1
  44. package/templates/qoder/scripts/common/paths.py +89 -0
  45. package/templates/qoder/scripts/common/pip_install.py +144 -0
  46. package/templates/qoder/scripts/common/platform_guard.py +61 -0
  47. package/templates/qoder/scripts/common/search_engine.py +205 -205
  48. package/templates/qoder/scripts/common/terms.py +57 -0
  49. package/templates/qoder/scripts/common/ts_extract.py +536 -0
  50. package/templates/qoder/scripts/context_pack.py +73 -13
  51. package/templates/qoder/scripts/enrich_prompt.py +226 -0
  52. package/templates/qoder/scripts/eval_prd.py +318 -225
  53. package/templates/qoder/scripts/export.py +487 -487
  54. package/templates/qoder/scripts/extract_api_params.py +246 -0
  55. package/templates/qoder/scripts/extract_routes.py +54 -0
  56. package/templates/qoder/scripts/extract_routes_tree.py +78 -0
  57. package/templates/qoder/scripts/fill_prototype.py +707 -0
  58. package/templates/qoder/scripts/gen_design_doc.py +394 -0
  59. package/templates/qoder/scripts/git_sync.py +27 -15
  60. package/templates/qoder/scripts/init_doctor.py +306 -41
  61. package/templates/qoder/scripts/install_qoderwork.py +366 -9
  62. package/templates/qoder/scripts/kg.py +708 -0
  63. package/templates/qoder/scripts/kg_auto_login.py +196 -0
  64. package/templates/qoder/scripts/kg_build.py +612 -0
  65. package/templates/qoder/scripts/kg_build_db.py +327 -0
  66. package/templates/qoder/scripts/kg_duckdb.py +549 -0
  67. package/templates/qoder/scripts/kg_incremental.py +393 -0
  68. package/templates/qoder/scripts/kg_link_db.py +224 -0
  69. package/templates/qoder/scripts/kg_mcp_server.py +801 -0
  70. package/templates/qoder/scripts/kg_semantic.py +150 -0
  71. package/templates/qoder/scripts/kg_test_runner.py +241 -0
  72. package/templates/qoder/scripts/lanhu_stdio_wrapper.py +119 -0
  73. package/templates/qoder/scripts/learn.py +118 -39
  74. package/templates/qoder/scripts/learn_aggregate.py +201 -0
  75. package/templates/qoder/scripts/mcp_launcher.py +359 -0
  76. package/templates/qoder/scripts/mysql_mcp_server.py +396 -0
  77. package/templates/qoder/scripts/repo_root.py +106 -0
  78. package/templates/qoder/scripts/role.py +12 -0
  79. package/templates/qoder/scripts/run_weekly_update.bat +5 -0
  80. package/templates/qoder/scripts/run_weekly_update.sh +5 -0
  81. package/templates/qoder/scripts/search_index.py +307 -60
  82. package/templates/qoder/scripts/secure-ls.js +5640 -0
  83. package/templates/qoder/scripts/setup.py +706 -453
  84. package/templates/qoder/scripts/setup_lanhu.py +963 -0
  85. package/templates/qoder/scripts/status.py +250 -11
  86. package/templates/qoder/scripts/sync_carriers.py +259 -0
  87. package/templates/qoder/scripts/syncgate.py +5 -4
  88. package/templates/qoder/scripts/task.py +75 -0
  89. package/templates/qoder/scripts/team_sync.py +60 -4
  90. package/templates/qoder/scripts/workspace_init.py +1 -1
  91. package/templates/qoder/skills/design-import/SKILL.md +226 -0
  92. package/templates/qoder/skills/design-import/figma-workflow.md +81 -0
  93. package/templates/qoder/skills/design-review/SKILL.md +82 -25
  94. package/templates/qoder/skills/prd-generator/SKILL.md +185 -58
  95. package/templates/qoder/skills/prd-review/SKILL.md +18 -1
  96. package/templates/qoder/skills/prompt-enrich/SKILL.md +90 -0
  97. package/templates/qoder/skills/prototype-generator/SKILL.md +256 -141
  98. package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
  99. package/templates/qoder/skills/spec-coder/SKILL.md +18 -1
  100. package/templates/qoder/skills/spec-generator/SKILL.md +18 -1
  101. package/templates/qoder/skills/test-generator/SKILL.md +15 -2
  102. package/templates/qoder/skills/wl-code/SKILL.md +55 -36
  103. package/templates/qoder/skills/wl-commit/SKILL.md +89 -76
  104. package/templates/qoder/skills/wl-design/SKILL.md +55 -0
  105. package/templates/qoder/skills/wl-init/SKILL.md +76 -67
  106. package/templates/qoder/skills/wl-insight/SKILL.md +201 -81
  107. package/templates/qoder/skills/wl-prd-full/SKILL.md +69 -0
  108. package/templates/qoder/skills/wl-prd-quick/SKILL.md +49 -0
  109. package/templates/qoder/skills/wl-prd-review/SKILL.md +34 -0
  110. package/templates/qoder/skills/wl-report/SKILL.md +131 -107
  111. package/templates/qoder/skills/wl-search/SKILL.md +141 -75
  112. package/templates/qoder/skills/wl-spec/SKILL.md +49 -39
  113. package/templates/qoder/skills/wl-status/SKILL.md +83 -61
  114. package/templates/qoder/skills/wl-task/SKILL.md +132 -58
  115. package/templates/qoder/skills/wl-test/SKILL.md +406 -40
  116. package/templates/qoder/templates/prd-full-template.md +2 -0
  117. package/templates/qoder/templates/prd-quick-template.md +1 -0
  118. package/templates/qoder/templates/prototype-app.html +13 -8
  119. package/templates/qoder/templates/prototype-web.html +376 -93
  120. package/templates/root/AGENTS.md +89 -34
  121. package/templates/root/requirements.txt +21 -0
  122. package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +259 -259
  123. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +186 -186
  124. package/templates/qoder/agents/prd-planning.md +0 -56
  125. package/templates/qoder/agents/prd-research.md +0 -33
  126. package/templates/qoder/commands/wl-insight.md +0 -51
  127. package/templates/qoder/skills/wl-prd/SKILL.md +0 -89
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ mcp_launcher.py - 统一 MCP 启动器(跨 Win/Mac/Linux,消除写死路径)
5
+
6
+ 被谁调用:
7
+ QoderWork 按 ~/.qoderwork/mcp.json 的 command 拉起本脚本。
8
+ mcp.json 的 args = [<本脚本路径>, <MCP名>]
9
+ 例: {"command": "py", "args": ["C:/Users/HP/.qoderwork/mcp_launcher.py", "kg"]}
10
+
11
+ 为什么需要它:
12
+ 之前 mcp.json 写死绝对路径 (E:\\...\\python.exe + 仓库路径 + venv路径),
13
+ 换电脑/换系统/别人 clone 全废。本 launcher 运行时自定位仓库根 + 找对的 python,
14
+ mcp.json 只需写 python 命令名 (py/python3) + 本脚本路径 (在 ~/.qoderwork/)。
15
+
16
+ 做什么:
17
+ 1. 解析 argv[1] = MCP 名 (kg / mysql / lanhu)
18
+ 2. find_repo_root(): 环境变量 QODER_REPO > ~/.qoderwork/.repo-root 文件 > 向上找 .qoder/
19
+ 3. 按 MCP 名选 python + server 脚本:
20
+ - kg: sys.executable, <repo>/.qoder/scripts/kg_mcp_server.py
21
+ - mysql: sys.executable, <repo>/.qoder/scripts/mysql_mcp_server.py (env 从 .mcp-mysql-env.json 读)
22
+ - lanhu: <repo>/external/lanhu-mcp/.venv python (3.10+), 复用 lanhu_stdio_wrapper 逻辑 (读角色 cookie)
23
+ 4. os.execv 启动 (接管进程, QoderWork 管生死)
24
+
25
+ 跨平台:
26
+ find_repo_root 用 os.path, venv python 用 _venv_python (win Scripts/mac bin)。
27
+ 本脚本被 python3/py/python 跑都行 (它只做定位+execv, 不依赖特定版本)。
28
+ """
29
+
30
+ import json
31
+ import os
32
+ import sys
33
+ from pathlib import Path
34
+
35
+ try:
36
+ sys.stderr.reconfigure(encoding='utf-8', errors='replace')
37
+ except (AttributeError, TypeError, OSError):
38
+ pass
39
+
40
+
41
+ def _home():
42
+ """跨平台 home 目录。"""
43
+ return Path(os.environ.get('USERPROFILE') or os.path.expanduser('~'))
44
+
45
+
46
+ def _log(msg):
47
+ """调试日志到 stderr (MCP stdio 用 stdout, 不能污染)。"""
48
+ sys.stderr.write('[mcp-launcher] ' + msg + '\n')
49
+
50
+
51
+ def _launch_disabled_mcp(name, reason):
52
+ """没装/没配时, 启动一个空壳 MCP (只回 initialize + tools/list 空)。
53
+ 让 QoderWork 显示绿(已连接)但无工具, 而不是黄(报错)。
54
+ reason 写进 serverInfo 让用户知道为什么没工具。"""
55
+ import json as _j
56
+ _log(f'{name} MCP 以"未启用"模式启动: {reason}')
57
+ # 简单的 stdio 循环, 只响应 initialize 和 tools/list
58
+ while True:
59
+ try:
60
+ line = sys.stdin.buffer.readline()
61
+ except (EOFError, OSError):
62
+ break
63
+ if not line:
64
+ break
65
+ try:
66
+ req = _j.loads(line.decode('utf-8'))
67
+ except Exception:
68
+ continue
69
+ method = req.get('method', '')
70
+ rid = req.get('id')
71
+ if method == 'initialize':
72
+ sys.stdout.write(_j.dumps({
73
+ "jsonrpc": "2.0", "id": rid,
74
+ "result": {
75
+ "protocolVersion": "2024-11-05",
76
+ "capabilities": {"tools": {}},
77
+ "serverInfo": {"name": name, "version": "0.0.0-disabled",
78
+ "reason": reason}
79
+ }
80
+ }) + '\n')
81
+ sys.stdout.flush()
82
+ elif method == 'tools/list':
83
+ sys.stdout.write(_j.dumps({
84
+ "jsonrpc": "2.0", "id": rid,
85
+ "result": {"tools": []}
86
+ }) + '\n')
87
+ sys.stdout.flush()
88
+ elif method == 'notifications/initialized':
89
+ pass
90
+ elif rid is not None:
91
+ sys.stdout.write(_j.dumps({
92
+ "jsonrpc": "2.0", "id": rid,
93
+ "error": {"code": -32601, "message": reason}
94
+ }) + '\n')
95
+ sys.stdout.flush()
96
+
97
+
98
+ def _find_python_with_deps(required_module='duckdb'):
99
+ """找一个装了指定依赖的 Python。优先级:
100
+ 1. sys.executable (当前 Python, 先测它有没有)
101
+ 2. 常见 Python 路径 (anaconda/python3等) 逐个测
102
+ 返回 python 路径(str) 或 None。
103
+ 解决"py -3 选的 Python 没装 duckdb"问题。
104
+ """
105
+ # 1. 当前 Python 有没有
106
+ try:
107
+ __import__(required_module)
108
+ return sys.executable
109
+ except ImportError:
110
+ pass
111
+ # 2. 找其它 Python
112
+ import shutil
113
+ candidates = []
114
+ for name in ('python', 'python3', 'py'):
115
+ p = shutil.which(name)
116
+ if p and p != sys.executable:
117
+ candidates.append(p)
118
+ # 常见安装路径 (Windows)
119
+ if sys.platform == 'win32':
120
+ for p in [
121
+ os.path.join(os.environ.get('USERPROFILE', ''), 'Anaconda3', 'python.exe'),
122
+ os.path.join(os.environ.get('USERPROFILE', ''), 'anaconda3', 'python.exe'),
123
+ os.path.join(os.environ.get('USERPROFILE', ''), 'miniconda3', 'python.exe'),
124
+ r'C:\ProgramData\Anaconda3\python.exe',
125
+ ]:
126
+ if os.path.isfile(p) and p not in candidates:
127
+ candidates.append(p)
128
+ for py in candidates:
129
+ try:
130
+ r = __import__('subprocess').run(
131
+ [py, '-c', 'import %s' % required_module],
132
+ capture_output=True, timeout=5)
133
+ if r.returncode == 0:
134
+ _log(f'找到装了 {required_module} 的 Python: {py}')
135
+ return py
136
+ except Exception:
137
+ continue
138
+ return None
139
+
140
+
141
+ def find_repo_root():
142
+ """定位 QODER 仓库根。优先级:
143
+ 1. 环境变量 QODER_REPO (用户显式设, 最可靠)
144
+ 2. ~/.qoderwork/.repo-root 文件 (install 时写)
145
+ 3. 向上找 .qoder/scripts/ (cwd 在仓库内, IDE/CLI 场景)
146
+ 4. 本脚本若在 <repo>/.qoder/scripts/ 下 → 上三级 (开发态)
147
+ 返回 Path 或 None。
148
+ """
149
+ # 1. 环境变量
150
+ env_repo = os.environ.get('QODER_REPO')
151
+ if env_repo and (Path(env_repo) / '.qoder' / 'scripts').is_dir():
152
+ return Path(env_repo)
153
+
154
+ # 2. ~/.qoderwork/.repo-root 文件 (install 写的, 主路径)
155
+ anchor = _home() / '.qoderwork' / '.repo-root'
156
+ if anchor.is_file():
157
+ try:
158
+ line = anchor.read_text(encoding='utf-8').strip()
159
+ if line and (Path(line) / '.qoder' / 'scripts').is_dir():
160
+ return Path(line)
161
+ except OSError:
162
+ pass
163
+
164
+ # 3. 向上找 .qoder/scripts/
165
+ here = Path(os.getcwd()).resolve()
166
+ for _ in range(8):
167
+ if (here / '.qoder' / 'scripts').is_dir():
168
+ return here
169
+ parent = here.parent
170
+ if parent == here:
171
+ break
172
+ here = parent
173
+
174
+ # 4. 本脚本位置 (开发态: 在 <repo>/.qoder/scripts/mcp_launcher.py)
175
+ try:
176
+ s = Path(__file__).resolve()
177
+ root = s.parent.parent.parent # scripts/ -> .qoder/ -> repo
178
+ if (root / '.qoder' / 'scripts').is_dir():
179
+ return root
180
+ except Exception:
181
+ pass
182
+
183
+ return None
184
+
185
+
186
+ def _venv_python(venv):
187
+ """venv 内 python 路径 (跨平台: win Scripts/python.exe, mac bin/python)。"""
188
+ if sys.platform == 'win32':
189
+ return venv / 'Scripts' / 'python.exe'
190
+ return venv / 'bin' / 'python'
191
+
192
+
193
+ def _read_current_developer(repo):
194
+ """读 .qoder/.developer 当前角色。"""
195
+ try:
196
+ sys.path.insert(0, str(repo / '.qoder' / 'scripts'))
197
+ from common.paths import get_developer
198
+ return get_developer(repo)
199
+ except Exception:
200
+ return None
201
+
202
+
203
+ def _read_lanhu_cookie(repo, dev):
204
+ """读角色的蓝湖 cookie。"""
205
+ try:
206
+ sys.path.insert(0, str(repo / '.qoder' / 'scripts'))
207
+ from setup_lanhu import _read_env_cookie, lanhu_env_file, COOKIE_PLACEHOLDER
208
+ ck = _read_env_cookie(dev)
209
+ return ck if (ck and ck != COOKIE_PLACEHOLDER) else None
210
+ except Exception:
211
+ return None
212
+
213
+
214
+ def launch_kg(repo):
215
+ """启动知识图谱 MCP。找装了 duckdb 的 Python (不是盲目用 sys.executable)。"""
216
+ server = repo / '.qoder' / 'scripts' / 'kg_mcp_server.py'
217
+ if not server.is_file():
218
+ _log(f'kg server 不存在: {server}')
219
+ sys.exit(1)
220
+ py = _find_python_with_deps('duckdb')
221
+ if not py:
222
+ _log('⚠️ 找不到装了 duckdb 的 Python! 跑: pip install duckdb')
223
+ _log(' 或: npx @hupan56/wlkj install-env (自动装依赖)')
224
+ _launch_disabled_mcp('qoder-knowledge-graph',
225
+ 'duckdb 未装。跑 pip install duckdb 或 npx @hupan56/wlkj install-env')
226
+ return
227
+ _log(f'启动 kg (python={py})')
228
+ os.execv(py, [py, str(server)])
229
+
230
+
231
+ def launch_mysql(repo):
232
+ """启动 MySQL MCP。连接信息优先从 config.yaml 读 (团队共享, git 里有)。
233
+ 回退: ~/.qoderwork/.mcp-mysql-env.json (旧版个人文件)。"""
234
+ server = repo / '.qoder' / 'scripts' / 'mysql_mcp_server.py'
235
+ if not server.is_file():
236
+ _log(f'mysql server 不存在: {server}')
237
+ sys.exit(1)
238
+ # 优先: 从 config.yaml 读 (团队共享, clone 就有)
239
+ cfg_path = repo / '.qoder' / 'config.yaml'
240
+ mysql_cfg = {}
241
+ if cfg_path.is_file():
242
+ try:
243
+ import yaml
244
+ d = yaml.safe_load(cfg_path.read_text(encoding='utf-8')) or {}
245
+ mysql_cfg = d.get('mysql', {}) or {}
246
+ except Exception:
247
+ pass
248
+ if mysql_cfg:
249
+ for k in ('host', 'port', 'user', 'password'):
250
+ v = mysql_cfg.get(k)
251
+ if v:
252
+ os.environ['MYSQL_' + k.upper()] = str(v)
253
+ if mysql_cfg.get('env_name'):
254
+ os.environ['MYSQL_ENV_NAME'] = str(mysql_cfg['env_name'])
255
+ _log(f'mysql env 从 config.yaml 注入 ({len(mysql_cfg)} 项)')
256
+ else:
257
+ # 回退: 旧版个人 env 文件 (~/.qoderwork/.mcp-mysql-env.json)
258
+ env_file = _home() / '.qoderwork' / '.mcp-mysql-env.json'
259
+ if env_file.is_file():
260
+ try:
261
+ cfg = json.loads(env_file.read_text(encoding='utf-8'))
262
+ for k, v in cfg.items():
263
+ if v:
264
+ os.environ[k] = str(v)
265
+ _log(f'mysql env 从个人文件注入 ({len(cfg)} 项)')
266
+ except (json.JSONDecodeError, OSError):
267
+ pass
268
+ else:
269
+ _log('⚠️ mysql 配置缺失: config.yaml 无 mysql 段, 且 ~/.qoderwork/.mcp-mysql-env.json 不存在')
270
+ _log(f'启动 mysql')
271
+ py = _find_python_with_deps('pymysql')
272
+ if not py:
273
+ _log('⚠️ 找不到装了 pymysql 的 Python! 跑: pip install pymysql')
274
+ _launch_disabled_mcp('qoder-mysql',
275
+ 'pymysql 未装。跑 pip install pymysql 或 npx @hupan56/wlkj install-env')
276
+ return
277
+ _log(f' python={py}')
278
+ os.execv(py, [py, str(server)])
279
+
280
+
281
+ def launch_lanhu(repo):
282
+ """启动蓝湖 MCP。用 venv 的 3.10+ python (fastmcp 要求), 读角色 cookie。
283
+ 没装 lanhu venv 或没配 cookie 时, 输出友好提示并启动一个空壳 MCP
284
+ (让 QoderWork 显示"未启用"而不是黄/报错)。"""
285
+ venv = repo / 'external' / 'lanhu-mcp' / '.venv'
286
+ server = repo / 'external' / 'lanhu-mcp' / 'lanhu_mcp_server.py'
287
+
288
+ # 检查 venv
289
+ if not venv.is_dir():
290
+ _log('lanhu venv 未安装 — MCP 将以"未启用"模式启动')
291
+ _launch_disabled_mcp('lanhu',
292
+ '蓝湖 MCP 未安装。跑 python .qoder/scripts/setup_lanhu.py 安装')
293
+ return
294
+ py = _venv_python(venv)
295
+ if not py.is_file():
296
+ _log(f'lanhu venv python 不存在: {py}')
297
+ _launch_disabled_mcp('lanhu', '蓝湖 MCP venv 异常, 重装: setup_lanhu.py')
298
+ return
299
+ if not server.is_file():
300
+ _log(f'lanhu server 不存在: {server}')
301
+ _launch_disabled_mcp('lanhu', '蓝湖 MCP server 文件缺失')
302
+ return
303
+
304
+ # 读角色 cookie
305
+ dev = _read_current_developer(repo)
306
+ if not dev:
307
+ _log('未初始化开发者 (.qoder/.developer), 跑 /wl-init')
308
+ _launch_disabled_mcp('lanhu', '未初始化开发者, 跑 /wl-init')
309
+ return
310
+ cookie = _read_lanhu_cookie(repo, dev)
311
+ if not cookie:
312
+ _log(f'角色 {dev} 的蓝湖 cookie 未配置 (占位/缺失)')
313
+ _log(f' 编辑 workspace/members/{dev}/.secrets/lanhu.env')
314
+ _launch_disabled_mcp('lanhu',
315
+ f'蓝湖 cookie 未配置: workspace/members/{dev}/.secrets/lanhu.env')
316
+ return
317
+
318
+ # 注入 env (角色隔离: cookie + 用户名都是当前角色的)
319
+ os.environ['LANHU_COOKIE'] = cookie
320
+ os.environ['MCP_TRANSPORT'] = 'stdio'
321
+ os.environ['LANHU_USER_NAME'] = dev or 'Developer'
322
+ os.environ['LANHU_USER_ROLE'] = 'Developer'
323
+ _log(f'启动 lanhu (角色={dev}, python={py})')
324
+ os.execv(str(py), [str(py), str(server)])
325
+
326
+
327
+ LAUNCHERS = {
328
+ 'kg': launch_kg,
329
+ 'knowledge-graph': launch_kg,
330
+ 'mysql': launch_mysql,
331
+ 'lanhu': launch_lanhu,
332
+ }
333
+
334
+
335
+ def main():
336
+ if len(sys.argv) < 2:
337
+ _log('用法: mcp_launcher.py <kg|mysql|lanhu>')
338
+ _log(f' argv={sys.argv}')
339
+ sys.exit(1)
340
+ name = sys.argv[1].lower()
341
+
342
+ repo = find_repo_root()
343
+ if not repo:
344
+ _log('无法定位 QODER 仓库根。')
345
+ _log(' 方式1: 设环境变量 QODER_REPO=<仓库根>')
346
+ _log(' 方式2: 重跑 python .qoder/scripts/install_qoderwork.py (写 .repo-root)')
347
+ _log(' 方式3: 在仓库目录内运行')
348
+ sys.exit(1)
349
+ _log(f'仓库根: {repo}')
350
+
351
+ fn = LAUNCHERS.get(name)
352
+ if not fn:
353
+ _log(f'未知 MCP 名: {name} (支持: {list(LAUNCHERS)})')
354
+ sys.exit(1)
355
+ fn(repo)
356
+
357
+
358
+ if __name__ == '__main__':
359
+ main()