@hupan56/wlkj 2.7.11 → 3.0.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 (152) hide show
  1. package/bin/cli.js +375 -78
  2. package/package.json +29 -29
  3. package/templates/.qoder/.runtime/ctx-cache-5660152f1d6dd819.md +23 -0
  4. package/templates/.qoder/.runtime/ctx-cache-afdce0dac06b25b0.md +23 -0
  5. package/templates/.qoder/.runtime/search-cache-eae7644e7b122f35.txt +1 -0
  6. package/templates/.qoder/learning/eval-history.jsonl +28 -0
  7. package/templates/data/index/wiki-index.json +8 -0
  8. package/templates/qoder/agents/insight-planning.md +1 -1
  9. package/templates/qoder/agents/insight-research.md +28 -15
  10. package/templates/qoder/commands/optional/wl-insight.md +159 -161
  11. package/templates/qoder/commands/optional/wl-report.md +4 -4
  12. package/templates/qoder/commands/optional/wl-status.md +2 -2
  13. package/templates/qoder/commands/wl-code.md +2 -2
  14. package/templates/qoder/commands/wl-design.md +2 -2
  15. package/templates/qoder/commands/wl-init.md +3 -3
  16. package/templates/qoder/commands/wl-prd.md +2 -2
  17. package/templates/qoder/commands/wl-req.md +43 -0
  18. package/templates/qoder/commands/wl-search.md +8 -8
  19. package/templates/qoder/commands/wl-task.md +17 -17
  20. package/templates/qoder/commands/wl-test.md +41 -15
  21. package/templates/qoder/config.yaml +17 -1
  22. package/templates/qoder/hooks/session-start.py +12 -22
  23. package/templates/qoder/nul +4 -0
  24. package/templates/qoder/rules/wl-pipeline.md +22 -48
  25. package/templates/qoder/scripts/README.md +139 -0
  26. package/templates/qoder/scripts/common/autotest_auth.py +109 -0
  27. package/templates/qoder/scripts/common/bootstrap.py +145 -0
  28. package/templates/qoder/scripts/common/check_publish.py +98 -0
  29. package/templates/qoder/scripts/common/cmd_registry.py +112 -0
  30. package/templates/qoder/scripts/common/config.py +187 -0
  31. package/templates/qoder/scripts/common/contract.py +317 -0
  32. package/templates/qoder/scripts/common/developer.py +2 -1
  33. package/templates/qoder/scripts/common/feishu.py +10 -9
  34. package/templates/qoder/scripts/common/guard.py +159 -0
  35. package/templates/qoder/scripts/common/identity.py +121 -2
  36. package/templates/qoder/scripts/common/kg_capabilities.py +182 -0
  37. package/templates/qoder/scripts/common/mcp_base.py +268 -0
  38. package/templates/qoder/scripts/common/paths.py +187 -1
  39. package/templates/qoder/scripts/common/result.py +223 -0
  40. package/templates/qoder/scripts/common/roles.py +60 -0
  41. package/templates/qoder/scripts/common/task_utils.py +21 -9
  42. package/templates/qoder/scripts/common/test_extract.py +115 -0
  43. package/templates/qoder/scripts/kg/__init__.py +11 -0
  44. package/templates/qoder/scripts/kg/build_entity_registry.py +196 -0
  45. package/templates/qoder/scripts/kg/build_relations.py +127 -0
  46. package/templates/qoder/scripts/{build_style_index.py → kg/build_style_index.py} +39 -10
  47. package/templates/qoder/scripts/kg/build_workflows.py +144 -0
  48. package/templates/qoder/scripts/{context_pack.py → kg/context_pack.py} +18 -11
  49. package/templates/qoder/scripts/{enrich_prompt.py → kg/enrich_prompt.py} +232 -226
  50. package/templates/qoder/scripts/{extract_api_params.py → kg/extract.py} +398 -246
  51. package/templates/qoder/scripts/{kg.py → kg/kg.py} +638 -708
  52. package/templates/qoder/scripts/{kg_build.py → kg/kg_build.py} +618 -612
  53. package/templates/qoder/scripts/{kg_build_db.py → kg/kg_build_db.py} +333 -327
  54. package/templates/qoder/scripts/{kg_duckdb.py → kg/kg_duckdb.py} +38 -37
  55. package/templates/qoder/scripts/{kg_incremental.py → kg/kg_incremental.py} +420 -393
  56. package/templates/qoder/scripts/{kg_link_db.py → kg/kg_link_db.py} +230 -224
  57. package/templates/qoder/scripts/{kg_semantic.py → kg/kg_semantic.py} +156 -150
  58. package/templates/qoder/scripts/kg/prefetch.py +359 -0
  59. package/templates/qoder/scripts/{search_index.py → kg/search_index.py} +70 -14
  60. package/templates/qoder/scripts/mcp/__init__.py +11 -0
  61. package/templates/qoder/scripts/{kg_mcp_server.py → mcp/kg_mcp_server.py} +77 -272
  62. package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → mcp/lanhu_stdio_wrapper.py} +125 -119
  63. package/templates/qoder/scripts/{check_mcp.py → mcp/mcp_doctor.py} +515 -298
  64. package/templates/qoder/scripts/{mcp_launcher.py → mcp/mcp_launcher.py} +442 -414
  65. package/templates/qoder/scripts/{mysql_mcp_server.py → mcp/mysql_mcp_server.py} +347 -396
  66. package/templates/qoder/scripts/{zentao_mcp_server.py → mcp/zentao_mcp_server.py} +384 -424
  67. package/templates/qoder/scripts/report/__init__.py +11 -0
  68. package/templates/qoder/scripts/{add_session.py → report/add_session.py} +250 -244
  69. package/templates/qoder/scripts/{archive_prd.py → report/archive_prd.py} +383 -377
  70. package/templates/qoder/scripts/{eval_prd.py → report/eval_prd.py} +73 -11
  71. package/templates/qoder/scripts/{export.py → report/export.py} +63 -0
  72. package/templates/qoder/scripts/{fill_prototype.py → report/fill_prototype.py} +6 -0
  73. package/templates/qoder/scripts/{gen_design_doc.py → report/gen_design_doc.py} +400 -394
  74. package/templates/qoder/scripts/{learn.py → report/learn.py} +152 -146
  75. package/templates/qoder/scripts/{learn_aggregate.py → report/learn_aggregate.py} +207 -201
  76. package/templates/qoder/scripts/{report.py → report/report.py} +287 -281
  77. package/templates/qoder/scripts/report/req.py +222 -0
  78. package/templates/qoder/scripts/report/role.py +33 -0
  79. package/templates/qoder/scripts/{status.py → report/status.py} +634 -628
  80. package/templates/qoder/scripts/setup/__init__.py +11 -0
  81. package/templates/qoder/scripts/setup/carriers.py +662 -0
  82. package/templates/qoder/scripts/{init_doctor.py → setup/init_doctor.py} +63 -26
  83. package/templates/qoder/scripts/{install_qoderwork.py → setup/install_qoderwork.py} +36 -26
  84. package/templates/qoder/scripts/{platform_doctor.py → setup/platform_doctor.py} +265 -259
  85. package/templates/qoder/scripts/{repo_root.py → setup/repo_root.py} +112 -106
  86. package/templates/qoder/scripts/{setup.py → setup/setup.py} +147 -0
  87. package/templates/qoder/scripts/{setup_lanhu.py → setup/setup_lanhu.py} +973 -963
  88. package/templates/qoder/scripts/task/__init__.py +11 -0
  89. package/templates/qoder/scripts/{git_sync.py → task/git_sync.py} +52 -31
  90. package/templates/qoder/scripts/{syncgate.py → task/syncgate.py} +6 -0
  91. package/templates/qoder/scripts/task/task.py +221 -0
  92. package/templates/qoder/scripts/task/task_lifecycle.py +596 -0
  93. package/templates/qoder/scripts/task/task_query.py +161 -0
  94. package/templates/qoder/scripts/task/task_relations.py +424 -0
  95. package/templates/qoder/scripts/{team_sync.py → task/team_sync.py} +93 -20
  96. package/templates/qoder/scripts/test/__init__.py +11 -0
  97. package/templates/qoder/scripts/{autotest.py → test/autotest.py} +1174 -1751
  98. package/templates/qoder/scripts/{autotest_batch.py → test/autotest_batch.py} +242 -224
  99. package/templates/qoder/scripts/test/autotest_data.py +675 -0
  100. package/templates/qoder/scripts/{autotest_run.py → test/autotest_run.py} +309 -297
  101. package/templates/qoder/scripts/{benchmark.py → test/benchmark.py} +6 -0
  102. package/templates/qoder/scripts/{kg_auto_login.py → test/kg_auto_login.py} +202 -196
  103. package/templates/qoder/scripts/{kg_test_runner.py → test/kg_test_runner.py} +7 -1
  104. package/templates/qoder/scripts/{page_probe.py → test/page_probe.py} +465 -459
  105. package/templates/qoder/scripts/wlkj.py +116 -0
  106. package/templates/qoder/settings.json +1 -10
  107. package/templates/qoder/skills/design-import/SKILL.md +226 -226
  108. package/templates/qoder/skills/design-review/SKILL.md +82 -82
  109. package/templates/qoder/skills/prd-generator/SKILL.md +26 -16
  110. package/templates/qoder/skills/prd-review/SKILL.md +5 -5
  111. package/templates/qoder/skills/prototype-generator/SKILL.md +256 -256
  112. package/templates/qoder/skills/spec-coder/SKILL.md +4 -4
  113. package/templates/qoder/skills/spec-generator/SKILL.md +4 -4
  114. package/templates/qoder/skills/test-generator/SKILL.md +5 -5
  115. package/templates/qoder/skills/wl-code/SKILL.md +4 -4
  116. package/templates/qoder/skills/wl-commit/SKILL.md +4 -4
  117. package/templates/qoder/skills/wl-design/SKILL.md +3 -3
  118. package/templates/qoder/skills/wl-init/SKILL.md +8 -8
  119. package/templates/qoder/skills/wl-insight/SKILL.md +5 -5
  120. package/templates/qoder/skills/wl-prd-full/SKILL.md +6 -6
  121. package/templates/qoder/skills/wl-prd-quick/SKILL.md +6 -6
  122. package/templates/qoder/skills/wl-prd-review/SKILL.md +4 -4
  123. package/templates/qoder/skills/wl-report/SKILL.md +7 -7
  124. package/templates/qoder/skills/wl-search/SKILL.md +13 -13
  125. package/templates/qoder/skills/wl-spec/SKILL.md +5 -5
  126. package/templates/qoder/skills/wl-status/SKILL.md +5 -5
  127. package/templates/qoder/skills/wl-task/SKILL.md +6 -6
  128. package/templates/qoder/skills/wl-test/SKILL.md +102 -39
  129. package/templates/root/AGENTS.md +39 -40
  130. package/templates/qoder/hooks/inject-workflow-state.py +0 -169
  131. package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
  132. package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
  133. package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
  134. package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
  135. package/templates/qoder/scripts/check_carriers.py +0 -238
  136. package/templates/qoder/scripts/check_mcp_launch.py +0 -183
  137. package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
  138. package/templates/qoder/scripts/collect_prds.py +0 -31
  139. package/templates/qoder/scripts/common/mentions.py +0 -134
  140. package/templates/qoder/scripts/common/utf8.py +0 -38
  141. package/templates/qoder/scripts/extract_routes.py +0 -54
  142. package/templates/qoder/scripts/extract_routes_tree.py +0 -78
  143. package/templates/qoder/scripts/handoff.py +0 -22
  144. package/templates/qoder/scripts/init_developer.py +0 -76
  145. package/templates/qoder/scripts/parse_prds.py +0 -33
  146. package/templates/qoder/scripts/role.py +0 -51
  147. package/templates/qoder/scripts/sync_carriers.py +0 -259
  148. package/templates/qoder/scripts/task.py +0 -1261
  149. package/templates/qoder/scripts/workspace_init.py +0 -102
  150. package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
  151. package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
  152. /package/templates/qoder/scripts/{secure-ls.js → test/secure-ls.js} +0 -0
