@task-mcp/shared 0.1.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.
- package/dist/algorithms/critical-path.d.ts +46 -0
- package/dist/algorithms/critical-path.d.ts.map +1 -0
- package/dist/algorithms/critical-path.js +308 -0
- package/dist/algorithms/critical-path.js.map +1 -0
- package/dist/algorithms/critical-path.test.d.ts +2 -0
- package/dist/algorithms/critical-path.test.d.ts.map +1 -0
- package/dist/algorithms/critical-path.test.js +194 -0
- package/dist/algorithms/critical-path.test.js.map +1 -0
- package/dist/algorithms/index.d.ts +3 -0
- package/dist/algorithms/index.d.ts.map +1 -0
- package/dist/algorithms/index.js +3 -0
- package/dist/algorithms/index.js.map +1 -0
- package/dist/algorithms/topological-sort.d.ts +41 -0
- package/dist/algorithms/topological-sort.d.ts.map +1 -0
- package/dist/algorithms/topological-sort.js +168 -0
- package/dist/algorithms/topological-sort.js.map +1 -0
- package/dist/algorithms/topological-sort.test.d.ts +2 -0
- package/dist/algorithms/topological-sort.test.d.ts.map +1 -0
- package/dist/algorithms/topological-sort.test.js +162 -0
- package/dist/algorithms/topological-sort.test.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/index.d.ts +4 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +7 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/project.d.ts +55 -0
- package/dist/schemas/project.d.ts.map +1 -0
- package/dist/schemas/project.js +48 -0
- package/dist/schemas/project.js.map +1 -0
- package/dist/schemas/task.d.ts +124 -0
- package/dist/schemas/task.d.ts.map +1 -0
- package/dist/schemas/task.js +89 -0
- package/dist/schemas/task.js.map +1 -0
- package/dist/schemas/view.d.ts +44 -0
- package/dist/schemas/view.d.ts.map +1 -0
- package/dist/schemas/view.js +33 -0
- package/dist/schemas/view.js.map +1 -0
- package/dist/utils/date.d.ts +25 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +103 -0
- package/dist/utils/date.js.map +1 -0
- package/dist/utils/date.test.d.ts +2 -0
- package/dist/utils/date.test.d.ts.map +1 -0
- package/dist/utils/date.test.js +138 -0
- package/dist/utils/date.test.js.map +1 -0
- package/dist/utils/id.d.ts +27 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +41 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/natural-language.d.ts +12 -0
- package/dist/utils/natural-language.d.ts.map +1 -0
- package/dist/utils/natural-language.js +112 -0
- package/dist/utils/natural-language.js.map +1 -0
- package/dist/utils/natural-language.test.d.ts +2 -0
- package/dist/utils/natural-language.test.d.ts.map +1 -0
- package/dist/utils/natural-language.test.js +132 -0
- package/dist/utils/natural-language.test.js.map +1 -0
- package/package.json +46 -0
- package/src/algorithms/critical-path.test.ts +241 -0
- package/src/algorithms/critical-path.ts +413 -0
- package/src/algorithms/index.ts +17 -0
- package/src/algorithms/topological-sort.test.ts +190 -0
- package/src/algorithms/topological-sort.ts +204 -0
- package/src/index.ts +8 -0
- package/src/schemas/index.ts +30 -0
- package/src/schemas/project.ts +62 -0
- package/src/schemas/task.ts +116 -0
- package/src/schemas/view.ts +46 -0
- package/src/utils/date.test.ts +160 -0
- package/src/utils/date.ts +119 -0
- package/src/utils/id.ts +45 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/natural-language.test.ts +154 -0
- package/src/utils/natural-language.ts +125 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Task } from "../schemas/task.js";
|
|
2
|
+
/**
|
|
3
|
+
* Task with computed CPM (Critical Path Method) values
|
|
4
|
+
*/
|
|
5
|
+
export interface CPMTask extends Task {
|
|
6
|
+
earliestStart: number;
|
|
7
|
+
earliestFinish: number;
|
|
8
|
+
latestStart: number;
|
|
9
|
+
latestFinish: number;
|
|
10
|
+
slack: number;
|
|
11
|
+
isCritical: boolean;
|
|
12
|
+
dependentCount: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Result of critical path analysis
|
|
16
|
+
*/
|
|
17
|
+
export interface CPMResult {
|
|
18
|
+
tasks: CPMTask[];
|
|
19
|
+
criticalPath: CPMTask[];
|
|
20
|
+
projectDuration: number;
|
|
21
|
+
bottlenecks: CPMTask[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Perform Critical Path Method analysis
|
|
25
|
+
*
|
|
26
|
+
* CPM calculates:
|
|
27
|
+
* - Earliest Start (ES): Earliest a task can start
|
|
28
|
+
* - Earliest Finish (EF): ES + duration
|
|
29
|
+
* - Latest Finish (LF): Latest a task can finish without delaying project
|
|
30
|
+
* - Latest Start (LS): LF - duration
|
|
31
|
+
* - Slack: LS - ES (or LF - EF)
|
|
32
|
+
* - Critical Path: Tasks with slack = 0
|
|
33
|
+
*/
|
|
34
|
+
export declare function criticalPathAnalysis(tasks: Task[]): CPMResult;
|
|
35
|
+
/**
|
|
36
|
+
* Find tasks that can be executed in parallel (no dependencies between them)
|
|
37
|
+
*/
|
|
38
|
+
export declare function findParallelTasks(tasks: Task[]): Task[][];
|
|
39
|
+
/**
|
|
40
|
+
* Suggest the next best task to work on
|
|
41
|
+
*/
|
|
42
|
+
export declare function suggestNextTask(tasks: Task[], options?: {
|
|
43
|
+
contexts?: string[];
|
|
44
|
+
maxMinutes?: number;
|
|
45
|
+
}): Task | null;
|
|
46
|
+
//# sourceMappingURL=critical-path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"critical-path.d.ts","sourceRoot":"","sources":["../../src/algorithms/critical-path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAG/C;;GAEG;AACH,MAAM,WAAW,OAAQ,SAAQ,IAAI;IAEnC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,YAAY,EAAE,OAAO,EAAE,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,OAAO,EAAE,CAAC;CACxB;AAiMD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,SAAS,CAgD7D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,CAiDzD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,IAAI,EAAE,EACb,OAAO,GAAE;IACP,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CAChB,GACL,IAAI,GAAG,IAAI,CAyDb"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { topologicalSort, findDependents } from "./topological-sort.js";
|
|
2
|
+
/** Default task duration if no estimate provided */
|
|
3
|
+
const DEFAULT_DURATION = 30;
|
|
4
|
+
/**
|
|
5
|
+
* Get task duration with fallback to default
|
|
6
|
+
*/
|
|
7
|
+
function getTaskDuration(task) {
|
|
8
|
+
return task.estimate?.expected ?? DEFAULT_DURATION;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get blocked_by dependencies for a task
|
|
12
|
+
*/
|
|
13
|
+
function getBlockedByDeps(task) {
|
|
14
|
+
return (task.dependencies ?? [])
|
|
15
|
+
.filter((d) => d.type === "blocked_by")
|
|
16
|
+
.map((d) => d.taskId);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Initialize CPM tasks with default values
|
|
20
|
+
*/
|
|
21
|
+
function initializeCPMTasks(sortedTasks) {
|
|
22
|
+
const taskMap = new Map();
|
|
23
|
+
for (const task of sortedTasks) {
|
|
24
|
+
const duration = getTaskDuration(task);
|
|
25
|
+
taskMap.set(task.id, {
|
|
26
|
+
...task,
|
|
27
|
+
earliestStart: 0,
|
|
28
|
+
earliestFinish: duration,
|
|
29
|
+
latestStart: Infinity,
|
|
30
|
+
latestFinish: Infinity,
|
|
31
|
+
slack: 0,
|
|
32
|
+
isCritical: false,
|
|
33
|
+
dependentCount: 0,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return taskMap;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Forward pass: Calculate earliest start/finish times
|
|
40
|
+
* ES = max(EF of all predecessors)
|
|
41
|
+
* EF = ES + duration
|
|
42
|
+
*/
|
|
43
|
+
function forwardPass(sortedTasks, taskMap) {
|
|
44
|
+
for (const task of sortedTasks) {
|
|
45
|
+
const cpmTask = taskMap.get(task.id);
|
|
46
|
+
if (!cpmTask)
|
|
47
|
+
continue;
|
|
48
|
+
const duration = getTaskDuration(task);
|
|
49
|
+
const deps = getBlockedByDeps(task);
|
|
50
|
+
// Find maximum EF among predecessors
|
|
51
|
+
let maxPredecessorEF = 0;
|
|
52
|
+
for (const depId of deps) {
|
|
53
|
+
const dep = taskMap.get(depId);
|
|
54
|
+
if (dep) {
|
|
55
|
+
maxPredecessorEF = Math.max(maxPredecessorEF, dep.earliestFinish);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
cpmTask.earliestStart = maxPredecessorEF;
|
|
59
|
+
cpmTask.earliestFinish = cpmTask.earliestStart + duration;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Calculate project duration (maximum earliest finish)
|
|
64
|
+
*/
|
|
65
|
+
function calculateProjectDuration(taskMap) {
|
|
66
|
+
return Math.max(...Array.from(taskMap.values()).map((t) => t.earliestFinish));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Find tasks that have successors (are dependencies of other tasks)
|
|
70
|
+
*/
|
|
71
|
+
function findTasksWithSuccessors(sortedTasks) {
|
|
72
|
+
const tasksWithSuccessors = new Set();
|
|
73
|
+
for (const task of sortedTasks) {
|
|
74
|
+
const deps = getBlockedByDeps(task);
|
|
75
|
+
for (const depId of deps) {
|
|
76
|
+
tasksWithSuccessors.add(depId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return tasksWithSuccessors;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Find successor tasks (tasks that depend on a given task)
|
|
83
|
+
*/
|
|
84
|
+
function findSuccessors(taskId, taskMap) {
|
|
85
|
+
const successors = [];
|
|
86
|
+
for (const other of taskMap.values()) {
|
|
87
|
+
const otherDeps = getBlockedByDeps(other);
|
|
88
|
+
if (otherDeps.includes(taskId)) {
|
|
89
|
+
successors.push(other);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return successors;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Backward pass: Calculate latest start/finish times
|
|
96
|
+
* LF = min(LS of all successors) or projectDuration for end tasks
|
|
97
|
+
* LS = LF - duration
|
|
98
|
+
*/
|
|
99
|
+
function backwardPass(sortedTasks, taskMap, projectDuration) {
|
|
100
|
+
const tasksWithSuccessors = findTasksWithSuccessors(sortedTasks);
|
|
101
|
+
// Initialize end tasks (tasks with no successors)
|
|
102
|
+
for (const task of taskMap.values()) {
|
|
103
|
+
if (!tasksWithSuccessors.has(task.id)) {
|
|
104
|
+
const duration = getTaskDuration(task);
|
|
105
|
+
task.latestFinish = projectDuration;
|
|
106
|
+
task.latestStart = task.latestFinish - duration;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Process in reverse topological order
|
|
110
|
+
const reverseSorted = [...sortedTasks].reverse();
|
|
111
|
+
for (const task of reverseSorted) {
|
|
112
|
+
const cpmTask = taskMap.get(task.id);
|
|
113
|
+
if (!cpmTask)
|
|
114
|
+
continue;
|
|
115
|
+
const duration = getTaskDuration(task);
|
|
116
|
+
const successors = findSuccessors(task.id, taskMap);
|
|
117
|
+
if (successors.length > 0) {
|
|
118
|
+
// LF = min(LS of all successors)
|
|
119
|
+
cpmTask.latestFinish = Math.min(...successors.map((s) => s.latestStart));
|
|
120
|
+
cpmTask.latestStart = cpmTask.latestFinish - duration;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Calculate slack and mark critical tasks
|
|
126
|
+
* Slack = LS - ES (or LF - EF)
|
|
127
|
+
* Critical = slack ≈ 0
|
|
128
|
+
*/
|
|
129
|
+
function calculateSlackAndCritical(taskMap) {
|
|
130
|
+
const FLOAT_TOLERANCE = 0.001;
|
|
131
|
+
for (const task of taskMap.values()) {
|
|
132
|
+
task.slack = task.latestStart - task.earliestStart;
|
|
133
|
+
task.isCritical = Math.abs(task.slack) < FLOAT_TOLERANCE;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Count dependents for bottleneck detection
|
|
138
|
+
*/
|
|
139
|
+
function countDependents(sortedTasks, activeTasks, taskMap) {
|
|
140
|
+
for (const task of sortedTasks) {
|
|
141
|
+
const cpmTask = taskMap.get(task.id);
|
|
142
|
+
if (cpmTask) {
|
|
143
|
+
cpmTask.dependentCount = findDependents(activeTasks, task.id).length;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Extract critical path tasks in topological order
|
|
149
|
+
*/
|
|
150
|
+
function extractCriticalPath(sortedTasks, taskMap) {
|
|
151
|
+
return sortedTasks
|
|
152
|
+
.map((t) => taskMap.get(t.id))
|
|
153
|
+
.filter((t) => t !== undefined && t.isCritical);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Find top bottlenecks (critical tasks blocking the most downstream work)
|
|
157
|
+
*/
|
|
158
|
+
function findBottlenecks(criticalPath, limit = 5) {
|
|
159
|
+
return [...criticalPath]
|
|
160
|
+
.sort((a, b) => b.dependentCount - a.dependentCount)
|
|
161
|
+
.slice(0, limit);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Perform Critical Path Method analysis
|
|
165
|
+
*
|
|
166
|
+
* CPM calculates:
|
|
167
|
+
* - Earliest Start (ES): Earliest a task can start
|
|
168
|
+
* - Earliest Finish (EF): ES + duration
|
|
169
|
+
* - Latest Finish (LF): Latest a task can finish without delaying project
|
|
170
|
+
* - Latest Start (LS): LF - duration
|
|
171
|
+
* - Slack: LS - ES (or LF - EF)
|
|
172
|
+
* - Critical Path: Tasks with slack = 0
|
|
173
|
+
*/
|
|
174
|
+
export function criticalPathAnalysis(tasks) {
|
|
175
|
+
// Filter to only pending/in_progress tasks
|
|
176
|
+
const activeTasks = tasks.filter((t) => t.status === "pending" || t.status === "in_progress");
|
|
177
|
+
if (activeTasks.length === 0) {
|
|
178
|
+
return {
|
|
179
|
+
tasks: [],
|
|
180
|
+
criticalPath: [],
|
|
181
|
+
projectDuration: 0,
|
|
182
|
+
bottlenecks: [],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
// Sort topologically
|
|
186
|
+
const sortedTasks = topologicalSort(activeTasks);
|
|
187
|
+
// Initialize CPM tasks
|
|
188
|
+
const taskMap = initializeCPMTasks(sortedTasks);
|
|
189
|
+
// Forward pass: Calculate earliest start/finish
|
|
190
|
+
forwardPass(sortedTasks, taskMap);
|
|
191
|
+
// Calculate project duration
|
|
192
|
+
const projectDuration = calculateProjectDuration(taskMap);
|
|
193
|
+
// Backward pass: Calculate latest start/finish
|
|
194
|
+
backwardPass(sortedTasks, taskMap, projectDuration);
|
|
195
|
+
// Calculate slack and mark critical tasks
|
|
196
|
+
calculateSlackAndCritical(taskMap);
|
|
197
|
+
// Count dependents for bottleneck detection
|
|
198
|
+
countDependents(sortedTasks, activeTasks, taskMap);
|
|
199
|
+
// Extract critical path
|
|
200
|
+
const criticalPath = extractCriticalPath(sortedTasks, taskMap);
|
|
201
|
+
// Find bottlenecks
|
|
202
|
+
const bottlenecks = findBottlenecks(criticalPath);
|
|
203
|
+
return {
|
|
204
|
+
tasks: sortedTasks.map((t) => taskMap.get(t.id)),
|
|
205
|
+
criticalPath,
|
|
206
|
+
projectDuration,
|
|
207
|
+
bottlenecks,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Find tasks that can be executed in parallel (no dependencies between them)
|
|
212
|
+
*/
|
|
213
|
+
export function findParallelTasks(tasks) {
|
|
214
|
+
const activeTasks = tasks.filter((t) => t.status === "pending" || t.status === "in_progress");
|
|
215
|
+
if (activeTasks.length === 0)
|
|
216
|
+
return [];
|
|
217
|
+
// Find tasks with no uncompleted dependencies
|
|
218
|
+
const completedIds = new Set(tasks.filter((t) => t.status === "completed").map((t) => t.id));
|
|
219
|
+
const available = activeTasks.filter((task) => {
|
|
220
|
+
const deps = getBlockedByDeps(task);
|
|
221
|
+
return deps.every((depId) => completedIds.has(depId));
|
|
222
|
+
});
|
|
223
|
+
if (available.length <= 1)
|
|
224
|
+
return [available];
|
|
225
|
+
// Group tasks that don't depend on each other
|
|
226
|
+
const groups = [];
|
|
227
|
+
const processed = new Set();
|
|
228
|
+
for (const task of available) {
|
|
229
|
+
if (processed.has(task.id))
|
|
230
|
+
continue;
|
|
231
|
+
const group = [task];
|
|
232
|
+
processed.add(task.id);
|
|
233
|
+
for (const other of available) {
|
|
234
|
+
if (processed.has(other.id))
|
|
235
|
+
continue;
|
|
236
|
+
// Check if these tasks are independent
|
|
237
|
+
const taskDeps = (task.dependencies ?? []).map((d) => d.taskId);
|
|
238
|
+
const otherDeps = (other.dependencies ?? []).map((d) => d.taskId);
|
|
239
|
+
const independent = !taskDeps.includes(other.id) && !otherDeps.includes(task.id);
|
|
240
|
+
if (independent) {
|
|
241
|
+
group.push(other);
|
|
242
|
+
processed.add(other.id);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
groups.push(group);
|
|
246
|
+
}
|
|
247
|
+
return groups;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Suggest the next best task to work on
|
|
251
|
+
*/
|
|
252
|
+
export function suggestNextTask(tasks, options = {}) {
|
|
253
|
+
const activeTasks = tasks.filter((t) => t.status === "pending" || t.status === "in_progress");
|
|
254
|
+
if (activeTasks.length === 0)
|
|
255
|
+
return null;
|
|
256
|
+
// Get CPM analysis
|
|
257
|
+
const cpm = criticalPathAnalysis(tasks);
|
|
258
|
+
// Filter by availability (all dependencies completed)
|
|
259
|
+
const completedIds = new Set(tasks.filter((t) => t.status === "completed").map((t) => t.id));
|
|
260
|
+
let candidates = cpm.tasks.filter((task) => {
|
|
261
|
+
const deps = getBlockedByDeps(task);
|
|
262
|
+
return deps.every((depId) => completedIds.has(depId));
|
|
263
|
+
});
|
|
264
|
+
// Filter by context if specified
|
|
265
|
+
if (options.contexts?.length) {
|
|
266
|
+
const contextSet = new Set(options.contexts);
|
|
267
|
+
const contextFiltered = candidates.filter((t) => (t.contexts ?? []).some((c) => contextSet.has(c)));
|
|
268
|
+
if (contextFiltered.length > 0) {
|
|
269
|
+
candidates = contextFiltered;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Filter by time if specified
|
|
273
|
+
if (options.maxMinutes) {
|
|
274
|
+
const timeFiltered = candidates.filter((t) => getTaskDuration(t) <= options.maxMinutes);
|
|
275
|
+
if (timeFiltered.length > 0) {
|
|
276
|
+
candidates = timeFiltered;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (candidates.length === 0)
|
|
280
|
+
return null;
|
|
281
|
+
// Score and rank candidates
|
|
282
|
+
// Priority: Critical path > High priority > Most dependents > Shortest duration
|
|
283
|
+
const scored = candidates.map((task) => {
|
|
284
|
+
let score = 0;
|
|
285
|
+
if (task.isCritical)
|
|
286
|
+
score += 1000;
|
|
287
|
+
score += task.dependentCount * 100;
|
|
288
|
+
score += priorityScore(task.priority) * 10;
|
|
289
|
+
// Prefer shorter tasks (quick wins)
|
|
290
|
+
score += Math.max(0, 100 - getTaskDuration(task));
|
|
291
|
+
return { task, score };
|
|
292
|
+
});
|
|
293
|
+
scored.sort((a, b) => b.score - a.score);
|
|
294
|
+
return scored[0]?.task ?? null;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Convert priority to numeric score
|
|
298
|
+
*/
|
|
299
|
+
function priorityScore(priority) {
|
|
300
|
+
const scores = {
|
|
301
|
+
critical: 4,
|
|
302
|
+
high: 3,
|
|
303
|
+
medium: 2,
|
|
304
|
+
low: 1,
|
|
305
|
+
};
|
|
306
|
+
return scores[priority] ?? 2;
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=critical-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"critical-path.js","sourceRoot":"","sources":["../../src/algorithms/critical-path.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"critical-path.test.d.ts","sourceRoot":"","sources":["../../src/algorithms/critical-path.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { criticalPathAnalysis, findParallelTasks, suggestNextTask, } from "./critical-path.js";
|
|
3
|
+
// Helper to create mock tasks
|
|
4
|
+
function createTask(id, options = {}) {
|
|
5
|
+
const task = {
|
|
6
|
+
id,
|
|
7
|
+
title: `Task ${id}`,
|
|
8
|
+
status: (options.status ?? "pending"),
|
|
9
|
+
priority: (options.priority ?? "medium"),
|
|
10
|
+
projectId: "test-project",
|
|
11
|
+
createdAt: new Date().toISOString(),
|
|
12
|
+
updatedAt: new Date().toISOString(),
|
|
13
|
+
dependencies: (options.deps ?? []).map((depId) => ({
|
|
14
|
+
taskId: depId,
|
|
15
|
+
type: "blocked_by",
|
|
16
|
+
})),
|
|
17
|
+
};
|
|
18
|
+
if (options.estimate) {
|
|
19
|
+
task.estimate = { expected: options.estimate, confidence: "medium" };
|
|
20
|
+
}
|
|
21
|
+
if (options.contexts) {
|
|
22
|
+
task.contexts = options.contexts;
|
|
23
|
+
}
|
|
24
|
+
return task;
|
|
25
|
+
}
|
|
26
|
+
describe("criticalPathAnalysis", () => {
|
|
27
|
+
test("returns empty result for empty input", () => {
|
|
28
|
+
const result = criticalPathAnalysis([]);
|
|
29
|
+
expect(result.tasks).toEqual([]);
|
|
30
|
+
expect(result.criticalPath).toEqual([]);
|
|
31
|
+
expect(result.projectDuration).toBe(0);
|
|
32
|
+
});
|
|
33
|
+
test("returns empty for all completed tasks", () => {
|
|
34
|
+
const tasks = [createTask("A", { status: "completed" })];
|
|
35
|
+
const result = criticalPathAnalysis(tasks);
|
|
36
|
+
expect(result.tasks).toEqual([]);
|
|
37
|
+
});
|
|
38
|
+
test("calculates single task correctly", () => {
|
|
39
|
+
const tasks = [createTask("A", { estimate: 60 })];
|
|
40
|
+
const result = criticalPathAnalysis(tasks);
|
|
41
|
+
expect(result.projectDuration).toBe(60);
|
|
42
|
+
expect(result.criticalPath.length).toBe(1);
|
|
43
|
+
expect(result.criticalPath[0].id).toBe("A");
|
|
44
|
+
expect(result.criticalPath[0].isCritical).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
test("identifies critical path in linear chain", () => {
|
|
47
|
+
// A -> B -> C (each 30 min)
|
|
48
|
+
const tasks = [
|
|
49
|
+
createTask("A", { estimate: 30 }),
|
|
50
|
+
createTask("B", { estimate: 30, deps: ["A"] }),
|
|
51
|
+
createTask("C", { estimate: 30, deps: ["B"] }),
|
|
52
|
+
];
|
|
53
|
+
const result = criticalPathAnalysis(tasks);
|
|
54
|
+
expect(result.projectDuration).toBe(90);
|
|
55
|
+
expect(result.criticalPath.length).toBe(3);
|
|
56
|
+
expect(result.criticalPath.map((t) => t.id)).toEqual(["A", "B", "C"]);
|
|
57
|
+
});
|
|
58
|
+
test("calculates slack for parallel tasks", () => {
|
|
59
|
+
// A (60) and B (30) both lead to C
|
|
60
|
+
// A is on critical path, B has 30 min slack
|
|
61
|
+
const tasks = [
|
|
62
|
+
createTask("A", { estimate: 60 }),
|
|
63
|
+
createTask("B", { estimate: 30 }),
|
|
64
|
+
createTask("C", { estimate: 30, deps: ["A", "B"] }),
|
|
65
|
+
];
|
|
66
|
+
const result = criticalPathAnalysis(tasks);
|
|
67
|
+
expect(result.projectDuration).toBe(90); // A + C
|
|
68
|
+
const taskA = result.tasks.find((t) => t.id === "A");
|
|
69
|
+
const taskB = result.tasks.find((t) => t.id === "B");
|
|
70
|
+
const taskC = result.tasks.find((t) => t.id === "C");
|
|
71
|
+
expect(taskA.isCritical).toBe(true);
|
|
72
|
+
expect(taskB.slack).toBe(30); // B can start 30 min late
|
|
73
|
+
expect(taskC.isCritical).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
test("identifies bottlenecks by dependent count", () => {
|
|
76
|
+
// A blocks B, C, D
|
|
77
|
+
const tasks = [
|
|
78
|
+
createTask("A", { estimate: 30 }),
|
|
79
|
+
createTask("B", { estimate: 30, deps: ["A"] }),
|
|
80
|
+
createTask("C", { estimate: 30, deps: ["A"] }),
|
|
81
|
+
createTask("D", { estimate: 30, deps: ["A"] }),
|
|
82
|
+
];
|
|
83
|
+
const result = criticalPathAnalysis(tasks);
|
|
84
|
+
expect(result.bottlenecks.length).toBeGreaterThan(0);
|
|
85
|
+
expect(result.bottlenecks[0].id).toBe("A"); // A blocks the most tasks
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe("findParallelTasks", () => {
|
|
89
|
+
test("returns empty for empty input", () => {
|
|
90
|
+
const result = findParallelTasks([]);
|
|
91
|
+
expect(result).toEqual([]);
|
|
92
|
+
});
|
|
93
|
+
test("returns single group for independent tasks", () => {
|
|
94
|
+
const tasks = [
|
|
95
|
+
createTask("A"),
|
|
96
|
+
createTask("B"),
|
|
97
|
+
createTask("C"),
|
|
98
|
+
];
|
|
99
|
+
const result = findParallelTasks(tasks);
|
|
100
|
+
expect(result.length).toBe(1);
|
|
101
|
+
expect(result[0].length).toBe(3);
|
|
102
|
+
});
|
|
103
|
+
test("excludes tasks with uncompleted dependencies", () => {
|
|
104
|
+
const tasks = [
|
|
105
|
+
createTask("A"),
|
|
106
|
+
createTask("B", { deps: ["A"] }),
|
|
107
|
+
];
|
|
108
|
+
const result = findParallelTasks(tasks);
|
|
109
|
+
// Only A is available (B is blocked)
|
|
110
|
+
expect(result.length).toBe(1);
|
|
111
|
+
expect(result[0].length).toBe(1);
|
|
112
|
+
expect(result[0][0].id).toBe("A");
|
|
113
|
+
});
|
|
114
|
+
test("includes task when dependency is completed", () => {
|
|
115
|
+
const tasks = [
|
|
116
|
+
createTask("A", { status: "completed" }),
|
|
117
|
+
createTask("B", { deps: ["A"] }),
|
|
118
|
+
createTask("C"),
|
|
119
|
+
];
|
|
120
|
+
const result = findParallelTasks(tasks);
|
|
121
|
+
// B and C can run in parallel
|
|
122
|
+
expect(result.length).toBe(1);
|
|
123
|
+
expect(result[0].map((t) => t.id).sort()).toEqual(["B", "C"]);
|
|
124
|
+
});
|
|
125
|
+
test("excludes completed tasks from result", () => {
|
|
126
|
+
const tasks = [
|
|
127
|
+
createTask("A", { status: "completed" }),
|
|
128
|
+
createTask("B"),
|
|
129
|
+
];
|
|
130
|
+
const result = findParallelTasks(tasks);
|
|
131
|
+
expect(result.length).toBe(1);
|
|
132
|
+
expect(result[0].length).toBe(1);
|
|
133
|
+
expect(result[0][0].id).toBe("B");
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe("suggestNextTask", () => {
|
|
137
|
+
test("returns null for empty input", () => {
|
|
138
|
+
const result = suggestNextTask([]);
|
|
139
|
+
expect(result).toBeNull();
|
|
140
|
+
});
|
|
141
|
+
test("returns null when all tasks completed", () => {
|
|
142
|
+
const tasks = [createTask("A", { status: "completed" })];
|
|
143
|
+
const result = suggestNextTask(tasks);
|
|
144
|
+
expect(result).toBeNull();
|
|
145
|
+
});
|
|
146
|
+
test("prefers critical path tasks", () => {
|
|
147
|
+
// A is critical (longer), B has slack
|
|
148
|
+
const tasks = [
|
|
149
|
+
createTask("A", { estimate: 60, priority: "low" }),
|
|
150
|
+
createTask("B", { estimate: 30, priority: "high" }),
|
|
151
|
+
createTask("C", { estimate: 30, deps: ["A", "B"] }),
|
|
152
|
+
];
|
|
153
|
+
const result = suggestNextTask(tasks);
|
|
154
|
+
// A is on critical path, should be suggested first
|
|
155
|
+
expect(result.id).toBe("A");
|
|
156
|
+
});
|
|
157
|
+
test("filters by context when specified", () => {
|
|
158
|
+
const tasks = [
|
|
159
|
+
createTask("A", { priority: "critical", contexts: ["office"] }),
|
|
160
|
+
createTask("B", { priority: "high", contexts: ["focus"] }),
|
|
161
|
+
];
|
|
162
|
+
const result = suggestNextTask(tasks, { contexts: ["focus"] });
|
|
163
|
+
expect(result.id).toBe("B");
|
|
164
|
+
});
|
|
165
|
+
test("filters by max time when specified", () => {
|
|
166
|
+
const tasks = [
|
|
167
|
+
createTask("A", { estimate: 120, priority: "critical" }),
|
|
168
|
+
createTask("B", { estimate: 30, priority: "high" }),
|
|
169
|
+
];
|
|
170
|
+
const result = suggestNextTask(tasks, { maxMinutes: 60 });
|
|
171
|
+
expect(result.id).toBe("B");
|
|
172
|
+
});
|
|
173
|
+
test("skips blocked tasks", () => {
|
|
174
|
+
const tasks = [
|
|
175
|
+
createTask("A", { priority: "low" }),
|
|
176
|
+
createTask("B", { priority: "critical", deps: ["A"] }),
|
|
177
|
+
];
|
|
178
|
+
const result = suggestNextTask(tasks);
|
|
179
|
+
// B is blocked, so A should be suggested
|
|
180
|
+
expect(result.id).toBe("A");
|
|
181
|
+
});
|
|
182
|
+
test("considers tasks with more dependents", () => {
|
|
183
|
+
const tasks = [
|
|
184
|
+
createTask("A", { priority: "medium" }),
|
|
185
|
+
createTask("B", { priority: "medium" }),
|
|
186
|
+
createTask("C", { priority: "medium", deps: ["A"] }),
|
|
187
|
+
createTask("D", { priority: "medium", deps: ["A"] }),
|
|
188
|
+
];
|
|
189
|
+
const result = suggestNextTask(tasks);
|
|
190
|
+
// A blocks more tasks (C and D), should be preferred over B
|
|
191
|
+
expect(result.id).toBe("A");
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
//# sourceMappingURL=critical-path.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"critical-path.test.js","sourceRoot":"","sources":["../../src/algorithms/critical-path.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { topologicalSort, wouldCreateCycle, findDependents, findDependencies, priorityToNumber, taskToNode, type TaskNode, } from "./topological-sort.js";
|
|
2
|
+
export { criticalPathAnalysis, findParallelTasks, suggestNextTask, type CPMTask, type CPMResult, } from "./critical-path.js";
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/algorithms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,KAAK,QAAQ,GACd,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,EACf,KAAK,OAAO,EACZ,KAAK,SAAS,GACf,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/algorithms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,GAEX,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,GAGhB,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Task } from "../schemas/task.js";
|
|
2
|
+
/**
|
|
3
|
+
* Node representation for graph algorithms
|
|
4
|
+
*/
|
|
5
|
+
export interface TaskNode {
|
|
6
|
+
id: string;
|
|
7
|
+
dependencies: string[];
|
|
8
|
+
priority: number;
|
|
9
|
+
estimate: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Convert priority string to numeric value
|
|
13
|
+
*/
|
|
14
|
+
export declare function priorityToNumber(priority: string): number;
|
|
15
|
+
/**
|
|
16
|
+
* Convert Task to TaskNode
|
|
17
|
+
*/
|
|
18
|
+
export declare function taskToNode(task: Task): TaskNode;
|
|
19
|
+
/**
|
|
20
|
+
* Topological sort using Kahn's algorithm with priority tie-breaking
|
|
21
|
+
*
|
|
22
|
+
* Returns tasks in optimal execution order:
|
|
23
|
+
* 1. Respects dependencies (blocked_by relationships)
|
|
24
|
+
* 2. Higher priority tasks come first when dependencies allow
|
|
25
|
+
*
|
|
26
|
+
* @throws Error if circular dependency detected
|
|
27
|
+
*/
|
|
28
|
+
export declare function topologicalSort(tasks: Task[]): Task[];
|
|
29
|
+
/**
|
|
30
|
+
* Detect if adding a dependency would create a cycle
|
|
31
|
+
*/
|
|
32
|
+
export declare function wouldCreateCycle(tasks: Task[], fromId: string, toId: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Find all tasks that depend on a given task (directly or transitively)
|
|
35
|
+
*/
|
|
36
|
+
export declare function findDependents(tasks: Task[], taskId: string): Task[];
|
|
37
|
+
/**
|
|
38
|
+
* Find all tasks that a given task depends on (directly or transitively)
|
|
39
|
+
*/
|
|
40
|
+
export declare function findDependencies(tasks: Task[], taskId: string): Task[];
|
|
41
|
+
//# sourceMappingURL=topological-sort.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topological-sort.d.ts","sourceRoot":"","sources":["../../src/algorithms/topological-sort.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQzD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ,CAS/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAmErD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,IAAI,EAAE,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAqBT;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,CAqBpE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,CA2BtE"}
|