@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.
- package/README.md +18 -15
- package/dist/cli.js +56 -41
- package/dist/config.d.ts +23 -3
- package/dist/config.js +94 -36
- package/dist/index.d.ts +1 -1
- package/dist/index.js +45 -17
- package/dist/retainedCoreMatrix.d.ts +19 -0
- package/dist/retainedCoreMatrix.js +40 -0
- package/dist/storage/httpClient.d.ts +6 -75
- package/dist/storage/httpClient.js +5 -177
- package/dist/storage/types.d.ts +0 -35
- 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 -10
- package/dist/tools/index.js +8 -32
- 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/shared.d.ts +2 -2
- package/dist/tools/shared.js +1 -1
- 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/templates/AGENT.md +2 -5
- package/templates/README.md +0 -0
- package/templates/commands/init.md +0 -0
- package/templates/commands/pp/Zero_Defec_Genesis.md +0 -0
- package/templates/commands/pp/diagnose.md +0 -0
- package/templates/commands/pp/init.md +2 -1
- package/templates/commands/pp/review.md +0 -0
- package/templates/commands/pp/sync.md +2 -1
- package/templates/cursorrules.md +2 -5
- package/templates/hooks/SystemPrompt.md +1 -1
- package/templates/hooks/hook.py +0 -0
- package/templates/kiro-rules/ppdocs.md +2 -5
- 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/init.d.ts +0 -12
- package/dist/tools/init.js +0 -89
- 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/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
|
}
|
package/dist/tools/shared.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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: {
|
package/dist/tools/shared.js
CHANGED
package/dist/tools/tasks.d.ts
CHANGED
package/dist/tools/tasks.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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', '
|
|
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('
|
|
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
|
|
63
|
+
bindMsg = `\n已绑定到节点: ${decoded.bindTo} [${chartId}]`;
|
|
68
64
|
}
|
|
69
65
|
else {
|
|
70
|
-
bindMsg = `\n
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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('
|
|
170
|
+
return wrap('update 需要 taskId');
|
|
179
171
|
if (!decoded.content)
|
|
180
|
-
return wrap('
|
|
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
|
-
|
|
187
|
-
|
|
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('
|
|
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(
|
|
192
|
+
return wrap(`任务已归档: ${task.title}`);
|
|
208
193
|
}
|
|
209
194
|
case 'delete': {
|
|
210
195
|
if (!decoded.taskId)
|
|
211
|
-
return wrap('
|
|
196
|
+
return wrap('delete 需要 taskId');
|
|
212
197
|
const ok = await client().deleteTask(decoded.taskId);
|
|
213
|
-
return wrap(ok ?
|
|
198
|
+
return wrap(ok ? `任务已删除 (ID: ${decoded.taskId})` : '删除失败(任务不存在)');
|
|
214
199
|
}
|
|
215
200
|
default:
|
|
216
|
-
return wrap(
|
|
201
|
+
return wrap(`未知 action: ${decoded.action}`);
|
|
217
202
|
}
|
|
218
203
|
}));
|
|
219
204
|
}
|
package/dist/tools/workflow.js
CHANGED
|
@@ -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', '
|
|
8
|
-
'
|
|
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
package/templates/AGENT.md
CHANGED
|
@@ -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
|
-
| `
|
|
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
|
|
package/templates/README.md
CHANGED
|
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
|
```
|
package/templates/cursorrules.md
CHANGED
|
@@ -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
|
-
| `
|
|
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
|
|
package/templates/hooks/hook.py
CHANGED
|
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
|
-
| `
|
|
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
|
|