@hupan56/wlkj 2.7.12 → 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 (253) hide show
  1. package/bin/cli.js +1435 -1107
  2. package/package.json +29 -29
  3. package/templates/qoder/agents/insight-planning.md +1 -1
  4. package/templates/qoder/agents/insight-research.md +28 -15
  5. package/templates/qoder/commands/optional/wl-insight.md +161 -161
  6. package/templates/qoder/commands/optional/wl-report.md +20 -39
  7. package/templates/qoder/commands/optional/wl-status.md +4 -4
  8. package/templates/qoder/commands/wl-code.md +2 -2
  9. package/templates/qoder/commands/wl-design.md +6 -6
  10. package/templates/qoder/commands/wl-init.md +3 -3
  11. package/templates/qoder/commands/wl-prd.md +26 -13
  12. package/templates/qoder/commands/wl-req.md +43 -0
  13. package/templates/qoder/commands/wl-search.md +78 -47
  14. package/templates/qoder/commands/wl-task.md +343 -40
  15. package/templates/qoder/commands/wl-test.md +43 -17
  16. package/templates/qoder/config.yaml +52 -14
  17. package/templates/qoder/hooks/post-tool-use.py +181 -0
  18. package/templates/qoder/hooks/session-start.py +94 -23
  19. package/templates/qoder/hooks/stop-eval.py +207 -0
  20. package/templates/qoder/hooks/user-prompt-submit.py +139 -0
  21. package/templates/qoder/rules/wl-pipeline.md +85 -49
  22. package/templates/qoder/scripts/README.md +102 -0
  23. package/templates/qoder/scripts/capability/__init__.py +26 -0
  24. package/templates/qoder/scripts/capability/__main__.py +72 -0
  25. package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
  26. package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
  27. package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
  28. package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
  29. package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
  30. package/templates/qoder/scripts/capability/caps/context.py +36 -0
  31. package/templates/qoder/scripts/capability/caps/cron.py +50 -0
  32. package/templates/qoder/scripts/capability/caps/identity.py +43 -0
  33. package/templates/qoder/scripts/capability/caps/memory.py +71 -0
  34. package/templates/qoder/scripts/capability/caps/notify.py +41 -0
  35. package/templates/qoder/scripts/capability/caps/present.py +48 -0
  36. package/templates/qoder/scripts/capability/caps/repo.py +31 -0
  37. package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
  38. package/templates/qoder/scripts/capability/chain.py +92 -0
  39. package/templates/qoder/scripts/capability/memory_chain.py +41 -0
  40. package/templates/qoder/scripts/capability/registry.py +246 -0
  41. package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
  42. package/templates/qoder/scripts/capability/smoke_test.py +211 -0
  43. package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
  44. package/templates/qoder/scripts/deployment/setup/__init__.py +11 -0
  45. package/templates/qoder/scripts/deployment/setup/carriers.py +669 -0
  46. package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
  47. package/templates/qoder/scripts/{init_doctor.py → deployment/setup/init_doctor.py} +100 -39
  48. package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py} +48 -30
  49. package/templates/qoder/scripts/{platform_doctor.py → deployment/setup/platform_doctor.py} +271 -259
  50. package/templates/qoder/scripts/{repo_root.py → deployment/setup/repo_root.py} +9 -2
  51. package/templates/qoder/scripts/{setup.py → deployment/setup/setup.py} +143 -14
  52. package/templates/qoder/scripts/{setup_lanhu.py → deployment/setup/setup_lanhu.py} +979 -963
  53. package/templates/qoder/scripts/domain/design/__init__.py +0 -0
  54. package/templates/qoder/scripts/{fill_prototype.py → domain/design/fill_prototype.py} +17 -5
  55. package/templates/qoder/scripts/{gen_design_doc.py → domain/design/gen_design_doc.py} +406 -394
  56. package/templates/qoder/scripts/domain/kg/__init__.py +11 -0
  57. package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
  58. package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +201 -0
  59. package/templates/qoder/scripts/domain/kg/build/build_relations.py +132 -0
  60. package/templates/qoder/scripts/{build_style_index.py → domain/kg/build/build_style_index.py} +50 -15
  61. package/templates/qoder/scripts/domain/kg/build/build_workflows.py +149 -0
  62. package/templates/qoder/scripts/{kg_build.py → domain/kg/build/kg_build.py} +685 -612
  63. package/templates/qoder/scripts/{kg_build_db.py → domain/kg/build/kg_build_db.py} +338 -327
  64. package/templates/qoder/scripts/{kg_incremental.py → domain/kg/build/kg_incremental.py} +425 -393
  65. package/templates/qoder/scripts/{learn_aggregate.py → domain/kg/build/learn_aggregate.py} +212 -201
  66. package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
  67. package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
  68. package/templates/qoder/scripts/domain/kg/extract/test_extract.py +115 -0
  69. package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
  70. package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
  71. package/templates/qoder/scripts/{kg_link_db.py → domain/kg/graph/kg_link_db.py} +235 -224
  72. package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
  73. package/templates/qoder/scripts/{kg.py → domain/kg/kg.py} +871 -708
  74. package/templates/qoder/scripts/domain/kg/kg_capabilities.py +182 -0
  75. package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
  76. package/templates/qoder/scripts/{context_pack.py → domain/kg/search/context_pack.py} +45 -17
  77. package/templates/qoder/scripts/{enrich_prompt.py → domain/kg/search/enrich_prompt.py} +238 -226
  78. package/templates/qoder/scripts/domain/kg/search/prefetch.py +384 -0
  79. package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
  80. package/templates/qoder/scripts/{search_index.py → domain/kg/search/search_index.py} +210 -88
  81. package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
  82. package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
  83. package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
  84. package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
  85. package/templates/qoder/scripts/{kg_duckdb.py → domain/kg/storage/kg_duckdb.py} +70 -45
  86. package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
  87. package/templates/qoder/scripts/{learn.py → domain/learning/learn.py} +157 -146
  88. package/templates/qoder/scripts/domain/report/__init__.py +11 -0
  89. package/templates/qoder/scripts/{add_session.py → domain/report/add_session.py} +256 -244
  90. package/templates/qoder/scripts/{export.py → domain/report/export.py} +72 -4
  91. package/templates/qoder/scripts/{report.py → domain/report/report.py} +292 -281
  92. package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
  93. package/templates/qoder/scripts/domain/report/role.py +39 -0
  94. package/templates/qoder/scripts/{status.py → domain/report/status.py} +737 -628
  95. package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
  96. package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
  97. package/templates/qoder/scripts/{archive_prd.py → domain/requirement/archive_prd.py} +389 -377
  98. package/templates/qoder/scripts/domain/requirement/req.py +228 -0
  99. package/templates/qoder/scripts/domain/task/__init__.py +11 -0
  100. package/templates/qoder/scripts/{git_sync.py → domain/task/git_sync.py} +90 -50
  101. package/templates/qoder/scripts/{syncgate.py → domain/task/syncgate.py} +18 -7
  102. package/templates/qoder/scripts/domain/task/task.py +229 -0
  103. package/templates/qoder/scripts/domain/task/task_lifecycle.py +606 -0
  104. package/templates/qoder/scripts/domain/task/task_query.py +162 -0
  105. package/templates/qoder/scripts/domain/task/task_relations.py +425 -0
  106. package/templates/qoder/scripts/{team_sync.py → domain/task/team_sync.py} +101 -23
  107. package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
  108. package/templates/qoder/scripts/foundation/__init__.py +0 -0
  109. package/templates/qoder/scripts/foundation/bootstrap.py +145 -0
  110. package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
  111. package/templates/qoder/scripts/foundation/core/cmd_registry.py +112 -0
  112. package/templates/qoder/scripts/foundation/core/config.py +187 -0
  113. package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -400
  114. package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
  115. package/templates/qoder/scripts/foundation/data/contract.py +317 -0
  116. package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
  117. package/templates/qoder/scripts/foundation/data/result.py +223 -0
  118. package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -392
  119. package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
  120. package/templates/qoder/scripts/foundation/identity/check_publish.py +103 -0
  121. package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -238
  122. package/templates/qoder/scripts/foundation/identity/guard.py +159 -0
  123. package/templates/qoder/scripts/{common → foundation/identity}/identity.py +132 -9
  124. package/templates/qoder/scripts/foundation/identity/roles.py +60 -0
  125. package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
  126. package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
  127. package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +11 -10
  128. package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
  129. package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
  130. package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
  131. package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
  132. package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
  133. package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
  134. package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
  135. package/templates/qoder/scripts/foundation/protocol/mcp_base.py +268 -0
  136. package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
  137. package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
  138. package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
  139. package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
  140. package/templates/qoder/scripts/orchestration/__init__.py +0 -0
  141. package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
  142. package/templates/qoder/scripts/protocol/__init__.py +0 -0
  143. package/templates/qoder/scripts/protocol/browser/README.md +207 -0
  144. package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
  145. package/templates/qoder/scripts/protocol/browser/config.env +9 -0
  146. package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
  147. package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
  148. package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
  149. package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
  150. package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
  151. package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
  152. package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
  153. package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
  154. package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
  155. package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
  156. package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
  157. package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → protocol/mcp/lanhu_stdio_wrapper.py} +131 -119
  158. package/templates/qoder/scripts/protocol/mcp/mcp_doctor.py +548 -0
  159. package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
  160. package/templates/qoder/scripts/protocol/mcp/mysql_mcp_server.py +461 -0
  161. package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
  162. package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
  163. package/templates/qoder/scripts/protocol/transports/base.py +87 -0
  164. package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
  165. package/templates/qoder/scripts/protocol/transports/http.py +141 -0
  166. package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
  167. package/templates/qoder/scripts/run_weekly_update.bat +27 -18
  168. package/templates/qoder/scripts/run_weekly_update.sh +22 -13
  169. package/templates/qoder/scripts/validation/__init__.py +0 -0
  170. package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
  171. package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
  172. package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
  173. package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
  174. package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
  175. package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
  176. package/templates/qoder/scripts/{eval_prd.py → validation/metrics/eval_prd.py} +84 -15
  177. package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
  178. package/templates/qoder/scripts/validation/test/__init__.py +11 -0
  179. package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
  180. package/templates/qoder/scripts/{autotest.py → validation/test/autotest.py} +1234 -1751
  181. package/templates/qoder/scripts/validation/test/autotest_auth.py +109 -0
  182. package/templates/qoder/scripts/{autotest_batch.py → validation/test/autotest_batch.py} +255 -224
  183. package/templates/qoder/scripts/validation/test/autotest_data.py +680 -0
  184. package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
  185. package/templates/qoder/scripts/{autotest_run.py → validation/test/autotest_run.py} +323 -297
  186. package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
  187. package/templates/qoder/scripts/{benchmark.py → validation/test/benchmark.py} +17 -5
  188. package/templates/qoder/scripts/{kg_auto_login.py → validation/test/kg_auto_login.py} +208 -196
  189. package/templates/qoder/scripts/{kg_test_runner.py → validation/test/kg_test_runner.py} +13 -2
  190. package/templates/qoder/scripts/{page_probe.py → validation/test/page_probe.py} +470 -459
  191. package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
  192. package/templates/qoder/settings.json +23 -2
  193. package/templates/qoder/skills/design-import/SKILL.md +235 -226
  194. package/templates/qoder/skills/design-review/SKILL.md +91 -82
  195. package/templates/qoder/skills/prd-generator/SKILL.md +38 -24
  196. package/templates/qoder/skills/prd-review/SKILL.md +8 -6
  197. package/templates/qoder/skills/prototype-generator/SKILL.md +296 -247
  198. package/templates/qoder/skills/spec-coder/SKILL.md +24 -15
  199. package/templates/qoder/skills/spec-generator/SKILL.md +24 -12
  200. package/templates/qoder/skills/test-generator/SKILL.md +9 -7
  201. package/templates/qoder/skills/wl-code/SKILL.md +25 -13
  202. package/templates/qoder/skills/wl-commit/SKILL.md +10 -9
  203. package/templates/qoder/skills/wl-design/SKILL.md +7 -5
  204. package/templates/qoder/skills/wl-init/SKILL.md +8 -8
  205. package/templates/qoder/skills/wl-insight/SKILL.md +25 -12
  206. package/templates/qoder/skills/wl-prd-full/SKILL.md +59 -8
  207. package/templates/qoder/skills/wl-prd-quick/SKILL.md +7 -7
  208. package/templates/qoder/skills/wl-prd-review/SKILL.md +18 -6
  209. package/templates/qoder/skills/wl-report/SKILL.md +27 -25
  210. package/templates/qoder/skills/wl-search/SKILL.md +151 -80
  211. package/templates/qoder/skills/wl-spec/SKILL.md +9 -7
  212. package/templates/qoder/skills/wl-status/SKILL.md +50 -18
  213. package/templates/qoder/skills/wl-task/SKILL.md +94 -12
  214. package/templates/qoder/skills/wl-test/SKILL.md +154 -45
  215. package/templates/qoder/templates/prd-full-template.md +7 -0
  216. package/templates/root/AGENTS.md +275 -237
  217. package/templates/root/requirements.txt +3 -0
  218. package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
  219. package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
  220. package/templates/qoder/hooks/inject-workflow-state.py +0 -169
  221. package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
  222. package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
  223. package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
  224. package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
  225. package/templates/qoder/scripts/check_carriers.py +0 -238
  226. package/templates/qoder/scripts/check_mcp.py +0 -298
  227. package/templates/qoder/scripts/check_mcp_launch.py +0 -183
  228. package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
  229. package/templates/qoder/scripts/collect_prds.py +0 -31
  230. package/templates/qoder/scripts/common/mentions.py +0 -134
  231. package/templates/qoder/scripts/common/utf8.py +0 -38
  232. package/templates/qoder/scripts/extract_api_params.py +0 -246
  233. package/templates/qoder/scripts/extract_routes.py +0 -54
  234. package/templates/qoder/scripts/extract_routes_tree.py +0 -78
  235. package/templates/qoder/scripts/handoff.py +0 -22
  236. package/templates/qoder/scripts/init_developer.py +0 -76
  237. package/templates/qoder/scripts/kg_mcp_server.py +0 -801
  238. package/templates/qoder/scripts/kg_semantic.py +0 -150
  239. package/templates/qoder/scripts/mcp_launcher.py +0 -414
  240. package/templates/qoder/scripts/mysql_mcp_server.py +0 -396
  241. package/templates/qoder/scripts/parse_prds.py +0 -33
  242. package/templates/qoder/scripts/role.py +0 -51
  243. package/templates/qoder/scripts/sync_carriers.py +0 -259
  244. package/templates/qoder/scripts/task.py +0 -1261
  245. package/templates/qoder/scripts/workspace_init.py +0 -102
  246. package/templates/qoder/scripts/zentao_mcp_server.py +0 -424
  247. package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
  248. /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
  249. /package/templates/qoder/{skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp → scripts/domain/__init__.py} +0 -0
  250. /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
  251. /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
  252. /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
  253. /package/templates/qoder/scripts/{secure-ls.js → validation/test/secure-ls.js} +0 -0
