@cliangdev/flux-plugin 0.0.0-dev.cbdf207 → 0.0.0-dev.df3e9bb

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 (86) hide show
  1. package/README.md +8 -4
  2. package/bin/install.cjs +150 -16
  3. package/package.json +7 -11
  4. package/src/__tests__/version.test.ts +37 -0
  5. package/src/adapters/local/.gitkeep +0 -0
  6. package/src/server/__tests__/config.test.ts +163 -0
  7. package/src/server/adapters/__tests__/a-client-linear.test.ts +197 -0
  8. package/src/server/adapters/__tests__/adapter-factory.test.ts +230 -0
  9. package/src/server/adapters/__tests__/dependency-ops.test.ts +395 -0
  10. package/src/server/adapters/__tests__/document-ops.test.ts +306 -0
  11. package/src/server/adapters/__tests__/linear-adapter.test.ts +91 -0
  12. package/src/server/adapters/__tests__/linear-config.test.ts +425 -0
  13. package/src/server/adapters/__tests__/linear-criteria-parser.test.ts +287 -0
  14. package/src/server/adapters/__tests__/linear-description-test.ts +238 -0
  15. package/src/server/adapters/__tests__/linear-epic-crud.test.ts +496 -0
  16. package/src/server/adapters/__tests__/linear-mappers-description.test.ts +276 -0
  17. package/src/server/adapters/__tests__/linear-mappers-epic.test.ts +294 -0
  18. package/src/server/adapters/__tests__/linear-mappers-prd.test.ts +300 -0
  19. package/src/server/adapters/__tests__/linear-mappers-task.test.ts +197 -0
  20. package/src/server/adapters/__tests__/linear-prd-crud.test.ts +620 -0
  21. package/src/server/adapters/__tests__/linear-stats.test.ts +450 -0
  22. package/src/server/adapters/__tests__/linear-task-crud.test.ts +534 -0
  23. package/src/server/adapters/__tests__/linear-types.test.ts +243 -0
  24. package/src/server/adapters/__tests__/status-ops.test.ts +441 -0
  25. package/src/server/adapters/factory.ts +90 -0
  26. package/src/server/adapters/index.ts +9 -0
  27. package/src/server/adapters/linear/adapter.ts +1136 -0
  28. package/src/server/adapters/linear/client.ts +169 -0
  29. package/src/server/adapters/linear/config.ts +152 -0
  30. package/src/server/adapters/linear/helpers/criteria-parser.ts +197 -0
  31. package/src/server/adapters/linear/helpers/index.ts +7 -0
  32. package/src/server/adapters/linear/index.ts +16 -0
  33. package/src/server/adapters/linear/mappers/description.ts +136 -0
  34. package/src/server/adapters/linear/mappers/epic.ts +81 -0
  35. package/src/server/adapters/linear/mappers/index.ts +27 -0
  36. package/src/server/adapters/linear/mappers/prd.ts +178 -0
  37. package/src/server/adapters/linear/mappers/task.ts +82 -0
  38. package/src/server/adapters/linear/types.ts +264 -0
  39. package/src/server/adapters/local-adapter.ts +968 -0
  40. package/src/server/adapters/types.ts +293 -0
  41. package/src/server/config.ts +73 -0
  42. package/src/server/db/__tests__/queries.test.ts +472 -0
  43. package/src/server/db/ids.ts +17 -0
  44. package/src/server/db/index.ts +69 -0
  45. package/src/server/db/queries.ts +142 -0
  46. package/src/server/db/refs.ts +60 -0
  47. package/src/server/db/schema.ts +88 -0
  48. package/src/server/db/sqlite.ts +10 -0
  49. package/src/server/index.ts +83 -0
  50. package/src/server/tools/__tests__/crud.test.ts +301 -0
  51. package/src/server/tools/__tests__/get-version.test.ts +27 -0
  52. package/src/server/tools/__tests__/mcp-interface.test.ts +388 -0
  53. package/src/server/tools/__tests__/query.test.ts +353 -0
  54. package/src/server/tools/__tests__/z-configure-linear.test.ts +511 -0
  55. package/src/server/tools/__tests__/z-get-linear-url.test.ts +108 -0
  56. package/src/server/tools/configure-linear.ts +373 -0
  57. package/src/server/tools/create-epic.ts +35 -0
  58. package/src/server/tools/create-prd.ts +31 -0
  59. package/src/server/tools/create-task.ts +38 -0
  60. package/src/server/tools/criteria.ts +50 -0
  61. package/src/server/tools/delete-entity.ts +76 -0
  62. package/src/server/tools/dependencies.ts +55 -0
  63. package/src/server/tools/get-entity.ts +238 -0
  64. package/src/server/tools/get-linear-url.ts +28 -0
  65. package/src/server/tools/get-project-context.ts +33 -0
  66. package/src/server/tools/get-stats.ts +52 -0
  67. package/src/server/tools/get-version.ts +20 -0
  68. package/src/server/tools/index.ts +114 -0
  69. package/src/server/tools/init-project.ts +108 -0
  70. package/src/server/tools/query-entities.ts +167 -0
  71. package/src/server/tools/render-status.ts +201 -0
  72. package/src/server/tools/update-entity.ts +140 -0
  73. package/src/server/tools/update-status.ts +166 -0
  74. package/src/server/utils/__tests__/mcp-response.test.ts +331 -0
  75. package/src/server/utils/logger.ts +9 -0
  76. package/src/server/utils/mcp-response.ts +254 -0
  77. package/src/server/utils/status-transitions.ts +160 -0
  78. package/src/status-line/__tests__/status-line.test.ts +215 -0
  79. package/src/status-line/index.ts +147 -0
  80. package/src/utils/__tests__/chalk-import.test.ts +32 -0
  81. package/src/utils/__tests__/display.test.ts +97 -0
  82. package/src/utils/__tests__/status-renderer.test.ts +310 -0
  83. package/src/utils/display.ts +62 -0
  84. package/src/utils/status-renderer.ts +188 -0
  85. package/src/version.ts +5 -0
  86. package/dist/server/index.js +0 -87063
