@ppdocs/mcp 3.12.0 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dist/tools/analyzer.d.ts +1 -4
- package/dist/tools/analyzer.js +4 -7
- package/dist/tools/flowchart.js +1 -4
- package/dist/tools/index.d.ts +8 -9
- package/dist/tools/index.js +8 -29
- package/dist/tools/init.js +1 -1
- package/dist/tools/kg_status.d.ts +1 -1
- package/dist/tools/kg_status.js +4 -6
- package/dist/tools/refs.js +38 -172
- package/dist/tools/tasks.d.ts +1 -2
- package/dist/tools/tasks.js +32 -47
- package/dist/tools/workflow.js +2 -3
- package/package.json +1 -1
- package/dist/tools/discussion.d.ts +0 -15
- package/dist/tools/discussion.js +0 -264
- package/dist/tools/doc_query.d.ts +0 -10
- package/dist/tools/doc_query.js +0 -185
- package/dist/tools/files.d.ts +0 -6
- package/dist/tools/files.js +0 -107
- package/dist/tools/meeting.d.ts +0 -7
- package/dist/tools/meeting.js +0 -97
- package/dist/tools/pitfalls.d.ts +0 -6
- package/dist/tools/pitfalls.js +0 -190
- package/dist/tools/projects.d.ts +0 -7
- package/dist/tools/projects.js +0 -19
package/README.md
CHANGED
|
@@ -116,9 +116,7 @@ npx @ppdocs/mcp init -p <projectId> -k <key> --codex
|
|
|
116
116
|
| `kg_flowchart` | 统一的知识图谱入口,负责查询、更新、关系分析 |
|
|
117
117
|
| `kg_workflow` | Markdown 文档工作流管理 |
|
|
118
118
|
| `kg_task` | 任务管理 |
|
|
119
|
-
| `
|
|
120
|
-
| `kg_discuss` | 讨论区 |
|
|
121
|
-
| `kg_meeting` | 多 AI 协作会议 |
|
|
119
|
+
| `kg_ref` | 外部参考资料(URL拉取、查看、删除) |
|
|
122
120
|
| `code_scan` | 代码扫描 |
|
|
123
121
|
| `code_smart_context` | 获取符号的智能上下文 |
|
|
124
122
|
| `code_full_path` | 获取两个符号之间的全路径 |
|
|
@@ -207,6 +205,12 @@ kg_flowchart(action:"update_node", chartId:"main", nodeId:"n_storage",
|
|
|
207
205
|
|
|
208
206
|
## 更新日志
|
|
209
207
|
|
|
208
|
+
### v3.3.0
|
|
209
|
+
- 移除 6 个低使用率工具: `kg_doc`, `kg_discuss`, `kg_meeting`, `kg_files`, `kg_projects`, `kg_pitfall`
|
|
210
|
+
- `kg_ref` 精简为 4 个 action: `list|get|fetch|delete`
|
|
211
|
+
- 所有工具描述精简,减少 token 开销
|
|
212
|
+
- 移除前端孤立组件: FloatingDiscussion, PitfallRecord, ImpactGraph
|
|
213
|
+
|
|
210
214
|
### v3.2.36
|
|
211
215
|
- 统一为 `flowchart-first` MCP 接口模型
|
|
212
216
|
- `kg_flowchart` 新增 `search` / `get_relations` / `find_path`
|
package/dist/tools/analyzer.d.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 代码分析引擎工具
|
|
2
|
+
* 代码分析引擎工具
|
|
3
3
|
* code_scan, code_smart_context, code_full_path
|
|
4
|
-
*
|
|
5
|
-
* code_smart_context: 代码+KG文档/工作流全关联 (含影响范围)
|
|
6
|
-
* code_full_path: 两点间代码链路+共享KG文档
|
|
7
4
|
*/
|
|
8
5
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
6
|
import { type McpContext } from './shared.js';
|
package/dist/tools/analyzer.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 代码分析引擎工具
|
|
2
|
+
* 代码分析引擎工具
|
|
3
3
|
* code_scan, code_smart_context, code_full_path
|
|
4
|
-
*
|
|
5
|
-
* code_smart_context: 代码+KG文档/工作流全关联 (含影响范围)
|
|
6
|
-
* code_full_path: 两点间代码链路+共享KG文档
|
|
7
4
|
*/
|
|
8
5
|
import { z } from 'zod';
|
|
9
6
|
import { getClient } from '../storage/httpClient.js';
|
|
@@ -17,7 +14,7 @@ const SYMBOL_ICONS = {
|
|
|
17
14
|
export function registerAnalyzerTools(server, ctx) {
|
|
18
15
|
const client = () => getClient();
|
|
19
16
|
// ===== code_scan: 扫描项目代码 =====
|
|
20
|
-
server.tool('code_scan', '
|
|
17
|
+
server.tool('code_scan', '扫描项目代码并构建索引。返回文件数、符号数、语言统计。首次使用 code_smart_context/code_full_path 前需先执行', {
|
|
21
18
|
projectPath: z.string().optional().describe('项目源码的绝对路径(如"D:/projects/myapp")。不传则自动从Beacon同步目录或项目配置解析'),
|
|
22
19
|
force: z.boolean().optional().describe('是否强制全量重建(默认false, 增量更新)'),
|
|
23
20
|
}, async (args) => safeTool(async () => {
|
|
@@ -33,7 +30,7 @@ export function registerAnalyzerTools(server, ctx) {
|
|
|
33
30
|
].join('\n'));
|
|
34
31
|
}));
|
|
35
32
|
// ===== code_smart_context: 代码+文档全关联上下文 =====
|
|
36
|
-
server.tool('code_smart_context', '
|
|
33
|
+
server.tool('code_smart_context', '代码+文档全关联上下文。输入函数名,返回代码依赖、关联文档、匹配工作流、活跃任务、影响范围。需先运行 code_scan', {
|
|
37
34
|
projectPath: z.string().optional().describe('项目源码的绝对路径(不传则自动解析)'),
|
|
38
35
|
symbolName: z.string().describe('要查询的符号名称(如"handleLogin", "AuthService")'),
|
|
39
36
|
}, async (args) => safeTool(async () => {
|
|
@@ -94,7 +91,7 @@ export function registerAnalyzerTools(server, ctx) {
|
|
|
94
91
|
return wrap(lines.join('\n'));
|
|
95
92
|
}));
|
|
96
93
|
// ===== code_full_path: 全关联路径 =====
|
|
97
|
-
server.tool('code_full_path', '
|
|
94
|
+
server.tool('code_full_path', '全关联路径。查找两个符号之间的代码引用链路、共享KG文档、共同导入。需先运行 code_scan', {
|
|
98
95
|
projectPath: z.string().optional().describe('项目源码的绝对路径(不传则自动解析)'),
|
|
99
96
|
symbolA: z.string().describe('起点符号名(如 "handleLogin")'),
|
|
100
97
|
symbolB: z.string().describe('终点符号名(如 "AuthService")'),
|
package/dist/tools/flowchart.js
CHANGED
|
@@ -317,10 +317,7 @@ function findDirectedPath(chart, fromId, toId) {
|
|
|
317
317
|
}
|
|
318
318
|
export function registerFlowchartTools(server, _ctx) {
|
|
319
319
|
const client = () => getClient();
|
|
320
|
-
server.tool('kg_flowchart', '
|
|
321
|
-
'★★ 编码前必须先设计:用 batch_add/update_node 在流程图中设计逻辑并验证每个节点正确后才能编码。执行 kg_workflow(id:"design-first") 查看完整工作流 ★★\n' +
|
|
322
|
-
'⚡ 开始任何任务前必须先查图谱:search 搜关键词 → get_node 看详情 → 有 subFlowchart 则递归下探。\n' +
|
|
323
|
-
'📝 完成修改后必须回写:bind 绑定文件 → update_node 更新描述和文档 → 新模块用 batch_add。\n' +
|
|
320
|
+
server.tool('kg_flowchart', '知识图谱核心。查询/更新流程图节点、关系、绑定文件和内嵌文档。' +
|
|
324
321
|
'actions: list|get|search|get_relations|find_path|get_node|update_node|delete_node|batch_add|bind|unbind|orphans|health|create_chart|delete_chart', {
|
|
325
322
|
action: z
|
|
326
323
|
.enum(['list', 'get', 'search', 'get_relations', 'find_path', 'get_node', 'update_node', 'delete_node', 'batch_add', 'bind', 'unbind', 'orphans', 'health', 'create_chart', 'delete_chart'])
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP 工具注册入口
|
|
3
|
-
*
|
|
3
|
+
* 7 个工具, 6 个子模块
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 🏛️ 协作: kg_meeting (1个)
|
|
5
|
+
* kg_init — 项目初始化
|
|
6
|
+
* kg_status — 仪表盘
|
|
7
|
+
* kg_flowchart — 知识图谱核心
|
|
8
|
+
* kg_workflow — AI 工作流
|
|
9
|
+
* kg_task — 任务管理
|
|
10
|
+
* kg_ref — 外部参考 (fetch/list/get/delete)
|
|
11
|
+
* code_scan/code_smart_context/code_full_path — 代码分析 (可选)
|
|
13
12
|
*/
|
|
14
13
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
15
14
|
export declare function registerTools(server: McpServer, projectId: string, user: string, agentId: string, onProjectChange?: (newProjectId: string, newApiUrl: string) => void): void;
|
package/dist/tools/index.js
CHANGED
|
@@ -1,51 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP 工具注册入口
|
|
3
|
-
*
|
|
3
|
+
* 7 个工具, 6 个子模块
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 🏛️ 协作: kg_meeting (1个)
|
|
5
|
+
* kg_init — 项目初始化
|
|
6
|
+
* kg_status — 仪表盘
|
|
7
|
+
* kg_flowchart — 知识图谱核心
|
|
8
|
+
* kg_workflow — AI 工作流
|
|
9
|
+
* kg_task — 任务管理
|
|
10
|
+
* kg_ref — 外部参考 (fetch/list/get/delete)
|
|
11
|
+
* code_scan/code_smart_context/code_full_path — 代码分析 (可选)
|
|
13
12
|
*/
|
|
14
13
|
import { createContext } from './shared.js';
|
|
15
14
|
import { registerInitTool } from './init.js';
|
|
16
15
|
import { registerStatusTool } from './kg_status.js';
|
|
17
|
-
import { registerProjectTools } from './projects.js';
|
|
18
16
|
import { registerWorkflowTools } from './workflow.js';
|
|
19
17
|
import { registerTaskTools } from './tasks.js';
|
|
20
|
-
import { registerFileTools } from './files.js';
|
|
21
|
-
import { registerDiscussionTools } from './discussion.js';
|
|
22
18
|
import { registerReferenceTools } from './refs.js';
|
|
23
|
-
import { registerPitfallTools } from './pitfalls.js';
|
|
24
19
|
import { registerAnalyzerTools } from './analyzer.js';
|
|
25
|
-
import { registerMeetingTools } from './meeting.js';
|
|
26
20
|
import { registerFlowchartTools } from './flowchart.js';
|
|
27
|
-
import { registerDocQueryTools } from './doc_query.js';
|
|
28
21
|
export function registerTools(server, projectId, user, agentId, onProjectChange) {
|
|
29
22
|
const ctx = createContext(projectId, user, agentId);
|
|
30
|
-
// 🔗 初始化
|
|
31
23
|
registerInitTool(server, ctx, onProjectChange || (() => { }));
|
|
32
|
-
// 📊 导航
|
|
33
24
|
registerStatusTool(server, ctx);
|
|
34
|
-
// 📚 知识
|
|
35
|
-
registerProjectTools(server, ctx);
|
|
36
25
|
registerWorkflowTools(server, ctx);
|
|
37
|
-
// 📝 工作流
|
|
38
26
|
registerTaskTools(server, ctx);
|
|
39
|
-
registerFileTools(server);
|
|
40
|
-
registerDiscussionTools(server, ctx);
|
|
41
27
|
registerReferenceTools(server);
|
|
42
|
-
registerPitfallTools(server);
|
|
43
|
-
// 🔬 代码分析
|
|
44
28
|
registerAnalyzerTools(server, ctx);
|
|
45
|
-
// 🏛️ 多AI协作
|
|
46
|
-
registerMeetingTools(server, ctx);
|
|
47
|
-
// 🔀 流程图
|
|
48
29
|
registerFlowchartTools(server, ctx);
|
|
49
|
-
// 📘 文档查询
|
|
50
|
-
registerDocQueryTools(server, ctx);
|
|
51
30
|
}
|
package/dist/tools/init.js
CHANGED
|
@@ -49,7 +49,7 @@ function readPpdocsFrom(dir) {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
export function registerInitTool(server, ctx, onProjectChange) {
|
|
52
|
-
server.tool('kg_init', '
|
|
52
|
+
server.tool('kg_init', '项目上下文初始化。读取指定目录的 .ppdocs 配置,切换 MCP 连接到对应项目。首次对话必须调用。不传 projectPath 则使用当前工作目录', {
|
|
53
53
|
projectPath: z.string().optional().describe('项目根目录的绝对路径(含 .ppdocs 文件),不传则使用 cwd'),
|
|
54
54
|
}, async ({ projectPath }) => safeTool(async () => {
|
|
55
55
|
const targetDir = projectPath || process.cwd();
|
package/dist/tools/kg_status.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* kg_status — 项目速览仪表盘
|
|
3
3
|
* 一次调用返回项目全貌:摘要、核心模块、活跃任务、快速导航
|
|
4
4
|
*/
|
|
5
5
|
import { getClient } from '../storage/httpClient.js';
|
|
6
6
|
import { wrap, safeTool } from './shared.js';
|
|
7
7
|
export function registerStatusTool(server, ctx) {
|
|
8
8
|
const client = () => getClient();
|
|
9
|
-
server.tool('kg_status', '
|
|
10
|
-
const [charts, activeTasks,
|
|
9
|
+
server.tool('kg_status', '项目速览仪表盘。返回流程图节点数、活跃任务数、最近变更。每次对话开始建议首先调用', {}, async () => safeTool(async () => {
|
|
10
|
+
const [charts, activeTasks, mainChart] = await Promise.all([
|
|
11
11
|
client().listFlowcharts(),
|
|
12
12
|
client().listTasks('active'),
|
|
13
|
-
client().discussionList().catch(() => []),
|
|
14
13
|
client().getFlowchart('main').catch(() => null),
|
|
15
14
|
]);
|
|
16
15
|
const nodeCount = charts.reduce((sum, c) => sum + (c.nodeCount || 0), 0);
|
|
17
|
-
const activeDiscussions = discussions.filter((d) => d.status === 'active');
|
|
18
16
|
const lines = [
|
|
19
17
|
`# 项目速览 [${ctx.projectId}]`,
|
|
20
18
|
`身份: ${ctx.user}:${ctx.agentId}`,
|
|
@@ -42,7 +40,7 @@ export function registerStatusTool(server, ctx) {
|
|
|
42
40
|
}
|
|
43
41
|
}
|
|
44
42
|
// --- 当前状态 ---
|
|
45
|
-
lines.push('', '## 当前状态', `- 流程图: ${charts.length} 张 | 节点: ${nodeCount} 个`, `- 活跃任务: ${activeTasks.length}
|
|
43
|
+
lines.push('', '## 当前状态', `- 流程图: ${charts.length} 张 | 节点: ${nodeCount} 个`, `- 活跃任务: ${activeTasks.length} 个`);
|
|
46
44
|
if (activeTasks.length > 0) {
|
|
47
45
|
lines.push('', '### 进行中的任务');
|
|
48
46
|
for (const t of activeTasks.slice(0, 5)) {
|
package/dist/tools/refs.js
CHANGED
|
@@ -9,36 +9,12 @@ function formatBytes(bytes) {
|
|
|
9
9
|
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
10
10
|
return `${(bytes / 1048576).toFixed(1)} MB`;
|
|
11
11
|
}
|
|
12
|
-
const linkSchema = z.object({
|
|
13
|
-
url: z.string(),
|
|
14
|
-
label: z.string(),
|
|
15
|
-
});
|
|
16
|
-
const fileSchema = z.object({
|
|
17
|
-
name: z.string(),
|
|
18
|
-
path: z.string(),
|
|
19
|
-
});
|
|
20
|
-
const adoptedNodeSchema = z.object({
|
|
21
|
-
chartId: z.string(),
|
|
22
|
-
nodeId: z.string(),
|
|
23
|
-
nodeLabel: z.string(),
|
|
24
|
-
});
|
|
25
12
|
export function registerReferenceTools(server) {
|
|
26
13
|
const client = () => getClient();
|
|
27
|
-
server.tool('kg_ref', '
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'actions: list|get|save|delete|read_file|fetch|refetch|import_file|reimport', {
|
|
32
|
-
action: z.enum(['list', 'get', 'save', 'delete', 'read_file', 'fetch', 'refetch', 'import_file', 'reimport']).describe('操作类型'),
|
|
33
|
-
id: z.string().optional().describe('参考 ID'),
|
|
34
|
-
title: z.string().optional().describe('参考标题 (save)'),
|
|
35
|
-
summary: z.string().optional().describe('Markdown 总结 (save)'),
|
|
36
|
-
links: z.array(linkSchema).optional().describe('参考链接 (save)'),
|
|
37
|
-
files: z.array(fileSchema).optional().describe('参考文件 (save)'),
|
|
38
|
-
scripts: z.array(fileSchema).optional().describe('解析脚本 (save)'),
|
|
39
|
-
adoptedBy: z.array(adoptedNodeSchema).optional().describe('已采用该参考的节点 (save)'),
|
|
40
|
-
path: z.string().optional().describe('参考文件路径 (read_file/import_file)'),
|
|
41
|
-
url: z.string().optional().describe('要拉取的文档 URL (fetch/refetch)'),
|
|
14
|
+
server.tool('kg_ref', '外部参考管理。fetch: 输入URL拉取网页内容到本地持久化。list/get/delete: 管理已有参考。\nactions: list|get|fetch|delete', {
|
|
15
|
+
action: z.enum(['list', 'get', 'fetch', 'delete']).describe('操作类型'),
|
|
16
|
+
id: z.string().optional().describe('参考 ID (get/delete/fetch时指定ID则为refetch)'),
|
|
17
|
+
url: z.string().optional().describe('要拉取的文档 URL (fetch)'),
|
|
42
18
|
}, async (args) => safeTool(async () => {
|
|
43
19
|
const decoded = decodeObjectStrings(args);
|
|
44
20
|
switch (decoded.action) {
|
|
@@ -47,20 +23,20 @@ export function registerReferenceTools(server) {
|
|
|
47
23
|
if (refs.length === 0)
|
|
48
24
|
return wrap('当前没有外部参考');
|
|
49
25
|
const lines = [
|
|
50
|
-
|
|
26
|
+
`外部参考 (${refs.length})`,
|
|
51
27
|
'',
|
|
52
|
-
'| ID | 标题 | 链接 | 文件 |
|
|
53
|
-
'
|
|
28
|
+
'| ID | 标题 | 链接 | 文件 | 拉取次数 |',
|
|
29
|
+
'|:---|:---|---:|---:|---:|',
|
|
54
30
|
];
|
|
55
31
|
for (const ref of refs) {
|
|
56
32
|
const fetchCount = ref.fetchHistory?.length || 0;
|
|
57
|
-
lines.push(`| ${ref.id} | ${ref.title} | ${ref.links.length} | ${ref.files.length} | ${fetchCount}
|
|
33
|
+
lines.push(`| ${ref.id} | ${ref.title} | ${ref.links.length} | ${ref.files.length} | ${fetchCount} |`);
|
|
58
34
|
}
|
|
59
35
|
return wrap(lines.join('\n'));
|
|
60
36
|
}
|
|
61
37
|
case 'get': {
|
|
62
38
|
if (!decoded.id)
|
|
63
|
-
return wrap('
|
|
39
|
+
return wrap('get 需要 id');
|
|
64
40
|
const ref = await client().getReference(decoded.id);
|
|
65
41
|
if (!ref)
|
|
66
42
|
return wrap(`未找到外部参考: ${decoded.id}`);
|
|
@@ -70,8 +46,6 @@ export function registerReferenceTools(server) {
|
|
|
70
46
|
`- ID: ${ref.id}`,
|
|
71
47
|
`- Links: ${ref.links.length}`,
|
|
72
48
|
`- Files: ${ref.files.length}`,
|
|
73
|
-
`- Scripts: ${ref.scripts.length}`,
|
|
74
|
-
`- AdoptedBy: ${ref.adoptedBy.length}`,
|
|
75
49
|
];
|
|
76
50
|
if (ref.sourceUrl) {
|
|
77
51
|
lines.push(`- SourceURL: ${ref.sourceUrl}`);
|
|
@@ -95,12 +69,6 @@ export function registerReferenceTools(server) {
|
|
|
95
69
|
lines.push(`- ${file.name}: ${file.path}`);
|
|
96
70
|
}
|
|
97
71
|
}
|
|
98
|
-
if (ref.scripts.length > 0) {
|
|
99
|
-
lines.push('', '## Scripts');
|
|
100
|
-
for (const script of ref.scripts) {
|
|
101
|
-
lines.push(`- ${script.name}: ${script.path}`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
72
|
if (ref.fetchHistory && ref.fetchHistory.length > 0) {
|
|
105
73
|
lines.push('', '## Fetch History');
|
|
106
74
|
lines.push('| # | 时间 | 标题 | 大小 | 文件 |');
|
|
@@ -109,160 +77,58 @@ export function registerReferenceTools(server) {
|
|
|
109
77
|
lines.push(`| ${i + 1} | ${h.fetchedAt.substring(0, 19)} | ${h.title} | ${formatBytes(h.contentLength)} | ${h.contentFile} |`);
|
|
110
78
|
});
|
|
111
79
|
}
|
|
112
|
-
if (ref.adoptedBy.length > 0) {
|
|
113
|
-
lines.push('', '## AdoptedBy');
|
|
114
|
-
for (const node of ref.adoptedBy) {
|
|
115
|
-
lines.push(`- ${node.nodeLabel} [${node.chartId}/${node.nodeId}]`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
80
|
return wrap(lines.join('\n'));
|
|
119
81
|
}
|
|
120
|
-
case 'save': {
|
|
121
|
-
if (!decoded.id)
|
|
122
|
-
return wrap('❌ save 需要 id');
|
|
123
|
-
if (!decoded.title)
|
|
124
|
-
return wrap('❌ save 需要 title');
|
|
125
|
-
const existing = await client().getReference(decoded.id);
|
|
126
|
-
const now = new Date().toISOString();
|
|
127
|
-
// 直接传递原始路径给后端, Rust save_reference 会自动:
|
|
128
|
-
// 1. 检测绝对路径文件
|
|
129
|
-
// 2. 复制到公共文件池/外部参考/{项目名}/
|
|
130
|
-
// 3. 复制到 ref-files/ (供 read_file API 读取)
|
|
131
|
-
// 4. 替换为相对路径保存
|
|
132
|
-
await client().saveReference({
|
|
133
|
-
id: decoded.id,
|
|
134
|
-
title: decoded.title,
|
|
135
|
-
summary: decoded.summary || existing?.summary || '',
|
|
136
|
-
links: decoded.links || existing?.links || [],
|
|
137
|
-
files: decoded.files || existing?.files || [],
|
|
138
|
-
scripts: decoded.scripts || existing?.scripts || [],
|
|
139
|
-
adoptedBy: decoded.adoptedBy || existing?.adoptedBy || [],
|
|
140
|
-
createdAt: existing?.createdAt || now,
|
|
141
|
-
updatedAt: now,
|
|
142
|
-
});
|
|
143
|
-
return wrap(`✅ 外部参考已保存 (${decoded.id})`);
|
|
144
|
-
}
|
|
145
|
-
case 'delete': {
|
|
146
|
-
if (!decoded.id)
|
|
147
|
-
return wrap('❌ delete 需要 id');
|
|
148
|
-
const ok = await client().deleteReference(decoded.id);
|
|
149
|
-
return wrap(ok ? `✅ 外部参考已删除 (${decoded.id})` : `ℹ️ 未找到外部参考 (${decoded.id})`);
|
|
150
|
-
}
|
|
151
|
-
case 'read_file': {
|
|
152
|
-
if (!decoded.path)
|
|
153
|
-
return wrap('❌ read_file 需要 path');
|
|
154
|
-
const content = await client().readReferenceFile(decoded.path);
|
|
155
|
-
return wrap(content);
|
|
156
|
-
}
|
|
157
82
|
case 'fetch': {
|
|
83
|
+
// 如果提供了 id 但没有 url,视为 refetch(从已有参考的 sourceUrl 重新拉取)
|
|
84
|
+
if (!decoded.url && decoded.id) {
|
|
85
|
+
const ref = await client().getReference(decoded.id);
|
|
86
|
+
if (!ref)
|
|
87
|
+
return wrap(`未找到参考: ${decoded.id}`);
|
|
88
|
+
if (!ref.sourceUrl)
|
|
89
|
+
return wrap(`参考 ${decoded.id} 没有 sourceUrl,请提供 url 参数`);
|
|
90
|
+
const result = await client().fetchRefUrl(ref.sourceUrl, decoded.id);
|
|
91
|
+
return wrap([
|
|
92
|
+
`文档已重新拉取 (第 ${result.fetchCount} 次)`,
|
|
93
|
+
'',
|
|
94
|
+
`| 字段 | 值 |`,
|
|
95
|
+
`|:---|:---|`,
|
|
96
|
+
`| 参考 ID | ${result.refId} |`,
|
|
97
|
+
`| 标题 | ${result.title} |`,
|
|
98
|
+
`| 内容大小 | ${formatBytes(result.contentLength)} |`,
|
|
99
|
+
'',
|
|
100
|
+
'### 内容预览',
|
|
101
|
+
'',
|
|
102
|
+
result.contentPreview.substring(0, 300),
|
|
103
|
+
].join('\n'));
|
|
104
|
+
}
|
|
158
105
|
if (!decoded.url)
|
|
159
|
-
return wrap('
|
|
106
|
+
return wrap('fetch 需要 url (要拉取的文档地址)');
|
|
160
107
|
const result = await client().fetchRefUrl(decoded.url, decoded.id);
|
|
161
108
|
const lines = [
|
|
162
|
-
|
|
109
|
+
`文档已拉取并保存`,
|
|
163
110
|
'',
|
|
164
111
|
`| 字段 | 值 |`,
|
|
165
112
|
`|:---|:---|`,
|
|
166
113
|
`| 参考 ID | ${result.refId} |`,
|
|
167
114
|
`| 标题 | ${result.title} |`,
|
|
168
|
-
`| 内容文件 | ${result.contentFile} |`,
|
|
169
115
|
`| 内容大小 | ${formatBytes(result.contentLength)} |`,
|
|
170
116
|
`| 累计拉取 | ${result.fetchCount} 次 |`,
|
|
171
117
|
'',
|
|
172
118
|
'### 内容预览',
|
|
173
119
|
'',
|
|
174
120
|
result.contentPreview.substring(0, 500),
|
|
175
|
-
'',
|
|
176
|
-
`> 💡 用 \`kg_ref(get, id:"${result.refId}")\` 查看完整详情`,
|
|
177
|
-
`> 💡 用 \`kg_ref(read_file, path:"${result.contentFile}")\` 读取完整内容`,
|
|
178
|
-
`> 💡 用 \`kg_ref(refetch, id:"${result.refId}")\` 重新拉取最新版本`,
|
|
179
|
-
];
|
|
180
|
-
return wrap(lines.join('\n'));
|
|
181
|
-
}
|
|
182
|
-
case 'refetch': {
|
|
183
|
-
if (!decoded.id)
|
|
184
|
-
return wrap('❌ refetch 需要 id (参考 ID)');
|
|
185
|
-
const ref = await client().getReference(decoded.id);
|
|
186
|
-
if (!ref)
|
|
187
|
-
return wrap(`❌ 未找到参考: ${decoded.id}`);
|
|
188
|
-
const refetchUrl = decoded.url || ref.sourceUrl;
|
|
189
|
-
if (!refetchUrl)
|
|
190
|
-
return wrap(`❌ 参考 ${decoded.id} 没有 sourceUrl,请提供 url 参数`);
|
|
191
|
-
const result = await client().fetchRefUrl(refetchUrl, decoded.id);
|
|
192
|
-
const lines = [
|
|
193
|
-
`✅ 文档已重新拉取 (第 ${result.fetchCount} 次)`,
|
|
194
|
-
'',
|
|
195
|
-
`| 字段 | 值 |`,
|
|
196
|
-
`|:---|:---|`,
|
|
197
|
-
`| 参考 ID | ${result.refId} |`,
|
|
198
|
-
`| 标题 | ${result.title} |`,
|
|
199
|
-
`| 新内容文件 | ${result.contentFile} |`,
|
|
200
|
-
`| 内容大小 | ${formatBytes(result.contentLength)} |`,
|
|
201
|
-
'',
|
|
202
|
-
'### 内容预览',
|
|
203
|
-
'',
|
|
204
|
-
result.contentPreview.substring(0, 300),
|
|
205
121
|
];
|
|
206
122
|
return wrap(lines.join('\n'));
|
|
207
123
|
}
|
|
208
|
-
case '
|
|
209
|
-
if (!decoded.id)
|
|
210
|
-
return wrap('❌ import_file 需要 id (参考 ID)');
|
|
211
|
-
if (!decoded.path)
|
|
212
|
-
return wrap('❌ import_file 需要 path (本地文件路径)');
|
|
213
|
-
const result = await client().importLocalFile(decoded.id, decoded.path);
|
|
214
|
-
const lines = [
|
|
215
|
-
`✅ 文件内容已导入参考系统`,
|
|
216
|
-
'',
|
|
217
|
-
`| 字段 | 值 |`,
|
|
218
|
-
`|:---|:---|`,
|
|
219
|
-
`| 参考 ID | ${result.refId} |`,
|
|
220
|
-
`| 源文件 | ${result.sourcePath} |`,
|
|
221
|
-
`| 存储文件 | ${result.storedFile} |`,
|
|
222
|
-
`| 内容大小 | ${formatBytes(result.contentLength)} |`,
|
|
223
|
-
`| 累计导入 | ${result.importCount} 次 |`,
|
|
224
|
-
'',
|
|
225
|
-
'### 内容预览',
|
|
226
|
-
'',
|
|
227
|
-
result.contentPreview.substring(0, 300),
|
|
228
|
-
'',
|
|
229
|
-
`> 💡 用 \`kg_ref(read_file, path:"${result.storedFile}")\` 读取完整内容`,
|
|
230
|
-
`> 💡 用 \`kg_ref(reimport, id:"${result.refId}", path:"${result.sourcePath}")\` 重新导入最新版本`,
|
|
231
|
-
];
|
|
232
|
-
return wrap(lines.join('\n'));
|
|
233
|
-
}
|
|
234
|
-
case 'reimport': {
|
|
124
|
+
case 'delete': {
|
|
235
125
|
if (!decoded.id)
|
|
236
|
-
return wrap('
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
return wrap(`❌ 未找到参考: ${decoded.id}`);
|
|
240
|
-
// 获取源文件路径: 优先使用参数中的 path, 否则取最后一次导入的源路径
|
|
241
|
-
const reimportPath = decoded.path ||
|
|
242
|
-
(ref.fileImportHistory && ref.fileImportHistory.length > 0
|
|
243
|
-
? ref.fileImportHistory[ref.fileImportHistory.length - 1].sourcePath
|
|
244
|
-
: null);
|
|
245
|
-
if (!reimportPath)
|
|
246
|
-
return wrap(`❌ 参考 ${decoded.id} 没有导入历史,请提供 path 参数`);
|
|
247
|
-
const result = await client().importLocalFile(decoded.id, reimportPath);
|
|
248
|
-
const lines = [
|
|
249
|
-
`✅ 文件已重新导入 (第 ${result.importCount} 次)`,
|
|
250
|
-
'',
|
|
251
|
-
`| 字段 | 值 |`,
|
|
252
|
-
`|:---|:---|`,
|
|
253
|
-
`| 参考 ID | ${result.refId} |`,
|
|
254
|
-
`| 源文件 | ${result.sourcePath} |`,
|
|
255
|
-
`| 新存储文件 | ${result.storedFile} |`,
|
|
256
|
-
`| 内容大小 | ${formatBytes(result.contentLength)} |`,
|
|
257
|
-
'',
|
|
258
|
-
'### 内容预览',
|
|
259
|
-
'',
|
|
260
|
-
result.contentPreview.substring(0, 300),
|
|
261
|
-
];
|
|
262
|
-
return wrap(lines.join('\n'));
|
|
126
|
+
return wrap('delete 需要 id');
|
|
127
|
+
const ok = await client().deleteReference(decoded.id);
|
|
128
|
+
return wrap(ok ? `外部参考已删除 (${decoded.id})` : `未找到外部参考 (${decoded.id})`);
|
|
263
129
|
}
|
|
264
130
|
default:
|
|
265
|
-
return wrap(
|
|
131
|
+
return wrap(`未知 action: ${decoded.action}`);
|
|
266
132
|
}
|
|
267
133
|
}));
|
|
268
134
|
}
|