@@ -0,0 +1,148 @@
1
+ import os
2
+ import sys
3
+
4
+ HERE = os.path.dirname(os.path.abspath(__file__))
5
+ SCRIPTS = None
6
+ cur = HERE
7
+ for _ in range(8):
8
+ if os.path.isdir(os.path.join(cur, "foundation")) and os.path.isdir(os.path.join(cur, "domain")):
9
+ SCRIPTS = cur
10
+ break
11
+ cur = os.path.dirname(cur)
12
+ if SCRIPTS is None:
13
+ SCRIPTS = os.path.dirname(os.path.dirname(HERE))
14
+ if SCRIPTS not in sys.path:
15
+ sys.path.insert(0, SCRIPTS)
16
+ sys.path.insert(0, os.path.join(SCRIPTS, "foundation"))
17
+ try:
18
+ from bootstrap import setup
19
+ setup()
20
+ except Exception as e:
21
+ print("bootstrap setup failed: %s" % e)
22
+
23
+ from foundation.core.paths import get_repo_root
24
+ ROOT = get_repo_root()
25
+
26
+ COMMANDS = os.path.join(str(ROOT), ".qoder", "commands")
27
+ SKILLS = os.path.join(str(ROOT), ".qoder", "skills")
28
+ AGENTS = os.path.join(str(ROOT), "AGENTS.md")
29
+
30
+ CMD_PREFIX = "wl-"
31
+
32
+ def names_cmds():
33
+ s = set()
34
+ if os.path.isdir(COMMANDS):
35
+ for r, d, fs in os.walk(COMMANDS):
36
+ for f in fs:
37
+ if f.endswith(".md") and f.startswith(CMD_PREFIX):
38
+ s.add(os.path.splitext(f)[0])
39
+ return s
40
+
41
+ def names_skills():
42
+ s = set()
43
+ if os.path.isdir(SKILLS):
44
+ for d in os.listdir(SKILLS):
45
+ p = os.path.join(SKILLS, d, "SKILL.md")
46
+ if os.path.isfile(p) and d.startswith(CMD_PREFIX):
47
+ s.add(d)
48
+ return s
49
+
50
+ def agents_table_cmds():
51
+ s = set()
52
+ if not os.path.isfile(AGENTS):
53
+ return s, False
54
+ txt = open(AGENTS, encoding="utf-8", errors="ignore").read()
55
+ in_table = False
56
+ for line in txt.splitlines():
57
+ if "7 Core" in line or "核心命令" in line:
58
+ in_table = True
59
+ if not in_table:
60
+ continue
61
+ # match /wl-xxx only when followed by a table cell boundary
62
+ i = 0
63
+ while True:
64
+ a = line.find("/wl-", i)
65
+ if a < 0:
66
+ break
67
+ b = a + 4
68
+ while b < len(line) and (line[b].isalpha() or line[b] == "-"):
69
+ b += 1
70
+ # name must be at least /wl- + 1 char, and next char must be a boundary
71
+ ok = b >= a + 5
72
+ nxt = line[b] if b < len(line) else ""
73
+ if ok and nxt in ("`", " ", "|", "", "\t"):
74
+ s.add(line[a + 1:b])
75
+ i = b
76
+ return s, True
77
+
78
+ def main():
79
+ errs = 0
80
+ warns = 0
81
+ print("=" * 60)
82
+ print("carriers verify: 4-carrier name-set + table integrity")
83
+ print("ROOT = %s" % ROOT)
84
+ print("=" * 60)
85
+
86
+ cs = names_cmds()
87
+ ss = names_skills()
88
+ ts, has_agents = agents_table_cmds()
89
+
90
+ only_cmd = sorted(cs - ss)
91
+ only_skill = sorted(ss - cs)
92
+
93
+ print("\n[1] command<->skill name set diff (wl-* prefixed only)")
94
+ print(" commands: %d, skills: %d" % (len(cs), len(ss)))
95
+ if only_cmd:
96
+ warns += 1
97
+ print(" [warn] commands without matching skill dir:")
98
+ for n in only_cmd:
99
+ print(" -%s" % n)
100
+ # 白名单: wl-prd-full/quick/review 是 wl-prd 路由器命令的模式子 skill (设计如此, 非漂移)
101
+ PRD_ROUTER_SKILLS = {"wl-prd-full", "wl-prd-quick", "wl-prd-review"}
102
+ real_orphans = [n for n in only_skill if n not in PRD_ROUTER_SKILLS]
103
+ prd_router = [n for n in only_skill if n in PRD_ROUTER_SKILLS]
104
+ if real_orphans:
105
+ errs += 1
106
+ print(" [FAIL] skills without matching command file (orphan):")
107
+ for n in real_orphans:
108
+ print(" -%s" % n)
109
+ if prd_router:
110
+ print(" [info] %d prd-router sub-skills (wl-prd-full/quick/review, by-design)" % len(prd_router))
111
+ if not only_cmd and not only_skill:
112
+ print(" [ok] wl-* command<->skill name sets aligned")
113
+
114
+ print("\n[2] AGENTS.md core-command table vs files")
115
+ if not has_agents:
116
+ errs += 1
117
+ print(" [FAIL] AGENTS.md missing")
118
+ else:
119
+ missing = sorted(ts - cs - ss)
120
+ if missing:
121
+ errs += 1
122
+ print(" [FAIL] table lists commands that have no file:")
123
+ for n in missing:
124
+ print(" /%s" % n)
125
+ else:
126
+ print(" [ok] all %d table entries have a file" % len(ts))
127
+
128
+ print("\n[3] delegate content check to existing carriers.py check")
129
+ chk = os.path.join(str(ROOT), ".qoder", "scripts", "deployment", "setup", "carriers.py")
130
+ if os.path.isfile(chk):
131
+ import subprocess
132
+ r = subprocess.run([sys.executable, chk, "check"], capture_output=True, text=True, encoding="utf-8", errors="replace")
133
+ if r.returncode == 0:
134
+ print(" [ok] carriers check passed")
135
+ else:
136
+ errs += 1
137
+ print(" [FAIL] carriers check reported drift (rc=%d)" % r.returncode)
138
+ for ln in (r.stdout or "").splitlines():
139
+ if ln.strip():
140
+ print(" %s" % ln[:90])
141
+
142
+ print("-" * 60)
143
+ print("RESULT: %d FAIL / %d warn" % (errs, warns))
144
+ print("FAIL=must-fix drift; warn=advisory. exit 0 only if FAIL==0.")
145
+ return 1 if errs else 0
146
+
147
+ if __name__ == "__main__":
148
+ sys.exit(main())
@@ -1,5 +1,16 @@
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
+ _f = _o.path.abspath(__file__)
6
+ for _ in range(10):
7
+ _f = _o.path.dirname(_f)
8
+ _cp = _o.path.join(_f, 'foundation')
9
+ if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
10
+ break
11
+ if _cp not in _s.path: _s.path.insert(0, _cp)
12
+ from bootstrap import setup; setup()
13
+
3
14
  """
4
15
  QODER Init Doctor - 幂等的一站式环境体检 + 按需修复
5
16
 
@@ -44,12 +55,12 @@ except (AttributeError, TypeError, OSError, IOError):
44
55
  except Exception:
45
56
  pass
46
57
 
47
- SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
48
- sys.path.insert(0, SCRIPTS_DIR)
58
+ from foundation.core.paths import get_repo_root, get_developer, get_developer_info
49
59
 
50
- from common.paths import get_repo_root, get_developer, get_developer_info
60
+ SCRIPTS_DIR = str(get_repo_root())
61
+ sys.path.insert(0, SCRIPTS_DIR)
51
62
 
52
- BASE = str(get_repo_root())
63
+ BASE = SCRIPTS_DIR
53
64
  CODE_DIR = os.path.join(BASE, 'data', 'code')
54
65
  INDEX_DIR = os.path.join(BASE, 'data', 'index')
55
66
  TPL_DIR = os.path.join(BASE, '.qoder', 'templates')
@@ -76,22 +87,37 @@ def problem(msg, hint=None):
76
87
 
77
88
 
78
89
  def run_script(script, *args, timeout=1800):
79
- """跑同目录下的另一个脚本, 实时透传输出"""
80
- cmd = [sys.executable, os.path.join(SCRIPTS_DIR, script)] + list(args)
90
+ """跑另一个脚本, 实时透传输出。
91
+ v3.0: 支持子目录化 先在 scripts/根找, 找不到扫各子包。"""
92
+ # 先试 scripts/根
93
+ cand = os.path.join(SCRIPTS_DIR, script)
94
+ if not os.path.isfile(cand):
95
+ # 扫各子包
96
+ for sub in ('io', 'setup', 'kg', 'build', 'test'):
97
+ c2 = os.path.join(SCRIPTS_DIR, sub, script)
98
+ if os.path.isfile(c2):
99
+ cand = c2
100
+ break
101
+ cmd = [sys.executable, cand] + list(args)
81
102
  r = subprocess.run(cmd, cwd=BASE, timeout=timeout)
82
103
  return r.returncode == 0
83
104
 
84
105
 
85
106
  def load_config():
86
- cfg = os.path.join(BASE, '.qoder', 'config.yaml')
87
- if not os.path.isfile(cfg):
88
- return {}
107
+ """v3.0: 转发到 Config 单例 ( mtime 缓存)。"""
89
108
  try:
90
- import yaml
91
- with open(cfg, encoding='utf-8') as f:
92
- return yaml.safe_load(f) or {}
109
+ from foundation.core.config import Config
110
+ return Config.load().raw
93
111
  except Exception:
94
- return {}
112
+ cfg = os.path.join(BASE, '.qoder', 'config.yaml')
113
+ if not os.path.isfile(cfg):
114
+ return {}
115
+ try:
116
+ import yaml
117
+ with open(cfg, encoding='utf-8') as f:
118
+ return yaml.safe_load(f) or {}
119
+ except Exception:
120
+ return {}
95
121
 
96
122
 
97
123
  # ============================================================
@@ -100,7 +126,18 @@ def load_config():
100
126
 
101
127
  def check_basics():
102
128
  print('\n--- 1. 基础环境 ---')
103
- say(OK, 'Python {}.{} ({})'.format(sys.version_info[0], sys.version_info[1], sys.platform))
129
+ # Python 版本下限检查 (v3.0+ 新增, 治"3.7 用户装 duckdb 难定位"隐患)
130
+ # duckdb>=1.1.0 / playwright>=1.40 / tree-sitter>=0.21 都要求 Python >=3.8
131
+ py_major, py_minor = sys.version_info[0], sys.version_info[1]
132
+ say(OK, 'Python {}.{} ({})'.format(py_major, py_minor, sys.platform))
133
+ if (py_major, py_minor) < (3, 8):
134
+ problem(
135
+ 'Python {}.{} 低于最低要求 3.8 (duckdb/playwright/tree-sitter 需要)'.format(py_major, py_minor),
136
+ hint='升级 Python: 装 3.10+ (Windows python.org / Mac brew install python@3.10)'
137
+ )
138
+ elif (py_major, py_minor) < (3, 9):
139
+ say(WARN, 'Python {}.{} 可用但建议升级到 3.10+ (部分依赖未来可能提高要求)'.format(py_major, py_minor))
140
+
104
141
  # git 可选 (无 git 时核心功能仍可用, 仅团队同步禁用)
105
142
  try:
106
143
  r = subprocess.run(['git', '--version'], capture_output=True, text=True,
@@ -153,11 +190,29 @@ def check_dependencies(fix=False):
153
190
  req = os.path.join(BASE, 'requirements.txt')
154
191
  if os.path.isfile(req):
155
192
  # 用镜像兜底安装: 默认源失败 → 自动切清华/阿里/腾讯
156
- from common.pip_install import install_with_fallback
193
+ # 注意: install_with_fallback 在 Anaconda 3.9+ Windows 上, pip 长时间编译
194
+ # 退出时偶发 GIL 崩溃 (Python 进程死)。这里 try 兜住: 崩了就用 os.system
195
+ # 跑裸 pip (独立进程, 无 GIL 问题) 兜底, 绝不让 doctor --fix 卡死。
157
196
  print(' → 安装: pip install -r requirements.txt (失败自动切国内镜像)')
158
- result = install_with_fallback(['-r', req])
197
+ result = None
198
+ try:
199
+ from foundation.utils.pip_install import install_with_fallback
200
+ result = install_with_fallback(['-r', req])
201
+ except Exception as _e:
202
+ print(' [WARN] 镜像安装工具异常 (%s), 改用直装' % str(_e)[:60])
203
+ # 工具崩了或返回失败 → 用 os.system 直装 (清华镜像, 独立进程无 GIL 风险)
204
+ if not result or not result.get('ok'):
205
+ print(' → 直装: pip install -r requirements.txt (清华镜像)')
206
+ import subprocess as _sp
207
+ _rc = _sp.call([sys.executable, '-m', 'pip', 'install', '--no-input',
208
+ '--progress-bar', 'off', '-i',
209
+ 'https://pypi.tuna.tsinghua.edu.cn/simple',
210
+ '--trusted-host', 'pypi.tuna.tsinghua.edu.cn',
211
+ '-r', req])
212
+ result = {'ok': _rc == 0, 'source': '清华' if _rc == 0 else None,
213
+ 'missing': [], 'hint': None}
159
214
  if result['ok']:
160
- src = result['source']
215
+ src = result.get('source')
161
216
  say(FIX, '依赖安装成功' + ('' if src == 'default' else ' (镜像源: %s)' % src))
162
217
  # 装完后重新验证 (旧版装完不复查, 会把装失败误报为已装)
163
218
  missing_core = []
@@ -168,7 +223,8 @@ def check_dependencies(fix=False):
168
223
  except ImportError:
169
224
  missing_core.append(pkg)
170
225
  else:
171
- print(' [MISS] ' + result['hint'])
226
+ print(' [MISS] 依赖仍未装齐, 手动: pip install -r requirements.txt '
227
+ '-i https://pypi.tuna.tsinghua.edu.cn/simple')
172
228
  # 重新验证, 看哪些真的还缺
173
229
  missing_core = []
174
230
  for mod, pkg, usage, is_core in PIP_DEPS:
@@ -214,7 +270,7 @@ def check_developer(fix, name=None, role=None):
214
270
  return None
215
271
  if name and (not current or current != name):
216
272
  if fix:
217
- from common.developer import init_developer
273
+ from foundation.identity.developer import init_developer
218
274
  if init_developer(name, role):
219
275
  actions.append('注册/切换开发者: ' + name)
220
276
  return name
@@ -225,9 +281,9 @@ def check_developer(fix, name=None, role=None):
225
281
  if current:
226
282
  info = get_developer_info() or {}
227
283
  say(OK, '开发者: {} ({})'.format(current, info.get('role', '?')))
228
- # 个人空间目录
284
+ # 个人空间目录 (v3.0 M3: drafts/outputs/journal/.private, 不再有inbox)
229
285
  member = os.path.join(BASE, 'workspace', 'members', current)
230
- missing = [s for s in ('drafts', 'inbox', 'journal')
286
+ missing = [s for s in ('drafts', 'outputs', 'journal')
231
287
  if not os.path.isdir(os.path.join(member, s))]
232
288
  if missing:
233
289
  if fix:
@@ -377,17 +433,6 @@ def index_age_days():
377
433
  return None
378
434
  import time as _time
379
435
  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
436
 
392
437
 
393
438
  def _has_source_code():
@@ -409,11 +454,11 @@ def print_degraded_status():
409
454
  print('=' * 56)
410
455
  print(' 以下功能需要源码, 当前不可用:')
411
456
  print(' /wl-search 搜代码 (无源码 = 搜不到)')
412
- print(' /wl-prd-full 完整 PRD 的字段/API 锚定 (会跳过, 原型可用)')
457
+ print(' /wl-prd 完整 PRD 的字段/API 锚定 (会跳过, 原型可用)')
413
458
  print(' 知识图谱 MCP search_code/api 等工具 (启动但返回空)')
414
459
  print()
415
460
  print(' 以下功能不依赖源码, 当前可用:')
416
- print(' /wl-prd-quick 零星需求 (纯文案/小改动)')
461
+ print(' /wl-prd 零星需求模式 (纯文案/小改动)')
417
462
  print(' /wl-task 任务管理')
418
463
  print(' /wl-report 日报周报')
419
464
  print(' /wl-commit 代码提交 (提交的是工作流本身的改动)')
@@ -544,11 +589,12 @@ def check_prd_templates():
544
589
  problem('PRD 模板缺失: ' + md, '需要 AI 从团队章程 docx 重新蒸馏')
545
590
 
546
591
  # Qoder 全系列载体: rules 在 IDE/Quest 生效, AGENTS.md 在 CLI 生效
592
+ # (Trellis 化: rules 是引擎的一部分, 由 wlkj init/update 生成, 不在团队 git 里)
547
593
  rules_file = os.path.join(BASE, '.qoder', 'rules', 'wl-pipeline.md')
548
594
  if os.path.isfile(rules_file):
549
- say(OK, '.qoder/rules/wl-pipeline.md (Qoder IDE/Quest 规则载体)')
595
+ say(OK, '.qoder/rules/wl-pipeline.md (Qoder IDE/Quest 规则载体, wlkj 生成)')
550
596
  else:
551
- problem('.qoder/rules/wl-pipeline.md 缺失', 'Quest 模式将不知道工作流规则, git 同步取回')
597
+ problem('.qoder/rules/wl-pipeline.md 缺失', 'Quest 模式将不知道工作流规则, npx @hupan56/wlkj update 重新生成')
552
598
  if os.path.isfile(os.path.join(BASE, 'AGENTS.md')):
553
599
  say(OK, 'AGENTS.md (Qoder CLI / 其他 Agent 工具载体)')
554
600
  else:
@@ -590,6 +636,21 @@ def check_weekly(fix):
590
636
  def check_qoderwork():
591
637
  """检查 QoderWork 桌面端是否已安装本项目的技能(junction 到 ~/.qoderwork/skills/)。"""
592
638
  print('\n--- 9. QoderWork 桌面端技能 (可选) ---')
639
+
640
+ # ★ v3.0 新增: launcher 文件就位检查 (全局, 缺失则所有 stdio MCP 黄灯)
641
+ # 场景: 换机器后没跑 install_qoderwork, mcp.json 引用的 launcher 不存在
642
+ home = os.environ.get('USERPROFILE') or os.environ.get('HOME') or os.path.expanduser('~')
643
+ launcher = os.path.join(home, '.qoderwork', 'mcp_launcher.py')
644
+ mcp_json = os.path.join(home, '.qoderwork', 'mcp.json')
645
+ if os.path.isfile(mcp_json):
646
+ # 装了 QoderWork 才检查 launcher (没装 mcp.json 的不算缺失)
647
+ if not os.path.isfile(launcher):
648
+ say(BAD, 'mcp_launcher.py 缺失: {} (所有 STDIO MCP 会黄灯)'.format(launcher))
649
+ print(' -> 跑: python .qoder/scripts/install_qoderwork.py')
650
+ issues.append('mcp_launcher.py 缺失')
651
+ else:
652
+ say(OK, 'mcp_launcher.py 就位 (STDIO MCP 启动器)')
653
+
593
654
  src_skills = os.path.join(BASE, '.qoder', 'skills')
594
655
  if not os.path.isdir(src_skills):
595
656
  say(WARN, '.qoder/skills/ 不存在, 跳过 QoderWork 检查')
@@ -693,7 +754,7 @@ def check_lanhu():
693
754
  mcp_file = os.path.join(home, '.qoderwork', 'mcp.json')
694
755
  if not os.path.isfile(mcp_file):
695
756
  say(WARN, '未配置蓝湖 MCP (团队不用蓝湖可忽略)')
696
- print(' 用的话跑: python .qoder/scripts/setup.py 或 setup_lanhu.py')
757
+ print(' 用的话跑: python .qoder/scripts/deployment/setup/setup_lanhu.py')
697
758
  return
698
759
 
699
760
  try:
@@ -705,7 +766,7 @@ def check_lanhu():
705
766
 
706
767
  if 'lanhu' not in servers:
707
768
  say(WARN, '未配置蓝湖 MCP (团队不用蓝湖可忽略)')
708
- print(' 用的话跑: python .qoder/scripts/setup.py 或 setup_lanhu.py')
769
+ print(' 用的话跑: python .qoder/scripts/deployment/setup/setup_lanhu.py')
709
770
  return
710
771
 
711
772
  entry = servers['lanhu']
@@ -781,7 +842,7 @@ def main():
781
842
  print('=' * 56)
782
843
  return 1
783
844
  if _has_source_code():
784
- print('环境健康, 可以开始工作: /wl-prd-full <需求描述>')
845
+ print('环境健康, 可以开始工作: /wl-prd <需求描述>')
785
846
  else:
786
847
  print('工作流核心已就绪 (见上方降级说明补源码)。')
787
848
  print('=' * 56)
@@ -1,4 +1,16 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
3
+ import os as _o, sys as _s
4
+ _f = _o.path.abspath(__file__)
5
+ for _ in range(10):
6
+ _f = _o.path.dirname(_f)
7
+ _cp = _o.path.join(_f, 'foundation')
8
+ if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
9
+ break
10
+ if _cp not in _s.path: _s.path.insert(0, _cp)
11
+ from bootstrap import setup; setup()
12
+ from foundation.core.paths import get_repo_root
13
+
2
14
  """
