@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,479 @@
1
+ /**
2
+ * Unit Tests for TaskDatabaseService
3
+ *
4
+ * Tests all CRUD operations, message operations, and progress tracking
5
+ * with mocked Firestore.
6
+ */
7
+
8
+ import { describe, it, expect, jest, beforeAll, beforeEach } from '@jest/globals'
9
+ import { TaskDatabaseService } from '../../src/services/task-database.service.js'
10
+ import type { Task, Milestone, TaskItem } from '../../src/schemas/task.js'
11
+
12
+ // Mock Firestore
13
+ const mockGet = jest.fn<any>()
14
+ const mockAdd = jest.fn<any>()
15
+ const mockUpdate = jest.fn<any>()
16
+ const mockDelete = jest.fn<any>()
17
+ const mockWhere = jest.fn<any>()
18
+ const mockOrderBy = jest.fn<any>()
19
+ const mockLimit = jest.fn<any>()
20
+ const mockBatch = jest.fn<any>()
21
+ const mockCommit = jest.fn<any>()
22
+
23
+ const mockDoc = jest.fn<any>(() => ({
24
+ get: mockGet,
25
+ update: mockUpdate,
26
+ delete: mockDelete
27
+ }))
28
+
29
+ const mockCollection = jest.fn<any>(() => ({
30
+ add: mockAdd,
31
+ get: mockGet,
32
+ where: mockWhere,
33
+ orderBy: mockOrderBy,
34
+ limit: mockLimit,
35
+ doc: mockDoc
36
+ }))
37
+
38
+ const mockDb = {
39
+ collection: mockCollection,
40
+ doc: mockDoc,
41
+ batch: mockBatch
42
+ } as any
43
+
44
+ // Mock firebase-admin/firestore
45
+ jest.mock('firebase-admin/firestore', () => ({
46
+ getFirestore: jest.fn(() => mockDb),
47
+ FieldValue: {
48
+ arrayUnion: jest.fn((value: any) => ({ _methodName: 'arrayUnion', _elements: [value] }))
49
+ }
50
+ }))
51
+
52
+ describe('TaskDatabaseService', () => {
53
+ beforeAll(() => {
54
+ TaskDatabaseService.initialize(mockDb)
55
+ })
56
+
57
+ beforeEach(() => {
58
+ jest.clearAllMocks()
59
+
60
+ // Setup default mock chain
61
+ mockWhere.mockReturnThis()
62
+ mockOrderBy.mockReturnThis()
63
+ mockLimit.mockReturnThis()
64
+ mockBatch.mockReturnValue({
65
+ delete: jest.fn().mockReturnThis(),
66
+ commit: mockCommit
67
+ })
68
+ })
69
+
70
+ describe('createTask', () => {
71
+ it('should create a task with default config', async () => {
72
+ const mockTaskId = 'task-123'
73
+ mockAdd.mockResolvedValue({ id: mockTaskId })
74
+
75
+ const task = await TaskDatabaseService.createTask(
76
+ 'user-456',
77
+ 'Test Task',
78
+ 'Test Description'
79
+ )
80
+
81
+ expect(task.id).toBe(mockTaskId)
82
+ expect(task.title).toBe('Test Task')
83
+ expect(task.description).toBe('Test Description')
84
+ expect(task.user_id).toBe('user-456')
85
+ expect(task.status).toBe('not_started')
86
+ expect(task.config.auto_approve).toBe(true)
87
+ expect(mockCollection).toHaveBeenCalledWith('users/user-456/tasks')
88
+ expect(mockAdd).toHaveBeenCalled()
89
+ })
90
+
91
+ it('should create a task with custom config', async () => {
92
+ const mockTaskId = 'task-789'
93
+ mockAdd.mockResolvedValue({ id: mockTaskId })
94
+
95
+ const task = await TaskDatabaseService.createTask(
96
+ 'user-456',
97
+ 'Custom Task',
98
+ 'Custom Description',
99
+ {
100
+
101
+ auto_approve: false,
102
+ max_iterations: 50
103
+ }
104
+ )
105
+
106
+ expect(task.config.auto_approve).toBe(false)
107
+ expect(task.config.max_iterations).toBe(50)
108
+ })
109
+ })
110
+
111
+ describe('getTask', () => {
112
+ it('should return a task if it exists', async () => {
113
+ const mockTaskData = {
114
+ user_id: 'user-456',
115
+ title: 'Test Task',
116
+ description: 'Test Description',
117
+ status: 'in_progress',
118
+ created_at: '2026-02-16T00:00:00Z',
119
+ updated_at: '2026-02-16T00:00:00Z',
120
+ progress: {
121
+ current_milestone: 'M1',
122
+ current_task: 'task-1',
123
+ overall_percentage: 50,
124
+ milestones: [],
125
+ tasks: {}
126
+ },
127
+ execution: {
128
+ api_messages: [],
129
+ task_messages: [],
130
+ tool_results: []
131
+ },
132
+ config: {
133
+
134
+ system_prompt: 'test',
135
+ auto_approve: true
136
+ }
137
+ }
138
+
139
+ mockGet.mockResolvedValue({
140
+ exists: true,
141
+ id: 'task-123',
142
+ data: () => mockTaskData
143
+ })
144
+
145
+ const task = await TaskDatabaseService.getTask('user-456', 'task-123')
146
+
147
+ expect(task).not.toBeNull()
148
+ expect(task?.id).toBe('task-123')
149
+ expect(task?.title).toBe('Test Task')
150
+ expect(mockDoc).toHaveBeenCalledWith('users/user-456/tasks/task-123')
151
+ })
152
+
153
+ it('should return null if task does not exist', async () => {
154
+ mockGet.mockResolvedValue({
155
+ exists: false
156
+ })
157
+
158
+ const task = await TaskDatabaseService.getTask('user-456', 'nonexistent')
159
+
160
+ expect(task).toBeNull()
161
+ })
162
+ })
163
+
164
+ describe('updateTaskStatus', () => {
165
+ it('should update task status to in_progress and set started_at', async () => {
166
+ mockUpdate.mockResolvedValue(undefined)
167
+
168
+ await TaskDatabaseService.updateTaskStatus('user-456', 'task-123', 'in_progress')
169
+
170
+ expect(mockDoc).toHaveBeenCalledWith('users/user-456/tasks/task-123')
171
+ expect(mockUpdate).toHaveBeenCalledWith(
172
+ expect.objectContaining({
173
+ status: 'in_progress',
174
+ started_at: expect.any(String),
175
+ updated_at: expect.any(String)
176
+ })
177
+ )
178
+ })
179
+
180
+ it('should update task status to completed and set completed_at', async () => {
181
+ mockUpdate.mockResolvedValue(undefined)
182
+
183
+ await TaskDatabaseService.updateTaskStatus('user-456', 'task-123', 'completed')
184
+
185
+ expect(mockUpdate).toHaveBeenCalledWith(
186
+ expect.objectContaining({
187
+ status: 'completed',
188
+ completed_at: expect.any(String),
189
+ updated_at: expect.any(String)
190
+ })
191
+ )
192
+ })
193
+ })
194
+
195
+ describe('deleteTask', () => {
196
+ it('should delete task and all its messages', async () => {
197
+ const mockMessages = [
198
+ { ref: { path: 'msg1' } },
199
+ { ref: { path: 'msg2' } }
200
+ ]
201
+
202
+ mockGet.mockResolvedValue({
203
+ docs: mockMessages
204
+ })
205
+
206
+ const mockBatchInstance = {
207
+ delete: jest.fn().mockReturnThis(),
208
+ commit: jest.fn<any>().mockResolvedValue(undefined)
209
+ }
210
+
211
+ mockBatch.mockReturnValue(mockBatchInstance)
212
+
213
+ await TaskDatabaseService.deleteTask('user-456', 'task-123')
214
+
215
+ expect(mockCollection).toHaveBeenCalledWith('users/user-456/tasks/task-123/messages')
216
+ expect(mockBatchInstance.delete).toHaveBeenCalledTimes(3) // 2 messages + 1 task
217
+ expect(mockBatchInstance.commit).toHaveBeenCalled()
218
+ })
219
+ })
220
+
221
+ describe('listTasks', () => {
222
+ it('should return list of tasks', async () => {
223
+ const mockTasks = [
224
+ {
225
+ id: 'task-1',
226
+ data: () => ({
227
+ user_id: 'user-456',
228
+ title: 'Task 1',
229
+ description: 'Description 1',
230
+ status: 'not_started',
231
+ created_at: '2026-02-16T00:00:00Z',
232
+ updated_at: '2026-02-16T00:00:00Z',
233
+ progress: {
234
+ current_milestone: '',
235
+ current_task: '',
236
+ overall_percentage: 0,
237
+ milestones: [],
238
+ tasks: {}
239
+ },
240
+ execution: {
241
+ api_messages: [],
242
+ task_messages: [],
243
+ tool_results: []
244
+ },
245
+ config: {
246
+
247
+ system_prompt: '',
248
+ auto_approve: true
249
+ }
250
+ })
251
+ }
252
+ ]
253
+
254
+ mockGet.mockResolvedValue({
255
+ docs: mockTasks
256
+ })
257
+
258
+ const tasks = await TaskDatabaseService.listTasks('user-456', 10)
259
+
260
+ expect(tasks).toHaveLength(1)
261
+ expect(tasks[0].id).toBe('task-1')
262
+ expect(mockOrderBy).toHaveBeenCalledWith('created_at', 'desc')
263
+ expect(mockLimit).toHaveBeenCalledWith(10)
264
+ })
265
+ })
266
+
267
+ describe('addMessage', () => {
268
+ it('should add a message to a task', async () => {
269
+ const mockMessageId = 'msg-123'
270
+ mockAdd.mockResolvedValue({ id: mockMessageId })
271
+
272
+ const messageId = await TaskDatabaseService.addMessage(
273
+ 'user-456',
274
+ 'task-123',
275
+ 'assistant',
276
+ 'Test message'
277
+ )
278
+
279
+ expect(messageId).toBe(mockMessageId)
280
+ expect(mockCollection).toHaveBeenCalledWith('users/user-456/tasks/task-123/messages')
281
+ expect(mockAdd).toHaveBeenCalledWith(
282
+ expect.objectContaining({
283
+ task_id: 'task-123',
284
+ role: 'assistant',
285
+ content: 'Test message',
286
+ timestamp: expect.any(String)
287
+ })
288
+ )
289
+ })
290
+ })
291
+
292
+ describe('getMessages', () => {
293
+ it('should return messages for a task', async () => {
294
+ const mockMessages = [
295
+ {
296
+ id: 'msg-1',
297
+ data: () => ({
298
+ task_id: 'task-123',
299
+ role: 'user',
300
+ content: 'Hello',
301
+ timestamp: '2026-02-16T00:00:00Z'
302
+ })
303
+ },
304
+ {
305
+ id: 'msg-2',
306
+ data: () => ({
307
+ task_id: 'task-123',
308
+ role: 'assistant',
309
+ content: 'Hi there',
310
+ timestamp: '2026-02-16T00:01:00Z'
311
+ })
312
+ }
313
+ ]
314
+
315
+ mockGet.mockResolvedValue({
316
+ docs: mockMessages
317
+ })
318
+
319
+ const messages = await TaskDatabaseService.getMessages('user-456', 'task-123')
320
+
321
+ expect(messages).toHaveLength(2)
322
+ expect(messages[0].role).toBe('user')
323
+ expect(messages[1].role).toBe('assistant')
324
+ expect(mockOrderBy).toHaveBeenCalledWith('timestamp', 'asc')
325
+ })
326
+ })
327
+
328
+ describe('updateOverallProgress', () => {
329
+ it('should update overall progress percentage', async () => {
330
+ mockUpdate.mockResolvedValue(undefined)
331
+
332
+ await TaskDatabaseService.updateOverallProgress('user-456', 'task-123', 75)
333
+
334
+ expect(mockUpdate).toHaveBeenCalledWith(
335
+ expect.objectContaining({
336
+ 'progress.overall_percentage': 75,
337
+ updated_at: expect.any(String)
338
+ })
339
+ )
340
+ })
341
+
342
+ it('should clamp progress to 0-100 range', async () => {
343
+ mockUpdate.mockResolvedValue(undefined)
344
+
345
+ await TaskDatabaseService.updateOverallProgress('user-456', 'task-123', 150)
346
+
347
+ expect(mockUpdate).toHaveBeenCalledWith(
348
+ expect.objectContaining({
349
+ 'progress.overall_percentage': 100
350
+ })
351
+ )
352
+
353
+ await TaskDatabaseService.updateOverallProgress('user-456', 'task-123', -10)
354
+
355
+ expect(mockUpdate).toHaveBeenCalledWith(
356
+ expect.objectContaining({
357
+ 'progress.overall_percentage': 0
358
+ })
359
+ )
360
+ })
361
+ })
362
+
363
+ describe('getTasksByStatus', () => {
364
+ it('should return tasks filtered by status', async () => {
365
+ const mockTasks = [
366
+ {
367
+ id: 'task-1',
368
+ data: () => ({
369
+ user_id: 'user-456',
370
+ title: 'Active Task',
371
+ description: 'Description',
372
+ status: 'in_progress',
373
+ created_at: '2026-02-16T00:00:00Z',
374
+ updated_at: '2026-02-16T00:00:00Z',
375
+ progress: {
376
+ current_milestone: '',
377
+ current_task: '',
378
+ overall_percentage: 0,
379
+ milestones: [],
380
+ tasks: {}
381
+ },
382
+ execution: {
383
+ api_messages: [],
384
+ task_messages: [],
385
+ tool_results: []
386
+ },
387
+ config: {
388
+
389
+ system_prompt: '',
390
+ auto_approve: true
391
+ }
392
+ })
393
+ }
394
+ ]
395
+
396
+ mockGet.mockResolvedValue({
397
+ docs: mockTasks
398
+ })
399
+
400
+ const tasks = await TaskDatabaseService.getTasksByStatus('user-456', 'in_progress')
401
+
402
+ expect(tasks).toHaveLength(1)
403
+ expect(tasks[0].status).toBe('in_progress')
404
+ expect(mockWhere).toHaveBeenCalledWith('status', '==', 'in_progress')
405
+ })
406
+ })
407
+
408
+ describe('searchTasksByTitle', () => {
409
+ it('should search tasks by title and description', async () => {
410
+ const mockTasks = [
411
+ {
412
+ id: 'task-1',
413
+ data: () => ({
414
+ user_id: 'user-456',
415
+ title: 'Build MCP Server',
416
+ description: 'Create task-mcp server',
417
+ status: 'in_progress',
418
+ created_at: '2026-02-16T00:00:00Z',
419
+ updated_at: '2026-02-16T00:00:00Z',
420
+ progress: {
421
+ current_milestone: '',
422
+ current_task: '',
423
+ overall_percentage: 0,
424
+ milestones: [],
425
+ tasks: {}
426
+ },
427
+ execution: {
428
+ api_messages: [],
429
+ task_messages: [],
430
+ tool_results: []
431
+ },
432
+ config: {
433
+
434
+ system_prompt: '',
435
+ auto_approve: true
436
+ }
437
+ })
438
+ },
439
+ {
440
+ id: 'task-2',
441
+ data: () => ({
442
+ user_id: 'user-456',
443
+ title: 'Write Documentation',
444
+ description: 'Document the API',
445
+ status: 'not_started',
446
+ created_at: '2026-02-16T00:00:00Z',
447
+ updated_at: '2026-02-16T00:00:00Z',
448
+ progress: {
449
+ current_milestone: '',
450
+ current_task: '',
451
+ overall_percentage: 0,
452
+ milestones: [],
453
+ tasks: {}
454
+ },
455
+ execution: {
456
+ api_messages: [],
457
+ task_messages: [],
458
+ tool_results: []
459
+ },
460
+ config: {
461
+
462
+ system_prompt: '',
463
+ auto_approve: true
464
+ }
465
+ })
466
+ }
467
+ ]
468
+
469
+ mockGet.mockResolvedValue({
470
+ docs: mockTasks
471
+ })
472
+
473
+ const results = await TaskDatabaseService.searchTasksByTitle('user-456', 'mcp')
474
+
475
+ expect(results).toHaveLength(1)
476
+ expect(results[0].title).toBe('Build MCP Server')
477
+ })
478
+ })
479
+ })