@@ -0,0 +1,331 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import type {
3
+ AcceptanceCriterion,
4
+ CascadeResult,
5
+ Epic,
6
+ PaginatedResult,
7
+ Prd,
8
+ Stats,
9
+ Task,
10
+ } from "../../adapters/types.js";
11
+ import {
12
+ toMcpCascadeResult,
13
+ toMcpCriterion,
14
+ toMcpEpic,
15
+ toMcpPaginatedEpics,
16
+ toMcpPaginatedPrds,
17
+ toMcpPaginatedTasks,
18
+ toMcpPrd,
19
+ toMcpStats,
20
+ toMcpTask,
21
+ } from "../mcp-response.js";
22
+
23
+ describe("MCP Response Transformations", () => {
24
+ describe("toMcpPrd", () => {
25
+ test("transforms PRD with all fields", () => {
26
+ const prd: Prd = {
27
+ id: "prd_123",
28
+ projectId: "proj_456",
29
+ ref: "TEST-P1",
30
+ title: "Test PRD",
31
+ description: "A description",
32
+ status: "DRAFT",
33
+ tag: "mvp",
34
+ folderPath: ".flux/prds/test",
35
+ createdAt: "2024-01-01T00:00:00Z",
36
+ updatedAt: "2024-01-02T00:00:00Z",
37
+ };
38
+
39
+ const result = toMcpPrd(prd);
40
+
41
+ expect(result.id).toBe("prd_123");
42
+ expect(result.project_id).toBe("proj_456");
43
+ expect(result.ref).toBe("TEST-P1");
44
+ expect(result.title).toBe("Test PRD");
45
+ expect(result.description).toBe("A description");
46
+ expect(result.status).toBe("DRAFT");
47
+ expect(result.tag).toBe("mvp");
48
+ expect(result.folder_path).toBe(".flux/prds/test");
49
+ expect(result.created_at).toBe("2024-01-01T00:00:00Z");
50
+ expect(result.updated_at).toBe("2024-01-02T00:00:00Z");
51
+ });
52
+
53
+ test("transforms PRD with optional fields undefined", () => {
54
+ const prd: Prd = {
55
+ id: "prd_123",
56
+ projectId: "proj_456",
57
+ ref: "TEST-P1",
58
+ title: "Test PRD",
59
+ status: "DRAFT",
60
+ createdAt: "2024-01-01T00:00:00Z",
61
+ updatedAt: "2024-01-02T00:00:00Z",
62
+ };
63
+
64
+ const result = toMcpPrd(prd);
65
+
66
+ expect(result.description).toBeUndefined();
67
+ expect(result.tag).toBeUndefined();
68
+ expect(result.folder_path).toBeUndefined();
69
+ });
70
+ });
71
+
72
+ describe("toMcpEpic", () => {
73
+ test("transforms Epic with all fields", () => {
74
+ const epic: Epic = {
75
+ id: "epic_123",
76
+ prdId: "prd_456",
77
+ ref: "TEST-E1",
78
+ title: "Test Epic",
79
+ description: "Epic description",
80
+ status: "IN_PROGRESS",
81
+ createdAt: "2024-01-01T00:00:00Z",
82
+ updatedAt: "2024-01-02T00:00:00Z",
83
+ };
84
+
85
+ const result = toMcpEpic(epic);
86
+
87
+ expect(result.id).toBe("epic_123");
88
+ expect(result.prd_id).toBe("prd_456");
89
+ expect(result.ref).toBe("TEST-E1");
90
+ expect(result.title).toBe("Test Epic");
91
+ expect(result.description).toBe("Epic description");
92
+ expect(result.status).toBe("IN_PROGRESS");
93
+ expect(result.created_at).toBe("2024-01-01T00:00:00Z");
94
+ expect(result.updated_at).toBe("2024-01-02T00:00:00Z");
95
+ });
96
+ });
97
+
98
+ describe("toMcpTask", () => {
99
+ test("transforms Task with all fields", () => {
100
+ const task: Task = {
101
+ id: "task_123",
102
+ epicId: "epic_456",
103
+ ref: "TEST-T1",
104
+ title: "Test Task",
105
+ description: "Task description",
106
+ status: "PENDING",
107
+ priority: "HIGH",
108
+ createdAt: "2024-01-01T00:00:00Z",
109
+ updatedAt: "2024-01-02T00:00:00Z",
110
+ };
111
+
112
+ const result = toMcpTask(task);
113
+
114
+ expect(result.id).toBe("task_123");
115
+ expect(result.epic_id).toBe("epic_456");
116
+ expect(result.ref).toBe("TEST-T1");
117
+ expect(result.title).toBe("Test Task");
118
+ expect(result.description).toBe("Task description");
119
+ expect(result.status).toBe("PENDING");
120
+ expect(result.priority).toBe("HIGH");
121
+ expect(result.created_at).toBe("2024-01-01T00:00:00Z");
122
+ expect(result.updated_at).toBe("2024-01-02T00:00:00Z");
123
+ });
124
+ });
125
+
126
+ describe("toMcpCriterion", () => {
127
+ test("transforms AcceptanceCriterion", () => {
128
+ const criterion: AcceptanceCriterion = {
129
+ id: "ac_123",
130
+ parentType: "epic",
131
+ parentId: "epic_456",
132
+ criteria: "Should work correctly",
133
+ isMet: true,
134
+ createdAt: "2024-01-01T00:00:00Z",
135
+ };
136
+
137
+ const result = toMcpCriterion(criterion);
138
+
139
+ expect(result.id).toBe("ac_123");
140
+ expect(result.parent_type).toBe("epic");
141
+ expect(result.parent_id).toBe("epic_456");
142
+ expect(result.criteria).toBe("Should work correctly");
143
+ expect(result.is_met).toBe(true);
144
+ expect(result.created_at).toBe("2024-01-01T00:00:00Z");
145
+ });
146
+
147
+ test("transforms criterion with is_met false", () => {
148
+ const criterion: AcceptanceCriterion = {
149
+ id: "ac_123",
150
+ parentType: "task",
151
+ parentId: "task_456",
152
+ criteria: "Not done yet",
153
+ isMet: false,
154
+ createdAt: "2024-01-01T00:00:00Z",
155
+ };
156
+
157
+ const result = toMcpCriterion(criterion);
158
+
159
+ expect(result.is_met).toBe(false);
160
+ expect(result.parent_type).toBe("task");
161
+ });
162
+ });
163
+
164
+ describe("toMcpStats", () => {
165
+ test("transforms Stats with all status counts", () => {
166
+ const stats: Stats = {
167
+ prds: {
168
+ total: 10,
169
+ draft: 3,
170
+ pendingReview: 2,
171
+ reviewed: 1,
172
+ approved: 2,
173
+ breakdownReady: 1,
174
+ completed: 1,
175
+ archived: 2,
176
+ },
177
+ epics: {
178
+ total: 20,
179
+ pending: 10,
180
+ inProgress: 5,
181
+ completed: 5,
182
+ },
183
+ tasks: {
184
+ total: 50,
185
+ pending: 25,
186
+ inProgress: 15,
187
+ completed: 10,
188
+ },
189
+ };
190
+
191
+ const result = toMcpStats(stats);
192
+
193
+ // PRD stats
194
+ expect(result.prds.total).toBe(10);
195
+ expect(result.prds.draft).toBe(3);
196
+ expect(result.prds.pending_review).toBe(2);
197
+ expect(result.prds.reviewed).toBe(1);
198
+ expect(result.prds.approved).toBe(2);
199
+ expect(result.prds.breakdown_ready).toBe(1);
200
+ expect(result.prds.completed).toBe(1);
201
+ expect(result.prds.archived).toBe(2);
202
+
203
+ // Epic stats
204
+ expect(result.epics.total).toBe(20);
205
+ expect(result.epics.pending).toBe(10);
206
+ expect(result.epics.in_progress).toBe(5);
207
+ expect(result.epics.completed).toBe(5);
208
+
209
+ // Task stats
210
+ expect(result.tasks.total).toBe(50);
211
+ expect(result.tasks.pending).toBe(25);
212
+ expect(result.tasks.in_progress).toBe(15);
213
+ expect(result.tasks.completed).toBe(10);
214
+ });
215
+ });
216
+
217
+ describe("toMcpPaginatedPrds", () => {
218
+ test("transforms paginated PRD result", () => {
219
+ const paginatedResult: PaginatedResult<Prd> = {
220
+ items: [
221
+ {
222
+ id: "prd_1",
223
+ projectId: "proj_1",
224
+ ref: "TEST-P1",
225
+ title: "PRD 1",
226
+ status: "DRAFT",
227
+ createdAt: "2024-01-01T00:00:00Z",
228
+ updatedAt: "2024-01-02T00:00:00Z",
229
+ },
230
+ {
231
+ id: "prd_2",
232
+ projectId: "proj_1",
233
+ ref: "TEST-P2",
234
+ title: "PRD 2",
235
+ status: "APPROVED",
236
+ createdAt: "2024-01-01T00:00:00Z",
237
+ updatedAt: "2024-01-02T00:00:00Z",
238
+ },
239
+ ],
240
+ total: 5,
241
+ limit: 2,
242
+ offset: 0,
243
+ hasMore: true,
244
+ };
245
+
246
+ const result = toMcpPaginatedPrds(paginatedResult);
247
+
248
+ expect(result.items.length).toBe(2);
249
+ expect(result.items[0].project_id).toBe("proj_1"); // camelCase -> snake_case
250
+ expect(result.total).toBe(5);
251
+ expect(result.limit).toBe(2);
252
+ expect(result.offset).toBe(0);
253
+ expect(result.has_more).toBe(true); // hasMore -> has_more
254
+ });
255
+ });
256
+
257
+ describe("toMcpPaginatedEpics", () => {
258
+ test("transforms paginated Epic result", () => {
259
+ const paginatedResult: PaginatedResult<Epic> = {
260
+ items: [
261
+ {
262
+ id: "epic_1",
263
+ prdId: "prd_1",
264
+ ref: "TEST-E1",
265
+ title: "Epic 1",
266
+ status: "PENDING",
267
+ createdAt: "2024-01-01T00:00:00Z",
268
+ updatedAt: "2024-01-02T00:00:00Z",
269
+ },
270
+ ],
271
+ total: 1,
272
+ limit: 10,
273
+ offset: 0,
274
+ hasMore: false,
275
+ };
276
+
277
+ const result = toMcpPaginatedEpics(paginatedResult);
278
+
279
+ expect(result.items.length).toBe(1);
280
+ expect(result.items[0].prd_id).toBe("prd_1");
281
+ expect(result.has_more).toBe(false);
282
+ });
283
+ });
284
+
285
+ describe("toMcpPaginatedTasks", () => {
286
+ test("transforms paginated Task result", () => {
287
+ const paginatedResult: PaginatedResult<Task> = {
288
+ items: [
289
+ {
290
+ id: "task_1",
291
+ epicId: "epic_1",
292
+ ref: "TEST-T1",
293
+ title: "Task 1",
294
+ status: "IN_PROGRESS",
295
+ priority: "MEDIUM",
296
+ createdAt: "2024-01-01T00:00:00Z",
297
+ updatedAt: "2024-01-02T00:00:00Z",
298
+ },
299
+ ],
300
+ total: 1,
301
+ limit: 10,
302
+ offset: 0,
303
+ hasMore: false,
304
+ };
305
+
306
+ const result = toMcpPaginatedTasks(paginatedResult);
307
+
308
+ expect(result.items.length).toBe(1);
309
+ expect(result.items[0].epic_id).toBe("epic_1");
310
+ expect(result.has_more).toBe(false);
311
+ });
312
+ });
313
+
314
+ describe("toMcpCascadeResult", () => {
315
+ test("transforms cascade deletion result", () => {
316
+ const cascade: CascadeResult = {
317
+ criteria: 5,
318
+ tasks: 10,
319
+ epics: 2,
320
+ dependencies: 3,
321
+ };
322
+
323
+ const result = toMcpCascadeResult(cascade);
324
+
325
+ expect(result.criteria).toBe(5);
326
+ expect(result.tasks).toBe(10);
327
+ expect(result.epics).toBe(2);
328
+ expect(result.dependencies).toBe(3);
329
+ });
330
+ });
331
+ });
@@ -0,0 +1,9 @@
1
+ export const logger = {
2
+ info: (msg: string, ...args: unknown[]) =>
3
+ console.error(`[INFO] ${msg}`, ...args),
4
+ error: (msg: string, ...args: unknown[]) =>
5
+ console.error(`[ERROR] ${msg}`, ...args),
6
+ debug: (msg: string, ...args: unknown[]) => {
7
+ if (process.env.DEBUG) console.error(`[DEBUG] ${msg}`, ...args);
8
+ },
9
+ };
@@ -0,0 +1,254 @@
1
+ /**
2
+ * MCP Response Utilities
3
+ *
4
+ * Transforms adapter responses (camelCase) to MCP format (snake_case).
5
+ * This ensures consistent API responses while keeping the adapter interface clean.
6
+ */
7
+
8
+ import type {
9
+ AcceptanceCriterion,
10
+ CascadeResult,
11
+ Epic,
12
+ PaginatedResult,
13
+ Prd,
14
+ Stats,
15
+ Task,
16
+ } from "../adapters/types.js";
17
+
18
+ // =============================================================================
19
+ // PRD Response Transformations
20
+ // =============================================================================
21
+
22
+ export interface McpPrd {
23
+ id: string;
24
+ project_id: string;
25
+ ref: string;
26
+ title: string;
27
+ description?: string;
28
+ status: string;
29
+ tag?: string;
30
+ folder_path?: string;
31
+ created_at: string;
32
+ updated_at: string;
33
+ }
34
+
35
+ export function toMcpPrd(prd: Prd): McpPrd {
36
+ return {
37
+ id: prd.id,
38
+ project_id: prd.projectId,
39
+ ref: prd.ref,
40
+ title: prd.title,
41
+ description: prd.description,
42
+ status: prd.status,
43
+ tag: prd.tag,
44
+ folder_path: prd.folderPath,
45
+ created_at: prd.createdAt,
46
+ updated_at: prd.updatedAt,
47
+ };
48
+ }
49
+
50
+ // =============================================================================
51
+ // Epic Response Transformations
52
+ // =============================================================================
53
+
54
+ export interface McpEpic {
55
+ id: string;
56
+ prd_id: string;
57
+ ref: string;
58
+ title: string;
59
+ description?: string;
60
+ status: string;
61
+ created_at: string;
62
+ updated_at: string;
63
+ }
64
+
65
+ export function toMcpEpic(epic: Epic): McpEpic {
66
+ return {
67
+ id: epic.id,
68
+ prd_id: epic.prdId,
69
+ ref: epic.ref,
70
+ title: epic.title,
71
+ description: epic.description,
72
+ status: epic.status,
73
+ created_at: epic.createdAt,
74
+ updated_at: epic.updatedAt,
75
+ };
76
+ }
77
+
78
+ // =============================================================================
79
+ // Task Response Transformations
80
+ // =============================================================================
81
+
82
+ export interface McpTask {
83
+ id: string;
84
+ epic_id: string;
85
+ ref: string;
86
+ title: string;
87
+ description?: string;
88
+ status: string;
89
+ priority: string;
90
+ created_at: string;
91
+ updated_at: string;
92
+ }
93
+
94
+ export function toMcpTask(task: Task): McpTask {
95
+ return {
96
+ id: task.id,
97
+ epic_id: task.epicId,
98
+ ref: task.ref,
99
+ title: task.title,
100
+ description: task.description,
101
+ status: task.status,
102
+ priority: task.priority,
103
+ created_at: task.createdAt,
104
+ updated_at: task.updatedAt,
105
+ };
106
+ }
107
+
108
+ // =============================================================================
109
+ // Acceptance Criteria Response Transformations
110
+ // =============================================================================
111
+
112
+ export interface McpCriterion {
113
+ id: string;
114
+ parent_type: string;
115
+ parent_id: string;
116
+ criteria: string;
117
+ is_met: boolean;
118
+ created_at: string;
119
+ }
120
+
121
+ export function toMcpCriterion(criterion: AcceptanceCriterion): McpCriterion {
122
+ return {
123
+ id: criterion.id,
124
+ parent_type: criterion.parentType,
125
+ parent_id: criterion.parentId,
126
+ criteria: criterion.criteria,
127
+ is_met: criterion.isMet,
128
+ created_at: criterion.createdAt,
129
+ };
130
+ }
131
+
132
+ // =============================================================================
133
+ // Stats Response Transformations
134
+ // =============================================================================
135
+
136
+ export interface McpStats {
137
+ prds: {
138
+ total: number;
139
+ draft: number;
140
+ pending_review: number;
141
+ reviewed: number;
142
+ approved: number;
143
+ breakdown_ready: number;
144
+ completed: number;
145
+ archived: number;
146
+ };
147
+ epics: {
148
+ total: number;
149
+ pending: number;
150
+ in_progress: number;
151
+ completed: number;
152
+ };
153
+ tasks: {
154
+ total: number;
155
+ pending: number;
156
+ in_progress: number;
157
+ completed: number;
158
+ };
159
+ }
160
+
161
+ export function toMcpStats(stats: Stats): McpStats {
162
+ return {
163
+ prds: {
164
+ total: stats.prds.total,
165
+ draft: stats.prds.draft,
166
+ pending_review: stats.prds.pendingReview,
167
+ reviewed: stats.prds.reviewed,
168
+ approved: stats.prds.approved,
169
+ breakdown_ready: stats.prds.breakdownReady,
170
+ completed: stats.prds.completed,
171
+ archived: stats.prds.archived,
172
+ },
173
+ epics: {
174
+ total: stats.epics.total,
175
+ pending: stats.epics.pending,
176
+ in_progress: stats.epics.inProgress,
177
+ completed: stats.epics.completed,
178
+ },
179
+ tasks: {
180
+ total: stats.tasks.total,
181
+ pending: stats.tasks.pending,
182
+ in_progress: stats.tasks.inProgress,
183
+ completed: stats.tasks.completed,
184
+ },
185
+ };
186
+ }
187
+
188
+ // =============================================================================
189
+ // Paginated Response Transformations
190
+ // =============================================================================
191
+
192
+ export interface McpPaginatedResult<T> {
193
+ items: T[];
194
+ total: number;
195
+ limit: number;
196
+ offset: number;
197
+ has_more: boolean;
198
+ }
199
+
200
+ export function toMcpPaginatedPrds(
201
+ result: PaginatedResult<Prd>,
202
+ ): McpPaginatedResult<McpPrd> {
203
+ return {
204
+ items: result.items.map(toMcpPrd),
205
+ total: result.total,
206
+ limit: result.limit,
207
+ offset: result.offset,
208
+ has_more: result.hasMore,
209
+ };
210
+ }
211
+
212
+ export function toMcpPaginatedEpics(
213
+ result: PaginatedResult<Epic>,
214
+ ): McpPaginatedResult<McpEpic> {
215
+ return {
216
+ items: result.items.map(toMcpEpic),
217
+ total: result.total,
218
+ limit: result.limit,
219
+ offset: result.offset,
220
+ has_more: result.hasMore,
221
+ };
222
+ }
223
+
224
+ export function toMcpPaginatedTasks(
225
+ result: PaginatedResult<Task>,
226
+ ): McpPaginatedResult<McpTask> {
227
+ return {
228
+ items: result.items.map(toMcpTask),
229
+ total: result.total,
230
+ limit: result.limit,
231
+ offset: result.offset,
232
+ has_more: result.hasMore,
233
+ };
234
+ }
235
+
236
+ // =============================================================================
237
+ // Cascade Result Transformation
238
+ // =============================================================================
239
+
240
+ export interface McpCascadeResult {
241
+ criteria: number;
242
+ tasks: number;
243
+ epics: number;
244
+ dependencies: number;
245
+ }
246
+
247
+ export function toMcpCascadeResult(cascade: CascadeResult): McpCascadeResult {
248
+ return {
249
+ criteria: cascade.criteria,
250
+ tasks: cascade.tasks,
251
+ epics: cascade.epics,
252
+ dependencies: cascade.dependencies,
253
+ };
254
+ }