3
15
  install_qoderwork.py - 把 .qoder/skills/ 安装到 QoderWork 桌面端可识别的位置
4
16
 
@@ -38,9 +50,9 @@ if sys.platform == "win32":
38
50
  except (AttributeError, IOError):
39
51
  pass
40
52
 
41
- # 路径自检:项目根 = 本文件上三级(scripts/ -> .qoder/ -> repo
53
+ # 路径自检:项目根 = 本文件上四级 (v3.0: setup/ -> scripts/ -> .qoder/ -> repo)
42
54
  SCRIPT_DIR = Path(__file__).resolve().parent
43
- PROJECT_ROOT = SCRIPT_DIR.parent.parent
55
+ PROJECT_ROOT = get_repo_root()
44
56
  SOURCE_SKILLS_DIR = PROJECT_ROOT / ".qoder" / "skills"
45
57
  SOURCE_COMMANDS_DIR = PROJECT_ROOT / ".qoder" / "commands"
46
58
 
@@ -405,14 +417,17 @@ def rewrite_mcp_json_for_launcher():
405
417
  return
406
418
 
407
419
  # 选 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:
415
- py_cmd = detect_python_command() or "python"
420
+ # v3.0 修复: 用装了核心依赖的 Python 绝对路径 (不用命令名)!
421
+ # 原因: QoderWork 桌面应用不继承 shell PATH, 命令名(python/py -3)找不到 → 黄灯。
422
+ # 绝对路径每次 install 时探测, 换机器重跑 update 自动刷新。
423
+ # 依赖查找: 先找装了 duckdb 的 python (find_python_with_core_deps),
424
+ # 找不到回退 detect_python_command。
425
+ # find_python_with_core_deps 返回 None (不抛异常) 时, 旧代码因 try/except
426
+ # 捕不到 → py_cmd 留 None → mcp.json 写 "command": null → QoderWork 起 MCP
427
+ # 失败变黄。这里显式 None 判断 + 多级回退, 绝不写 null。
428
+ py_cmd = find_python_with_core_deps()
429
+ if not py_cmd:
430
+ py_cmd = detect_python_command() or sys.executable or "python"
416
431
  launcher = str(_HOME / ".qoderwork" / "mcp_launcher.py")
