@ppdocs/mcp 2.6.7 → 2.6.9
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 +5 -2
- package/dist/cli.js +6 -7
- package/dist/storage/httpClient.d.ts +7 -14
- package/dist/storage/httpClient.js +30 -10
- package/dist/tools/index.js +11 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -109,10 +109,9 @@ npx @ppdocs/mcp init -p <projectId> -k <key> --codex
|
|
|
109
109
|
| `kg_update_node` | 更新节点内容 |
|
|
110
110
|
| `kg_delete_node` | 删除节点 |
|
|
111
111
|
| `kg_lock_node` | 锁定节点 (AI 只能锁定,解锁需前端手动) |
|
|
112
|
-
| `kg_list_nodes` |
|
|
112
|
+
| `kg_list_nodes` | 列出节点 (支持 status/edges 过滤,maxEdges=0 查孤立节点) |
|
|
113
113
|
| `kg_search` | 关键词搜索 |
|
|
114
114
|
| `kg_find_path` | 查找依赖路径 |
|
|
115
|
-
| `kg_find_orphans` | 查找孤立节点 |
|
|
116
115
|
| `kg_get_relations` | 获取节点关系 |
|
|
117
116
|
|
|
118
117
|
### 任务管理工具
|
|
@@ -175,6 +174,10 @@ npx @ppdocs/mcp init -p <projectId> -k <key> --codex
|
|
|
175
174
|
|
|
176
175
|
## 更新日志
|
|
177
176
|
|
|
177
|
+
### v2.6.9
|
|
178
|
+
- ✨ `kg_list_nodes` 支持 status/minEdges/maxEdges 过滤
|
|
179
|
+
- 🗑️ 移除 `kg_find_orphans` (用 `kg_list_nodes(maxEdges: 0)` 替代)
|
|
180
|
+
|
|
178
181
|
### v2.5.0
|
|
179
182
|
- ✨ 新增 CLI init 命令,自动安装工作流模板
|
|
180
183
|
- ✨ 支持 Codex 模式 (--codex)
|
package/dist/cli.js
CHANGED
|
@@ -242,7 +242,6 @@ function generateHooksConfig() {
|
|
|
242
242
|
hooks: {
|
|
243
243
|
UserPromptSubmit: [
|
|
244
244
|
{
|
|
245
|
-
matcher: '*',
|
|
246
245
|
hooks: [{ type: 'command', command }]
|
|
247
246
|
}
|
|
248
247
|
]
|
|
@@ -266,12 +265,12 @@ function installClaudeTemplates(cwd) {
|
|
|
266
265
|
copyDirRecursive(srcHooks, destHooks);
|
|
267
266
|
console.log(`✅ Installed .claude/hooks/`);
|
|
268
267
|
}
|
|
269
|
-
// 3. 生成跨平台 hooks 配置并合并到 settings.
|
|
270
|
-
const
|
|
268
|
+
// 3. 生成跨平台 hooks 配置并合并到 settings.json (hooks 必须在 settings.json 才能生效)
|
|
269
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
271
270
|
let existingSettings = {};
|
|
272
|
-
if (fs.existsSync(
|
|
271
|
+
if (fs.existsSync(settingsPath)) {
|
|
273
272
|
try {
|
|
274
|
-
existingSettings = JSON.parse(fs.readFileSync(
|
|
273
|
+
existingSettings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
275
274
|
}
|
|
276
275
|
catch { /* ignore */ }
|
|
277
276
|
}
|
|
@@ -281,8 +280,8 @@ function installClaudeTemplates(cwd) {
|
|
|
281
280
|
...hooksConfig
|
|
282
281
|
};
|
|
283
282
|
fs.mkdirSync(claudeDir, { recursive: true });
|
|
284
|
-
fs.writeFileSync(
|
|
285
|
-
console.log(`✅ Configured .claude/settings.
|
|
283
|
+
fs.writeFileSync(settingsPath, JSON.stringify(mergedSettings, null, 2));
|
|
284
|
+
console.log(`✅ Configured .claude/settings.json hooks (${process.platform})`);
|
|
286
285
|
}
|
|
287
286
|
/** 安装 Codex 模板 (生成 AGENTS.md) */
|
|
288
287
|
function installCodexTemplates(cwd) {
|
|
@@ -5,11 +5,16 @@
|
|
|
5
5
|
* API URL 格式: http://localhost:20001/api/:projectId/:password/...
|
|
6
6
|
*/
|
|
7
7
|
import type { NodeData, SearchResult, PathResult, BugfixRecord, Task, TaskSummary, TaskLogType, TaskExperience } from './types.js';
|
|
8
|
+
export interface ListNodesFilter {
|
|
9
|
+
status?: 'incomplete' | 'complete' | 'fixing' | 'refactoring' | 'deprecated';
|
|
10
|
+
minEdges?: number;
|
|
11
|
+
maxEdges?: number;
|
|
12
|
+
}
|
|
8
13
|
export declare class PpdocsApiClient {
|
|
9
14
|
private baseUrl;
|
|
10
15
|
constructor(apiUrl: string);
|
|
11
16
|
private request;
|
|
12
|
-
listNodes(): Promise<NodeData[]>;
|
|
17
|
+
listNodes(filter?: ListNodesFilter): Promise<NodeData[]>;
|
|
13
18
|
getNode(nodeId: string): Promise<NodeData | null>;
|
|
14
19
|
createNode(node: Partial<NodeData>): Promise<NodeData>;
|
|
15
20
|
updateNode(nodeId: string, updates: Partial<NodeData>): Promise<NodeData | null>;
|
|
@@ -22,12 +27,6 @@ export declare class PpdocsApiClient {
|
|
|
22
27
|
lockNode(nodeId: string, locked: boolean): Promise<NodeData | null>;
|
|
23
28
|
searchNodes(keywords: string[], limit?: number): Promise<SearchResult[]>;
|
|
24
29
|
findPath(startId: string, endId: string): Promise<PathResult | null>;
|
|
25
|
-
findOrphans(): Promise<Array<{
|
|
26
|
-
id: string;
|
|
27
|
-
title: string;
|
|
28
|
-
type: string;
|
|
29
|
-
status: string;
|
|
30
|
-
}>>;
|
|
31
30
|
getRelations(nodeId: string): Promise<Array<{
|
|
32
31
|
nodeId: string;
|
|
33
32
|
title: string;
|
|
@@ -52,7 +51,7 @@ export declare class PpdocsApiClient {
|
|
|
52
51
|
completeTask(taskId: string, experience: TaskExperience): Promise<Task | null>;
|
|
53
52
|
}
|
|
54
53
|
export declare function initClient(apiUrl: string): void;
|
|
55
|
-
export declare function listNodes(_projectId: string): Promise<NodeData[]>;
|
|
54
|
+
export declare function listNodes(_projectId: string, filter?: ListNodesFilter): Promise<NodeData[]>;
|
|
56
55
|
export declare function getNode(_projectId: string, nodeId: string): Promise<NodeData | null>;
|
|
57
56
|
export declare function createNode(_projectId: string, node: Partial<NodeData>): Promise<NodeData>;
|
|
58
57
|
export declare function updateNode(_projectId: string, nodeId: string, updates: Partial<NodeData>): Promise<NodeData | null>;
|
|
@@ -65,12 +64,6 @@ export declare function deleteNode(_projectId: string, nodeId: string): Promise<
|
|
|
65
64
|
export declare function lockNode(_projectId: string, nodeId: string, locked: boolean): Promise<NodeData | null>;
|
|
66
65
|
export declare function searchNodes(_projectId: string, keywords: string[], limit?: number): Promise<SearchResult[]>;
|
|
67
66
|
export declare function findPath(_projectId: string, startId: string, endId: string): Promise<PathResult | null>;
|
|
68
|
-
export declare function findOrphans(_projectId: string): Promise<Array<{
|
|
69
|
-
id: string;
|
|
70
|
-
title: string;
|
|
71
|
-
type: string;
|
|
72
|
-
status: string;
|
|
73
|
-
}>>;
|
|
74
67
|
export declare function getRelations(_projectId: string, nodeId: string): Promise<Array<{
|
|
75
68
|
nodeId: string;
|
|
76
69
|
title: string;
|
|
@@ -99,8 +99,34 @@ export class PpdocsApiClient {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
// ============ 节点操作 ============
|
|
102
|
-
async listNodes() {
|
|
103
|
-
|
|
102
|
+
async listNodes(filter) {
|
|
103
|
+
const nodes = await this.request('/nodes');
|
|
104
|
+
if (!filter)
|
|
105
|
+
return nodes;
|
|
106
|
+
// 计算每个节点的连接数 (入边+出边)
|
|
107
|
+
const edgeCounts = new Map();
|
|
108
|
+
nodes.forEach(n => edgeCounts.set(n.id, 0));
|
|
109
|
+
nodes.forEach(n => {
|
|
110
|
+
(n.dependencies || []).forEach(dep => {
|
|
111
|
+
// 出边: 当前节点依赖别人
|
|
112
|
+
edgeCounts.set(n.id, (edgeCounts.get(n.id) || 0) + 1);
|
|
113
|
+
// 入边: 被依赖的节点 (按 signature 匹配)
|
|
114
|
+
const target = nodes.find(t => t.signature?.toLowerCase() === dep.name.toLowerCase());
|
|
115
|
+
if (target)
|
|
116
|
+
edgeCounts.set(target.id, (edgeCounts.get(target.id) || 0) + 1);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
// 过滤
|
|
120
|
+
return nodes.filter(n => {
|
|
121
|
+
if (filter.status && n.status !== filter.status)
|
|
122
|
+
return false;
|
|
123
|
+
const edges = edgeCounts.get(n.id) || 0;
|
|
124
|
+
if (filter.minEdges !== undefined && edges < filter.minEdges)
|
|
125
|
+
return false;
|
|
126
|
+
if (filter.maxEdges !== undefined && edges > filter.maxEdges)
|
|
127
|
+
return false;
|
|
128
|
+
return true;
|
|
129
|
+
});
|
|
104
130
|
}
|
|
105
131
|
async getNode(nodeId) {
|
|
106
132
|
try {
|
|
@@ -228,9 +254,6 @@ export class PpdocsApiClient {
|
|
|
228
254
|
return null;
|
|
229
255
|
}
|
|
230
256
|
}
|
|
231
|
-
async findOrphans() {
|
|
232
|
-
return this.request('/orphans');
|
|
233
|
-
}
|
|
234
257
|
async getRelations(nodeId) {
|
|
235
258
|
const result = await this.request(`/relations/${nodeId}`);
|
|
236
259
|
// 转换字段名 (API 返回 type,前端期望 edgeType)
|
|
@@ -323,8 +346,8 @@ function getClient() {
|
|
|
323
346
|
return client;
|
|
324
347
|
}
|
|
325
348
|
// 导出与 fileStorage 相同的函数签名
|
|
326
|
-
export async function listNodes(_projectId) {
|
|
327
|
-
return getClient().listNodes();
|
|
349
|
+
export async function listNodes(_projectId, filter) {
|
|
350
|
+
return getClient().listNodes(filter);
|
|
328
351
|
}
|
|
329
352
|
export async function getNode(_projectId, nodeId) {
|
|
330
353
|
return getClient().getNode(nodeId);
|
|
@@ -350,9 +373,6 @@ export async function searchNodes(_projectId, keywords, limit) {
|
|
|
350
373
|
export async function findPath(_projectId, startId, endId) {
|
|
351
374
|
return getClient().findPath(startId, endId);
|
|
352
375
|
}
|
|
353
|
-
export async function findOrphans(_projectId) {
|
|
354
|
-
return getClient().findOrphans();
|
|
355
|
-
}
|
|
356
376
|
export async function getRelations(_projectId, nodeId) {
|
|
357
377
|
return getClient().getRelations(nodeId);
|
|
358
378
|
}
|
package/dist/tools/index.js
CHANGED
|
@@ -120,21 +120,20 @@ export function registerTools(server, projectId, _user) {
|
|
|
120
120
|
};
|
|
121
121
|
return wrap(projectId, JSON.stringify(output, null, 2));
|
|
122
122
|
});
|
|
123
|
-
// 7.
|
|
124
|
-
server.tool('kg_list_nodes', '
|
|
125
|
-
|
|
123
|
+
// 7. 列出节点 (支持过滤)
|
|
124
|
+
server.tool('kg_list_nodes', '列出节点,支持按状态/连接数过滤。maxEdges=0 可查孤立节点', {
|
|
125
|
+
status: z.enum(['incomplete', 'complete', 'fixing', 'refactoring', 'deprecated']).optional().describe('状态过滤'),
|
|
126
|
+
minEdges: z.number().optional().describe('最小连接数(含)'),
|
|
127
|
+
maxEdges: z.number().optional().describe('最大连接数(含),0=孤立节点')
|
|
128
|
+
}, async (args) => {
|
|
129
|
+
const filter = (args.status || args.minEdges !== undefined || args.maxEdges !== undefined)
|
|
130
|
+
? { status: args.status, minEdges: args.minEdges, maxEdges: args.maxEdges }
|
|
131
|
+
: undefined;
|
|
132
|
+
const nodes = await storage.listNodes(projectId, filter);
|
|
126
133
|
const output = nodes.map(n => ({ id: n.id, title: n.title, type: n.type, status: n.status, locked: n.locked }));
|
|
127
134
|
return wrap(projectId, JSON.stringify(output, null, 2));
|
|
128
135
|
});
|
|
129
|
-
// 8.
|
|
130
|
-
server.tool('kg_find_orphans', '查找无连线的孤立节点(用于清理)', {}, async () => {
|
|
131
|
-
const orphans = await storage.findOrphans(projectId);
|
|
132
|
-
if (orphans.length === 0) {
|
|
133
|
-
return wrap(projectId, '没有孤立节点');
|
|
134
|
-
}
|
|
135
|
-
return wrap(projectId, JSON.stringify(orphans, null, 2));
|
|
136
|
-
});
|
|
137
|
-
// 9. 查询节点关系网 (支持多层)
|
|
136
|
+
// 8. 查询节点关系网 (支持多层)
|
|
138
137
|
server.tool('kg_get_relations', '获取节点的上下游关系(谁依赖它/它依赖谁),支持多层查询', {
|
|
139
138
|
nodeId: z.string().describe('节点ID'),
|
|
140
139
|
depth: z.number().min(1).max(3).optional().describe('查询层数(默认1,最大3)')
|