@prmichaelsen/task-mcp 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 (142) hide show
  1. package/.env.example +19 -0
  2. package/AGENT.md +1165 -0
  3. package/CHANGELOG.md +72 -0
  4. package/agent/commands/acp.commit.md +511 -0
  5. package/agent/commands/acp.init.md +376 -0
  6. package/agent/commands/acp.package-install.md +347 -0
  7. package/agent/commands/acp.proceed.md +311 -0
  8. package/agent/commands/acp.report.md +392 -0
  9. package/agent/commands/acp.status.md +280 -0
  10. package/agent/commands/acp.sync.md +323 -0
  11. package/agent/commands/acp.update.md +301 -0
  12. package/agent/commands/acp.validate.md +385 -0
  13. package/agent/commands/acp.version-check-for-updates.md +275 -0
  14. package/agent/commands/acp.version-check.md +190 -0
  15. package/agent/commands/acp.version-update.md +288 -0
  16. package/agent/commands/command.template.md +273 -0
  17. package/agent/commands/git.commit.md +511 -0
  18. package/agent/commands/git.init.md +513 -0
  19. package/agent/design/.gitkeep +0 -0
  20. package/agent/design/acp-task-execution-requirements.md +555 -0
  21. package/agent/design/api-dto-design.md +394 -0
  22. package/agent/design/code-extraction-guide.md +827 -0
  23. package/agent/design/design.template.md +136 -0
  24. package/agent/design/requirements.template.md +387 -0
  25. package/agent/design/rest-api-integration.md +489 -0
  26. package/agent/design/sdk-export-requirements.md +549 -0
  27. package/agent/milestones/.gitkeep +0 -0
  28. package/agent/milestones/milestone-1-{title}.template.md +206 -0
  29. package/agent/milestones/milestone-2-task-infrastructure.md +232 -0
  30. package/agent/milestones/milestone-4-autonomous-execution.md +235 -0
  31. package/agent/patterns/.gitkeep +0 -0
  32. package/agent/patterns/bootstrap.md +1271 -0
  33. package/agent/patterns/bootstrap.template.md +1237 -0
  34. package/agent/patterns/pattern.template.md +364 -0
  35. package/agent/progress.template.yaml +158 -0
  36. package/agent/progress.yaml +375 -0
  37. package/agent/scripts/check-for-updates.sh +88 -0
  38. package/agent/scripts/install.sh +157 -0
  39. package/agent/scripts/uninstall.sh +75 -0
  40. package/agent/scripts/update.sh +139 -0
  41. package/agent/scripts/version.sh +35 -0
  42. package/agent/tasks/.gitkeep +0 -0
  43. package/agent/tasks/task-1-{title}.template.md +225 -0
  44. package/agent/tasks/task-86-task-data-model-schemas.md +143 -0
  45. package/agent/tasks/task-87-task-database-service.md +220 -0
  46. package/agent/tasks/task-88-firebase-client-wrapper.md +139 -0
  47. package/agent/tasks/task-88-task-execution-engine.md +277 -0
  48. package/agent/tasks/task-89-mcp-server-implementation.md +197 -0
  49. package/agent/tasks/task-90-build-configuration.md +146 -0
  50. package/agent/tasks/task-91-deployment-configuration.md +128 -0
  51. package/coverage/base.css +224 -0
  52. package/coverage/block-navigation.js +87 -0
  53. package/coverage/favicon.png +0 -0
  54. package/coverage/index.html +191 -0
  55. package/coverage/lcov-report/base.css +224 -0
  56. package/coverage/lcov-report/block-navigation.js +87 -0
  57. package/coverage/lcov-report/favicon.png +0 -0
  58. package/coverage/lcov-report/index.html +191 -0
  59. package/coverage/lcov-report/prettify.css +1 -0
  60. package/coverage/lcov-report/prettify.js +2 -0
  61. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  62. package/coverage/lcov-report/sorter.js +210 -0
  63. package/coverage/lcov-report/src/client.ts.html +1030 -0
  64. package/coverage/lcov-report/src/constant/collections.ts.html +469 -0
  65. package/coverage/lcov-report/src/constant/index.html +116 -0
  66. package/coverage/lcov-report/src/dto/index.html +116 -0
  67. package/coverage/lcov-report/src/dto/transformers.ts.html +568 -0
  68. package/coverage/lcov-report/src/index.html +146 -0
  69. package/coverage/lcov-report/src/schemas/index.html +116 -0
  70. package/coverage/lcov-report/src/schemas/task.ts.html +547 -0
  71. package/coverage/lcov-report/src/server-factory.ts.html +418 -0
  72. package/coverage/lcov-report/src/server.ts.html +289 -0
  73. package/coverage/lcov-report/src/services/index.html +116 -0
  74. package/coverage/lcov-report/src/services/task-database.service.ts.html +1495 -0
  75. package/coverage/lcov-report/src/tools/index.html +236 -0
  76. package/coverage/lcov-report/src/tools/index.ts.html +292 -0
  77. package/coverage/lcov-report/src/tools/task-add-message.ts.html +277 -0
  78. package/coverage/lcov-report/src/tools/task-complete-task-item.ts.html +343 -0
  79. package/coverage/lcov-report/src/tools/task-create-milestone.ts.html +286 -0
  80. package/coverage/lcov-report/src/tools/task-create-task-item.ts.html +358 -0
  81. package/coverage/lcov-report/src/tools/task-get-next-step.ts.html +460 -0
  82. package/coverage/lcov-report/src/tools/task-get-status.ts.html +316 -0
  83. package/coverage/lcov-report/src/tools/task-report-completion.ts.html +343 -0
  84. package/coverage/lcov-report/src/tools/task-update-progress.ts.html +232 -0
  85. package/coverage/lcov.info +974 -0
  86. package/coverage/prettify.css +1 -0
  87. package/coverage/prettify.js +2 -0
  88. package/coverage/sort-arrow-sprite.png +0 -0
  89. package/coverage/sorter.js +210 -0
  90. package/coverage/src/client.ts.html +1030 -0
  91. package/coverage/src/constant/collections.ts.html +469 -0
  92. package/coverage/src/constant/index.html +116 -0
  93. package/coverage/src/dto/index.html +116 -0
  94. package/coverage/src/dto/transformers.ts.html +568 -0
  95. package/coverage/src/index.html +146 -0
  96. package/coverage/src/schemas/index.html +116 -0
  97. package/coverage/src/schemas/task.ts.html +547 -0
  98. package/coverage/src/server-factory.ts.html +418 -0
  99. package/coverage/src/server.ts.html +289 -0
  100. package/coverage/src/services/index.html +116 -0
  101. package/coverage/src/services/task-database.service.ts.html +1495 -0
  102. package/coverage/src/tools/index.html +236 -0
  103. package/coverage/src/tools/index.ts.html +292 -0
  104. package/coverage/src/tools/task-add-message.ts.html +277 -0
  105. package/coverage/src/tools/task-complete-task-item.ts.html +343 -0
  106. package/coverage/src/tools/task-create-milestone.ts.html +286 -0
  107. package/coverage/src/tools/task-create-task-item.ts.html +358 -0
  108. package/coverage/src/tools/task-get-next-step.ts.html +460 -0
  109. package/coverage/src/tools/task-get-status.ts.html +316 -0
  110. package/coverage/src/tools/task-report-completion.ts.html +343 -0
  111. package/coverage/src/tools/task-update-progress.ts.html +232 -0
  112. package/firestore.rules +95 -0
  113. package/jest.config.js +31 -0
  114. package/package.json +67 -0
  115. package/src/client.spec.ts +199 -0
  116. package/src/client.ts +315 -0
  117. package/src/constant/collections.ts +128 -0
  118. package/src/dto/index.ts +47 -0
  119. package/src/dto/task-api.dto.ts +219 -0
  120. package/src/dto/transformers.spec.ts +462 -0
  121. package/src/dto/transformers.ts +161 -0
  122. package/src/schemas/task.ts +154 -0
  123. package/src/server-factory.spec.ts +70 -0
  124. package/src/server-factory.ts +111 -0
  125. package/src/server.ts +68 -0
  126. package/src/services/task-database.service.e2e.ts +116 -0
  127. package/src/services/task-database.service.spec.ts +479 -0
  128. package/src/services/task-database.service.ts +470 -0
  129. package/src/test-schemas.ts +161 -0
  130. package/src/tools/index.ts +69 -0
  131. package/src/tools/task-add-message.ts +64 -0
  132. package/src/tools/task-complete-task-item.ts +86 -0
  133. package/src/tools/task-create-milestone.ts +67 -0
  134. package/src/tools/task-create-task-item.ts +91 -0
  135. package/src/tools/task-get-next-step.spec.ts +136 -0
  136. package/src/tools/task-get-next-step.ts +125 -0
  137. package/src/tools/task-get-status.spec.ts +213 -0
  138. package/src/tools/task-get-status.ts +77 -0
  139. package/src/tools/task-report-completion.ts +86 -0
  140. package/src/tools/task-update-progress.ts +49 -0
  141. package/src/tools/tools.spec.ts +194 -0
  142. package/tsconfig.json +31 -0
