@ppdocs/mcp 3.6.0 → 3.7.1
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/dist/tools/discussion.js +5 -2
- package/dist/tools/doc_query.js +30 -21
- package/dist/tools/kg_status.js +5 -1
- package/dist/tools/tasks.js +26 -5
- package/package.json +1 -1
package/dist/tools/discussion.js
CHANGED
|
@@ -139,9 +139,12 @@ export function registerDiscussionTools(server, ctx) {
|
|
|
139
139
|
switch (action) {
|
|
140
140
|
// ============ 公开操作 ============
|
|
141
141
|
case 'list': {
|
|
142
|
-
const
|
|
143
|
-
if (!Array.isArray(
|
|
142
|
+
const all = await client().discussionList();
|
|
143
|
+
if (!Array.isArray(all))
|
|
144
144
|
return wrap(`当前无活跃的讨论 (本项目: ${me})`);
|
|
145
|
+
const active = all.filter(d => isMember(d, myPid));
|
|
146
|
+
if (active.length === 0)
|
|
147
|
+
return wrap(`当前无参与的活跃讨论 (本项目: ${myPid})`);
|
|
145
148
|
return wrap(formatList(active, ctx));
|
|
146
149
|
}
|
|
147
150
|
case 'history': {
|
package/dist/tools/doc_query.js
CHANGED
|
@@ -92,17 +92,13 @@ export function registerDocQueryTools(server, _ctx) {
|
|
|
92
92
|
.slice(0, decoded.limit ?? 10);
|
|
93
93
|
if (matches.length === 0)
|
|
94
94
|
return wrap(`No docs matched: ${rawQuery}`);
|
|
95
|
-
const
|
|
96
|
-
for (const { chart, node } of matches) {
|
|
95
|
+
const rows = matches.map(({ chart, node }) => {
|
|
97
96
|
const docCount = node.docEntries?.length ?? 0;
|
|
98
97
|
const latest = docCount > 0 ? node.docEntries[docCount - 1] : null;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
lines.push(` desc: ${node.description.slice(0, 80)}${node.description.length > 80 ? '...' : ''}`);
|
|
104
|
-
}
|
|
105
|
-
return wrap(lines.join('\n'));
|
|
98
|
+
const summary = latest ? latest.summary.substring(0, 50) : (node.description?.substring(0, 50) || '');
|
|
99
|
+
return `| ${node.label} | ${chart.id}/${node.id} | ${docCount} | ${latest ? shortDate(latest.date).slice(5) : '-'} | ${summary} |`;
|
|
100
|
+
}).join('\n');
|
|
101
|
+
return wrap(`Doc search: "${rawQuery}" (${matches.length} results)\n\n| 节点 | 图谱 | 文档数 | 日期 | 摘要 |\n|:-----|:-----|:-------|:-----|:-----|\n${rows}`);
|
|
106
102
|
}
|
|
107
103
|
case 'list': {
|
|
108
104
|
const charts = decoded.chartId
|
|
@@ -113,30 +109,43 @@ export function registerDocQueryTools(server, _ctx) {
|
|
|
113
109
|
.map((n) => ({ chart, node: n })));
|
|
114
110
|
if (nodesWithDocs.length === 0)
|
|
115
111
|
return wrap('No nodes with documentation found.');
|
|
116
|
-
const
|
|
117
|
-
for (const { chart, node } of nodesWithDocs) {
|
|
112
|
+
const rows = nodesWithDocs.map(({ chart, node }) => {
|
|
118
113
|
const docCount = node.docEntries?.length ?? 0;
|
|
119
114
|
const latest = docCount > 0 ? node.docEntries[docCount - 1] : null;
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
return wrap(
|
|
115
|
+
const summary = latest ? latest.summary.substring(0, 50) : '';
|
|
116
|
+
return `| ${node.label} | ${chart.id}/${node.id} | ${docCount} | ${latest ? shortDate(latest.date).slice(5) : '-'} | ${summary} |`;
|
|
117
|
+
}).join('\n');
|
|
118
|
+
return wrap(`Documented nodes: ${nodesWithDocs.length}\n\n| 节点 | 图谱 | 文档数 | 日期 | 摘要 |\n|:-----|:-----|:-------|:-----|:-----|\n${rows}`);
|
|
124
119
|
}
|
|
125
120
|
case 'read': {
|
|
126
121
|
if (!decoded.nodeId)
|
|
127
122
|
return wrap('read requires nodeId.');
|
|
128
|
-
|
|
129
|
-
|
|
123
|
+
let chart = null;
|
|
124
|
+
let foundChartId = decoded.chartId || '';
|
|
125
|
+
if (foundChartId) {
|
|
126
|
+
chart = (await getClient().getFlowchart(foundChartId));
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// 不指定 chartId 时自动搜全部图
|
|
130
|
+
const all = await loadAllCharts();
|
|
131
|
+
for (const c of all) {
|
|
132
|
+
if (c.nodes.some(n => n.id === decoded.nodeId)) {
|
|
133
|
+
chart = c;
|
|
134
|
+
foundChartId = c.id;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
130
139
|
if (!chart)
|
|
131
|
-
return wrap(`
|
|
140
|
+
return wrap(`Node not found: ${decoded.nodeId}${decoded.chartId ? ` in ${decoded.chartId}` : ' (searched all charts)'}`);
|
|
132
141
|
const node = chart.nodes.find((n) => n.id === decoded.nodeId);
|
|
133
142
|
if (!node)
|
|
134
|
-
return wrap(`Node not found: ${decoded.nodeId} in ${
|
|
143
|
+
return wrap(`Node not found: ${decoded.nodeId} in ${foundChartId}`);
|
|
135
144
|
const entries = node.docEntries ?? [];
|
|
136
145
|
// history=true → 列出历史记录(标题+日期)
|
|
137
146
|
if (decoded.history) {
|
|
138
147
|
if (entries.length === 0)
|
|
139
|
-
return wrap(`No history for ${node.label} [${
|
|
148
|
+
return wrap(`No history for ${node.label} [${foundChartId}/${decoded.nodeId}]`);
|
|
140
149
|
const lines = [`## ${node.label} — history (${entries.length})`, ''];
|
|
141
150
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
142
151
|
const e = entries[i];
|
|
@@ -162,7 +171,7 @@ export function registerDocQueryTools(server, _ctx) {
|
|
|
162
171
|
return wrap(lines.join('\n'));
|
|
163
172
|
}
|
|
164
173
|
// 默认: 返回当前文档 (description + 最新 docEntry)
|
|
165
|
-
const lines = [`## ${node.label} [${
|
|
174
|
+
const lines = [`## ${node.label} [${foundChartId}/${node.id}]`, ''];
|
|
166
175
|
if (node.description) {
|
|
167
176
|
lines.push(node.description, '');
|
|
168
177
|
}
|
package/dist/tools/kg_status.js
CHANGED
|
@@ -6,16 +6,20 @@ import { wrap, safeTool } from './shared.js';
|
|
|
6
6
|
export function registerStatusTool(server, ctx) {
|
|
7
7
|
const client = () => getClient();
|
|
8
8
|
server.tool('kg_status', '📊 项目速览仪表盘 — 一键了解项目健康。返回: 流程图节点数、活跃任务数、最近变更。每次对话开始建议首先调用', {}, async () => safeTool(async () => {
|
|
9
|
-
const [charts, activeTasks] = await Promise.all([
|
|
9
|
+
const [charts, activeTasks, discussions] = await Promise.all([
|
|
10
10
|
client().listFlowcharts(),
|
|
11
11
|
client().listTasks('active'),
|
|
12
|
+
client().discussionList().catch(() => []),
|
|
12
13
|
]);
|
|
13
14
|
const nodeCount = charts.reduce((sum, c) => sum + (c.nodeCount || 0), 0);
|
|
15
|
+
const activeDiscussions = discussions.filter((d) => d.status === 'active');
|
|
14
16
|
const lines = [
|
|
15
17
|
`📊 项目速览 [${ctx.projectId}]`,
|
|
18
|
+
`🆔 身份: ${ctx.user}${ctx.agentId !== 'default' ? ':' + ctx.agentId : ''}`,
|
|
16
19
|
``,
|
|
17
20
|
`🔀 流程图: ${charts.length} 张 | 📦 节点: ${nodeCount} 个`,
|
|
18
21
|
`📝 活跃任务: ${activeTasks.length} 个`,
|
|
22
|
+
`📨 活跃讨论: ${activeDiscussions.length} 个`,
|
|
19
23
|
];
|
|
20
24
|
if (activeTasks.length > 0) {
|
|
21
25
|
lines.push(``, `🔧 进行中的任务:`);
|
package/dist/tools/tasks.js
CHANGED
|
@@ -97,8 +97,13 @@ export function registerTaskTools(server, ctx) {
|
|
|
97
97
|
}
|
|
98
98
|
if (tasks.length === 0)
|
|
99
99
|
return wrap(`📋 ${status === 'archived' ? '归档' : '活跃'}任务为空`);
|
|
100
|
-
const
|
|
101
|
-
|
|
100
|
+
const rows = tasks.map(t => {
|
|
101
|
+
const icon = t.status === 'active' ? '🟢' : '⚪';
|
|
102
|
+
const date = t.updated_at?.split('T')[0]?.slice(5) || '';
|
|
103
|
+
const log = (t.last_log || '').substring(0, 40);
|
|
104
|
+
return `| ${icon} | ${t.title} | ${t.id} | ${date} | ${log} |`;
|
|
105
|
+
}).join('\n');
|
|
106
|
+
return wrap(`📋 共 ${tasks.length} 个任务\n\n| 状态 | 标题 | ID | 更新 | 最近日志 |\n|:-----|:-----|:---|:-----|:---------|\n${rows}`);
|
|
102
107
|
}
|
|
103
108
|
// 有 taskId = 获取详情 (结构化摘要)
|
|
104
109
|
if (decoded.taskId) {
|
|
@@ -145,7 +150,24 @@ export function registerTaskTools(server, ctx) {
|
|
|
145
150
|
const task = await client().getTask(matched[0].id, 'smart');
|
|
146
151
|
if (!task)
|
|
147
152
|
return wrap('任务详情获取失败');
|
|
148
|
-
|
|
153
|
+
const lines = [
|
|
154
|
+
`📝 ${task.title}`,
|
|
155
|
+
`ID: ${task.id} | 状态: ${task.status}`,
|
|
156
|
+
`创建: ${task.created_at?.split('T')[0] || ''}`,
|
|
157
|
+
];
|
|
158
|
+
if (task.detail?.description)
|
|
159
|
+
lines.push(`\n📋 ${task.detail.description}`);
|
|
160
|
+
if (task.detail?.goals?.length) {
|
|
161
|
+
lines.push(`\n🎯 验收标准:`);
|
|
162
|
+
task.detail.goals.forEach((g, i) => lines.push(` ${i + 1}. ☐ ${g}`));
|
|
163
|
+
}
|
|
164
|
+
if (task.logs?.length) {
|
|
165
|
+
lines.push(`\n📊 进度日志 (${task.logs.length}条, 最近3条):`);
|
|
166
|
+
for (const log of task.logs.slice(-3)) {
|
|
167
|
+
lines.push(` [${log.log_type}] ${log.time?.split('T')[0] || ''}: ${log.content?.substring(0, 120)}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return wrap(lines.join('\n'));
|
|
149
171
|
}
|
|
150
172
|
const list = matched.map(t => `• [${t.status}] ${t.title}\n ID: ${t.id}`).join('\n');
|
|
151
173
|
return wrap(`找到 ${matched.length} 个匹配任务:\n\n${list}\n\n请使用 taskId 获取详情`);
|
|
@@ -166,8 +188,7 @@ export function registerTaskTools(server, ctx) {
|
|
|
166
188
|
: ' (无验收标准)';
|
|
167
189
|
return wrap(`✅ 进度已记录 [${lastLog?.log_type || 'progress'}]\n` +
|
|
168
190
|
`📋 ${task.title} | 日志: ${task.logs.length}条\n` +
|
|
169
|
-
`\n🎯 验收标准:\n${goalsDisplay}
|
|
170
|
-
`\n⚠️ 提示: 每完成一个步骤请立即追加进度,确保时间线完整`);
|
|
191
|
+
`\n🎯 验收标准:\n${goalsDisplay}`);
|
|
171
192
|
}
|
|
172
193
|
case 'archive': {
|
|
173
194
|
if (!decoded.taskId)
|