@mcoda/core 0.1.4

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/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +9 -0
  4. package/dist/api/AgentsApi.d.ts +36 -0
  5. package/dist/api/AgentsApi.d.ts.map +1 -0
  6. package/dist/api/AgentsApi.js +176 -0
  7. package/dist/api/QaTasksApi.d.ts +8 -0
  8. package/dist/api/QaTasksApi.d.ts.map +1 -0
  9. package/dist/api/QaTasksApi.js +36 -0
  10. package/dist/api/TasksApi.d.ts +7 -0
  11. package/dist/api/TasksApi.d.ts.map +1 -0
  12. package/dist/api/TasksApi.js +34 -0
  13. package/dist/config/ConfigService.d.ts +3 -0
  14. package/dist/config/ConfigService.d.ts.map +1 -0
  15. package/dist/config/ConfigService.js +2 -0
  16. package/dist/domain/dependencies/Dependency.d.ts +3 -0
  17. package/dist/domain/dependencies/Dependency.d.ts.map +1 -0
  18. package/dist/domain/dependencies/Dependency.js +2 -0
  19. package/dist/domain/epics/Epic.d.ts +3 -0
  20. package/dist/domain/epics/Epic.d.ts.map +1 -0
  21. package/dist/domain/epics/Epic.js +2 -0
  22. package/dist/domain/projects/Project.d.ts +3 -0
  23. package/dist/domain/projects/Project.d.ts.map +1 -0
  24. package/dist/domain/projects/Project.js +2 -0
  25. package/dist/domain/tasks/Task.d.ts +3 -0
  26. package/dist/domain/tasks/Task.d.ts.map +1 -0
  27. package/dist/domain/tasks/Task.js +2 -0
  28. package/dist/domain/userStories/UserStory.d.ts +3 -0
  29. package/dist/domain/userStories/UserStory.d.ts.map +1 -0
  30. package/dist/domain/userStories/UserStory.js +2 -0
  31. package/dist/index.d.ts +28 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +27 -0
  34. package/dist/prompts/PdrPrompts.d.ts +4 -0
  35. package/dist/prompts/PdrPrompts.d.ts.map +1 -0
  36. package/dist/prompts/PdrPrompts.js +21 -0
  37. package/dist/prompts/PromptLoader.d.ts +3 -0
  38. package/dist/prompts/PromptLoader.d.ts.map +1 -0
  39. package/dist/prompts/PromptLoader.js +2 -0
  40. package/dist/prompts/SdsPrompts.d.ts +5 -0
  41. package/dist/prompts/SdsPrompts.d.ts.map +1 -0
  42. package/dist/prompts/SdsPrompts.js +44 -0
  43. package/dist/services/agents/AgentManagementService.d.ts +3 -0
  44. package/dist/services/agents/AgentManagementService.d.ts.map +1 -0
  45. package/dist/services/agents/AgentManagementService.js +2 -0
  46. package/dist/services/agents/GatewayAgentService.d.ts +92 -0
  47. package/dist/services/agents/GatewayAgentService.d.ts.map +1 -0
  48. package/dist/services/agents/GatewayAgentService.js +870 -0
  49. package/dist/services/agents/RoutingApiClient.d.ts +23 -0
  50. package/dist/services/agents/RoutingApiClient.d.ts.map +1 -0
  51. package/dist/services/agents/RoutingApiClient.js +62 -0
  52. package/dist/services/agents/RoutingService.d.ts +50 -0
  53. package/dist/services/agents/RoutingService.d.ts.map +1 -0
  54. package/dist/services/agents/RoutingService.js +386 -0
  55. package/dist/services/agents/generated/RoutingApiClient.d.ts +21 -0
  56. package/dist/services/agents/generated/RoutingApiClient.d.ts.map +1 -0
  57. package/dist/services/agents/generated/RoutingApiClient.js +68 -0
  58. package/dist/services/backlog/BacklogService.d.ts +98 -0
  59. package/dist/services/backlog/BacklogService.d.ts.map +1 -0
  60. package/dist/services/backlog/BacklogService.js +453 -0
  61. package/dist/services/backlog/TaskOrderingService.d.ts +88 -0
  62. package/dist/services/backlog/TaskOrderingService.d.ts.map +1 -0
  63. package/dist/services/backlog/TaskOrderingService.js +675 -0
  64. package/dist/services/docs/DocsService.d.ts +82 -0
  65. package/dist/services/docs/DocsService.d.ts.map +1 -0
  66. package/dist/services/docs/DocsService.js +1631 -0
  67. package/dist/services/estimate/EstimateService.d.ts +12 -0
  68. package/dist/services/estimate/EstimateService.d.ts.map +1 -0
  69. package/dist/services/estimate/EstimateService.js +103 -0
  70. package/dist/services/estimate/VelocityService.d.ts +19 -0
  71. package/dist/services/estimate/VelocityService.d.ts.map +1 -0
  72. package/dist/services/estimate/VelocityService.js +237 -0
  73. package/dist/services/estimate/types.d.ts +30 -0
  74. package/dist/services/estimate/types.d.ts.map +1 -0
  75. package/dist/services/estimate/types.js +1 -0
  76. package/dist/services/execution/ExecutionService.d.ts +3 -0
  77. package/dist/services/execution/ExecutionService.d.ts.map +1 -0
  78. package/dist/services/execution/ExecutionService.js +2 -0
  79. package/dist/services/execution/QaFollowupService.d.ts +38 -0
  80. package/dist/services/execution/QaFollowupService.d.ts.map +1 -0
  81. package/dist/services/execution/QaFollowupService.js +236 -0
  82. package/dist/services/execution/QaProfileService.d.ts +22 -0
  83. package/dist/services/execution/QaProfileService.d.ts.map +1 -0
  84. package/dist/services/execution/QaProfileService.js +142 -0
  85. package/dist/services/execution/QaTasksService.d.ts +101 -0
  86. package/dist/services/execution/QaTasksService.d.ts.map +1 -0
  87. package/dist/services/execution/QaTasksService.js +1117 -0
  88. package/dist/services/execution/TaskSelectionService.d.ts +50 -0
  89. package/dist/services/execution/TaskSelectionService.d.ts.map +1 -0
  90. package/dist/services/execution/TaskSelectionService.js +281 -0
  91. package/dist/services/execution/TaskStateService.d.ts +19 -0
  92. package/dist/services/execution/TaskStateService.d.ts.map +1 -0
  93. package/dist/services/execution/TaskStateService.js +59 -0
  94. package/dist/services/execution/WorkOnTasksService.d.ts +80 -0
  95. package/dist/services/execution/WorkOnTasksService.d.ts.map +1 -0
  96. package/dist/services/execution/WorkOnTasksService.js +1833 -0
  97. package/dist/services/jobs/JobInsightsService.d.ts +97 -0
  98. package/dist/services/jobs/JobInsightsService.d.ts.map +1 -0
  99. package/dist/services/jobs/JobInsightsService.js +263 -0
  100. package/dist/services/jobs/JobResumeService.d.ts +16 -0
  101. package/dist/services/jobs/JobResumeService.d.ts.map +1 -0
  102. package/dist/services/jobs/JobResumeService.js +113 -0
  103. package/dist/services/jobs/JobService.d.ts +149 -0
  104. package/dist/services/jobs/JobService.d.ts.map +1 -0
  105. package/dist/services/jobs/JobService.js +490 -0
  106. package/dist/services/jobs/JobsApiClient.d.ts +73 -0
  107. package/dist/services/jobs/JobsApiClient.d.ts.map +1 -0
  108. package/dist/services/jobs/JobsApiClient.js +67 -0
  109. package/dist/services/openapi/OpenApiService.d.ts +54 -0
  110. package/dist/services/openapi/OpenApiService.d.ts.map +1 -0
  111. package/dist/services/openapi/OpenApiService.js +503 -0
  112. package/dist/services/planning/CreateTasksService.d.ts +68 -0
  113. package/dist/services/planning/CreateTasksService.d.ts.map +1 -0
  114. package/dist/services/planning/CreateTasksService.js +989 -0
  115. package/dist/services/planning/KeyHelpers.d.ts +5 -0
  116. package/dist/services/planning/KeyHelpers.d.ts.map +1 -0
  117. package/dist/services/planning/KeyHelpers.js +62 -0
  118. package/dist/services/planning/PlanningService.d.ts +3 -0
  119. package/dist/services/planning/PlanningService.d.ts.map +1 -0
  120. package/dist/services/planning/PlanningService.js +2 -0
  121. package/dist/services/planning/RefineTasksService.d.ts +56 -0
  122. package/dist/services/planning/RefineTasksService.d.ts.map +1 -0
  123. package/dist/services/planning/RefineTasksService.js +1328 -0
  124. package/dist/services/review/CodeReviewService.d.ts +103 -0
  125. package/dist/services/review/CodeReviewService.d.ts.map +1 -0
  126. package/dist/services/review/CodeReviewService.js +1187 -0
  127. package/dist/services/system/SystemUpdateService.d.ts +55 -0
  128. package/dist/services/system/SystemUpdateService.d.ts.map +1 -0
  129. package/dist/services/system/SystemUpdateService.js +136 -0
  130. package/dist/services/tasks/TaskApiResolver.d.ts +7 -0
  131. package/dist/services/tasks/TaskApiResolver.d.ts.map +1 -0
  132. package/dist/services/tasks/TaskApiResolver.js +41 -0
  133. package/dist/services/tasks/TaskDetailService.d.ts +106 -0
  134. package/dist/services/tasks/TaskDetailService.d.ts.map +1 -0
  135. package/dist/services/tasks/TaskDetailService.js +332 -0
  136. package/dist/services/telemetry/TelemetryService.d.ts +53 -0
  137. package/dist/services/telemetry/TelemetryService.d.ts.map +1 -0
  138. package/dist/services/telemetry/TelemetryService.js +434 -0
  139. package/dist/workspace/WorkspaceManager.d.ts +35 -0
  140. package/dist/workspace/WorkspaceManager.d.ts.map +1 -0
  141. package/dist/workspace/WorkspaceManager.js +201 -0
  142. package/package.json +45 -0
