@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
|
@@ -1,606 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
# v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
|
|
4
|
-
import os as _o, sys as _s
|
|
5
|
-
_cp = _o.path.join(_o.path.dirname(_o.path.dirname(_o.path.abspath(__file__))), 'common')
|
|
6
|
-
if _cp not in _s.path: _s.path.insert(0, _cp)
|
|
7
|
-
from bootstrap import setup; setup()
|
|
8
|
-
|
|
9
|
-
"""
|
|
10
|
-
Knowledge Graph MCP Server for QoderWork (pure Python, no SDK dependency).
|
|
11
|
-
|
|
12
|
-
Exposes the QODER knowledge graph as MCP tools that QoderWork's AI agent
|
|
13
|
-
can call directly. Uses the MCP stdio protocol (JSON-RPC 2.0 over stdin/stdout)
|
|
14
|
-
implemented from scratch — no external dependencies beyond the standard library.
|
|
15
|
-
|
|
16
|
-
Registered in ~/.qoderwork/mcp.json as "qoder-knowledge-graph".
|
|
17
|
-
Tools surface as mcp__qoder-knowledge-graph__<tool> to the AI.
|
|
18
|
-
|
|
19
|
-
Each tool captures the stdout of the existing search_index/context_pack
|
|
20
|
-
functions, so no logic is duplicated — this is a thin stdio→MCP wrapper.
|
|
21
|
-
"""
|
|
22
|
-
import io
|
|
23
|
-
import os
|
|
24
|
-
import sys
|
|
25
|
-
import json
|
|
26
|
-
import asyncio
|
|
27
|
-
|
|
28
|
-
# Ensure UTF-8 output
|
|
29
|
-
try:
|
|
30
|
-
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
31
|
-
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
|
32
|
-
except Exception:
|
|
33
|
-
pass
|
|
34
|
-
|
|
35
|
-
# Locate the scripts dir
|
|
36
|
-
SCRIPTS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 子包→scripts/根
|
|
37
|
-
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(SCRIPTS_DIR))) # 多一层: 子包→scripts→repo # repo root (.qoder 的上级)
|
|
38
|
-
sys.path.insert(0, SCRIPTS_DIR)
|
|
39
|
-
_KG_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
40
|
-
if _KG_DIR not in sys.path: sys.path.insert(0, _KG_DIR) # 同包import
|
|
41
|
-
|
|
42
|
-
# stdout 重定向 + 协议层由 BaseMCPServer 接管 (v3.0 重构)
|
|
43
|
-
# _real_stdout 不再模块级定义, 改为运行时从基类取 (见 _capture)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def _capture(func, *args, **kwargs):
|
|
47
|
-
"""Run a function, capture its stdout (which the scripts print to).
|
|
48
|
-
|
|
49
|
-
v3.0: sys.stdout 在 run() 后已是 stderr (基类 _setup_streams 重定向),
|
|
50
|
-
_real_stdout 存在 BaseMCPServer._real_stdout。这里捕获时临时换回 buf。
|
|
51
|
-
"""
|
|
52
|
-
old = sys.stdout
|
|
53
|
-
buf = io.StringIO()
|
|
54
|
-
sys.stdout = buf
|
|
55
|
-
try:
|
|
56
|
-
func(*args, **kwargs)
|
|
57
|
-
except Exception as e:
|
|
58
|
-
buf.write('\n[error] %s' % str(e))
|
|
59
|
-
finally:
|
|
60
|
-
sys.stdout = old
|
|
61
|
-
return buf.getvalue().strip()
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# Lazy imports
|
|
65
|
-
_search = None
|
|
66
|
-
_context = None
|
|
67
|
-
|
|
68
|
-
def _ensure_imports():
|
|
69
|
-
global _search, _context
|
|
70
|
-
if _search is None:
|
|
71
|
-
import search_index as _search
|
|
72
|
-
if _context is None:
|
|
73
|
-
import context_pack as _context
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
# ── MCP Tool Definitions ─────────────────────────────────────────────
|
|
77
|
-
|
|
78
|
-
TOOLS = [
|
|
79
|
-
{
|
|
80
|
-
"name": "search_code",
|
|
81
|
-
"description": "搜索代码: 按关键词查找代码文件在哪。例: search_code(keyword='考勤') 或 search_code(keyword='车辆', platform='web')",
|
|
82
|
-
"inputSchema": {
|
|
83
|
-
"type": "object",
|
|
84
|
-
"properties": {
|
|
85
|
-
"keyword": {"type": "string", "description": "搜索关键词(中文或英文)"},
|
|
86
|
-
"platform": {"type": "string", "enum": ["web", "app"],
|
|
87
|
-
"description": "平台: web=PC管理端, app=移动端。可选"},
|
|
88
|
-
},
|
|
89
|
-
"required": ["keyword"],
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
"name": "search_api",
|
|
94
|
-
"description": "搜索API端点: 按关键词查找后端接口。例: search_api(keyword='salary')",
|
|
95
|
-
"inputSchema": {
|
|
96
|
-
"type": "object",
|
|
97
|
-
"properties": {"keyword": {"type": "string", "description": "API关键词"}},
|
|
98
|
-
"required": ["keyword"],
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
"name": "search_prd",
|
|
103
|
-
"description": "搜索PRD: 查已有需求文档,防止重复造轮子。例: search_prd(keyword='考勤')",
|
|
104
|
-
"inputSchema": {
|
|
105
|
-
"type": "object",
|
|
106
|
-
"properties": {"keyword": {"type": "string", "description": "PRD关键词"}},
|
|
107
|
-
"required": ["keyword"],
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
"name": "get_impact",
|
|
112
|
-
"description": "影响分析: 改某接口/路径会影响哪些前端页面和按钮?评估改动风险。例: get_impact(endpoint='/asset') 或 get_impact(endpoint='export')",
|
|
113
|
-
"inputSchema": {
|
|
114
|
-
"type": "object",
|
|
115
|
-
"properties": {"endpoint": {"type": "string",
|
|
116
|
-
"description": "API端点路径或关键词, 如 /asset/list 或 export"}},
|
|
117
|
-
"required": ["endpoint"],
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
"name": "context_360",
|
|
122
|
-
"description": "360度视图: 查看符号(端点/函数/处理器)的全部关联信息。例: context_360(symbol='handleExport') 或 context_360(symbol='/system/role')",
|
|
123
|
-
"inputSchema": {
|
|
124
|
-
"type": "object",
|
|
125
|
-
"properties": {"symbol": {"type": "string", "description": "符号名: 端点/函数名/处理器名"}},
|
|
126
|
-
"required": ["symbol"],
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
"name": "context_pack",
|
|
131
|
-
"description": "上下文打包: 一次返回关键词相关的全部上下文(代码+页面+字段+PRD+API+布局指纹)。避免来回查。例: context_pack(keyword='车辆', platform='web')",
|
|
132
|
-
"inputSchema": {
|
|
133
|
-
"type": "object",
|
|
134
|
-
"properties": {
|
|
135
|
-
"keyword": {"type": "string", "description": "业务关键词"},
|
|
136
|
-
"platform": {"type": "string", "enum": ["web", "app"],
|
|
137
|
-
"description": "平台: web或app。可选"},
|
|
138
|
-
"page_type": {"type": "string",
|
|
139
|
-
"description": "页面类型: table-page/form-page/detail-page。可选"},
|
|
140
|
-
"role": {"type": "string", "enum": ["pm", "design", "dev", "test", "admin"],
|
|
141
|
-
"description": "角色裁剪: pm=字段+PRD+API, design=页面+布局+风格, dev=代码+API, test=代码+PRD+API。可选"},
|
|
142
|
-
},
|
|
143
|
-
"required": ["keyword"],
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
"name": "table_impact",
|
|
148
|
-
"description": "改表影响分析: 改某数据库表会影响哪些 Entity 类和接口? 跨层遍历(数据层→代码层)。例: table_impact(table='quality_case') → 找到 QualityCase 类 + 影响的 Controller/接口",
|
|
149
|
-
"inputSchema": {
|
|
150
|
-
"type": "object",
|
|
151
|
-
"properties": {
|
|
152
|
-
"table": {"type": "string", "description": "数据库表名 (如 quality_case)"},
|
|
153
|
-
},
|
|
154
|
-
"required": ["table"],
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
"name": "coverage_matrix",
|
|
159
|
-
"description": "功能×测试覆盖矩阵: 哪些功能有测试覆盖, 哪些是空白? 标出回归测试盲区。例: coverage_matrix()",
|
|
160
|
-
"inputSchema": {
|
|
161
|
-
"type": "object",
|
|
162
|
-
"properties": {},
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
"name": "feature_overview",
|
|
167
|
-
"description": "功能画像: 一个功能的完整画像(端点+按钮+测试+页面)。例: feature_overview(feature='资产管理') 或 feature_overview(feature='asset')",
|
|
168
|
-
"inputSchema": {
|
|
169
|
-
"type": "object",
|
|
170
|
-
"properties": {
|
|
171
|
-
"feature": {"type": "string", "description": "功能名(中/英文都行): 资产管理/asset/hr"},
|
|
172
|
-
},
|
|
173
|
-
"required": ["feature"],
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
"name": "get_workflow",
|
|
178
|
-
"description": "业务流程: 查看一个模块的完整操作链(query→create→audit→archive→export→delete)。数据源: DuckDB workflows 表(168条/13模块)。优先匹配英文模块名(assets/cost/hr/maintenance等), 中文输入会返回候选模块列表。例: get_workflow(module='assets') 或 get_workflow(module='cost')",
|
|
179
|
-
"inputSchema": {
|
|
180
|
-
"type": "object",
|
|
181
|
-
"properties": {
|
|
182
|
-
"module": {"type": "string", "description": "模块名(英文优先): assets/cost/hr/maintenance/operation/qualityAndSafety/veh/settings/dashboard/flow/systems/monitor/quasafe"},
|
|
183
|
-
},
|
|
184
|
-
"required": ["module"],
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
"name": "search_wiki",
|
|
189
|
-
"description": "搜索Repo Wiki: 查Qoder IDE生成的模块级语义文档(架构/职责/依赖), 比读源码省token。写PRD前优先查。数据源: wiki-index.json(730关键词)。注意: 不会自动构建索引, 索引缺失会提示手动跑。例: search_wiki(keyword='考勤') 或 search_wiki(keyword='权限')",
|
|
190
|
-
"inputSchema": {
|
|
191
|
-
"type": "object",
|
|
192
|
-
"properties": {"keyword": {"type": "string", "description": "Wiki关键词(中/英文)"}},
|
|
193
|
-
"required": ["keyword"],
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
"name": "multi_hop",
|
|
198
|
-
"description": "知识图谱多跳遍历: 从一个符号出发, 沿edges递归N跳, 找出全部可达实体(页面→接口→控制器→测试)。知识图谱核心能力。数据源: DuckDB edges表(8251边)。支持别名解析(传中文/端点/函数名自动转entity_id)。例: multi_hop(symbol='资产管理') 或 multi_hop(symbol='/asset/list', max_depth=3)",
|
|
199
|
-
"inputSchema": {
|
|
200
|
-
"type": "object",
|
|
201
|
-
"properties": {
|
|
202
|
-
"symbol": {"type": "string", "description": "起点符号: 可传entity_id(E:/asset/list)、别名(资产管理)、端点(/asset/list)或函数名"},
|
|
203
|
-
"max_depth": {"type": "integer", "default": 3, "description": "最大跳数(1-5), 默认3。越大越全但越慢"},
|
|
204
|
-
},
|
|
205
|
-
"required": ["symbol"],
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
"name": "search_field",
|
|
210
|
-
"description": "搜索字段: 查某个字段在哪些页面用过、中文标题是什么。例: search_field(field='plateNo') 或 search_field(field='车牌号')",
|
|
211
|
-
"inputSchema": {
|
|
212
|
-
"type": "object",
|
|
213
|
-
"properties": {
|
|
214
|
-
"field": {"type": "string", "description": "字段名(英文field或中文标题)"},
|
|
215
|
-
},
|
|
216
|
-
"required": ["field"],
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
"name": "list_modules",
|
|
221
|
-
"description": "模块清单: 列出项目所有业务模块及其中文名。例: list_modules() 或 list_modules(platform='web')",
|
|
222
|
-
"inputSchema": {
|
|
223
|
-
"type": "object",
|
|
224
|
-
"properties": {
|
|
225
|
-
"platform": {"type": "string", "enum": ["web", "app"], "description": "平台过滤。可选"},
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
"name": "fill_prototype",
|
|
231
|
-
"description": "原型预填: 用真实代码数据(表格列/表单字段/布局参数)自动填模板,输出80%完成的HTML草稿+数据清单。AI只需微调20%。出原型前必须调。例: fill_prototype(keyword='营业外合同', platform='web')",
|
|
232
|
-
"inputSchema": {
|
|
233
|
-
"type": "object",
|
|
234
|
-
"properties": {
|
|
235
|
-
"keyword": {"type": "string", "description": "业务关键词"},
|
|
236
|
-
"platform": {"type": "string", "enum": ["web", "app"],
|
|
237
|
-
"description": "平台: web或app。默认web"},
|
|
238
|
-
},
|
|
239
|
-
"required": ["keyword"],
|
|
240
|
-
},
|
|
241
|
-
},
|
|
242
|
-
{
|
|
243
|
-
"name": "get_design_system",
|
|
244
|
-
"description": "设计系统查询: 返回项目完整设计规范(布局指纹/颜色token/页面模式/组件表/图标规则/behaviors)。出任何原型前必须先调。例: get_design_system() 或 get_design_system(platform='web')",
|
|
245
|
-
"inputSchema": {
|
|
246
|
-
"type": "object",
|
|
247
|
-
"properties": {
|
|
248
|
-
"platform": {"type": "string", "enum": ["web", "app"],
|
|
249
|
-
"description": "平台: 只返回指定平台的指纹。不传则返回全部"},
|
|
250
|
-
},
|
|
251
|
-
},
|
|
252
|
-
},
|
|
253
|
-
]
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
# Role-based section filtering for context_pack output
|
|
259
|
-
# v3.0: 单一真源移到 common/roles.py, 消除与 kg.py 的逐字重复
|
|
260
|
-
try:
|
|
261
|
-
from common.roles import ROLE_SECTIONS, filter_by_role as _filter_by_role
|
|
262
|
-
except Exception:
|
|
263
|
-
# 回退: 内联定义 (common 不可用时)
|
|
264
|
-
import re
|
|
265
|
-
ROLE_SECTIONS = {
|
|
266
|
-
'pm': {'1', '2', '2b', '3', '4', '5', '7'},
|
|
267
|
-
'design': {'2', '2b', '3', '6'},
|
|
268
|
-
'dev': {'1', '3', '5', '7'},
|
|
269
|
-
'test': {'1', '4', '5'},
|
|
270
|
-
'admin': {'4', '5'},
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
def _filter_by_role(text, role):
|
|
274
|
-
visible = ROLE_SECTIONS.get((role or '').lower())
|
|
275
|
-
if not visible:
|
|
276
|
-
return text
|
|
277
|
-
sections = re.split(r'(\n## \d+[ab]?\.)', text)
|
|
278
|
-
result = sections[0]
|
|
279
|
-
i = 1
|
|
280
|
-
while i < len(sections):
|
|
281
|
-
header = sections[i]
|
|
282
|
-
num_m = re.match(r'\n## (\d+[ab]?)\.', header)
|
|
283
|
-
if num_m and num_m.group(1) in visible:
|
|
284
|
-
result += header
|
|
285
|
-
if i + 1 < len(sections):
|
|
286
|
-
result += sections[i + 1]
|
|
287
|
-
i += 2
|
|
288
|
-
else:
|
|
289
|
-
i += 2
|
|
290
|
-
return result.strip()
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
# ── Tool implementations (added 2026-06-17) ──────────────────────────
|
|
294
|
-
# 三个工具的底层能力均已实测: get_workflow/multi_hop 走 DuckDB(有数据),
|
|
295
|
-
# search_wiki 走 wiki-index.json(730词)。均不依赖已失效的旧 JSON 索引。
|
|
296
|
-
|
|
297
|
-
def _tool_get_workflow(module):
|
|
298
|
-
"""业务操作链查询 — v3.0 f3: 转发到 common.kg_capabilities (与 kg.py 共用单一真源)。"""
|
|
299
|
-
q = (module or '').strip()
|
|
300
|
-
if not q:
|
|
301
|
-
return "请提供模块名。可选: assets/cost/hr/maintenance/operation/qualityAndSafety/veh/settings/dashboard/flow/systems/monitor/quasafe"
|
|
302
|
-
import kg_duckdb as K
|
|
303
|
-
from common.kg_capabilities import workflow_query
|
|
304
|
-
con = K.get_db()
|
|
305
|
-
try:
|
|
306
|
-
return workflow_query(con, q)
|
|
307
|
-
finally:
|
|
308
|
-
try: con.close()
|
|
309
|
-
except Exception: pass
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
def _tool_search_wiki(keyword):
|
|
313
|
-
"""Repo Wiki 搜索 — v3.0 f3: 转发到 common.kg_capabilities.wiki_query (CLI/MCP共用)。"""
|
|
314
|
-
kw = (keyword or '').strip()
|
|
315
|
-
if not kw:
|
|
316
|
-
return "请提供搜索关键词"
|
|
317
|
-
from common.kg_capabilities import wiki_query
|
|
318
|
-
return wiki_query(kw)
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
def _tool_multi_hop(symbol, max_depth=3):
|
|
322
|
-
"""知识图谱多跳遍历。先别名解析, 再递归 CTE 遍历。
|
|
323
|
-
|
|
324
|
-
审核: kg_duckdb.multi_hop 实测从 E:/code 3 跳到 C:SysSmsCodeController。
|
|
325
|
-
edges 表 8251 边。用户可能传中文名/端点/函数名, 需先走 aliases 解析成
|
|
326
|
-
entity_id。
|
|
327
|
-
"""
|
|
328
|
-
import kg_duckdb as K
|
|
329
|
-
sym = (symbol or '').strip()
|
|
330
|
-
if not sym:
|
|
331
|
-
return "请提供起点符号 (entity_id / 端点 / 函数名 / 中文别名)"
|
|
332
|
-
try:
|
|
333
|
-
depth = int(max_depth)
|
|
334
|
-
except (TypeError, ValueError):
|
|
335
|
-
depth = 3
|
|
336
|
-
depth = max(1, min(5, depth)) # 钳制 1-5
|
|
337
|
-
con = K.get_db()
|
|
338
|
-
try:
|
|
339
|
-
# v3.0 f3: 核心查询转发到 common.kg_capabilities.hop_query (CLI/MCP共用)
|
|
340
|
-
from common.kg_capabilities import hop_query
|
|
341
|
-
return hop_query(con, sym, depth, multi_hop_fn=K.multi_hop)
|
|
342
|
-
finally:
|
|
343
|
-
try: con.close()
|
|
344
|
-
except Exception: pass
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
def _tool_search_field(field):
|
|
348
|
-
from common.paths import DATA_INDEX_DIR
|
|
349
|
-
from kg_duckdb import get_db
|
|
350
|
-
con = get_db()
|
|
351
|
-
rows = con.execute(
|
|
352
|
-
"SELECT field, title, count, sample_files FROM fields WHERE LOWER(field) LIKE ? OR LOWER(title) LIKE ? LIMIT 20",
|
|
353
|
-
['%' + field.lower() + '%', '%' + field.lower() + '%']
|
|
354
|
-
).fetchall()
|
|
355
|
-
con.close()
|
|
356
|
-
if not rows:
|
|
357
|
-
return "未找到字段 '%s'" % field
|
|
358
|
-
lines = ["字段搜索 '%s' (%d个匹配)" % (field, len(rows))]
|
|
359
|
-
for f, t, c, s in rows:
|
|
360
|
-
lines.append(" %s -> %s (用了%d次)" % (f, t, c))
|
|
361
|
-
return '\n'.join(lines)
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
def _tool_list_modules(platform=''):
|
|
365
|
-
from kg_duckdb import get_db
|
|
366
|
-
con = get_db()
|
|
367
|
-
if platform == 'web':
|
|
368
|
-
rows = con.execute("SELECT dir, cn, project FROM modules WHERE project LIKE '%fywl%' ORDER BY dir").fetchall()
|
|
369
|
-
elif platform == 'app':
|
|
370
|
-
rows = con.execute("SELECT dir, cn, project FROM modules WHERE project LIKE '%Carmg%' OR project LIKE '%H5%' ORDER BY dir").fetchall()
|
|
371
|
-
else:
|
|
372
|
-
rows = con.execute("SELECT dir, cn, project FROM modules ORDER BY project, dir").fetchall()
|
|
373
|
-
con.close()
|
|
374
|
-
if not rows:
|
|
375
|
-
return "无模块数据"
|
|
376
|
-
lines = ["模块清单 (%d个)" % len(rows)]
|
|
377
|
-
cur = ""
|
|
378
|
-
for d, cn, proj in rows:
|
|
379
|
-
if proj != cur:
|
|
380
|
-
cur = proj
|
|
381
|
-
lines.append("[%s]" % proj)
|
|
382
|
-
lines.append(" %s -> %s" % (d, cn))
|
|
383
|
-
return '\n'.join(lines)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
def _execute_tool(name, arguments):
|
|
387
|
-
"""Execute a tool and return result text."""
|
|
388
|
-
_ensure_imports()
|
|
389
|
-
|
|
390
|
-
if name == "search_code":
|
|
391
|
-
kw = arguments.get("keyword", "")
|
|
392
|
-
plat = arguments.get("platform") or ""
|
|
393
|
-
return _capture(_search.search_keywords, kw, plat) or "无匹配结果"
|
|
394
|
-
|
|
395
|
-
elif name == "search_api":
|
|
396
|
-
return _capture(_search.search_api, arguments.get("keyword", "")) or "无匹配结果"
|
|
397
|
-
|
|
398
|
-
elif name == "search_prd":
|
|
399
|
-
return _capture(_search.search_prds, arguments.get("keyword", "")) or "无匹配PRD"
|
|
400
|
-
|
|
401
|
-
elif name == "get_impact":
|
|
402
|
-
return _capture(_search.search_impact, arguments.get("endpoint", "")) or "未找到匹配端点"
|
|
403
|
-
|
|
404
|
-
elif name == "context_360":
|
|
405
|
-
return _capture(_search.search_context360, arguments.get("symbol", "")) or "未找到匹配符号"
|
|
406
|
-
|
|
407
|
-
elif name == "context_pack":
|
|
408
|
-
kw = arguments.get("keyword", "")
|
|
409
|
-
plat = arguments.get("platform", "")
|
|
410
|
-
pt = arguments.get("page_type", "")
|
|
411
|
-
role = arguments.get("role", "")
|
|
412
|
-
full = _capture(_context._compute_and_print, kw, plat, pt)
|
|
413
|
-
if not full:
|
|
414
|
-
return "无匹配上下文"
|
|
415
|
-
if role:
|
|
416
|
-
return _filter_by_role(full, role)
|
|
417
|
-
return full
|
|
418
|
-
|
|
419
|
-
elif name == "fill_prototype":
|
|
420
|
-
# 调用 fill_prototype.py 的 main 逻辑 (捕获 stdout)
|
|
421
|
-
kw = arguments.get("keyword", "")
|
|
422
|
-
plat = arguments.get("platform", "web")
|
|
423
|
-
import subprocess as _sp
|
|
424
|
-
script = os.path.join(SCRIPTS_DIR, 'fill_prototype.py')
|
|
425
|
-
try:
|
|
426
|
-
r = _sp.run([sys.executable, script, kw, '--platform', plat],
|
|
427
|
-
capture_output=True, text=True, encoding='utf-8',
|
|
428
|
-
errors='replace', timeout=30, cwd=SCRIPTS_DIR)
|
|
429
|
-
return r.stdout or "fill_prototype 无输出 (可能索引缺失)"
|
|
430
|
-
except Exception as e:
|
|
431
|
-
return "[error] fill_prototype: %s" % str(e)
|
|
432
|
-
|
|
433
|
-
elif name == "get_design_system":
|
|
434
|
-
# 返回 DESIGN.md 内容 (若不存在则生成)
|
|
435
|
-
import subprocess as _sp
|
|
436
|
-
design_md = os.path.join(BASE_DIR, 'data', 'design', 'DESIGN.md')
|
|
437
|
-
if not os.path.isfile(design_md):
|
|
438
|
-
gen_script = os.path.join(SCRIPTS_DIR, 'gen_design_doc.py')
|
|
439
|
-
if os.path.isfile(gen_script):
|
|
440
|
-
_sp.run([sys.executable, gen_script], capture_output=True, timeout=30)
|
|
441
|
-
if os.path.isfile(design_md):
|
|
442
|
-
content = open(design_md, encoding='utf-8').read()
|
|
443
|
-
# 若指定了 platform, 只返回该平台的布局指纹段
|
|
444
|
-
plat = arguments.get("platform")
|
|
445
|
-
if plat:
|
|
446
|
-
lines = content.split('\n')
|
|
447
|
-
result = []
|
|
448
|
-
in_section = False
|
|
449
|
-
for line in lines:
|
|
450
|
-
if line.startswith('### ') and plat.lower() in line.lower():
|
|
451
|
-
in_section = True
|
|
452
|
-
elif line.startswith('### ') and in_section:
|
|
453
|
-
break
|
|
454
|
-
if in_section:
|
|
455
|
-
result.append(line)
|
|
456
|
-
return '\n'.join(result) if result else content
|
|
457
|
-
return content
|
|
458
|
-
return "DESIGN.md 不存在且生成失败。请手动跑: python gen_design_doc.py"
|
|
459
|
-
|
|
460
|
-
elif name == "table_impact":
|
|
461
|
-
# 改表影响分析: 表 → entity_table_map → Entity → 代码图谱 Controller/接口
|
|
462
|
-
from common.paths import DATA_INDEX_DIR as _idx
|
|
463
|
-
import duckdb as _ddb
|
|
464
|
-
db_path = str(_idx / 'kg_db.duckdb')
|
|
465
|
-
if not os.path.isfile(db_path):
|
|
466
|
-
return "数据层图谱未构建。请先跑: python .qoder/scripts/kg_build_db.py"
|
|
467
|
-
table = arguments.get("table", "").strip()
|
|
468
|
-
try:
|
|
469
|
-
con = _ddb.connect(db_path, read_only=True)
|
|
470
|
-
except Exception as e:
|
|
471
|
-
return "数据层图谱暂时不可用(可能正在重建): %s。稍后重试。" % str(e)[:60]
|
|
472
|
-
try:
|
|
473
|
-
# 1. 表 → Entity
|
|
474
|
-
ents = con.execute(
|
|
475
|
-
"SELECT DISTINCT entity_class FROM entity_table_map WHERE table_name = ?",
|
|
476
|
-
[table]
|
|
477
|
-
).fetchall()
|
|
478
|
-
ent_list = [r[0] for r in ents]
|
|
479
|
-
if not ent_list:
|
|
480
|
-
return "表 '%s' 在 entity_table_map 中无映射 (可能该表无 @TableName 注解的 Entity)" % table
|
|
481
|
-
lines = ["改表影响分析: %s" % table]
|
|
482
|
-
lines.append(" 对应 Entity: %s" % ', '.join(ent_list))
|
|
483
|
-
# 2. 表的真实列 (给个概览)
|
|
484
|
-
cols = con.execute(
|
|
485
|
-
"SELECT column_name, data_type, column_comment FROM db_columns "
|
|
486
|
-
"WHERE table_name = ? LIMIT 10", [table]
|
|
487
|
-
).fetchall()
|
|
488
|
-
if cols:
|
|
489
|
-
lines.append(" 表字段 (%d+):" % len(cols))
|
|
490
|
-
for cn, dt, cc in cols:
|
|
491
|
-
lines.append(" %s (%s) %s" % (cn, dt, (cc or '')[:30]))
|
|
492
|
-
# 3. Entity → 代码图谱 (attach kg.duckdb 查接口/Controller)
|
|
493
|
-
try:
|
|
494
|
-
con.execute("ATTACH '%s' AS code (READ_ONLY)" % str(_idx / 'kg.duckdb'))
|
|
495
|
-
lines.append(" 影响的接口/控制器:")
|
|
496
|
-
found = 0
|
|
497
|
-
for ec in ent_list:
|
|
498
|
-
refs = con.execute(
|
|
499
|
-
"SELECT type, canonical FROM code.entities "
|
|
500
|
-
"WHERE contains(canonical, ?) AND type IN ('CONTROLLER','ENDPOINT') "
|
|
501
|
-
"LIMIT 12", [ec]
|
|
502
|
-
).fetchall()
|
|
503
|
-
for t, c in refs:
|
|
504
|
-
lines.append(" [%s] %s" % (t, c))
|
|
505
|
-
found += 1
|
|
506
|
-
if not found:
|
|
507
|
-
lines.append(" (代码图谱中未找到引用 %s 的接口)" % ent_list[0])
|
|
508
|
-
con.execute("DETACH code")
|
|
509
|
-
except Exception as e:
|
|
510
|
-
lines.append(" (代码图谱查询失败: %s)" % str(e)[:60])
|
|
511
|
-
return '\n'.join(lines)
|
|
512
|
-
finally:
|
|
513
|
-
con.close()
|
|
514
|
-
|
|
515
|
-
elif name == "coverage_matrix":
|
|
516
|
-
from common.graph_traverse import CodeGraph
|
|
517
|
-
from common.paths import DATA_INDEX_DIR as _idx
|
|
518
|
-
g = CodeGraph(str(_idx))
|
|
519
|
-
result = g.coverage_matrix()
|
|
520
|
-
return result or "无法生成覆盖矩阵"
|
|
521
|
-
|
|
522
|
-
elif name == "feature_overview":
|
|
523
|
-
from common.graph_traverse import CodeGraph
|
|
524
|
-
from common.paths import DATA_INDEX_DIR as _idx
|
|
525
|
-
g = CodeGraph(str(_idx))
|
|
526
|
-
result = g.feature_overview(arguments.get("feature", ""))
|
|
527
|
-
if result and '不存在' not in result:
|
|
528
|
-
return result
|
|
529
|
-
# DuckDB 兜底: entity-registry.json 缺失时, 用 DuckDB 直接查
|
|
530
|
-
feat = arguments.get("feature", "")
|
|
531
|
-
fl = feat.lower()
|
|
532
|
-
try:
|
|
533
|
-
import duckdb as _ddb
|
|
534
|
-
con = _ddb.connect(str(_idx / 'kg.duckdb'), read_only=True)
|
|
535
|
-
# 端点
|
|
536
|
-
eps = con.execute(
|
|
537
|
-
"SELECT canonical FROM entities WHERE type='ENDPOINT' AND "
|
|
538
|
-
"(lower(canonical) LIKE ? OR lower(canonical) LIKE ?) LIMIT 15",
|
|
539
|
-
['%'+fl+'%', '%'+feat+'%']).fetchall()
|
|
540
|
-
# 按钮
|
|
541
|
-
btns = con.execute(
|
|
542
|
-
"SELECT canonical FROM entities WHERE type='BUTTON' AND "
|
|
543
|
-
"lower(canonical) LIKE ? LIMIT 10", ['%'+fl+'%']).fetchall()
|
|
544
|
-
con.close()
|
|
545
|
-
lines = ['功能画像: %s (DuckDB兜底)' % feat]
|
|
546
|
-
if eps:
|
|
547
|
-
lines.append('端点(%d):' % len(eps))
|
|
548
|
-
for (e,) in eps[:10]:
|
|
549
|
-
lines.append(' %s' % e)
|
|
550
|
-
if btns:
|
|
551
|
-
lines.append('按钮(%d):' % len(btns))
|
|
552
|
-
for (b,) in btns[:5]:
|
|
553
|
-
lines.append(' %s' % b)
|
|
554
|
-
if not eps and not btns:
|
|
555
|
-
lines.append(' (无匹配。换关键词重试)')
|
|
556
|
-
return chr(10).join(lines)
|
|
557
|
-
except Exception as e:
|
|
558
|
-
return '未找到功能 "%s" (DuckDB兜底也失败: %s)' % (feat, str(e)[:60])
|
|
559
|
-
|
|
560
|
-
elif name == "get_workflow":
|
|
561
|
-
return _tool_get_workflow(arguments.get("module", ""))
|
|
562
|
-
|
|
563
|
-
elif name == "search_wiki":
|
|
564
|
-
return _tool_search_wiki(arguments.get("keyword", ""))
|
|
565
|
-
|
|
566
|
-
elif name == "multi_hop":
|
|
567
|
-
return _tool_multi_hop(arguments.get("symbol", ""),
|
|
568
|
-
arguments.get("max_depth", 3))
|
|
569
|
-
|
|
570
|
-
elif name == "search_field":
|
|
571
|
-
return _tool_search_field(arguments.get("field", ""))
|
|
572
|
-
|
|
573
|
-
elif name == "list_modules":
|
|
574
|
-
return _tool_list_modules(arguments.get("platform", ""))
|
|
575
|
-
|
|
576
|
-
return "未知工具: %s" % name
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
# ── MCP Server (继承 BaseMCPServer, 协议层由基类接管) ──────────────────
|
|
580
|
-
# v3.0 重构: 删除 _send/_handle/main 三段逐字复制代码 (~96行),
|
|
581
|
-
# 改为继承 BaseMCPServer。kg 的 initialize 不做内网校验 (本地知识图谱)。
|
|
582
|
-
|
|
583
|
-
def _bootstrap_base():
|
|
584
|
-
"""延迟 import BaseMCPServer (scripts/ 在 sys.path)。"""
|
|
585
|
-
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
586
|
-
from common.mcp_base import BaseMCPServer
|
|
587
|
-
return BaseMCPServer
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
class KnowledgeGraphMCPServer(_bootstrap_base()):
|
|
591
|
-
"""知识图谱 MCP Server。继承基类协议层, 只填工具逻辑。
|
|
592
|
-
|
|
593
|
-
与 mysql/zentao 不同: kg 不做内网校验 (本地 DuckDB),
|
|
594
|
-
所以不覆写 on_initialize_hook (基类默认空)。
|
|
595
|
-
"""
|
|
596
|
-
|
|
597
|
-
SERVER_NAME = "qoder-knowledge-graph"
|
|
598
|
-
SERVER_VERSION = "1.0.0"
|
|
599
|
-
TOOLS = TOOLS # 引用上方模块级 TOOLS (17个)
|
|
600
|
-
|
|
601
|
-
def handle_tool(self, name, args):
|
|
602
|
-
return _execute_tool(name, args)
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
if __name__ == "__main__":
|
|
606
|
-
KnowledgeGraphMCPServer().run()
|