@hupan56/wlkj 2.7.11 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +375 -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} +147 -0
- 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,259 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
sync_carriers.py - 四载体 description 自动同步 (skill -> command)
|
|
5
|
-
|
|
6
|
-
为什么需要它:
|
|
7
|
-
check_carriers.py 能发现漂移, 但修复一直靠手工。git status 里
|
|
8
|
-
sync_templates.py 长期 M 状态就是手工同步的证据。
|
|
9
|
-
本脚本把权威源 (skill 的 SKILL.md frontmatter) 的 description 自动同步到
|
|
10
|
-
command 文件, 让"手工对齐"变成"一条命令"。
|
|
11
|
-
|
|
12
|
-
权威源 = skill (skills/wl-X/SKILL.md):
|
|
13
|
-
- skill 更新最频繁 (QoderWork 直接读, 迭代快)
|
|
14
|
-
- skill 的 description 最全 (含中文 + 英文 + 触发词)
|
|
15
|
-
- command 的 description 往往是旧版/简化版
|
|
16
|
-
|
|
17
|
-
同步规则:
|
|
18
|
-
- 只同步 description 字段 (其他字段 name/argument-hint/auto-approve/allowed-tools 保留)
|
|
19
|
-
- skill description 可能很长 (含触发词), 但 command 的 description 该简洁
|
|
20
|
-
→ 截取到第一个"。用户说"之前的部分 (核心功能描述), 去掉冗长的触发词列举
|
|
21
|
-
- 幂等: 已一致则跳过
|
|
22
|
-
- 支持 --dry-run 预览
|
|
23
|
-
|
|
24
|
-
Usage:
|
|
25
|
-
python .qoder/scripts/sync_carriers.py # 同步所有漂移的
|
|
26
|
-
python .qoder/scripts/sync_carriers.py --dry-run # 只预览不改
|
|
27
|
-
python .qoder/scripts/sync_carriers.py wl-search # 只同步指定命令
|
|
28
|
-
Exit: 0 = 全部同步完成 (或已一致), 1 = 出错
|
|
29
|
-
"""
|
|
30
|
-
import argparse
|
|
31
|
-
import os
|
|
32
|
-
import re
|
|
33
|
-
import sys
|
|
34
|
-
|
|
35
|
-
if sys.platform == 'win32':
|
|
36
|
-
try:
|
|
37
|
-
sys.stdout.reconfigure(encoding='utf-8')
|
|
38
|
-
except (AttributeError, IOError):
|
|
39
|
-
pass
|
|
40
|
-
|
|
41
|
-
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
42
|
-
from common.paths import get_repo_root
|
|
43
|
-
|
|
44
|
-
BASE = get_repo_root()
|
|
45
|
-
COMMANDS_DIR = BASE / '.qoder' / 'commands'
|
|
46
|
-
SKILLS_DIR = BASE / '.qoder' / 'skills'
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def _read_frontmatter(path):
|
|
50
|
-
"""读 .md 的 frontmatter, 返回 (fm_dict, body, raw_fm_text)。
|
|
51
|
-
raw_fm_text 用于精确替换 (保留原始格式)。
|
|
52
|
-
"""
|
|
53
|
-
text = path.read_text(encoding='utf-8', errors='ignore')
|
|
54
|
-
if not text.startswith('---'):
|
|
55
|
-
return {}, text, ''
|
|
56
|
-
parts = text.split('---', 2)
|
|
57
|
-
if len(parts) < 3:
|
|
58
|
-
return {}, text, ''
|
|
59
|
-
fm_raw = parts[1]
|
|
60
|
-
body = parts[2]
|
|
61
|
-
return _parse_fm(fm_raw), body, fm_raw
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def _parse_fm(fm_text):
|
|
65
|
-
"""解析 YAML frontmatter (简化版, 不引外部库)。返回 dict。
|
|
66
|
-
处理带引号的值和裸值。
|
|
67
|
-
"""
|
|
68
|
-
fm = {}
|
|
69
|
-
for line in fm_text.splitlines():
|
|
70
|
-
line = line.rstrip()
|
|
71
|
-
if not line.strip() or line.strip().startswith('#'):
|
|
72
|
-
continue
|
|
73
|
-
if ':' not in line:
|
|
74
|
-
continue
|
|
75
|
-
key, _, val = line.partition(':')
|
|
76
|
-
key = key.strip()
|
|
77
|
-
val = val.strip()
|
|
78
|
-
# 去引号
|
|
79
|
-
if (val.startswith('"') and val.endswith('"')) or \
|
|
80
|
-
(val.startswith("'") and val.endswith("'")):
|
|
81
|
-
val = val[1:-1]
|
|
82
|
-
fm[key] = val
|
|
83
|
-
return fm
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def _condense_description(desc):
|
|
87
|
-
"""skill 的 description 往往很长 (含触发词列举), command 的该简洁。
|
|
88
|
-
截取策略: 保留到"用户说"或"触发"或第一个句号之前的核心描述。
|
|
89
|
-
保留中英双语部分 (英文描述通常是核心)。
|
|
90
|
-
"""
|
|
91
|
-
if not desc:
|
|
92
|
-
return desc
|
|
93
|
-
# 找截断点: "用户说" / "触发" / "时触发"
|
|
94
|
-
for marker in ['。用户说', ';用户说', ' 用户说', '。触发:', ' 触发:', ';触发', '。时触发', ' 时触发']:
|
|
95
|
-
idx = desc.find(marker)
|
|
96
|
-
if idx > 0:
|
|
97
|
-
return desc[:idx + 1] # 含句号
|
|
98
|
-
# 没找到截断点 → 原样返回 (可能本就简洁)
|
|
99
|
-
return desc
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def _find_command_file(name):
|
|
103
|
-
"""在 commands/ 和 commands/optional/ 下找命令文件。返回 Path 或 None。"""
|
|
104
|
-
for cand in [COMMANDS_DIR / (name + '.md'),
|
|
105
|
-
COMMANDS_DIR / 'optional' / (name + '.md')]:
|
|
106
|
-
if cand.is_file():
|
|
107
|
-
return cand
|
|
108
|
-
return None
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def _find_skill_file(name):
|
|
112
|
-
"""skills/wl-X/SKILL.md。"""
|
|
113
|
-
p = SKILLS_DIR / name / 'SKILL.md'
|
|
114
|
-
return p if p.is_file() else None
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def sync_one(name, dry_run=False):
|
|
118
|
-
"""同步一个命令。返回 ('synced'/'skipped'/'error', 详情)。"""
|
|
119
|
-
skill_path = _find_skill_file(name)
|
|
120
|
-
cmd_path = _find_command_file(name)
|
|
121
|
-
if not skill_path:
|
|
122
|
-
return 'error', 'skill 不存在: %s' % skill_path
|
|
123
|
-
if not cmd_path:
|
|
124
|
-
return 'error', 'command 不存在: %s' % cmd_path
|
|
125
|
-
|
|
126
|
-
skill_fm, _, _ = _read_frontmatter(skill_path)
|
|
127
|
-
skill_desc = skill_fm.get('description', '')
|
|
128
|
-
if not skill_desc:
|
|
129
|
-
return 'skipped', 'skill 无 description 字段'
|
|
130
|
-
|
|
131
|
-
cmd_fm, cmd_body, cmd_fm_raw = _read_frontmatter(cmd_path)
|
|
132
|
-
cmd_desc = cmd_fm.get('description', '')
|
|
133
|
-
|
|
134
|
-
# 精简 skill desc (command 不该塞一堆触发词)
|
|
135
|
-
condensed = _condense_description(skill_desc)
|
|
136
|
-
|
|
137
|
-
if cmd_desc == condensed:
|
|
138
|
-
return 'skipped', '已一致'
|
|
139
|
-
|
|
140
|
-
# dry-run 只预览
|
|
141
|
-
if dry_run:
|
|
142
|
-
return 'would-sync', ' 旧: %s\n 新: %s' % (cmd_desc[:70], condensed[:70])
|
|
143
|
-
|
|
144
|
-
# 精确替换 frontmatter 里的 description 行
|
|
145
|
-
new_fm = _replace_desc_in_fm(cmd_fm_raw, condensed)
|
|
146
|
-
if new_fm is None:
|
|
147
|
-
return 'error', '无法解析 command frontmatter 的 description 行'
|
|
148
|
-
|
|
149
|
-
new_text = '---' + new_fm + '---' + cmd_body
|
|
150
|
-
cmd_path.write_text(new_text, encoding='utf-8')
|
|
151
|
-
return 'synced', condensed[:70]
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def _replace_desc_in_fm(fm_raw, new_desc):
|
|
155
|
-
"""在 frontmatter 原文里替换 description 行。保留其他行原样。
|
|
156
|
-
返回新的 fm_raw 或 None (无法替换)。
|
|
157
|
-
"""
|
|
158
|
-
# 匹配 description: 开头的行 (可能带引号)
|
|
159
|
-
# 用 json.dumps 保证引号转义正确 (description 含特殊字符)
|
|
160
|
-
escaped = _yaml_escape(new_desc)
|
|
161
|
-
pat = re.compile(r'^(\s*description:\s*)(.*)$', re.MULTILINE)
|
|
162
|
-
new_fm, n = pat.subn(lambda m: m.group(1) + escaped, fm_raw, count=1)
|
|
163
|
-
if n == 0:
|
|
164
|
-
# 没有 description 行 → 插一行 (在 name: 之后)
|
|
165
|
-
if re.search(r'^\s*name:', fm_raw, re.MULTILINE):
|
|
166
|
-
new_fm = re.sub(
|
|
167
|
-
r'^(\s*name:\s*.*)$',
|
|
168
|
-
r'\1\ndescription: ' + escaped,
|
|
169
|
-
fm_raw, count=1, flags=re.MULTILINE)
|
|
170
|
-
else:
|
|
171
|
-
return None
|
|
172
|
-
return new_fm
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def _yaml_escape(s):
|
|
176
|
-
"""YAML 字符串转义: 含特殊字符(: # 等)用双引号包裹, 内部双引号转义。
|
|
177
|
-
简化: 一律用双引号 + JSON 转义 (最安全)。
|
|
178
|
-
"""
|
|
179
|
-
import json
|
|
180
|
-
return json.dumps(s, ensure_ascii=False)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
def find_drifted_commands():
|
|
184
|
-
"""找出所有 command↔skill 配对且 description 漂移的命令名。
|
|
185
|
-
(复用 check_carriers 的相似度判断)
|
|
186
|
-
"""
|
|
187
|
-
from difflib import SequenceMatcher
|
|
188
|
-
drifted = []
|
|
189
|
-
if not SKILLS_DIR.is_dir():
|
|
190
|
-
return drifted
|
|
191
|
-
for d in sorted(SKILLS_DIR.iterdir()):
|
|
192
|
-
if not d.is_dir() or not (d / 'SKILL.md').is_file():
|
|
193
|
-
continue
|
|
194
|
-
name = d.name
|
|
195
|
-
cmd = _find_command_file(name)
|
|
196
|
-
if not cmd:
|
|
197
|
-
continue
|
|
198
|
-
skill_fm, _, _ = _read_frontmatter(d / 'SKILL.md')
|
|
199
|
-
cmd_fm, _, _ = _read_frontmatter(cmd)
|
|
200
|
-
sd, cd = skill_fm.get('description', ''), cmd_fm.get('description', '')
|
|
201
|
-
if not sd or not cd:
|
|
202
|
-
continue
|
|
203
|
-
sim = SequenceMatcher(None, sd.lower(), cd.lower()).ratio()
|
|
204
|
-
if sim < 0.85:
|
|
205
|
-
drifted.append(name)
|
|
206
|
-
return drifted
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
def main():
|
|
210
|
-
ap = argparse.ArgumentParser(description='四载体 description 自动同步 (skill -> command)')
|
|
211
|
-
ap.add_argument('command', nargs='?', default=None,
|
|
212
|
-
help='只同步指定命令 (如 wl-search); 不传则同步所有漂移的')
|
|
213
|
-
ap.add_argument('--dry-run', action='store_true', help='只预览不写文件')
|
|
214
|
-
args = ap.parse_args()
|
|
215
|
-
|
|
216
|
-
print('=' * 56)
|
|
217
|
-
print('四载体 description 同步 (skill -> command)')
|
|
218
|
-
print('=' * 56)
|
|
219
|
-
|
|
220
|
-
if args.command:
|
|
221
|
-
names = [args.command]
|
|
222
|
-
else:
|
|
223
|
-
names = find_drifted_commands()
|
|
224
|
-
if not names:
|
|
225
|
-
print('✅ 无漂移, 所有 command/skill description 已一致。')
|
|
226
|
-
return 0
|
|
227
|
-
print('发现 %d 个漂移命令: %s' % (len(names), ', '.join(names)))
|
|
228
|
-
print()
|
|
229
|
-
|
|
230
|
-
synced, skipped, errors = [], [], []
|
|
231
|
-
for name in names:
|
|
232
|
-
status, detail = sync_one(name, dry_run=args.dry_run)
|
|
233
|
-
if status == 'synced':
|
|
234
|
-
synced.append(name)
|
|
235
|
-
print(' [✓] %s' % name)
|
|
236
|
-
print(' %s' % detail)
|
|
237
|
-
elif status == 'would-sync':
|
|
238
|
-
synced.append(name)
|
|
239
|
-
print(' [~] %s (dry-run)' % name)
|
|
240
|
-
print('%s' % detail)
|
|
241
|
-
elif status == 'skipped':
|
|
242
|
-
skipped.append(name)
|
|
243
|
-
else:
|
|
244
|
-
errors.append((name, detail))
|
|
245
|
-
print(' [✗] %s: %s' % (name, detail))
|
|
246
|
-
|
|
247
|
-
print()
|
|
248
|
-
verb = '将同步' if args.dry_run else '已同步'
|
|
249
|
-
print('%s: %d 同步 / %d 跳过 / %d 错误'
|
|
250
|
-
% (verb, len(synced), len(skipped), len(errors)))
|
|
251
|
-
if not args.dry_run and synced:
|
|
252
|
-
print('提示: 重跑 check_carriers.py 验证一致性。')
|
|
253
|
-
if errors:
|
|
254
|
-
return 1
|
|
255
|
-
return 0
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
if __name__ == '__main__':
|
|
259
|
-
sys.exit(main())
|