@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,64 @@
1
+ /**
2
+ * MCP Tool: task_add_message
3
+ *
4
+ * Add a message to the task conversation thread.
5
+ */
6
+
7
+ import { FirebaseClient } from '@/client.js'
8
+
9
+ export const taskAddMessageTool = {
10
+ name: 'task_add_message',
11
+ description: 'Add a message to the task conversation thread',
12
+ inputSchema: {
13
+ type: 'object',
14
+ properties: {
15
+ task_id: {
16
+ type: 'string',
17
+ description: 'Task ID'
18
+ },
19
+ role: {
20
+ type: 'string',
21
+ enum: ['user', 'assistant', 'system'],
22
+ description: 'Message role'
23
+ },
24
+ content: {
25
+ type: 'string',
26
+ description: 'Message content'
27
+ },
28
+ metadata: {
29
+ type: 'object',
30
+ description: 'Optional metadata (JSON object)'
31
+ }
32
+ },
33
+ required: ['task_id', 'role', 'content']
34
+ }
35
+ }
36
+
37
+ export async function handleTaskAddMessage(
38
+ client: FirebaseClient,
39
+ args: {
40
+ task_id: string
41
+ role: 'user' | 'assistant' | 'system'
42
+ content: string
43
+ metadata?: any
44
+ }
45
+ ): Promise<string> {
46
+ try {
47
+ const messageId = await client.addMessage(
48
+ args.task_id,
49
+ args.role,
50
+ args.content,
51
+ args.metadata
52
+ )
53
+
54
+ return JSON.stringify({
55
+ success: true,
56
+ task_id: args.task_id,
57
+ message_id: messageId,
58
+ role: args.role,
59
+ message: 'Message added to task thread'
60
+ }, null, 2)
61
+ } catch (error) {
62
+ throw new Error(`Failed to add message: ${error instanceof Error ? error.message : String(error)}`)
63
+ }
64
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * MCP Tool: task_complete_task_item
3
+ *
4
+ * Mark a task item as complete and update milestone progress.
5
+ */
6
+
7
+ import { FirebaseClient } from '@/client.js'
8
+
9
+ export const taskCompleteTaskItemTool = {
10
+ name: 'task_complete_task_item',
11
+ description: 'Mark a task item as complete',
12
+ inputSchema: {
13
+ type: 'object',
14
+ properties: {
15
+ task_id: {
16
+ type: 'string',
17
+ description: 'Task ID'
18
+ },
19
+ milestone_id: {
20
+ type: 'string',
21
+ description: 'Milestone ID'
22
+ },
23
+ task_item_id: {
24
+ type: 'string',
25
+ description: 'Task item ID to complete'
26
+ }
27
+ },
28
+ required: ['task_id', 'milestone_id', 'task_item_id']
29
+ }
30
+ }
31
+
32
+ export async function handleTaskCompleteTaskItem(
33
+ client: FirebaseClient,
34
+ args: { task_id: string; milestone_id: string; task_item_id: string }
35
+ ): Promise<string> {
36
+ try {
37
+ // Complete the task item
38
+ await client.completeTaskItem(args.task_id, args.milestone_id, args.task_item_id)
39
+
40
+ // Get updated task to calculate new progress
41
+ const task = await client.getTask(args.task_id)
42
+
43
+ if (!task) {
44
+ throw new Error('Task not found after update')
45
+ }
46
+
47
+ // Find the milestone
48
+ const milestone = task.progress.milestones.find(m => m.id === args.milestone_id)
49
+ const milestoneItems = task.progress.tasks[args.milestone_id] || []
50
+ const completedCount = milestoneItems.filter(item => item.status === 'completed').length
51
+
52
+ // Update milestone progress
53
+ const milestoneProgress = milestone ? Math.round((completedCount / milestoneItems.length) * 100) : 0
54
+
55
+ if (milestone && milestone.progress !== milestoneProgress) {
56
+ await client.updateMilestone(args.task_id, args.milestone_id, {
57
+ progress: milestoneProgress,
58
+ tasks_completed: completedCount,
59
+ tasks_total: milestoneItems.length
60
+ })
61
+ }
62
+
63
+ // Find next task
64
+ const nextTask = milestoneItems.find(
65
+ item => item.status === 'not_started' || item.status === 'in_progress'
66
+ )
67
+
68
+ return JSON.stringify({
69
+ success: true,
70
+ task_id: args.task_id,
71
+ completed_task: args.task_item_id,
72
+ milestone_progress: milestoneProgress,
73
+ milestone_tasks_completed: completedCount,
74
+ milestone_tasks_total: milestoneItems.length,
75
+ next_task: nextTask ? {
76
+ id: nextTask.id,
77
+ name: nextTask.name
78
+ } : null,
79
+ message: nextTask
80
+ ? `Task item completed. Next: ${nextTask.name}`
81
+ : 'Task item completed. Milestone complete!'
82
+ }, null, 2)
83
+ } catch (error) {
84
+ throw new Error(`Failed to complete task item: ${error instanceof Error ? error.message : String(error)}`)
85
+ }
86
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * MCP Tool: task_create_milestone
3
+ *
4
+ * Create a new milestone in a task.
5
+ */
6
+
7
+ import { FirebaseClient } from '@/client.js'
8
+ import type { Milestone } from '@/schemas/task.js'
9
+
10
+ export const taskCreateMilestoneTool = {
11
+ name: 'task_create_milestone',
12
+ description: 'Create a new milestone in a task',
13
+ inputSchema: {
14
+ type: 'object',
15
+ properties: {
16
+ task_id: {
17
+ type: 'string',
18
+ description: 'Task ID'
19
+ },
20
+ milestone_id: {
21
+ type: 'string',
22
+ description: 'Unique milestone ID (e.g., "milestone-1")'
23
+ },
24
+ name: {
25
+ type: 'string',
26
+ description: 'Milestone name'
27
+ },
28
+ description: {
29
+ type: 'string',
30
+ description: 'Milestone description'
31
+ }
32
+ },
33
+ required: ['task_id', 'milestone_id', 'name', 'description']
34
+ }
35
+ }
36
+
37
+ export async function handleTaskCreateMilestone(
38
+ client: FirebaseClient,
39
+ args: { task_id: string; milestone_id: string; name: string; description: string }
40
+ ): Promise<string> {
41
+ try {
42
+ const milestone: Milestone = {
43
+ id: args.milestone_id,
44
+ name: args.name,
45
+ description: args.description,
46
+ status: 'not_started',
47
+ progress: 0,
48
+ tasks_completed: 0,
49
+ tasks_total: 0
50
+ }
51
+
52
+ await client.createMilestone(args.task_id, milestone)
53
+
54
+ return JSON.stringify({
55
+ success: true,
56
+ task_id: args.task_id,
57
+ milestone: {
58
+ id: milestone.id,
59
+ name: milestone.name,
60
+ description: milestone.description
61
+ },
62
+ message: `Milestone "${milestone.name}" created successfully`
63
+ }, null, 2)
64
+ } catch (error) {
65
+ throw new Error(`Failed to create milestone: ${error instanceof Error ? error.message : String(error)}`)
66
+ }
67
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * MCP Tool: task_create_task_item
3
+ *
4
+ * Create a new task item within a milestone.
5
+ */
6
+
7
+ import { FirebaseClient } from '@/client.js'
8
+ import type { TaskItem } from '@/schemas/task.js'
9
+
10
+ export const taskCreateTaskItemTool = {
11
+ name: 'task_create_task_item',
12
+ description: 'Create a new task item within a milestone',
13
+ inputSchema: {
14
+ type: 'object',
15
+ properties: {
16
+ task_id: {
17
+ type: 'string',
18
+ description: 'Task ID'
19
+ },
20
+ milestone_id: {
21
+ type: 'string',
22
+ description: 'Milestone ID'
23
+ },
24
+ task_item_id: {
25
+ type: 'string',
26
+ description: 'Unique task item ID (e.g., "task-1")'
27
+ },
28
+ name: {
29
+ type: 'string',
30
+ description: 'Task item name'
31
+ },
32
+ description: {
33
+ type: 'string',
34
+ description: 'Task item description'
35
+ },
36
+ estimated_hours: {
37
+ type: 'number',
38
+ description: 'Estimated hours to complete (optional)'
39
+ }
40
+ },
41
+ required: ['task_id', 'milestone_id', 'task_item_id', 'name', 'description']
42
+ }
43
+ }
44
+
45
+ export async function handleTaskCreateTaskItem(
46
+ client: FirebaseClient,
47
+ args: {
48
+ task_id: string
49
+ milestone_id: string
50
+ task_item_id: string
51
+ name: string
52
+ description: string
53
+ estimated_hours?: number
54
+ }
55
+ ): Promise<string> {
56
+ try {
57
+ const taskItem: TaskItem = {
58
+ id: args.task_item_id,
59
+ name: args.name,
60
+ description: args.description,
61
+ status: 'not_started',
62
+ estimated_hours: args.estimated_hours
63
+ }
64
+
65
+ await client.createTaskItem(args.task_id, args.milestone_id, taskItem)
66
+
67
+ // Update milestone task count
68
+ const task = await client.getTask(args.task_id)
69
+ if (task) {
70
+ const milestoneItems = task.progress.tasks[args.milestone_id] || []
71
+ await client.updateMilestone(args.task_id, args.milestone_id, {
72
+ tasks_total: milestoneItems.length
73
+ })
74
+ }
75
+
76
+ return JSON.stringify({
77
+ success: true,
78
+ task_id: args.task_id,
79
+ milestone_id: args.milestone_id,
80
+ task_item: {
81
+ id: taskItem.id,
82
+ name: taskItem.name,
83
+ description: taskItem.description,
84
+ estimated_hours: taskItem.estimated_hours
85
+ },
86
+ message: `Task item "${taskItem.name}" created in milestone`
87
+ }, null, 2)
88
+ } catch (error) {
89
+ throw new Error(`Failed to create task item: ${error instanceof Error ? error.message : String(error)}`)
90
+ }
91
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Tests for task_get_next_step tool
3
+ */
4
+
5
+ import { handleTaskGetNextStep, taskGetNextStepTool } from './task-get-next-step.js'
6
+ import { FirebaseClient } from '@/client.js'
7
+ import type { Task } from '@/schemas/task.js'
8
+
9
+ jest.mock('@/client.js')
10
+
11
+ describe('task_get_next_step', () => {
12
+ let mockClient: jest.Mocked<FirebaseClient>
13
+
14
+ beforeEach(() => {
15
+ mockClient = {
16
+ getTask: jest.fn()
17
+ } as any
18
+ })
19
+
20
+ describe('Tool Definition', () => {
21
+ it('should have correct name', () => {
22
+ expect(taskGetNextStepTool.name).toBe('task_get_next_step')
23
+ })
24
+
25
+ it('should require task_id parameter', () => {
26
+ expect(taskGetNextStepTool.inputSchema.required).toContain('task_id')
27
+ })
28
+ })
29
+
30
+ describe('Handler', () => {
31
+ it('should return paused status for paused task', async () => {
32
+ const mockTask: Task = {
33
+ id: 'task-123',
34
+ user_id: 'user-456',
35
+ title: 'Test Task',
36
+ description: 'Test',
37
+ status: 'paused',
38
+ created_at: '2026-02-16T00:00:00Z',
39
+ updated_at: '2026-02-16T00:00:00Z',
40
+ progress: {
41
+ current_milestone: '',
42
+ current_task: '',
43
+ overall_percentage: 0,
44
+ milestones: [],
45
+ tasks: {}
46
+ },
47
+ execution: { api_messages: [], task_messages: [], tool_results: [] },
48
+ config: { system_prompt: '', auto_approve: true },
49
+ metadata: undefined
50
+ }
51
+
52
+ mockClient.getTask.mockResolvedValue(mockTask)
53
+
54
+ const result = await handleTaskGetNextStep(mockClient, { task_id: 'task-123' })
55
+ const parsed = JSON.parse(result)
56
+
57
+ expect(parsed.status).toBe('paused')
58
+ expect(parsed.instructions).toBeNull()
59
+ })
60
+
61
+ it('should return no_milestone status when no milestones exist', async () => {
62
+ const mockTask: Task = {
63
+ id: 'task-123',
64
+ user_id: 'user-456',
65
+ title: 'Test Task',
66
+ description: 'Test',
67
+ status: 'in_progress',
68
+ created_at: '2026-02-16T00:00:00Z',
69
+ updated_at: '2026-02-16T00:00:00Z',
70
+ progress: {
71
+ current_milestone: '',
72
+ current_task: '',
73
+ overall_percentage: 0,
74
+ milestones: [],
75
+ tasks: {}
76
+ },
77
+ execution: { api_messages: [], task_messages: [], tool_results: [] },
78
+ config: { system_prompt: '', auto_approve: true },
79
+ metadata: undefined
80
+ }
81
+
82
+ mockClient.getTask.mockResolvedValue(mockTask)
83
+
84
+ const result = await handleTaskGetNextStep(mockClient, { task_id: 'task-123' })
85
+ const parsed = JSON.parse(result)
86
+
87
+ expect(parsed.status).toBe('no_milestone')
88
+ })
89
+
90
+ it('should return current task instructions', async () => {
91
+ const mockTask: Task = {
92
+ id: 'task-123',
93
+ user_id: 'user-456',
94
+ title: 'Test Task',
95
+ description: 'Test',
96
+ status: 'in_progress',
97
+ created_at: '2026-02-16T00:00:00Z',
98
+ updated_at: '2026-02-16T00:00:00Z',
99
+ progress: {
100
+ current_milestone: 'milestone-1',
101
+ current_task: 'task-1',
102
+ overall_percentage: 50,
103
+ milestones: [{
104
+ id: 'milestone-1',
105
+ name: 'Milestone 1',
106
+ description: 'First milestone',
107
+ status: 'in_progress',
108
+ progress: 50,
109
+ tasks_completed: 0,
110
+ tasks_total: 2
111
+ }],
112
+ tasks: {
113
+ 'milestone-1': [{
114
+ id: 'task-1',
115
+ name: 'Task 1',
116
+ description: 'First task',
117
+ status: 'in_progress'
118
+ }]
119
+ }
120
+ },
121
+ execution: { api_messages: [], task_messages: [], tool_results: [] },
122
+ config: { system_prompt: '', auto_approve: true },
123
+ metadata: undefined
124
+ }
125
+
126
+ mockClient.getTask.mockResolvedValue(mockTask)
127
+
128
+ const result = await handleTaskGetNextStep(mockClient, { task_id: 'task-123' })
129
+ const parsed = JSON.parse(result)
130
+
131
+ expect(parsed.status).toBe('in_progress')
132
+ expect(parsed.current_task.name).toBe('Task 1')
133
+ expect(parsed.instructions).toContain('Task 1')
134
+ })
135
+ })
136
+ })
@@ -0,0 +1,125 @@
1
+ /**
2
+ * MCP Tool: task_get_next_step
3
+ *
4
+ * Get instructions for the next step in the current task.
5
+ * Returns the current task item with steps and verification criteria.
6
+ */
7
+
8
+ import { FirebaseClient } from '@/client.js'
9
+
10
+ export const taskGetNextStepTool = {
11
+ name: 'task_get_next_step',
12
+ description: 'Get instructions for the next step in the current task',
13
+ inputSchema: {
14
+ type: 'object',
15
+ properties: {
16
+ task_id: {
17
+ type: 'string',
18
+ description: 'Task ID'
19
+ }
20
+ },
21
+ required: ['task_id']
22
+ }
23
+ }
24
+
25
+ export async function handleTaskGetNextStep(
26
+ client: FirebaseClient,
27
+ args: { task_id: string }
28
+ ): Promise<string> {
29
+ try {
30
+ const task = await client.getTask(args.task_id)
31
+
32
+ if (!task) {
33
+ throw new Error(`Task not found: ${args.task_id}`)
34
+ }
35
+
36
+ // Check if task is paused or completed
37
+ if (task.status === 'paused') {
38
+ return JSON.stringify({
39
+ status: 'paused',
40
+ message: 'Task is paused. Resume the task to continue.',
41
+ instructions: null
42
+ }, null, 2)
43
+ }
44
+
45
+ if (task.status === 'completed') {
46
+ return JSON.stringify({
47
+ status: 'completed',
48
+ message: 'Task is already completed.',
49
+ instructions: null
50
+ }, null, 2)
51
+ }
52
+
53
+ // Find current milestone
54
+ const currentMilestone = task.progress.milestones.find(
55
+ m => m.id === task.progress.current_milestone
56
+ )
57
+
58
+ if (!currentMilestone) {
59
+ return JSON.stringify({
60
+ status: 'no_milestone',
61
+ message: 'No current milestone. Create milestones to begin work.',
62
+ instructions: 'Use task_create_milestone to add milestones to this task.'
63
+ }, null, 2)
64
+ }
65
+
66
+ // Find current task item
67
+ const milestoneItems = task.progress.tasks[task.progress.current_milestone] || []
68
+ const currentTaskItem = milestoneItems.find(
69
+ item => item.id === task.progress.current_task
70
+ )
71
+
72
+ if (!currentTaskItem) {
73
+ // Find next not_started or in_progress task
74
+ const nextTask = milestoneItems.find(
75
+ item => item.status === 'not_started' || item.status === 'in_progress'
76
+ )
77
+
78
+ if (!nextTask) {
79
+ return JSON.stringify({
80
+ status: 'milestone_complete',
81
+ message: `Milestone "${currentMilestone.name}" is complete.`,
82
+ instructions: 'Move to the next milestone or complete the task.',
83
+ current_milestone: currentMilestone.name
84
+ }, null, 2)
85
+ }
86
+
87
+ return JSON.stringify({
88
+ status: 'ready',
89
+ current_milestone: {
90
+ id: currentMilestone.id,
91
+ name: currentMilestone.name
92
+ },
93
+ next_task: {
94
+ id: nextTask.id,
95
+ name: nextTask.name,
96
+ description: nextTask.description,
97
+ status: nextTask.status,
98
+ estimated_hours: nextTask.estimated_hours
99
+ },
100
+ instructions: `Begin work on: ${nextTask.name}\n\nDescription: ${nextTask.description}`
101
+ }, null, 2)
102
+ }
103
+
104
+ // Return current task item details
105
+ return JSON.stringify({
106
+ status: 'in_progress',
107
+ current_milestone: {
108
+ id: currentMilestone.id,
109
+ name: currentMilestone.name,
110
+ progress: currentMilestone.progress
111
+ },
112
+ current_task: {
113
+ id: currentTaskItem.id,
114
+ name: currentTaskItem.name,
115
+ description: currentTaskItem.description,
116
+ status: currentTaskItem.status,
117
+ estimated_hours: currentTaskItem.estimated_hours,
118
+ notes: currentTaskItem.notes
119
+ },
120
+ instructions: `Continue work on: ${currentTaskItem.name}\n\nDescription: ${currentTaskItem.description}${currentTaskItem.notes ? `\n\nNotes: ${currentTaskItem.notes}` : ''}`
121
+ }, null, 2)
122
+ } catch (error) {
123
+ throw new Error(`Failed to get next step: ${error instanceof Error ? error.message : String(error)}`)
124
+ }
125
+ }