@ppdocs/mcp 3.2.35 → 3.2.37

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 (41) hide show
  1. package/README.md +53 -27
  2. package/dist/cli.js +4 -48
  3. package/dist/storage/httpClient.d.ts +2 -27
  4. package/dist/storage/httpClient.js +3 -132
  5. package/dist/storage/types.d.ts +0 -28
  6. package/dist/storage/types.js +1 -1
  7. package/dist/tools/flowchart.d.ts +1 -5
  8. package/dist/tools/flowchart.js +647 -454
  9. package/dist/tools/index.d.ts +3 -3
  10. package/dist/tools/index.js +10 -13
  11. package/dist/tools/kg_status.d.ts +1 -1
  12. package/dist/tools/kg_status.js +6 -20
  13. package/dist/tools/projects.d.ts +2 -3
  14. package/dist/tools/projects.js +2 -3
  15. package/dist/tools/rules.d.ts +2 -3
  16. package/dist/tools/rules.js +2 -3
  17. package/dist/tools/shared.d.ts +0 -12
  18. package/dist/tools/shared.js +0 -59
  19. package/package.json +1 -1
  20. package/templates/AGENT.md +63 -38
  21. package/templates/commands/init.md +3 -3
  22. package/templates/commands/pp/diagnose.md +3 -3
  23. package/templates/commands/pp/init.md +9 -18
  24. package/templates/commands/pp/sync.md +10 -12
  25. package/templates/cursorrules.md +63 -64
  26. package/templates/hooks/SystemPrompt.md +14 -14
  27. package/templates/kiro-rules/ppdocs.md +63 -142
  28. package/dist/agent.d.ts +0 -6
  29. package/dist/agent.js +0 -130
  30. package/dist/tools/docs.d.ts +0 -8
  31. package/dist/tools/docs.js +0 -285
  32. package/dist/tools/helpers.d.ts +0 -37
  33. package/dist/tools/helpers.js +0 -94
  34. package/dist/vector/index.d.ts +0 -56
  35. package/dist/vector/index.js +0 -228
  36. package/dist/vector/manager.d.ts +0 -48
  37. package/dist/vector/manager.js +0 -250
  38. package/dist/web/server.d.ts +0 -43
  39. package/dist/web/server.js +0 -808
  40. package/dist/web/ui.d.ts +0 -5
  41. package/dist/web/ui.js +0 -642
