@task-mcp/shared 1.0.29 → 1.0.30
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/schemas/inbox.d.ts +2 -2
- package/dist/schemas/response-format.d.ts +11 -0
- package/dist/schemas/response-format.d.ts.map +1 -1
- package/dist/schemas/response-format.js.map +1 -1
- package/dist/schemas/session.d.ts +12 -12
- package/dist/schemas/state.d.ts +2 -2
- package/dist/schemas/task.d.ts +9 -0
- package/dist/schemas/task.d.ts.map +1 -1
- package/dist/schemas/task.js +3 -0
- package/dist/schemas/task.js.map +1 -1
- package/dist/utils/clustering.d.ts +60 -0
- package/dist/utils/clustering.d.ts.map +1 -0
- package/dist/utils/clustering.js +283 -0
- package/dist/utils/clustering.js.map +1 -0
- package/dist/utils/clustering.test.d.ts +2 -0
- package/dist/utils/clustering.test.d.ts.map +1 -0
- package/dist/utils/clustering.test.js +237 -0
- package/dist/utils/clustering.test.js.map +1 -0
- package/dist/utils/env.d.ts +24 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +40 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/hierarchy.d.ts.map +1 -1
- package/dist/utils/hierarchy.js +7 -3
- package/dist/utils/hierarchy.js.map +1 -1
- package/dist/utils/index.d.ts +4 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +7 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/intent-extractor.d.ts +30 -0
- package/dist/utils/intent-extractor.d.ts.map +1 -0
- package/dist/utils/intent-extractor.js +135 -0
- package/dist/utils/intent-extractor.js.map +1 -0
- package/dist/utils/intent-extractor.test.d.ts +2 -0
- package/dist/utils/intent-extractor.test.d.ts.map +1 -0
- package/dist/utils/intent-extractor.test.js +69 -0
- package/dist/utils/intent-extractor.test.js.map +1 -0
- package/dist/utils/natural-language.d.ts.map +1 -1
- package/dist/utils/natural-language.js +9 -8
- package/dist/utils/natural-language.js.map +1 -1
- package/dist/utils/natural-language.test.js +22 -0
- package/dist/utils/natural-language.test.js.map +1 -1
- package/dist/utils/plan-parser.d.ts.map +1 -1
- package/dist/utils/plan-parser.js +2 -8
- package/dist/utils/plan-parser.js.map +1 -1
- package/dist/utils/projection.d.ts.map +1 -1
- package/dist/utils/projection.js +43 -1
- package/dist/utils/projection.js.map +1 -1
- package/dist/utils/projection.test.js +57 -7
- package/dist/utils/projection.test.js.map +1 -1
- package/dist/utils/terminal-ui.test.js +13 -22
- package/dist/utils/terminal-ui.test.js.map +1 -1
- package/package.json +1 -1
- package/src/schemas/response-format.ts +15 -2
- package/src/schemas/task.ts +3 -0
- package/src/utils/clustering.test.ts +285 -0
- package/src/utils/clustering.ts +336 -0
- package/src/utils/env.ts +41 -0
- package/src/utils/hierarchy.ts +9 -5
- package/src/utils/index.ts +17 -0
- package/src/utils/intent-extractor.test.ts +84 -0
- package/src/utils/intent-extractor.ts +156 -0
- package/src/utils/natural-language.test.ts +27 -0
- package/src/utils/natural-language.ts +10 -9
- package/src/utils/plan-parser.ts +4 -16
- package/src/utils/projection.test.ts +61 -7
- package/src/utils/projection.ts +44 -1
- package/src/utils/terminal-ui.test.ts +13 -22
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic clustering utilities for task similarity
|
|
3
|
+
*
|
|
4
|
+
* Uses lightweight text similarity without external APIs.
|
|
5
|
+
* Designed for session resumption to suggest related tasks.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Stop words to ignore in similarity calculation
|
|
9
|
+
* Common words that don't contribute to semantic meaning
|
|
10
|
+
*/
|
|
11
|
+
const STOP_WORDS = new Set([
|
|
12
|
+
// English
|
|
13
|
+
"a",
|
|
14
|
+
"am",
|
|
15
|
+
"an",
|
|
16
|
+
"the",
|
|
17
|
+
"is",
|
|
18
|
+
"are",
|
|
19
|
+
"was",
|
|
20
|
+
"were",
|
|
21
|
+
"be",
|
|
22
|
+
"been",
|
|
23
|
+
"being",
|
|
24
|
+
"have",
|
|
25
|
+
"has",
|
|
26
|
+
"had",
|
|
27
|
+
"do",
|
|
28
|
+
"does",
|
|
29
|
+
"did",
|
|
30
|
+
"will",
|
|
31
|
+
"would",
|
|
32
|
+
"could",
|
|
33
|
+
"should",
|
|
34
|
+
"may",
|
|
35
|
+
"might",
|
|
36
|
+
"must",
|
|
37
|
+
"can",
|
|
38
|
+
"to",
|
|
39
|
+
"of",
|
|
40
|
+
"in",
|
|
41
|
+
"for",
|
|
42
|
+
"on",
|
|
43
|
+
"with",
|
|
44
|
+
"at",
|
|
45
|
+
"by",
|
|
46
|
+
"from",
|
|
47
|
+
"as",
|
|
48
|
+
"into",
|
|
49
|
+
"through",
|
|
50
|
+
"during",
|
|
51
|
+
"before",
|
|
52
|
+
"after",
|
|
53
|
+
"above",
|
|
54
|
+
"below",
|
|
55
|
+
"and",
|
|
56
|
+
"or",
|
|
57
|
+
"but",
|
|
58
|
+
"if",
|
|
59
|
+
"then",
|
|
60
|
+
"else",
|
|
61
|
+
"when",
|
|
62
|
+
"where",
|
|
63
|
+
"why",
|
|
64
|
+
"how",
|
|
65
|
+
"all",
|
|
66
|
+
"each",
|
|
67
|
+
"every",
|
|
68
|
+
"both",
|
|
69
|
+
"few",
|
|
70
|
+
"more",
|
|
71
|
+
"most",
|
|
72
|
+
"other",
|
|
73
|
+
"some",
|
|
74
|
+
"such",
|
|
75
|
+
"no",
|
|
76
|
+
"nor",
|
|
77
|
+
"not",
|
|
78
|
+
"only",
|
|
79
|
+
"own",
|
|
80
|
+
"same",
|
|
81
|
+
"so",
|
|
82
|
+
"than",
|
|
83
|
+
"too",
|
|
84
|
+
"very",
|
|
85
|
+
"just",
|
|
86
|
+
"also",
|
|
87
|
+
"this",
|
|
88
|
+
"that",
|
|
89
|
+
"these",
|
|
90
|
+
"those",
|
|
91
|
+
"it",
|
|
92
|
+
"its",
|
|
93
|
+
// Korean particles (common suffixes)
|
|
94
|
+
"을",
|
|
95
|
+
"를",
|
|
96
|
+
"이",
|
|
97
|
+
"가",
|
|
98
|
+
"은",
|
|
99
|
+
"는",
|
|
100
|
+
"에",
|
|
101
|
+
"에서",
|
|
102
|
+
"으로",
|
|
103
|
+
"로",
|
|
104
|
+
"와",
|
|
105
|
+
"과",
|
|
106
|
+
"의",
|
|
107
|
+
"도",
|
|
108
|
+
"만",
|
|
109
|
+
"까지",
|
|
110
|
+
"부터",
|
|
111
|
+
"하다",
|
|
112
|
+
"하기",
|
|
113
|
+
"해서",
|
|
114
|
+
"하고",
|
|
115
|
+
]);
|
|
116
|
+
/**
|
|
117
|
+
* Tokenize text into meaningful words
|
|
118
|
+
* - Converts to lowercase
|
|
119
|
+
* - Splits on whitespace and punctuation
|
|
120
|
+
* - Removes stop words
|
|
121
|
+
* - Removes short tokens (< 2 chars)
|
|
122
|
+
*/
|
|
123
|
+
export function tokenize(text) {
|
|
124
|
+
if (!text)
|
|
125
|
+
return [];
|
|
126
|
+
return text
|
|
127
|
+
.toLowerCase()
|
|
128
|
+
.split(/[\s\-_.,;:!?()\[\]{}'"\/\\]+/)
|
|
129
|
+
.filter((token) => token.length >= 2 && !STOP_WORDS.has(token));
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Calculate Jaccard similarity between two token sets
|
|
133
|
+
* Returns value between 0 (no overlap) and 1 (identical)
|
|
134
|
+
*/
|
|
135
|
+
export function jaccardSimilarity(tokensA, tokensB) {
|
|
136
|
+
if (tokensA.length === 0 && tokensB.length === 0)
|
|
137
|
+
return 0;
|
|
138
|
+
if (tokensA.length === 0 || tokensB.length === 0)
|
|
139
|
+
return 0;
|
|
140
|
+
const setA = new Set(tokensA);
|
|
141
|
+
const setB = new Set(tokensB);
|
|
142
|
+
let intersection = 0;
|
|
143
|
+
for (const token of setA) {
|
|
144
|
+
if (setB.has(token)) {
|
|
145
|
+
intersection++;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const union = setA.size + setB.size - intersection;
|
|
149
|
+
return union === 0 ? 0 : intersection / union;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Calculate weighted similarity between two tasks
|
|
153
|
+
*
|
|
154
|
+
* Weights:
|
|
155
|
+
* - Intent: 0.4 (highest priority - captures purpose)
|
|
156
|
+
* - Title: 0.3 (strong signal)
|
|
157
|
+
* - Description: 0.2 (additional context)
|
|
158
|
+
* - Tags: 0.1 (explicit categorization)
|
|
159
|
+
*/
|
|
160
|
+
export function calculateTaskSimilarity(task1, task2) {
|
|
161
|
+
// Extract text fields
|
|
162
|
+
const intent1 = task1.workContext?.intent || "";
|
|
163
|
+
const intent2 = task2.workContext?.intent || "";
|
|
164
|
+
const title1 = task1.title || "";
|
|
165
|
+
const title2 = task2.title || "";
|
|
166
|
+
const desc1 = task1.description || "";
|
|
167
|
+
const desc2 = task2.description || "";
|
|
168
|
+
const tags1 = task1.tags?.join(" ") || "";
|
|
169
|
+
const tags2 = task2.tags?.join(" ") || "";
|
|
170
|
+
// Tokenize
|
|
171
|
+
const intentTokens1 = tokenize(intent1);
|
|
172
|
+
const intentTokens2 = tokenize(intent2);
|
|
173
|
+
const titleTokens1 = tokenize(title1);
|
|
174
|
+
const titleTokens2 = tokenize(title2);
|
|
175
|
+
const descTokens1 = tokenize(desc1);
|
|
176
|
+
const descTokens2 = tokenize(desc2);
|
|
177
|
+
const tagTokens1 = tokenize(tags1);
|
|
178
|
+
const tagTokens2 = tokenize(tags2);
|
|
179
|
+
// Calculate component similarities
|
|
180
|
+
const intentSim = jaccardSimilarity(intentTokens1, intentTokens2);
|
|
181
|
+
const titleSim = jaccardSimilarity(titleTokens1, titleTokens2);
|
|
182
|
+
const descSim = jaccardSimilarity(descTokens1, descTokens2);
|
|
183
|
+
const tagSim = jaccardSimilarity(tagTokens1, tagTokens2);
|
|
184
|
+
// Weighted combination
|
|
185
|
+
// Adjust weights based on available content
|
|
186
|
+
let totalWeight = 0;
|
|
187
|
+
let weightedSum = 0;
|
|
188
|
+
if (intentTokens1.length > 0 || intentTokens2.length > 0) {
|
|
189
|
+
weightedSum += intentSim * 0.4;
|
|
190
|
+
totalWeight += 0.4;
|
|
191
|
+
}
|
|
192
|
+
if (titleTokens1.length > 0 || titleTokens2.length > 0) {
|
|
193
|
+
weightedSum += titleSim * 0.3;
|
|
194
|
+
totalWeight += 0.3;
|
|
195
|
+
}
|
|
196
|
+
if (descTokens1.length > 0 || descTokens2.length > 0) {
|
|
197
|
+
weightedSum += descSim * 0.2;
|
|
198
|
+
totalWeight += 0.2;
|
|
199
|
+
}
|
|
200
|
+
if (tagTokens1.length > 0 || tagTokens2.length > 0) {
|
|
201
|
+
weightedSum += tagSim * 0.1;
|
|
202
|
+
totalWeight += 0.1;
|
|
203
|
+
}
|
|
204
|
+
return totalWeight === 0 ? 0 : weightedSum / totalWeight;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Find tasks related to a given task
|
|
208
|
+
*
|
|
209
|
+
* @param task - The reference task
|
|
210
|
+
* @param allTasks - All tasks to search from
|
|
211
|
+
* @param options - Configuration options
|
|
212
|
+
* @returns Array of related tasks sorted by similarity (highest first)
|
|
213
|
+
*/
|
|
214
|
+
export function findRelatedTasks(task, allTasks, options = {}) {
|
|
215
|
+
const { limit = 5, minSimilarity = 0.1, excludeCompleted = true, excludeSameParent = true, } = options;
|
|
216
|
+
const candidates = allTasks.filter((t) => {
|
|
217
|
+
// Exclude self
|
|
218
|
+
if (t.id === task.id)
|
|
219
|
+
return false;
|
|
220
|
+
// Exclude completed if requested
|
|
221
|
+
if (excludeCompleted && t.status === "completed")
|
|
222
|
+
return false;
|
|
223
|
+
// Exclude same parent (siblings are already contextually related)
|
|
224
|
+
if (excludeSameParent && task.parentId && t.parentId === task.parentId)
|
|
225
|
+
return false;
|
|
226
|
+
// Exclude direct parent/child relationships
|
|
227
|
+
if (t.parentId === task.id || task.parentId === t.id)
|
|
228
|
+
return false;
|
|
229
|
+
return true;
|
|
230
|
+
});
|
|
231
|
+
// Calculate similarities
|
|
232
|
+
const withSimilarity = candidates
|
|
233
|
+
.map((t) => ({
|
|
234
|
+
task: t,
|
|
235
|
+
similarity: calculateTaskSimilarity(task, t),
|
|
236
|
+
}))
|
|
237
|
+
.filter((item) => item.similarity >= minSimilarity)
|
|
238
|
+
.sort((a, b) => b.similarity - a.similarity)
|
|
239
|
+
.slice(0, limit);
|
|
240
|
+
return withSimilarity;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Cluster tasks by similarity
|
|
244
|
+
* Returns groups of related tasks
|
|
245
|
+
*/
|
|
246
|
+
export function clusterTasks(tasks, options = {}) {
|
|
247
|
+
const { minClusterSize = 2, similarityThreshold = 0.2 } = options;
|
|
248
|
+
// Simple greedy clustering
|
|
249
|
+
const assigned = new Set();
|
|
250
|
+
const clusters = [];
|
|
251
|
+
// Sort by priority to use higher priority tasks as cluster representatives
|
|
252
|
+
const sortedTasks = [...tasks].sort((a, b) => {
|
|
253
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
254
|
+
return (priorityOrder[a.priority] ?? 2) - (priorityOrder[b.priority] ?? 2);
|
|
255
|
+
});
|
|
256
|
+
for (const task of sortedTasks) {
|
|
257
|
+
if (assigned.has(task.id))
|
|
258
|
+
continue;
|
|
259
|
+
// Find related tasks that haven't been assigned
|
|
260
|
+
const related = findRelatedTasks(task, tasks, {
|
|
261
|
+
minSimilarity: similarityThreshold,
|
|
262
|
+
excludeCompleted: false,
|
|
263
|
+
excludeSameParent: false,
|
|
264
|
+
limit: 10,
|
|
265
|
+
}).filter((r) => !assigned.has(r.task.id));
|
|
266
|
+
if (related.length >= minClusterSize - 1) {
|
|
267
|
+
// Create cluster
|
|
268
|
+
const members = [task, ...related.map((r) => r.task)];
|
|
269
|
+
const avgSimilarity = related.length > 0 ? related.reduce((sum, r) => sum + r.similarity, 0) / related.length : 0;
|
|
270
|
+
clusters.push({
|
|
271
|
+
representative: task,
|
|
272
|
+
members,
|
|
273
|
+
avgSimilarity,
|
|
274
|
+
});
|
|
275
|
+
// Mark as assigned
|
|
276
|
+
for (const member of members) {
|
|
277
|
+
assigned.add(member.id);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return clusters;
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=clustering.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clustering.js","sourceRoot":"","sources":["../../src/utils/clustering.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clustering.test.d.ts","sourceRoot":"","sources":["../../src/utils/clustering.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { tokenize, jaccardSimilarity, calculateTaskSimilarity, findRelatedTasks, clusterTasks, } from "./clustering.js";
|
|
3
|
+
// Helper to create minimal task for testing
|
|
4
|
+
function createTask(overrides) {
|
|
5
|
+
return {
|
|
6
|
+
status: "pending",
|
|
7
|
+
priority: "medium",
|
|
8
|
+
workspace: "test-workspace",
|
|
9
|
+
tags: [],
|
|
10
|
+
contexts: [],
|
|
11
|
+
createdAt: new Date().toISOString(),
|
|
12
|
+
updatedAt: new Date().toISOString(),
|
|
13
|
+
...overrides,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
describe("tokenize", () => {
|
|
17
|
+
test("splits on whitespace and punctuation", () => {
|
|
18
|
+
expect(tokenize("Fix the bug")).toEqual(["fix", "bug"]);
|
|
19
|
+
expect(tokenize("user-authentication")).toEqual(["user", "authentication"]);
|
|
20
|
+
expect(tokenize("task.create")).toEqual(["task", "create"]);
|
|
21
|
+
});
|
|
22
|
+
test("removes stop words", () => {
|
|
23
|
+
expect(tokenize("the user is authenticated")).toEqual(["user", "authenticated"]);
|
|
24
|
+
expect(tokenize("to be or not to be")).toEqual([]);
|
|
25
|
+
});
|
|
26
|
+
test("removes short tokens (< 2 chars)", () => {
|
|
27
|
+
// "I" and "a" are 1 char, removed. "am" is a stop word
|
|
28
|
+
expect(tokenize("I am a user")).toEqual(["user"]);
|
|
29
|
+
});
|
|
30
|
+
test("handles Korean text", () => {
|
|
31
|
+
expect(tokenize("버그 수정하기")).toEqual(["버그", "수정하기"]);
|
|
32
|
+
// Korean particles are removed
|
|
33
|
+
expect(tokenize("사용자를")).toEqual(["사용자를"]); // 를 is suffix, not separate word
|
|
34
|
+
});
|
|
35
|
+
test("handles empty input", () => {
|
|
36
|
+
expect(tokenize("")).toEqual([]);
|
|
37
|
+
expect(tokenize(" ")).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe("jaccardSimilarity", () => {
|
|
41
|
+
test("returns 1 for identical sets", () => {
|
|
42
|
+
expect(jaccardSimilarity(["fix", "bug"], ["fix", "bug"])).toBe(1);
|
|
43
|
+
});
|
|
44
|
+
test("returns 0 for completely different sets", () => {
|
|
45
|
+
expect(jaccardSimilarity(["fix", "bug"], ["create", "feature"])).toBe(0);
|
|
46
|
+
});
|
|
47
|
+
test("returns 0.5 for half overlap", () => {
|
|
48
|
+
expect(jaccardSimilarity(["fix", "bug"], ["fix", "error"])).toBeCloseTo(0.333, 2);
|
|
49
|
+
});
|
|
50
|
+
test("handles empty arrays", () => {
|
|
51
|
+
expect(jaccardSimilarity([], [])).toBe(0);
|
|
52
|
+
expect(jaccardSimilarity(["fix"], [])).toBe(0);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe("calculateTaskSimilarity", () => {
|
|
56
|
+
test("returns high similarity for tasks with same intent", () => {
|
|
57
|
+
const task1 = createTask({
|
|
58
|
+
id: "1",
|
|
59
|
+
title: "Fix login bug",
|
|
60
|
+
workContext: { intent: "Fix issue or bug" },
|
|
61
|
+
});
|
|
62
|
+
const task2 = createTask({
|
|
63
|
+
id: "2",
|
|
64
|
+
title: "Debug authentication",
|
|
65
|
+
workContext: { intent: "Fix issue or bug" },
|
|
66
|
+
});
|
|
67
|
+
const similarity = calculateTaskSimilarity(task1, task2);
|
|
68
|
+
expect(similarity).toBeGreaterThan(0.3);
|
|
69
|
+
});
|
|
70
|
+
test("returns high similarity for tasks with similar titles", () => {
|
|
71
|
+
const task1 = createTask({
|
|
72
|
+
id: "1",
|
|
73
|
+
title: "Implement user authentication",
|
|
74
|
+
});
|
|
75
|
+
const task2 = createTask({
|
|
76
|
+
id: "2",
|
|
77
|
+
title: "Implement admin authentication",
|
|
78
|
+
});
|
|
79
|
+
const similarity = calculateTaskSimilarity(task1, task2);
|
|
80
|
+
expect(similarity).toBeGreaterThan(0.3);
|
|
81
|
+
});
|
|
82
|
+
test("returns low similarity for unrelated tasks", () => {
|
|
83
|
+
const task1 = createTask({
|
|
84
|
+
id: "1",
|
|
85
|
+
title: "Fix login bug",
|
|
86
|
+
workContext: { intent: "Fix issue or bug" },
|
|
87
|
+
});
|
|
88
|
+
const task2 = createTask({
|
|
89
|
+
id: "2",
|
|
90
|
+
title: "Write documentation",
|
|
91
|
+
workContext: { intent: "Document functionality" },
|
|
92
|
+
});
|
|
93
|
+
const similarity = calculateTaskSimilarity(task1, task2);
|
|
94
|
+
expect(similarity).toBeLessThan(0.2);
|
|
95
|
+
});
|
|
96
|
+
test("handles tasks with missing fields", () => {
|
|
97
|
+
const task1 = createTask({ id: "1", title: "Task one" });
|
|
98
|
+
const task2 = createTask({ id: "2", title: "Task two" });
|
|
99
|
+
// Should not throw
|
|
100
|
+
const similarity = calculateTaskSimilarity(task1, task2);
|
|
101
|
+
expect(similarity).toBeGreaterThanOrEqual(0);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe("findRelatedTasks", () => {
|
|
105
|
+
const tasks = [
|
|
106
|
+
createTask({
|
|
107
|
+
id: "auth-1",
|
|
108
|
+
title: "Implement user authentication",
|
|
109
|
+
workContext: { intent: "Implement new functionality" },
|
|
110
|
+
}),
|
|
111
|
+
createTask({
|
|
112
|
+
id: "auth-2",
|
|
113
|
+
title: "Implement OAuth authentication",
|
|
114
|
+
workContext: { intent: "Implement new functionality" },
|
|
115
|
+
}),
|
|
116
|
+
createTask({
|
|
117
|
+
id: "bug-1",
|
|
118
|
+
title: "Fix login bug",
|
|
119
|
+
workContext: { intent: "Fix issue or bug" },
|
|
120
|
+
}),
|
|
121
|
+
createTask({
|
|
122
|
+
id: "doc-1",
|
|
123
|
+
title: "Write API documentation",
|
|
124
|
+
workContext: { intent: "Document functionality" },
|
|
125
|
+
}),
|
|
126
|
+
createTask({
|
|
127
|
+
id: "completed-1",
|
|
128
|
+
title: "Implement session authentication",
|
|
129
|
+
status: "completed",
|
|
130
|
+
workContext: { intent: "Implement new functionality" },
|
|
131
|
+
}),
|
|
132
|
+
];
|
|
133
|
+
test("finds related tasks by similarity", () => {
|
|
134
|
+
const related = findRelatedTasks(tasks[0], tasks);
|
|
135
|
+
// auth-2 should be most similar to auth-1
|
|
136
|
+
expect(related.length).toBeGreaterThan(0);
|
|
137
|
+
expect(related[0].task.id).toBe("auth-2");
|
|
138
|
+
});
|
|
139
|
+
test("excludes completed tasks by default", () => {
|
|
140
|
+
const related = findRelatedTasks(tasks[0], tasks);
|
|
141
|
+
const hasCompleted = related.some((r) => r.task.status === "completed");
|
|
142
|
+
expect(hasCompleted).toBe(false);
|
|
143
|
+
});
|
|
144
|
+
test("includes completed tasks when requested", () => {
|
|
145
|
+
const related = findRelatedTasks(tasks[0], tasks, { excludeCompleted: false });
|
|
146
|
+
const hasCompleted = related.some((r) => r.task.status === "completed");
|
|
147
|
+
expect(hasCompleted).toBe(true);
|
|
148
|
+
});
|
|
149
|
+
test("respects limit option", () => {
|
|
150
|
+
const related = findRelatedTasks(tasks[0], tasks, { limit: 1 });
|
|
151
|
+
expect(related.length).toBe(1);
|
|
152
|
+
});
|
|
153
|
+
test("excludes self", () => {
|
|
154
|
+
const related = findRelatedTasks(tasks[0], tasks);
|
|
155
|
+
const hasSelf = related.some((r) => r.task.id === tasks[0].id);
|
|
156
|
+
expect(hasSelf).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
test("excludes siblings when excludeSameParent is true", () => {
|
|
159
|
+
const parentId = "parent-1";
|
|
160
|
+
const tasksWithParent = [
|
|
161
|
+
createTask({ id: "child-1", title: "Task one", parentId }),
|
|
162
|
+
createTask({ id: "child-2", title: "Task one similar", parentId }),
|
|
163
|
+
createTask({ id: "other", title: "Task one other" }),
|
|
164
|
+
];
|
|
165
|
+
const related = findRelatedTasks(tasksWithParent[0], tasksWithParent, {
|
|
166
|
+
excludeSameParent: true,
|
|
167
|
+
});
|
|
168
|
+
const hasSibling = related.some((r) => r.task.id === "child-2");
|
|
169
|
+
expect(hasSibling).toBe(false);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
describe("clusterTasks", () => {
|
|
173
|
+
test("groups similar tasks together", () => {
|
|
174
|
+
const tasks = [
|
|
175
|
+
createTask({
|
|
176
|
+
id: "auth-1",
|
|
177
|
+
title: "Implement user authentication",
|
|
178
|
+
priority: "high",
|
|
179
|
+
workContext: { intent: "Implement new functionality" },
|
|
180
|
+
}),
|
|
181
|
+
createTask({
|
|
182
|
+
id: "auth-2",
|
|
183
|
+
title: "Implement OAuth authentication",
|
|
184
|
+
priority: "medium",
|
|
185
|
+
workContext: { intent: "Implement new functionality" },
|
|
186
|
+
}),
|
|
187
|
+
createTask({
|
|
188
|
+
id: "bug-1",
|
|
189
|
+
title: "Fix critical login bug",
|
|
190
|
+
priority: "critical",
|
|
191
|
+
workContext: { intent: "Fix issue or bug" },
|
|
192
|
+
}),
|
|
193
|
+
createTask({
|
|
194
|
+
id: "bug-2",
|
|
195
|
+
title: "Fix authentication bug",
|
|
196
|
+
priority: "high",
|
|
197
|
+
workContext: { intent: "Fix issue or bug" },
|
|
198
|
+
}),
|
|
199
|
+
];
|
|
200
|
+
const clusters = clusterTasks(tasks, { minClusterSize: 2 });
|
|
201
|
+
// Should have at least one cluster
|
|
202
|
+
expect(clusters.length).toBeGreaterThan(0);
|
|
203
|
+
// Each cluster should have at least minClusterSize members
|
|
204
|
+
for (const cluster of clusters) {
|
|
205
|
+
expect(cluster.members.length).toBeGreaterThanOrEqual(2);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
test("uses higher priority tasks as representatives", () => {
|
|
209
|
+
const tasks = [
|
|
210
|
+
createTask({
|
|
211
|
+
id: "low",
|
|
212
|
+
title: "Implement feature",
|
|
213
|
+
priority: "low",
|
|
214
|
+
workContext: { intent: "Implement new functionality" },
|
|
215
|
+
}),
|
|
216
|
+
createTask({
|
|
217
|
+
id: "high",
|
|
218
|
+
title: "Implement similar feature",
|
|
219
|
+
priority: "high",
|
|
220
|
+
workContext: { intent: "Implement new functionality" },
|
|
221
|
+
}),
|
|
222
|
+
];
|
|
223
|
+
const clusters = clusterTasks(tasks, { minClusterSize: 2, similarityThreshold: 0.1 });
|
|
224
|
+
if (clusters.length > 0) {
|
|
225
|
+
expect(clusters[0].representative.id).toBe("high");
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
test("returns empty array for no matching clusters", () => {
|
|
229
|
+
const tasks = [
|
|
230
|
+
createTask({ id: "1", title: "Unique task one" }),
|
|
231
|
+
createTask({ id: "2", title: "Different topic completely" }),
|
|
232
|
+
];
|
|
233
|
+
const clusters = clusterTasks(tasks, { minClusterSize: 2, similarityThreshold: 0.5 });
|
|
234
|
+
expect(clusters.length).toBe(0);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
//# sourceMappingURL=clustering.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clustering.test.js","sourceRoot":"","sources":["../../src/utils/clustering.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment detection utilities
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Check if running in test environment.
|
|
6
|
+
* Detects: NODE_ENV=test, BUN_TEST (set by bun test), VITEST, JEST_WORKER_ID
|
|
7
|
+
*/
|
|
8
|
+
export declare function isTestEnv(): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Check if debug mode is enabled via environment variable
|
|
11
|
+
*/
|
|
12
|
+
export declare function isDebugEnabled(): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Check if verbose logging should be suppressed.
|
|
15
|
+
*
|
|
16
|
+
* Logs are suppressed if:
|
|
17
|
+
* - In test environment AND
|
|
18
|
+
* - TASK_MCP_DEBUG is not set AND
|
|
19
|
+
* - TASK_MCP_TEST_VERBOSE is not set
|
|
20
|
+
*
|
|
21
|
+
* Set TASK_MCP_TEST_VERBOSE=true to enable logs in specific tests.
|
|
22
|
+
*/
|
|
23
|
+
export declare function shouldSuppressLogs(): boolean;
|
|
24
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/utils/env.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAOnC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAGxC;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAK5C"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment detection utilities
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Check if running in test environment.
|
|
6
|
+
* Detects: NODE_ENV=test, BUN_TEST (set by bun test), VITEST, JEST_WORKER_ID
|
|
7
|
+
*/
|
|
8
|
+
export function isTestEnv() {
|
|
9
|
+
return (process.env.NODE_ENV === "test" ||
|
|
10
|
+
process.env["BUN_TEST"] !== undefined ||
|
|
11
|
+
process.env["VITEST"] !== undefined ||
|
|
12
|
+
process.env["JEST_WORKER_ID"] !== undefined);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Check if debug mode is enabled via environment variable
|
|
16
|
+
*/
|
|
17
|
+
export function isDebugEnabled() {
|
|
18
|
+
const debugEnv = process.env["TASK_MCP_DEBUG"];
|
|
19
|
+
return debugEnv === "true" || debugEnv === "1";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Check if verbose logging should be suppressed.
|
|
23
|
+
*
|
|
24
|
+
* Logs are suppressed if:
|
|
25
|
+
* - In test environment AND
|
|
26
|
+
* - TASK_MCP_DEBUG is not set AND
|
|
27
|
+
* - TASK_MCP_TEST_VERBOSE is not set
|
|
28
|
+
*
|
|
29
|
+
* Set TASK_MCP_TEST_VERBOSE=true to enable logs in specific tests.
|
|
30
|
+
*/
|
|
31
|
+
export function shouldSuppressLogs() {
|
|
32
|
+
if (!isTestEnv())
|
|
33
|
+
return false;
|
|
34
|
+
if (isDebugEnabled())
|
|
35
|
+
return false;
|
|
36
|
+
if (process.env["TASK_MCP_TEST_VERBOSE"] === "true")
|
|
37
|
+
return false;
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/utils/env.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,MAAM,UAAU,SAAS;IACvB,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM;QAC/B,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,SAAS;QACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,SAAS;QACnC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,KAAK,SAAS,CAC5C,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC/C,OAAO,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,GAAG,CAAC;AACjD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,CAAC,SAAS,EAAE;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,cAAc,EAAE;QAAE,OAAO,KAAK,CAAC;IACnC,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IAClE,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hierarchy.d.ts","sourceRoot":"","sources":["../../src/utils/hierarchy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"hierarchy.d.ts","sourceRoot":"","sources":["../../src/utils/hierarchy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAG/C;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CA+BlE;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAU/E;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BtE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAyBxE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,CAEnE;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAiBtE;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,IAAI,EAAE,EACb,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,GAAE,MAAgC,GACzC,YAAY,EAAE,CAmDhB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3D,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,gBAAgB,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACvD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,yBAAyB,CAsC1E"}
|
package/dist/utils/hierarchy.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { shouldSuppressLogs } from "./env.js";
|
|
1
2
|
/**
|
|
2
3
|
* Maximum allowed hierarchy depth (0-indexed)
|
|
3
4
|
* 0 = root task
|
|
@@ -28,7 +29,9 @@ export function getTaskLevel(tasks, taskId) {
|
|
|
28
29
|
while (currentTask.parentId) {
|
|
29
30
|
const parent = taskMap.get(currentTask.parentId);
|
|
30
31
|
if (!parent) {
|
|
31
|
-
|
|
32
|
+
if (!shouldSuppressLogs()) {
|
|
33
|
+
console.warn(`[task-mcp] Detached parent reference: task "${currentTask.id}" references non-existent parent "${currentTask.parentId}"`);
|
|
34
|
+
}
|
|
32
35
|
break;
|
|
33
36
|
}
|
|
34
37
|
level++;
|
|
@@ -186,8 +189,9 @@ export function buildTaskTree(tasks, rootId, maxDepth = MAX_HIERARCHY_DEPTH + 1)
|
|
|
186
189
|
}
|
|
187
190
|
return [buildNode(rootTask, 0, new Set())];
|
|
188
191
|
}
|
|
189
|
-
// Return all root tasks (no parent)
|
|
190
|
-
|
|
192
|
+
// Return all root tasks (no parent OR parent not in filtered tasks)
|
|
193
|
+
// This ensures orphaned subtasks (whose parent was filtered out) are still visible
|
|
194
|
+
const rootTasks = tasks.filter((t) => !t.parentId || !taskMap.has(t.parentId));
|
|
191
195
|
return rootTasks.map((task) => buildNode(task, 0, new Set()));
|
|
192
196
|
}
|
|
193
197
|
/**
|