@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/init.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* kg_init 工具 — 项目上下文初始化
|
|
3
|
-
*
|
|
4
|
-
* 解决全局共用 MCP 进程时的多项目隔离问题:
|
|
5
|
-
* - 智能体调用 kg_init 传入 projectPath
|
|
6
|
-
* - MCP 从该路径读取 .ppdocs 配置
|
|
7
|
-
* - 重新初始化 HTTP Client 指向正确项目
|
|
8
|
-
* - 更新共享 McpContext,所有工具自动获取新 projectId
|
|
9
|
-
*/
|
|
10
|
-
import * as fs from 'fs';
|
|
11
|
-
import * as path from 'path';
|
|
12
|
-
import { z } from 'zod';
|
|
13
|
-
import { initClient } from '../storage/httpClient.js';
|
|
14
|
-
import { generateAgentId } from '../config.js';
|
|
15
|
-
import { wrap, safeTool } from './shared.js';
|
|
16
|
-
// 当前会话的 API URL (用于幂等检查)
|
|
17
|
-
let currentApiUrl = null;
|
|
18
|
-
function findPpdocsPathFrom(dir) {
|
|
19
|
-
let currentDir = path.resolve(dir);
|
|
20
|
-
while (true) {
|
|
21
|
-
const configPath = path.join(currentDir, '.ppdocs');
|
|
22
|
-
if (fs.existsSync(configPath) && fs.statSync(configPath).isFile()) {
|
|
23
|
-
return configPath;
|
|
24
|
-
}
|
|
25
|
-
const parentDir = path.dirname(currentDir);
|
|
26
|
-
if (parentDir === currentDir) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
currentDir = parentDir;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
/** 尝试从指定目录读取 .ppdocs 配置 */
|
|
33
|
-
function readPpdocsFrom(dir) {
|
|
34
|
-
const configPath = findPpdocsPathFrom(dir);
|
|
35
|
-
if (!configPath)
|
|
36
|
-
return null;
|
|
37
|
-
try {
|
|
38
|
-
const c = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
39
|
-
if (!c.api || !c.projectId || !c.key)
|
|
40
|
-
return null;
|
|
41
|
-
return {
|
|
42
|
-
apiUrl: `${c.api}/api/${c.projectId}/${c.key}`,
|
|
43
|
-
projectId: c.projectId,
|
|
44
|
-
user: c.user || 'mcp-agent',
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
export function registerInitTool(server, ctx, onProjectChange) {
|
|
52
|
-
server.tool('kg_init', '🔗 项目上下文初始化 — 读取指定目录的 .ppdocs 配置,切换 MCP 连接到对应项目。首次对话必须调用。不传 projectPath 则使用当前工作目录', {
|
|
53
|
-
projectPath: z.string().optional().describe('项目根目录的绝对路径(含 .ppdocs 文件),不传则使用 cwd'),
|
|
54
|
-
}, async ({ projectPath }) => safeTool(async () => {
|
|
55
|
-
const targetDir = projectPath || process.cwd();
|
|
56
|
-
// 1. 尝试从目标目录读取 .ppdocs
|
|
57
|
-
const config = readPpdocsFrom(targetDir);
|
|
58
|
-
if (!config) {
|
|
59
|
-
return wrap(`❌ 未在 ${targetDir} 找到 .ppdocs 配置文件\n\n` +
|
|
60
|
-
`请先在项目目录中运行:\n` +
|
|
61
|
-
` npx @ppdocs/mcp init -p <projectId> -k <key>\n\n` +
|
|
62
|
-
`或通过 WebUI 绑定项目:\n` +
|
|
63
|
-
` npx @ppdocs/mcp webui`);
|
|
64
|
-
}
|
|
65
|
-
// 2. 幂等检查
|
|
66
|
-
if (currentApiUrl === config.apiUrl) {
|
|
67
|
-
return wrap(`✅ 项目上下文已就绪 (无需切换)\n\n` +
|
|
68
|
-
`📂 项目: ${config.projectId}\n` +
|
|
69
|
-
`🔗 API: ${config.apiUrl.replace(/\/[^/]+$/, '/***')}\n` +
|
|
70
|
-
`👤 用户: ${config.user}`);
|
|
71
|
-
}
|
|
72
|
-
// 3. 重新初始化 HTTP Client
|
|
73
|
-
initClient(config.apiUrl);
|
|
74
|
-
currentApiUrl = config.apiUrl;
|
|
75
|
-
// 4. 更新共享上下文 — 所有工具立刻获得新 projectId
|
|
76
|
-
ctx.projectId = config.projectId;
|
|
77
|
-
ctx.user = config.user;
|
|
78
|
-
// agentId: 优先环境变量 > 保留当前值(进程启动时已生成)
|
|
79
|
-
ctx.agentId = process.env.PPDOCS_AGENT_ID || ctx.agentId || generateAgentId();
|
|
80
|
-
// 5. 通知主进程
|
|
81
|
-
onProjectChange(config.projectId, config.apiUrl);
|
|
82
|
-
return wrap(`✅ 项目上下文已初始化\n\n` +
|
|
83
|
-
`📂 项目: ${config.projectId}\n` +
|
|
84
|
-
`📁 路径: ${targetDir}\n` +
|
|
85
|
-
`🔗 API: ${config.apiUrl.replace(/\/[^/]+$/, '/***')}\n` +
|
|
86
|
-
`👤 用户: ${config.user}\n\n` +
|
|
87
|
-
`所有后续工具调用将自动连接到此项目。`);
|
|
88
|
-
}));
|
|
89
|
-
}
|
package/dist/tools/meeting.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 多AI协作会议工具
|
|
3
|
-
* kg_meeting: join | leave | post | claim | release | status
|
|
4
|
-
*/
|
|
5
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
|
-
import { type McpContext } from './shared.js';
|
|
7
|
-
export declare function registerMeetingTools(server: McpServer, ctx: McpContext): void;
|
package/dist/tools/meeting.js
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 多AI协作会议工具
|
|
3
|
-
* kg_meeting: join | leave | post | claim | release | status
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
import { getClient } from '../storage/httpClient.js';
|
|
7
|
-
import { wrap, safeTool } from './shared.js';
|
|
8
|
-
function formatStatus(status) {
|
|
9
|
-
const lines = [
|
|
10
|
-
`🏛️ 会议室: ${status.projectId}`,
|
|
11
|
-
``,
|
|
12
|
-
];
|
|
13
|
-
// 参与者
|
|
14
|
-
if (status.agents.length > 0) {
|
|
15
|
-
lines.push(`### 👥 参与者 (${status.agents.length})`);
|
|
16
|
-
for (const agent of status.agents) {
|
|
17
|
-
const ago = Math.floor((Date.now() / 1000 - agent.lastHeartbeat) / 60);
|
|
18
|
-
lines.push(` 🤖 ${agent.agentId} (${agent.agentType}) — 心跳 ${ago}分钟前`);
|
|
19
|
-
}
|
|
20
|
-
lines.push('');
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
lines.push('📭 会议室为空');
|
|
24
|
-
lines.push('');
|
|
25
|
-
}
|
|
26
|
-
// 文件锁
|
|
27
|
-
const locks = Object.entries(status.fileLocks);
|
|
28
|
-
if (locks.length > 0) {
|
|
29
|
-
lines.push(`### 🔒 文件锁定 (${locks.length})`);
|
|
30
|
-
for (const [file, owner] of locks) {
|
|
31
|
-
lines.push(` 📄 ${file} ← ${owner}`);
|
|
32
|
-
}
|
|
33
|
-
lines.push('');
|
|
34
|
-
}
|
|
35
|
-
// 最近消息
|
|
36
|
-
if (status.recentMessages.length > 0) {
|
|
37
|
-
lines.push(`### 💬 最近消息 (${status.recentMessages.length})`);
|
|
38
|
-
for (const msg of status.recentMessages.slice(-10)) {
|
|
39
|
-
const time = new Date(msg.timestamp * 1000).toLocaleTimeString();
|
|
40
|
-
const icon = msg.msgType === 'decision' ? '⚡' : msg.msgType === 'question' ? '❓' : '💬';
|
|
41
|
-
lines.push(` ${icon} [${time}] ${msg.agentId}: ${msg.content}`);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return lines.join('\n');
|
|
45
|
-
}
|
|
46
|
-
export function registerMeetingTools(server, ctx) {
|
|
47
|
-
const client = () => getClient();
|
|
48
|
-
server.tool('kg_meeting', '🏛️ 多AI协作会议 — 多个AI编辑器同时操作同一项目时,通过"会议室"共享状态、认领文件、发布决策。action: join(加入)|leave(离开)|post(发消息)|claim(认领文件)|release(释放文件)|status(查看状态)', {
|
|
49
|
-
action: z.enum(['join', 'leave', 'post', 'claim', 'release', 'status']).describe('操作类型'),
|
|
50
|
-
agentId: z.string().optional().describe('Agent标识(如"cursor-1", "claude-code-2")。join/leave/post/claim/release 必填'),
|
|
51
|
-
agentType: z.string().optional().describe('Agent类型(如"cursor", "claude", "kiro")。join 时使用'),
|
|
52
|
-
content: z.string().optional().describe('消息内容 (post 时必填)'),
|
|
53
|
-
msgType: z.enum(['status', 'decision', 'question']).optional().describe('消息类型 (post, 默认status)'),
|
|
54
|
-
filePath: z.string().optional().describe('文件路径 (claim/release 时必填)'),
|
|
55
|
-
}, async (args) => safeTool(async () => {
|
|
56
|
-
switch (args.action) {
|
|
57
|
-
case 'join': {
|
|
58
|
-
if (!args.agentId)
|
|
59
|
-
return wrap('❌ join 需要 agentId');
|
|
60
|
-
const status = await client().meetingJoin(args.agentId, args.agentType || 'unknown');
|
|
61
|
-
return wrap(`✅ ${args.agentId} 已加入会议\n\n${formatStatus(status)}`);
|
|
62
|
-
}
|
|
63
|
-
case 'leave': {
|
|
64
|
-
if (!args.agentId)
|
|
65
|
-
return wrap('❌ leave 需要 agentId');
|
|
66
|
-
const status = await client().meetingLeave(args.agentId);
|
|
67
|
-
return wrap(`✅ ${args.agentId} 已离开会议\n\n${formatStatus(status)}`);
|
|
68
|
-
}
|
|
69
|
-
case 'post': {
|
|
70
|
-
if (!args.agentId || !args.content)
|
|
71
|
-
return wrap('❌ post 需要 agentId 和 content');
|
|
72
|
-
const status = await client().meetingPost(args.agentId, args.content, args.msgType || 'status');
|
|
73
|
-
return wrap(`✅ 消息已发送\n\n${formatStatus(status)}`);
|
|
74
|
-
}
|
|
75
|
-
case 'claim': {
|
|
76
|
-
if (!args.agentId || !args.filePath)
|
|
77
|
-
return wrap('❌ claim 需要 agentId 和 filePath');
|
|
78
|
-
const status = await client().meetingClaim(args.agentId, args.filePath);
|
|
79
|
-
return wrap(`✅ ${args.agentId} 已认领 ${args.filePath}\n\n${formatStatus(status)}`);
|
|
80
|
-
}
|
|
81
|
-
case 'release': {
|
|
82
|
-
if (!args.agentId || !args.filePath)
|
|
83
|
-
return wrap('❌ release 需要 agentId 和 filePath');
|
|
84
|
-
const status = await client().meetingRelease(args.agentId, args.filePath);
|
|
85
|
-
return wrap(`✅ ${args.agentId} 已释放 ${args.filePath}\n\n${formatStatus(status)}`);
|
|
86
|
-
}
|
|
87
|
-
case 'status': {
|
|
88
|
-
const status = await client().meetingStatus();
|
|
89
|
-
if (!status)
|
|
90
|
-
return wrap('📭 该项目暂无活跃会议');
|
|
91
|
-
return wrap(formatStatus(status));
|
|
92
|
-
}
|
|
93
|
-
default:
|
|
94
|
-
return wrap(`❌ 未知 action: ${args.action}`);
|
|
95
|
-
}
|
|
96
|
-
}));
|
|
97
|
-
}
|
package/dist/tools/pitfalls.d.ts
DELETED
package/dist/tools/pitfalls.js
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 💡 kg_pitfall — 踩坑经验管理
|
|
3
|
-
* 记录项目中遇到的问题、根因和解决方案,避免重复踩坑
|
|
4
|
-
*/
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
import { getClient } from '../storage/httpClient.js';
|
|
7
|
-
import { decodeObjectStrings } from '../utils.js';
|
|
8
|
-
import { safeTool, wrap } from './shared.js';
|
|
9
|
-
const SEVERITY_ICONS = {
|
|
10
|
-
critical: '🔴',
|
|
11
|
-
warning: '🟡',
|
|
12
|
-
info: '🔵',
|
|
13
|
-
};
|
|
14
|
-
const STATUS_ICONS = {
|
|
15
|
-
open: '🔓',
|
|
16
|
-
resolved: '✅',
|
|
17
|
-
recurring: '🔄',
|
|
18
|
-
};
|
|
19
|
-
export function registerPitfallTools(server) {
|
|
20
|
-
const client = () => getClient();
|
|
21
|
-
server.tool('kg_pitfall', '💡 踩坑经验 — 记录和查询项目中的踩坑经验,避免重复踩坑。\n' +
|
|
22
|
-
'遇到问题时用 create 记录现象和根因,找到方案后用 resolve 标记解决。\n' +
|
|
23
|
-
'下次遇到类似问题先 search 查历史经验。\n' +
|
|
24
|
-
'actions: create|get|update|search|resolve|list|delete', {
|
|
25
|
-
action: z.enum(['create', 'get', 'update', 'search', 'resolve', 'list', 'delete'])
|
|
26
|
-
.describe('操作类型'),
|
|
27
|
-
id: z.string().optional().describe('踩坑经验 ID (get/update/resolve/delete)'),
|
|
28
|
-
title: z.string().optional().describe('标题 (create)'),
|
|
29
|
-
category: z.string().optional().describe('分类: protocol/concurrency/config/logic/ui/other (create/update/search)'),
|
|
30
|
-
symptom: z.string().optional().describe('现象描述 Markdown (create/update)'),
|
|
31
|
-
root_cause: z.string().optional().describe('根因分析 Markdown (create/update)'),
|
|
32
|
-
solution: z.string().optional().describe('解决方案 Markdown (create/update/resolve)'),
|
|
33
|
-
prevention: z.string().optional().describe('预防措施 (create/update)'),
|
|
34
|
-
severity: z.string().optional().describe('严重程度: critical/warning/info (create/update)'),
|
|
35
|
-
related_nodes: z.array(z.string()).optional().describe('关联流程图节点 ID 列表 (create/update)'),
|
|
36
|
-
source_task: z.string().optional().describe('来源任务 ID (create)'),
|
|
37
|
-
tags: z.array(z.string()).optional().describe('标签列表 (create/update)'),
|
|
38
|
-
query: z.string().optional().describe('搜索关键词 (search)'),
|
|
39
|
-
status: z.string().optional().describe('状态过滤: open/resolved/recurring (search/list)'),
|
|
40
|
-
}, async (args) => safeTool(async () => {
|
|
41
|
-
const decoded = decodeObjectStrings(args);
|
|
42
|
-
switch (decoded.action) {
|
|
43
|
-
case 'list': {
|
|
44
|
-
const pitfalls = decoded.status || decoded.category
|
|
45
|
-
? await client().searchPitfalls('', decoded.status, decoded.category)
|
|
46
|
-
: await client().listPitfalls();
|
|
47
|
-
if (pitfalls.length === 0)
|
|
48
|
-
return wrap('当前没有踩坑经验记录');
|
|
49
|
-
const lines = [
|
|
50
|
-
`💡 踩坑经验 (${pitfalls.length})`,
|
|
51
|
-
'',
|
|
52
|
-
'| 状态 | 严重 | 标题 | 分类 | 标签 | 更新 |',
|
|
53
|
-
'|:-----|:-----|:-----|:-----|:-----|:-----|',
|
|
54
|
-
];
|
|
55
|
-
for (const p of pitfalls) {
|
|
56
|
-
const statusIcon = STATUS_ICONS[p.status] || p.status;
|
|
57
|
-
const sevIcon = SEVERITY_ICONS[p.severity] || p.severity;
|
|
58
|
-
const tags = p.tags.length > 0 ? p.tags.join(',') : '-';
|
|
59
|
-
const date = p.updated_at.substring(0, 10);
|
|
60
|
-
lines.push(`| ${statusIcon} | ${sevIcon} | ${p.title} | ${p.category} | ${tags} | ${date} |`);
|
|
61
|
-
}
|
|
62
|
-
return wrap(lines.join('\n'));
|
|
63
|
-
}
|
|
64
|
-
case 'get': {
|
|
65
|
-
if (!decoded.id)
|
|
66
|
-
return wrap('❌ get 需要 id');
|
|
67
|
-
const p = await client().getPitfall(decoded.id);
|
|
68
|
-
if (!p)
|
|
69
|
-
return wrap(`未找到踩坑经验: ${decoded.id}`);
|
|
70
|
-
const sevIcon = SEVERITY_ICONS[p.severity] || p.severity;
|
|
71
|
-
const statusIcon = STATUS_ICONS[p.status] || p.status;
|
|
72
|
-
const lines = [
|
|
73
|
-
`# ${sevIcon} ${p.title}`,
|
|
74
|
-
'',
|
|
75
|
-
`| 字段 | 值 |`,
|
|
76
|
-
`|:---|:---|`,
|
|
77
|
-
`| ID | ${p.id} |`,
|
|
78
|
-
`| 状态 | ${statusIcon} ${p.status} |`,
|
|
79
|
-
`| 严重程度 | ${sevIcon} ${p.severity} |`,
|
|
80
|
-
`| 分类 | ${p.category} |`,
|
|
81
|
-
`| 标签 | ${p.tags.join(', ') || '-'} |`,
|
|
82
|
-
`| 关联节点 | ${p.related_nodes.join(', ') || '-'} |`,
|
|
83
|
-
];
|
|
84
|
-
if (p.source_task)
|
|
85
|
-
lines.push(`| 来源任务 | ${p.source_task} |`);
|
|
86
|
-
lines.push(`| 创建 | ${p.created_at.substring(0, 19)} |`);
|
|
87
|
-
lines.push(`| 更新 | ${p.updated_at.substring(0, 19)} |`);
|
|
88
|
-
if (p.resolved_at)
|
|
89
|
-
lines.push(`| 解决 | ${p.resolved_at.substring(0, 19)} |`);
|
|
90
|
-
if (p.symptom) {
|
|
91
|
-
lines.push('', '## 现象', '', p.symptom);
|
|
92
|
-
}
|
|
93
|
-
if (p.root_cause) {
|
|
94
|
-
lines.push('', '## 根因', '', p.root_cause);
|
|
95
|
-
}
|
|
96
|
-
if (p.solution) {
|
|
97
|
-
lines.push('', '## 解决方案', '', p.solution);
|
|
98
|
-
}
|
|
99
|
-
if (p.prevention) {
|
|
100
|
-
lines.push('', '## 预防措施', '', p.prevention);
|
|
101
|
-
}
|
|
102
|
-
return wrap(lines.join('\n'));
|
|
103
|
-
}
|
|
104
|
-
case 'create': {
|
|
105
|
-
if (!decoded.title)
|
|
106
|
-
return wrap('❌ create 需要 title');
|
|
107
|
-
const pitfall = await client().createPitfall({
|
|
108
|
-
title: decoded.title,
|
|
109
|
-
category: decoded.category,
|
|
110
|
-
symptom: decoded.symptom,
|
|
111
|
-
root_cause: decoded.root_cause,
|
|
112
|
-
solution: decoded.solution,
|
|
113
|
-
prevention: decoded.prevention,
|
|
114
|
-
severity: decoded.severity,
|
|
115
|
-
related_nodes: decoded.related_nodes,
|
|
116
|
-
source_task: decoded.source_task,
|
|
117
|
-
tags: decoded.tags,
|
|
118
|
-
});
|
|
119
|
-
return wrap(`✅ 踩坑经验已创建\n\n- ID: ${pitfall.id}\n- 标题: ${pitfall.title}\n- 分类: ${pitfall.category}\n- 严重程度: ${pitfall.severity}`);
|
|
120
|
-
}
|
|
121
|
-
case 'update': {
|
|
122
|
-
if (!decoded.id)
|
|
123
|
-
return wrap('❌ update 需要 id');
|
|
124
|
-
const existing = await client().getPitfall(decoded.id);
|
|
125
|
-
if (!existing)
|
|
126
|
-
return wrap(`❌ 未找到踩坑经验: ${decoded.id}`);
|
|
127
|
-
const updated = {
|
|
128
|
-
...existing,
|
|
129
|
-
...(decoded.title && { title: decoded.title }),
|
|
130
|
-
...(decoded.category && { category: decoded.category }),
|
|
131
|
-
...(decoded.symptom && { symptom: decoded.symptom }),
|
|
132
|
-
...(decoded.root_cause && { root_cause: decoded.root_cause }),
|
|
133
|
-
...(decoded.solution && { solution: decoded.solution }),
|
|
134
|
-
...(decoded.prevention && { prevention: decoded.prevention }),
|
|
135
|
-
...(decoded.severity && { severity: decoded.severity }),
|
|
136
|
-
...(decoded.related_nodes && { related_nodes: decoded.related_nodes }),
|
|
137
|
-
...(decoded.tags && { tags: decoded.tags }),
|
|
138
|
-
updated_at: new Date().toISOString(),
|
|
139
|
-
};
|
|
140
|
-
await client().savePitfall(updated);
|
|
141
|
-
return wrap(`✅ 踩坑经验已更新 (${decoded.id})`);
|
|
142
|
-
}
|
|
143
|
-
case 'resolve': {
|
|
144
|
-
if (!decoded.id)
|
|
145
|
-
return wrap('❌ resolve 需要 id');
|
|
146
|
-
const existing = await client().getPitfall(decoded.id);
|
|
147
|
-
if (!existing)
|
|
148
|
-
return wrap(`❌ 未找到踩坑经验: ${decoded.id}`);
|
|
149
|
-
const now = new Date().toISOString();
|
|
150
|
-
const resolved = {
|
|
151
|
-
...existing,
|
|
152
|
-
status: 'resolved',
|
|
153
|
-
resolved_at: now,
|
|
154
|
-
updated_at: now,
|
|
155
|
-
...(decoded.solution && { solution: decoded.solution }),
|
|
156
|
-
...(decoded.prevention && { prevention: decoded.prevention }),
|
|
157
|
-
};
|
|
158
|
-
await client().savePitfall(resolved);
|
|
159
|
-
return wrap(`✅ 踩坑经验已标记为解决 (${decoded.id})`);
|
|
160
|
-
}
|
|
161
|
-
case 'search': {
|
|
162
|
-
const query = decoded.query || '';
|
|
163
|
-
const results = await client().searchPitfalls(query, decoded.status, decoded.category);
|
|
164
|
-
if (results.length === 0)
|
|
165
|
-
return wrap(`未找到匹配的踩坑经验 (搜索: "${query}")`);
|
|
166
|
-
const lines = [
|
|
167
|
-
`💡 搜索结果: "${query}" (${results.length} 条)`,
|
|
168
|
-
'',
|
|
169
|
-
'| 状态 | 严重 | 标题 | 分类 | ID |',
|
|
170
|
-
'|:-----|:-----|:-----|:-----|:---|',
|
|
171
|
-
];
|
|
172
|
-
for (const p of results) {
|
|
173
|
-
const statusIcon = STATUS_ICONS[p.status] || p.status;
|
|
174
|
-
const sevIcon = SEVERITY_ICONS[p.severity] || p.severity;
|
|
175
|
-
lines.push(`| ${statusIcon} | ${sevIcon} | ${p.title} | ${p.category} | ${p.id} |`);
|
|
176
|
-
}
|
|
177
|
-
lines.push('', '> 用 `kg_pitfall(get, id:"xxx")` 查看详情');
|
|
178
|
-
return wrap(lines.join('\n'));
|
|
179
|
-
}
|
|
180
|
-
case 'delete': {
|
|
181
|
-
if (!decoded.id)
|
|
182
|
-
return wrap('❌ delete 需要 id');
|
|
183
|
-
const ok = await client().deletePitfall(decoded.id);
|
|
184
|
-
return wrap(ok ? `✅ 踩坑经验已删除 (${decoded.id})` : `ℹ️ 未找到踩坑经验 (${decoded.id})`);
|
|
185
|
-
}
|
|
186
|
-
default:
|
|
187
|
-
return wrap(`❌ 未知 action: ${decoded.action}`);
|
|
188
|
-
}
|
|
189
|
-
}));
|
|
190
|
-
}
|
package/dist/tools/projects.d.ts
DELETED
package/dist/tools/projects.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 📋 kg_projects
|
|
3
|
-
* 列出所有可访问的项目(返回名称和ID)
|
|
4
|
-
*/
|
|
5
|
-
import { getClient } from '../storage/httpClient.js';
|
|
6
|
-
import { wrap, safeTool } from './shared.js';
|
|
7
|
-
export function registerProjectTools(server, ctx) {
|
|
8
|
-
const client = () => getClient();
|
|
9
|
-
server.tool('kg_projects', '📋 列出所有可访问的项目(返回名称和ID)。跨项目操作的第一步: 获取项目ID后用于其他工具的 targetProject 参数', {}, async () => safeTool(async () => {
|
|
10
|
-
const projects = await client().crossListProjects();
|
|
11
|
-
if (projects.length === 0)
|
|
12
|
-
return wrap('暂无可访问的项目');
|
|
13
|
-
const lines = projects.map(p => {
|
|
14
|
-
const isCurrent = p.id === ctx.projectId ? ' ★当前' : '';
|
|
15
|
-
return `- ${p.name} (${p.id})${isCurrent}`;
|
|
16
|
-
});
|
|
17
|
-
return wrap(`可访问的项目 (${projects.length} 个):\n\n${lines.join('\n')}`);
|
|
18
|
-
}));
|
|
19
|
-
}
|