@hupan56/wlkj 3.3.13 → 3.3.15
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 +0 -7
- package/package.json +29 -29
- package/templates/qoder/commands/optional/wl-spec.md +0 -8
- package/templates/qoder/commands/optional/wl-status.md +0 -8
- package/templates/qoder/commands/wl-code.md +0 -8
- package/templates/qoder/commands/wl-commit.md +0 -8
- package/templates/qoder/commands/wl-init.md +129 -129
- package/templates/qoder/commands/wl-search.md +0 -8
- package/templates/qoder/commands/wl-task.md +0 -8
- package/templates/qoder/commands/wl-test.md +0 -8
- package/templates/qoder/config.yaml +2 -3
- package/templates/qoder/contracts/insight.md +55 -55
- package/templates/qoder/hooks/pre-tool-use-commit.py +124 -124
- package/templates/qoder/hooks/session-start.py +384 -384
- package/templates/qoder/hooks/stop-eval.py +383 -383
- package/templates/qoder/scripts/capability/adapters/mcp.py +322 -322
- package/templates/qoder/scripts/capability/registry.py +245 -245
- package/templates/qoder/scripts/deployment/setup/install_qoderwork.py +14 -0
- package/templates/qoder/scripts/deployment/setup/setup.py +0 -11
- package/templates/qoder/scripts/domain/task/wlkj_panel.py +1503 -1503
- package/templates/qoder/scripts/foundation/bootstrap.py +145 -156
- package/templates/qoder/scripts/foundation/io/context_cache.py +94 -94
- package/templates/qoder/scripts/orchestration/wlkj.py +295 -306
- package/templates/qoder/scripts/tool_guide.md +70 -70
- package/templates/qoder/scripts/validation/eval/alignment_matrix.py +176 -176
- package/templates/qoder/scripts/validation/eval/bf2_content_fidelity.py +110 -110
- package/templates/qoder/scripts/validation/eval/bf2_llmjudge.py +104 -104
- package/templates/qoder/scripts/validation/eval/bf_score.py +218 -218
- package/templates/qoder/scripts/validation/eval/code_flywheel.py +150 -150
- package/templates/qoder/scripts/validation/eval/dispatcher_ab.py +156 -156
- package/templates/qoder/scripts/validation/eval/dispatcher_ab_2026-07-21.json +23 -23
- package/templates/qoder/scripts/validation/eval/feature_fidelity_flywheel.py +143 -143
- package/templates/qoder/scripts/validation/eval/gradient_matrix.py +261 -261
- package/templates/qoder/scripts/validation/eval/gradient_matrix_baseline_2026-07-21.json +33 -33
- package/templates/qoder/scripts/validation/eval/metrics_dashboard.py +105 -105
- package/templates/qoder/scripts/validation/eval/multi_turn_flywheel.py +118 -118
- package/templates/qoder/scripts/validation/eval/prd_fidelity_flywheel.py +128 -128
- package/templates/qoder/scripts/validation/eval/prd_flywheel.py +148 -148
- package/templates/qoder/scripts/validation/eval/prototype_fidelity_flywheel.py +166 -166
- package/templates/qoder/scripts/validation/eval/recall_flywheel.py +148 -148
- package/templates/qoder/scripts/validation/eval/robustness_flywheel.py +139 -139
- package/templates/qoder/scripts/validation/eval/speed_accuracy_flywheel.py +188 -188
- package/templates/qoder/scripts/validation/eval/task_flywheel.py +124 -124
- package/templates/qoder/scripts/validation/eval/token_flywheel.py +88 -88
- package/templates/qoder/scripts/validation/metrics/eval_code_ac.py +177 -177
- package/templates/qoder/scripts/validation/metrics/lint_cases.py +170 -170
- package/templates/qoder/scripts/validation/test/test_context_cache.py +78 -78
- package/templates/qoder/scripts/validation/test/test_lint_cases.py +60 -60
- package/templates/qoder/scripts/validation/test/test_pre_tool_use_commit.py +70 -70
- package/templates/qoder/commands/wl-data.md +0 -46
- package/templates/qoder/commands/wl-fix.md +0 -47
- package/templates/qoder/commands/wl-knowledge.md +0 -41
- package/templates/qoder/commands/wl-review.md +0 -49
- package/templates/workspace/specs/REQ-TEST-CTX/.context-cache.json +0 -1
|
@@ -1,306 +1,295 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
wlkj.py
|
|
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
|
-
|
|
80
|
-
'
|
|
81
|
-
'
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
'
|
|
86
|
-
|
|
87
|
-
'
|
|
88
|
-
'
|
|
89
|
-
|
|
90
|
-
'
|
|
91
|
-
'
|
|
92
|
-
'
|
|
93
|
-
|
|
94
|
-
'
|
|
95
|
-
|
|
96
|
-
'
|
|
97
|
-
'
|
|
98
|
-
|
|
99
|
-
'
|
|
100
|
-
'
|
|
101
|
-
'
|
|
102
|
-
'
|
|
103
|
-
'
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
'
|
|
135
|
-
'',
|
|
136
|
-
'
|
|
137
|
-
'',
|
|
138
|
-
'
|
|
139
|
-
'
|
|
140
|
-
'
|
|
141
|
-
'
|
|
142
|
-
'
|
|
143
|
-
'
|
|
144
|
-
'
|
|
145
|
-
'
|
|
146
|
-
'
|
|
147
|
-
'
|
|
148
|
-
'
|
|
149
|
-
'
|
|
150
|
-
'
|
|
151
|
-
'
|
|
152
|
-
'
|
|
153
|
-
'
|
|
154
|
-
'
|
|
155
|
-
'
|
|
156
|
-
'
|
|
157
|
-
'
|
|
158
|
-
'
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
]
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
sys.stderr.write("[cache]
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if not os.path.isfile(script_path):
|
|
297
|
-
sys.stderr.write('脚本不存在: %s\n' % script_path)
|
|
298
|
-
return 1
|
|
299
|
-
|
|
300
|
-
# 透明转发: 用当前 python 跑目标脚本, 透传所有参数和 exit code
|
|
301
|
-
r = subprocess.run([sys.executable, script_path] + rest)
|
|
302
|
-
return r.returncode
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if __name__ == '__main__':
|
|
306
|
-
sys.exit(main())
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
wlkj.py - 工作流顶层调度器 (Facade 统一入口)
|
|
5
|
+
|
|
6
|
+
角色: 所有 Qoder 载体 (commands/skills/rules 里写的脚本调用) 统一走本入口,
|
|
7
|
+
由 DISPATCH 表路由到 scripts/ 下分层子目录的真实脚本。这样:
|
|
8
|
+
- 载体文件只认一个路径 (orchestration/wlkj.py); scripts/ 重组目录时
|
|
9
|
+
只需改 DISPATCH 一处, 载体文件不受影响 (根治"目录一重组就断链")。
|
|
10
|
+
- 脚本内部互调 (Python 进程间) 仍直接用分层路径, 不绕本入口 (避免 subprocess 嵌套)。
|
|
11
|
+
|
|
12
|
+
用法:
|
|
13
|
+
python .qoder/scripts/orchestration/wlkj.py kg search 保险 --platform web
|
|
14
|
+
python .qoder/scripts/orchestration/wlkj.py task list
|
|
15
|
+
python .qoder/scripts/orchestration/wlkj.py doctor --fix
|
|
16
|
+
python .qoder/scripts/orchestration/wlkj.py search 考勤
|
|
17
|
+
python .qoder/scripts/orchestration/wlkj.py status
|
|
18
|
+
|
|
19
|
+
分组 (逻辑导航, 非物理目录):
|
|
20
|
+
kg/* 知识图谱 (kg.py 的子命令直接透传)
|
|
21
|
+
task 任务系统
|
|
22
|
+
search 代码搜索
|
|
23
|
+
doctor 环境体检
|
|
24
|
+
status 项目状态
|
|
25
|
+
sync 团队同步
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
设计:
|
|
29
|
+
- 透明转发: 不改变任何子脚本的行为, 只是把 argv 转过去
|
|
30
|
+
- 单一真源: 脚本物理路径只在 DISPATCH 出现一次, 载体层不写死路径
|
|
31
|
+
- 零维护: 加新命令只需在 DISPATCH 表加一行
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
import os
|
|
35
|
+
import subprocess
|
|
36
|
+
import sys
|
|
37
|
+
|
|
38
|
+
# 命令 → 脚本 映射 (加新命令只需在此加一行)
|
|
39
|
+
# v3.0: 脚本已分子目录, 路径更新为 子包/脚本
|
|
40
|
+
DISPATCH = {
|
|
41
|
+
# ── 知识图谱 ──
|
|
42
|
+
'kg': 'domain/kg/kg.py',
|
|
43
|
+
'search': 'domain/kg/search/search_index.py',
|
|
44
|
+
'context': 'domain/kg/search/context_pack.py',
|
|
45
|
+
'prefetch': 'domain/kg/search/prefetch.py',
|
|
46
|
+
'kg-build': 'domain/kg/build/kg_build.py',
|
|
47
|
+
'kg-incremental': 'domain/kg/build/kg_incremental.py',
|
|
48
|
+
'kg-semantic': 'domain/kg/graph/kg_semantic.py',
|
|
49
|
+
'semantic': 'domain/kg/graph/kg_semantic.py',
|
|
50
|
+
# ★ MCP 名别名(AI 常把 MCP 工具名当 wlkj.py 命令调 → 未知命令。归一化后两边名都认)
|
|
51
|
+
'search_code': 'domain/kg/search/search_index.py',
|
|
52
|
+
'search_api': 'domain/kg/search/search_index.py',
|
|
53
|
+
'search_prds': 'domain/kg/search/search_index.py',
|
|
54
|
+
'search_field': 'domain/kg/search/search_index.py',
|
|
55
|
+
'search_style': 'domain/kg/search/search_index.py',
|
|
56
|
+
'rag_search': 'domain/kg/graph/kg_semantic.py',
|
|
57
|
+
'ask_corpus': 'domain/kg/graph/kg_semantic.py',
|
|
58
|
+
'context_pack': 'domain/kg/search/context_pack.py',
|
|
59
|
+
'context_360': 'domain/kg/search/context_pack.py',
|
|
60
|
+
'get_impact': 'domain/kg/search/search_index.py',
|
|
61
|
+
'style': 'domain/kg/build/build_style_index.py',
|
|
62
|
+
# ── 任务 / 同步 ──
|
|
63
|
+
'task': 'domain/task/task.py',
|
|
64
|
+
'sync': 'domain/task/team_sync.py',
|
|
65
|
+
'team-sync': 'domain/task/team_sync.py',
|
|
66
|
+
'git-sync': 'domain/task/git_sync.py',
|
|
67
|
+
'zentao-sync': 'domain/task/zentao_sync.py',
|
|
68
|
+
# ── 需求 / PRD ──
|
|
69
|
+
'req': 'domain/requirement/req.py',
|
|
70
|
+
'archive-prd': 'domain/requirement/archive_prd.py',
|
|
71
|
+
# ── 回流平台 (引擎产出 → 平台 AI回流Tab) ──
|
|
72
|
+
# 用法: wlkj.py return prd <路径> <标题> | return prototype <路径> <功能名> [--platform web]
|
|
73
|
+
'return': 'domain/integration/return_to_platform.py',
|
|
74
|
+
'spec-upload': 'domain/integration/spec_upload.py',
|
|
75
|
+
# ── 设计 ──
|
|
76
|
+
'fill-prototype': 'domain/design/fill_prototype.py',
|
|
77
|
+
'design-doc': 'domain/design/gen_design_doc.py',
|
|
78
|
+
# ── 评估 / 学习 (飞轮) ──
|
|
79
|
+
'eval': 'validation/metrics/eval_prd.py',
|
|
80
|
+
'eva': 'validation/metrics/eval_prd.py',
|
|
81
|
+
'usability': 'validation/metrics/usability_score.py',
|
|
82
|
+
'learn': 'domain/learning/learn.py',
|
|
83
|
+
'learn-aggregate': 'domain/kg/build/learn_aggregate.py',
|
|
84
|
+
# ── 报告 / 状态 ──
|
|
85
|
+
'status': 'domain/report/status.py',
|
|
86
|
+
'status-snapshot': 'domain/report/status_snapshot.py',
|
|
87
|
+
'report': 'domain/report/report.py',
|
|
88
|
+
'report-snapshot': 'domain/report/report_snapshot.py',
|
|
89
|
+
'session': 'domain/report/add_session.py',
|
|
90
|
+
'add-session': 'domain/report/add_session.py',
|
|
91
|
+
'role': 'domain/report/role.py',
|
|
92
|
+
'export': 'domain/report/export.py',
|
|
93
|
+
# ── 测试 ──
|
|
94
|
+
'test': 'validation/test/autotest.py',
|
|
95
|
+
'autotest': 'validation/test/autotest.py',
|
|
96
|
+
'autotest-batch': 'validation/test/autotest_batch.py',
|
|
97
|
+
'benchmark': 'validation/test/benchmark.py',
|
|
98
|
+
# ── 环境 / 初始化 ──
|
|
99
|
+
'switch-project': 'domain/kg/switch_project.py',
|
|
100
|
+
'doctor': 'deployment/setup/init_doctor.py',
|
|
101
|
+
'init': 'deployment/setup/setup.py',
|
|
102
|
+
'install-qw': 'deployment/setup/install_qoderwork.py',
|
|
103
|
+
'setup-lanhu': 'deployment/setup/setup_lanhu.py',
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _find_script_by_name(scripts_root, name):
|
|
110
|
+
"""未知命令时, 当作脚本名在 scripts/ 下递归查找。
|
|
111
|
+
支撑 'wlkj.py <脚本名>' 万能跑 (cli.js 的 run 命令底层依赖)。
|
|
112
|
+
返回绝对路径或 None。"""
|
|
113
|
+
import glob
|
|
114
|
+
if not name.endswith('.py'):
|
|
115
|
+
name = name + '.py'
|
|
116
|
+
# 递归找 scripts/**/name
|
|
117
|
+
pattern = os.path.join(scripts_root, '**', name)
|
|
118
|
+
hits = glob.glob(pattern, recursive=True)
|
|
119
|
+
return hits[0] if hits else None
|
|
120
|
+
|
|
121
|
+
def _usage():
|
|
122
|
+
lines = [
|
|
123
|
+
'wlkj.py — 工作流顶层调度器',
|
|
124
|
+
'',
|
|
125
|
+
'用法: python .qoder/scripts/orchestration/wlkj.py <命令> [参数...]',
|
|
126
|
+
'',
|
|
127
|
+
'命令分组 (转发到对应脚本):',
|
|
128
|
+
' 知识图谱: kg <子命令> (kg.py, 13+ 能力)',
|
|
129
|
+
' search <词> (search_index.py)',
|
|
130
|
+
' context <词> (context_pack.py)',
|
|
131
|
+
' kg-build (全量构建图谱)',
|
|
132
|
+
' kg-incremental (增量构建, 定时任务用)',
|
|
133
|
+
' 任务: task <子命令> (task.py, create/list/start/finish)',
|
|
134
|
+
' 同步: sync pull|push (team_sync.py)',
|
|
135
|
+
' 需求: req <子命令> (req.py, 需求生命周期)',
|
|
136
|
+
' archive-prd (PRD 归档)',
|
|
137
|
+
' 回流平台: return prd|prototype <文件> <标题> [--platform web] (PRD/原型回流到平台 AI回流Tab)',
|
|
138
|
+
' 设计: fill-prototype <tpl> (真实数据预填原型)',
|
|
139
|
+
' design-doc (生成 DESIGN.md)',
|
|
140
|
+
' 评估/学习: eval <prd.md> (eval_prd.py, EVA 评分)',
|
|
141
|
+
' usability [--days N] (usability_score.py, 工作流可用性: 采纳+流程)',
|
|
142
|
+
' learn record/status (个人埋点)',
|
|
143
|
+
' learn-aggregate (团队聚合 → kg.duckdb)',
|
|
144
|
+
' 状态/报告: status (status.py, 健康度)',
|
|
145
|
+
' report daily|weekly (report.py)',
|
|
146
|
+
' export <格式> (export.py)',
|
|
147
|
+
' 测试: test <子命令> (autotest.py)',
|
|
148
|
+
' autotest-batch <f> (批量串测)',
|
|
149
|
+
' benchmark (性能基线)',
|
|
150
|
+
' 体检: doctor [--fix] (init_doctor.py)',
|
|
151
|
+
'',
|
|
152
|
+
'示例:',
|
|
153
|
+
' wlkj.py kg search 保险 --platform web',
|
|
154
|
+
' wlkj.py task create "登录优化"',
|
|
155
|
+
' wlkj.py doctor --fix',
|
|
156
|
+
' wlkj.py learn record prd_accepted \'{"req_id":"REQ-001","score":87}\'',
|
|
157
|
+
'',
|
|
158
|
+
'注: 所有 Qoder 载体 (命令/skills/rules) 统一走本入口; 脚本物理位置见 DISPATCH 表。',
|
|
159
|
+
]
|
|
160
|
+
return '\n'.join(lines)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _ctx_cache_cmd(rest, scripts_root):
|
|
164
|
+
"""wlkj.py ctx-cache <REQ-ID> <query> [--platform web|app] — 按 REQ 缓存 context_pack 结果(T1.3)。
|
|
165
|
+
HIT 回放缓存(省一次 KG/MCP 往返);MISS 跑 context_pack 捕获 stdout 写缓存再回放。
|
|
166
|
+
wlkj.py ctx-cache <REQ-ID> --clear — 清该 REQ 缓存(PRD/spec 改动后失效)。
|
|
167
|
+
任何异常 fail-open 降级直拉(缓存坏了不影响主流程)。
|
|
168
|
+
"""
|
|
169
|
+
do_clear = "--clear" in rest
|
|
170
|
+
rest = [a for a in rest if a != "--clear"]
|
|
171
|
+
|
|
172
|
+
req_id = None
|
|
173
|
+
query_parts, passthrough = [], []
|
|
174
|
+
i = 0
|
|
175
|
+
while i < len(rest):
|
|
176
|
+
a = rest[i]
|
|
177
|
+
if a == "--platform" and i + 1 < len(rest):
|
|
178
|
+
passthrough.extend([a, rest[i + 1]]); i += 2; continue
|
|
179
|
+
if a.startswith("-"):
|
|
180
|
+
passthrough.append(a); i += 1; continue
|
|
181
|
+
if req_id is None:
|
|
182
|
+
req_id = a; i += 1; continue
|
|
183
|
+
query_parts.append(a); i += 1
|
|
184
|
+
query = " ".join(query_parts).strip()
|
|
185
|
+
if not req_id or (not query and not do_clear):
|
|
186
|
+
sys.stderr.write("用法: wlkj.py ctx-cache <REQ-ID> <query> [--platform web|app] | ctx-cache <REQ-ID> --clear\n")
|
|
187
|
+
return 1
|
|
188
|
+
|
|
189
|
+
cc = None
|
|
190
|
+
try:
|
|
191
|
+
if scripts_root not in sys.path:
|
|
192
|
+
sys.path.insert(0, scripts_root)
|
|
193
|
+
from foundation.io import context_cache as _cc # noqa
|
|
194
|
+
cc = _cc
|
|
195
|
+
except Exception as e:
|
|
196
|
+
sys.stderr.write("[cache] 模块导入失败, 降级直拉: %s\n" % str(e)[:80])
|
|
197
|
+
|
|
198
|
+
# --clear: 清该 REQ 缓存
|
|
199
|
+
if do_clear:
|
|
200
|
+
if cc is None:
|
|
201
|
+
sys.stderr.write("[cache] 模块不可用, 无法 clear\n"); return 1
|
|
202
|
+
cc.invalidate(req_id)
|
|
203
|
+
sys.stderr.write("[cache] CLEARED req=%s\n" % req_id)
|
|
204
|
+
return 0
|
|
205
|
+
|
|
206
|
+
if cc is not None:
|
|
207
|
+
try:
|
|
208
|
+
cached = cc.get(req_id, query)
|
|
209
|
+
if cached is not None:
|
|
210
|
+
sys.stdout.write(cached if cached.endswith("\n") else cached + "\n")
|
|
211
|
+
sys.stderr.write("[cache] HIT req=%s q=%s (省一次 context_pack 往返)\n" % (req_id, query[:30]))
|
|
212
|
+
return 0
|
|
213
|
+
except Exception as e:
|
|
214
|
+
sys.stderr.write("[cache] get 失败, 降级直拉: %s\n" % str(e)[:80])
|
|
215
|
+
|
|
216
|
+
# MISS / 降级:跑 context_pack,捕获 stdout,写缓存,回放
|
|
217
|
+
script_path = os.path.join(scripts_root, DISPATCH["context"])
|
|
218
|
+
r = subprocess.run([sys.executable, script_path, query] + passthrough, capture_output=True)
|
|
219
|
+
sys.stderr.write(r.stderr.decode(errors="replace"))
|
|
220
|
+
out = r.stdout.decode(errors="replace")
|
|
221
|
+
sys.stdout.write(out)
|
|
222
|
+
if out.strip() and cc is not None:
|
|
223
|
+
try:
|
|
224
|
+
cc.put(req_id, query, out)
|
|
225
|
+
sys.stderr.write("[cache] MISS+PUT req=%s q=%s\n" % (req_id, query[:30]))
|
|
226
|
+
except Exception as e:
|
|
227
|
+
sys.stderr.write("[cache] put 失败(不影响本次输出): %s\n" % str(e)[:80])
|
|
228
|
+
return r.returncode
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _bf_score_cmd(rest, scripts_root):
|
|
232
|
+
"""wlkj.py bf-score <prd|proto|code|search> <产物文件> [--feature 主题] — 业务保真打分(T1/FW12)。
|
|
233
|
+
转发 bf_score.py 对真命令产出文件打 BF 分(PRD→BF1/2, 原型→BF3/4, code/search→BF5/6)。
|
|
234
|
+
bf_score.py 跑在云容器(接真 db_tables/design_system/真PRD);本地无业务数据时降级提示。"""
|
|
235
|
+
script = os.path.join(scripts_root, "validation", "eval", "bf_score.py")
|
|
236
|
+
if not os.path.isfile(script):
|
|
237
|
+
sys.stderr.write("[bf-score] bf_score.py 不在,跳过\n")
|
|
238
|
+
return 1
|
|
239
|
+
r = subprocess.run([sys.executable, script] + rest)
|
|
240
|
+
return r.returncode
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# 带 custom 逻辑的命令(在 DISPATCH 透明转发前拦截)
|
|
244
|
+
SPECIAL = {
|
|
245
|
+
'ctx-cache': _ctx_cache_cmd,
|
|
246
|
+
'context-cache': _ctx_cache_cmd,
|
|
247
|
+
'bf-score': _bf_score_cmd,
|
|
248
|
+
'bfscore': _bf_score_cmd,
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def main():
|
|
253
|
+
argv = sys.argv[1:]
|
|
254
|
+
if not argv or argv[0] in ('-h', '--help', 'help'):
|
|
255
|
+
print(_usage())
|
|
256
|
+
return 0
|
|
257
|
+
|
|
258
|
+
cmd = argv[0]
|
|
259
|
+
rest = argv[1:]
|
|
260
|
+
|
|
261
|
+
# scripts/ 根 = __file__ 的上两级 (orchestration/ -> scripts/)
|
|
262
|
+
scripts_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
263
|
+
|
|
264
|
+
# T1.3 带 custom 逻辑的命令(如 ctx-cache 缓存)在透明转发前拦截
|
|
265
|
+
if cmd in SPECIAL:
|
|
266
|
+
return SPECIAL[cmd](rest, scripts_root)
|
|
267
|
+
|
|
268
|
+
script = DISPATCH.get(cmd)
|
|
269
|
+
if not script:
|
|
270
|
+
# Fallback: 当作脚本名在 scripts/ 下递归找 (支撑 'wlkj.py run <脚本>')
|
|
271
|
+
# 这样 cli.js 的 'run' 和直接 'wlkj.py xxx.py' 都能走单一入口, 不需另维护路径表
|
|
272
|
+
hit = _find_script_by_name(scripts_root, cmd)
|
|
273
|
+
if hit:
|
|
274
|
+
script_path = hit
|
|
275
|
+
r = subprocess.run([sys.executable, script_path] + rest)
|
|
276
|
+
return r.returncode
|
|
277
|
+
sys.stderr.write('未知命令: %s\n' % cmd)
|
|
278
|
+
sys.stderr.write('可用命令: %s\n' % ', '.join(sorted(DISPATCH.keys())))
|
|
279
|
+
sys.stderr.write('提示: 也可用 wlkj.py <脚本名.py> 递归查找脚本\n')
|
|
280
|
+
return 1
|
|
281
|
+
|
|
282
|
+
# scripts/ 根 = __file__ 的上两级 (orchestration/ → scripts/)
|
|
283
|
+
scripts_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
284
|
+
script_path = os.path.join(scripts_root, script)
|
|
285
|
+
if not os.path.isfile(script_path):
|
|
286
|
+
sys.stderr.write('脚本不存在: %s\n' % script_path)
|
|
287
|
+
return 1
|
|
288
|
+
|
|
289
|
+
# 透明转发: 用当前 python 跑目标脚本, 透传所有参数和 exit code
|
|
290
|
+
r = subprocess.run([sys.executable, script_path] + rest)
|
|
291
|
+
return r.returncode
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
if __name__ == '__main__':
|
|
295
|
+
sys.exit(main())
|