@hupan56/wlkj 3.0.0 → 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 -1373
- package/package.json +1 -1
- package/templates/qoder/commands/optional/wl-insight.md +6 -4
- 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 +5 -5
- 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 +3 -3
- 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 +10 -10
- package/templates/qoder/config.yaml +35 -13
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +82 -1
- 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 +70 -8
- package/templates/qoder/scripts/README.md +78 -115
- 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/{report → deployment/setup}/__init__.py +11 -11
- package/templates/qoder/scripts/{setup → deployment/setup}/carriers.py +33 -26
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{setup → deployment/setup}/init_doctor.py +40 -16
- package/templates/qoder/scripts/{setup → deployment/setup}/install_qoderwork.py +20 -12
- package/templates/qoder/scripts/{setup → deployment/setup}/platform_doctor.py +8 -2
- package/templates/qoder/scripts/{setup → deployment/setup}/repo_root.py +113 -112
- package/templates/qoder/scripts/{setup → deployment/setup}/setup.py +31 -11
- package/templates/qoder/scripts/{setup → deployment/setup}/setup_lanhu.py +13 -7
- package/templates/qoder/scripts/domain/__init__.py +0 -0
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/design}/fill_prototype.py +12 -6
- package/templates/qoder/scripts/{report → domain/design}/gen_design_doc.py +8 -2
- package/templates/qoder/scripts/{setup → domain/kg}/__init__.py +11 -11
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_entity_registry.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_relations.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_style_index.py +13 -7
- package/templates/qoder/scripts/{kg → domain/kg/build}/build_workflows.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build.py +76 -9
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_build_db.py +7 -2
- package/templates/qoder/scripts/{kg → domain/kg/build}/kg_incremental.py +11 -6
- package/templates/qoder/scripts/{report → domain/kg/build}/learn_aggregate.py +9 -4
- 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/{common → domain/kg/extract}/test_extract.py +115 -115
- 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 → domain/kg/graph}/kg_link_db.py +7 -2
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg → domain/kg}/kg.py +263 -30
- package/templates/qoder/scripts/{common → domain/kg}/kg_capabilities.py +182 -182
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{kg → domain/kg/search}/context_pack.py +30 -9
- package/templates/qoder/scripts/{kg → domain/kg/search}/enrich_prompt.py +10 -4
- package/templates/qoder/scripts/{kg → domain/kg/search}/prefetch.py +33 -8
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{kg → domain/kg/search}/search_index.py +143 -77
- 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 → domain/kg/storage}/kg_duckdb.py +38 -14
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{report → domain/learning}/learn.py +8 -3
- package/templates/qoder/scripts/{mcp → domain/report}/__init__.py +11 -11
- package/templates/qoder/scripts/{report → domain/report}/add_session.py +9 -3
- package/templates/qoder/scripts/{report → domain/report}/export.py +10 -5
- package/templates/qoder/scripts/{report → domain/report}/report.py +9 -4
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/{report → domain/report}/role.py +10 -4
- package/templates/qoder/scripts/{report → domain/report}/status.py +112 -9
- 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/{report → domain/requirement}/archive_prd.py +8 -2
- package/templates/qoder/scripts/{report → domain/requirement}/req.py +228 -222
- package/templates/qoder/scripts/{kg → domain/task}/__init__.py +11 -11
- package/templates/qoder/scripts/{task → domain/task}/git_sync.py +40 -21
- package/templates/qoder/scripts/{task → domain/task}/syncgate.py +13 -8
- package/templates/qoder/scripts/{task → domain/task}/task.py +13 -5
- package/templates/qoder/scripts/{task → domain/task}/task_lifecycle.py +606 -596
- package/templates/qoder/scripts/{task → domain/task}/task_query.py +162 -161
- package/templates/qoder/scripts/{task → domain/task}/task_relations.py +425 -424
- package/templates/qoder/scripts/{task → domain/task}/team_sync.py +9 -4
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation}/bootstrap.py +10 -10
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/core}/cmd_registry.py +1 -1
- package/templates/qoder/scripts/{common → foundation/core}/config.py +1 -1
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -586
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/data}/contract.py +2 -2
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/{common → foundation/data}/result.py +1 -1
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -404
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/identity}/check_publish.py +103 -98
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -239
- package/templates/qoder/scripts/{common → foundation/identity}/guard.py +2 -2
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +14 -10
- package/templates/qoder/scripts/{common → foundation/identity}/roles.py +1 -1
- 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 +2 -2
- 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/{common → foundation/protocol}/mcp_base.py +1 -1
- 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/{mcp → protocol/mcp}/lanhu_stdio_wrapper.py +11 -5
- package/templates/qoder/scripts/{mcp → protocol/mcp}/mcp_doctor.py +47 -14
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/{mcp → protocol/mcp}/mysql_mcp_server.py +181 -67
- 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/{report → validation/metrics}/eval_prd.py +17 -10
- 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/{test → validation/test}/autotest.py +64 -4
- package/templates/qoder/scripts/{common → validation/test}/autotest_auth.py +1 -1
- package/templates/qoder/scripts/{test → validation/test}/autotest_batch.py +16 -3
- package/templates/qoder/scripts/{test → validation/test}/autotest_data.py +17 -12
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{test → validation/test}/autotest_run.py +17 -3
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{test → validation/test}/benchmark.py +12 -6
- package/templates/qoder/scripts/{test → validation/test}/kg_auto_login.py +8 -2
- package/templates/qoder/scripts/{test → validation/test}/kg_test_runner.py +7 -2
- package/templates/qoder/scripts/{test → validation/test}/page_probe.py +8 -3
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +30 -0
- package/templates/qoder/skills/design-import/SKILL.md +22 -13
- package/templates/qoder/skills/design-review/SKILL.md +19 -10
- package/templates/qoder/skills/prd-generator/SKILL.md +22 -18
- package/templates/qoder/skills/prd-review/SKILL.md +6 -4
- package/templates/qoder/skills/prototype-generator/SKILL.md +63 -14
- package/templates/qoder/skills/spec-coder/SKILL.md +22 -13
- package/templates/qoder/skills/spec-generator/SKILL.md +22 -10
- package/templates/qoder/skills/test-generator/SKILL.md +7 -5
- package/templates/qoder/skills/wl-code/SKILL.md +23 -11
- package/templates/qoder/skills/wl-commit/SKILL.md +9 -8
- package/templates/qoder/skills/wl-design/SKILL.md +5 -3
- package/templates/qoder/skills/wl-init/SKILL.md +6 -6
- package/templates/qoder/skills/wl-insight/SKILL.md +23 -10
- package/templates/qoder/skills/wl-prd-full/SKILL.md +57 -6
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +3 -3
- package/templates/qoder/skills/wl-prd-review/SKILL.md +16 -4
- package/templates/qoder/skills/wl-report/SKILL.md +25 -23
- package/templates/qoder/skills/wl-search/SKILL.md +149 -78
- package/templates/qoder/skills/wl-spec/SKILL.md +7 -5
- package/templates/qoder/skills/wl-status/SKILL.md +48 -16
- package/templates/qoder/skills/wl-task/SKILL.md +92 -10
- package/templates/qoder/skills/wl-test/SKILL.md +75 -29
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -236
- 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/.runtime/ctx-cache-5660152f1d6dd819.md +0 -23
- package/templates/.qoder/.runtime/ctx-cache-afdce0dac06b25b0.md +0 -23
- package/templates/.qoder/.runtime/search-cache-eae7644e7b122f35.txt +0 -1
- package/templates/.qoder/learning/eval-history.jsonl +0 -28
- package/templates/data/index/wiki-index.json +0 -8
- package/templates/qoder/nul +0 -4
- package/templates/qoder/scripts/kg/extract.py +0 -398
- package/templates/qoder/scripts/kg/kg_semantic.py +0 -156
- package/templates/qoder/scripts/mcp/kg_mcp_server.py +0 -606
- package/templates/qoder/scripts/mcp/mcp_launcher.py +0 -442
- package/templates/qoder/scripts/mcp/zentao_mcp_server.py +0 -384
- package/templates/qoder/scripts/task/__init__.py +0 -11
- package/templates/qoder/scripts/test/__init__.py +0 -11
- package/templates/qoder/scripts/wlkj.py +0 -116
- /package/templates/qoder/scripts/{common → deployment}/__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/{test → validation/test}/secure-ls.js +0 -0
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
# v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
|
|
4
4
|
import os as _o, sys as _s
|
|
5
|
-
|
|
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
|
|
6
11
|
if _cp not in _s.path: _s.path.insert(0, _cp)
|
|
7
12
|
from bootstrap import setup; setup()
|
|
13
|
+
from foundation.core.paths import get_repo_root
|
|
8
14
|
|
|
9
15
|
"""
|
|
10
16
|
Search knowledge index with platform filtering and PRD search.
|
|
@@ -33,10 +39,10 @@ except (AttributeError, TypeError, OSError, IOError):
|
|
|
33
39
|
pass
|
|
34
40
|
|
|
35
41
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
36
|
-
from
|
|
42
|
+
from foundation.integrations.terms import CN_MAP, BUSINESS_PATH_MAP, CN_TO_EN, get_platform_map, expand_chinese_query
|
|
37
43
|
PLATFORM_MAP = get_platform_map()
|
|
38
44
|
|
|
39
|
-
BASE =
|
|
45
|
+
BASE = get_repo_root()
|
|
40
46
|
INDEX_DIR = os.path.join(BASE, 'data', 'index')
|
|
41
47
|
|
|
42
48
|
# Substring matching against keys shorter than this produces too much noise
|
|
@@ -109,17 +115,24 @@ def _get_kg_db():
|
|
|
109
115
|
return None
|
|
110
116
|
|
|
111
117
|
|
|
112
|
-
def
|
|
113
|
-
"""
|
|
118
|
+
def _load_with_pickle_cache(con, cache_name, loader_fn, empty_sentinel=None):
|
|
119
|
+
"""通用 pickle/mtime 缓存层 (抽自 _load_keyword_with_cache, 推广到全部索引)。
|
|
114
120
|
|
|
115
|
-
性能: DuckDB
|
|
116
|
-
pickle.load
|
|
121
|
+
性能: ui-style 等索引每次从 DuckDB 全量重建 (3 次查询 + 逐行 json.loads)
|
|
122
|
+
耗时显著; pickle.load 直接读回对象快数倍。code-keyword 实测 180ms→37ms。
|
|
117
123
|
缓存失效: kg.duckdb 的 mtime 变了就重建 (和 context_pack 缓存逻辑一致)。
|
|
118
124
|
降级: pickle 损坏/版本不兼容 → 静默回退 DuckDB 重建, 不报错。
|
|
125
|
+
线程/进程安全: 只读连接共享, pickle 写用 temp+replace (原子), 失败不影响返回。
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
con: DuckDB 只读连接。
|
|
129
|
+
cache_name: 缓存文件名 (如 'code-keyword' → .runtime/code-keyword.pkl)。
|
|
130
|
+
loader_fn: 接收 con, 返回重建后的对象; 返回 None / 空视为无数据。
|
|
131
|
+
empty_sentinel: loader 返回此值时也视为无数据、不缓存 (默认 None)。
|
|
119
132
|
"""
|
|
120
133
|
import pickle
|
|
121
134
|
duckdb_path = os.path.join(INDEX_DIR, 'kg.duckdb')
|
|
122
|
-
cache_path = os.path.join(BASE, '.qoder', '.runtime', '
|
|
135
|
+
cache_path = os.path.join(BASE, '.qoder', '.runtime', cache_name + '.pkl')
|
|
123
136
|
db_mtime = os.path.getmtime(duckdb_path) if os.path.isfile(duckdb_path) else 0
|
|
124
137
|
|
|
125
138
|
# ① 缓存有效 (存在 + mtime 匹配) → 直接 load, 跳过 DuckDB
|
|
@@ -132,18 +145,17 @@ def _load_keyword_with_cache(con):
|
|
|
132
145
|
pass # 缓存损坏 → 回退重建
|
|
133
146
|
|
|
134
147
|
# ② 缓存无效/缺失 → 从 DuckDB 重建
|
|
135
|
-
|
|
136
|
-
result
|
|
137
|
-
|
|
138
|
-
result.setdefault(kw, []).append(fp)
|
|
139
|
-
if not result:
|
|
140
|
-
return None
|
|
148
|
+
result = loader_fn(con)
|
|
149
|
+
if result is None or result == empty_sentinel:
|
|
150
|
+
return None # 无数据, 不缓存 (避免缓存空结果误导后续)
|
|
141
151
|
|
|
142
|
-
# ③ 写缓存 (best-effort,
|
|
152
|
+
# ③ 写缓存 (best-effort, 失败不影响返回结果; 用原子写防止半文件)
|
|
143
153
|
try:
|
|
144
154
|
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
|
|
145
|
-
|
|
155
|
+
tmp = cache_path + '.tmp'
|
|
156
|
+
with open(tmp, 'wb') as f:
|
|
146
157
|
pickle.dump(result, f, protocol=pickle.HIGHEST_PROTOCOL)
|
|
158
|
+
os.replace(tmp, cache_path)
|
|
147
159
|
except OSError:
|
|
148
160
|
pass
|
|
149
161
|
return result
|
|
@@ -162,58 +174,75 @@ def _duckdb_load(filename):
|
|
|
162
174
|
try:
|
|
163
175
|
if filename == 'code-keyword.json':
|
|
164
176
|
# {keyword: [files]} — 11836 行重建耗时 180ms, 用 pickle 缓存提速到 37ms
|
|
165
|
-
|
|
177
|
+
def _kw_loader(con):
|
|
178
|
+
rows = con.execute("SELECT keyword, file_path FROM keywords").fetchall()
|
|
179
|
+
result = {}
|
|
180
|
+
for kw, fp in rows:
|
|
181
|
+
result.setdefault(kw, []).append(fp)
|
|
182
|
+
return result if result else None
|
|
183
|
+
return _load_with_pickle_cache(con, 'code-keyword', _kw_loader)
|
|
166
184
|
|
|
167
185
|
elif filename == 'code-api.json':
|
|
168
186
|
# {endpoint: controller_file}
|
|
169
|
-
|
|
170
|
-
|
|
187
|
+
def _api_loader(con):
|
|
188
|
+
rows = con.execute("SELECT endpoint, controller_file FROM endpoints").fetchall()
|
|
189
|
+
return dict(rows) if rows else None
|
|
190
|
+
return _load_with_pickle_cache(con, 'code-api', _api_loader)
|
|
171
191
|
|
|
172
192
|
elif filename == 'ui-style.json':
|
|
173
193
|
# Reconstruct the full ui-style structure from pages/fields/style_meta
|
|
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
|
-
|
|
194
|
+
# 最贵的索引: 3 次 DuckDB 查询 + 对 components/table_columns/
|
|
195
|
+
# form_fields/layout 逐行 json.loads。加 pickle 缓存后二次调用秒回。
|
|
196
|
+
def _style_loader(con):
|
|
197
|
+
result = {'version': '1.0', 'projects': {}, 'common_components': {}, 'form_components': {}, 'field_map': {}}
|
|
198
|
+
# Pages
|
|
199
|
+
page_rows = con.execute("SELECT file, project, page_type, components, table_columns, form_fields, layout FROM pages").fetchall()
|
|
200
|
+
for f, proj, ptype, comps, cols, fields, layout in page_rows:
|
|
201
|
+
proj_data = result['projects'].setdefault(proj, {'page_types': {}, 'page_examples': {}})
|
|
202
|
+
proj_data['page_types'][ptype] = proj_data['page_types'].get(ptype, 0) + 1
|
|
203
|
+
examples = proj_data['page_examples'].setdefault(ptype, [])
|
|
204
|
+
examples.append({'file': f, 'components': json.loads(comps) if comps else [],
|
|
205
|
+
'table_columns': json.loads(cols) if cols else [],
|
|
206
|
+
'form_fields': json.loads(fields) if fields else [],
|
|
207
|
+
'layout': json.loads(layout) if layout else {}})
|
|
208
|
+
# Fields
|
|
209
|
+
field_rows = con.execute("SELECT field, title, count, sample_files FROM fields").fetchall()
|
|
210
|
+
for field, title, count, samples in field_rows:
|
|
211
|
+
titles = title.split('/') if title else []
|
|
212
|
+
result['field_map'][field] = {'field': field, 'titles': titles, 'count': count,
|
|
213
|
+
'sample_files': json.loads(samples) if samples else []}
|
|
214
|
+
# Style meta (layout_fingerprint per project)
|
|
215
|
+
meta_rows = con.execute("SELECT project, stack, layout, access_mode, sidebar_width, color_primary, color_success, color_warning, color_destructive, radius, vue_files, common_components, form_components FROM style_meta").fetchall()
|
|
216
|
+
for row in meta_rows:
|
|
217
|
+
proj = row[0]
|
|
218
|
+
proj_data = result['projects'].setdefault(proj, {'page_types': {}, 'page_examples': {}})
|
|
219
|
+
proj_data['layout_fingerprint'] = {
|
|
220
|
+
'stack': row[1], 'layout': row[2], 'access_mode': row[3],
|
|
221
|
+
'sidebar_width': row[4], 'color_primary': row[5], 'color_success': row[6],
|
|
222
|
+
'color_warning': row[7], 'color_destructive': row[8], 'radius': row[9],
|
|
223
|
+
}
|
|
224
|
+
proj_data['vue_files'] = row[10]
|
|
225
|
+
if row[11]:
|
|
226
|
+
result['common_components'] = json.loads(row[11])
|
|
227
|
+
if row[12]:
|
|
228
|
+
result['form_components'] = json.loads(row[12])
|
|
229
|
+
return result if result['projects'] or result['field_map'] else None
|
|
230
|
+
# empty_sentinel: 全空结构 (version 是固定键, 不算数据)
|
|
231
|
+
return _load_with_pickle_cache(
|
|
232
|
+
con, 'ui-style', _style_loader,
|
|
233
|
+
empty_sentinel={'version': '1.0', 'projects': {}, 'common_components': {}, 'form_components': {}, 'field_map': {}})
|
|
207
234
|
|
|
208
235
|
elif filename == 'prd-index.json':
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
236
|
+
def _prd_loader(con):
|
|
237
|
+
rows = con.execute("SELECT file, title, keywords, features, cn_terms FROM prds").fetchall()
|
|
238
|
+
result = {}
|
|
239
|
+
for f, title, kws, feats, cn in rows:
|
|
240
|
+
result[f] = {'title': title,
|
|
241
|
+
'keywords': json.loads(kws) if kws else [],
|
|
242
|
+
'features': json.loads(feats) if feats else [],
|
|
243
|
+
'cn_terms': json.loads(cn) if cn else []}
|
|
244
|
+
return result if result else None
|
|
245
|
+
return _load_with_pickle_cache(con, 'prd-index', _prd_loader)
|
|
217
246
|
|
|
218
247
|
elif filename == 'ui-modules.json':
|
|
219
248
|
rows = con.execute("SELECT dir, cn, project, path FROM modules").fetchall()
|
|
@@ -337,7 +366,7 @@ def _check_index_freshness():
|
|
|
337
366
|
if warn_mtime > meta_mtime:
|
|
338
367
|
return # 已经提示过了 (在本次索引周期内)
|
|
339
368
|
print('[提示] 代码有更新但知识图谱未刷新 (上次索引: %s)' % last_sync_str)
|
|
340
|
-
print(' 刷新: python .qoder/scripts/git_sync.py --index-only')
|
|
369
|
+
print(' 刷新: python .qoder/scripts/domain/task/git_sync.py --index-only')
|
|
341
370
|
try:
|
|
342
371
|
os.makedirs(os.path.dirname(warned_path), exist_ok=True)
|
|
343
372
|
open(warned_path, 'w').close()
|
|
@@ -415,12 +444,12 @@ def _cleanup_cache_dir(cache_dir, prefix, max_files=50):
|
|
|
415
444
|
|
|
416
445
|
|
|
417
446
|
# _expand_chinese_query 已移至 common/terms.py (单一数据源, context_pack 共用)
|
|
418
|
-
# import 见文件头部: from
|
|
447
|
+
# import 见文件头部: from foundation.integrations.terms import expand_chinese_query
|
|
419
448
|
|
|
420
449
|
|
|
421
450
|
def _search_keywords_impl(query, platform=None):
|
|
422
451
|
"""实际的搜索逻辑 (原 search_keywords 内容)。"""
|
|
423
|
-
ki = load_index('code-keyword.json', hint='Run: python git_sync.py')
|
|
452
|
+
ki = load_index('code-keyword.json', hint='Run: python .qoder/scripts/domain/task/git_sync.py')
|
|
424
453
|
if ki is None:
|
|
425
454
|
return
|
|
426
455
|
|
|
@@ -437,8 +466,8 @@ def _search_keywords_impl(query, platform=None):
|
|
|
437
466
|
|
|
438
467
|
# 性能优化 A2: 用倒排索引缩小候选集 (从 O(all_keys) 降到 O(命中数))
|
|
439
468
|
try:
|
|
440
|
-
from
|
|
441
|
-
from
|
|
469
|
+
from search_engine import get_inverted_index, search_keywords_fast
|
|
470
|
+
from foundation.core.paths import get_repo_root
|
|
442
471
|
inverted = get_inverted_index(str(get_repo_root()))
|
|
443
472
|
kw_matches = search_keywords_fast(words, inverted, ki)
|
|
444
473
|
except Exception:
|
|
@@ -460,6 +489,7 @@ def _search_keywords_impl(query, platform=None):
|
|
|
460
489
|
|
|
461
490
|
if not matches:
|
|
462
491
|
print('No matches for: {}'.format(query))
|
|
492
|
+
_semantic_fallback(query, platform)
|
|
463
493
|
return
|
|
464
494
|
|
|
465
495
|
total = 0
|
|
@@ -478,8 +508,44 @@ def _search_keywords_impl(query, platform=None):
|
|
|
478
508
|
print('Total: {} matches'.format(total))
|
|
479
509
|
|
|
480
510
|
|
|
511
|
+
def _semantic_fallback(query, platform=None):
|
|
512
|
+
"""关键词 0 命中时, 用语义检索 (bge-small-zh embedding) 补一轮。
|
|
513
|
+
|
|
514
|
+
解决"中文查英文代码"的漏召: 用户查"报销"时关键词匹配不到 reimburse/expense,
|
|
515
|
+
但语义向量能跨语言命中。把命中的实体解析成文件, 按 platform 过滤后列出。
|
|
516
|
+
|
|
517
|
+
模型未装/无 embedding → 静默跳过, 不报错 (符合优雅降级原则)。
|
|
518
|
+
"""
|
|
519
|
+
try:
|
|
520
|
+
from domain.kg.graph.kg_semantic import semantic_search_files
|
|
521
|
+
except ImportError:
|
|
522
|
+
return
|
|
523
|
+
results = semantic_search_files(query, top_k=8)
|
|
524
|
+
if not results:
|
|
525
|
+
return
|
|
526
|
+
# 按 platform 过滤 + 补全项目前缀 (相对 vue 路径用存在性兜底)
|
|
527
|
+
filtered = []
|
|
528
|
+
try:
|
|
529
|
+
from domain.kg.graph.kg_semantic import filter_semantic_by_platform
|
|
530
|
+
filtered = filter_semantic_by_platform(results, platform, BASE)
|
|
531
|
+
except Exception:
|
|
532
|
+
filtered = [(fp.replace('\\', '/'), cn, sc) for fp, eid, cn, sc in results]
|
|
533
|
+
if not filtered:
|
|
534
|
+
return
|
|
535
|
+
print()
|
|
536
|
+
print('[语义补充] 关键词未命中, 用语义检索找到 (中→英跨语言):')
|
|
537
|
+
for fp, cn, score in filtered[:6]:
|
|
538
|
+
short = fp
|
|
539
|
+
if len(short.split('/')) > 5:
|
|
540
|
+
parts = short.split('/')
|
|
541
|
+
short = '/'.join(parts[:2]) + '/.../' + '/'.join(parts[-2:])
|
|
542
|
+
full = 'data/code/' + fp
|
|
543
|
+
print(' %.2f %s -> %s' % (score, cn[:20], full))
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
|
|
481
547
|
def search_prds(query):
|
|
482
|
-
prd_index = load_index('prd-index.json', hint='Run: python git_sync.py --prd-only')
|
|
548
|
+
prd_index = load_index('prd-index.json', hint='Run: python .qoder/scripts/domain/task/git_sync.py --prd-only')
|
|
483
549
|
if prd_index is None:
|
|
484
550
|
return
|
|
485
551
|
q = query.lower()
|
|
@@ -580,7 +646,7 @@ def search_field(field_name):
|
|
|
580
646
|
|
|
581
647
|
def search_api(query):
|
|
582
648
|
"""Search API endpoints in code-api.json."""
|
|
583
|
-
ai = load_index('code-api.json', hint='Run: python git_sync.py --index-only')
|
|
649
|
+
ai = load_index('code-api.json', hint='Run: python .qoder/scripts/domain/task/git_sync.py --index-only')
|
|
584
650
|
if ai is None:
|
|
585
651
|
return
|
|
586
652
|
q = query.lower()
|
|
@@ -597,7 +663,7 @@ def search_api(query):
|
|
|
597
663
|
|
|
598
664
|
def show_modules():
|
|
599
665
|
"""Show project overview from code-stats.json."""
|
|
600
|
-
mm = load_index('code-stats.json', hint='Run: python git_sync.py --index-only')
|
|
666
|
+
mm = load_index('code-stats.json', hint='Run: python .qoder/scripts/domain/task/git_sync.py --index-only')
|
|
601
667
|
if mm is None:
|
|
602
668
|
return
|
|
603
669
|
print('=== Project Modules ===')
|
|
@@ -611,7 +677,7 @@ def show_modules():
|
|
|
611
677
|
|
|
612
678
|
def show_top_keywords():
|
|
613
679
|
"""Show top keywords by file count."""
|
|
614
|
-
ki = load_index('code-keyword.json', hint='Run: python git_sync.py')
|
|
680
|
+
ki = load_index('code-keyword.json', hint='Run: python .qoder/scripts/domain/task/git_sync.py')
|
|
615
681
|
if ki is None:
|
|
616
682
|
return
|
|
617
683
|
top = sorted(ki.items(), key=lambda x: len(x[1]), reverse=True)[:50]
|
|
@@ -666,8 +732,8 @@ def show_vben():
|
|
|
666
732
|
def search_wiki_cli(query):
|
|
667
733
|
"""搜 Repo Wiki (Qoder IDE 生成的语义级模块文档)。"""
|
|
668
734
|
try:
|
|
669
|
-
from
|
|
670
|
-
from
|
|
735
|
+
from repowiki import search_wiki, build_wiki_index
|
|
736
|
+
from foundation.integrations.terms import get_cn_map_with_auto
|
|
671
737
|
except ImportError:
|
|
672
738
|
print('Wiki 搜索不可用 (common.repowiki 模块缺失)')
|
|
673
739
|
return
|
|
@@ -712,7 +778,7 @@ def search_wiki_cli(query):
|
|
|
712
778
|
|
|
713
779
|
def search_impact(query):
|
|
714
780
|
"""改这个接口/符号影响哪些页面/按钮/模块? (U1: 内存图遍历)"""
|
|
715
|
-
from
|
|
781
|
+
from domain.kg.graph.graph_traverse import CodeGraph
|
|
716
782
|
g = CodeGraph(INDEX_DIR)
|
|
717
783
|
result = g.impact(query)
|
|
718
784
|
if result:
|
|
@@ -724,7 +790,7 @@ def search_impact(query):
|
|
|
724
790
|
|
|
725
791
|
def search_context360(query):
|
|
726
792
|
"""符号的360度视图: 端点/函数/处理器/PRD (U1: 内存图遍历)"""
|
|
727
|
-
from
|
|
793
|
+
from domain.kg.graph.graph_traverse import CodeGraph
|
|
728
794
|
g = CodeGraph(INDEX_DIR)
|
|
729
795
|
result = g.context360(query)
|
|
730
796
|
if result:
|
|
@@ -810,8 +876,8 @@ def _graph_cli(method, *args):
|
|
|
810
876
|
try:
|
|
811
877
|
if method == 'get_workflow':
|
|
812
878
|
# DuckDB 路径 (v3.0: kg_duckdb 已迁入 kg/ 子包)
|
|
813
|
-
from kg.kg_duckdb import get_db as _kg_get_db
|
|
814
|
-
import kg.kg_duckdb as K
|
|
879
|
+
from domain.kg.storage.kg_duckdb import get_db as _kg_get_db
|
|
880
|
+
import domain.kg.storage.kg_duckdb as K
|
|
815
881
|
q = (args[0] if args else '').strip()
|
|
816
882
|
if not q:
|
|
817
883
|
print('请提供模块名, 如: assets / cost / hr / maintenance')
|
|
@@ -830,8 +896,8 @@ def _graph_cli(method, *args):
|
|
|
830
896
|
print(' %d. [%s] %s — %s' % (r[0], r[1] or '?', r[2] or '', r[3] or ''))
|
|
831
897
|
return
|
|
832
898
|
# JSON 索引路径 (CodeGraph)
|
|
833
|
-
from
|
|
834
|
-
from
|
|
899
|
+
from domain.kg.graph.graph_traverse import CodeGraph
|
|
900
|
+
from foundation.core.paths import DATA_INDEX_DIR
|
|
835
901
|
g = CodeGraph(str(DATA_INDEX_DIR))
|
|
836
902
|
fn = getattr(g, method, None)
|
|
837
903
|
if not fn:
|
|
File without changes
|