@@ -1,285 +0,0 @@
1
- /**
2
- * 📄 kg_doc (5合1) + 🏗️ kg_tree (2合1)
3
- * 合并: kg_create_node, kg_delete_node, kg_update_node, kg_read_node, kg_copy_node
4
- * kg_get_tree, kg_get_docs_by_status
5
- */
6
- import { z } from 'zod';
7
- import { getClient } from '../storage/httpClient.js';
8
- import { decodeObjectStrings } from '../utils.js';
9
- import { wrap, safeTool, crossPrefix, formatDocMarkdown, formatTreeText, countTreeDocs } from './shared.js';
10
- export function registerDocTools(server, ctx) {
11
- const client = () => getClient();
12
- // ========== kg_doc: 文档 CRUD 统一入口 ==========
13
- server.tool('kg_doc', '📄 参考文档 — action: create(创建,建议bindTo关联节点)|read|update|delete|batch_update|copy。⚠️ 不绑定节点的文档会成为孤岛!', {
14
- action: z.enum(['create', 'read', 'update', 'delete', 'batch_update', 'copy'])
15
- .describe('操作类型'),
16
- path: z.string().optional()
17
- .describe('文档路径(如"/前端/组件/Modal")。create/read/update/delete 必填'),
18
- content: z.string().optional()
19
- .describe('Markdown内容 (create/update)'),
20
- summary: z.string().optional()
21
- .describe('一句话简介 (create/update)'),
22
- status: z.string().optional()
23
- .describe('文档状态 (create/update, 默认"已完成")'),
24
- versions: z.array(z.object({
25
- version: z.number(), date: z.string(), changes: z.string()
26
- })).optional().describe('版本记录 (update)'),
27
- bugfixes: z.array(z.object({
28
- date: z.string(), issue: z.string(), solution: z.string()
29
- })).optional().describe('修复记录 (update)'),
30
- updates: z.array(z.object({
31
- nodeId: z.string().describe('文档路径'),
32
- summary: z.string().optional(),
33
- content: z.string().optional(),
34
- status: z.string().optional(),
35
- versions: z.array(z.object({
36
- version: z.number(), date: z.string(), changes: z.string()
37
- })).optional(),
38
- bugfixes: z.array(z.object({
39
- date: z.string(), issue: z.string(), solution: z.string()
40
- })).optional()
41
- })).optional().describe('批量更新数组 (batch_update)'),
42
- targetProject: z.string().optional()
43
- .describe('跨项目读取时的目标项目ID (read)'),
44
- sourceProject: z.string().optional()
45
- .describe('复制源项目ID (copy)'),
46
- sourcePath: z.union([z.string(), z.array(z.string())]).optional()
47
- .describe('复制源路径 (copy, 单个或数组)'),
48
- targetPath: z.string().optional()
49
- .describe('复制目标路径前缀 (copy)'),
50
- bindTo: z.string().optional()
51
- .describe('绑定到流程图节点ID (create/update, 如"n_frontend")'),
52
- bindChart: z.string().optional()
53
- .describe('绑定目标流程图ID (create/update, 默认"main")'),
54
- }, async (args) => safeTool(async () => {
55
- const d = decodeObjectStrings(args);
56
- switch (d.action) {
57
- // ---- CREATE ----
58
- case 'create': {
59
- if (!d.path)
60
- return wrap('❌ create 需要 path');
61
- if (!d.content)
62
- return wrap('❌ create 需要 content');
63
- const doc = await client().createDoc(d.path, {
64
- summary: d.summary || '',
65
- content: d.content,
66
- versions: [{ version: 0.1, date: new Date().toISOString(), changes: '初始创建' }],
67
- bugfixes: [],
68
- status: d.status || '已完成'
69
- });
70
- // 绑定到流程图节点
71
- let bindMsg = '';
72
- if (d.bindTo) {
73
- try {
74
- const chartId = d.bindChart || 'main';
75
- const chart = await client().getFlowchart(chartId);
76
- if (chart) {
77
- const node = chart.nodes?.find((n) => n.id === d.bindTo);
78
- if (node) {
79
- if (!node.boundDocs)
80
- node.boundDocs = [];
81
- if (!node.boundDocs.includes(d.path)) {
82
- node.boundDocs.push(d.path);
83
- }
84
- // Save back via PUT (use the existing save method)
85
- chart.id = chartId;
86
- await client().saveFlowchart(chartId, chart);
87
- bindMsg = `\n🔗 已绑定到节点: ${d.bindTo} [${chartId}]`;
88
- }
89
- else {
90
- bindMsg = `\n⚠️ 节点 "${d.bindTo}" 不存在于流程图 "${chartId}"`;
91
- }
92
- }
93
- }
94
- catch {
95
- bindMsg = `\n⚠️ 绑定失败(流程图操作异常)`;
96
- }
97
- }
98
- // 约束: 未绑定流程图节点 → 强制警告
99
- if (!d.bindTo) {
100
- bindMsg = `\n\n⚠️ 【未关联知识锚点】文档未绑定到流程图节点!\n💡 建议: 使用 kg_flowchart(bind, nodeId, docs:["${d.path}"]) 绑定\n📌 孤立文档无法被 AI 通过节点关联发现`;
101
- }
102
- return wrap(`✅ 文档已创建: ${d.path}${bindMsg}`);
103
- }
104
- // ---- READ ----
105
- case 'read': {
106
- if (!d.path)
107
- return wrap('❌ read 需要 path');
108
- const doc = d.targetProject
109
- ? await client().crossGetDoc(d.targetProject, d.path)
110
- : await client().getDoc(d.path);
111
- if (!doc)
112
- return wrap(d.targetProject ? `文档不存在(项目: ${d.targetProject})` : '文档不存在');
113
- const lines = formatDocMarkdown(doc, d.path);
114
- return wrap(d.targetProject ? crossPrefix(d.targetProject) + lines.join('\n') : lines.join('\n'));
115
- }
116
- // ---- UPDATE ----
117
- case 'update': {
118
- if (!d.path)
119
- return wrap('❌ update 需要 path');
120
- if (d.path === '/' || d.path === '_root') {
121
- // 根文档更新
122
- const existing = await client().getDoc('/');
123
- if (!existing)
124
- return wrap('更新失败(根文档不存在)');
125
- const updates = {};
126
- if (d.summary !== undefined)
127
- updates.summary = d.summary;
128
- if (d.content !== undefined)
129
- updates.content = d.content;
130
- const upd = await client().updateDoc('/', updates);
131
- return wrap(upd ? '✅ 项目介绍已更新' : '更新失败');
132
- }
133
- const existing = await client().getDoc(d.path);
134
- if (!existing)
135
- return wrap('更新失败(文档不存在)');
136
- const updates = {};
137
- if (d.summary !== undefined)
138
- updates.summary = d.summary;
139
- if (d.content !== undefined)
140
- updates.content = d.content;
141
- if (d.status !== undefined)
142
- updates.status = d.status;
143
- if (d.versions !== undefined)
144
- updates.versions = d.versions;
145
- if (d.bugfixes !== undefined)
146
- updates.bugfixes = d.bugfixes;
147
- const doc = await client().updateDoc(d.path, updates);
148
- if (!doc)
149
- return wrap('更新失败');
150
- return wrap(`✅ 文档已更新: ${d.path}\n状态: ${doc.status || '已完成'}\n摘要: ${doc.summary || ''}`);
151
- }
152
- // ---- DELETE ----
153
- case 'delete': {
154
- if (!d.path)
155
- return wrap('❌ delete 需要 path');
156
- const paths = Array.isArray(d.path) ? d.path : [d.path];
157
- const results = [];
158
- for (const p of paths) {
159
- results.push({ path: p, success: await client().deleteDoc(p) });
160
- }
161
- if (paths.length === 1) {
162
- return wrap(results[0].success ? '删除成功' : '删除失败(文档不存在或是根文档)');
163
- }
164
- const ok = results.filter(r => r.success).length;
165
- const failed = results.filter(r => !r.success).map(r => r.path);
166
- let msg = `✅ 批量删除: ${ok}/${paths.length} 成功`;
167
- if (failed.length > 0)
168
- msg += `\n❌ 失败: ${failed.join(', ')}`;
169
- return wrap(msg);
170
- }
171
- // ---- BATCH UPDATE ----
172
- case 'batch_update': {
173
- if (!d.updates || !Array.isArray(d.updates))
174
- return wrap('❌ batch_update 需要 updates 数组');
175
- const results = [];
176
- for (const item of d.updates) {
177
- if (item.nodeId === '/' || item.nodeId === '_root') {
178
- results.push({ path: item.nodeId, success: false });
179
- continue;
180
- }
181
- const existing = await client().getDoc(item.nodeId);
182
- if (!existing) {
183
- results.push({ path: item.nodeId, success: false });
184
- continue;
185
- }
186
- const upd = {};
187
- if (item.summary !== undefined)
188
- upd.summary = item.summary;
189
- if (item.content !== undefined)
190
- upd.content = item.content;
191
- if (item.status !== undefined)
192
- upd.status = item.status;
193
- if (item.versions !== undefined)
194
- upd.versions = item.versions;
195
- if (item.bugfixes !== undefined)
196
- upd.bugfixes = item.bugfixes;
197
- const doc = await client().updateDoc(item.nodeId, upd);
198
- results.push({ path: item.nodeId, success: !!doc });
199
- }
200
- const ok = results.filter(r => r.success).length;
201
- const failed = results.filter(r => !r.success).map(r => r.path);
202
- let msg = `✅ 批量更新: ${ok}/${d.updates.length} 成功`;
203
- if (failed.length > 0)
204
- msg += `\n❌ 失败: ${failed.join(', ')}`;
205
- return wrap(msg);
206
- }
207
- // ---- COPY ----
208
- case 'copy': {
209
- if (!d.sourceProject)
210
- return wrap('❌ copy 需要 sourceProject');
211
- if (!d.sourcePath)
212
- return wrap('❌ copy 需要 sourcePath');
213
- const paths = Array.isArray(d.sourcePath) ? d.sourcePath : [d.sourcePath];
214
- const results = [];
215
- for (const srcPath of paths) {
216
- try {
217
- const doc = await client().crossGetDoc(d.sourceProject, srcPath);
218
- if (!doc) {
219
- results.push({ path: srcPath, success: false, error: '源文档不存在' });
220
- continue;
221
- }
222
- const destPath = d.targetPath
223
- ? `${d.targetPath.replace(/\/$/, '')}${srcPath}`
224
- : srcPath;
225
- const existing = await client().getDoc(destPath);
226
- if (existing) {
227
- await client().updateDoc(destPath, { summary: doc.summary, content: doc.content });
228
- }
229
- else {
230
- await client().createDoc(destPath, { summary: doc.summary, content: doc.content, status: doc.status });
231
- }
232
- results.push({ path: destPath, success: true });
233
- }
234
- catch (e) {
235
- results.push({ path: srcPath, success: false, error: String(e) });
236
- }
237
- }
238
- const ok = results.filter(r => r.success).length;
239
- if (paths.length === 1) {
240
- return wrap(results[0].success
241
- ? `✅ 已复制: ${results[0].path}\n来源: [${d.sourceProject}] ${paths[0]}`
242
- : `❌ 复制失败: ${results[0].error}`);
243
- }
244
- const failed = results.filter(r => !r.success);
245
- let msg = `✅ 批量复制: ${ok}/${paths.length} 成功`;
246
- if (failed.length > 0)
247
- msg += `\n❌ 失败:\n${failed.map(f => ` - ${f.path}: ${f.error}`).join('\n')}`;
248
- return wrap(msg);
249
- }
250
- default:
251
- return wrap(`❌ 未知 action: ${d.action}`);
252
- }
253
- }));
254
- // ========== kg_tree: 知识全景图 ==========
255
- server.tool('kg_tree', '🏗️ 项目知识全景图 ★最快了解项目的方式★ 获取完整知识库目录树(含文档数统计)。传 statusFilter 可筛选特定状态文档。建议任何操作前先调用获取全局视图', {
256
- statusFilter: z.array(z.string()).optional()
257
- .describe('按状态筛选(如["未完成","待修复"]),不传则展示全景目录树'),
258
- targetProject: z.string().optional()
259
- .describe('跨项目查看,填目标项目ID或名称'),
260
- }, async (args) => safeTool(async () => {
261
- // 状态筛选模式
262
- if (args.statusFilter && args.statusFilter.length > 0) {
263
- const docs = args.targetProject
264
- ? await client().crossGetDocsByStatus(args.targetProject, args.statusFilter)
265
- : await client().getDocsByStatus(args.statusFilter);
266
- if (docs.length === 0) {
267
- return wrap(`未找到状态为 [${args.statusFilter.join(', ')}] 的文档${args.targetProject ? `(项目: ${args.targetProject})` : ''}`);
268
- }
269
- const lines = docs.map(d => `- ${d.path} (${d.status || '已完成'}): ${d.summary || '无简介'}`);
270
- const prefix = args.targetProject ? crossPrefix(args.targetProject) : '';
271
- return wrap(`${prefix}找到 ${docs.length} 个文档:\n\n${lines.join('\n')}`);
272
- }
273
- // 全景目录树模式
274
- const tree = args.targetProject
275
- ? await client().crossGetTree(args.targetProject)
276
- : await client().getTree();
277
- if (!tree || tree.length === 0) {
278
- return wrap(args.targetProject ? `知识库为空(项目: ${args.targetProject})` : '知识库为空');
279
- }
280
- const treeText = formatTreeText(tree);
281
- const docCount = countTreeDocs(tree);
282
- const prefix = args.targetProject ? crossPrefix(args.targetProject) : '';
283
- return wrap(`${prefix}共 ${docCount} 个文档\n\n${treeText}`);
284
- }));
285
- }
@@ -1,37 +0,0 @@
1
- /**
2
- * MCP 工具辅助函数
3
- */
4
- import type { DocData } from '../storage/types.js';
5
- /**
6
- * 包装返回结果为 MCP 格式
7
- */
8
- export declare function wrap(text: string): {
9
- content: {
10
- type: "text";
11
- text: string;
12
- }[];
13
- };
14
- /**
15
- * 格式化文档为 Markdown 输出
16
- */
17
- export declare function formatDocMarkdown(doc: DocData, path: string): string[];
18
- interface TreeNode {
19
- path: string;
20
- name: string;
21
- summary?: string;
22
- isDir: boolean;
23
- children?: TreeNode[];
24
- }
25
- /**
26
- * 格式化树为文本
27
- */
28
- export declare function formatTreeText(tree: TreeNode[]): string;
29
- /**
30
- * 统计树中的文档数
31
- */
32
- export declare function countTreeDocs(tree: TreeNode[]): number;
33
- /**
34
- * 格式化文件大小
35
- */
36
- export declare function formatFileSize(bytes: number): string;
37
- export {};
@@ -1,94 +0,0 @@
1
- /**
2
- * MCP 工具辅助函数
3
- */
4
- // ==================== 通用包装 ====================
5
- /**
6
- * 包装返回结果为 MCP 格式
7
- */
8
- export function wrap(text) {
9
- return { content: [{ type: 'text', text }] };
10
- }
11
- // ==================== 文档格式化 ====================
12
- /**
13
- * 格式化文档为 Markdown 输出
14
- */
15
- export function formatDocMarkdown(doc, path) {
16
- const lines = [];
17
- const name = path.split('/').pop() || path;
18
- // 标题和简介
19
- lines.push(`## ${name}\n`);
20
- if (doc.summary) {
21
- lines.push(`> ${doc.summary}\n`);
22
- }
23
- // 内容
24
- if (doc.content) {
25
- lines.push('**内容**');
26
- lines.push(doc.content);
27
- lines.push('');
28
- }
29
- // 更新历史
30
- if (doc.versions && doc.versions.length > 0) {
31
- lines.push('**更新历史**');
32
- lines.push('| 版本 | 日期 | 变更 |');
33
- lines.push('|:---|:---|:---|');
34
- doc.versions.forEach((v) => lines.push(`| ${v.version} | ${v.date} | ${v.changes} |`));
35
- lines.push('');
36
- }
37
- // 修复历史
38
- if (doc.bugfixes && doc.bugfixes.length > 0) {
39
- lines.push('**修复历史**');
40
- lines.push('| 日期 | 问题 | 方案 |');
41
- lines.push('|:---|:---|:---|');
42
- doc.bugfixes.forEach((b) => lines.push(`| ${b.date} | ${b.issue} | ${b.solution} |`));
43
- lines.push('');
44
- }
45
- return lines;
46
- }
47
- /**
48
- * 格式化树为文本
49
- */
50
- export function formatTreeText(tree) {
51
- function format(nodes, prefix = '') {
52
- const lines = [];
53
- nodes.forEach((node, index) => {
54
- const isLast = index === nodes.length - 1;
55
- const connector = isLast ? '└── ' : '├── ';
56
- const childPrefix = isLast ? ' ' : '│ ';
57
- const icon = node.isDir ? '📁' : '📄';
58
- const summary = node.summary ? ` # ${node.summary}` : '';
59
- lines.push(`${prefix}${connector}${icon} ${node.name}${summary}`);
60
- if (node.children && node.children.length > 0) {
61
- lines.push(...format(node.children, prefix + childPrefix));
62
- }
63
- });
64
- return lines;
65
- }
66
- return format(tree).join('\n');
67
- }
68
- /**
69
- * 统计树中的文档数
70
- */
71
- export function countTreeDocs(tree) {
72
- let count = 0;
73
- function traverse(nodes) {
74
- for (const node of nodes) {
75
- if (!node.isDir)
76
- count++;
77
- if (node.children)
78
- traverse(node.children);
79
- }
80
- }
81
- traverse(tree);
82
- return count;
83
- }
84
- // ==================== 文件大小格式化 ====================
85
- /**
86
- * 格式化文件大小
87
- */
88
- export function formatFileSize(bytes) {
89
- if (bytes < 1024)
90
- return `${bytes} B`;
91
- if (bytes < 1024 * 1024)
92
- return `${(bytes / 1024).toFixed(1)} KB`;
93
- return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
94
- }
@@ -1,56 +0,0 @@
1
- /**
2
- * 向量检索服务 - 基于 transformers.js 的语义搜索
3
- * @version 0.2
4
- */
5
- interface VectorConfig {
6
- model: string;
7
- quantized: boolean;
8
- }
9
- /**
10
- * 获取向量配置 (从 ~/.ppdocs/config/vector.json 读取)
11
- */
12
- export declare function getVectorConfig(): VectorConfig;
13
- interface VectorSearchResult {
14
- id: string;
15
- title: string;
16
- similarity: number;
17
- }
18
- /**
19
- * 获取索引文件路径
20
- */
21
- export declare function getIndexPath(projectId: string): string;
22
- /**
23
- * 添加或更新节点向量
24
- */
25
- export declare function upsertNode(projectId: string, nodeId: string, title: string, description: string, categories?: string[]): Promise<void>;
26
- /**
27
- * 删除节点向量
28
- */
29
- export declare function removeNode(projectId: string, nodeId: string): Promise<void>;
30
- /**
31
- * 向量语义搜索
32
- */
33
- export declare function semanticSearch(projectId: string, query: string, limit?: number): Promise<VectorSearchResult[]>;
34
- /**
35
- * 批量构建索引 (用于初始化或重建)
36
- */
37
- export declare function buildIndex(projectId: string, nodes: Array<{
38
- id: string;
39
- title: string;
40
- description: string;
41
- categories?: string[];
42
- }>): Promise<number>;
43
- /**
44
- * 检查索引状态
45
- */
46
- export declare function getIndexStats(projectId: string): Promise<{
47
- count: number;
48
- model: string;
49
- dimension: number;
50
- loaded: boolean;
51
- }>;
52
- /**
53
- * 预加载模型 (可选, 用于启动时预热)
54
- */
55
- export declare function preloadModel(): Promise<void>;
56
- export {};