openspec-mcp 0.1.0 → 0.2.0

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.
Files changed (92) hide show
  1. package/README.md +83 -35
  2. package/README.zh.md +81 -33
  3. package/dist/api/routes/changes.d.ts.map +1 -1
  4. package/dist/api/routes/changes.js +54 -0
  5. package/dist/api/routes/changes.js.map +1 -1
  6. package/dist/api/routes/project.d.ts +7 -0
  7. package/dist/api/routes/project.d.ts.map +1 -0
  8. package/dist/api/routes/project.js +14 -0
  9. package/dist/api/routes/project.js.map +1 -0
  10. package/dist/api/routes/specs.d.ts.map +1 -1
  11. package/dist/api/routes/specs.js +100 -1
  12. package/dist/api/routes/specs.js.map +1 -1
  13. package/dist/api/routes/tasks.d.ts.map +1 -1
  14. package/dist/api/routes/tasks.js +33 -0
  15. package/dist/api/routes/tasks.js.map +1 -1
  16. package/dist/api/server.d.ts +5 -1
  17. package/dist/api/server.d.ts.map +1 -1
  18. package/dist/api/server.js +107 -14
  19. package/dist/api/server.js.map +1 -1
  20. package/dist/core/approval-manager.test.d.ts +5 -0
  21. package/dist/core/approval-manager.test.d.ts.map +1 -0
  22. package/dist/core/approval-manager.test.js +114 -0
  23. package/dist/core/approval-manager.test.js.map +1 -0
  24. package/dist/core/file-watcher.d.ts.map +1 -1
  25. package/dist/core/file-watcher.js +13 -0
  26. package/dist/core/file-watcher.js.map +1 -1
  27. package/dist/core/hooks-manager.d.ts +43 -0
  28. package/dist/core/hooks-manager.d.ts.map +1 -0
  29. package/dist/core/hooks-manager.js +194 -0
  30. package/dist/core/hooks-manager.js.map +1 -0
  31. package/dist/core/openspec-cli.d.ts +12 -0
  32. package/dist/core/openspec-cli.d.ts.map +1 -1
  33. package/dist/core/openspec-cli.js +85 -0
  34. package/dist/core/openspec-cli.js.map +1 -1
  35. package/dist/core/proposal-generator.d.ts +46 -0
  36. package/dist/core/proposal-generator.d.ts.map +1 -0
  37. package/dist/core/proposal-generator.js +155 -0
  38. package/dist/core/proposal-generator.js.map +1 -0
  39. package/dist/core/review-manager.d.ts +147 -0
  40. package/dist/core/review-manager.d.ts.map +1 -0
  41. package/dist/core/review-manager.js +235 -0
  42. package/dist/core/review-manager.js.map +1 -0
  43. package/dist/core/spec-parser.d.ts +51 -0
  44. package/dist/core/spec-parser.d.ts.map +1 -0
  45. package/dist/core/spec-parser.js +130 -0
  46. package/dist/core/spec-parser.js.map +1 -0
  47. package/dist/core/task-parser.test.d.ts +5 -0
  48. package/dist/core/task-parser.test.d.ts.map +1 -0
  49. package/dist/core/task-parser.test.js +124 -0
  50. package/dist/core/task-parser.test.js.map +1 -0
  51. package/dist/core/template-manager.d.ts +48 -0
  52. package/dist/core/template-manager.d.ts.map +1 -0
  53. package/dist/core/template-manager.js +268 -0
  54. package/dist/core/template-manager.js.map +1 -0
  55. package/dist/index.js +17 -1
  56. package/dist/index.js.map +1 -1
  57. package/dist/server/tools/generator.d.ts +8 -0
  58. package/dist/server/tools/generator.d.ts.map +1 -0
  59. package/dist/server/tools/generator.js +121 -0
  60. package/dist/server/tools/generator.js.map +1 -0
  61. package/dist/server/tools/hooks.d.ts +8 -0
  62. package/dist/server/tools/hooks.d.ts.map +1 -0
  63. package/dist/server/tools/hooks.js +86 -0
  64. package/dist/server/tools/hooks.js.map +1 -0
  65. package/dist/server/tools/management.d.ts.map +1 -1
  66. package/dist/server/tools/management.js +37 -2
  67. package/dist/server/tools/management.js.map +1 -1
  68. package/dist/server/tools/reviews.d.ts +8 -0
  69. package/dist/server/tools/reviews.d.ts.map +1 -0
  70. package/dist/server/tools/reviews.js +178 -0
  71. package/dist/server/tools/reviews.js.map +1 -0
  72. package/dist/server/tools/tasks.d.ts.map +1 -1
  73. package/dist/server/tools/tasks.js +38 -0
  74. package/dist/server/tools/tasks.js.map +1 -1
  75. package/dist/server/tools/templates.d.ts +8 -0
  76. package/dist/server/tools/templates.d.ts.map +1 -0
  77. package/dist/server/tools/templates.js +89 -0
  78. package/dist/server/tools/templates.js.map +1 -0
  79. package/dist/utils/constants.d.ts +54 -0
  80. package/dist/utils/constants.d.ts.map +1 -0
  81. package/dist/utils/constants.js +54 -0
  82. package/dist/utils/constants.js.map +1 -0
  83. package/dist/utils/version.d.ts +13 -0
  84. package/dist/utils/version.d.ts.map +1 -0
  85. package/dist/utils/version.js +26 -0
  86. package/dist/utils/version.js.map +1 -0
  87. package/package.json +2 -6
  88. package/web/dist/assets/index-BGNTCJhf.css +1 -0
  89. package/web/dist/assets/index-DKBVRb7-.js +99 -0
  90. package/web/dist/index.html +2 -2
  91. package/web/dist/assets/index--LppUKpS.js +0 -67
  92. package/web/dist/assets/index-DdJQfs9Z.css +0 -1
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Review Manager
3
+ * 管理评审意见(Review Comments)
4
+ *
5
+ * 替代原有的 AnnotationManager,提供更完善的评审功能:
6
+ * - 支持多种目标类型 (proposal/design/spec/tasks)
7
+ * - 基于行号定位
8
+ * - 评论类型和优先级
9
+ * - 回复功能
10
+ */
11
+ /**
12
+ * 评审目标类型
13
+ */
14
+ export type ReviewTargetType = 'proposal' | 'design' | 'spec' | 'tasks';
15
+ /**
16
+ * 评审类型
17
+ */
18
+ export type ReviewType = 'comment' | 'suggestion' | 'question' | 'issue';
19
+ /**
20
+ * 严重性等级
21
+ */
22
+ export type ReviewSeverity = 'low' | 'medium' | 'high';
23
+ /**
24
+ * 评审状态
25
+ */
26
+ export type ReviewStatus = 'open' | 'resolved' | 'wont_fix';
27
+ /**
28
+ * 评审回复
29
+ */
30
+ export interface ReviewReply {
31
+ id: string;
32
+ author: string;
33
+ body: string;
34
+ createdAt: string;
35
+ }
36
+ /**
37
+ * 评审意见
38
+ */
39
+ export interface ReviewComment {
40
+ id: string;
41
+ targetType: ReviewTargetType;
42
+ targetId: string;
43
+ lineNumber?: number;
44
+ type: ReviewType;
45
+ severity?: ReviewSeverity;
46
+ body: string;
47
+ suggestedChange?: string;
48
+ author: string;
49
+ status: ReviewStatus;
50
+ createdAt: string;
51
+ resolvedAt?: string;
52
+ resolvedBy?: string;
53
+ replies: ReviewReply[];
54
+ }
55
+ /**
56
+ * 评审统计
57
+ */
58
+ export interface ReviewSummary {
59
+ total: number;
60
+ open: number;
61
+ resolved: number;
62
+ wontFix: number;
63
+ byType: Record<ReviewType, number>;
64
+ bySeverity: Record<ReviewSeverity, number>;
65
+ hasBlockingIssues: boolean;
66
+ }
67
+ export declare class ReviewManager {
68
+ private cwd;
69
+ constructor(options?: {
70
+ cwd?: string;
71
+ });
72
+ /**
73
+ * 获取评审存储目录
74
+ */
75
+ private getReviewsDir;
76
+ /**
77
+ * 获取评审文件路径
78
+ */
79
+ private getReviewFilePath;
80
+ /**
81
+ * ID 安全校验
82
+ */
83
+ private ensureSafeId;
84
+ /**
85
+ * 确保目录存在
86
+ */
87
+ private ensureDir;
88
+ /**
89
+ * 加载评审列表
90
+ */
91
+ loadReviews(targetType: ReviewTargetType, targetId: string): Promise<ReviewComment[]>;
92
+ /**
93
+ * 保存评审列表
94
+ */
95
+ private saveReviews;
96
+ /**
97
+ * 添加评审意见
98
+ */
99
+ addReview(options: {
100
+ targetType: ReviewTargetType;
101
+ targetId: string;
102
+ lineNumber?: number;
103
+ type: ReviewType;
104
+ severity?: ReviewSeverity;
105
+ body: string;
106
+ suggestedChange?: string;
107
+ author: string;
108
+ }): Promise<ReviewComment>;
109
+ /**
110
+ * 列出评审意见
111
+ */
112
+ listReviews(targetType: ReviewTargetType, targetId: string, options?: {
113
+ status?: ReviewStatus;
114
+ type?: ReviewType;
115
+ }): Promise<ReviewComment[]>;
116
+ /**
117
+ * 获取单个评审
118
+ */
119
+ getReview(targetType: ReviewTargetType, targetId: string, reviewId: string): Promise<ReviewComment | null>;
120
+ /**
121
+ * 添加回复
122
+ */
123
+ addReply(targetType: ReviewTargetType, targetId: string, reviewId: string, author: string, body: string): Promise<ReviewReply | null>;
124
+ /**
125
+ * 解决评审意见
126
+ */
127
+ resolveReview(targetType: ReviewTargetType, targetId: string, reviewId: string, resolvedBy: string, status?: 'resolved' | 'wont_fix'): Promise<boolean>;
128
+ /**
129
+ * 获取评审统计
130
+ */
131
+ getReviewSummary(targetType: ReviewTargetType, targetId: string): Promise<ReviewSummary>;
132
+ /**
133
+ * 获取 Change 的所有评审(包括 proposal/design/tasks)
134
+ */
135
+ getChangeReviews(changeId: string): Promise<{
136
+ proposal: ReviewComment[];
137
+ design: ReviewComment[];
138
+ tasks: ReviewComment[];
139
+ summary: ReviewSummary;
140
+ }>;
141
+ /**
142
+ * 检查是否可以请求审批
143
+ * 返回阻塞原因列表,空数组表示可以审批
144
+ */
145
+ checkApprovalReadiness(changeId: string): Promise<string[]>;
146
+ }
147
+ //# sourceMappingURL=review-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-manager.d.ts","sourceRoot":"","sources":["../../src/core/review-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Review Manager
3
+ * 管理评审意见(Review Comments)
4
+ *
5
+ * 替代原有的 AnnotationManager,提供更完善的评审功能:
6
+ * - 支持多种目标类型 (proposal/design/spec/tasks)
7
+ * - 基于行号定位
8
+ * - 评论类型和优先级
9
+ * - 回复功能
10
+ */
11
+ import * as fs from 'fs/promises';
12
+ import * as path from 'path';
13
+ import { randomUUID } from 'crypto';
14
+ export class ReviewManager {
15
+ cwd;
16
+ constructor(options) {
17
+ this.cwd = options?.cwd || process.cwd();
18
+ }
19
+ /**
20
+ * 获取评审存储目录
21
+ */
22
+ getReviewsDir() {
23
+ return path.join(this.cwd, 'openspec', 'reviews');
24
+ }
25
+ /**
26
+ * 获取评审文件路径
27
+ */
28
+ getReviewFilePath(targetType, targetId) {
29
+ const safeId = this.ensureSafeId(targetId);
30
+ // spec -> reviews/specs/{specId}.json
31
+ // proposal/design/tasks -> reviews/changes/{changeId}/{targetType}.json
32
+ if (targetType === 'spec') {
33
+ return path.join(this.getReviewsDir(), 'specs', `${safeId}.json`);
34
+ }
35
+ // For change-related reviews, store in separate files per type
36
+ return path.join(this.getReviewsDir(), 'changes', safeId, `${targetType}.json`);
37
+ }
38
+ /**
39
+ * ID 安全校验
40
+ */
41
+ ensureSafeId(id) {
42
+ const trimmed = id.trim();
43
+ if (!trimmed || trimmed.includes('..') || trimmed.includes('/') || trimmed.includes('\\')) {
44
+ throw new Error(`Invalid id: ${id}`);
45
+ }
46
+ return trimmed;
47
+ }
48
+ /**
49
+ * 确保目录存在
50
+ */
51
+ async ensureDir(filePath) {
52
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
53
+ }
54
+ /**
55
+ * 加载评审列表
56
+ */
57
+ async loadReviews(targetType, targetId) {
58
+ const filePath = this.getReviewFilePath(targetType, targetId);
59
+ try {
60
+ const content = await fs.readFile(filePath, 'utf-8');
61
+ return JSON.parse(content);
62
+ }
63
+ catch {
64
+ return [];
65
+ }
66
+ }
67
+ /**
68
+ * 保存评审列表
69
+ */
70
+ async saveReviews(targetType, targetId, reviews) {
71
+ const filePath = this.getReviewFilePath(targetType, targetId);
72
+ await this.ensureDir(filePath);
73
+ await fs.writeFile(filePath, JSON.stringify(reviews, null, 2), 'utf-8');
74
+ }
75
+ /**
76
+ * 添加评审意见
77
+ */
78
+ async addReview(options) {
79
+ const reviews = await this.loadReviews(options.targetType, options.targetId);
80
+ const review = {
81
+ id: randomUUID().substring(0, 8),
82
+ targetType: options.targetType,
83
+ targetId: options.targetId,
84
+ lineNumber: options.lineNumber,
85
+ type: options.type,
86
+ severity: options.severity,
87
+ body: options.body,
88
+ suggestedChange: options.suggestedChange,
89
+ author: options.author,
90
+ status: 'open',
91
+ createdAt: new Date().toISOString(),
92
+ replies: [],
93
+ };
94
+ reviews.push(review);
95
+ await this.saveReviews(options.targetType, options.targetId, reviews);
96
+ return review;
97
+ }
98
+ /**
99
+ * 列出评审意见
100
+ */
101
+ async listReviews(targetType, targetId, options) {
102
+ let reviews = await this.loadReviews(targetType, targetId);
103
+ if (options?.status) {
104
+ reviews = reviews.filter((r) => r.status === options.status);
105
+ }
106
+ if (options?.type) {
107
+ reviews = reviews.filter((r) => r.type === options.type);
108
+ }
109
+ return reviews;
110
+ }
111
+ /**
112
+ * 获取单个评审
113
+ */
114
+ async getReview(targetType, targetId, reviewId) {
115
+ const reviews = await this.loadReviews(targetType, targetId);
116
+ return reviews.find((r) => r.id === reviewId) || null;
117
+ }
118
+ /**
119
+ * 添加回复
120
+ */
121
+ async addReply(targetType, targetId, reviewId, author, body) {
122
+ const reviews = await this.loadReviews(targetType, targetId);
123
+ const review = reviews.find((r) => r.id === reviewId);
124
+ if (!review) {
125
+ return null;
126
+ }
127
+ const reply = {
128
+ id: randomUUID().substring(0, 8),
129
+ author,
130
+ body,
131
+ createdAt: new Date().toISOString(),
132
+ };
133
+ review.replies.push(reply);
134
+ await this.saveReviews(targetType, targetId, reviews);
135
+ return reply;
136
+ }
137
+ /**
138
+ * 解决评审意见
139
+ */
140
+ async resolveReview(targetType, targetId, reviewId, resolvedBy, status = 'resolved') {
141
+ const reviews = await this.loadReviews(targetType, targetId);
142
+ const review = reviews.find((r) => r.id === reviewId);
143
+ if (!review) {
144
+ return false;
145
+ }
146
+ review.status = status;
147
+ review.resolvedAt = new Date().toISOString();
148
+ review.resolvedBy = resolvedBy;
149
+ await this.saveReviews(targetType, targetId, reviews);
150
+ return true;
151
+ }
152
+ /**
153
+ * 获取评审统计
154
+ */
155
+ async getReviewSummary(targetType, targetId) {
156
+ const reviews = await this.loadReviews(targetType, targetId);
157
+ const summary = {
158
+ total: reviews.length,
159
+ open: 0,
160
+ resolved: 0,
161
+ wontFix: 0,
162
+ byType: { comment: 0, suggestion: 0, question: 0, issue: 0 },
163
+ bySeverity: { low: 0, medium: 0, high: 0 },
164
+ hasBlockingIssues: false,
165
+ };
166
+ for (const review of reviews) {
167
+ // 状态统计
168
+ if (review.status === 'open')
169
+ summary.open++;
170
+ else if (review.status === 'resolved')
171
+ summary.resolved++;
172
+ else if (review.status === 'wont_fix')
173
+ summary.wontFix++;
174
+ // 类型统计
175
+ summary.byType[review.type]++;
176
+ // 严重性统计
177
+ if (review.severity) {
178
+ summary.bySeverity[review.severity]++;
179
+ }
180
+ // 检查阻塞性问题
181
+ if (review.status === 'open' && review.type === 'issue' && review.severity === 'high') {
182
+ summary.hasBlockingIssues = true;
183
+ }
184
+ }
185
+ return summary;
186
+ }
187
+ /**
188
+ * 获取 Change 的所有评审(包括 proposal/design/tasks)
189
+ */
190
+ async getChangeReviews(changeId) {
191
+ const proposal = await this.loadReviews('proposal', changeId);
192
+ const design = await this.loadReviews('design', changeId);
193
+ const tasks = await this.loadReviews('tasks', changeId);
194
+ const allReviews = [...proposal, ...design, ...tasks];
195
+ // 计算总体统计
196
+ const summary = {
197
+ total: allReviews.length,
198
+ open: allReviews.filter((r) => r.status === 'open').length,
199
+ resolved: allReviews.filter((r) => r.status === 'resolved').length,
200
+ wontFix: allReviews.filter((r) => r.status === 'wont_fix').length,
201
+ byType: { comment: 0, suggestion: 0, question: 0, issue: 0 },
202
+ bySeverity: { low: 0, medium: 0, high: 0 },
203
+ hasBlockingIssues: false,
204
+ };
205
+ for (const review of allReviews) {
206
+ summary.byType[review.type]++;
207
+ if (review.severity)
208
+ summary.bySeverity[review.severity]++;
209
+ if (review.status === 'open' && review.type === 'issue' && review.severity === 'high') {
210
+ summary.hasBlockingIssues = true;
211
+ }
212
+ }
213
+ return { proposal, design, tasks, summary };
214
+ }
215
+ /**
216
+ * 检查是否可以请求审批
217
+ * 返回阻塞原因列表,空数组表示可以审批
218
+ */
219
+ async checkApprovalReadiness(changeId) {
220
+ const blockers = [];
221
+ const { summary, proposal, design, tasks } = await this.getChangeReviews(changeId);
222
+ // 检查高严重性 issue
223
+ if (summary.hasBlockingIssues) {
224
+ const highIssues = [...proposal, ...design, ...tasks].filter((r) => r.status === 'open' && r.type === 'issue' && r.severity === 'high');
225
+ blockers.push(`${highIssues.length} high-severity issue(s) must be resolved`);
226
+ }
227
+ // 检查未回答的问题
228
+ const openQuestions = [...proposal, ...design, ...tasks].filter((r) => r.status === 'open' && r.type === 'question');
229
+ if (openQuestions.length > 0) {
230
+ blockers.push(`${openQuestions.length} question(s) need to be answered`);
231
+ }
232
+ return blockers;
233
+ }
234
+ }
235
+ //# sourceMappingURL=review-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-manager.js","sourceRoot":"","sources":["../../src/core/review-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Spec 依赖解析器
3
+ * 解析 Spec 之间的依赖关系并生成可视化图表
4
+ */
5
+ export interface SpecNode {
6
+ id: string;
7
+ title: string;
8
+ }
9
+ export interface SpecEdge {
10
+ from: string;
11
+ to: string;
12
+ }
13
+ export interface DependencyGraph {
14
+ nodes: SpecNode[];
15
+ edges: SpecEdge[];
16
+ }
17
+ export declare class SpecParser {
18
+ private cwd;
19
+ constructor(options?: {
20
+ cwd?: string;
21
+ });
22
+ /**
23
+ * 获取 specs 目录
24
+ */
25
+ private getSpecsDir;
26
+ /**
27
+ * 解析单个 Spec 的依赖
28
+ *
29
+ * 支持的格式:
30
+ * <!-- @depends-on: spec-a, spec-b -->
31
+ * <!-- @requires: spec-c -->
32
+ */
33
+ parseDependencies(specId: string): Promise<string[]>;
34
+ /**
35
+ * 获取 Spec 标题
36
+ */
37
+ getSpecTitle(specId: string): Promise<string>;
38
+ /**
39
+ * 构建完整依赖图
40
+ */
41
+ buildDependencyGraph(): Promise<DependencyGraph>;
42
+ /**
43
+ * 生成 Mermaid 格式的依赖图
44
+ */
45
+ toMermaid(): Promise<string>;
46
+ /**
47
+ * 获取某个 Spec 的依赖链(递归解析)
48
+ */
49
+ getDependencyChain(specId: string, visited?: Set<string>): Promise<string[]>;
50
+ }
51
+ //# sourceMappingURL=spec-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-parser.d.ts","sourceRoot":"","sources":["../../src/core/spec-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,GAAG,CAAS;gBAER,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE;IAItC;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;;;;;OAMG;IACG,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA4B1D;;OAEG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAYnD;;OAEG;IACG,oBAAoB,IAAI,OAAO,CAAC,eAAe,CAAC;IA4BtD;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IA0BlC;;OAEG;IACG,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,cAAoB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAgBzF"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Spec 依赖解析器
3
+ * 解析 Spec 之间的依赖关系并生成可视化图表
4
+ */
5
+ import * as fs from 'fs/promises';
6
+ import * as path from 'path';
7
+ export class SpecParser {
8
+ cwd;
9
+ constructor(options) {
10
+ this.cwd = options?.cwd || process.cwd();
11
+ }
12
+ /**
13
+ * 获取 specs 目录
14
+ */
15
+ getSpecsDir() {
16
+ return path.join(this.cwd, 'openspec', 'specs');
17
+ }
18
+ /**
19
+ * 解析单个 Spec 的依赖
20
+ *
21
+ * 支持的格式:
22
+ * <!-- @depends-on: spec-a, spec-b -->
23
+ * <!-- @requires: spec-c -->
24
+ */
25
+ async parseDependencies(specId) {
26
+ const specPath = path.join(this.getSpecsDir(), specId, 'spec.md');
27
+ try {
28
+ const content = await fs.readFile(specPath, 'utf-8');
29
+ const dependencies = [];
30
+ // 匹配 @depends-on 或 @requires 注释
31
+ const dependsMatch = content.match(/<!--\s*@(?:depends-on|requires):\s*([^-]+)\s*-->/gi);
32
+ if (dependsMatch) {
33
+ for (const match of dependsMatch) {
34
+ const deps = match
35
+ .replace(/<!--\s*@(?:depends-on|requires):\s*/i, '')
36
+ .replace(/\s*-->/, '')
37
+ .split(',')
38
+ .map((d) => d.trim())
39
+ .filter((d) => d.length > 0);
40
+ dependencies.push(...deps);
41
+ }
42
+ }
43
+ return [...new Set(dependencies)]; // 去重
44
+ }
45
+ catch {
46
+ return [];
47
+ }
48
+ }
49
+ /**
50
+ * 获取 Spec 标题
51
+ */
52
+ async getSpecTitle(specId) {
53
+ const specPath = path.join(this.getSpecsDir(), specId, 'spec.md');
54
+ try {
55
+ const content = await fs.readFile(specPath, 'utf-8');
56
+ const titleMatch = content.match(/^#\s+(.+)/m);
57
+ return titleMatch ? titleMatch[1].trim() : specId;
58
+ }
59
+ catch {
60
+ return specId;
61
+ }
62
+ }
63
+ /**
64
+ * 构建完整依赖图
65
+ */
66
+ async buildDependencyGraph() {
67
+ const nodes = [];
68
+ const edges = [];
69
+ const specsDir = this.getSpecsDir();
70
+ try {
71
+ const entries = await fs.readdir(specsDir, { withFileTypes: true });
72
+ for (const entry of entries) {
73
+ if (!entry.isDirectory())
74
+ continue;
75
+ const specId = entry.name;
76
+ const title = await this.getSpecTitle(specId);
77
+ nodes.push({ id: specId, title });
78
+ // 解析依赖
79
+ const dependencies = await this.parseDependencies(specId);
80
+ for (const dep of dependencies) {
81
+ edges.push({ from: specId, to: dep });
82
+ }
83
+ }
84
+ }
85
+ catch {
86
+ // specs 目录不存在
87
+ }
88
+ return { nodes, edges };
89
+ }
90
+ /**
91
+ * 生成 Mermaid 格式的依赖图
92
+ */
93
+ async toMermaid() {
94
+ const graph = await this.buildDependencyGraph();
95
+ if (graph.nodes.length === 0) {
96
+ return 'graph LR\n NoSpecs[No specs found]';
97
+ }
98
+ let mermaid = 'graph LR\n';
99
+ // 添加节点定义(使用标题)
100
+ for (const node of graph.nodes) {
101
+ const safeTitle = node.title.replace(/"/g, "'");
102
+ mermaid += ` ${node.id}["${safeTitle}"]\n`;
103
+ }
104
+ // 添加边
105
+ if (graph.edges.length > 0) {
106
+ mermaid += '\n';
107
+ for (const edge of graph.edges) {
108
+ mermaid += ` ${edge.from} --> ${edge.to}\n`;
109
+ }
110
+ }
111
+ return mermaid;
112
+ }
113
+ /**
114
+ * 获取某个 Spec 的依赖链(递归解析)
115
+ */
116
+ async getDependencyChain(specId, visited = new Set()) {
117
+ if (visited.has(specId)) {
118
+ return []; // 避免循环依赖
119
+ }
120
+ visited.add(specId);
121
+ const directDeps = await this.parseDependencies(specId);
122
+ const allDeps = [...directDeps];
123
+ for (const dep of directDeps) {
124
+ const transitiveDeps = await this.getDependencyChain(dep, visited);
125
+ allDeps.push(...transitiveDeps);
126
+ }
127
+ return [...new Set(allDeps)];
128
+ }
129
+ }
130
+ //# sourceMappingURL=spec-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-parser.js","sourceRoot":"","sources":["../../src/core/spec-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * TaskParser 单元测试
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=task-parser.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-parser.test.d.ts","sourceRoot":"","sources":["../../src/core/task-parser.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}