@@ -1,5 +1,11 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
+ # v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
4
+ import os as _o, sys as _s
5
+ _cp = _o.path.join(_o.path.dirname(_o.path.dirname(_o.path.abspath(__file__))), 'common')
6
+ if _cp not in _s.path: _s.path.insert(0, _cp)
7
+ from bootstrap import setup; setup()
8
+
3
9
  """
4
10
  QODER Init Doctor - 幂等的一站式环境体检 + 按需修复
5
11
 
@@ -44,7 +50,7 @@ except (AttributeError, TypeError, OSError, IOError):
44
50
  except Exception:
45
51
  pass
46
52
 
47
- SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
53
+ SCRIPTS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 子包→scripts根
48
54
  sys.path.insert(0, SCRIPTS_DIR)
49
55
 
50
56
  from common.paths import get_repo_root, get_developer, get_developer_info
@@ -76,22 +82,37 @@ def problem(msg, hint=None):
76
82
 
77
83
 
78
84
  def run_script(script, *args, timeout=1800):
79
- """跑同目录下的另一个脚本, 实时透传输出"""
80
- cmd = [sys.executable, os.path.join(SCRIPTS_DIR, script)] + list(args)
85
+ """跑另一个脚本, 实时透传输出。
86
+ v3.0: 支持子目录化 先在 scripts/根找, 找不到扫各子包。"""
87
+ # 先试 scripts/根
88
+ cand = os.path.join(SCRIPTS_DIR, script)
89
+ if not os.path.isfile(cand):
90
+ # 扫各子包
91
+ for sub in ('io', 'setup', 'kg', 'build', 'test'):
92
+ c2 = os.path.join(SCRIPTS_DIR, sub, script)
93
+ if os.path.isfile(c2):
94
+ cand = c2
95
+ break
96
+ cmd = [sys.executable, cand] + list(args)
81
97
  r = subprocess.run(cmd, cwd=BASE, timeout=timeout)
82
98
  return r.returncode == 0
83
99
 
84
100
 
85
101
  def load_config():
86
- cfg = os.path.join(BASE, '.qoder', 'config.yaml')
87
- if not os.path.isfile(cfg):
88
- return {}
102
+ """v3.0: 转发到 Config 单例 ( mtime 缓存)。"""
89
103
  try:
90
- import yaml
91
- with open(cfg, encoding='utf-8') as f:
92
- return yaml.safe_load(f) or {}
104
+ from common.config import Config
105
+ return Config.load().raw
93
106
  except Exception:
94
- return {}
107
+ cfg = os.path.join(BASE, '.qoder', 'config.yaml')
108
+ if not os.path.isfile(cfg):
109
+ return {}
110
+ try:
111
+ import yaml
112
+ with open(cfg, encoding='utf-8') as f:
113
+ return yaml.safe_load(f) or {}
114
+ except Exception:
115
+ return {}
95
116
 
96
117
 
97
118
  # ============================================================
@@ -100,7 +121,18 @@ def load_config():
100
121
 
101
122
  def check_basics():
102
123
  print('\n--- 1. 基础环境 ---')
103
- say(OK, 'Python {}.{} ({})'.format(sys.version_info[0], sys.version_info[1], sys.platform))
124
+ # Python 版本下限检查 (v3.0+ 新增, 治"3.7 用户装 duckdb 难定位"隐患)
125
+ # duckdb>=1.1.0 / playwright>=1.40 / tree-sitter>=0.21 都要求 Python >=3.8
126
+ py_major, py_minor = sys.version_info[0], sys.version_info[1]
127
+ say(OK, 'Python {}.{} ({})'.format(py_major, py_minor, sys.platform))
128
+ if (py_major, py_minor) < (3, 8):
129
+ problem(
130
+ 'Python {}.{} 低于最低要求 3.8 (duckdb/playwright/tree-sitter 需要)'.format(py_major, py_minor),
131
+ hint='升级 Python: 装 3.10+ (Windows python.org / Mac brew install python@3.10)'
132
+ )
133
+ elif (py_major, py_minor) < (3, 9):
134
+ say(WARN, 'Python {}.{} 可用但建议升级到 3.10+ (部分依赖未来可能提高要求)'.format(py_major, py_minor))
135
+
104
136
  # git 可选 (无 git 时核心功能仍可用, 仅团队同步禁用)
105
137
  try:
106
138
  r = subprocess.run(['git', '--version'], capture_output=True, text=True,
@@ -225,9 +257,9 @@ def check_developer(fix, name=None, role=None):
225
257
  if current:
226
258
  info = get_developer_info() or {}
227
259
  say(OK, '开发者: {} ({})'.format(current, info.get('role', '?')))
228
- # 个人空间目录
260
+ # 个人空间目录 (v3.0 M3: drafts/outputs/journal/.private, 不再有inbox)
229
261
  member = os.path.join(BASE, 'workspace', 'members', current)
230
- missing = [s for s in ('drafts', 'inbox', 'journal')
262
+ missing = [s for s in ('drafts', 'outputs', 'journal')
231
263
  if not os.path.isdir(os.path.join(member, s))]
232
264
  if missing:
233
265
  if fix:
@@ -377,17 +409,6 @@ def index_age_days():
377
409
  return None
378
410
  import time as _time
379
411
  return int((_time.time() - os.path.getmtime(kg_path)) / 86400)
380
- if os.path.isfile(meta_path):
381
- try:
382
- import json
383
- with open(meta_path, encoding='utf-8') as f:
384
- meta = json.load(f)
385
- ts = datetime.strptime(meta.get('last_sync', ''), '%Y-%m-%d %H:%M')
386
- except Exception:
387
- ts = None
388
- if ts is None:
389
- ts = datetime.fromtimestamp(os.path.getmtime(ki_path))
390
- return (datetime.now() - ts).total_seconds() / 86400
391
412
 
392
413
 
393
414
  def _has_source_code():
@@ -544,11 +565,12 @@ def check_prd_templates():
544
565
  problem('PRD 模板缺失: ' + md, '需要 AI 从团队章程 docx 重新蒸馏')
545
566
 
546
567
  # Qoder 全系列载体: rules 在 IDE/Quest 生效, AGENTS.md 在 CLI 生效
568
+ # (Trellis 化: rules 是引擎的一部分, 由 wlkj init/update 生成, 不在团队 git 里)
547
569
  rules_file = os.path.join(BASE, '.qoder', 'rules', 'wl-pipeline.md')
548
570
  if os.path.isfile(rules_file):
549
- say(OK, '.qoder/rules/wl-pipeline.md (Qoder IDE/Quest 规则载体)')
571
+ say(OK, '.qoder/rules/wl-pipeline.md (Qoder IDE/Quest 规则载体, wlkj 生成)')
550
572
  else:
551
- problem('.qoder/rules/wl-pipeline.md 缺失', 'Quest 模式将不知道工作流规则, git 同步取回')
573
+ problem('.qoder/rules/wl-pipeline.md 缺失', 'Quest 模式将不知道工作流规则, npx @hupan56/wlkj update 重新生成')
552
574
  if os.path.isfile(os.path.join(BASE, 'AGENTS.md')):
553
575
  say(OK, 'AGENTS.md (Qoder CLI / 其他 Agent 工具载体)')
554
576
  else:
@@ -590,6 +612,21 @@ def check_weekly(fix):
590
612
  def check_qoderwork():
591
613
  """检查 QoderWork 桌面端是否已安装本项目的技能(junction 到 ~/.qoderwork/skills/)。"""
592
614
  print('\n--- 9. QoderWork 桌面端技能 (可选) ---')
615
+
616
+ # ★ v3.0 新增: launcher 文件就位检查 (全局, 缺失则所有 stdio MCP 黄灯)
617
+ # 场景: 换机器后没跑 install_qoderwork, mcp.json 引用的 launcher 不存在
618
+ home = os.environ.get('USERPROFILE') or os.environ.get('HOME') or os.path.expanduser('~')
619
+ launcher = os.path.join(home, '.qoderwork', 'mcp_launcher.py')
620
+ mcp_json = os.path.join(home, '.qoderwork', 'mcp.json')
621
+ if os.path.isfile(mcp_json):
622
+ # 装了 QoderWork 才检查 launcher (没装 mcp.json 的不算缺失)
623
+ if not os.path.isfile(launcher):
624
+ say(BAD, 'mcp_launcher.py 缺失: {} (所有 STDIO MCP 会黄灯)'.format(launcher))
625
+ print(' -> 跑: python .qoder/scripts/install_qoderwork.py')
626
+ issues.append('mcp_launcher.py 缺失')
627
+ else:
628
+ say(OK, 'mcp_launcher.py 就位 (STDIO MCP 启动器)')
629
+
593
630
  src_skills = os.path.join(BASE, '.qoder', 'skills')
594
631
  if not os.path.isdir(src_skills):
595
632
  say(WARN, '.qoder/skills/ 不存在, 跳过 QoderWork 检查')
@@ -1,4 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
3
+ import os as _o, sys as _s
4
+ _cp = _o.path.join(_o.path.dirname(_o.path.dirname(_o.path.abspath(__file__))), 'common')
5
+ if _cp not in _s.path: _s.path.insert(0, _cp)
6
+ from bootstrap import setup; setup()
7
+
2
8
  """
