@hupan56/wlkj 2.7.12 → 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.
- package/bin/cli.js +344 -78
- package/package.json +29 -29
- package/templates/.qoder/.runtime/ctx-cache-5660152f1d6dd819.md +23 -0
- package/templates/.qoder/.runtime/ctx-cache-afdce0dac06b25b0.md +23 -0
- package/templates/.qoder/.runtime/search-cache-eae7644e7b122f35.txt +1 -0
- package/templates/.qoder/learning/eval-history.jsonl +28 -0
- package/templates/data/index/wiki-index.json +8 -0
- 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 +159 -161
- package/templates/qoder/commands/optional/wl-report.md +4 -4
- package/templates/qoder/commands/optional/wl-status.md +2 -2
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +2 -2
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +2 -2
- package/templates/qoder/commands/wl-req.md +43 -0
- package/templates/qoder/commands/wl-search.md +8 -8
- package/templates/qoder/commands/wl-task.md +17 -17
- package/templates/qoder/commands/wl-test.md +41 -15
- package/templates/qoder/config.yaml +17 -1
- package/templates/qoder/hooks/session-start.py +12 -22
- package/templates/qoder/nul +4 -0
- package/templates/qoder/rules/wl-pipeline.md +22 -48
- package/templates/qoder/scripts/README.md +139 -0
- package/templates/qoder/scripts/common/autotest_auth.py +109 -0
- package/templates/qoder/scripts/common/bootstrap.py +145 -0
- package/templates/qoder/scripts/common/check_publish.py +98 -0
- package/templates/qoder/scripts/common/cmd_registry.py +112 -0
- package/templates/qoder/scripts/common/config.py +187 -0
- package/templates/qoder/scripts/common/contract.py +317 -0
- package/templates/qoder/scripts/common/developer.py +2 -1
- package/templates/qoder/scripts/common/feishu.py +10 -9
- package/templates/qoder/scripts/common/guard.py +159 -0
- package/templates/qoder/scripts/common/identity.py +121 -2
- package/templates/qoder/scripts/common/kg_capabilities.py +182 -0
- package/templates/qoder/scripts/common/mcp_base.py +268 -0
- package/templates/qoder/scripts/common/paths.py +187 -1
- package/templates/qoder/scripts/common/result.py +223 -0
- package/templates/qoder/scripts/common/roles.py +60 -0
- package/templates/qoder/scripts/common/task_utils.py +21 -9
- package/templates/qoder/scripts/common/test_extract.py +115 -0
- package/templates/qoder/scripts/kg/__init__.py +11 -0
- package/templates/qoder/scripts/kg/build_entity_registry.py +196 -0
- package/templates/qoder/scripts/kg/build_relations.py +127 -0
- package/templates/qoder/scripts/{build_style_index.py → kg/build_style_index.py} +39 -10
- package/templates/qoder/scripts/kg/build_workflows.py +144 -0
- package/templates/qoder/scripts/{context_pack.py → kg/context_pack.py} +18 -11
- package/templates/qoder/scripts/{enrich_prompt.py → kg/enrich_prompt.py} +232 -226
- package/templates/qoder/scripts/{extract_api_params.py → kg/extract.py} +398 -246
- package/templates/qoder/scripts/{kg.py → kg/kg.py} +638 -708
- package/templates/qoder/scripts/{kg_build.py → kg/kg_build.py} +618 -612
- package/templates/qoder/scripts/{kg_build_db.py → kg/kg_build_db.py} +333 -327
- package/templates/qoder/scripts/{kg_duckdb.py → kg/kg_duckdb.py} +38 -37
- package/templates/qoder/scripts/{kg_incremental.py → kg/kg_incremental.py} +420 -393
- package/templates/qoder/scripts/{kg_link_db.py → kg/kg_link_db.py} +230 -224
- package/templates/qoder/scripts/{kg_semantic.py → kg/kg_semantic.py} +156 -150
- package/templates/qoder/scripts/kg/prefetch.py +359 -0
- package/templates/qoder/scripts/{search_index.py → kg/search_index.py} +70 -14
- package/templates/qoder/scripts/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/{kg_mcp_server.py → mcp/kg_mcp_server.py} +77 -272
- package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → mcp/lanhu_stdio_wrapper.py} +125 -119
- package/templates/qoder/scripts/{check_mcp.py → mcp/mcp_doctor.py} +515 -298
- package/templates/qoder/scripts/{mcp_launcher.py → mcp/mcp_launcher.py} +442 -414
- package/templates/qoder/scripts/{mysql_mcp_server.py → mcp/mysql_mcp_server.py} +347 -396
- package/templates/qoder/scripts/{zentao_mcp_server.py → mcp/zentao_mcp_server.py} +384 -424
- package/templates/qoder/scripts/report/__init__.py +11 -0
- package/templates/qoder/scripts/{add_session.py → report/add_session.py} +250 -244
- package/templates/qoder/scripts/{archive_prd.py → report/archive_prd.py} +383 -377
- package/templates/qoder/scripts/{eval_prd.py → report/eval_prd.py} +73 -11
- package/templates/qoder/scripts/{export.py → report/export.py} +63 -0
- package/templates/qoder/scripts/{fill_prototype.py → report/fill_prototype.py} +6 -0
- package/templates/qoder/scripts/{gen_design_doc.py → report/gen_design_doc.py} +400 -394
- package/templates/qoder/scripts/{learn.py → report/learn.py} +152 -146
- package/templates/qoder/scripts/{learn_aggregate.py → report/learn_aggregate.py} +207 -201
- package/templates/qoder/scripts/{report.py → report/report.py} +287 -281
- package/templates/qoder/scripts/report/req.py +222 -0
- package/templates/qoder/scripts/report/role.py +33 -0
- package/templates/qoder/scripts/{status.py → report/status.py} +634 -628
- package/templates/qoder/scripts/setup/__init__.py +11 -0
- package/templates/qoder/scripts/setup/carriers.py +662 -0
- package/templates/qoder/scripts/{init_doctor.py → setup/init_doctor.py} +63 -26
- package/templates/qoder/scripts/{install_qoderwork.py → setup/install_qoderwork.py} +36 -26
- package/templates/qoder/scripts/{platform_doctor.py → setup/platform_doctor.py} +265 -259
- package/templates/qoder/scripts/{repo_root.py → setup/repo_root.py} +112 -106
- package/templates/qoder/scripts/{setup.py → setup/setup.py} +113 -4
- package/templates/qoder/scripts/{setup_lanhu.py → setup/setup_lanhu.py} +973 -963
- package/templates/qoder/scripts/task/__init__.py +11 -0
- package/templates/qoder/scripts/{git_sync.py → task/git_sync.py} +52 -31
- package/templates/qoder/scripts/{syncgate.py → task/syncgate.py} +6 -0
- package/templates/qoder/scripts/task/task.py +221 -0
- package/templates/qoder/scripts/task/task_lifecycle.py +596 -0
- package/templates/qoder/scripts/task/task_query.py +161 -0
- package/templates/qoder/scripts/task/task_relations.py +424 -0
- package/templates/qoder/scripts/{team_sync.py → task/team_sync.py} +93 -20
- package/templates/qoder/scripts/test/__init__.py +11 -0
- package/templates/qoder/scripts/{autotest.py → test/autotest.py} +1174 -1751
- package/templates/qoder/scripts/{autotest_batch.py → test/autotest_batch.py} +242 -224
- package/templates/qoder/scripts/test/autotest_data.py +675 -0
- package/templates/qoder/scripts/{autotest_run.py → test/autotest_run.py} +309 -297
- package/templates/qoder/scripts/{benchmark.py → test/benchmark.py} +6 -0
- package/templates/qoder/scripts/{kg_auto_login.py → test/kg_auto_login.py} +202 -196
- package/templates/qoder/scripts/{kg_test_runner.py → test/kg_test_runner.py} +7 -1
- package/templates/qoder/scripts/{page_probe.py → test/page_probe.py} +465 -459
- package/templates/qoder/scripts/wlkj.py +116 -0
- package/templates/qoder/settings.json +1 -10
- package/templates/qoder/skills/design-import/SKILL.md +226 -226
- package/templates/qoder/skills/design-review/SKILL.md +82 -82
- package/templates/qoder/skills/prd-generator/SKILL.md +26 -16
- package/templates/qoder/skills/prd-review/SKILL.md +5 -5
- package/templates/qoder/skills/prototype-generator/SKILL.md +256 -256
- package/templates/qoder/skills/spec-coder/SKILL.md +4 -4
- package/templates/qoder/skills/spec-generator/SKILL.md +4 -4
- package/templates/qoder/skills/test-generator/SKILL.md +5 -5
- package/templates/qoder/skills/wl-code/SKILL.md +4 -4
- package/templates/qoder/skills/wl-commit/SKILL.md +4 -4
- package/templates/qoder/skills/wl-design/SKILL.md +3 -3
- package/templates/qoder/skills/wl-init/SKILL.md +8 -8
- package/templates/qoder/skills/wl-insight/SKILL.md +5 -5
- package/templates/qoder/skills/wl-prd-full/SKILL.md +6 -6
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +6 -6
- package/templates/qoder/skills/wl-prd-review/SKILL.md +4 -4
- package/templates/qoder/skills/wl-report/SKILL.md +7 -7
- package/templates/qoder/skills/wl-search/SKILL.md +13 -13
- package/templates/qoder/skills/wl-spec/SKILL.md +5 -5
- package/templates/qoder/skills/wl-status/SKILL.md +5 -5
- package/templates/qoder/skills/wl-task/SKILL.md +6 -6
- package/templates/qoder/skills/wl-test/SKILL.md +102 -39
- package/templates/root/AGENTS.md +39 -40
- 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_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_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/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/skills/prompt-enrich/SKILL.md +0 -90
- package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
- /package/templates/qoder/scripts/{secure-ls.js → test/secure-ls.js} +0 -0
|
@@ -1,106 +1,112 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
print(root)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
+
|
|
8
|
+
"""
|
|
9
|
+
repo_root.py — 自适应定位 QODER 流水线仓库根(QoderWork 桌面端专用)。
|
|
10
|
+
|
|
11
|
+
为什么需要它:
|
|
12
|
+
QoderWork 桌面端的工作目录是临时会话目录(workspace/xxx), 不是仓库根。
|
|
13
|
+
命令文件里的相对路径 .qoder/scripts/xxx.py 全部失效。
|
|
14
|
+
本脚本从 ~/.qoderwork/mcp.json 的 kg_mcp_server.py 绝对路径反推仓库根,
|
|
15
|
+
自适应(换机器重装 install 后 mcp.json 会更新, 无需硬编码)。
|
|
16
|
+
|
|
17
|
+
用法(命令文件里):
|
|
18
|
+
python ~/.qoderwork/repo_root.py # 打印仓库根绝对路径
|
|
19
|
+
python ~/.qoderwork/repo_root.py --script kg.py # 打印仓库内脚本的绝对路径
|
|
20
|
+
python ~/.qoderwork/repo_root.py --template prd-quick-template.md
|
|
21
|
+
|
|
22
|
+
R=$(python ~/.qoderwork/repo_root.py) # 在 Bash 里捕获仓库根
|
|
23
|
+
python "$R/.qoder/scripts/kg.py" context 保险 --platform web
|
|
24
|
+
"""
|
|
25
|
+
import json
|
|
26
|
+
import os
|
|
27
|
+
import sys
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def find_repo_root():
|
|
31
|
+
"""定位 QODER 仓库根。优先级:
|
|
32
|
+
1. ~/.qoderwork/.repo-root 文件 (install 时写, 主路径)
|
|
33
|
+
2. 环境变量 QODER_REPO
|
|
34
|
+
3. 向上找 .qoder/scripts/ (cwd 在仓库内)
|
|
35
|
+
4. 本脚本所在位置 (开发态)
|
|
36
|
+
失败返回 None。
|
|
37
|
+
"""
|
|
38
|
+
home = os.path.expanduser('~')
|
|
39
|
+
|
|
40
|
+
# 方法1: .repo-root 锚点文件 (install_qoderwork 写的, 最可靠)
|
|
41
|
+
anchor = os.path.join(home, '.qoderwork', '.repo-root')
|
|
42
|
+
if os.path.isfile(anchor):
|
|
43
|
+
try:
|
|
44
|
+
line = open(anchor, encoding='utf-8').read().strip()
|
|
45
|
+
if line and os.path.isdir(os.path.join(line, '.qoder', 'scripts')):
|
|
46
|
+
return line
|
|
47
|
+
except OSError:
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
# 方法2: 环境变量
|
|
51
|
+
env_repo = os.environ.get('QODER_REPO')
|
|
52
|
+
if env_repo and os.path.isdir(os.path.join(env_repo, '.qoder', 'scripts')):
|
|
53
|
+
return env_repo
|
|
54
|
+
|
|
55
|
+
# 方法3: 向上找 .qoder 目录(适用于工作目录在仓库内的情况, 如 IDE)
|
|
56
|
+
here = os.path.abspath(os.getcwd())
|
|
57
|
+
for _ in range(8):
|
|
58
|
+
if os.path.isdir(os.path.join(here, '.qoder', 'scripts')):
|
|
59
|
+
return here
|
|
60
|
+
parent = os.path.dirname(here)
|
|
61
|
+
if parent == here:
|
|
62
|
+
break
|
|
63
|
+
here = parent
|
|
64
|
+
|
|
65
|
+
# 方法4: 本脚本所在位置(若它被放到仓库 .qoder/scripts/ 下)
|
|
66
|
+
try:
|
|
67
|
+
s = os.path.abspath(__file__)
|
|
68
|
+
root = os.path.dirname(os.path.dirname(os.path.dirname(s)))
|
|
69
|
+
if os.path.isdir(os.path.join(root, '.qoder', 'scripts')):
|
|
70
|
+
return root
|
|
71
|
+
except Exception:
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def main():
|
|
78
|
+
root = find_repo_root()
|
|
79
|
+
if not root:
|
|
80
|
+
print('ERROR: 无法定位 QODER 仓库根。请确认 ~/.qoderwork/mcp.json 存在且指向有效的 kg_mcp_server.py', file=sys.stderr)
|
|
81
|
+
return 1
|
|
82
|
+
|
|
83
|
+
# 子路径模式: --script / --template
|
|
84
|
+
args = sys.argv[1:]
|
|
85
|
+
if args and args[0] in ('--script', '--scripts'):
|
|
86
|
+
# 打印仓库内 .qoder/scripts/<name> 的绝对路径
|
|
87
|
+
name = args[1] if len(args) > 1 else ''
|
|
88
|
+
print(os.path.join(root, '.qoder', 'scripts', name))
|
|
89
|
+
elif args and args[0] in ('--template', '--templates'):
|
|
90
|
+
name = args[1] if len(args) > 1 else ''
|
|
91
|
+
print(os.path.join(root, '.qoder', 'templates', name))
|
|
92
|
+
elif args and args[0] == '--test':
|
|
93
|
+
# 自检: 验证关键文件存在
|
|
94
|
+
checks = [
|
|
95
|
+
('kg.py', '.qoder/scripts/kg.py'),
|
|
96
|
+
('eval_prd.py', '.qoder/scripts/eval_prd.py'),
|
|
97
|
+
('quick模板', '.qoder/templates/prd-quick-template.md'),
|
|
98
|
+
('full模板', '.qoder/templates/prd-full-template.md'),
|
|
99
|
+
('kg.duckdb', 'data/index/kg.duckdb'),
|
|
100
|
+
]
|
|
101
|
+
print('仓库根: %s' % root)
|
|
102
|
+
for label, rel in checks:
|
|
103
|
+
ok = os.path.isfile(os.path.join(root, rel))
|
|
104
|
+
print(' %s %s' % ('✅' if ok else '❌', label))
|
|
105
|
+
else:
|
|
106
|
+
# 默认: 只打印仓库根
|
|
107
|
+
print(root)
|
|
108
|
+
return 0
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == '__main__':
|
|
112
|
+
sys.exit(main())
|
|
@@ -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
|
setup.py - 一键就绪 (小而美)
|
|
5
11
|
|
|
@@ -23,6 +29,7 @@ setup.py - 一键就绪 (小而美)
|
|
|
23
29
|
|
|
24
30
|
import argparse
|
|
25
31
|
import os
|
|
32
|
+
import shutil
|
|
26
33
|
import subprocess
|
|
27
34
|
import sys
|
|
28
35
|
from pathlib import Path
|
|
@@ -420,10 +427,78 @@ def _write_team_remote_to_config(url):
|
|
|
420
427
|
pass
|
|
421
428
|
|
|
422
429
|
|
|
430
|
+
def _detect_local_branch(base):
|
|
431
|
+
"""探测当前本地分支名。unborn 分支 (git init 后没 commit) 时,
|
|
432
|
+
git rev-parse --abbrev-ref HEAD 会报 fatal (exit 128), 必须用 symbolic-ref。
|
|
433
|
+
不同 git 版本默认分支是 master 或 main, 不能写死。"""
|
|
434
|
+
r = run(['git', 'symbolic-ref', '--short', 'HEAD'], cwd=str(base))
|
|
435
|
+
if r.returncode == 0 and r.stdout.strip():
|
|
436
|
+
return r.stdout.strip()
|
|
437
|
+
# 兜底 (理论上 symbolic-ref 不会失败, 但稳健起见)
|
|
438
|
+
r2 = run(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], cwd=str(base))
|
|
439
|
+
if r2.returncode == 0 and r2.stdout.strip() and r2.stdout.strip() != 'HEAD':
|
|
440
|
+
return r2.stdout.strip()
|
|
441
|
+
return 'master'
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _backup_local_data(base, remote_branch):
|
|
445
|
+
"""reset --hard 会用远程覆盖整个工作树。开发者可能本地填了 config.yaml 的真实
|
|
446
|
+
mysql/feishu 密码但没 commit, 直接 reset 会吞掉这些数据。
|
|
447
|
+
在 reset 前, 把"团队数据文件"里被本地修改过的内容备份到 .wlkj-pre-reset/,
|
|
448
|
+
reset 后提示用户: 这些是 reset 前的本地版本, 你可能需要把密码重新填回去。
|
|
449
|
+
引擎文件 (gitignored) 不需要备份 — 反正由 update 重新生成。
|
|
450
|
+
返回被备份的原始相对路径列表 (用 / 分隔, 与 cli.js JS 版一致)。
|
|
451
|
+
"""
|
|
452
|
+
backup_dir = base / '.wlkj-pre-reset'
|
|
453
|
+
backed = []
|
|
454
|
+
# 只备份团队数据文件 (git 跟踪的关键数据), 不备份引擎。
|
|
455
|
+
# 用正斜杠统一, 避免跨平台路径分隔符不一致 (Windows os.path.join 用反斜杠)。
|
|
456
|
+
DATA_FILES = ['.qoder/config.yaml', '.qoder/config.toml', '.qoder/settings.json']
|
|
457
|
+
backup_dir.mkdir(parents=True, exist_ok=True)
|
|
458
|
+
try:
|
|
459
|
+
for rel in DATA_FILES:
|
|
460
|
+
local = base / rel
|
|
461
|
+
if not local.is_file():
|
|
462
|
+
continue
|
|
463
|
+
try:
|
|
464
|
+
lc = local.read_text(encoding='utf-8')
|
|
465
|
+
except Exception:
|
|
466
|
+
continue
|
|
467
|
+
# 拿远程版本对比
|
|
468
|
+
# 注意: MSYS2/Git Bash 会把 'origin/master:path' 里的 / 转成 \ →
|
|
469
|
+
# 变成 'origin\master;path' 报 ambiguous argument。
|
|
470
|
+
# 设 MSYS_NO_PATHCONV=1 禁用转换 (Windows 原生 cmd 不受影响)。
|
|
471
|
+
rshow = run(['git', 'show', f'origin/{remote_branch}:{rel}'],
|
|
472
|
+
cwd=str(base),
|
|
473
|
+
env={**os.environ, 'MSYS_NO_PATHCONV': '1',
|
|
474
|
+
'MSYS2_ARG_CONV_EXCL': '*'})
|
|
475
|
+
remote_content = rshow.stdout if rshow.returncode == 0 else None
|
|
476
|
+
# 只在本地与远程不同 (或远程没有) 时备份
|
|
477
|
+
same = (remote_content is not None and remote_content.strip() == lc.strip())
|
|
478
|
+
if not same:
|
|
479
|
+
# 扁平化文件名: .qoder/config.yaml -> .qoder__config.yaml (跨平台)
|
|
480
|
+
flat = rel.replace('/', '__').replace('\\', '__')
|
|
481
|
+
target = backup_dir / flat
|
|
482
|
+
shutil.copy2(str(local), str(target))
|
|
483
|
+
backed.append(rel)
|
|
484
|
+
except Exception:
|
|
485
|
+
pass # 备份失败不阻塞 reset, 但会少一层保护
|
|
486
|
+
return backed
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
|
|
423
490
|
def _setup_tracking(base):
|
|
424
491
|
"""git remote add 之后调: fetch 远程 + 设置当前分支的上游 tracking。
|
|
425
492
|
没这步, git pull 会报 "no tracking information for the current branch"。
|
|
426
493
|
fetch 可能因网络/认证失败 — 失败只警告, 不阻塞 (remote 至少配上了)。
|
|
494
|
+
|
|
495
|
+
关键修复 (Trellis 化): 如果本地是空的 git init 历史 (没有 commit) 或与远程
|
|
496
|
+
不相关 (unrelated histories), 就直接 git reset --hard origin/<branch> 采用远程历史。
|
|
497
|
+
原理: 引擎代码现在由 npm 包生成 (不在 git 里), 团队数据 (config/data/workspace) 在
|
|
498
|
+
远程 → 本地空历史直接采用远程是唯一正确姿势, 彻底消除 "refusing to merge unrelated
|
|
499
|
+
histories"。
|
|
500
|
+
数据安全: reset 前会先把本地修改过的团队数据文件 (config 等) 备份到 .wlkj-pre-reset/,
|
|
501
|
+
防止开发者未提交的真实密码/配置被远程覆盖丢失。
|
|
427
502
|
"""
|
|
428
503
|
# fetch 远程分支
|
|
429
504
|
r = run(['git', 'fetch', 'origin'], cwd=str(base))
|
|
@@ -435,9 +510,7 @@ def _setup_tracking(base):
|
|
|
435
510
|
else:
|
|
436
511
|
warn('git fetch 失败 (不阻塞, 之后 /wl-init 时重试): ' + err[:80])
|
|
437
512
|
return
|
|
438
|
-
|
|
439
|
-
br_r = run(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], cwd=str(base))
|
|
440
|
-
local_branch = br_r.stdout.strip() if br_r.returncode == 0 else 'master'
|
|
513
|
+
|
|
441
514
|
# 远程有 master 还是 main? 优先 master (团队约定), fallback main
|
|
442
515
|
remote_branch = None
|
|
443
516
|
for cand in ('master', 'main'):
|
|
@@ -447,10 +520,46 @@ def _setup_tracking(base):
|
|
|
447
520
|
break
|
|
448
521
|
if not remote_branch:
|
|
449
522
|
return # 远程没分支(空仓库), 没法设 tracking, 等 push 后再说
|
|
450
|
-
|
|
523
|
+
|
|
524
|
+
# 确定本地当前分支名 (unborn 分支用 symbolic-ref, 不能用 rev-parse)
|
|
525
|
+
local_branch = _detect_local_branch(base)
|
|
526
|
+
|
|
527
|
+
# === 检测本地是否有有效历史 (是否有 commit) ===
|
|
528
|
+
head_r = run(['git', 'rev-parse', '--verify', 'HEAD'], cwd=str(base))
|
|
529
|
+
has_local_commit = head_r.returncode == 0
|
|
530
|
+
# 探测 unrelated histories (即便本地有 commit, 无公共祖先也需采用远程)
|
|
531
|
+
unrelated = False
|
|
532
|
+
if has_local_commit:
|
|
533
|
+
mb_r = run(['git', 'merge-base', local_branch, f'origin/{remote_branch}'], cwd=str(base))
|
|
534
|
+
unrelated = (mb_r.returncode != 0)
|
|
535
|
+
|
|
536
|
+
if not has_local_commit or unrelated:
|
|
537
|
+
# 本地无有效历史 (git init 没提交过) 或与远程无公共祖先。
|
|
538
|
+
# 本地这点平行历史无价值 (团队真实历史都在远程) → 采用远程历史。
|
|
539
|
+
# 但 reset --hard 会覆盖工作树 → 先备份本地改过的团队数据, 防丢密码。
|
|
540
|
+
if unrelated:
|
|
541
|
+
warn(f'检测到无关历史 (本地与 origin/{remote_branch} 无公共祖先) — 采用远程历史')
|
|
542
|
+
backed = _backup_local_data(base, remote_branch)
|
|
543
|
+
rr = run(['git', 'reset', '--hard', f'origin/{remote_branch}'], cwd=str(base))
|
|
544
|
+
if rr.returncode == 0:
|
|
545
|
+
ok(f'已采用远程历史 (origin/{remote_branch})')
|
|
546
|
+
if backed:
|
|
547
|
+
warn('reset 前已备份你本地修改过的数据文件 (可能含未提交的真实密码/配置):')
|
|
548
|
+
for rel in backed:
|
|
549
|
+
warn(f' → .wlkj-pre-reset/{rel.replace("/", "__")} (reset 前的本地版本)')
|
|
550
|
+
warn('请检查这些文件, 把需要的配置/密码重新填回 .qoder/config.yaml 后删除备份目录。')
|
|
551
|
+
else:
|
|
552
|
+
warn('reset --hard 失败: ' + (rr.stderr or '').strip()[:80])
|
|
553
|
+
# reset 后本地分支已指向远程 commit, 现在能设上游了
|
|
554
|
+
run(['git', 'branch', f'--set-upstream-to=origin/{remote_branch}', local_branch], cwd=str(base))
|
|
555
|
+
return
|
|
556
|
+
|
|
557
|
+
# 有公共祖先, 正常设上游
|
|
451
558
|
run(['git', 'branch', f'--set-upstream-to=origin/{remote_branch}', local_branch], cwd=str(base))
|
|
452
559
|
|
|
453
560
|
|
|
561
|
+
|
|
562
|
+
|
|
454
563
|
def configure_team_remote():
|
|
455
564
|
"""配置团队协作仓库的 git remote (workspace/ 产出往这里 push)。
|
|
456
565
|
|