@ppdocs/mcp 3.12.0 → 3.13.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.
Files changed (51) hide show
  1. package/README.md +18 -15
  2. package/dist/cli.js +56 -41
  3. package/dist/config.d.ts +23 -3
  4. package/dist/config.js +94 -36
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.js +45 -17
  7. package/dist/retainedCoreMatrix.d.ts +19 -0
  8. package/dist/retainedCoreMatrix.js +40 -0
  9. package/dist/storage/httpClient.d.ts +6 -75
  10. package/dist/storage/httpClient.js +5 -177
  11. package/dist/storage/types.d.ts +0 -35
  12. package/dist/tools/analyzer.d.ts +1 -4
  13. package/dist/tools/analyzer.js +4 -7
  14. package/dist/tools/flowchart.js +1 -4
  15. package/dist/tools/index.d.ts +8 -10
  16. package/dist/tools/index.js +8 -32
  17. package/dist/tools/kg_status.d.ts +1 -1
  18. package/dist/tools/kg_status.js +4 -6
  19. package/dist/tools/refs.js +38 -172
  20. package/dist/tools/shared.d.ts +2 -2
  21. package/dist/tools/shared.js +1 -1
  22. package/dist/tools/tasks.d.ts +1 -2
  23. package/dist/tools/tasks.js +32 -47
  24. package/dist/tools/workflow.js +2 -3
  25. package/package.json +1 -1
  26. package/templates/AGENT.md +2 -5
  27. package/templates/README.md +0 -0
  28. package/templates/commands/init.md +0 -0
  29. package/templates/commands/pp/Zero_Defec_Genesis.md +0 -0
  30. package/templates/commands/pp/diagnose.md +0 -0
  31. package/templates/commands/pp/init.md +2 -1
  32. package/templates/commands/pp/review.md +0 -0
  33. package/templates/commands/pp/sync.md +2 -1
  34. package/templates/cursorrules.md +2 -5
  35. package/templates/hooks/SystemPrompt.md +1 -1
  36. package/templates/hooks/hook.py +0 -0
  37. package/templates/kiro-rules/ppdocs.md +2 -5
  38. package/dist/tools/discussion.d.ts +0 -15
  39. package/dist/tools/discussion.js +0 -264
  40. package/dist/tools/doc_query.d.ts +0 -10
  41. package/dist/tools/doc_query.js +0 -185
  42. package/dist/tools/files.d.ts +0 -6
  43. package/dist/tools/files.js +0 -107
  44. package/dist/tools/init.d.ts +0 -12
  45. package/dist/tools/init.js +0 -89
  46. package/dist/tools/meeting.d.ts +0 -7
  47. package/dist/tools/meeting.js +0 -97
  48. package/dist/tools/pitfalls.d.ts +0 -6
  49. package/dist/tools/pitfalls.js +0 -190
  50. package/dist/tools/projects.d.ts +0 -7
  51. package/dist/tools/projects.js +0 -19