3
9
  install_qoderwork.py - 把 .qoder/skills/ 安装到 QoderWork 桌面端可识别的位置
4
10
 
@@ -38,9 +44,9 @@ if sys.platform == "win32":
38
44
  except (AttributeError, IOError):
39
45
  pass
40
46
 
41
- # 路径自检:项目根 = 本文件上三级(scripts/ -> .qoder/ -> repo
47
+ # 路径自检:项目根 = 本文件上四级 (v3.0: setup/ -> scripts/ -> .qoder/ -> repo)
42
48
  SCRIPT_DIR = Path(__file__).resolve().parent
43
- PROJECT_ROOT = SCRIPT_DIR.parent.parent
49
+ PROJECT_ROOT = SCRIPT_DIR.parent.parent.parent # v3.0: setup子包多一层
44
50
  SOURCE_SKILLS_DIR = PROJECT_ROOT / ".qoder" / "skills"
45
51
  SOURCE_COMMANDS_DIR = PROJECT_ROOT / ".qoder" / "commands"
46
52
 
@@ -405,13 +411,14 @@ def rewrite_mcp_json_for_launcher():
405
411
  return
406
412
 
407
413
  # 选 Python 写进 mcp.json:
408
- # 优先级 1: 找一个已经装了核心依赖(duckdb/pymysql/yaml/requests)的 Python
409
- # 这样 launcher 启动后秒命中, MCP 秒绿
410
- # 优先级 2: 任意可用 python 命令名 (launcher 内部会再找)
411
- py_with_deps = find_python_with_core_deps()
412
- if py_with_deps:
413
- py_cmd = py_with_deps # 完整路径, 最可靠
414
- else:
414
+ # v3.0 修复: 用装了核心依赖的 Python 绝对路径 (不用命令名)!
415
+ # 原因: QoderWork 桌面应用不继承 shell PATH, 命令名(python/py -3)找不到 → 黄灯。
416
+ # 绝对路径每次 install 时探测, 换机器重跑 update 自动刷新。
417
+ # 依赖查找: 先找装了 duckdb 的 python (find_python_with_core_deps),
418
+ # 找不到回退 detect_python_command。
419
+ try:
420
+ py_cmd = find_python_with_core_deps()
421
+ except Exception:
415
422
  py_cmd = detect_python_command() or "python"