@@ -0,0 +1,462 @@
1
+ /**
2
+ * DTO Transformers Unit Tests
3
+ *
4
+ * Tests for schema-to-DTO transformation functions
5
+ */
6
+
7
+ import type { Task, TaskMessage, Milestone, TaskItem } from '../schemas/task.js'
8
+ import {
9
+ toTaskItemApiResponse,
10
+ toMilestoneApiResponse,
11
+ toTaskProgressApiResponse,
12
+ toTaskConfigApiResponse,
13
+ toTaskMetadataApiResponse,
14
+ toTaskApiResponse,
15
+ toTaskMessageApiResponse,
16
+ toTaskListApiResponse,
17
+ toTaskMessageListApiResponse
18
+ } from './transformers.js'
19
+
20
+ describe('DTO Transformers', () => {
21
+ describe('toTaskItemApiResponse', () => {
22
+ it('should transform task item with all fields', () => {
23
+ const item: TaskItem = {
24
+ id: 'item-1',
25
+ name: 'Task Item 1',
26
+ description: 'Description 1',
27
+ status: 'completed',
28
+ estimated_hours: 4,
29
+ completed_at: '2026-02-16T12:00:00Z',
30
+ notes: 'Some notes'
31
+ }
32
+
33
+ const result = toTaskItemApiResponse(item)
34
+
35
+ expect(result).toEqual({
36
+ id: 'item-1',
37
+ name: 'Task Item 1',
38
+ description: 'Description 1',
39
+ status: 'completed',
40
+ estimated_hours: 4,
41
+ completed_at: '2026-02-16T12:00:00Z',
42
+ notes: 'Some notes'
43
+ })
44
+ })
45
+
46
+ it('should transform task item with optional fields undefined', () => {
47
+ const item: TaskItem = {
48
+ id: 'item-2',
49
+ name: 'Task Item 2',
50
+ description: 'Description 2',
51
+ status: 'not_started'
52
+ }
53
+
54
+ const result = toTaskItemApiResponse(item)
55
+
56
+ expect(result).toEqual({
57
+ id: 'item-2',
58
+ name: 'Task Item 2',
59
+ description: 'Description 2',
60
+ status: 'not_started',
61
+ estimated_hours: undefined,
62
+ completed_at: undefined,
63
+ notes: undefined
64
+ })
65
+ })
66
+ })
67
+
68
+ describe('toMilestoneApiResponse', () => {
69
+ it('should transform milestone with all fields', () => {
70
+ const milestone: Milestone = {
71
+ id: 'milestone-1',
72
+ name: 'Milestone 1',
73
+ description: 'Description 1',
74
+ status: 'completed',
75
+ progress: 100,
76
+ tasks_completed: 5,
77
+ tasks_total: 5,
78
+ started_at: '2026-02-15T10:00:00Z',
79
+ completed_at: '2026-02-16T12:00:00Z'
80
+ }
81
+
82
+ const result = toMilestoneApiResponse(milestone)
83
+
84
+ expect(result).toEqual({
85
+ id: 'milestone-1',
86
+ name: 'Milestone 1',
87
+ description: 'Description 1',
88
+ status: 'completed',
89
+ progress: 100,
90
+ tasks_completed: 5,
91
+ tasks_total: 5,
92
+ started_at: '2026-02-15T10:00:00Z',
93
+ completed_at: '2026-02-16T12:00:00Z'
94
+ })
95
+ })
96
+
97
+ it('should transform milestone with optional fields undefined', () => {
98
+ const milestone: Milestone = {
99
+ id: 'milestone-2',
100
+ name: 'Milestone 2',
101
+ description: 'Description 2',
102
+ status: 'not_started',
103
+ progress: 0,
104
+ tasks_completed: 0,
105
+ tasks_total: 3
106
+ }
107
+
108
+ const result = toMilestoneApiResponse(milestone)
109
+
110
+ expect(result.started_at).toBeUndefined()
111
+ expect(result.completed_at).toBeUndefined()
112
+ })
113
+ })
114
+
115
+ describe('toTaskProgressApiResponse', () => {
116
+ it('should transform task progress with milestones and tasks', () => {
117
+ const progress: Task['progress'] = {
118
+ current_milestone: 'milestone-1',
119
+ current_task: 'task-1',
120
+ overall_percentage: 50,
121
+ milestones: [
122
+ {
123
+ id: 'milestone-1',
124
+ name: 'Milestone 1',
125
+ description: 'Description 1',
126
+ status: 'in_progress',
127
+ progress: 50,
128
+ tasks_completed: 2,
129
+ tasks_total: 4
130
+ }
131
+ ],
132
+ tasks: {
133
+ 'milestone-1': [
134
+ {
135
+ id: 'task-1',
136
+ name: 'Task 1',
137
+ description: 'Description 1',
138
+ status: 'completed',
139
+ completed_at: '2026-02-16T12:00:00Z'
140
+ },
141
+ {
142
+ id: 'task-2',
143
+ name: 'Task 2',
144
+ description: 'Description 2',
145
+ status: 'in_progress'
146
+ }
147
+ ]
148
+ }
149
+ }
150
+
151
+ const result = toTaskProgressApiResponse(progress)
152
+
153
+ expect(result.current_milestone).toBe('milestone-1')
154
+ expect(result.current_task).toBe('task-1')
155
+ expect(result.overall_percentage).toBe(50)
156
+ expect(result.milestones).toHaveLength(1)
157
+ expect(result.milestones[0].id).toBe('milestone-1')
158
+ expect(result.tasks['milestone-1']).toHaveLength(2)
159
+ expect(result.tasks['milestone-1'][0].id).toBe('task-1')
160
+ })
161
+ })
162
+
163
+ describe('toTaskConfigApiResponse', () => {
164
+ it('should transform task config with all fields', () => {
165
+ const config: Task['config'] = {
166
+ system_prompt: 'Custom prompt',
167
+ auto_approve: false,
168
+ max_iterations: 100
169
+ }
170
+
171
+ const result = toTaskConfigApiResponse(config)
172
+
173
+ expect(result).toEqual({
174
+ system_prompt: 'Custom prompt',
175
+ auto_approve: false,
176
+ max_iterations: 100
177
+ })
178
+ })
179
+
180
+ it('should transform task config with optional fields undefined', () => {
181
+ const config: Task['config'] = {
182
+ system_prompt: '',
183
+ auto_approve: true
184
+ }
185
+
186
+ const result = toTaskConfigApiResponse(config)
187
+
188
+ expect(result.system_prompt).toBe('')
189
+ expect(result.auto_approve).toBe(true)
190
+ expect(result.max_iterations).toBeUndefined()
191
+ })
192
+ })
193
+
194
+ describe('toTaskMetadataApiResponse', () => {
195
+ it('should transform metadata with all fields', () => {
196
+ const metadata: Task['metadata'] = {
197
+ conversation_id: 'conv-123',
198
+ parent_task_id: 'task-parent',
199
+ tags: ['urgent', 'backend']
200
+ }
201
+
202
+ const result = toTaskMetadataApiResponse(metadata)
203
+
204
+ expect(result).toEqual({
205
+ conversation_id: 'conv-123',
206
+ parent_task_id: 'task-parent',
207
+ tags: ['urgent', 'backend']
208
+ })
209
+ })
210
+
211
+ it('should return undefined for undefined metadata', () => {
212
+ const result = toTaskMetadataApiResponse(undefined)
213
+ expect(result).toBeUndefined()
214
+ })
215
+
216
+ it('should transform metadata with optional fields undefined', () => {
217
+ const metadata: Task['metadata'] = {}
218
+
219
+ const result = toTaskMetadataApiResponse(metadata)
220
+
221
+ expect(result).toEqual({
222
+ conversation_id: undefined,
223
+ parent_task_id: undefined,
224
+ tags: undefined
225
+ })
226
+ })
227
+ })
228
+
229
+ describe('toTaskApiResponse', () => {
230
+ it('should transform complete task and exclude execution field', () => {
231
+ const task: Task = {
232
+ id: 'task-123',
233
+ user_id: 'user-456',
234
+ title: 'Test Task',
235
+ description: 'Test Description',
236
+ status: 'in_progress',
237
+ created_at: '2026-02-16T10:00:00Z',
238
+ updated_at: '2026-02-16T12:00:00Z',
239
+ started_at: '2026-02-16T10:30:00Z',
240
+ progress: {
241
+ current_milestone: 'milestone-1',
242
+ current_task: 'task-1',
243
+ overall_percentage: 25,
244
+ milestones: [],
245
+ tasks: {}
246
+ },
247
+ execution: {
248
+ api_messages: [{ role: 'user', content: 'secret' }],
249
+ task_messages: [],
250
+ tool_results: [{ tool: 'secret_tool', result: 'secret' }]
251
+ },
252
+ config: {
253
+ system_prompt: 'Test prompt',
254
+ auto_approve: true,
255
+ max_iterations: 500
256
+ },
257
+ metadata: {
258
+ conversation_id: 'conv-123'
259
+ }
260
+ }
261
+
262
+ const result = toTaskApiResponse(task)
263
+
264
+ // Verify all expected fields are present
265
+ expect(result.id).toBe('task-123')
266
+ expect(result.user_id).toBe('user-456')
267
+ expect(result.title).toBe('Test Task')
268
+ expect(result.description).toBe('Test Description')
269
+ expect(result.status).toBe('in_progress')
270
+ expect(result.created_at).toBe('2026-02-16T10:00:00Z')
271
+ expect(result.updated_at).toBe('2026-02-16T12:00:00Z')
272
+ expect(result.started_at).toBe('2026-02-16T10:30:00Z')
273
+ expect(result.progress).toBeDefined()
274
+ expect(result.config).toBeDefined()
275
+ expect(result.metadata).toBeDefined()
276
+
277
+ // Verify execution field is NOT present
278
+ expect((result as any).execution).toBeUndefined()
279
+ })
280
+
281
+ it('should transform task without optional fields', () => {
282
+ const task: Task = {
283
+ id: 'task-456',
284
+ user_id: 'user-789',
285
+ title: 'Simple Task',
286
+ description: 'Simple Description',
287
+ status: 'not_started',
288
+ created_at: '2026-02-16T10:00:00Z',
289
+ updated_at: '2026-02-16T10:00:00Z',
290
+ progress: {
291
+ current_milestone: '',
292
+ current_task: '',
293
+ overall_percentage: 0,
294
+ milestones: [],
295
+ tasks: {}
296
+ },
297
+ execution: {
298
+ api_messages: [],
299
+ task_messages: [],
300
+ tool_results: []
301
+ },
302
+ config: {
303
+ system_prompt: '',
304
+ auto_approve: true
305
+ }
306
+ }
307
+
308
+ const result = toTaskApiResponse(task)
309
+
310
+ expect(result.started_at).toBeUndefined()
311
+ expect(result.completed_at).toBeUndefined()
312
+ expect(result.metadata).toBeUndefined()
313
+ expect((result as any).execution).toBeUndefined()
314
+ })
315
+ })
316
+
317
+ describe('toTaskMessageApiResponse', () => {
318
+ it('should transform task message with all fields', () => {
319
+ const message: TaskMessage = {
320
+ id: 'msg-123',
321
+ task_id: 'task-456',
322
+ role: 'assistant',
323
+ content: 'Test message content',
324
+ timestamp: '2026-02-16T12:00:00Z',
325
+ metadata: { source: 'agent' }
326
+ }
327
+
328
+ const result = toTaskMessageApiResponse(message)
329
+
330
+ expect(result).toEqual({
331
+ id: 'msg-123',
332
+ task_id: 'task-456',
333
+ role: 'assistant',
334
+ content: 'Test message content',
335
+ timestamp: '2026-02-16T12:00:00Z',
336
+ metadata: { source: 'agent' }
337
+ })
338
+ })
339
+
340
+ it('should transform task message without metadata', () => {
341
+ const message: TaskMessage = {
342
+ id: 'msg-456',
343
+ task_id: 'task-789',
344
+ role: 'user',
345
+ content: 'User message',
346
+ timestamp: '2026-02-16T11:00:00Z'
347
+ }
348
+
349
+ const result = toTaskMessageApiResponse(message)
350
+
351
+ expect(result.metadata).toBeUndefined()
352
+ })
353
+ })
354
+
355
+ describe('toTaskListApiResponse', () => {
356
+ it('should transform array of tasks to list response', () => {
357
+ const tasks: Task[] = [
358
+ {
359
+ id: 'task-1',
360
+ user_id: 'user-1',
361
+ title: 'Task 1',
362
+ description: 'Description 1',
363
+ status: 'completed',
364
+ created_at: '2026-02-16T10:00:00Z',
365
+ updated_at: '2026-02-16T12:00:00Z',
366
+ progress: {
367
+ current_milestone: '',
368
+ current_task: '',
369
+ overall_percentage: 100,
370
+ milestones: [],
371
+ tasks: {}
372
+ },
373
+ execution: {
374
+ api_messages: [],
375
+ task_messages: [],
376
+ tool_results: []
377
+ },
378
+ config: {
379
+ system_prompt: '',
380
+ auto_approve: true
381
+ }
382
+ },
383
+ {
384
+ id: 'task-2',
385
+ user_id: 'user-1',
386
+ title: 'Task 2',
387
+ description: 'Description 2',
388
+ status: 'in_progress',
389
+ created_at: '2026-02-16T11:00:00Z',
390
+ updated_at: '2026-02-16T12:00:00Z',
391
+ progress: {
392
+ current_milestone: '',
393
+ current_task: '',
394
+ overall_percentage: 50,
395
+ milestones: [],
396
+ tasks: {}
397
+ },
398
+ execution: {
399
+ api_messages: [],
400
+ task_messages: [],
401
+ tool_results: []
402
+ },
403
+ config: {
404
+ system_prompt: '',
405
+ auto_approve: true
406
+ }
407
+ }
408
+ ]
409
+
410
+ const result = toTaskListApiResponse(tasks)
411
+
412
+ expect(result.tasks).toHaveLength(2)
413
+ expect(result.total).toBe(2)
414
+ expect(result.tasks[0].id).toBe('task-1')
415
+ expect(result.tasks[1].id).toBe('task-2')
416
+ expect((result.tasks[0] as any).execution).toBeUndefined()
417
+ expect((result.tasks[1] as any).execution).toBeUndefined()
418
+ })
419
+
420
+ it('should handle empty task array', () => {
421
+ const result = toTaskListApiResponse([])
422
+
423
+ expect(result.tasks).toHaveLength(0)
424
+ expect(result.total).toBe(0)
425
+ })
426
+ })
427
+
428
+ describe('toTaskMessageListApiResponse', () => {
429
+ it('should transform array of messages to list response', () => {
430
+ const messages: TaskMessage[] = [
431
+ {
432
+ id: 'msg-1',
433
+ task_id: 'task-1',
434
+ role: 'user',
435
+ content: 'Message 1',
436
+ timestamp: '2026-02-16T10:00:00Z'
437
+ },
438
+ {
439
+ id: 'msg-2',
440
+ task_id: 'task-1',
441
+ role: 'assistant',
442
+ content: 'Message 2',
443
+ timestamp: '2026-02-16T10:01:00Z'
444
+ }
445
+ ]
446
+
447
+ const result = toTaskMessageListApiResponse(messages)
448
+
449
+ expect(result.messages).toHaveLength(2)
450
+ expect(result.total).toBe(2)
451
+ expect(result.messages[0].id).toBe('msg-1')
452
+ expect(result.messages[1].id).toBe('msg-2')
453
+ })
454
+
455
+ it('should handle empty message array', () => {
456
+ const result = toTaskMessageListApiResponse([])
457
+
458
+ expect(result.messages).toHaveLength(0)
459
+ expect(result.total).toBe(0)
460
+ })
461
+ })
462
+ })
@@ -0,0 +1,161 @@
1
+ /**
2
+ * DTO Transformers
3
+ *
4
+ * Functions to transform internal schemas to API response DTOs.
5
+ * These transformers exclude internal fields and ensure API responses
6
+ * match the expected structure for agentbase.me.
7
+ */
8
+
9
+ import type { Task, TaskMessage, Milestone, TaskItem } from '../schemas/task.js'
10
+ import type {
11
+ TaskApiResponse,
12
+ TaskMessageApiResponse,
13
+ MilestoneApiResponse,
14
+ TaskItemApiResponse,
15
+ TaskProgressApiResponse,
16
+ TaskConfigApiResponse,
17
+ TaskMetadataApiResponse
18
+ } from './task-api.dto.js'
19
+
20
+ /**
21
+ * Transform Task Item schema to API response DTO
22
+ */
23
+ export function toTaskItemApiResponse(item: TaskItem): TaskItemApiResponse {
24
+ return {
25
+ id: item.id,
26
+ name: item.name,
27
+ description: item.description,
28
+ status: item.status,
29
+ estimated_hours: item.estimated_hours,
30
+ completed_at: item.completed_at,
31
+ notes: item.notes
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Transform Milestone schema to API response DTO
37
+ */
38
+ export function toMilestoneApiResponse(milestone: Milestone): MilestoneApiResponse {
39
+ return {
40
+ id: milestone.id,
41
+ name: milestone.name,
42
+ description: milestone.description,
43
+ status: milestone.status,
44
+ progress: milestone.progress,
45
+ tasks_completed: milestone.tasks_completed,
46
+ tasks_total: milestone.tasks_total,
47
+ started_at: milestone.started_at,
48
+ completed_at: milestone.completed_at
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Transform Task Progress schema to API response DTO
54
+ */
55
+ export function toTaskProgressApiResponse(progress: Task['progress']): TaskProgressApiResponse {
56
+ return {
57
+ current_milestone: progress.current_milestone,
58
+ current_task: progress.current_task,
59
+ overall_percentage: progress.overall_percentage,
60
+ milestones: progress.milestones.map(toMilestoneApiResponse),
61
+ tasks: Object.fromEntries(
62
+ Object.entries(progress.tasks).map(([milestoneId, items]) => [
63
+ milestoneId,
64
+ items.map(toTaskItemApiResponse)
65
+ ])
66
+ )
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Transform Task Config schema to API response DTO
72
+ */
73
+ export function toTaskConfigApiResponse(config: Task['config']): TaskConfigApiResponse {
74
+ return {
75
+ system_prompt: config.system_prompt,
76
+ auto_approve: config.auto_approve,
77
+ max_iterations: config.max_iterations
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Transform Task Metadata schema to API response DTO
83
+ */
84
+ export function toTaskMetadataApiResponse(
85
+ metadata: Task['metadata']
86
+ ): TaskMetadataApiResponse | undefined {
87
+ if (!metadata) {
88
+ return undefined
89
+ }
90
+
91
+ return {
92
+ conversation_id: metadata.conversation_id,
93
+ parent_task_id: metadata.parent_task_id,
94
+ tags: metadata.tags
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Transform internal Task schema to API response DTO
100
+ * Excludes internal execution details (api_messages, tool_results)
101
+ */
102
+ export function toTaskApiResponse(task: Task): TaskApiResponse {
103
+ return {
104
+ id: task.id,
105
+ user_id: task.user_id,
106
+ title: task.title,
107
+ description: task.description,
108
+ status: task.status,
109
+ created_at: task.created_at,
110
+ updated_at: task.updated_at,
111
+ started_at: task.started_at,
112
+ completed_at: task.completed_at,
113
+
114
+ progress: toTaskProgressApiResponse(task.progress),
115
+ config: toTaskConfigApiResponse(task.config),
116
+ metadata: toTaskMetadataApiResponse(task.metadata)
117
+
118
+ // execution field is intentionally excluded
119
+ // Internal fields (api_messages, tool_results) are not exposed
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Transform internal TaskMessage schema to API response DTO
125
+ */
126
+ export function toTaskMessageApiResponse(message: TaskMessage): TaskMessageApiResponse {
127
+ return {
128
+ id: message.id,
129
+ task_id: message.task_id,
130
+ role: message.role,
131
+ content: message.content,
132
+ timestamp: message.timestamp,
133
+ metadata: message.metadata
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Transform array of tasks to list response DTO
139
+ */
140
+ export function toTaskListApiResponse(tasks: Task[]): {
141
+ tasks: TaskApiResponse[]
142
+ total: number
143
+ } {
144
+ return {
145
+ tasks: tasks.map(toTaskApiResponse),
146
+ total: tasks.length
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Transform array of messages to list response DTO
152
+ */
153
+ export function toTaskMessageListApiResponse(messages: TaskMessage[]): {
154
+ messages: TaskMessageApiResponse[]
155
+ total: number
156
+ } {
157
+ return {
158
+ messages: messages.map(toTaskMessageApiResponse),
159
+ total: messages.length
160
+ }
161
+ }