@@ -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', '📎 外部参考 管理项目引用的第三方资料、链接、脚本和样例文件。\n' +
28
- '涉及参考资料时主动调用:list 查看已有 → get 读详情 → read_file 读附件内容 → save 新增。\n' +
29
- ' fetch: 输入URL自动拉取网页内容到本地,生成解析报告,支持持续更新(refetch)。\n' +
30
- ' import_file: 导入本地文件内容到参考系统(自动保存内容副本+版本追踪)。\n' +
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
- `📎 外部参考 (${refs.length})`,
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} | ${ref.adoptedBy.length} |`);
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('get 需要 id');
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('fetch 需要 url (要拉取的文档地址)');
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 'import_file': {
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(' reimport 需要 id (参考 ID)');
237
- const ref = await client().getReference(decoded.id);
238
- if (!ref)
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(`❌ 未知 action: ${decoded.action}`);
131
+ return wrap(`未知 action: ${decoded.action}`);
266
132
  }
267
133
  }));
268
134
  }
@@ -2,13 +2,13 @@
2
2
  * MCP 工具共享模块
3
3
  * 合并原 helpers.ts + 新增 safeTool / withCross 模式
4
4
  */
5
- /** 运行时可变上下文 所有工具通过引用获取最新值 */
5
+ /** MCP 工具共享上下文 */
6
6
  export interface McpContext {
7
7
  projectId: string;
8
8
  user: string;
9
9
  agentId: string;
10
10
  }
11
- /** 创建可变上下文对象 (工具闭包捕获此对象的引用,kg_init 更新其属性) */
11
+ /** 创建共享上下文对象 */
12
12
  export declare function createContext(projectId: string, user: string, agentId: string): McpContext;
13
13
  export declare function wrap(text: string): {
14
14
  content: {
@@ -2,7 +2,7 @@
2
2
  * MCP 工具共享模块
3
3
  * 合并原 helpers.ts + 新增 safeTool / withCross 模式
4
4
  */
5
- /** 创建可变上下文对象 (工具闭包捕获此对象的引用,kg_init 更新其属性) */
5
+ /** 创建共享上下文对象 */
6
6
  export function createContext(projectId, user, agentId) {
7
7
  return { projectId, user, agentId };
8
8
  }
@@ -1,6 +1,5 @@
1
1
  /**
2
- * 📝 kg_task (4→1) 极简任务管理
3
- * AI 只需说人话写内容,后端负责所有格式化、分类、时间戳
2
+ * kg_task — 任务管理
4
3
  */
5
4
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
6
5
  import { type McpContext } from './shared.js';
@@ -1,6 +1,5 @@
1
1
  /**
2
- * 📝 kg_task (4→1) 极简任务管理
3
- * AI 只需说人话写内容,后端负责所有格式化、分类、时间戳
2
+ * kg_task — 任务管理
4
3
  */
5
4
  import { z } from 'zod';
6
5
  import { getClient } from '../storage/httpClient.js';
@@ -8,8 +7,7 @@ import { decodeObjectStrings } from '../utils.js';
8
7
  import { wrap, safeTool } from './shared.js';
9
8
  export function registerTaskTools(server, ctx) {
10
9
  const client = () => getClient();
11
- server.tool('kg_task', '📝 任务记录 — action: create(创建,建议bindTo关联节点)|get(查询)|update(追加进度)|archive(归档)|delete(删除)。⚠️ 每完成一个步骤必须立即update!\n' +
12
- '★ 涉及逻辑变更的任务,创建后必须先执行 design-first 工作流(kg_workflow(id:"design-first")),在流程图中完成设计并验证后才能编码。', {
10
+ server.tool('kg_task', '任务记录 — action: create(创建)|get(查询)|update(追加进度)|archive(归档)|delete(删除)', {
13
11
  action: z.enum(['create', 'get', 'update', 'archive', 'delete'])
14
12
  .describe('操作类型'),
15
13
  title: z.string().optional()
@@ -27,9 +25,9 @@ export function registerTaskTools(server, ctx) {
27
25
  summary: z.string().optional()
28
26
  .describe('经验总结Markdown (archive)'),
29
27
  difficulties: z.array(z.string()).optional()
30
- .describe('遇到的困难 (archive)'),
28
+ .describe('遇到的困难 (archive, 可选)'),
31
29
  solutions: z.array(z.string()).optional()
32
- .describe('解决方案 (archive)'),
30
+ .describe('解决方案 (archive, 可选)'),
33
31
  status: z.enum(['active', 'archived', 'all']).optional()
34
32
  .describe('状态筛选 (get, 默认active)'),
35
33
  bindTo: z.string().optional()
@@ -41,12 +39,10 @@ export function registerTaskTools(server, ctx) {
41
39
  switch (decoded.action) {
42
40
  case 'create': {
43
41
  if (!decoded.title)
44
- return wrap('create 需要 title');
45
- if (!decoded.description)
46
- return wrap('❌ create 需要 description');
42
+ return wrap('create 需要 title');
47
43
  const task = await client().createTask({
48
44
  title: decoded.title,
49
- description: decoded.description,
45
+ description: decoded.description || '',
50
46
  goals: decoded.goals || []
51
47
  }, ctx.user);
52
48
  // 自动绑定到流程图节点
@@ -64,22 +60,18 @@ export function registerTaskTools(server, ctx) {
64
60
  node.boundTasks.push(task.id);
65
61
  }
66
62
  await client().saveFlowchart(chartId, chart);
67
- bindMsg = `\n🔗 已绑定到节点: ${decoded.bindTo} [${chartId}]`;
63
+ bindMsg = `\n已绑定到节点: ${decoded.bindTo} [${chartId}]`;
68
64
  }
69
65
  else {
70
- bindMsg = `\n⚠️ 节点 "${decoded.bindTo}" 不存在于流程图 "${chartId}"`;
66
+ bindMsg = `\n节点 "${decoded.bindTo}" 不存在于流程图 "${chartId}"`;
71
67
  }
72
68
  }
73
69
  }
74
70
  catch {
75
- bindMsg = `\n⚠️ 绑定失败`;
71
+ bindMsg = `\n绑定失败`;
76
72
  }
77
73
  }
78
- // 约束: 未绑定流程图 强制警告
79
- if (!decoded.bindTo) {
80
- bindMsg = `\n\n⚠️ 【未关联知识锚点】此任务未绑定到任何流程图节点!\n💡 建议: 使用 kg_flowchart(get) 查看节点列表, 然后用 kg_flowchart(bind, nodeId, tasks:["${task.id}"]) 绑定\n📌 不关联的任务会成为孤岛数据, 无法被其他AI发现`;
81
- }
82
- return wrap(`✅ 任务已创建\nID: ${task.id}\n标题: ${task.title}\n验收标准: ${task.detail.goals.length}条${bindMsg}`);
74
+ return wrap(`任务已创建\nID: ${task.id}\n标题: ${task.title}\n验收标准: ${task.detail.goals.length}条${bindMsg}`);
83
75
  }
84
76
  case 'get': {
85
77
  // 无参数 = 列出活跃任务
@@ -97,33 +89,33 @@ export function registerTaskTools(server, ctx) {
97
89
  tasks = await client().listTasks(status);
98
90
  }
99
91
  if (tasks.length === 0)
100
- return wrap(`📋 ${status === 'archived' ? '归档' : '活跃'}任务为空`);
92
+ return wrap(`${status === 'archived' ? '归档' : '活跃'}任务为空`);
101
93
  const rows = tasks.map(t => {
102
- const icon = t.status === 'active' ? '🟢' : '';
94
+ const icon = t.status === 'active' ? '' : '';
103
95
  const date = t.updated_at?.split('T')[0]?.slice(5) || '';
104
96
  const log = (t.last_log || '').substring(0, 40);
105
97
  return `| ${icon} | ${t.title} | ${t.id} | ${date} | ${log} |`;
106
98
  }).join('\n');
107
- return wrap(`📋 ${tasks.length} 个任务\n\n| 状态 | 标题 | ID | 更新 | 最近日志 |\n|:-----|:-----|:---|:-----|:---------|\n${rows}`);
99
+ return wrap(`共 ${tasks.length} 个任务\n\n| 状态 | 标题 | ID | 更新 | 最近日志 |\n|:-----|:-----|:---|:-----|:---------|\n${rows}`);
108
100
  }
109
101
  // 有 taskId = 获取详情 (结构化摘要)
110
102
  if (decoded.taskId) {
111
103
  const task = await client().getTask(decoded.taskId, 'smart');
112
104
  if (!task)
113
- return wrap('任务未找到');
105
+ return wrap('任务未找到');
114
106
  const lines = [
115
- `📝 ${task.title}`,
107
+ `${task.title}`,
116
108
  `ID: ${task.id} | 状态: ${task.status}`,
117
109
  `创建: ${task.created_at?.split('T')[0] || ''}`,
118
110
  ];
119
111
  if (task.detail?.description)
120
- lines.push(`\n📋 ${task.detail.description}`);
112
+ lines.push(`\n${task.detail.description}`);
121
113
  if (task.detail?.goals?.length) {
122
- lines.push(`\n🎯 验收标准:`);
114
+ lines.push(`\n验收标准:`);
123
115
  task.detail.goals.forEach((g, i) => lines.push(` ${i + 1}. ☐ ${g}`));
124
116
  }
125
117
  if (task.logs?.length) {
126
- lines.push(`\n📊 进度日志 (${task.logs.length}条, 最近3条):`);
118
+ lines.push(`\n进度日志 (${task.logs.length}条, 最近3条):`);
127
119
  for (const log of task.logs.slice(-3)) {
128
120
  lines.push(` [${log.log_type}] ${log.time?.split('T')[0] || ''}: ${log.content?.substring(0, 120)}`);
129
121
  }
@@ -152,18 +144,18 @@ export function registerTaskTools(server, ctx) {
152
144
  if (!task)
153
145
  return wrap('任务详情获取失败');
154
146
  const lines = [
155
- `📝 ${task.title}`,
147
+ `${task.title}`,
156
148
  `ID: ${task.id} | 状态: ${task.status}`,
157
149
  `创建: ${task.created_at?.split('T')[0] || ''}`,
158
150
  ];
159
151
  if (task.detail?.description)
160
- lines.push(`\n📋 ${task.detail.description}`);
152
+ lines.push(`\n${task.detail.description}`);
161
153
  if (task.detail?.goals?.length) {
162
- lines.push(`\n🎯 验收标准:`);
154
+ lines.push(`\n验收标准:`);
163
155
  task.detail.goals.forEach((g, i) => lines.push(` ${i + 1}. ☐ ${g}`));
164
156
  }
165
157
  if (task.logs?.length) {
166
- lines.push(`\n📊 进度日志 (${task.logs.length}条, 最近3条):`);
158
+ lines.push(`\n进度日志 (${task.logs.length}条, 最近3条):`);
167
159
  for (const log of task.logs.slice(-3)) {
168
160
  lines.push(` [${log.log_type}] ${log.time?.split('T')[0] || ''}: ${log.content?.substring(0, 120)}`);
169
161
  }
@@ -175,45 +167,38 @@ export function registerTaskTools(server, ctx) {
175
167
  }
176
168
  case 'update': {
177
169
  if (!decoded.taskId)
178
- return wrap('update 需要 taskId');
170
+ return wrap('update 需要 taskId');
179
171
  if (!decoded.content)
180
- return wrap('update 需要 content');
172
+ return wrap('update 需要 content');
181
173
  // log_type 可选 — 不传则后端自动检测关键词
182
174
  const task = await client().addTaskLog(decoded.taskId, decoded.content, decoded.log_type);
183
175
  if (!task)
184
176
  return wrap('更新失败(任务不存在或已归档)');
185
177
  const lastLog = task.logs[task.logs.length - 1];
186
- // 返回任务摘要 + 验收清单,让 AI 始终知道剩余目标
187
- const goalsDisplay = task.detail.goals.length > 0
188
- ? task.detail.goals.map((g, i) => ` ${i + 1}. ☐ ${g}`).join('\n')
189
- : ' (无验收标准)';
190
- return wrap(`✅ 进度已记录 [${lastLog?.log_type || 'progress'}]\n` +
191
- `📋 ${task.title} | 日志: ${task.logs.length}条\n` +
192
- `\n🎯 验收标准:\n${goalsDisplay}`);
178
+ return wrap(`进度已记录 [${lastLog?.log_type || 'progress'}]\n` +
179
+ `${task.title} | 日志: ${task.logs.length}条`);
193
180
  }
194
181
  case 'archive': {
195
182
  if (!decoded.taskId)
196
- return wrap('archive 需要 taskId');
197
- if (!decoded.summary)
198
- return wrap('❌ archive 需要 summary');
183
+ return wrap('archive 需要 taskId');
199
184
  const task = await client().completeTask(decoded.taskId, {
200
- summary: decoded.summary,
185
+ summary: decoded.summary || '任务已完成',
201
186
  difficulties: decoded.difficulties || [],
202
187
  solutions: decoded.solutions || [],
203
188
  references: []
204
189
  });
205
190
  if (!task)
206
191
  return wrap('归档失败(任务不存在或已归档)');
207
- return wrap(`✅ 任务已归档: ${task.title}`);
192
+ return wrap(`任务已归档: ${task.title}`);
208
193
  }
209
194
  case 'delete': {
210
195
  if (!decoded.taskId)
211
- return wrap('delete 需要 taskId');
196
+ return wrap('delete 需要 taskId');
212
197
  const ok = await client().deleteTask(decoded.taskId);
213
- return wrap(ok ? `✅ 任务已删除 (ID: ${decoded.taskId})` : '删除失败(任务不存在)');
198
+ return wrap(ok ? `任务已删除 (ID: ${decoded.taskId})` : '删除失败(任务不存在)');
214
199
  }
215
200
  default:
216
- return wrap(`❌ 未知 action: ${decoded.action}`);
201
+ return wrap(`未知 action: ${decoded.action}`);
217
202
  }
218
203
  }));
219
204
  }
@@ -4,9 +4,8 @@ import { decodeObjectStrings } from '../utils.js';
4
4
  import { safeTool, wrap } from './shared.js';
5
5
  export function registerWorkflowTools(server, _ctx) {
6
6
  const client = () => getClient();
7
- server.tool('kg_workflow', '📘 文档工作流 — AI 的标准操作手册。每个工作流是一份 Markdown 文档,定义了特定场景下 AI 应该遵循的步骤和规范。\n' +
8
- '调用方式:空参数=列出所有工作流;传 id=获取工作流正文;save/delete 需显式 action。\n' +
9
- '典型用法:拿到任务后先 kg_workflow() 看有无相关工作流,有则 kg_workflow(id:"xxx") 读取并遵循执行。', {
7
+ server.tool('kg_workflow', '文档工作流。AI 的标准操作手册,每个工作流定义特定场景下应遵循的步骤。空参数=列出全部;传id=获取正文;save/delete需显式action。' +
8
+ 'actions: list|get|save|delete', {
10
9
  action: z.enum(['list', 'get', 'save', 'delete']).optional().describe('省略时自动推断:无参数=list,有id=get'),
11
10
  id: z.string().optional().describe('工作流 ID'),
12
11
  scope: z.enum(['all', 'global', 'project']).optional().describe('范围:all(默认)|global|project'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ppdocs/mcp",
3
- "version": "3.12.0",
3
+ "version": "3.13.1",
4
4
  "description": "ppdocs MCP Server - Knowledge Graph for Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -44,13 +44,10 @@
44
44
  | `kg_flowchart(batch_add, nodes, edges)` | 新增模块时批量创建节点 |
45
45
  | `kg_flowchart(create_chart)` | 为复杂模块创建子流程图 |
46
46
 
47
- ### 协作与文件
47
+ ### 外部参考
48
48
  | 工具 | 用途 |
49
49
  |:---|:---|
50
- | `kg_discuss(create/reply/list)` | AI 实例讨论 |
51
- | `kg_meeting(join/post/status)` | 多 AI 协作会议 |
52
- | `kg_files(list/read/download)` | 项目文件管理 |
53
- | `kg_ref(list/get/save)` | 外部参考资料管理 |
50
+ | `kg_ref(list|get|fetch|delete)` | 外部参考资料管理 |
54
51
 
55
52
  ## 核心原则
56
53
 
File without changes
File without changes
File without changes
File without changes
@@ -27,11 +27,12 @@
27
27
 
28
28
  ### Phase 0: 环境准备
29
29
  ```
