@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.
- package/bin/cli.js +1435 -1107
- package/package.json +29 -29
- package/templates/qoder/agents/insight-planning.md +1 -1
- package/templates/qoder/agents/insight-research.md +28 -15
- package/templates/qoder/commands/optional/wl-insight.md +161 -161
- package/templates/qoder/commands/optional/wl-report.md +20 -39
- package/templates/qoder/commands/optional/wl-status.md +4 -4
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +6 -6
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +26 -13
- package/templates/qoder/commands/wl-req.md +43 -0
- package/templates/qoder/commands/wl-search.md +78 -47
- package/templates/qoder/commands/wl-task.md +343 -40
- package/templates/qoder/commands/wl-test.md +43 -17
- package/templates/qoder/config.yaml +52 -14
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +94 -23
- package/templates/qoder/hooks/stop-eval.py +207 -0
- package/templates/qoder/hooks/user-prompt-submit.py +139 -0
- package/templates/qoder/rules/wl-pipeline.md +85 -49
- package/templates/qoder/scripts/README.md +102 -0
- package/templates/qoder/scripts/capability/__init__.py +26 -0
- package/templates/qoder/scripts/capability/__main__.py +72 -0
- package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
- package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
- package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
- package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
- package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
- package/templates/qoder/scripts/capability/caps/context.py +36 -0
- package/templates/qoder/scripts/capability/caps/cron.py +50 -0
- package/templates/qoder/scripts/capability/caps/identity.py +43 -0
- package/templates/qoder/scripts/capability/caps/memory.py +71 -0
- package/templates/qoder/scripts/capability/caps/notify.py +41 -0
- package/templates/qoder/scripts/capability/caps/present.py +48 -0
- package/templates/qoder/scripts/capability/caps/repo.py +31 -0
- package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
- package/templates/qoder/scripts/capability/chain.py +92 -0
- package/templates/qoder/scripts/capability/memory_chain.py +41 -0
- package/templates/qoder/scripts/capability/registry.py +246 -0
- package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
- package/templates/qoder/scripts/capability/smoke_test.py +211 -0
- package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
- package/templates/qoder/scripts/deployment/setup/__init__.py +11 -0
- package/templates/qoder/scripts/deployment/setup/carriers.py +669 -0
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{init_doctor.py → deployment/setup/init_doctor.py} +100 -39
- package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py} +48 -30
- package/templates/qoder/scripts/{platform_doctor.py → deployment/setup/platform_doctor.py} +271 -259
- package/templates/qoder/scripts/{repo_root.py → deployment/setup/repo_root.py} +9 -2
- package/templates/qoder/scripts/{setup.py → deployment/setup/setup.py} +143 -14
- package/templates/qoder/scripts/{setup_lanhu.py → deployment/setup/setup_lanhu.py} +979 -963
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{fill_prototype.py → domain/design/fill_prototype.py} +17 -5
- package/templates/qoder/scripts/{gen_design_doc.py → domain/design/gen_design_doc.py} +406 -394
- package/templates/qoder/scripts/domain/kg/__init__.py +11 -0
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +201 -0
- package/templates/qoder/scripts/domain/kg/build/build_relations.py +132 -0
- package/templates/qoder/scripts/{build_style_index.py → domain/kg/build/build_style_index.py} +50 -15
- package/templates/qoder/scripts/domain/kg/build/build_workflows.py +149 -0
- package/templates/qoder/scripts/{kg_build.py → domain/kg/build/kg_build.py} +685 -612
- package/templates/qoder/scripts/{kg_build_db.py → domain/kg/build/kg_build_db.py} +338 -327
- package/templates/qoder/scripts/{kg_incremental.py → domain/kg/build/kg_incremental.py} +425 -393
- package/templates/qoder/scripts/{learn_aggregate.py → domain/kg/build/learn_aggregate.py} +212 -201
- package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
- package/templates/qoder/scripts/domain/kg/extract/test_extract.py +115 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
- package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_link_db.py → domain/kg/graph/kg_link_db.py} +235 -224
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg.py → domain/kg/kg.py} +871 -708
- package/templates/qoder/scripts/domain/kg/kg_capabilities.py +182 -0
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{context_pack.py → domain/kg/search/context_pack.py} +45 -17
- package/templates/qoder/scripts/{enrich_prompt.py → domain/kg/search/enrich_prompt.py} +238 -226
- package/templates/qoder/scripts/domain/kg/search/prefetch.py +384 -0
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{search_index.py → domain/kg/search/search_index.py} +210 -88
- package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
- package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
- package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_duckdb.py → domain/kg/storage/kg_duckdb.py} +70 -45
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{learn.py → domain/learning/learn.py} +157 -146
- package/templates/qoder/scripts/domain/report/__init__.py +11 -0
- package/templates/qoder/scripts/{add_session.py → domain/report/add_session.py} +256 -244
- package/templates/qoder/scripts/{export.py → domain/report/export.py} +72 -4
- package/templates/qoder/scripts/{report.py → domain/report/report.py} +292 -281
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/domain/report/role.py +39 -0
- package/templates/qoder/scripts/{status.py → domain/report/status.py} +737 -628
- package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
- package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
- package/templates/qoder/scripts/{archive_prd.py → domain/requirement/archive_prd.py} +389 -377
- package/templates/qoder/scripts/domain/requirement/req.py +228 -0
- package/templates/qoder/scripts/domain/task/__init__.py +11 -0
- package/templates/qoder/scripts/{git_sync.py → domain/task/git_sync.py} +90 -50
- package/templates/qoder/scripts/{syncgate.py → domain/task/syncgate.py} +18 -7
- package/templates/qoder/scripts/domain/task/task.py +229 -0
- package/templates/qoder/scripts/domain/task/task_lifecycle.py +606 -0
- package/templates/qoder/scripts/domain/task/task_query.py +162 -0
- package/templates/qoder/scripts/domain/task/task_relations.py +425 -0
- package/templates/qoder/scripts/{team_sync.py → domain/task/team_sync.py} +101 -23
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/bootstrap.py +145 -0
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/core/cmd_registry.py +112 -0
- package/templates/qoder/scripts/foundation/core/config.py +187 -0
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -400
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/data/contract.py +317 -0
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/foundation/data/result.py +223 -0
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -392
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/identity/check_publish.py +103 -0
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -238
- package/templates/qoder/scripts/foundation/identity/guard.py +159 -0
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +132 -9
- package/templates/qoder/scripts/foundation/identity/roles.py +60 -0
- package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
- package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +11 -10
- package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
- package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
- package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
- package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
- package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
- package/templates/qoder/scripts/foundation/protocol/mcp_base.py +268 -0
- package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
- package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
- package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
- package/templates/qoder/scripts/orchestration/__init__.py +0 -0
- package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
- package/templates/qoder/scripts/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/protocol/browser/README.md +207 -0
- package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
- package/templates/qoder/scripts/protocol/browser/config.env +9 -0
- package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
- package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
- package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
- package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
- package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
- package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
- package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
- package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
- package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
- package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
- package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → protocol/mcp/lanhu_stdio_wrapper.py} +131 -119
- package/templates/qoder/scripts/protocol/mcp/mcp_doctor.py +548 -0
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/protocol/mcp/mysql_mcp_server.py +461 -0
- package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
- package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
- package/templates/qoder/scripts/protocol/transports/base.py +87 -0
- package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
- package/templates/qoder/scripts/protocol/transports/http.py +141 -0
- package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
- package/templates/qoder/scripts/run_weekly_update.bat +27 -18
- package/templates/qoder/scripts/run_weekly_update.sh +22 -13
- package/templates/qoder/scripts/validation/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
- package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
- package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
- package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
- package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
- package/templates/qoder/scripts/{eval_prd.py → validation/metrics/eval_prd.py} +84 -15
- package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
- package/templates/qoder/scripts/validation/test/__init__.py +11 -0
- package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
- package/templates/qoder/scripts/{autotest.py → validation/test/autotest.py} +1234 -1751
- package/templates/qoder/scripts/validation/test/autotest_auth.py +109 -0
- package/templates/qoder/scripts/{autotest_batch.py → validation/test/autotest_batch.py} +255 -224
- package/templates/qoder/scripts/validation/test/autotest_data.py +680 -0
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{autotest_run.py → validation/test/autotest_run.py} +323 -297
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{benchmark.py → validation/test/benchmark.py} +17 -5
- package/templates/qoder/scripts/{kg_auto_login.py → validation/test/kg_auto_login.py} +208 -196
- package/templates/qoder/scripts/{kg_test_runner.py → validation/test/kg_test_runner.py} +13 -2
- package/templates/qoder/scripts/{page_probe.py → validation/test/page_probe.py} +470 -459
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +23 -2
- package/templates/qoder/skills/design-import/SKILL.md +235 -226
- package/templates/qoder/skills/design-review/SKILL.md +91 -82
- package/templates/qoder/skills/prd-generator/SKILL.md +38 -24
- package/templates/qoder/skills/prd-review/SKILL.md +8 -6
- package/templates/qoder/skills/prototype-generator/SKILL.md +296 -247
- package/templates/qoder/skills/spec-coder/SKILL.md +24 -15
- package/templates/qoder/skills/spec-generator/SKILL.md +24 -12
- package/templates/qoder/skills/test-generator/SKILL.md +9 -7
- package/templates/qoder/skills/wl-code/SKILL.md +25 -13
- package/templates/qoder/skills/wl-commit/SKILL.md +10 -9
- package/templates/qoder/skills/wl-design/SKILL.md +7 -5
- package/templates/qoder/skills/wl-init/SKILL.md +8 -8
- package/templates/qoder/skills/wl-insight/SKILL.md +25 -12
- package/templates/qoder/skills/wl-prd-full/SKILL.md +59 -8
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +7 -7
- package/templates/qoder/skills/wl-prd-review/SKILL.md +18 -6
- package/templates/qoder/skills/wl-report/SKILL.md +27 -25
- package/templates/qoder/skills/wl-search/SKILL.md +151 -80
- package/templates/qoder/skills/wl-spec/SKILL.md +9 -7
- package/templates/qoder/skills/wl-status/SKILL.md +50 -18
- package/templates/qoder/skills/wl-task/SKILL.md +94 -12
- package/templates/qoder/skills/wl-test/SKILL.md +154 -45
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -237
- package/templates/root/requirements.txt +3 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
- package/templates/qoder/hooks/inject-workflow-state.py +0 -169
- package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/check_carriers.py +0 -238
- package/templates/qoder/scripts/check_mcp.py +0 -298
- package/templates/qoder/scripts/check_mcp_launch.py +0 -183
- package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
- package/templates/qoder/scripts/collect_prds.py +0 -31
- package/templates/qoder/scripts/common/mentions.py +0 -134
- package/templates/qoder/scripts/common/utf8.py +0 -38
- package/templates/qoder/scripts/extract_api_params.py +0 -246
- package/templates/qoder/scripts/extract_routes.py +0 -54
- package/templates/qoder/scripts/extract_routes_tree.py +0 -78
- package/templates/qoder/scripts/handoff.py +0 -22
- package/templates/qoder/scripts/init_developer.py +0 -76
- package/templates/qoder/scripts/kg_mcp_server.py +0 -801
- package/templates/qoder/scripts/kg_semantic.py +0 -150
- package/templates/qoder/scripts/mcp_launcher.py +0 -414
- package/templates/qoder/scripts/mysql_mcp_server.py +0 -396
- package/templates/qoder/scripts/parse_prds.py +0 -33
- package/templates/qoder/scripts/role.py +0 -51
- package/templates/qoder/scripts/sync_carriers.py +0 -259
- package/templates/qoder/scripts/task.py +0 -1261
- package/templates/qoder/scripts/workspace_init.py +0 -102
- package/templates/qoder/scripts/zentao_mcp_server.py +0 -424
- package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
- /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
- /package/templates/qoder/{skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp → scripts/domain/__init__.py} +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
- /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
- /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
|
-
|
|
48
|
-
sys.path.insert(0, SCRIPTS_DIR)
|
|
58
|
+
from foundation.core.paths import get_repo_root, get_developer, get_developer_info
|
|
49
59
|
|
|
50
|
-
|
|
60
|
+
SCRIPTS_DIR = str(get_repo_root())
|
|
61
|
+
sys.path.insert(0, SCRIPTS_DIR)
|
|
51
62
|
|
|
52
|
-
BASE =
|
|
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
|
-
|
|
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
|
-
|
|
87
|
-
if not os.path.isfile(cfg):
|
|
88
|
-
return {}
|
|
107
|
+
"""v3.0: 转发到 Config 单例 (带 mtime 缓存)。"""
|
|
89
108
|
try:
|
|
90
|
-
import
|
|
91
|
-
|
|
92
|
-
return yaml.safe_load(f) or {}
|
|
109
|
+
from foundation.core.config import Config
|
|
110
|
+
return Config.load().raw
|
|
93
111
|
except Exception:
|
|
94
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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]
|
|
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
|
|
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', '
|
|
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
|
|
457
|
+
print(' /wl-prd 完整 PRD 的字段/API 锚定 (会跳过, 原型可用)')
|
|
413
458
|
print(' 知识图谱 MCP search_code/api 等工具 (启动但返回空)')
|
|
414
459
|
print()
|
|
415
460
|
print(' 以下功能不依赖源码, 当前可用:')
|
|
416
|
-
print(' /wl-prd
|
|
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 模式将不知道工作流规则,
|
|
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
|
|
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
|
|
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
|
|
845
|
+
print('环境健康, 可以开始工作: /wl-prd <需求描述>')
|
|
785
846
|
else:
|
|
786
847
|
print('工作流核心已就绪 (见上方降级说明补源码)。')
|
|
787
848
|
print('=' * 56)
|
package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py}
RENAMED
|
@@ -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
|
-
# 路径自检:项目根 =
|
|
53
|
+
# 路径自检:项目根 = 本文件上四级 (v3.0: setup/ -> scripts/ -> .qoder/ -> repo)
|
|
42
54
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
43
|
-
PROJECT_ROOT =
|
|
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
|
-
#
|
|
409
|
-
#
|
|
410
|
-
#
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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
|
-
|
|
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"
|
|
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
|
-
#
|
|
512
|
-
#
|
|
513
|
-
|
|
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
|
-
|
|
519
|
-
|
|
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
|
|
771
|
-
print(' "列表加个导出按钮" -> /wl-prd
|
|
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] 行。")
|