@ppdocs/mcp 3.1.1 → 3.1.3
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 +6 -2
- package/dist/cli.js +189 -19
- package/dist/storage/httpClient.d.ts +13 -0
- package/dist/storage/httpClient.js +20 -0
- package/dist/tools/index.js +15 -15
- package/package.json +1 -1
- package/templates/README.md +13 -7
- package/templates/cursorrules.md +102 -0
- package/templates/kiro-rules/ppdocs.md +102 -0
package/README.md
CHANGED
|
@@ -47,8 +47,12 @@ npx @ppdocs/mcp init -p <项目ID> -k <密钥>
|
|
|
47
47
|
|
|
48
48
|
这会自动:
|
|
49
49
|
- 创建 `.ppdocs` 配置文件
|
|
50
|
-
-
|
|
51
|
-
-
|
|
50
|
+
- 检测工作区 IDE 配置目录,自动安装对应模板:
|
|
51
|
+
- `.claude/` → Claude Code 工作流模板 + MCP 权限
|
|
52
|
+
- `.cursor/` → Cursor MCP 配置 + .cursorrules
|
|
53
|
+
- `.gemini/` → Antigravity MCP 配置 + AGENTS.md
|
|
54
|
+
- `.kiro/` → Kiro MCP 配置 + Agent 规则
|
|
55
|
+
- 自动检测并注册 CLI MCP (claude/codex/gemini)
|
|
52
56
|
|
|
53
57
|
### 3. 配置 Claude Desktop
|
|
54
58
|
|
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import * as path from 'path';
|
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import { execSync } from 'child_process';
|
|
9
9
|
import { generateUser } from './config.js';
|
|
10
|
+
import { PpdocsApiClient } from './storage/httpClient.js';
|
|
10
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
12
|
const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
|
|
12
13
|
function parseArgs(args) {
|
|
@@ -62,6 +63,20 @@ function parseArgs(args) {
|
|
|
62
63
|
else if (arg === '--codex') {
|
|
63
64
|
opts.codex = true;
|
|
64
65
|
}
|
|
66
|
+
else if (arg === '--from') {
|
|
67
|
+
if (i + 1 >= args.length || args[i + 1].startsWith('-')) {
|
|
68
|
+
console.error('Error: --from requires a project ID');
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
opts.from = args[++i];
|
|
72
|
+
}
|
|
73
|
+
else if (arg === '--sync') {
|
|
74
|
+
if (i + 1 >= args.length || args[i + 1].startsWith('-')) {
|
|
75
|
+
console.error('Error: --sync requires a project ID');
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
opts.sync = args[++i];
|
|
79
|
+
}
|
|
65
80
|
}
|
|
66
81
|
if (!opts.project || !opts.key)
|
|
67
82
|
return null;
|
|
@@ -86,10 +101,14 @@ Options:
|
|
|
86
101
|
--port API port (default: 20001)
|
|
87
102
|
--api API host (default: localhost)
|
|
88
103
|
--codex Codex mode: generate AGENTS.md instead of .claude/
|
|
104
|
+
--from <id> Pull IDE configs from a template project
|
|
105
|
+
--sync <id> Sync knowledge base docs from a source project to ./docs/ppdocs/
|
|
89
106
|
|
|
90
107
|
Example:
|
|
91
108
|
npx @ppdocs/mcp init -p myproject -k abc123xyz
|
|
92
109
|
npx @ppdocs/mcp init -p myproject -k abc123xyz --codex
|
|
110
|
+
npx @ppdocs/mcp init -p myproject -k abc123xyz --from _templates
|
|
111
|
+
npx @ppdocs/mcp init -p myproject -k abc123xyz --sync shared-rules
|
|
93
112
|
`);
|
|
94
113
|
}
|
|
95
114
|
export function runCli(args) {
|
|
@@ -101,7 +120,10 @@ export function runCli(args) {
|
|
|
101
120
|
showHelp();
|
|
102
121
|
process.exit(1);
|
|
103
122
|
}
|
|
104
|
-
initProject(opts)
|
|
123
|
+
initProject(opts).catch(e => {
|
|
124
|
+
console.error(`❌ Init failed: ${e}`);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
});
|
|
105
127
|
return true;
|
|
106
128
|
}
|
|
107
129
|
if (cmd === '--help' || cmd === '-h' || cmd === 'help') {
|
|
@@ -110,7 +132,7 @@ export function runCli(args) {
|
|
|
110
132
|
}
|
|
111
133
|
return false; // Not a CLI command, run as MCP server
|
|
112
134
|
}
|
|
113
|
-
function initProject(opts) {
|
|
135
|
+
async function initProject(opts) {
|
|
114
136
|
const cwd = process.cwd();
|
|
115
137
|
const apiUrl = `http://${opts.api}:${opts.port}/api/${opts.project}/${opts.key}`;
|
|
116
138
|
// Create .ppdocs config
|
|
@@ -131,19 +153,48 @@ function initProject(opts) {
|
|
|
131
153
|
}
|
|
132
154
|
fs.writeFileSync(ppdocsPath, JSON.stringify(ppdocsConfig, null, 2));
|
|
133
155
|
console.log(`✅ Created ${ppdocsPath}`);
|
|
134
|
-
// Install workflow templates
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
else {
|
|
156
|
+
// Install workflow templates based on IDE detection
|
|
157
|
+
const detectedIdes = detectIDEs(cwd);
|
|
158
|
+
let hasIdeDir = false;
|
|
159
|
+
if (detectedIdes.includes('claude')) {
|
|
139
160
|
installClaudeTemplates(cwd);
|
|
161
|
+
hasIdeDir = true;
|
|
162
|
+
}
|
|
163
|
+
if (detectedIdes.includes('cursor')) {
|
|
164
|
+
installCursorTemplates(cwd);
|
|
165
|
+
hasIdeDir = true;
|
|
166
|
+
}
|
|
167
|
+
if (detectedIdes.includes('antigravity')) {
|
|
168
|
+
installAntigravityTemplates(cwd);
|
|
169
|
+
hasIdeDir = true;
|
|
170
|
+
}
|
|
171
|
+
if (detectedIdes.includes('kiro')) {
|
|
172
|
+
installKiroTemplates(cwd);
|
|
173
|
+
hasIdeDir = true;
|
|
174
|
+
}
|
|
175
|
+
// Fallback behavior: if no specific IDE config directories found
|
|
176
|
+
if (!hasIdeDir) {
|
|
177
|
+
if (opts.codex) {
|
|
178
|
+
installCodexTemplates(cwd);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
installClaudeTemplates(cwd); // Default to Claude templates
|
|
182
|
+
}
|
|
140
183
|
}
|
|
141
184
|
// 自动检测并注册 MCP
|
|
142
185
|
const registered = autoRegisterMcp(apiUrl, opts.user);
|
|
143
|
-
// 如果没有检测到任何 AI CLI
|
|
144
|
-
if (!registered) {
|
|
186
|
+
// 如果没有检测到任何 AI CLI,并且也没有检测到配置文件夹,创建 .mcp.json 作为备用
|
|
187
|
+
if (!registered && !hasIdeDir) {
|
|
145
188
|
createMcpJson(cwd);
|
|
146
189
|
}
|
|
190
|
+
// --from: 从模板项目拉取 IDE 配置文件
|
|
191
|
+
if (opts.from) {
|
|
192
|
+
await pullTemplateProject(cwd, apiUrl, opts.from);
|
|
193
|
+
}
|
|
194
|
+
// --sync: 从源项目同步知识库文档到本地
|
|
195
|
+
if (opts.sync) {
|
|
196
|
+
await syncKnowledgeBase(cwd, apiUrl, opts.sync);
|
|
197
|
+
}
|
|
147
198
|
console.log(`
|
|
148
199
|
🎉 Done! ppdocs MCP configured for project: ${opts.project}
|
|
149
200
|
User: ${opts.user}
|
|
@@ -151,6 +202,65 @@ function initProject(opts) {
|
|
|
151
202
|
Restart your AI tool to use ppdocs knowledge graph.
|
|
152
203
|
`);
|
|
153
204
|
}
|
|
205
|
+
/** 从模板项目拉取 IDE 配置到当前项目目录 */
|
|
206
|
+
async function pullTemplateProject(cwd, apiUrl, templateId) {
|
|
207
|
+
console.log(`📦 Pulling IDE configs from template project: ${templateId}...`);
|
|
208
|
+
try {
|
|
209
|
+
const client = new PpdocsApiClient(apiUrl);
|
|
210
|
+
const files = await client.crossListFiles(templateId);
|
|
211
|
+
if (files.length === 0) {
|
|
212
|
+
console.log(`⚠️ Template project "${templateId}" has no files (upload files via the desktop app first)`);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const result = await client.crossDownload(templateId, '/', cwd);
|
|
216
|
+
console.log(`✅ Pulled ${result.fileCount} files from template project "${templateId}"`);
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
console.log(`⚠️ Failed to pull from template project: ${e}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/** 从源项目同步知识库文档到本地 ./docs/ppdocs/ */
|
|
223
|
+
async function syncKnowledgeBase(cwd, apiUrl, sourceId) {
|
|
224
|
+
console.log(`📚 Syncing knowledge base from project: ${sourceId}...`);
|
|
225
|
+
try {
|
|
226
|
+
const client = new PpdocsApiClient(apiUrl);
|
|
227
|
+
const docs = await client.crossListDocs(sourceId);
|
|
228
|
+
const docNodes = docs.filter(d => !d.isDir);
|
|
229
|
+
if (docNodes.length === 0) {
|
|
230
|
+
console.log(`⚠️ Project "${sourceId}" has no knowledge base documents`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const docsDir = path.join(cwd, 'docs', 'ppdocs', sourceId);
|
|
234
|
+
fs.mkdirSync(docsDir, { recursive: true });
|
|
235
|
+
let synced = 0;
|
|
236
|
+
for (const node of docNodes) {
|
|
237
|
+
const doc = await client.crossGetDoc(sourceId, node.path);
|
|
238
|
+
if (!doc)
|
|
239
|
+
continue;
|
|
240
|
+
// 将文档路径转为本地文件路径 (e.g. "/前端/组件/Modal" → "前端/组件/Modal.md")
|
|
241
|
+
const safePath = node.path.replace(/^\//, '').replace(/[<>:"|?*]/g, '_');
|
|
242
|
+
const localFile = path.join(docsDir, `${safePath}.md`);
|
|
243
|
+
const localDir = path.dirname(localFile);
|
|
244
|
+
if (!fs.existsSync(localDir)) {
|
|
245
|
+
fs.mkdirSync(localDir, { recursive: true });
|
|
246
|
+
}
|
|
247
|
+
// 生成 Markdown 文件内容
|
|
248
|
+
const content = [
|
|
249
|
+
`# ${node.name}`,
|
|
250
|
+
'',
|
|
251
|
+
doc.summary ? `> ${doc.summary}` : '',
|
|
252
|
+
'',
|
|
253
|
+
doc.content || '',
|
|
254
|
+
].filter(Boolean).join('\n');
|
|
255
|
+
fs.writeFileSync(localFile, content, 'utf-8');
|
|
256
|
+
synced++;
|
|
257
|
+
}
|
|
258
|
+
console.log(`✅ Synced ${synced} documents from "${sourceId}" to docs/ppdocs/${sourceId}/`);
|
|
259
|
+
}
|
|
260
|
+
catch (e) {
|
|
261
|
+
console.log(`⚠️ Failed to sync knowledge base: ${e}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
154
264
|
/** 检测命令是否存在 */
|
|
155
265
|
function commandExists(cmd) {
|
|
156
266
|
try {
|
|
@@ -233,9 +343,8 @@ function autoRegisterMcp(apiUrl, user) {
|
|
|
233
343
|
console.log(`✅ Registered MCP for: ${detected.join(', ')}`);
|
|
234
344
|
return true;
|
|
235
345
|
}
|
|
236
|
-
/**
|
|
237
|
-
function
|
|
238
|
-
const mcpPath = path.join(cwd, '.mcp.json');
|
|
346
|
+
/** 在指定路径创建或更新 mcp.json 配置 */
|
|
347
|
+
function createMcpConfigAt(mcpPath, autoCreatedTemplate) {
|
|
239
348
|
let mcpConfig = {};
|
|
240
349
|
if (fs.existsSync(mcpPath)) {
|
|
241
350
|
try {
|
|
@@ -248,14 +357,23 @@ function createMcpJson(cwd) {
|
|
|
248
357
|
// Windows 需要 cmd /c 包装才能执行 npx
|
|
249
358
|
const isWindows = process.platform === 'win32';
|
|
250
359
|
const ppdocsServer = isWindows
|
|
251
|
-
? { command: 'cmd', args: ['/c', 'npx', '@ppdocs/mcp@latest'] }
|
|
252
|
-
: { command: 'npx', args: ['@ppdocs/mcp@latest'] };
|
|
360
|
+
? { command: 'cmd', args: ['/c', 'npx', '-y', '@ppdocs/mcp@latest'] }
|
|
361
|
+
: { command: 'npx', args: ['-y', '@ppdocs/mcp@latest'] };
|
|
253
362
|
mcpConfig.mcpServers = {
|
|
254
363
|
...(mcpConfig.mcpServers || {}),
|
|
255
|
-
ppdocs: ppdocsServer,
|
|
364
|
+
"ppdocs-kg": ppdocsServer,
|
|
256
365
|
};
|
|
366
|
+
// 确保父目录存在
|
|
367
|
+
const parentDir = path.dirname(mcpPath);
|
|
368
|
+
if (!fs.existsSync(parentDir)) {
|
|
369
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
370
|
+
}
|
|
257
371
|
fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2));
|
|
258
|
-
console.log(`✅
|
|
372
|
+
console.log(`✅ Configured MCP in ${mcpPath}`);
|
|
373
|
+
}
|
|
374
|
+
/** 创建 .mcp.json (手动配置备用) */
|
|
375
|
+
function createMcpJson(cwd) {
|
|
376
|
+
createMcpConfigAt(path.join(cwd, '.mcp.json'));
|
|
259
377
|
}
|
|
260
378
|
/** 递归复制目录 */
|
|
261
379
|
function copyDirRecursive(src, dest) {
|
|
@@ -363,13 +481,65 @@ function installClaudeTemplates(cwd) {
|
|
|
363
481
|
}
|
|
364
482
|
/** 安装 Codex 模板 (生成 AGENTS.md) */
|
|
365
483
|
function installCodexTemplates(cwd) {
|
|
484
|
+
generateAgentsMd(cwd);
|
|
485
|
+
}
|
|
486
|
+
/** 生成 AGENTS.md 工具函数 (去重) */
|
|
487
|
+
function generateAgentsMd(cwd) {
|
|
366
488
|
const srcPrompt = path.join(TEMPLATES_DIR, 'hooks', 'SystemPrompt.md');
|
|
367
489
|
const destAgents = path.join(cwd, 'AGENTS.md');
|
|
368
|
-
if (fs.existsSync(srcPrompt)) {
|
|
490
|
+
if (fs.existsSync(srcPrompt) && !fs.existsSync(destAgents)) {
|
|
369
491
|
fs.copyFileSync(srcPrompt, destAgents);
|
|
370
|
-
console.log(`✅ Created AGENTS.md
|
|
492
|
+
console.log(`✅ Created AGENTS.md`);
|
|
371
493
|
}
|
|
372
|
-
else {
|
|
494
|
+
else if (!fs.existsSync(srcPrompt)) {
|
|
373
495
|
console.log(`⚠️ SystemPrompt.md template not found`);
|
|
374
496
|
}
|
|
375
497
|
}
|
|
498
|
+
/** 安装 Cursor 模板 */
|
|
499
|
+
function installCursorTemplates(cwd) {
|
|
500
|
+
// 1. 生成 Cursor MCP 配置
|
|
501
|
+
createMcpConfigAt(path.join(cwd, '.cursor', 'mcp.json'));
|
|
502
|
+
// 2. 生成 .cursorrules 提示词
|
|
503
|
+
const srcRules = path.join(TEMPLATES_DIR, 'cursorrules.md');
|
|
504
|
+
const destRules = path.join(cwd, '.cursorrules');
|
|
505
|
+
if (fs.existsSync(srcRules) && !fs.existsSync(destRules)) {
|
|
506
|
+
fs.copyFileSync(srcRules, destRules);
|
|
507
|
+
console.log(`✅ Created .cursorrules`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
/** 安装 Antigravity (Gemini IDE) 模板 */
|
|
511
|
+
function installAntigravityTemplates(cwd) {
|
|
512
|
+
// 1. 填充 .gemini/settings.json
|
|
513
|
+
createMcpConfigAt(path.join(cwd, '.gemini', 'settings.json'));
|
|
514
|
+
// 2. 生成 AGENTS.md
|
|
515
|
+
generateAgentsMd(cwd);
|
|
516
|
+
}
|
|
517
|
+
/** 安装 Kiro 模板 */
|
|
518
|
+
function installKiroTemplates(cwd) {
|
|
519
|
+
// 1. 生成 Kiro MCP 配置
|
|
520
|
+
createMcpConfigAt(path.join(cwd, '.kiro', 'settings', 'mcp.json'));
|
|
521
|
+
// 2. 生成 Kiro Agent 规则
|
|
522
|
+
const kiroRulesDir = path.join(cwd, '.kiro', 'rules');
|
|
523
|
+
if (!fs.existsSync(kiroRulesDir)) {
|
|
524
|
+
fs.mkdirSync(kiroRulesDir, { recursive: true });
|
|
525
|
+
}
|
|
526
|
+
const srcRules = path.join(TEMPLATES_DIR, 'kiro-rules', 'ppdocs.md');
|
|
527
|
+
const destRules = path.join(kiroRulesDir, 'ppdocs.md');
|
|
528
|
+
if (fs.existsSync(srcRules) && !fs.existsSync(destRules)) {
|
|
529
|
+
fs.copyFileSync(srcRules, destRules);
|
|
530
|
+
console.log(`✅ Created .kiro/rules/ppdocs.md`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/** 检测当前工作区包含的 IDE */
|
|
534
|
+
function detectIDEs(cwd) {
|
|
535
|
+
const ides = [];
|
|
536
|
+
if (fs.existsSync(path.join(cwd, '.claude')))
|
|
537
|
+
ides.push('claude');
|
|
538
|
+
if (fs.existsSync(path.join(cwd, '.cursor')))
|
|
539
|
+
ides.push('cursor');
|
|
540
|
+
if (fs.existsSync(path.join(cwd, '.gemini')))
|
|
541
|
+
ides.push('antigravity');
|
|
542
|
+
if (fs.existsSync(path.join(cwd, '.kiro')))
|
|
543
|
+
ides.push('kiro');
|
|
544
|
+
return ides;
|
|
545
|
+
}
|
|
@@ -104,11 +104,24 @@ export declare function getDocsByStatus(_projectId: string, statusList: string[]
|
|
|
104
104
|
export declare function getTree(_projectId: string): Promise<TreeNode[]>;
|
|
105
105
|
export declare function getRules(_projectId: string, ruleType: string): Promise<string[]>;
|
|
106
106
|
export declare function saveRules(_projectId: string, ruleType: string, rules: string[]): Promise<boolean>;
|
|
107
|
+
export declare function appendRules(_projectId: string, ruleType: string, newRules: string[]): Promise<{
|
|
108
|
+
success: boolean;
|
|
109
|
+
total: number;
|
|
110
|
+
added: number;
|
|
111
|
+
}>;
|
|
107
112
|
export declare function getRulesMeta(): Promise<Record<string, RuleMeta>>;
|
|
108
113
|
export declare function saveRulesMeta(meta: Record<string, RuleMeta>): Promise<boolean>;
|
|
114
|
+
export declare function mergeRulesMeta(newMeta: Record<string, RuleMeta>): Promise<{
|
|
115
|
+
success: boolean;
|
|
116
|
+
total: number;
|
|
117
|
+
}>;
|
|
109
118
|
export declare function crossGetRulesMeta(target: string): Promise<Record<string, RuleMeta>>;
|
|
110
119
|
export declare function getGlobalRulesMeta(): Promise<Record<string, RuleMeta>>;
|
|
111
120
|
export declare function saveGlobalRulesMeta(meta: Record<string, RuleMeta>): Promise<boolean>;
|
|
121
|
+
export declare function mergeGlobalRulesMeta(newMeta: Record<string, RuleMeta>): Promise<{
|
|
122
|
+
success: boolean;
|
|
123
|
+
total: number;
|
|
124
|
+
}>;
|
|
112
125
|
export declare function listTasks(_projectId: string, status?: 'active' | 'archived'): Promise<TaskSummary[]>;
|
|
113
126
|
export declare function getTask(_projectId: string, taskId: string): Promise<Task | null>;
|
|
114
127
|
export declare function createTask(_projectId: string, task: {
|
|
@@ -508,12 +508,26 @@ export async function getRules(_projectId, ruleType) {
|
|
|
508
508
|
export async function saveRules(_projectId, ruleType, rules) {
|
|
509
509
|
return getClient().saveRulesApi(ruleType, rules);
|
|
510
510
|
}
|
|
511
|
+
export async function appendRules(_projectId, ruleType, newRules) {
|
|
512
|
+
const existing = await getClient().getRulesApi(ruleType);
|
|
513
|
+
const existingSet = new Set(existing.map(r => r.trim()));
|
|
514
|
+
const toAdd = newRules.filter(r => !existingSet.has(r.trim()));
|
|
515
|
+
const merged = [...existing, ...toAdd];
|
|
516
|
+
const success = await getClient().saveRulesApi(ruleType, merged);
|
|
517
|
+
return { success, total: merged.length, added: toAdd.length };
|
|
518
|
+
}
|
|
511
519
|
export async function getRulesMeta() {
|
|
512
520
|
return getClient().getRulesMeta();
|
|
513
521
|
}
|
|
514
522
|
export async function saveRulesMeta(meta) {
|
|
515
523
|
return getClient().saveRulesMeta(meta);
|
|
516
524
|
}
|
|
525
|
+
export async function mergeRulesMeta(newMeta) {
|
|
526
|
+
const existing = await getClient().getRulesMeta();
|
|
527
|
+
const merged = { ...existing, ...newMeta };
|
|
528
|
+
const success = await getClient().saveRulesMeta(merged);
|
|
529
|
+
return { success, total: Object.keys(merged).length };
|
|
530
|
+
}
|
|
517
531
|
export async function crossGetRulesMeta(target) {
|
|
518
532
|
return getClient().crossGetRulesMeta(target);
|
|
519
533
|
}
|
|
@@ -523,6 +537,12 @@ export async function getGlobalRulesMeta() {
|
|
|
523
537
|
export async function saveGlobalRulesMeta(meta) {
|
|
524
538
|
return getClient().saveGlobalRulesMeta(meta);
|
|
525
539
|
}
|
|
540
|
+
export async function mergeGlobalRulesMeta(newMeta) {
|
|
541
|
+
const existing = await getClient().getGlobalRulesMeta();
|
|
542
|
+
const merged = { ...existing, ...newMeta };
|
|
543
|
+
const success = await getClient().saveGlobalRulesMeta(merged);
|
|
544
|
+
return { success, total: Object.keys(merged).length };
|
|
545
|
+
}
|
|
526
546
|
// ============ 任务管理 ============
|
|
527
547
|
export async function listTasks(_projectId, status) {
|
|
528
548
|
return getClient().listTasks(status);
|
package/dist/tools/index.js
CHANGED
|
@@ -237,20 +237,20 @@ export function registerTools(server, projectId, _user) {
|
|
|
237
237
|
}
|
|
238
238
|
return { content: [{ type: 'text', text: rules }] };
|
|
239
239
|
});
|
|
240
|
-
// 3.7 保存项目规则
|
|
241
|
-
server.tool('kg_save_rules', '保存单个类型的项目规则(
|
|
240
|
+
// 3.7 保存项目规则 (自动合并,去重追加)
|
|
241
|
+
server.tool('kg_save_rules', '保存单个类型的项目规则(自动合并:已有规则保留,新规则去重追加)', {
|
|
242
242
|
ruleType: z.string()
|
|
243
243
|
.describe('规则类型(如 userStyles, codeStyle, reviewRules, testRules, unitTests, 或自定义类型)'),
|
|
244
244
|
rules: z.array(z.string()).describe('规则数组')
|
|
245
245
|
}, async (args) => {
|
|
246
246
|
const decoded = decodeObjectStrings(args);
|
|
247
|
-
const
|
|
248
|
-
if (!success) {
|
|
247
|
+
const result = await storage.appendRules(projectId, decoded.ruleType, decoded.rules);
|
|
248
|
+
if (!result.success) {
|
|
249
249
|
return wrap('❌ 保存失败');
|
|
250
250
|
}
|
|
251
251
|
const meta = await storage.getRulesMeta();
|
|
252
252
|
const label = meta[decoded.ruleType]?.label || decoded.ruleType;
|
|
253
|
-
return wrap(`✅ ${label}已保存 (
|
|
253
|
+
return wrap(`✅ ${label}已保存 (新增${result.added}条, 共${result.total}条)`);
|
|
254
254
|
});
|
|
255
255
|
// 3.8 获取规则触发配置
|
|
256
256
|
server.tool('kg_get_rules_meta', '获取规则触发配置(所有类型的标签、关键词、触发数)。用于查看/编辑 hooks 触发条件', {}, async () => {
|
|
@@ -270,8 +270,8 @@ export function registerTools(server, projectId, _user) {
|
|
|
270
270
|
return wrap(`❌ ${String(e)}`);
|
|
271
271
|
}
|
|
272
272
|
});
|
|
273
|
-
// 3.9 保存规则触发配置
|
|
274
|
-
server.tool('kg_save_rules_meta', '保存规则触发配置(
|
|
273
|
+
// 3.9 保存规则触发配置 (自动合并)
|
|
274
|
+
server.tool('kg_save_rules_meta', '保存规则触发配置(自动合并:已有类型更新,新类型创建,其他类型不受影响)', {
|
|
275
275
|
meta: z.record(z.string(), z.object({
|
|
276
276
|
label: z.string().describe('规则显示名称'),
|
|
277
277
|
keywords: z.array(z.string()).default([]).describe('触发关键词列表'),
|
|
@@ -281,11 +281,11 @@ export function registerTools(server, projectId, _user) {
|
|
|
281
281
|
}, async (args) => {
|
|
282
282
|
try {
|
|
283
283
|
const decoded = decodeObjectStrings(args.meta);
|
|
284
|
-
const
|
|
285
|
-
if (!success) {
|
|
284
|
+
const result = await storage.mergeRulesMeta(decoded);
|
|
285
|
+
if (!result.success) {
|
|
286
286
|
return wrap('❌ 保存失败');
|
|
287
287
|
}
|
|
288
|
-
return wrap(`✅
|
|
288
|
+
return wrap(`✅ 触发配置已保存 (更新${Object.keys(decoded).length}个类型, 共${result.total}个类型)`);
|
|
289
289
|
}
|
|
290
290
|
catch (e) {
|
|
291
291
|
return wrap(`❌ ${String(e)}`);
|
|
@@ -309,8 +309,8 @@ export function registerTools(server, projectId, _user) {
|
|
|
309
309
|
return wrap(`❌ ${String(e)}`);
|
|
310
310
|
}
|
|
311
311
|
});
|
|
312
|
-
// 3.11 保存全局默认规则触发配置
|
|
313
|
-
server.tool('kg_save_global_rules_meta', '
|
|
312
|
+
// 3.11 保存全局默认规则触发配置 (自动合并)
|
|
313
|
+
server.tool('kg_save_global_rules_meta', '保存全局默认触发配置(自动合并:已有类型更新,新类型创建,其他类型不受影响)', {
|
|
314
314
|
meta: z.record(z.string(), z.object({
|
|
315
315
|
label: z.string().describe('规则显示名称'),
|
|
316
316
|
keywords: z.array(z.string()).default([]).describe('触发关键词列表'),
|
|
@@ -320,11 +320,11 @@ export function registerTools(server, projectId, _user) {
|
|
|
320
320
|
}, async (args) => {
|
|
321
321
|
try {
|
|
322
322
|
const decoded = decodeObjectStrings(args.meta);
|
|
323
|
-
const
|
|
324
|
-
if (!success) {
|
|
323
|
+
const result = await storage.mergeGlobalRulesMeta(decoded);
|
|
324
|
+
if (!result.success) {
|
|
325
325
|
return wrap('❌ 保存失败');
|
|
326
326
|
}
|
|
327
|
-
return wrap(`✅
|
|
327
|
+
return wrap(`✅ 全局触发配置已保存 (更新${Object.keys(decoded).length}个类型, 共${result.total}个类型)`);
|
|
328
328
|
}
|
|
329
329
|
catch (e) {
|
|
330
330
|
return wrap(`❌ ${String(e)}`);
|
package/package.json
CHANGED
package/templates/README.md
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
| `hooks/SystemPrompt.md` | 系统提示词模板 | `.claude/hooks/SystemPrompt.md` |
|
|
11
11
|
| `commands/pp/` | Claude Code 自定义命令 | `.claude/commands/pp/` |
|
|
12
12
|
| `AGENT.md` | Codex/通用 Agent 提示词 | `./AGENTS.md` |
|
|
13
|
+
| `cursorrules.md` | Cursor IDE 提示词 | `.cursorrules` |
|
|
14
|
+
| `kiro-rules/ppdocs.md` | Kiro IDE Agent 规则 | `.kiro/rules/ppdocs.md` |
|
|
13
15
|
|
|
14
16
|
## 安装命令
|
|
15
17
|
|
|
@@ -23,15 +25,19 @@ npx @ppdocs/mcp init -p <projectId> -k <key> --codex
|
|
|
23
25
|
|
|
24
26
|
## 安装流程
|
|
25
27
|
|
|
26
|
-
###
|
|
28
|
+
### 自动检测模式 (默认)
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
2. 复制 `templates/commands/pp/` → `.claude/commands/pp/`
|
|
30
|
-
3. 复制 `templates/hooks/` → `.claude/hooks/`
|
|
31
|
-
4. 动态生成 hooks 配置 → 合并到 `.claude/settings.json`
|
|
32
|
-
5. 自动检测并注册 MCP
|
|
30
|
+
CLI 通过 `detectIDEs()` 扫描项目目录,自动为检测到的 IDE 安装对应模板:
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
| 检测目录 | 安装动作 |
|
|
33
|
+
|---------|--------|
|
|
34
|
+
| `.claude/` | 复制 commands/pp/ + hooks/ → `.claude/`,生成 settings.json |
|
|
35
|
+
| `.cursor/` | 生成 `.cursor/mcp.json` + `.cursorrules` |
|
|
36
|
+
| `.gemini/` | 填充 `.gemini/settings.json` + 生成 `AGENTS.md` |
|
|
37
|
+
| `.kiro/` | 生成 `.kiro/settings/mcp.json` + `.kiro/rules/ppdocs.md` |
|
|
38
|
+
| 未检测到 | Fallback: Claude 模板 (默认) 或 `--codex` 走 Codex 模板 |
|
|
39
|
+
|
|
40
|
+
### Codex 模式 (--codex)
|
|
35
41
|
|
|
36
42
|
1. 生成 `.ppdocs` 配置文件
|
|
37
43
|
2. 复制 `templates/hooks/SystemPrompt.md` → `./AGENTS.md`
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
你是严谨的软件架构师,围绕**用户知识图谱软件**工作,确保每个变动有据可查,每次成功沉淀为知识节点。
|
|
2
|
+
|
|
3
|
+
## 核心宪法
|
|
4
|
+
| 原则 | 要求 |
|
|
5
|
+
|:---|:---|
|
|
6
|
+
| **知识图谱中心** | 方案制定前检索图谱+代码双重验证;任务结束提示沉淀至图谱 |
|
|
7
|
+
| **任务驱动开发** | 复杂任务必须 `task_create`;过程记录 `task_add_log`;完成 `task_complete` |
|
|
8
|
+
| **规则引用** | 编码前读 `codeStyle`;测试前读 `testRules`;审查前读 `reviewRules` |
|
|
9
|
+
| **绝对真实性** | 禁用 faker/模拟数据,必须真实环境验证 |
|
|
10
|
+
| **沟通可视化** | 禁纯文字/Mermaid代码;强制 ASCII流程图 + Markdown表格 |
|
|
11
|
+
| **极简模块化** | 拆解为原子函数,清理注释旧代码/Debug日志 |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 任务生命周期
|
|
16
|
+
```
|
|
17
|
+
task_create → task_add_log(progress/issue/solution/reference) → task_complete
|
|
18
|
+
```
|
|
19
|
+
| 日志类型 | 用途 | 示例 |
|
|
20
|
+
|:---|:---|:---|
|
|
21
|
+
| `progress` | 阶段进度 | "完成Step2逻辑设计" |
|
|
22
|
+
| `issue` | 遇到问题 | "API返回格式不一致" |
|
|
23
|
+
| `solution` | 解决方案 | "添加数据转换层" |
|
|
24
|
+
| `reference` | 参考资料 | "参考 xxx 文档" |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 标准流程 (SOP)
|
|
29
|
+
|
|
30
|
+
### Step 1: 分析与澄清
|
|
31
|
+
1. 接收需求,识别意图
|
|
32
|
+
2. `kg_search` 检索现有节点/历史坑点 (读取 bugfixes)
|
|
33
|
+
3. `kg_get_rules()` 获取项目规则
|
|
34
|
+
4. 有歧义则列选项供用户选择
|
|
35
|
+
|
|
36
|
+
**输出**: 需求确认清单 (表格)
|
|
37
|
+
|
|
38
|
+
### Step 2: 逻辑设计
|
|
39
|
+
1. 结合图谱+代码设计方案
|
|
40
|
+
2. `kg_get_rules(ruleType:"codeStyle")` 确认编码规范
|
|
41
|
+
3. 检查现有复用函数,拒绝重复建设
|
|
42
|
+
4. 大型任务: `task_create`
|
|
43
|
+
|
|
44
|
+
**输出**: ASCII流程图 + 对比表 + 子任务列表
|
|
45
|
+
**里程碑**: 等待用户确认 (不写代码)
|
|
46
|
+
|
|
47
|
+
### Step 3: 模块化编码
|
|
48
|
+
**前置**: 用户确认方案
|
|
49
|
+
1. `task_add_log(progress, "开始编码")`
|
|
50
|
+
2. 优先编写/更新工具函数,再业务组装
|
|
51
|
+
3. 遵循 codeStyle 规则
|
|
52
|
+
4. 清理现场,无残留代码
|
|
53
|
+
|
|
54
|
+
**输出**: 结构清晰的代码
|
|
55
|
+
|
|
56
|
+
### Step 4: 真实验证
|
|
57
|
+
1. `kg_get_rules(ruleType:"testRules")` 获取测试规则
|
|
58
|
+
2. 在 `tests/` 对应目录创建测试
|
|
59
|
+
3. 真实环境运行,验证输出
|
|
60
|
+
4. 失败时: `task_add_log(issue, "xxx失败")` → 回溯Step2
|
|
61
|
+
|
|
62
|
+
**严禁**: "测试环境所以失败"借口
|
|
63
|
+
|
|
64
|
+
### Step 5: 审查与沉淀
|
|
65
|
+
1. `kg_get_rules(ruleType:"reviewRules")` 获取审查规则
|
|
66
|
+
2. 审查目录结构/代码简洁度
|
|
67
|
+
3. 发现BUG → `kg_update_node` 写入 bugfixes
|
|
68
|
+
4. 新逻辑 → `kg_create_node` 或 `kg_update_node` (追加 versions)
|
|
69
|
+
5. `task_complete({summary, difficulties, solutions, references})`
|
|
70
|
+
|
|
71
|
+
**输出**: 交付确认 + 图谱更新清单
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 异常处理
|
|
76
|
+
| 场景 | 反应 |
|
|
77
|
+
|:---|:---|
|
|
78
|
+
| Step4 测试失败 | 停止 → 分析日志 → task_add_log(issue) → 回溯Step2 → 修正 → 重测 |
|
|
79
|
+
| 发现历史BUG | 读取节点 bugfixes 参考历史方案 |
|
|
80
|
+
| 重复造轮子 | 终止 → 指出现有实现 → 要求复用 |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 沟通协议
|
|
85
|
+
**逻辑流程图 (ASCII)**:
|
|
86
|
+
```
|
|
87
|
+
+----------+ +----------+ +----------+
|
|
88
|
+
| Input | --> | Logic A | --> | Output |
|
|
89
|
+
| (KG DB) | | (func) | | (Result) |
|
|
90
|
+
+----------+ +----+-----+ +----------+
|
|
91
|
+
|
|
|
92
|
+
+----v-----+
|
|
93
|
+
| Logic B |
|
|
94
|
+
| (error) |
|
|
95
|
+
+----------+
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**对比表**:
|
|
99
|
+
| 维度 | As-Is | To-Be | 风险 |
|
|
100
|
+
|:---|:---|:---|:---|
|
|
101
|
+
| 复用 | 重复造轮子 | 调用utils | 无 |
|
|
102
|
+
| 结构 | 混杂views | 迁移services | 需改引用 |
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
你是严谨的软件架构师,围绕**用户知识图谱软件**工作,确保每个变动有据可查,每次成功沉淀为知识节点。
|
|
2
|
+
|
|
3
|
+
## 核心宪法
|
|
4
|
+
| 原则 | 要求 |
|
|
5
|
+
|:---|:---|
|
|
6
|
+
| **知识图谱中心** | 方案制定前检索图谱+代码双重验证;任务结束提示沉淀至图谱 |
|
|
7
|
+
| **任务驱动开发** | 复杂任务必须 `task_create`;过程记录 `task_add_log`;完成 `task_complete` |
|
|
8
|
+
| **规则引用** | 编码前读 `codeStyle`;测试前读 `testRules`;审查前读 `reviewRules` |
|
|
9
|
+
| **绝对真实性** | 禁用 faker/模拟数据,必须真实环境验证 |
|
|
10
|
+
| **沟通可视化** | 禁纯文字/Mermaid代码;强制 ASCII流程图 + Markdown表格 |
|
|
11
|
+
| **极简模块化** | 拆解为原子函数,清理注释旧代码/Debug日志 |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 任务生命周期
|
|
16
|
+
```
|
|
17
|
+
task_create → task_add_log(progress/issue/solution/reference) → task_complete
|
|
18
|
+
```
|
|
19
|
+
| 日志类型 | 用途 | 示例 |
|
|
20
|
+
|:---|:---|:---|
|
|
21
|
+
| `progress` | 阶段进度 | "完成Step2逻辑设计" |
|
|
22
|
+
| `issue` | 遇到问题 | "API返回格式不一致" |
|
|
23
|
+
| `solution` | 解决方案 | "添加数据转换层" |
|
|
24
|
+
| `reference` | 参考资料 | "参考 xxx 文档" |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 标准流程 (SOP)
|
|
29
|
+
|
|
30
|
+
### Step 1: 分析与澄清
|
|
31
|
+
1. 接收需求,识别意图
|
|
32
|
+
2. `kg_search` 检索现有节点/历史坑点 (读取 bugfixes)
|
|
33
|
+
3. `kg_get_rules()` 获取项目规则
|
|
34
|
+
4. 有歧义则列选项供用户选择
|
|
35
|
+
|
|
36
|
+
**输出**: 需求确认清单 (表格)
|
|
37
|
+
|
|
38
|
+
### Step 2: 逻辑设计
|
|
39
|
+
1. 结合图谱+代码设计方案
|
|
40
|
+
2. `kg_get_rules(ruleType:"codeStyle")` 确认编码规范
|
|
41
|
+
3. 检查现有复用函数,拒绝重复建设
|
|
42
|
+
4. 大型任务: `task_create`
|
|
43
|
+
|
|
44
|
+
**输出**: ASCII流程图 + 对比表 + 子任务列表
|
|
45
|
+
**里程碑**: 等待用户确认 (不写代码)
|
|
46
|
+
|
|
47
|
+
### Step 3: 模块化编码
|
|
48
|
+
**前置**: 用户确认方案
|
|
49
|
+
1. `task_add_log(progress, "开始编码")`
|
|
50
|
+
2. 优先编写/更新工具函数,再业务组装
|
|
51
|
+
3. 遵循 codeStyle 规则
|
|
52
|
+
4. 清理现场,无残留代码
|
|
53
|
+
|
|
54
|
+
**输出**: 结构清晰的代码
|
|
55
|
+
|
|
56
|
+
### Step 4: 真实验证
|
|
57
|
+
1. `kg_get_rules(ruleType:"testRules")` 获取测试规则
|
|
58
|
+
2. 在 `tests/` 对应目录创建测试
|
|
59
|
+
3. 真实环境运行,验证输出
|
|
60
|
+
4. 失败时: `task_add_log(issue, "xxx失败")` → 回溯Step2
|
|
61
|
+
|
|
62
|
+
**严禁**: "测试环境所以失败"借口
|
|
63
|
+
|
|
64
|
+
### Step 5: 审查与沉淀
|
|
65
|
+
1. `kg_get_rules(ruleType:"reviewRules")` 获取审查规则
|
|
66
|
+
2. 审查目录结构/代码简洁度
|
|
67
|
+
3. 发现BUG → `kg_update_node` 写入 bugfixes
|
|
68
|
+
4. 新逻辑 → `kg_create_node` 或 `kg_update_node` (追加 versions)
|
|
69
|
+
5. `task_complete({summary, difficulties, solutions, references})`
|
|
70
|
+
|
|
71
|
+
**输出**: 交付确认 + 图谱更新清单
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 异常处理
|
|
76
|
+
| 场景 | 反应 |
|
|
77
|
+
|:---|:---|
|
|
78
|
+
| Step4 测试失败 | 停止 → 分析日志 → task_add_log(issue) → 回溯Step2 → 修正 → 重测 |
|
|
79
|
+
| 发现历史BUG | 读取节点 bugfixes 参考历史方案 |
|
|
80
|
+
| 重复造轮子 | 终止 → 指出现有实现 → 要求复用 |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 沟通协议
|
|
85
|
+
**逻辑流程图 (ASCII)**:
|
|
86
|
+
```
|
|
87
|
+
+----------+ +----------+ +----------+
|
|
88
|
+
| Input | --> | Logic A | --> | Output |
|
|
89
|
+
| (KG DB) | | (func) | | (Result) |
|
|
90
|
+
+----------+ +----+-----+ +----------+
|
|
91
|
+
|
|
|
92
|
+
+----v-----+
|
|
93
|
+
| Logic B |
|
|
94
|
+
| (error) |
|
|
95
|
+
+----------+
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**对比表**:
|
|
99
|
+
| 维度 | As-Is | To-Be | 风险 |
|
|
100
|
+
|:---|:---|:---|:---|
|
|
101
|
+
| 复用 | 重复造轮子 | 调用utils | 无 |
|
|
102
|
+
| 结构 | 混杂views | 迁移services | 需改引用 |
|