416
423
  launcher = str(_HOME / ".qoderwork" / "mcp_launcher.py")
417
424
  # MCP 名 → launcher 子命令
@@ -426,7 +433,7 @@ def rewrite_mcp_json_for_launcher():
426
433
  entry = servers.get(srv)
427
434
  # 全新机器: kg/mysql/lanhu 条目可能不存在 → 创建它 (launcher 模式)
428
435
  if not entry:
429
- servers[srv] = {"command": py_cmd, "args": [launcher, sub]}
436
+ servers[srv] = {"command": py_cmd, "args": [launcher, sub], "enabled": True}
430
437
  changed = True
431
438
  continue
432
439
  # 已是 launcher 模式? 检查 command 是否需要更新 (Python 路径可能变了)
@@ -435,7 +442,11 @@ def rewrite_mcp_json_for_launcher():
435
442
  # launcher 模式, 但 command 可能指向旧的/错的 Python → 更新
436
443
  old_cmd = entry.get("command", "") if isinstance(entry, dict) else ""
437
444
  if old_cmd != py_cmd:
438
- servers[srv] = {"command": py_cmd, "args": [launcher, sub]}
445
+ servers[srv] = {"command": py_cmd, "args": [launcher, sub], "enabled": True}
446
+ changed = True
447
+ # 补 enabled (旧版可能没写, 导致黄灯)
448
+ if isinstance(entry, dict) and not entry.get("enabled"):
449
+ entry["enabled"] = True
439
450
  changed = True
