@ppdocs/mcp 3.2.19 → 3.2.21

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.
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * API URL 格式: http://localhost:20001/api/:projectId/:password/...
6
6
  */
7
- import type { DocData, DocNode, DocSearchResult, Task, TaskSummary, TaskLogType, TaskExperience, FileInfo, RuleMeta } from './types.js';
7
+ import type { DocData, DocNode, Task, TaskSummary, TaskLogType, TaskExperience, FileInfo, RuleMeta } from './types.js';
8
8
  interface TreeNode {
9
9
  path: string;
10
10
  name: string;
@@ -22,7 +22,6 @@ export declare class PpdocsApiClient {
22
22
  createDoc(docPath: string, doc: Partial<DocData>): Promise<DocData>;
23
23
  updateDoc(docPath: string, updates: Partial<DocData>): Promise<DocData | null>;
24
24
  deleteDoc(docPath: string): Promise<boolean>;
25
- searchDocs(keywords: string[], limit?: number): Promise<DocSearchResult[]>;
26
25
  /** 按状态筛选文档 */
27
26
  getDocsByStatus(statusList: string[]): Promise<DocNode[]>;
28
27
  /** 获取目录树结构 */
@@ -43,14 +42,6 @@ export declare class PpdocsApiClient {
43
42
  }, creator: string): Promise<Task>;
44
43
  addTaskLog(taskId: string, logType: TaskLogType, content: string): Promise<Task | null>;
45
44
  completeTask(taskId: string, experience: TaskExperience): Promise<Task | null>;
46
- /** 创建项目 (公开路由,无需认证) */
47
- createProject(id: string, name: string, description?: string, projectPath?: string): Promise<{
48
- project: {
49
- id: string;
50
- name: string;
51
- };
52
- password: string;
53
- }>;
54
45
  /** 列出所有可访问的项目 */
55
46
  crossListProjects(): Promise<{
56
47
  id: string;
@@ -77,12 +68,10 @@ export declare class PpdocsApiClient {
77
68
  localPath: string;
78
69
  fileCount: number;
79
70
  }>;
80
- /** 上传本地目录到中心服务器 (打包 zip → POST) */
81
- uploadFiles(localDir: string, remoteDir?: string): Promise<{
71
+ /** 上传本地目录或单文件到中心服务器 (打包 zip → POST) */
72
+ uploadFiles(localPath: string, remoteDir?: string): Promise<{
82
73
  fileCount: number;
83
74
  }>;
84
- /** 清空项目文件存储区 */
85
- clearFiles(): Promise<void>;
86
75
  /** 上传已打包好的 zip 数据到本项目 files/ (供 Code Beacon 使用) */
87
76
  uploadRawZip(zipData: Buffer | Uint8Array | any, remoteDir?: string): Promise<{
88
77
  fileCount: number;
@@ -104,6 +93,56 @@ export declare class PpdocsApiClient {
104
93
  analyzerImpactTree(projectPath: string, symbolName: string, depth?: number): Promise<unknown>;
105
94
  /** 获取文件 360° 上下文 */
106
95
  analyzerContext(projectPath: string, filePath: string): Promise<unknown>;
96
+ /** 智能上下文: 代码+文档+规则+任务全关联 */
97
+ analyzerSmartContext(projectPath: string, symbolName: string): Promise<unknown>;
98
+ /** 全关联路径: 两个符号之间的代码+文档路径 */
99
+ analyzerFullPath(projectPath: string, symbolA: string, symbolB: string, maxDepth?: number): Promise<unknown>;
100
+ private publicRequest;
101
+ /** 列出活跃讨论 (公开路由) */
102
+ discussionList(): Promise<unknown>;
103
+ /** 创建讨论 (公开路由) */
104
+ discussionCreate(title: string, initiator: string, participants: string[], content: string, msgSummary?: string): Promise<{
105
+ id: string;
106
+ }>;
107
+ /** 批量读取讨论 (公开路由, 支持已读追踪) */
108
+ discussionReadByIds(ids: string[], reader?: string, mode?: string): Promise<unknown>;
109
+ /** 列出所有讨论 (含历史, 按参与方筛选) */
110
+ discussionListAll(participant?: string): Promise<unknown>;
111
+ /** 回复讨论 (公开路由) */
112
+ discussionReply(id: string, sender: string, content: string, msgSummary?: string, newSummary?: string): Promise<boolean>;
113
+ /** 完成讨论 (公开路由, 标记为 completed) */
114
+ discussionComplete(id: string): Promise<boolean>;
115
+ /** 删除讨论 (公开路由) */
116
+ discussionDelete(id: string): Promise<boolean>;
117
+ /** 结案归档讨论 (认证路由) */
118
+ discussionClose(id: string, conclusion: string): Promise<{
119
+ archived_path: string;
120
+ }>;
121
+ /** 列出公共文件 */
122
+ publicFilesList(dir?: string): Promise<FileInfo[]>;
123
+ /** 读取公共文件 (文本) */
124
+ publicFilesRead(filePath: string): Promise<string>;
125
+ /** 创建公共文件池子目录 */
126
+ publicFilesMkdir(dirPath: string): Promise<boolean>;
127
+ /** 重命名公共文件或目录 */
128
+ publicFilesRename(filePath: string, newName: string): Promise<boolean>;
129
+ /** 删除公共文件 */
130
+ publicFilesDelete(filePath: string): Promise<boolean>;
131
+ /** 下载公共文件或目录 */
132
+ publicFilesDownload(remotePath: string, localPath?: string): Promise<{
133
+ localPath: string;
134
+ fileCount: number;
135
+ }>;
136
+ /** 上传本地目录或单文件到公共文件池 */
137
+ publicFilesUpload(localPath: string, remoteDir?: string): Promise<{
138
+ fileCount: number;
139
+ }>;
140
+ meetingJoin(agentId: string, agentType: string): Promise<unknown>;
141
+ meetingLeave(agentId: string): Promise<unknown>;
142
+ meetingPost(agentId: string, content: string, msgType?: string): Promise<unknown>;
143
+ meetingClaim(agentId: string, filePath: string): Promise<unknown>;
144
+ meetingRelease(agentId: string, filePath: string): Promise<unknown>;
145
+ meetingStatus(): Promise<unknown>;
107
146
  }
108
147
  export declare function initClient(apiUrl: string): void;
109
148
  export declare function getClient(): PpdocsApiClient;
@@ -191,12 +191,6 @@ export class PpdocsApiClient {
191
191
  return false;
192
192
  }
193
193
  }
194
- async searchDocs(keywords, limit = 20) {
195
- return this.request('/docs/search', {
196
- method: 'POST',
197
- body: JSON.stringify({ keywords, limit })
198
- });
199
- }
200
194
  /** 按状态筛选文档 */
201
195
  async getDocsByStatus(statusList) {
202
196
  return this.request('/docs/by-status', {
@@ -328,22 +322,6 @@ export class PpdocsApiClient {
328
322
  return null;
329
323
  }
330
324
  }
331
- // ============ 项目管理 ============
332
- /** 创建项目 (公开路由,无需认证) */
333
- async createProject(id, name, description, projectPath) {
334
- const url = `${this.serverUrl}/api/projects`;
335
- const response = await fetch(url, {
336
- method: 'POST',
337
- headers: { 'Content-Type': 'application/json' },
338
- body: JSON.stringify({ id, name, description: description || '', project_path: projectPath || '' }),
339
- });
340
- if (!response.ok) {
341
- const error = await response.json().catch(() => ({ error: response.statusText }));
342
- throw new Error(error.error || `HTTP ${response.status}`);
343
- }
344
- const json = await response.json();
345
- return json.data;
346
- }
347
325
  // ============ 跨项目只读访问 ============
348
326
  /** 列出所有可访问的项目 */
349
327
  async crossListProjects() {
@@ -397,33 +375,41 @@ export class PpdocsApiClient {
397
375
  async download(remotePath, localPath) {
398
376
  return fetchAndExtractZip(`${this.baseUrl}/files-download/${cleanPath(remotePath)}`, localPath);
399
377
  }
400
- /** 上传本地目录到中心服务器 (打包 zip → POST) */
401
- async uploadFiles(localDir, remoteDir) {
378
+ /** 上传本地目录或单文件到中心服务器 (打包 zip → POST) */
379
+ async uploadFiles(localPath, remoteDir) {
402
380
  const fs = await import('fs');
403
381
  const path = await import('path');
404
382
  const AdmZip = (await import('adm-zip')).default;
405
- if (!fs.existsSync(localDir) || !fs.statSync(localDir).isDirectory()) {
406
- throw new Error(`本地目录不存在: ${localDir}`);
383
+ if (!fs.existsSync(localPath)) {
384
+ throw new Error(`本地路径不存在: ${localPath}`);
407
385
  }
408
386
  const zip = new AdmZip();
409
- const addDir = (dirPath, zipPath) => {
410
- for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
411
- if (entry.name.startsWith('.'))
412
- continue;
413
- const fullPath = path.join(dirPath, entry.name);
414
- if (entry.isDirectory()) {
415
- if (EXCLUDED_DIRS.has(entry.name))
416
- continue;
417
- addDir(fullPath, zipPath ? `${zipPath}/${entry.name}` : entry.name);
418
- }
419
- else {
420
- if (fs.statSync(fullPath).size > 10_485_760)
387
+ const stat = fs.statSync(localPath);
388
+ if (stat.isDirectory()) {
389
+ const addDir = (dirPath, zipPath) => {
390
+ for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
391
+ if (entry.name.startsWith('.'))
421
392
  continue;
422
- zip.addLocalFile(fullPath, zipPath || undefined);
393
+ const fullPath = path.join(dirPath, entry.name);
394
+ if (entry.isDirectory()) {
395
+ if (EXCLUDED_DIRS.has(entry.name))
396
+ continue;
397
+ addDir(fullPath, zipPath ? `${zipPath}/${entry.name}` : entry.name);
398
+ }
399
+ else {
400
+ if (fs.statSync(fullPath).size > 10_485_760)
401
+ continue;
402
+ zip.addLocalFile(fullPath, zipPath || undefined);
403
+ }
423
404
  }
424
- }
425
- };
426
- addDir(localDir, '');
405
+ };
406
+ addDir(localPath, '');
407
+ }
408
+ else {
409
+ if (stat.size > 10_485_760)
410
+ throw new Error('文件超过 10MB 限制');
411
+ zip.addLocalFile(localPath);
412
+ }
427
413
  const buffer = zip.toBuffer();
428
414
  if (buffer.length > 104_857_600) {
429
415
  throw new Error('打包后超过 100MB 限制,请缩小目录范围');
@@ -441,15 +427,6 @@ export class PpdocsApiClient {
441
427
  const result = await response.json();
442
428
  return { fileCount: result.data?.fileCount || 0 };
443
429
  }
444
- /** 清空项目文件存储区 */
445
- async clearFiles() {
446
- const url = `${this.baseUrl}/files`;
447
- const response = await fetch(url, { method: 'DELETE' });
448
- if (!response.ok) {
449
- const error = await response.json().catch(() => ({ error: response.statusText }));
450
- throw new Error(error.error || `HTTP ${response.status}`);
451
- }
452
- }
453
430
  // ============ 直接上传已打包的 ZIP ============
454
431
  /** 上传已打包好的 zip 数据到本项目 files/ (供 Code Beacon 使用) */
455
432
  async uploadRawZip(zipData, remoteDir) {
@@ -509,6 +486,207 @@ export class PpdocsApiClient {
509
486
  body: JSON.stringify({ project_path: projectPath, file_path: filePath }),
510
487
  });
511
488
  }
489
+ /** 智能上下文: 代码+文档+规则+任务全关联 */
490
+ async analyzerSmartContext(projectPath, symbolName) {
491
+ return this.request('/analyzer/smart-context', {
492
+ method: 'POST',
493
+ body: JSON.stringify({ project_path: projectPath, symbol_name: symbolName }),
494
+ });
495
+ }
496
+ /** 全关联路径: 两个符号之间的代码+文档路径 */
497
+ async analyzerFullPath(projectPath, symbolA, symbolB, maxDepth = 3) {
498
+ return this.request('/analyzer/full-path', {
499
+ method: 'POST',
500
+ body: JSON.stringify({ project_path: projectPath, symbol_a: symbolA, symbol_b: symbolB, max_depth: maxDepth }),
501
+ });
502
+ }
503
+ // ============ 公开路由请求工具 (无需项目认证) ============
504
+ async publicRequest(path, options) {
505
+ const response = await fetch(`${this.serverUrl}${path}`, {
506
+ headers: { 'Content-Type': 'application/json' },
507
+ ...options,
508
+ });
509
+ if (!response.ok) {
510
+ const error = await response.json().catch(() => ({ error: response.statusText }));
511
+ throw new Error(error.error || `HTTP ${response.status}`);
512
+ }
513
+ const json = await response.json();
514
+ if (!json.success)
515
+ throw new Error(json.error || 'Unknown error');
516
+ return json.data;
517
+ }
518
+ // ============ 讨论系统 ============
519
+ /** 列出活跃讨论 (公开路由) */
520
+ async discussionList() {
521
+ return this.publicRequest('/api/discussions');
522
+ }
523
+ /** 创建讨论 (公开路由) */
524
+ async discussionCreate(title, initiator, participants, content, msgSummary) {
525
+ return this.publicRequest('/api/discussions', {
526
+ method: 'POST',
527
+ body: JSON.stringify({ title, initiator, participants, content, msg_summary: msgSummary }),
528
+ });
529
+ }
530
+ /** 批量读取讨论 (公开路由, 支持已读追踪) */
531
+ async discussionReadByIds(ids, reader, mode) {
532
+ return this.publicRequest('/api/discussions/read', {
533
+ method: 'POST',
534
+ body: JSON.stringify({ ids, reader, mode }),
535
+ });
536
+ }
537
+ /** 列出所有讨论 (含历史, 按参与方筛选) */
538
+ async discussionListAll(participant) {
539
+ return this.publicRequest('/api/discussions/all', {
540
+ method: 'POST',
541
+ body: JSON.stringify({ participant }),
542
+ });
543
+ }
544
+ /** 回复讨论 (公开路由) */
545
+ async discussionReply(id, sender, content, msgSummary, newSummary) {
546
+ return this.publicRequest(`/api/discussions/${encodeURIComponent(id)}/reply`, {
547
+ method: 'POST',
548
+ body: JSON.stringify({ sender, content, msg_summary: msgSummary, new_summary: newSummary }),
549
+ });
550
+ }
551
+ /** 完成讨论 (公开路由, 标记为 completed) */
552
+ async discussionComplete(id) {
553
+ return this.publicRequest(`/api/discussions/${encodeURIComponent(id)}/complete`, {
554
+ method: 'POST',
555
+ });
556
+ }
557
+ /** 删除讨论 (公开路由) */
558
+ async discussionDelete(id) {
559
+ return this.publicRequest(`/api/discussions/${encodeURIComponent(id)}`, {
560
+ method: 'DELETE',
561
+ });
562
+ }
563
+ /** 结案归档讨论 (认证路由) */
564
+ async discussionClose(id, conclusion) {
565
+ return this.request(`/discussions/${encodeURIComponent(id)}/close`, {
566
+ method: 'POST',
567
+ body: JSON.stringify({ conclusion }),
568
+ });
569
+ }
570
+ // ============ 公共文件池 ============
571
+ /** 列出公共文件 */
572
+ async publicFilesList(dir) {
573
+ const query = dir ? `?dir=${encodeURIComponent(dir)}` : '';
574
+ return this.publicRequest(`/api/public-files${query}`);
575
+ }
576
+ /** 读取公共文件 (文本) */
577
+ async publicFilesRead(filePath) {
578
+ return this.publicRequest(`/api/public-files/read/${cleanPath(filePath)}`);
579
+ }
580
+ /** 创建公共文件池子目录 */
581
+ async publicFilesMkdir(dirPath) {
582
+ return this.publicRequest(`/api/public-files/mkdir`, {
583
+ method: 'POST',
584
+ body: JSON.stringify({ path: dirPath }),
585
+ });
586
+ }
587
+ /** 重命名公共文件或目录 */
588
+ async publicFilesRename(filePath, newName) {
589
+ return this.publicRequest(`/api/public-files/rename`, {
590
+ method: 'POST',
591
+ body: JSON.stringify({ path: filePath, new_name: newName }),
592
+ });
593
+ }
594
+ /** 删除公共文件 */
595
+ async publicFilesDelete(filePath) {
596
+ return this.publicRequest(`/api/public-files/${cleanPath(filePath)}`, {
597
+ method: 'DELETE',
598
+ });
599
+ }
600
+ /** 下载公共文件或目录 */
601
+ async publicFilesDownload(remotePath, localPath) {
602
+ return fetchAndExtractZip(`${this.serverUrl}/api/public-files/download/${cleanPath(remotePath)}`, localPath);
603
+ }
604
+ /** 上传本地目录或单文件到公共文件池 */
605
+ async publicFilesUpload(localPath, remoteDir) {
606
+ const fs = await import('fs');
607
+ const path = await import('path');
608
+ const AdmZip = (await import('adm-zip')).default;
609
+ if (!fs.existsSync(localPath)) {
610
+ throw new Error(`本地路径不存在: ${localPath}`);
611
+ }
612
+ const zip = new AdmZip();
613
+ const stat = fs.statSync(localPath);
614
+ if (stat.isDirectory()) {
615
+ const addDir = (dirPath, zipPath) => {
616
+ for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
617
+ if (entry.name.startsWith('.'))
618
+ continue;
619
+ const fullPath = path.join(dirPath, entry.name);
620
+ if (entry.isDirectory()) {
621
+ if (EXCLUDED_DIRS.has(entry.name))
622
+ continue;
623
+ addDir(fullPath, zipPath ? `${zipPath}/${entry.name}` : entry.name);
624
+ }
625
+ else {
626
+ if (fs.statSync(fullPath).size > 10_485_760)
627
+ continue;
628
+ zip.addLocalFile(fullPath, zipPath || undefined);
629
+ }
630
+ }
631
+ };
632
+ addDir(localPath, '');
633
+ }
634
+ else {
635
+ if (stat.size > 10_485_760)
636
+ throw new Error('文件超过 10MB 限制');
637
+ zip.addLocalFile(localPath);
638
+ }
639
+ const buffer = zip.toBuffer();
640
+ if (buffer.length > 104_857_600) {
641
+ throw new Error('打包后超过 100MB 限制');
642
+ }
643
+ const query = remoteDir ? `?dir=${encodeURIComponent(remoteDir)}` : '';
644
+ const response = await fetch(`${this.serverUrl}/api/public-files/upload${query}`, {
645
+ method: 'POST',
646
+ headers: { 'Content-Type': 'application/octet-stream' },
647
+ body: new Uint8Array(buffer),
648
+ });
649
+ if (!response.ok) {
650
+ const error = await response.json().catch(() => ({ error: response.statusText }));
651
+ throw new Error(error.error || `HTTP ${response.status}`);
652
+ }
653
+ const result = await response.json();
654
+ return { fileCount: result.data?.fileCount || 0 };
655
+ }
656
+ // ============ 多AI协作会议 ============
657
+ async meetingJoin(agentId, agentType) {
658
+ return this.request('/meeting/join', {
659
+ method: 'POST',
660
+ body: JSON.stringify({ agent_id: agentId, agent_type: agentType }),
661
+ });
662
+ }
663
+ async meetingLeave(agentId) {
664
+ return this.request('/meeting/leave', {
665
+ method: 'POST',
666
+ body: JSON.stringify({ agent_id: agentId }),
667
+ });
668
+ }
669
+ async meetingPost(agentId, content, msgType = 'status') {
670
+ return this.request('/meeting/post', {
671
+ method: 'POST',
672
+ body: JSON.stringify({ agent_id: agentId, content, msg_type: msgType }),
673
+ });
674
+ }
675
+ async meetingClaim(agentId, filePath) {
676
+ return this.request('/meeting/claim', {
677
+ method: 'POST',
678
+ body: JSON.stringify({ agent_id: agentId, file_path: filePath }),
679
+ });
680
+ }
681
+ async meetingRelease(agentId, filePath) {
682
+ return this.request('/meeting/release', {
683
+ method: 'POST',
684
+ body: JSON.stringify({ agent_id: agentId, file_path: filePath }),
685
+ });
686
+ }
687
+ async meetingStatus() {
688
+ return this.request('/meeting/status');
689
+ }
512
690
  }
513
691
  // ============ 模块级 API ============
514
692
  let client = null;
@@ -26,27 +26,6 @@ export interface DocNode {
26
26
  isDir: boolean;
27
27
  status?: string;
28
28
  }
29
- /** 文档搜索结果 */
30
- export interface DocSearchResult {
31
- path: string;
32
- name: string;
33
- summary: string;
34
- score: number;
35
- }
36
- export interface ProjectMeta {
37
- projectId: string;
38
- projectName: string;
39
- updatedAt: string;
40
- password?: string;
41
- }
42
- export interface Project {
43
- id: string;
44
- name: string;
45
- description?: string;
46
- updatedAt: string;
47
- createdAt?: string;
48
- projectPath?: string;
49
- }
50
29
  export interface RuleMeta {
51
30
  label: string;
52
31
  keywords: string[];
@@ -5,4 +5,5 @@
5
5
  * 让 AI Agent 通过 MCP 工具直接查询代码结构、依赖关系和影响范围
6
6
  */
7
7
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
- export declare function registerAnalyzerTools(server: McpServer, projectId: string): void;
8
+ import { type McpContext } from './shared.js';
9
+ export declare function registerAnalyzerTools(server: McpServer, ctx: McpContext): void;