417
432
  # MCP 名 → launcher 子命令
418
433
  name_map = {
@@ -426,7 +441,7 @@ def rewrite_mcp_json_for_launcher():
426
441
  entry = servers.get(srv)
427
442
  # 全新机器: kg/mysql/lanhu 条目可能不存在 → 创建它 (launcher 模式)
428
443
  if not entry:
429
- servers[srv] = {"command": py_cmd, "args": [launcher, sub]}
444
+ servers[srv] = {"command": py_cmd, "args": [launcher, sub], "enabled": True}
430
445
  changed = True
431
446
  continue
432
447
  # 已是 launcher 模式? 检查 command 是否需要更新 (Python 路径可能变了)
@@ -435,7 +450,11 @@ def rewrite_mcp_json_for_launcher():
435
450
  # launcher 模式, 但 command 可能指向旧的/错的 Python → 更新
436
451
  old_cmd = entry.get("command", "") if isinstance(entry, dict) else ""
437
452
  if old_cmd != py_cmd:
438
- servers[srv] = {"command": py_cmd, "args": [launcher, sub]}
453
+ servers[srv] = {"command": py_cmd, "args": [launcher, sub], "enabled": True}
454
+ changed = True
455
+ # 补 enabled (旧版可能没写, 导致黄灯)
456
+ if isinstance(entry, dict) and not entry.get("enabled"):
457
+ entry["enabled"] = True
439
458
  changed = True
440
459
  continue
441
460
  # mysql env 抽离到独立文件
@@ -447,7 +466,7 @@ def rewrite_mcp_json_for_launcher():
447
466
  except OSError:
448
467
  pass
449
468
  # 改成 launcher 模式
450
- servers[srv] = {"command": py_cmd, "args": [launcher, sub]}
469
+ servers[srv] = {"command": py_cmd, "args": [launcher, sub], "enabled": True}
451
470
  changed = True
452
471
 
453
472
  if changed:
@@ -456,12 +475,9 @@ def rewrite_mcp_json_for_launcher():
456
475
  QODERWORK_MCP_FILE.write_text(
457
476
  json.dumps(cfg, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
458
477
  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}")
478
+ print(f" [UPD] kg/mysql/zentao/lanhu → command={py_cmd} (命令名, 跨机器通用)")
463
479
  print(f" args=[mcp_launcher.py, <名>] (launcher 内部找装依赖的 Python)")
464
- print(f" 不再写死仓库路径/venv路径, 跨机器通用")
480
+ print(f" 不再写死绝对路径, 换机器/重装Python不会失效")
465
481
  except OSError as e:
466
482
  print(f" [ERR] mcp.json 重写失败: {e}")
467
483
 
@@ -488,7 +504,7 @@ def main():
488
504
  print("MCP 配置刷新 (--mcp-only)")
489
505
  print("=" * 56)
490
506
  # 1. 复制 launcher
491
- launcher_src = SOURCE_COMMANDS_DIR.parent / "scripts" / "mcp_launcher.py"
507
+ launcher_src = SOURCE_COMMANDS_DIR.parent / "scripts" / "protocol" / "mcp" / "mcp_launcher.py" # v3.0: protocol/mcp子包
492
508
  launcher_dst = _HOME / ".qoderwork" / "mcp_launcher.py"
493
509
  try:
494
510
  launcher_dst.parent.mkdir(parents=True, exist_ok=True)
@@ -508,15 +524,17 @@ def main():
508
524
  except Exception:
509
525
  pass
510
526
  # 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 无关。
527
+ # 防御: 二次验证 PROJECT_ROOT 是"真仓库" (有 workspace/data/.git),
528
+ # 防止测试在临时目录跑 install 污染全局 .repo-root。
529
+ is_real_repo = any((PROJECT_ROOT / m).exists() for m in ('workspace', 'data', '.git'))
515
530
  anchor = _HOME / ".qoderwork" / ".repo-root"
516
531
  try:
517
532
  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}")
533
+ if is_real_repo:
534
+ anchor.write_text(str(PROJECT_ROOT), encoding="utf-8")
535
+ print(f" [OK] .repo-root 锚点: {anchor} -> {PROJECT_ROOT}")
536
+ else:
537
+ print(f" [SKIP] .repo-root 未更新 ({PROJECT_ROOT} 非真仓库, 缺 workspace/data/.git)")
520
538
  except OSError:
521
539
  pass
522
540
  return
@@ -659,7 +677,7 @@ def main():
659
677
  # QoderWork 桌面端工作目录是临时会话目录, 不是仓库根, 相对路径失效。
660
678
  # repo_root.py 从 mcp.json 反推仓库根, 命令文件靠它定位脚本/模板。
661
679
  if action in ("install", "check"):
662
- repo_root_src = SOURCE_COMMANDS_DIR.parent / "scripts" / "repo_root.py"
680
+ repo_root_src = SOURCE_COMMANDS_DIR.parent / "scripts" / "deployment" / "setup" / "repo_root.py" # v3.0: deployment/setup子包
663
681
  repo_root_dst = _HOME / ".qoderwork" / "repo_root.py"
664
682
  if repo_root_src.is_file():
665
683
  if action == "check":
@@ -684,7 +702,7 @@ def main():
684
702
  # .repo-root 锚点文件让 launcher 能定位仓库根 (不依赖 mcp.json 里的路径)。
685
703
  if action in ("install", "check"):
686
704
  print(f"\n--- mcp_launcher.py (统一 MCP 启动器) ---")
687
- launcher_src = SOURCE_COMMANDS_DIR.parent / "scripts" / "mcp_launcher.py"
705
+ launcher_src = SOURCE_COMMANDS_DIR.parent / "scripts" / "protocol" / "mcp" / "mcp_launcher.py" # v3.0: protocol/mcp子包
688
706
  launcher_dst = _HOME / ".qoderwork" / "mcp_launcher.py"
689
707
  anchor_dst = _HOME / ".qoderwork" / ".repo-root"
690
708
  if action == "check":
@@ -755,7 +773,7 @@ def main():
755
773
  elif lh_status == "would-add":
756
774
  print(f" [MISS] 未注册蓝湖 MCP, 安装时会写入 {QODERWORK_MCP_FILE}")
757
775
  # STDIO 模式由 QoderWork 自动管, 不查端口; 只确认 wrapper + 源码就绪
758
- wrapper = PROJECT_ROOT / ".qoder" / "scripts" / "lanhu_stdio_wrapper.py"
776
+ wrapper = PROJECT_ROOT / ".qoder" / "scripts" / "protocol" / "mcp" / "lanhu_stdio_wrapper.py"
759
777
  lanhu_dir = PROJECT_ROOT / "external" / "lanhu-mcp"
760
778
  if wrapper.is_file():
761
779
  print(f" wrapper 就绪: {wrapper.name} (按角色读 cookie)")
@@ -767,8 +785,8 @@ def main():
767
785
  if action == "install" and counts["err"] == 0:
768
786
  print("\n✓ 安装完成。重启 QoderWork(或新建对话)后技能生效。")
769
787
  print(" 在 QoderWork 里直接用自然语言即可,例如:")
770
- print(' "写个保险异常筛选的 PRD" -> /wl-prd-full(正经需求)')
771
- print(' "列表加个导出按钮" -> /wl-prd-quick(小改动)')
788
+ print(' "写个保险异常筛选的 PRD" -> /wl-prd(正经需求, 完整档)')
789
+ print(' "列表加个导出按钮" -> /wl-prd(小改动, 自动走快速档)')
772
790
  print(' "查一下考勤代码在哪" -> /wl-search')
773
791
  elif action == "install" and counts["err"] > 0:
774
792
  print(f"\n⚠ 有 {counts['err']} 个安装失败,见上方 [ERR] 行。")