30
- kg_init() → 仅在自动连接失效时手动兜底
31
30
  kg_status() → 查看仪表盘 (流程图/任务/状态)
32
31
  kg_flowchart(action:"list") → 已有流程图列表
33
32
  kg_flowchart(action:"get") → 主图结构总览
34
33
  ```
34
+ 若 `kg_status()` 失败,先检查当前项目目录下的 `.ppdocs` 绑定是否存在且可用。
35
+
35
36
  如果已有图谱 → 提示用户选择: 增量补充 or 重建
36
37
 
37
38
  ### Phase 1: 代码侦察
File without changes
@@ -29,11 +29,12 @@
29
29
 
30
30
  **1.1 获取图谱现状**
31
31
  ```
32
- kg_init() → 仅在自动连接失效时手动兜底
33
32
  kg_flowchart(action:"list") → 所有流程图
34
33
  kg_flowchart(action:"get") → 主图节点+连线
35
34
  kg_flowchart(action:"search", query:"关键词") → 快速定位相关节点
36
35
  ```
36
+ 若工具调用失败,先检查当前项目目录下的 `.ppdocs` 绑定是否存在且可用。
37
+
37
38
 
38
39
  **1.2 获取代码变更**
39
40
  ```
@@ -44,13 +44,10 @@
44
44
  | `kg_flowchart(batch_add, nodes, edges)` | 新增模块时批量创建节点 |