440
451
  continue
441
452
  # mysql env 抽离到独立文件
@@ -447,7 +458,7 @@ def rewrite_mcp_json_for_launcher():
447
458
  except OSError:
448
459
  pass
449
460
  # 改成 launcher 模式
450
- servers[srv] = {"command": py_cmd, "args": [launcher, sub]}
461
+ servers[srv] = {"command": py_cmd, "args": [launcher, sub], "enabled": True}
451
462
  changed = True
452
463
 
453
464
  if changed:
@@ -456,12 +467,9 @@ def rewrite_mcp_json_for_launcher():
456
467
  QODERWORK_MCP_FILE.write_text(
457
468
  json.dumps(cfg, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
458
469
  print(f"\n--- mcp.json 重写为 launcher 模式 ---")
459
- if py_with_deps:
460
- print(f" [UPD] kg/mysql/zentao/lanhu → command={py_cmd} (装了依赖的Python)")
461
- else:
462
- print(f" [UPD] kg/mysql/zentao/lanhu → command={py_cmd}")
470
+ print(f" [UPD] kg/mysql/zentao/lanhu → command={py_cmd} (命令名, 跨机器通用)")
463
471
  print(f" args=[mcp_launcher.py, <名>] (launcher 内部找装依赖的 Python)")
464
- print(f" 不再写死仓库路径/venv路径, 跨机器通用")
472
+ print(f" 不再写死绝对路径, 换机器/重装Python不会失效")
465
473
  except OSError as e:
466
474
  print(f" [ERR] mcp.json 重写失败: {e}")
467
475
 
@@ -488,7 +496,7 @@ def main():
488
496
  print("MCP 配置刷新 (--mcp-only)")
489
497
  print("=" * 56)
490
498
  # 1. 复制 launcher
491
- launcher_src = SOURCE_COMMANDS_DIR.parent / "scripts" / "mcp_launcher.py"
499
+ launcher_src = SOURCE_COMMANDS_DIR.parent / "scripts" / "mcp" / "mcp_launcher.py" # v3.0: mcp子包
492
500
  launcher_dst = _HOME / ".qoderwork" / "mcp_launcher.py"
493
501
  try:
494
502
  launcher_dst.parent.mkdir(parents=True, exist_ok=True)
@@ -508,15 +516,17 @@ def main():
508
516
  except Exception:
509
517
  pass
510
518
  # 4. 写 .repo-root 锚点 (launcher 定位仓库根用)
511
- # PROJECT_ROOT (脚本自身定位的仓库根) 而非 cwd —— 修"安装目录不一致"bug:
512
- # 用户可能从任意目录跑 install_qoderwork (home/其他项目), cwd ≠ 仓库根,
513
- # cwd 会让 .repo-root 指向错误目录 MCP 全找不到仓库。
514
- # PROJECT_ROOT 由本文件位置推出 (scripts/ -> .qoder/ -> repo), 与 cwd 无关。
519
+ # 防御: 二次验证 PROJECT_ROOT 是"真仓库" (有 workspace/data/.git),
520
+ # 防止测试在临时目录跑 install 污染全局 .repo-root。
521
+ is_real_repo = any((PROJECT_ROOT / m).exists() for m in ('workspace', 'data', '.git'))
515
522
  anchor = _HOME / ".qoderwork" / ".repo-root"
516
523
  try:
517
524
  anchor.parent.mkdir(parents=True, exist_ok=True)
518
- anchor.write_text(str(PROJECT_ROOT), encoding="utf-8")
519
- print(f" [OK] .repo-root 锚点: {anchor} -> {PROJECT_ROOT}")
525
+ if is_real_repo:
526
+ anchor.write_text(str(PROJECT_ROOT), encoding="utf-8")
527
+ print(f" [OK] .repo-root 锚点: {anchor} -> {PROJECT_ROOT}")
528
+ else:
529
+ print(f" [SKIP] .repo-root 未更新 ({PROJECT_ROOT} 非真仓库, 缺 workspace/data/.git)")
520
530
  except OSError:
521
531
  pass
522
532
  return
@@ -659,7 +669,7 @@ def main():
659
669
  # QoderWork 桌面端工作目录是临时会话目录, 不是仓库根, 相对路径失效。
660
670
  # repo_root.py 从 mcp.json 反推仓库根, 命令文件靠它定位脚本/模板。
661
671
  if action in ("install", "check"):
662
- repo_root_src = SOURCE_COMMANDS_DIR.parent / "scripts" / "repo_root.py"
672
+ repo_root_src = SOURCE_COMMANDS_DIR.parent / "scripts" / "setup" / "repo_root.py" # v3.0: setup子包
663
673
  repo_root_dst = _HOME / ".qoderwork" / "repo_root.py"
664
674
  if repo_root_src.is_file():
665
675
  if action == "check":
@@ -684,7 +694,7 @@ def main():
684
694
  # .repo-root 锚点文件让 launcher 能定位仓库根 (不依赖 mcp.json 里的路径)。
685
695
  if action in ("install", "check"):
686
696
  print(f"\n--- mcp_launcher.py (统一 MCP 启动器) ---")
687
- launcher_src = SOURCE_COMMANDS_DIR.parent / "scripts" / "mcp_launcher.py"
697
+ launcher_src = SOURCE_COMMANDS_DIR.parent / "scripts" / "mcp" / "mcp_launcher.py" # v3.0: mcp子包
688
698
  launcher_dst = _HOME / ".qoderwork" / "mcp_launcher.py"
689
699
  anchor_dst = _HOME / ".qoderwork" / ".repo-root"
690
700
  if action == "check":