@ppdocs/mcp 3.2.21 → 3.2.23
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/cli.js +21 -22
- package/dist/storage/httpClient.d.ts +13 -12
- package/dist/storage/httpClient.js +60 -32
- package/dist/tools/analyzer.d.ts +4 -3
- package/dist/tools/analyzer.js +7 -236
- package/dist/tools/discussion.d.ts +8 -2
- package/dist/tools/discussion.js +123 -34
- package/dist/tools/docs.js +41 -3
- package/dist/tools/flowchart.d.ts +7 -0
- package/dist/tools/flowchart.js +483 -0
- package/dist/tools/index.d.ts +8 -7
- package/dist/tools/index.js +12 -8
- package/dist/tools/rules.js +19 -1
- package/dist/tools/tasks.d.ts +2 -2
- package/dist/tools/tasks.js +112 -24
- package/package.json +1 -1
package/dist/tools/tasks.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 📝 kg_task (4→1)
|
|
3
|
-
*
|
|
2
|
+
* 📝 kg_task (4→1) — 极简任务管理
|
|
3
|
+
* AI 只需说人话写内容,后端负责所有格式化、分类、时间戳
|
|
4
4
|
*/
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { getClient } from '../storage/httpClient.js';
|
|
@@ -8,21 +8,21 @@ import { decodeObjectStrings } from '../utils.js';
|
|
|
8
8
|
import { wrap, safeTool } from './shared.js';
|
|
9
9
|
export function registerTaskTools(server, ctx) {
|
|
10
10
|
const client = () => getClient();
|
|
11
|
-
server.tool('kg_task', '📝
|
|
12
|
-
action: z.enum(['create', 'get', 'update', 'archive'])
|
|
11
|
+
server.tool('kg_task', '📝 任务记录 — action: create(创建,建议bindTo关联节点)|get(查询)|update(追加进度)|archive(归档)|delete(删除)。⚠️ 每完成一个步骤必须立即update!', {
|
|
12
|
+
action: z.enum(['create', 'get', 'update', 'archive', 'delete'])
|
|
13
13
|
.describe('操作类型'),
|
|
14
14
|
title: z.string().optional()
|
|
15
|
-
.describe('任务标题 (create/get)'),
|
|
15
|
+
.describe('任务标题 (create/get按标题搜)'),
|
|
16
16
|
description: z.string().optional()
|
|
17
17
|
.describe('任务描述Markdown (create)'),
|
|
18
18
|
goals: z.array(z.string()).optional()
|
|
19
19
|
.describe('目标清单 (create)'),
|
|
20
20
|
taskId: z.string().optional()
|
|
21
|
-
.describe('任务ID (update/archive)'),
|
|
21
|
+
.describe('任务ID (get/update/archive)'),
|
|
22
22
|
log_type: z.enum(['progress', 'issue', 'solution', 'reference']).optional()
|
|
23
|
-
.describe('日志类型 (update)'),
|
|
23
|
+
.describe('日志类型 (update, 可选-不传则自动检测)'),
|
|
24
24
|
content: z.string().optional()
|
|
25
|
-
.describe('日志内容Markdown (update)'),
|
|
25
|
+
.describe('日志内容Markdown (update, 内容长短由AI决定,支持完整Markdown)'),
|
|
26
26
|
summary: z.string().optional()
|
|
27
27
|
.describe('经验总结Markdown (archive)'),
|
|
28
28
|
difficulties: z.array(z.string()).optional()
|
|
@@ -30,7 +30,11 @@ export function registerTaskTools(server, ctx) {
|
|
|
30
30
|
solutions: z.array(z.string()).optional()
|
|
31
31
|
.describe('解决方案 (archive)'),
|
|
32
32
|
status: z.enum(['active', 'archived', 'all']).optional()
|
|
33
|
-
.describe('状态筛选 (get, 默认
|
|
33
|
+
.describe('状态筛选 (get, 默认active)'),
|
|
34
|
+
bindTo: z.string().optional()
|
|
35
|
+
.describe('绑定到流程图节点ID (create, 如"n_entry")'),
|
|
36
|
+
bindChart: z.string().optional()
|
|
37
|
+
.describe('绑定目标流程图ID (create, 默认"main")'),
|
|
34
38
|
}, async (args) => safeTool(async () => {
|
|
35
39
|
const decoded = decodeObjectStrings(args);
|
|
36
40
|
switch (decoded.action) {
|
|
@@ -44,13 +48,85 @@ export function registerTaskTools(server, ctx) {
|
|
|
44
48
|
description: decoded.description,
|
|
45
49
|
goals: decoded.goals || []
|
|
46
50
|
}, ctx.user);
|
|
47
|
-
|
|
51
|
+
// 自动绑定到流程图节点
|
|
52
|
+
let bindMsg = '';
|
|
53
|
+
if (decoded.bindTo) {
|
|
54
|
+
try {
|
|
55
|
+
const chartId = decoded.bindChart || 'main';
|
|
56
|
+
const chart = await client().getFlowchart(chartId);
|
|
57
|
+
if (chart) {
|
|
58
|
+
const node = chart.nodes?.find((n) => n.id === decoded.bindTo);
|
|
59
|
+
if (node) {
|
|
60
|
+
if (!node.boundTasks)
|
|
61
|
+
node.boundTasks = [];
|
|
62
|
+
if (!node.boundTasks.includes(task.id)) {
|
|
63
|
+
node.boundTasks.push(task.id);
|
|
64
|
+
}
|
|
65
|
+
await client().saveFlowchart(chartId, chart);
|
|
66
|
+
bindMsg = `\n🔗 已绑定到节点: ${decoded.bindTo} [${chartId}]`;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
bindMsg = `\n⚠️ 节点 "${decoded.bindTo}" 不存在于流程图 "${chartId}"`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
bindMsg = `\n⚠️ 绑定失败`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// 约束: 未绑定流程图 → 强制警告
|
|
78
|
+
if (!decoded.bindTo) {
|
|
79
|
+
bindMsg = `\n\n⚠️ 【未关联知识锚点】此任务未绑定到任何流程图节点!\n💡 建议: 使用 kg_flowchart(get) 查看节点列表, 然后用 kg_flowchart(bind, nodeId, tasks:["${task.id}"]) 绑定\n📌 不关联的任务会成为孤岛数据, 无法被其他AI发现`;
|
|
80
|
+
}
|
|
81
|
+
return wrap(`✅ 任务已创建\nID: ${task.id}\n标题: ${task.title}\n验收标准: ${task.detail.goals.length}条${bindMsg}`);
|
|
48
82
|
}
|
|
49
83
|
case 'get': {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
84
|
+
// 无参数 = 列出活跃任务
|
|
85
|
+
if (!decoded.taskId && !decoded.title) {
|
|
86
|
+
const status = decoded.status || 'active';
|
|
87
|
+
let tasks;
|
|
88
|
+
if (status === 'all') {
|
|
89
|
+
const [active, archived] = await Promise.all([
|
|
90
|
+
client().listTasks('active'),
|
|
91
|
+
client().listTasks('archived')
|
|
92
|
+
]);
|
|
93
|
+
tasks = [...active, ...archived];
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
tasks = await client().listTasks(status);
|
|
97
|
+
}
|
|
98
|
+
if (tasks.length === 0)
|
|
99
|
+
return wrap(`📋 ${status === 'archived' ? '归档' : '活跃'}任务为空`);
|
|
100
|
+
const list = tasks.map(t => `• [${t.status}] ${t.title}\n ID: ${t.id} | 更新: ${t.updated_at.split('T')[0]} | ${t.last_log || ''}`).join('\n');
|
|
101
|
+
return wrap(`📋 共 ${tasks.length} 个任务:\n\n${list}`);
|
|
102
|
+
}
|
|
103
|
+
// 有 taskId = 获取详情 (结构化摘要)
|
|
104
|
+
if (decoded.taskId) {
|
|
105
|
+
const task = await client().getTask(decoded.taskId, 'smart');
|
|
106
|
+
if (!task)
|
|
107
|
+
return wrap('❌ 任务未找到');
|
|
108
|
+
const lines = [
|
|
109
|
+
`📝 ${task.title}`,
|
|
110
|
+
`ID: ${task.id} | 状态: ${task.status}`,
|
|
111
|
+
`创建: ${task.created_at?.split('T')[0] || ''}`,
|
|
112
|
+
];
|
|
113
|
+
if (task.detail?.description)
|
|
114
|
+
lines.push(`\n📋 ${task.detail.description}`);
|
|
115
|
+
if (task.detail?.goals?.length) {
|
|
116
|
+
lines.push(`\n🎯 验收标准:`);
|
|
117
|
+
task.detail.goals.forEach((g, i) => lines.push(` ${i + 1}. ☐ ${g}`));
|
|
118
|
+
}
|
|
119
|
+
if (task.logs?.length) {
|
|
120
|
+
lines.push(`\n📊 进度日志 (${task.logs.length}条, 最近3条):`);
|
|
121
|
+
for (const log of task.logs.slice(-3)) {
|
|
122
|
+
lines.push(` [${log.log_type}] ${log.time?.split('T')[0] || ''}: ${log.content?.substring(0, 120)}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return wrap(lines.join('\n'));
|
|
126
|
+
}
|
|
127
|
+
// 有 title = 按标题搜索
|
|
53
128
|
const searchTitle = decoded.title.toLowerCase();
|
|
129
|
+
const filterStatus = decoded.status || 'all';
|
|
54
130
|
let tasks;
|
|
55
131
|
if (filterStatus === 'all') {
|
|
56
132
|
const [active, archived] = await Promise.all([
|
|
@@ -66,26 +142,32 @@ export function registerTaskTools(server, ctx) {
|
|
|
66
142
|
if (matched.length === 0)
|
|
67
143
|
return wrap(`未找到匹配 "${decoded.title}" 的任务`);
|
|
68
144
|
if (matched.length === 1) {
|
|
69
|
-
const task = await client().getTask(matched[0].id);
|
|
70
|
-
|
|
145
|
+
const task = await client().getTask(matched[0].id, 'smart');
|
|
146
|
+
if (!task)
|
|
147
|
+
return wrap('任务详情获取失败');
|
|
148
|
+
return wrap(`📝 ${task.title}\nID: ${task.id} | 状态: ${task.status}`);
|
|
71
149
|
}
|
|
72
|
-
const list = matched.map(t => (
|
|
73
|
-
|
|
74
|
-
creator: t.creator, created_at: t.created_at
|
|
75
|
-
}));
|
|
76
|
-
return wrap(`找到 ${matched.length} 个匹配任务:\n${JSON.stringify(list, null, 2)}\n\n请提供更精确的任务名称`);
|
|
150
|
+
const list = matched.map(t => `• [${t.status}] ${t.title}\n ID: ${t.id}`).join('\n');
|
|
151
|
+
return wrap(`找到 ${matched.length} 个匹配任务:\n\n${list}\n\n请使用 taskId 获取详情`);
|
|
77
152
|
}
|
|
78
153
|
case 'update': {
|
|
79
154
|
if (!decoded.taskId)
|
|
80
155
|
return wrap('❌ update 需要 taskId');
|
|
81
|
-
if (!decoded.log_type)
|
|
82
|
-
return wrap('❌ update 需要 log_type');
|
|
83
156
|
if (!decoded.content)
|
|
84
157
|
return wrap('❌ update 需要 content');
|
|
85
|
-
|
|
158
|
+
// log_type 可选 — 不传则后端自动检测关键词
|
|
159
|
+
const task = await client().addTaskLog(decoded.taskId, decoded.content, decoded.log_type);
|
|
86
160
|
if (!task)
|
|
87
161
|
return wrap('更新失败(任务不存在或已归档)');
|
|
88
|
-
|
|
162
|
+
const lastLog = task.logs[task.logs.length - 1];
|
|
163
|
+
// 返回任务摘要 + 验收清单,让 AI 始终知道剩余目标
|
|
164
|
+
const goalsDisplay = task.detail.goals.length > 0
|
|
165
|
+
? task.detail.goals.map((g, i) => ` ${i + 1}. ☐ ${g}`).join('\n')
|
|
166
|
+
: ' (无验收标准)';
|
|
167
|
+
return wrap(`✅ 进度已记录 [${lastLog?.log_type || 'progress'}]\n` +
|
|
168
|
+
`📋 ${task.title} | 日志: ${task.logs.length}条\n` +
|
|
169
|
+
`\n🎯 验收标准:\n${goalsDisplay}\n` +
|
|
170
|
+
`\n⚠️ 提示: 每完成一个步骤请立即追加进度,确保时间线完整`);
|
|
89
171
|
}
|
|
90
172
|
case 'archive': {
|
|
91
173
|
if (!decoded.taskId)
|
|
@@ -104,6 +186,12 @@ export function registerTaskTools(server, ctx) {
|
|
|
104
186
|
}
|
|
105
187
|
default:
|
|
106
188
|
return wrap(`❌ 未知 action: ${decoded.action}`);
|
|
189
|
+
case 'delete': {
|
|
190
|
+
if (!decoded.taskId)
|
|
191
|
+
return wrap('❌ delete 需要 taskId');
|
|
192
|
+
const ok = await client().deleteTask(decoded.taskId);
|
|
193
|
+
return wrap(ok ? `✅ 任务已删除 (ID: ${decoded.taskId})` : '❌ 删除失败(任务不存在)');
|
|
194
|
+
}
|
|
107
195
|
}
|
|
108
196
|
}));
|
|
109
197
|
}
|