45
45
  | `kg_flowchart(create_chart)` | 为复杂模块创建子流程图 |
46
46
 
47
- ### 协作与文件
47
+ ### 外部参考
48
48
  | 工具 | 用途 |
49
49
  |:---|:---|
50
- | `kg_discuss(create/reply/list)` | AI 实例讨论 |
51
- | `kg_meeting(join/post/status)` | 多 AI 协作会议 |
52
- | `kg_files(list/read/download)` | 项目文件管理 |
53
- | `kg_ref(list/get/save)` | 外部参考资料管理 |
50
+ | `kg_ref(list|get|fetch|delete)` | 外部参考资料管理 |
54
51
 
55
52
  ## 核心原则
56
53
 
@@ -1,7 +1,7 @@
1
1
  你是严谨的软件架构师,围绕**用户知识图谱软件**工作,确保每个变动有据可查,每次成功沉淀为知识节点。
2
2
 
3
3
  ## 会话启动检查
4
- 每次会话开始时, 执行 `kg_discuss({ action: "list" })` 检查是否有涉及本项目的待回复讨论。如有, 主动提醒用户: "发现 N 条待处理讨论, 是否需要查看?"
4
+ 每次会话开始时, 执行 `kg_status()` 获取当前项目总览;如需流程指导,再执行 `kg_workflow()` 查看可用工作流。
5
5
 
6
6
  ## 核心宪法
7
7
  | 原则 | 要求 |
File without changes
@@ -44,13 +44,10 @@
44
44
  | `kg_flowchart(batch_add, nodes, edges)` | 新增模块时批量创建节点 |
45
45
  | `kg_flowchart(create_chart)` | 为复杂模块创建子流程图 |
46
46
 
47
- ### 协作与文件
47
+ ### 外部参考
48
48
  | 工具 | 用途 |
49
49
  |:---|:---|
50
- | `kg_discuss(create/reply/list)` | AI 实例讨论 |
51
- | `kg_meeting(join/post/status)` | 多 AI 协作会议 |
52
- | `kg_files(list/read/download)` | 项目文件管理 |
53
- | `kg_ref(list/get/save)` | 外部参考资料管理 |
50
+ | `kg_ref(list|get|fetch|delete)` | 外部参考资料管理 |
54
51
 
55
52
  ## 核心原则
56
53