@@ -0,0 +1,50 @@
1
+ import { WorkspaceRepository, TaskRow, ProjectRow } from "@mcoda/db";
2
+ import { WorkspaceResolution } from "../../workspace/WorkspaceManager.js";
3
+ export interface TaskSelectionFilters {
4
+ projectKey?: string;
5
+ epicKey?: string;
6
+ storyKey?: string;
7
+ taskKeys?: string[];
8
+ statusFilter?: string[];
9
+ limit?: number;
10
+ parallel?: number;
11
+ }
12
+ export interface SelectedTask {
13
+ task: TaskRow & {
14
+ epicKey: string;
15
+ storyKey: string;
16
+ epicTitle?: string;
17
+ storyTitle?: string;
18
+ storyDescription?: string;
19
+ acceptanceCriteria?: string[];
20
+ };
21
+ dependencies: {
22
+ ids: string[];
23
+ keys: string[];
24
+ blocking: string[];
25
+ };
26
+ blockedReason?: string;
27
+ }
28
+ export interface TaskSelectionPlan {
29
+ project?: ProjectRow;
30
+ filters: TaskSelectionFilters & {
31
+ effectiveStatuses: string[];
32
+ };
33
+ ordered: SelectedTask[];
34
+ blocked: SelectedTask[];
35
+ warnings: string[];
36
+ }
37
+ export declare class TaskSelectionService {
38
+ private workspace;
39
+ private workspaceRepo;
40
+ constructor(workspace: WorkspaceResolution, workspaceRepo: WorkspaceRepository);
41
+ static create(workspace: WorkspaceResolution): Promise<TaskSelectionService>;
42
+ close(): Promise<void>;
43
+ private fetchProject;
44
+ private buildTaskFromRow;
45
+ private loadTasks;
46
+ private loadDependencies;
47
+ private topologicalOrder;
48
+ selectTasks(filters: TaskSelectionFilters): Promise<TaskSelectionPlan>;
49
+ }
50
+ //# sourceMappingURL=TaskSelectionService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TaskSelectionService.d.ts","sourceRoot":"","sources":["../../../src/services/execution/TaskSelectionService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAE1E,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,GAAG;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC/B,CAAC;IACF,YAAY,EAAE;QACZ,GAAG,EAAE,MAAM,EAAE,CAAC;QACd,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,OAAO,EAAE,oBAAoB,GAAG;QAAE,iBAAiB,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAChE,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAyDD,qBAAa,oBAAoB;IACnB,OAAO,CAAC,SAAS;IAAuB,OAAO,CAAC,aAAa;gBAArD,SAAS,EAAE,mBAAmB,EAAU,aAAa,EAAE,mBAAmB;WAEjF,MAAM,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAK5E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAId,YAAY;IAK1B,OAAO,CAAC,gBAAgB;YA+BV,SAAS;YA2DT,gBAAgB;IAgC9B,OAAO,CAAC,gBAAgB;IAgElB,WAAW,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAiE7E"}
@@ -0,0 +1,281 @@
1
+ import { WorkspaceRepository } from "@mcoda/db";
2
+ const DEFAULT_IMPLEMENTATION_STATUSES = ["not_started", "in_progress"];
3
+ const DONE_DEPENDENCY_STATUSES = new Set(["completed", "cancelled"]);
4
+ const normalizeStatusList = (statuses) => {
5
+ if (!statuses || statuses.length === 0)
6
+ return DEFAULT_IMPLEMENTATION_STATUSES;
7
+ return Array.from(new Set(statuses.map((s) => s.toLowerCase().trim()).filter(Boolean)));
8
+ };
9
+ const parseAcceptanceCriteria = (raw) => {
10
+ if (!raw)
11
+ return undefined;
12
+ return raw
13
+ .split(/\r?\n/)
14
+ .map((line) => line.trim())
15
+ .filter(Boolean);
16
+ };
17
+ export class TaskSelectionService {
18
+ constructor(workspace, workspaceRepo) {
19
+ this.workspace = workspace;
20
+ this.workspaceRepo = workspaceRepo;
21
+ }
22
+ static async create(workspace) {
23
+ const workspaceRepo = await WorkspaceRepository.create(workspace.workspaceRoot);
24
+ return new TaskSelectionService(workspace, workspaceRepo);
25
+ }
26
+ async close() {
27
+ await this.workspaceRepo.close();
28
+ }
29
+ async fetchProject(projectKey) {
30
+ if (!projectKey)
31
+ return undefined;
32
+ return this.workspaceRepo.getProjectByKey(projectKey);
33
+ }
34
+ buildTaskFromRow(row) {
35
+ return {
36
+ id: row.task_id,
37
+ projectId: row.project_id,
38
+ epicId: row.epic_id,
39
+ userStoryId: row.story_id,
40
+ key: row.task_key,
41
+ title: row.task_title,
42
+ description: row.task_description ?? "",
43
+ type: row.task_type ?? undefined,
44
+ status: row.task_status,
45
+ storyPoints: row.task_story_points ?? undefined,
46
+ priority: row.task_priority ?? undefined,
47
+ assignedAgentId: row.task_assigned_agent_id ?? undefined,
48
+ assigneeHuman: row.task_assignee_human ?? undefined,
49
+ vcsBranch: row.task_vcs_branch ?? undefined,
50
+ vcsBaseBranch: row.task_vcs_base_branch ?? undefined,
51
+ vcsLastCommitSha: row.task_vcs_last_commit_sha ?? undefined,
52
+ metadata: row.task_metadata ? JSON.parse(row.task_metadata) : undefined,
53
+ openapiVersionAtCreation: undefined,
54
+ createdAt: row.task_created_at,
55
+ updatedAt: row.task_updated_at,
56
+ epicKey: row.epic_key,
57
+ storyKey: row.story_key,
58
+ epicTitle: row.epic_title ?? undefined,
59
+ storyTitle: row.story_title ?? undefined,
60
+ storyDescription: row.story_description ?? undefined,
61
+ acceptanceCriteria: parseAcceptanceCriteria(row.story_acceptance),
62
+ };
63
+ }
64
+ async loadTasks(filters) {
65
+ const clauses = [];
66
+ const params = [];
67
+ if (filters.project?.id) {
68
+ clauses.push("t.project_id = ?");
69
+ params.push(filters.project.id);
70
+ }
71
+ if (filters.epicKey) {
72
+ clauses.push("e.key = ?");
73
+ params.push(filters.epicKey);
74
+ }
75
+ if (filters.storyKey) {
76
+ clauses.push("us.key = ?");
77
+ params.push(filters.storyKey);
78
+ }
79
+ if (filters.taskKeys && filters.taskKeys.length > 0) {
80
+ clauses.push(`t.key IN (${filters.taskKeys.map(() => "?").join(", ")})`);
81
+ params.push(...filters.taskKeys);
82
+ }
83
+ const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
84
+ const db = this.workspaceRepo.getDb();
85
+ return db.all(`
86
+ SELECT
87
+ t.id as task_id,
88
+ t.project_id as project_id,
89
+ t.key as task_key,
90
+ t.status as task_status,
91
+ t.priority as task_priority,
92
+ t.story_points as task_story_points,
93
+ t.created_at as task_created_at,
94
+ t.updated_at as task_updated_at,
95
+ t.description as task_description,
96
+ t.title as task_title,
97
+ t.type as task_type,
98
+ t.metadata_json as task_metadata,
99
+ t.assigned_agent_id as task_assigned_agent_id,
100
+ t.assignee_human as task_assignee_human,
101
+ t.vcs_branch as task_vcs_branch,
102
+ t.vcs_base_branch as task_vcs_base_branch,
103
+ t.vcs_last_commit_sha as task_vcs_last_commit_sha,
104
+ e.id as epic_id,
105
+ e.key as epic_key,
106
+ e.title as epic_title,
107
+ e.description as epic_description,
108
+ us.id as story_id,
109
+ us.key as story_key,
110
+ us.title as story_title,
111
+ us.description as story_description,
112
+ us.acceptance_criteria as story_acceptance
113
+ FROM tasks t
114
+ JOIN epics e ON e.id = t.epic_id
115
+ JOIN user_stories us ON us.id = t.user_story_id
116
+ ${where}
117
+ `, ...params);
118
+ }
119
+ async loadDependencies(taskIds) {
120
+ if (taskIds.length === 0)
121
+ return new Map();
122
+ const placeholders = taskIds.map(() => "?").join(", ");
123
+ const db = this.workspaceRepo.getDb();
124
+ const rows = await db.all(`
125
+ SELECT td.id, td.task_id, td.depends_on_task_id, td.relation_type, td.created_at, td.updated_at,
126
+ dep.key as depends_on_key, dep.status as depends_on_status
127
+ FROM task_dependencies td
128
+ LEFT JOIN tasks dep ON dep.id = td.depends_on_task_id
129
+ WHERE td.task_id IN (${placeholders})
130
+ `, ...taskIds);
131
+ const grouped = new Map();
132
+ for (const row of rows) {
133
+ const normalized = {
134
+ taskId: row.task_id,
135
+ dependsOnTaskId: row.depends_on_task_id,
136
+ relationType: row.relation_type,
137
+ createdAt: row.created_at,
138
+ updatedAt: row.updated_at,
139
+ dependsOnKey: row.depends_on_key,
140
+ dependsOnStatus: row.depends_on_status,
141
+ };
142
+ const arr = grouped.get(normalized.taskId) ?? [];
143
+ arr.push(normalized);
144
+ grouped.set(normalized.taskId, arr);
145
+ }
146
+ return grouped;
147
+ }
148
+ topologicalOrder(tasks, deps) {
149
+ const warnings = [];
150
+ const taskMap = new Map(tasks.map((t) => [t.task.id, t]));
151
+ const indegree = new Map();
152
+ const edges = new Map();
153
+ for (const task of tasks) {
154
+ indegree.set(task.task.id, 0);
155
+ }
156
+ for (const task of tasks) {
157
+ const rels = deps.get(task.task.id) ?? [];
158
+ for (const dep of rels) {
159
+ if (!dep.dependsOnTaskId)
160
+ continue;
161
+ if (!taskMap.has(dep.dependsOnTaskId))
162
+ continue;
163
+ indegree.set(task.task.id, (indegree.get(task.task.id) ?? 0) + 1);
164
+ const list = edges.get(dep.dependsOnTaskId) ?? [];
165
+ list.push(task.task.id);
166
+ edges.set(dep.dependsOnTaskId, list);
167
+ }
168
+ }
169
+ const queue = [];
170
+ for (const task of tasks) {
171
+ if ((indegree.get(task.task.id) ?? 0) === 0) {
172
+ queue.push(task);
173
+ }
174
+ }
175
+ const ordered = [];
176
+ while (queue.length) {
177
+ queue.sort((a, b) => {
178
+ const pa = a.task.priority ?? Number.NEGATIVE_INFINITY;
179
+ const pb = b.task.priority ?? Number.NEGATIVE_INFINITY;
180
+ if (pa !== pb)
181
+ return pb - pa;
182
+ const spa = a.task.storyPoints ?? Number.POSITIVE_INFINITY;
183
+ const spb = b.task.storyPoints ?? Number.POSITIVE_INFINITY;
184
+ if (spa !== spb)
185
+ return spa - spb;
186
+ const ca = Date.parse(a.task.createdAt) || 0;
187
+ const cb = Date.parse(b.task.createdAt) || 0;
188
+ if (ca !== cb)
189
+ return ca - cb;
190
+ const sa = a.task.status === "in_progress" ? 0 : 1;
191
+ const sb = b.task.status === "in_progress" ? 0 : 1;
192
+ return sa - sb;
193
+ });
194
+ const current = queue.shift();
195
+ ordered.push(current);
196
+ const targets = edges.get(current.task.id) ?? [];
197
+ for (const tId of targets) {
198
+ indegree.set(tId, (indegree.get(tId) ?? 0) - 1);
199
+ if ((indegree.get(tId) ?? 0) === 0) {
200
+ const node = taskMap.get(tId);
201
+ if (node)
202
+ queue.push(node);
203
+ }
204
+ }
205
+ }
206
+ if (ordered.length !== tasks.length) {
207
+ warnings.push("Cycle detected in task dependencies; falling back to partial order.");
208
+ for (const task of tasks) {
209
+ if (!ordered.includes(task))
210
+ ordered.push(task);
211
+ }
212
+ }
213
+ return { ordered, warnings };
214
+ }
215
+ async selectTasks(filters) {
216
+ const project = await this.fetchProject(filters.projectKey);
217
+ if (filters.projectKey && !project) {
218
+ throw new Error(`Unknown project key: ${filters.projectKey}`);
219
+ }
220
+ const effectiveStatuses = normalizeStatusList(filters.statusFilter);
221
+ const allowBlocked = effectiveStatuses.includes("blocked");
222
+ const tasks = await this.loadTasks({ ...filters, project });
223
+ const filteredTasks = tasks.filter((task) => effectiveStatuses.includes(task.task_status.toLowerCase()));
224
+ const candidateIds = filteredTasks.map((t) => t.task_id);
225
+ const deps = await this.loadDependencies(candidateIds);
226
+ const taskMap = new Map();
227
+ for (const row of filteredTasks) {
228
+ const task = this.buildTaskFromRow(row);
229
+ taskMap.set(task.id, {
230
+ task,
231
+ dependencies: { ids: [], keys: [], blocking: [] },
232
+ });
233
+ }
234
+ const blocked = [];
235
+ const eligible = [];
236
+ for (const [taskId, entry] of taskMap.entries()) {
237
+ const depRows = deps.get(taskId) ?? [];
238
+ const ids = [];
239
+ const keys = [];
240
+ const blocking = [];
241
+ let blockedReason;
242
+ for (const dep of depRows) {
243
+ ids.push(dep.dependsOnTaskId);
244
+ if (dep.dependsOnKey)
245
+ keys.push(dep.dependsOnKey);
246
+ const status = dep.dependsOnStatus?.toLowerCase();
247
+ const depInSelection = taskMap.has(dep.dependsOnTaskId);
248
+ const clear = dep.dependsOnTaskId
249
+ ? DONE_DEPENDENCY_STATUSES.has(status ?? "")
250
+ : true;
251
+ if (!clear) {
252
+ blockedReason = "dependency_not_ready";
253
+ blocking.push(dep.dependsOnTaskId);
254
+ }
255
+ else if (!status && !depInSelection) {
256
+ // unknown status but dependency referenced; treat as blocked unless explicitly ignored
257
+ blockedReason = "dependency_not_ready";
258
+ blocking.push(dep.dependsOnTaskId);
259
+ }
260
+ }
261
+ entry.dependencies = { ids, keys, blocking };
262
+ if (blockedReason && !(allowBlocked || (filters.taskKeys ?? []).includes(entry.task.key))) {
263
+ entry.blockedReason = blockedReason;
264
+ blocked.push(entry);
265
+ }
266
+ else {
267
+ entry.blockedReason = blockedReason;
268
+ eligible.push(entry);
269
+ }
270
+ }
271
+ const { ordered, warnings } = this.topologicalOrder(eligible, deps);
272
+ const limited = typeof filters.limit === "number" && filters.limit > 0 ? ordered.slice(0, filters.limit) : ordered;
273
+ return {
274
+ project: project ?? undefined,
275
+ filters: { ...filters, effectiveStatuses },
276
+ ordered: limited,
277
+ blocked,
278
+ warnings,
279
+ };
280
+ }
281
+ }
@@ -0,0 +1,19 @@
1
+ import { WorkspaceRepository, TaskRow } from "@mcoda/db";
2
+ export declare class TaskStateService {
3
+ private workspaceRepo;
4
+ constructor(workspaceRepo: WorkspaceRepository);
5
+ transitionToInProgress(task: TaskRow): Promise<void>;
6
+ markReadyToReview(task: TaskRow, metadataPatch?: Record<string, unknown>): Promise<void>;
7
+ markReadyToQa(task: TaskRow, metadataPatch?: Record<string, unknown>): Promise<void>;
8
+ markCompleted(task: TaskRow, metadataPatch?: Record<string, unknown>): Promise<void>;
9
+ returnToInProgress(task: TaskRow, metadataPatch?: Record<string, unknown>): Promise<void>;
10
+ markBlocked(task: TaskRow, reason: string): Promise<void>;
11
+ recordReviewMetadata(task: TaskRow, metadata: {
12
+ decision: string;
13
+ agentId?: string | null;
14
+ modelName?: string | null;
15
+ jobId?: string | null;
16
+ reviewId?: string | null;
17
+ }): Promise<void>;
18
+ }
19
+ //# sourceMappingURL=TaskStateService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TaskStateService.d.ts","sourceRoot":"","sources":["../../../src/services/execution/TaskStateService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQzD,qBAAa,gBAAgB;IACf,OAAO,CAAC,aAAa;gBAAb,aAAa,EAAE,mBAAmB;IAEhD,sBAAsB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpD,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxF,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpF,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpF,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOzF,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQzD,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAW9K"}
@@ -0,0 +1,59 @@
1
+ const mergeMetadata = (existing, patch) => {
2
+ if (patch === undefined)
3
+ return existing;
4
+ if (patch === null)
5
+ return null;
6
+ return { ...(existing ?? {}), ...patch };
7
+ };
8
+ export class TaskStateService {
9
+ constructor(workspaceRepo) {
10
+ this.workspaceRepo = workspaceRepo;
11
+ }
12
+ async transitionToInProgress(task) {
13
+ if (task.status === "in_progress")
14
+ return;
15
+ await this.workspaceRepo.updateTask(task.id, { status: "in_progress" });
16
+ }
17
+ async markReadyToReview(task, metadataPatch) {
18
+ await this.workspaceRepo.updateTask(task.id, {
19
+ status: "ready_to_review",
20
+ metadata: mergeMetadata(task.metadata, metadataPatch ?? undefined) ?? undefined,
21
+ });
22
+ }
23
+ async markReadyToQa(task, metadataPatch) {
24
+ await this.workspaceRepo.updateTask(task.id, {
25
+ status: "ready_to_qa",
26
+ metadata: mergeMetadata(task.metadata, metadataPatch ?? undefined) ?? undefined,
27
+ });
28
+ }
29
+ async markCompleted(task, metadataPatch) {
30
+ await this.workspaceRepo.updateTask(task.id, {
31
+ status: "completed",
32
+ metadata: mergeMetadata(task.metadata, metadataPatch ?? undefined) ?? undefined,
33
+ });
34
+ }
35
+ async returnToInProgress(task, metadataPatch) {
36
+ await this.workspaceRepo.updateTask(task.id, {
37
+ status: "in_progress",
38
+ metadata: mergeMetadata(task.metadata, metadataPatch ?? undefined) ?? undefined,
39
+ });
40
+ }
41
+ async markBlocked(task, reason) {
42
+ const metadata = mergeMetadata(task.metadata, { blocked_reason: reason }) ?? { blocked_reason: reason };
43
+ await this.workspaceRepo.updateTask(task.id, {
44
+ status: "blocked",
45
+ metadata,
46
+ });
47
+ }
48
+ async recordReviewMetadata(task, metadata) {
49
+ const patch = mergeMetadata(task.metadata, {
50
+ last_review_decision: metadata.decision,
51
+ last_review_agent_id: metadata.agentId ?? null,
52
+ last_review_model: metadata.modelName ?? null,
53
+ last_review_job_id: metadata.jobId ?? null,
54
+ last_review_id: metadata.reviewId ?? null,
55
+ last_reviewed_at: new Date().toISOString(),
56
+ });
57
+ await this.workspaceRepo.updateTask(task.id, { metadata: patch ?? undefined });
58
+ }
59
+ }
@@ -0,0 +1,80 @@
1
+ import { AgentService } from "@mcoda/agents";
2
+ import { DocdexClient, VcsClient } from "@mcoda/integrations";
3
+ import { GlobalRepository, WorkspaceRepository } from "@mcoda/db";
4
+ import { WorkspaceResolution } from "../../workspace/WorkspaceManager.js";
5
+ import { JobService } from "../jobs/JobService.js";
6
+ import { TaskSelectionService, TaskSelectionFilters, TaskSelectionPlan } from "./TaskSelectionService.js";
7
+ import { TaskStateService } from "./TaskStateService.js";
8
+ import { RoutingService } from "../agents/RoutingService.js";
9
+ export interface WorkOnTasksRequest extends TaskSelectionFilters {
10
+ workspace: WorkspaceResolution;
11
+ noCommit?: boolean;
12
+ dryRun?: boolean;
13
+ agentName?: string;
14
+ agentStream?: boolean;
15
+ baseBranch?: string;
16
+ onAgentChunk?: (chunk: string) => void;
17
+ }
18
+ export interface TaskExecutionResult {
19
+ taskKey: string;
20
+ status: "succeeded" | "blocked" | "failed" | "skipped";
21
+ notes?: string;
22
+ branch?: string;
23
+ }
24
+ export interface WorkOnTasksResult {
25
+ jobId: string;
26
+ commandRunId: string;
27
+ selection: TaskSelectionPlan;
28
+ results: TaskExecutionResult[];
29
+ warnings: string[];
30
+ }
31
+ export declare class WorkOnTasksService {
32
+ private workspace;
33
+ private deps;
34
+ private selectionService;
35
+ private stateService;
36
+ private taskLogSeq;
37
+ private vcs;
38
+ private routingService;
39
+ private readPromptFiles;
40
+ constructor(workspace: WorkspaceResolution, deps: {
41
+ agentService: AgentService;
42
+ docdex: DocdexClient;
43
+ jobService: JobService;
44
+ workspaceRepo: WorkspaceRepository;
45
+ selectionService?: TaskSelectionService;
46
+ stateService?: TaskStateService;
47
+ repo: GlobalRepository;
48
+ vcsClient?: VcsClient;
49
+ routingService: RoutingService;
50
+ });
51
+ private loadPrompts;
52
+ private ensureMcoda;
53
+ private writeWorkCheckpoint;
54
+ private checkpoint;
55
+ static create(workspace: WorkspaceResolution): Promise<WorkOnTasksService>;
56
+ close(): Promise<void>;
57
+ private resolveAgent;
58
+ private nextLogSeq;
59
+ private logTask;
60
+ private recordTokenUsage;
61
+ private updateTaskPhase;
62
+ private gatherDocContext;
63
+ private buildPrompt;
64
+ private checkoutBaseBranch;
65
+ private commitPendingChanges;
66
+ private ensureBranches;
67
+ private formatGitError;
68
+ private isNonFastForwardPush;
69
+ private isNonFastForwardPull;
70
+ private isRemotePermissionError;
71
+ private isCommitHookFailure;
72
+ private isGpgSignFailure;
73
+ private pushWithRecovery;
74
+ private validateScope;
75
+ private applyPatches;
76
+ private applyFileBlocks;
77
+ private runTests;
78
+ workOnTasks(request: WorkOnTasksRequest): Promise<WorkOnTasksResult>;
79
+ }
80
+ //# sourceMappingURL=WorkOnTasksService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkOnTasksService.d.ts","sourceRoot":"","sources":["../../../src/services/execution/WorkOnTasksService.ts"],"names":[],"mappings":"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"}