@hupan56/wlkj 2.7.12 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +1435 -1107
- package/package.json +29 -29
- package/templates/qoder/agents/insight-planning.md +1 -1
- package/templates/qoder/agents/insight-research.md +28 -15
- package/templates/qoder/commands/optional/wl-insight.md +161 -161
- package/templates/qoder/commands/optional/wl-report.md +20 -39
- package/templates/qoder/commands/optional/wl-status.md +4 -4
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +6 -6
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +26 -13
- package/templates/qoder/commands/wl-req.md +43 -0
- package/templates/qoder/commands/wl-search.md +78 -47
- package/templates/qoder/commands/wl-task.md +343 -40
- package/templates/qoder/commands/wl-test.md +43 -17
- package/templates/qoder/config.yaml +52 -14
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +94 -23
- package/templates/qoder/hooks/stop-eval.py +207 -0
- package/templates/qoder/hooks/user-prompt-submit.py +139 -0
- package/templates/qoder/rules/wl-pipeline.md +85 -49
- package/templates/qoder/scripts/README.md +102 -0
- package/templates/qoder/scripts/capability/__init__.py +26 -0
- package/templates/qoder/scripts/capability/__main__.py +72 -0
- package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
- package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
- package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
- package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
- package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
- package/templates/qoder/scripts/capability/caps/context.py +36 -0
- package/templates/qoder/scripts/capability/caps/cron.py +50 -0
- package/templates/qoder/scripts/capability/caps/identity.py +43 -0
- package/templates/qoder/scripts/capability/caps/memory.py +71 -0
- package/templates/qoder/scripts/capability/caps/notify.py +41 -0
- package/templates/qoder/scripts/capability/caps/present.py +48 -0
- package/templates/qoder/scripts/capability/caps/repo.py +31 -0
- package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
- package/templates/qoder/scripts/capability/chain.py +92 -0
- package/templates/qoder/scripts/capability/memory_chain.py +41 -0
- package/templates/qoder/scripts/capability/registry.py +246 -0
- package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
- package/templates/qoder/scripts/capability/smoke_test.py +211 -0
- package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
- package/templates/qoder/scripts/deployment/setup/__init__.py +11 -0
- package/templates/qoder/scripts/deployment/setup/carriers.py +669 -0
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{init_doctor.py → deployment/setup/init_doctor.py} +100 -39
- package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py} +48 -30
- package/templates/qoder/scripts/{platform_doctor.py → deployment/setup/platform_doctor.py} +271 -259
- package/templates/qoder/scripts/{repo_root.py → deployment/setup/repo_root.py} +9 -2
- package/templates/qoder/scripts/{setup.py → deployment/setup/setup.py} +143 -14
- package/templates/qoder/scripts/{setup_lanhu.py → deployment/setup/setup_lanhu.py} +979 -963
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{fill_prototype.py → domain/design/fill_prototype.py} +17 -5
- package/templates/qoder/scripts/{gen_design_doc.py → domain/design/gen_design_doc.py} +406 -394
- package/templates/qoder/scripts/domain/kg/__init__.py +11 -0
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +201 -0
- package/templates/qoder/scripts/domain/kg/build/build_relations.py +132 -0
- package/templates/qoder/scripts/{build_style_index.py → domain/kg/build/build_style_index.py} +50 -15
- package/templates/qoder/scripts/domain/kg/build/build_workflows.py +149 -0
- package/templates/qoder/scripts/{kg_build.py → domain/kg/build/kg_build.py} +685 -612
- package/templates/qoder/scripts/{kg_build_db.py → domain/kg/build/kg_build_db.py} +338 -327
- package/templates/qoder/scripts/{kg_incremental.py → domain/kg/build/kg_incremental.py} +425 -393
- package/templates/qoder/scripts/{learn_aggregate.py → domain/kg/build/learn_aggregate.py} +212 -201
- package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
- package/templates/qoder/scripts/domain/kg/extract/test_extract.py +115 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
- package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_link_db.py → domain/kg/graph/kg_link_db.py} +235 -224
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg.py → domain/kg/kg.py} +871 -708
- package/templates/qoder/scripts/domain/kg/kg_capabilities.py +182 -0
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{context_pack.py → domain/kg/search/context_pack.py} +45 -17
- package/templates/qoder/scripts/{enrich_prompt.py → domain/kg/search/enrich_prompt.py} +238 -226
- package/templates/qoder/scripts/domain/kg/search/prefetch.py +384 -0
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{search_index.py → domain/kg/search/search_index.py} +210 -88
- package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
- package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
- package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_duckdb.py → domain/kg/storage/kg_duckdb.py} +70 -45
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{learn.py → domain/learning/learn.py} +157 -146
- package/templates/qoder/scripts/domain/report/__init__.py +11 -0
- package/templates/qoder/scripts/{add_session.py → domain/report/add_session.py} +256 -244
- package/templates/qoder/scripts/{export.py → domain/report/export.py} +72 -4
- package/templates/qoder/scripts/{report.py → domain/report/report.py} +292 -281
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/domain/report/role.py +39 -0
- package/templates/qoder/scripts/{status.py → domain/report/status.py} +737 -628
- package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
- package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
- package/templates/qoder/scripts/{archive_prd.py → domain/requirement/archive_prd.py} +389 -377
- package/templates/qoder/scripts/domain/requirement/req.py +228 -0
- package/templates/qoder/scripts/domain/task/__init__.py +11 -0
- package/templates/qoder/scripts/{git_sync.py → domain/task/git_sync.py} +90 -50
- package/templates/qoder/scripts/{syncgate.py → domain/task/syncgate.py} +18 -7
- package/templates/qoder/scripts/domain/task/task.py +229 -0
- package/templates/qoder/scripts/domain/task/task_lifecycle.py +606 -0
- package/templates/qoder/scripts/domain/task/task_query.py +162 -0
- package/templates/qoder/scripts/domain/task/task_relations.py +425 -0
- package/templates/qoder/scripts/{team_sync.py → domain/task/team_sync.py} +101 -23
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/bootstrap.py +145 -0
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/core/cmd_registry.py +112 -0
- package/templates/qoder/scripts/foundation/core/config.py +187 -0
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -400
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/data/contract.py +317 -0
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/foundation/data/result.py +223 -0
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -392
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/identity/check_publish.py +103 -0
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -238
- package/templates/qoder/scripts/foundation/identity/guard.py +159 -0
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +132 -9
- package/templates/qoder/scripts/foundation/identity/roles.py +60 -0
- package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
- package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +11 -10
- package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
- package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
- package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
- package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
- package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
- package/templates/qoder/scripts/foundation/protocol/mcp_base.py +268 -0
- package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
- package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
- package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
- package/templates/qoder/scripts/orchestration/__init__.py +0 -0
- package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
- package/templates/qoder/scripts/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/protocol/browser/README.md +207 -0
- package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
- package/templates/qoder/scripts/protocol/browser/config.env +9 -0
- package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
- package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
- package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
- package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
- package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
- package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
- package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
- package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
- package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
- package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
- package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → protocol/mcp/lanhu_stdio_wrapper.py} +131 -119
- package/templates/qoder/scripts/protocol/mcp/mcp_doctor.py +548 -0
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/protocol/mcp/mysql_mcp_server.py +461 -0
- package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
- package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
- package/templates/qoder/scripts/protocol/transports/base.py +87 -0
- package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
- package/templates/qoder/scripts/protocol/transports/http.py +141 -0
- package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
- package/templates/qoder/scripts/run_weekly_update.bat +27 -18
- package/templates/qoder/scripts/run_weekly_update.sh +22 -13
- package/templates/qoder/scripts/validation/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
- package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
- package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
- package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
- package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
- package/templates/qoder/scripts/{eval_prd.py → validation/metrics/eval_prd.py} +84 -15
- package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
- package/templates/qoder/scripts/validation/test/__init__.py +11 -0
- package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
- package/templates/qoder/scripts/{autotest.py → validation/test/autotest.py} +1234 -1751
- package/templates/qoder/scripts/validation/test/autotest_auth.py +109 -0
- package/templates/qoder/scripts/{autotest_batch.py → validation/test/autotest_batch.py} +255 -224
- package/templates/qoder/scripts/validation/test/autotest_data.py +680 -0
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{autotest_run.py → validation/test/autotest_run.py} +323 -297
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{benchmark.py → validation/test/benchmark.py} +17 -5
- package/templates/qoder/scripts/{kg_auto_login.py → validation/test/kg_auto_login.py} +208 -196
- package/templates/qoder/scripts/{kg_test_runner.py → validation/test/kg_test_runner.py} +13 -2
- package/templates/qoder/scripts/{page_probe.py → validation/test/page_probe.py} +470 -459
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +23 -2
- package/templates/qoder/skills/design-import/SKILL.md +235 -226
- package/templates/qoder/skills/design-review/SKILL.md +91 -82
- package/templates/qoder/skills/prd-generator/SKILL.md +38 -24
- package/templates/qoder/skills/prd-review/SKILL.md +8 -6
- package/templates/qoder/skills/prototype-generator/SKILL.md +296 -247
- package/templates/qoder/skills/spec-coder/SKILL.md +24 -15
- package/templates/qoder/skills/spec-generator/SKILL.md +24 -12
- package/templates/qoder/skills/test-generator/SKILL.md +9 -7
- package/templates/qoder/skills/wl-code/SKILL.md +25 -13
- package/templates/qoder/skills/wl-commit/SKILL.md +10 -9
- package/templates/qoder/skills/wl-design/SKILL.md +7 -5
- package/templates/qoder/skills/wl-init/SKILL.md +8 -8
- package/templates/qoder/skills/wl-insight/SKILL.md +25 -12
- package/templates/qoder/skills/wl-prd-full/SKILL.md +59 -8
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +7 -7
- package/templates/qoder/skills/wl-prd-review/SKILL.md +18 -6
- package/templates/qoder/skills/wl-report/SKILL.md +27 -25
- package/templates/qoder/skills/wl-search/SKILL.md +151 -80
- package/templates/qoder/skills/wl-spec/SKILL.md +9 -7
- package/templates/qoder/skills/wl-status/SKILL.md +50 -18
- package/templates/qoder/skills/wl-task/SKILL.md +94 -12
- package/templates/qoder/skills/wl-test/SKILL.md +154 -45
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -237
- package/templates/root/requirements.txt +3 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
- package/templates/qoder/hooks/inject-workflow-state.py +0 -169
- package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/check_carriers.py +0 -238
- package/templates/qoder/scripts/check_mcp.py +0 -298
- package/templates/qoder/scripts/check_mcp_launch.py +0 -183
- package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
- package/templates/qoder/scripts/collect_prds.py +0 -31
- package/templates/qoder/scripts/common/mentions.py +0 -134
- package/templates/qoder/scripts/common/utf8.py +0 -38
- package/templates/qoder/scripts/extract_api_params.py +0 -246
- package/templates/qoder/scripts/extract_routes.py +0 -54
- package/templates/qoder/scripts/extract_routes_tree.py +0 -78
- package/templates/qoder/scripts/handoff.py +0 -22
- package/templates/qoder/scripts/init_developer.py +0 -76
- package/templates/qoder/scripts/kg_mcp_server.py +0 -801
- package/templates/qoder/scripts/kg_semantic.py +0 -150
- package/templates/qoder/scripts/mcp_launcher.py +0 -414
- package/templates/qoder/scripts/mysql_mcp_server.py +0 -396
- package/templates/qoder/scripts/parse_prds.py +0 -33
- package/templates/qoder/scripts/role.py +0 -51
- package/templates/qoder/scripts/sync_carriers.py +0 -259
- package/templates/qoder/scripts/task.py +0 -1261
- package/templates/qoder/scripts/workspace_init.py +0 -102
- package/templates/qoder/scripts/zentao_mcp_server.py +0 -424
- package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
- /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
- /package/templates/qoder/{skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp → scripts/domain/__init__.py} +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
- /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
- /package/templates/qoder/scripts/{secure-ls.js → validation/test/secure-ls.js} +0 -0
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
+
# v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
|
|
4
|
+
import os as _o, sys as _s
|
|
5
|
+
_f = _o.path.abspath(__file__)
|
|
6
|
+
for _ in range(10):
|
|
7
|
+
_f = _o.path.dirname(_f)
|
|
8
|
+
_cp = _o.path.join(_f, 'foundation')
|
|
9
|
+
if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
|
|
10
|
+
break
|
|
11
|
+
if _cp not in _s.path: _s.path.insert(0, _cp)
|
|
12
|
+
from bootstrap import setup; setup()
|
|
13
|
+
from foundation.core.paths import get_repo_root
|
|
14
|
+
|
|
3
15
|
"""
|
|
4
16
|
Search knowledge index with platform filtering and PRD search.
|
|
5
17
|
|
|
@@ -27,10 +39,10 @@ except (AttributeError, TypeError, OSError, IOError):
|
|
|
27
39
|
pass
|
|
28
40
|
|
|
29
41
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
30
|
-
from
|
|
42
|
+
from foundation.integrations.terms import CN_MAP, BUSINESS_PATH_MAP, CN_TO_EN, get_platform_map, expand_chinese_query
|
|
31
43
|
PLATFORM_MAP = get_platform_map()
|
|
32
44
|
|
|
33
|
-
BASE =
|
|
45
|
+
BASE = get_repo_root()
|
|
34
46
|
INDEX_DIR = os.path.join(BASE, 'data', 'index')
|
|
35
47
|
|
|
36
48
|
# Substring matching against keys shorter than this produces too much noise
|
|
@@ -82,17 +94,45 @@ def _get_duckdb():
|
|
|
82
94
|
return None
|
|
83
95
|
|
|
84
96
|
|
|
85
|
-
|
|
86
|
-
|
|
97
|
+
# 第二个 DuckDB (kg_db.duckdb, 数据库图谱) 的惰性连接缓存。
|
|
98
|
+
# context_pack 第 8 段每次都新开连接读这个库, 与上面的 kg.duckdb 单例不对称,
|
|
99
|
+
# 每次调用多一次连接开销。这里镜像 _get_duckdb 的单例模式, 供 context_pack 复用。
|
|
100
|
+
_kgdb_con = None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _get_kg_db():
|
|
104
|
+
global _kgdb_con
|
|
105
|
+
if _kgdb_con is not None:
|
|
106
|
+
return _kgdb_con
|
|
107
|
+
db_path = os.path.join(INDEX_DIR, 'kg_db.duckdb')
|
|
108
|
+
if not os.path.isfile(db_path):
|
|
109
|
+
return None
|
|
110
|
+
try:
|
|
111
|
+
import duckdb
|
|
112
|
+
_kgdb_con = duckdb.connect(db_path, read_only=True)
|
|
113
|
+
return _kgdb_con
|
|
114
|
+
except Exception:
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _load_with_pickle_cache(con, cache_name, loader_fn, empty_sentinel=None):
|
|
119
|
+
"""通用 pickle/mtime 缓存层 (抽自 _load_keyword_with_cache, 推广到全部索引)。
|
|
87
120
|
|
|
88
|
-
性能: DuckDB
|
|
89
|
-
pickle.load
|
|
121
|
+
性能: ui-style 等索引每次从 DuckDB 全量重建 (3 次查询 + 逐行 json.loads)
|
|
122
|
+
耗时显著; pickle.load 直接读回对象快数倍。code-keyword 实测 180ms→37ms。
|
|
90
123
|
缓存失效: kg.duckdb 的 mtime 变了就重建 (和 context_pack 缓存逻辑一致)。
|
|
91
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)。
|
|
92
132
|
"""
|
|
93
133
|
import pickle
|
|
94
134
|
duckdb_path = os.path.join(INDEX_DIR, 'kg.duckdb')
|
|
95
|
-
cache_path = os.path.join(BASE, '.qoder', '.runtime', '
|
|
135
|
+
cache_path = os.path.join(BASE, '.qoder', '.runtime', cache_name + '.pkl')
|
|
96
136
|
db_mtime = os.path.getmtime(duckdb_path) if os.path.isfile(duckdb_path) else 0
|
|
97
137
|
|
|
98
138
|
# ① 缓存有效 (存在 + mtime 匹配) → 直接 load, 跳过 DuckDB
|
|
@@ -105,18 +145,17 @@ def _load_keyword_with_cache(con):
|
|
|
105
145
|
pass # 缓存损坏 → 回退重建
|
|
106
146
|
|
|
107
147
|
# ② 缓存无效/缺失 → 从 DuckDB 重建
|
|
108
|
-
|
|
109
|
-
result
|
|
110
|
-
|
|
111
|
-
result.setdefault(kw, []).append(fp)
|
|
112
|
-
if not result:
|
|
113
|
-
return None
|
|
148
|
+
result = loader_fn(con)
|
|
149
|
+
if result is None or result == empty_sentinel:
|
|
150
|
+
return None # 无数据, 不缓存 (避免缓存空结果误导后续)
|
|
114
151
|
|
|
115
|
-
# ③ 写缓存 (best-effort,
|
|
152
|
+
# ③ 写缓存 (best-effort, 失败不影响返回结果; 用原子写防止半文件)
|
|
116
153
|
try:
|
|
117
154
|
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
|
|
118
|
-
|
|
155
|
+
tmp = cache_path + '.tmp'
|
|
156
|
+
with open(tmp, 'wb') as f:
|
|
119
157
|
pickle.dump(result, f, protocol=pickle.HIGHEST_PROTOCOL)
|
|
158
|
+
os.replace(tmp, cache_path)
|
|
120
159
|
except OSError:
|
|
121
160
|
pass
|
|
122
161
|
return result
|
|
@@ -135,58 +174,75 @@ def _duckdb_load(filename):
|
|
|
135
174
|
try:
|
|
136
175
|
if filename == 'code-keyword.json':
|
|
137
176
|
# {keyword: [files]} — 11836 行重建耗时 180ms, 用 pickle 缓存提速到 37ms
|
|
138
|
-
|
|
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)
|
|
139
184
|
|
|
140
185
|
elif filename == 'code-api.json':
|
|
141
186
|
# {endpoint: controller_file}
|
|
142
|
-
|
|
143
|
-
|
|
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)
|
|
144
191
|
|
|
145
192
|
elif filename == 'ui-style.json':
|
|
146
193
|
# Reconstruct the full ui-style structure from pages/fields/style_meta
|
|
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
|
-
|
|
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': {}})
|
|
180
234
|
|
|
181
235
|
elif filename == 'prd-index.json':
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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)
|
|
190
246
|
|
|
191
247
|
elif filename == 'ui-modules.json':
|
|
192
248
|
rows = con.execute("SELECT dir, cn, project, path FROM modules").fetchall()
|
|
@@ -227,11 +283,24 @@ def _match_keyword(query_word, kw):
|
|
|
227
283
|
|
|
228
284
|
|
|
229
285
|
def _search_cache_key(query, platform):
|
|
230
|
-
"""缓存 key: query|platform
|
|
286
|
+
"""缓存 key: query|platform|数据源 mtime 指纹。
|
|
287
|
+
|
|
288
|
+
数据真正存在 DuckDB(kg.duckdb), code-keyword.json 在 DuckDB 模式下不存在。
|
|
289
|
+
若只用 JSON 的 mtime 做指纹, DuckDB 模式下恒为 '0' —— DuckDB 增量更新后,
|
|
290
|
+
旧的搜索缓存仍会命中, 返回陈旧结果(与 context_pack 同源 bug, 那边已修)。
|
|
291
|
+
所以指纹必须以 kg.duckdb mtime 为主, JSON 为辅。
|
|
292
|
+
"""
|
|
231
293
|
import hashlib
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
294
|
+
mtime_str = ''
|
|
295
|
+
# ① DuckDB (数据真正来源, 必须算)
|
|
296
|
+
duckdb_path = os.path.join(BASE, 'data', 'index', 'kg.duckdb')
|
|
297
|
+
if os.path.isfile(duckdb_path):
|
|
298
|
+
mtime_str += 'duck:%d_' % os.path.getmtime(duckdb_path)
|
|
299
|
+
# ② JSON 索引 (DuckDB 缺失时的降级数据源, 一起算)
|
|
300
|
+
ki_path = os.path.join(BASE, 'data', 'index', 'code-keyword.json')
|
|
301
|
+
if os.path.isfile(ki_path):
|
|
302
|
+
mtime_str += 'kw:%d' % os.path.getmtime(ki_path)
|
|
303
|
+
raw = '{}|{}|{}'.format(query.lower(), platform or '', mtime_str or '0')
|
|
235
304
|
return hashlib.md5(raw.encode('utf-8')).hexdigest()[:16]
|
|
236
305
|
|
|
237
306
|
|
|
@@ -297,7 +366,7 @@ def _check_index_freshness():
|
|
|
297
366
|
if warn_mtime > meta_mtime:
|
|
298
367
|
return # 已经提示过了 (在本次索引周期内)
|
|
299
368
|
print('[提示] 代码有更新但知识图谱未刷新 (上次索引: %s)' % last_sync_str)
|
|
300
|
-
print(' 刷新: python .qoder/scripts/git_sync.py --index-only')
|
|
369
|
+
print(' 刷新: python .qoder/scripts/domain/task/git_sync.py --index-only')
|
|
301
370
|
try:
|
|
302
371
|
os.makedirs(os.path.dirname(warned_path), exist_ok=True)
|
|
303
372
|
open(warned_path, 'w').close()
|
|
@@ -308,10 +377,9 @@ def _check_index_freshness():
|
|
|
308
377
|
|
|
309
378
|
|
|
310
379
|
def search_keywords(query, platform=None):
|
|
311
|
-
#
|
|
312
|
-
_check_index_freshness
|
|
313
|
-
|
|
314
|
-
# 结果缓存: 同 query+platform+索引未变 → 直接返回缓存输出
|
|
380
|
+
# 先查缓存 (命中即数据未变, 直接还)。缓存 key 已含 kg.duckdb mtime, 命中就说明
|
|
381
|
+
# 数据源没变, 跳过 _check_index_freshness 的 os.walk —— 50 人团队每天几十次
|
|
382
|
+
# 搜索, 每次都 walk 目录是纯浪费。未命中才做新鲜度检查 + 计算。
|
|
315
383
|
cache_key = _search_cache_key(query, platform)
|
|
316
384
|
cache_path = os.path.join(BASE, '.qoder', '.runtime', 'search-cache-%s.txt' % cache_key)
|
|
317
385
|
if os.path.isfile(cache_path):
|
|
@@ -324,6 +392,9 @@ def search_keywords(query, platform=None):
|
|
|
324
392
|
except OSError:
|
|
325
393
|
pass
|
|
326
394
|
|
|
395
|
+
# 缓存未命中 → 才做新鲜度检查 (代码改了但索引没更新 → 提示, 不阻塞)
|
|
396
|
+
_check_index_freshness()
|
|
397
|
+
|
|
327
398
|
# 缓存未命中, 正常计算并写入缓存
|
|
328
399
|
import io
|
|
329
400
|
old_stdout = sys.stdout
|
|
@@ -373,12 +444,12 @@ def _cleanup_cache_dir(cache_dir, prefix, max_files=50):
|
|
|
373
444
|
|
|
374
445
|
|
|
375
446
|
# _expand_chinese_query 已移至 common/terms.py (单一数据源, context_pack 共用)
|
|
376
|
-
# import 见文件头部: from
|
|
447
|
+
# import 见文件头部: from foundation.integrations.terms import expand_chinese_query
|
|
377
448
|
|
|
378
449
|
|
|
379
450
|
def _search_keywords_impl(query, platform=None):
|
|
380
451
|
"""实际的搜索逻辑 (原 search_keywords 内容)。"""
|
|
381
|
-
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')
|
|
382
453
|
if ki is None:
|
|
383
454
|
return
|
|
384
455
|
|
|
@@ -395,8 +466,8 @@ def _search_keywords_impl(query, platform=None):
|
|
|
395
466
|
|
|
396
467
|
# 性能优化 A2: 用倒排索引缩小候选集 (从 O(all_keys) 降到 O(命中数))
|
|
397
468
|
try:
|
|
398
|
-
from
|
|
399
|
-
from
|
|
469
|
+
from search_engine import get_inverted_index, search_keywords_fast
|
|
470
|
+
from foundation.core.paths import get_repo_root
|
|
400
471
|
inverted = get_inverted_index(str(get_repo_root()))
|
|
401
472
|
kw_matches = search_keywords_fast(words, inverted, ki)
|
|
402
473
|
except Exception:
|
|
@@ -418,6 +489,7 @@ def _search_keywords_impl(query, platform=None):
|
|
|
418
489
|
|
|
419
490
|
if not matches:
|
|
420
491
|
print('No matches for: {}'.format(query))
|
|
492
|
+
_semantic_fallback(query, platform)
|
|
421
493
|
return
|
|
422
494
|
|
|
423
495
|
total = 0
|
|
@@ -436,8 +508,44 @@ def _search_keywords_impl(query, platform=None):
|
|
|
436
508
|
print('Total: {} matches'.format(total))
|
|
437
509
|
|
|
438
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
|
+
|
|
439
547
|
def search_prds(query):
|
|
440
|
-
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')
|
|
441
549
|
if prd_index is None:
|
|
442
550
|
return
|
|
443
551
|
q = query.lower()
|
|
@@ -538,7 +646,7 @@ def search_field(field_name):
|
|
|
538
646
|
|
|
539
647
|
def search_api(query):
|
|
540
648
|
"""Search API endpoints in code-api.json."""
|
|
541
|
-
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')
|
|
542
650
|
if ai is None:
|
|
543
651
|
return
|
|
544
652
|
q = query.lower()
|
|
@@ -555,7 +663,7 @@ def search_api(query):
|
|
|
555
663
|
|
|
556
664
|
def show_modules():
|
|
557
665
|
"""Show project overview from code-stats.json."""
|
|
558
|
-
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')
|
|
559
667
|
if mm is None:
|
|
560
668
|
return
|
|
561
669
|
print('=== Project Modules ===')
|
|
@@ -569,7 +677,7 @@ def show_modules():
|
|
|
569
677
|
|
|
570
678
|
def show_top_keywords():
|
|
571
679
|
"""Show top keywords by file count."""
|
|
572
|
-
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')
|
|
573
681
|
if ki is None:
|
|
574
682
|
return
|
|
575
683
|
top = sorted(ki.items(), key=lambda x: len(x[1]), reverse=True)[:50]
|
|
@@ -580,20 +688,33 @@ def show_top_keywords():
|
|
|
580
688
|
|
|
581
689
|
|
|
582
690
|
def show_components():
|
|
583
|
-
|
|
584
|
-
|
|
691
|
+
# 组件统计存在 DuckDB style_meta.common_components/form_components (JSON 列),
|
|
692
|
+
# 由 kg_build -> kg_duckdb 从 ui-style.json 导入。
|
|
693
|
+
# 注意: 不是独立 JSON 文件, 走 DuckDB 路径。
|
|
694
|
+
si = load_index('ui-style.json')
|
|
695
|
+
if si is None:
|
|
696
|
+
print('UI 样式索引未构建。组件统计在 ui-style.json → kg.duckdb.style_meta 表。')
|
|
697
|
+
print('构建: npx @hupan56/wlkj run kg_build.py --rebuild (管理员)')
|
|
698
|
+
print(' 非 admin: npx @hupan56/wlkj doctor (拉团队已构建的 kg.duckdb)')
|
|
699
|
+
return
|
|
700
|
+
common = si.get('common_components', {})
|
|
701
|
+
forms = si.get('form_components', {})
|
|
702
|
+
if not common and not forms:
|
|
703
|
+
print('style_meta 表的组件列为空 — kg_duckdb 导入时 ui-style.json 缺 common_components。')
|
|
704
|
+
print('重建: npx @hupan56/wlkj run build_style_index.py 然后 kg_build.py --rebuild')
|
|
585
705
|
return
|
|
586
706
|
print('=== UI Components Usage ===')
|
|
587
707
|
print()
|
|
588
708
|
print('Ant Design Vue (import counts):')
|
|
589
|
-
for comp, count in
|
|
709
|
+
for comp, count in common.items():
|
|
590
710
|
print(' {} {}'.format(comp.ljust(20), str(count).ljust(5)))
|
|
591
711
|
print()
|
|
592
712
|
print('Form Components (usage in schemas):')
|
|
593
|
-
for comp, count in sorted(
|
|
713
|
+
for comp, count in sorted(forms.items(), key=lambda x: x[1], reverse=True)[:20]:
|
|
594
714
|
print(' {} {}'.format(comp.ljust(20), str(count).ljust(5)))
|
|
595
715
|
|
|
596
716
|
|
|
717
|
+
|
|
597
718
|
def show_vben():
|
|
598
719
|
data = load_index('ref-vben-style.json')
|
|
599
720
|
if data is None:
|
|
@@ -611,8 +732,8 @@ def show_vben():
|
|
|
611
732
|
def search_wiki_cli(query):
|
|
612
733
|
"""搜 Repo Wiki (Qoder IDE 生成的语义级模块文档)。"""
|
|
613
734
|
try:
|
|
614
|
-
from
|
|
615
|
-
from
|
|
735
|
+
from repowiki import search_wiki, build_wiki_index
|
|
736
|
+
from foundation.integrations.terms import get_cn_map_with_auto
|
|
616
737
|
except ImportError:
|
|
617
738
|
print('Wiki 搜索不可用 (common.repowiki 模块缺失)')
|
|
618
739
|
return
|
|
@@ -657,7 +778,7 @@ def search_wiki_cli(query):
|
|
|
657
778
|
|
|
658
779
|
def search_impact(query):
|
|
659
780
|
"""改这个接口/符号影响哪些页面/按钮/模块? (U1: 内存图遍历)"""
|
|
660
|
-
from
|
|
781
|
+
from domain.kg.graph.graph_traverse import CodeGraph
|
|
661
782
|
g = CodeGraph(INDEX_DIR)
|
|
662
783
|
result = g.impact(query)
|
|
663
784
|
if result:
|
|
@@ -669,7 +790,7 @@ def search_impact(query):
|
|
|
669
790
|
|
|
670
791
|
def search_context360(query):
|
|
671
792
|
"""符号的360度视图: 端点/函数/处理器/PRD (U1: 内存图遍历)"""
|
|
672
|
-
from
|
|
793
|
+
from domain.kg.graph.graph_traverse import CodeGraph
|
|
673
794
|
g = CodeGraph(INDEX_DIR)
|
|
674
795
|
result = g.context360(query)
|
|
675
796
|
if result:
|
|
@@ -754,8 +875,9 @@ def _graph_cli(method, *args):
|
|
|
754
875
|
"""
|
|
755
876
|
try:
|
|
756
877
|
if method == 'get_workflow':
|
|
757
|
-
# DuckDB 路径
|
|
758
|
-
|
|
878
|
+
# DuckDB 路径 (v3.0: kg_duckdb 已迁入 kg/ 子包)
|
|
879
|
+
from domain.kg.storage.kg_duckdb import get_db as _kg_get_db
|
|
880
|
+
import domain.kg.storage.kg_duckdb as K
|
|
759
881
|
q = (args[0] if args else '').strip()
|
|
760
882
|
if not q:
|
|
761
883
|
print('请提供模块名, 如: assets / cost / hr / maintenance')
|
|
@@ -774,8 +896,8 @@ def _graph_cli(method, *args):
|
|
|
774
896
|
print(' %d. [%s] %s — %s' % (r[0], r[1] or '?', r[2] or '', r[3] or ''))
|
|
775
897
|
return
|
|
776
898
|
# JSON 索引路径 (CodeGraph)
|
|
777
|
-
from
|
|
778
|
-
from
|
|
899
|
+
from domain.kg.graph.graph_traverse import CodeGraph
|
|
900
|
+
from foundation.core.paths import DATA_INDEX_DIR
|
|
779
901
|
g = CodeGraph(str(DATA_INDEX_DIR))
|
|
780
902
|
fn = getattr(g, method, None)
|
|
781
903
|
if not fn:
|
|
File without changes
|