@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 CHANGED
@@ -47,8 +47,12 @@ npx @ppdocs/mcp init -p <项目ID> -k <密钥>
47
47
 
48
48
  这会自动:
49
49
  - 创建 `.ppdocs` 配置文件
50
- - 创建 `.mcp.json` MCP 配置
51
- - 安装工作流模板到 `.claude/`
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
- if (opts.codex) {
136
- installCodexTemplates(cwd);
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,创建 .mcp.json 作为备用
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
- /** 创建 .mcp.json (手动配置备用) */
237
- function createMcpJson(cwd) {
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(`✅ Created ${mcpPath}`);
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 (Codex mode)`);
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);
@@ -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 success = await storage.saveRules(projectId, decoded.ruleType, decoded.rules);
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}已保存 (${decoded.rules.length} 条)`);
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 success = await storage.saveRulesMeta(decoded);
285
- if (!success) {
284
+ const result = await storage.mergeRulesMeta(decoded);
285
+ if (!result.success) {
286
286
  return wrap('❌ 保存失败');
287
287
  }
288
- return wrap(`✅ 规则触发配置已保存 (${Object.keys(decoded).length} 个类型)`);
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 success = await storage.saveGlobalRulesMeta(decoded);
324
- if (!success) {
323
+ const result = await storage.mergeGlobalRulesMeta(decoded);
324
+ if (!result.success) {
325
325
  return wrap('❌ 保存失败');
326
326
  }
327
- return wrap(`✅ 全局默认规则触发配置已保存 (${Object.keys(decoded).length} 个类型)`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ppdocs/mcp",
3
- "version": "3.1.1",
3
+ "version": "3.1.3",
4
4
  "description": "ppdocs MCP Server - Knowledge Graph for Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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
- ### 默认模式 (Claude Code)
28
+ ### 自动检测模式 (默认)
27
29
 
28
- 1. 生成 `.ppdocs` 配置文件 (api/projectId/key)
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
- ### Codex 模式
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 | 需改引用 |