@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.
@@ -1,6 +1,6 @@
1
1
  /**
2
- * 📝 kg_task (4→1)
3
- * 合并: task_create, task_get, task_update, task_archive
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', '📝 任务管理创建、查询、更新进展、归档。action: create(创建)|get(查询)|update(添加日志)|archive(归档)', {
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, 默认all)'),
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
- return wrap(JSON.stringify(task, null, 2));
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
- if (!decoded.title)
51
- return wrap('❌ get 需要 title');
52
- const filterStatus = decoded.status || 'all';
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
- return wrap(task ? JSON.stringify(task, null, 2) : '任务详情获取失败');
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
- id: t.id, title: t.title, status: t.status,
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
- const task = await client().addTaskLog(decoded.taskId, decoded.log_type, decoded.content);
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
- return wrap(`✅ 日志已添加,任务共有 ${task.logs.length} 条日志`);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ppdocs/mcp",
3
- "version": "3.2.21",
3
+ "version": "3.2.23",
4
4
  "description": "ppdocs MCP Server - Knowledge Graph for Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",