@task-mcp/shared 1.0.28 → 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.
Files changed (94) hide show
  1. package/dist/algorithms/index.d.ts +1 -1
  2. package/dist/algorithms/index.d.ts.map +1 -1
  3. package/dist/algorithms/index.js +1 -1
  4. package/dist/algorithms/index.js.map +1 -1
  5. package/dist/algorithms/topological-sort.d.ts +21 -1
  6. package/dist/algorithms/topological-sort.d.ts.map +1 -1
  7. package/dist/algorithms/topological-sort.js +12 -1
  8. package/dist/algorithms/topological-sort.js.map +1 -1
  9. package/dist/schemas/inbox.d.ts +2 -2
  10. package/dist/schemas/index.d.ts +1 -0
  11. package/dist/schemas/index.d.ts.map +1 -1
  12. package/dist/schemas/index.js +2 -0
  13. package/dist/schemas/index.js.map +1 -1
  14. package/dist/schemas/response-format.d.ts +11 -0
  15. package/dist/schemas/response-format.d.ts.map +1 -1
  16. package/dist/schemas/response-format.js.map +1 -1
  17. package/dist/schemas/session.d.ts +521 -0
  18. package/dist/schemas/session.d.ts.map +1 -0
  19. package/dist/schemas/session.js +79 -0
  20. package/dist/schemas/session.js.map +1 -0
  21. package/dist/schemas/state.d.ts +2 -2
  22. package/dist/schemas/task.d.ts +9 -0
  23. package/dist/schemas/task.d.ts.map +1 -1
  24. package/dist/schemas/task.js +23 -6
  25. package/dist/schemas/task.js.map +1 -1
  26. package/dist/schemas/view.d.ts +18 -18
  27. package/dist/utils/clustering.d.ts +60 -0
  28. package/dist/utils/clustering.d.ts.map +1 -0
  29. package/dist/utils/clustering.js +283 -0
  30. package/dist/utils/clustering.js.map +1 -0
  31. package/dist/utils/clustering.test.d.ts +2 -0
  32. package/dist/utils/clustering.test.d.ts.map +1 -0
  33. package/dist/utils/clustering.test.js +237 -0
  34. package/dist/utils/clustering.test.js.map +1 -0
  35. package/dist/utils/env.d.ts +24 -0
  36. package/dist/utils/env.d.ts.map +1 -0
  37. package/dist/utils/env.js +40 -0
  38. package/dist/utils/env.js.map +1 -0
  39. package/dist/utils/hierarchy.d.ts.map +1 -1
  40. package/dist/utils/hierarchy.js +13 -6
  41. package/dist/utils/hierarchy.js.map +1 -1
  42. package/dist/utils/index.d.ts +6 -2
  43. package/dist/utils/index.d.ts.map +1 -1
  44. package/dist/utils/index.js +24 -2
  45. package/dist/utils/index.js.map +1 -1
  46. package/dist/utils/intent-extractor.d.ts +30 -0
  47. package/dist/utils/intent-extractor.d.ts.map +1 -0
  48. package/dist/utils/intent-extractor.js +135 -0
  49. package/dist/utils/intent-extractor.js.map +1 -0
  50. package/dist/utils/intent-extractor.test.d.ts +2 -0
  51. package/dist/utils/intent-extractor.test.d.ts.map +1 -0
  52. package/dist/utils/intent-extractor.test.js +69 -0
  53. package/dist/utils/intent-extractor.test.js.map +1 -0
  54. package/dist/utils/natural-language.d.ts.map +1 -1
  55. package/dist/utils/natural-language.js +9 -8
  56. package/dist/utils/natural-language.js.map +1 -1
  57. package/dist/utils/natural-language.test.js +22 -0
  58. package/dist/utils/natural-language.test.js.map +1 -1
  59. package/dist/utils/plan-parser.d.ts +57 -0
  60. package/dist/utils/plan-parser.d.ts.map +1 -0
  61. package/dist/utils/plan-parser.js +371 -0
  62. package/dist/utils/plan-parser.js.map +1 -0
  63. package/dist/utils/projection.d.ts.map +1 -1
  64. package/dist/utils/projection.js +43 -1
  65. package/dist/utils/projection.js.map +1 -1
  66. package/dist/utils/projection.test.js +57 -7
  67. package/dist/utils/projection.test.js.map +1 -1
  68. package/dist/utils/terminal-ui.d.ts +129 -0
  69. package/dist/utils/terminal-ui.d.ts.map +1 -1
  70. package/dist/utils/terminal-ui.js +191 -0
  71. package/dist/utils/terminal-ui.js.map +1 -1
  72. package/dist/utils/terminal-ui.test.js +227 -0
  73. package/dist/utils/terminal-ui.test.js.map +1 -1
  74. package/package.json +2 -2
  75. package/src/algorithms/index.ts +3 -0
  76. package/src/algorithms/topological-sort.ts +31 -1
  77. package/src/schemas/index.ts +11 -0
  78. package/src/schemas/response-format.ts +15 -2
  79. package/src/schemas/session.ts +100 -0
  80. package/src/schemas/task.ts +33 -16
  81. package/src/utils/clustering.test.ts +285 -0
  82. package/src/utils/clustering.ts +336 -0
  83. package/src/utils/env.ts +41 -0
  84. package/src/utils/hierarchy.ts +17 -8
  85. package/src/utils/index.ts +48 -0
  86. package/src/utils/intent-extractor.test.ts +84 -0
  87. package/src/utils/intent-extractor.ts +156 -0
  88. package/src/utils/natural-language.test.ts +27 -0
  89. package/src/utils/natural-language.ts +10 -9
  90. package/src/utils/plan-parser.ts +466 -0
  91. package/src/utils/projection.test.ts +61 -7
  92. package/src/utils/projection.ts +44 -1
  93. package/src/utils/terminal-ui.test.ts +277 -0
  94. package/src/utils/terminal-ui.ts +315 -0
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Environment detection utilities
3
+ */
4
+
5
+ /**
6
+ * Check if running in test environment.
7
+ * Detects: NODE_ENV=test, BUN_TEST (set by bun test), VITEST, JEST_WORKER_ID
8
+ */
9
+ export function isTestEnv(): boolean {
10
+ return (
11
+ process.env.NODE_ENV === "test" ||
12
+ process.env["BUN_TEST"] !== undefined ||
13
+ process.env["VITEST"] !== undefined ||
14
+ process.env["JEST_WORKER_ID"] !== undefined
15
+ );
16
+ }
17
+
18
+ /**
19
+ * Check if debug mode is enabled via environment variable
20
+ */
21
+ export function isDebugEnabled(): boolean {
22
+ const debugEnv = process.env["TASK_MCP_DEBUG"];
23
+ return debugEnv === "true" || debugEnv === "1";
24
+ }
25
+
26
+ /**
27
+ * Check if verbose logging should be suppressed.
28
+ *
29
+ * Logs are suppressed if:
30
+ * - In test environment AND
31
+ * - TASK_MCP_DEBUG is not set AND
32
+ * - TASK_MCP_TEST_VERBOSE is not set
33
+ *
34
+ * Set TASK_MCP_TEST_VERBOSE=true to enable logs in specific tests.
35
+ */
36
+ export function shouldSuppressLogs(): boolean {
37
+ if (!isTestEnv()) return false;
38
+ if (isDebugEnabled()) return false;
39
+ if (process.env["TASK_MCP_TEST_VERBOSE"] === "true") return false;
40
+ return true;
41
+ }
@@ -1,4 +1,5 @@
1
1
  import type { Task } from "../schemas/task.js";
2
+ import { shouldSuppressLogs } from "./env.js";
2
3
 
3
4
  /**
4
5
  * Maximum allowed hierarchy depth (0-indexed)
@@ -34,9 +35,11 @@ export function getTaskLevel(tasks: Task[], taskId: string): number {
34
35
  while (currentTask.parentId) {
35
36
  const parent = taskMap.get(currentTask.parentId);
36
37
  if (!parent) {
37
- console.warn(
38
- `[task-mcp] Orphaned parent reference: task "${currentTask.id}" references non-existent parent "${currentTask.parentId}"`
39
- );
38
+ if (!shouldSuppressLogs()) {
39
+ console.warn(
40
+ `[task-mcp] Detached parent reference: task "${currentTask.id}" references non-existent parent "${currentTask.parentId}"`
41
+ );
42
+ }
40
43
  break;
41
44
  }
42
45
  level++;
@@ -217,13 +220,18 @@ export function buildTaskTree(
217
220
  return { task, children: [] };
218
221
  }
219
222
 
220
- const newVisited = new Set(visited);
221
- newVisited.add(task.id);
223
+ // Mark as visited (O(1) instead of copying the Set)
224
+ visited.add(task.id);
222
225
 
223
226
  const children = childrenMap.get(task.id) || [];
227
+ const childNodes = children.map((child) => buildNode(child, depth + 1, visited));
228
+
229
+ // Restore visited state after processing children (backtracking)
230
+ visited.delete(task.id);
231
+
224
232
  return {
225
233
  task,
226
- children: children.map((child) => buildNode(child, depth + 1, newVisited)),
234
+ children: childNodes,
227
235
  };
228
236
  }
229
237
 
@@ -235,8 +243,9 @@ export function buildTaskTree(
235
243
  return [buildNode(rootTask, 0, new Set())];
236
244
  }
237
245
 
238
- // Return all root tasks (no parent)
239
- const rootTasks = tasks.filter((t) => !t.parentId);
246
+ // Return all root tasks (no parent OR parent not in filtered tasks)
247
+ // This ensures orphaned subtasks (whose parent was filtered out) are still visible
248
+ const rootTasks = tasks.filter((t) => !t.parentId || !taskMap.has(t.parentId));
240
249
  return rootTasks.map((task) => buildNode(task, 0, new Set()));
241
250
  }
242
251
 
@@ -42,6 +42,8 @@ export {
42
42
  MAX_HIERARCHY_DEPTH,
43
43
  getTaskLevel,
44
44
  validateHierarchyDepth,
45
+ validateHierarchy,
46
+ type HierarchyValidationResult,
45
47
  getAncestorIds,
46
48
  getDescendantIds,
47
49
  getChildTasks,
@@ -127,6 +129,29 @@ export {
127
129
  formatDependencies,
128
130
  // Banner
129
131
  banner,
132
+ // Status symbols (checkbox style)
133
+ STATUS_SYMBOLS,
134
+ formatStatusSymbol,
135
+ // Priority badge (P0-P3 style)
136
+ PRIORITY_LEVELS,
137
+ formatPriorityBadge,
138
+ // Section renderer (GitHub CLI style)
139
+ renderSection,
140
+ renderSections,
141
+ type SectionItem,
142
+ type SectionOptions,
143
+ // Alert box
144
+ renderAlert,
145
+ type AlertSeverity,
146
+ type AlertOptions,
147
+ // Stats row
148
+ renderStats,
149
+ type StatItem,
150
+ // Due date formatter
151
+ formatDueDate,
152
+ // Task line formatter
153
+ formatTaskLine,
154
+ type TaskLineOptions,
130
155
  } from "./terminal-ui.js";
131
156
 
132
157
  // Workspace detection
@@ -140,3 +165,26 @@ export {
140
165
  detectWorkspace,
141
166
  detectWorkspaceSync,
142
167
  } from "./workspace.js";
168
+
169
+ // Plan parser
170
+ export {
171
+ parsePlan,
172
+ type ParsedPlanTask,
173
+ type ParsePlanOptions,
174
+ type ParsePlanResult,
175
+ } from "./plan-parser.js";
176
+
177
+ // Intent extractor (auto-generate intent from title/description)
178
+ export { extractIntent, generateFallbackIntent, getIntent } from "./intent-extractor.js";
179
+
180
+ // Semantic clustering (task similarity for session resumption)
181
+ export {
182
+ tokenize,
183
+ jaccardSimilarity,
184
+ calculateTaskSimilarity,
185
+ findRelatedTasks,
186
+ clusterTasks,
187
+ } from "./clustering.js";
188
+
189
+ // Environment detection
190
+ export { isTestEnv, isDebugEnabled, shouldSuppressLogs } from "./env.js";
@@ -0,0 +1,84 @@
1
+ import { describe, test, expect } from "bun:test";
2
+ import { extractIntent, generateFallbackIntent, getIntent } from "./intent-extractor.js";
3
+
4
+ describe("extractIntent", () => {
5
+ test("extracts intent from fix patterns", () => {
6
+ expect(extractIntent("Fix login bug")).toBe("Fix issue or bug");
7
+ expect(extractIntent("Debug authentication issue")).toBe("Fix issue or bug");
8
+ expect(extractIntent("로그인 버그 수정")).toBe("Fix issue or bug");
9
+ });
10
+
11
+ test("extracts intent from implement patterns", () => {
12
+ expect(extractIntent("Implement user authentication")).toBe("Implement new functionality");
13
+ expect(extractIntent("Create new dashboard")).toBe("Implement new functionality");
14
+ expect(extractIntent("사용자 인증 구현")).toBe("Implement new functionality");
15
+ });
16
+
17
+ test("extracts intent from refactor patterns", () => {
18
+ expect(extractIntent("Refactor database layer")).toBe("Improve code quality");
19
+ expect(extractIntent("API 리팩토링")).toBe("Improve code quality");
20
+ });
21
+
22
+ test("extracts intent from add patterns", () => {
23
+ expect(extractIntent("Add dark mode support")).toBe("Extend with new feature");
24
+ expect(extractIntent("다크 모드 추가")).toBe("Extend with new feature");
25
+ });
26
+
27
+ test("extracts intent from update patterns", () => {
28
+ expect(extractIntent("Update dependencies")).toBe("Modify existing behavior");
29
+ expect(extractIntent("의존성 업데이트")).toBe("Modify existing behavior");
30
+ });
31
+
32
+ test("extracts intent from remove patterns", () => {
33
+ expect(extractIntent("Remove deprecated code")).toBe("Remove or clean up");
34
+ expect(extractIntent("레거시 코드 삭제")).toBe("Remove or clean up");
35
+ });
36
+
37
+ test("extracts intent from test patterns", () => {
38
+ expect(extractIntent("Test payment flow")).toBe("Verify functionality");
39
+ expect(extractIntent("결제 플로우 테스트")).toBe("Verify functionality");
40
+ });
41
+
42
+ test("extracts intent from description when title has no match", () => {
43
+ expect(extractIntent("API endpoints", "Fix the bug in user endpoint")).toBe("Fix issue or bug");
44
+ });
45
+
46
+ test("returns undefined when no pattern matches", () => {
47
+ expect(extractIntent("Random task title")).toBeUndefined();
48
+ expect(extractIntent("Something else")).toBeUndefined();
49
+ });
50
+ });
51
+
52
+ describe("generateFallbackIntent", () => {
53
+ test("generates intent from verb-like first word", () => {
54
+ expect(generateFallbackIntent("Get user data")).toBe("Get operation");
55
+ expect(generateFallbackIntent("Run tests")).toBe("Run operation");
56
+ });
57
+
58
+ test("generates fallback for non-verb titles", () => {
59
+ expect(generateFallbackIntent("Dashboard redesign")).toBe("Complete: Dashboard redesign");
60
+ // Test truncation for titles > 50 chars
61
+ const longTitle =
62
+ "This is a very long task title that definitely exceeds fifty characters limit";
63
+ expect(generateFallbackIntent(longTitle)).toContain("...");
64
+ });
65
+ });
66
+
67
+ describe("getIntent", () => {
68
+ test("returns extracted intent when pattern matches", () => {
69
+ expect(getIntent("Fix bug")).toBe("Fix issue or bug");
70
+ expect(getIntent("Implement feature")).toBe("Implement new functionality");
71
+ });
72
+
73
+ test("returns fallback when autoGenerate is true", () => {
74
+ expect(getIntent("Random task")).toBe("Complete: Random task");
75
+ });
76
+
77
+ test("returns undefined when autoGenerate is false and no match", () => {
78
+ expect(getIntent("Random task", undefined, false)).toBeUndefined();
79
+ });
80
+
81
+ test("uses description for extraction", () => {
82
+ expect(getIntent("Task", "Fix the authentication bug")).toBe("Fix issue or bug");
83
+ });
84
+ });
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Intent Extractor
3
+ *
4
+ * Extracts intent from task title/description using pattern matching.
5
+ * Provides fallback intent generation when workContext.intent is not provided.
6
+ */
7
+
8
+ interface IntentPattern {
9
+ patterns: RegExp[];
10
+ intent: string;
11
+ }
12
+
13
+ const INTENT_PATTERNS: IntentPattern[] = [
14
+ // Fix/Bug patterns
15
+ {
16
+ patterns: [/^fix\b/i, /^debug\b/i, /^resolve\b/i, /버그/, /수정/, /고치/],
17
+ intent: "Fix issue or bug",
18
+ },
19
+ // Implementation patterns
20
+ {
21
+ patterns: [/^implement\b/i, /^create\b/i, /^build\b/i, /구현/, /만들/],
22
+ intent: "Implement new functionality",
23
+ },
24
+ // Refactoring patterns
25
+ {
26
+ patterns: [/^refactor\b/i, /^restructure\b/i, /^reorganize\b/i, /리팩/, /개선/],
27
+ intent: "Improve code quality",
28
+ },
29
+ // Add/Extend patterns
30
+ {
31
+ patterns: [/^add\b/i, /^extend\b/i, /^include\b/i, /추가/, /확장/],
32
+ intent: "Extend with new feature",
33
+ },
34
+ // Update/Modify patterns
35
+ {
36
+ patterns: [/^update\b/i, /^modify\b/i, /^change\b/i, /업데이트/, /변경/],
37
+ intent: "Modify existing behavior",
38
+ },
39
+ // Remove/Delete patterns
40
+ {
41
+ patterns: [/^remove\b/i, /^delete\b/i, /^clean\b/i, /삭제/, /제거/],
42
+ intent: "Remove or clean up",
43
+ },
44
+ // Test patterns
45
+ {
46
+ patterns: [/^test\b/i, /^verify\b/i, /^validate\b/i, /테스트/, /검증/],
47
+ intent: "Verify functionality",
48
+ },
49
+ // Documentation patterns
50
+ {
51
+ patterns: [/^document\b/i, /^write doc/i, /문서/, /docs/i],
52
+ intent: "Document or explain",
53
+ },
54
+ // Research/Analysis patterns
55
+ {
56
+ patterns: [/^research\b/i, /^analyze\b/i, /^investigate\b/i, /조사/, /분석/],
57
+ intent: "Research or analyze",
58
+ },
59
+ // Migration patterns
60
+ {
61
+ patterns: [/^migrate\b/i, /^upgrade\b/i, /^convert\b/i, /마이그레이션/, /전환/],
62
+ intent: "Migrate or upgrade",
63
+ },
64
+ // Setup/Config patterns
65
+ {
66
+ patterns: [/^setup\b/i, /^configure\b/i, /^initialize\b/i, /설정/, /초기화/],
67
+ intent: "Setup or configure",
68
+ },
69
+ // Review patterns
70
+ {
71
+ patterns: [/^review\b/i, /^check\b/i, /^audit\b/i, /리뷰/, /검토/],
72
+ intent: "Review or audit",
73
+ },
74
+ ];
75
+
76
+ /**
77
+ * Extract intent from task title and description
78
+ *
79
+ * @param title - Task title
80
+ * @param description - Optional task description
81
+ * @returns Extracted intent or undefined if no pattern matches
82
+ */
83
+ export function extractIntent(title: string, description?: string): string | undefined {
84
+ const text = title.toLowerCase();
85
+
86
+ // Try to match patterns against title
87
+ for (const { patterns, intent } of INTENT_PATTERNS) {
88
+ for (const pattern of patterns) {
89
+ if (pattern.test(text)) {
90
+ return intent;
91
+ }
92
+ }
93
+ }
94
+
95
+ // If no match found in title, try description
96
+ if (description) {
97
+ const descText = description.toLowerCase();
98
+ for (const { patterns, intent } of INTENT_PATTERNS) {
99
+ for (const pattern of patterns) {
100
+ if (pattern.test(descText)) {
101
+ return intent;
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ return undefined;
108
+ }
109
+
110
+ /**
111
+ * Generate a fallback intent based on title structure
112
+ *
113
+ * Used when no pattern matches but we still want some intent
114
+ */
115
+ export function generateFallbackIntent(title: string): string {
116
+ // Extract first verb-like word
117
+ const words = title.split(/\s+/);
118
+ const firstWord = words[0]?.toLowerCase() || "";
119
+
120
+ // If it looks like a verb (common task action words)
121
+ const verbPatterns =
122
+ /^(get|set|make|do|run|use|find|show|hide|load|save|send|fetch|call|start|stop|enable|disable)/i;
123
+ if (verbPatterns.test(firstWord)) {
124
+ return `${firstWord.charAt(0).toUpperCase() + firstWord.slice(1)} operation`;
125
+ }
126
+
127
+ // Default: use truncated title as intent
128
+ const truncatedTitle = title.length > 50 ? title.slice(0, 47) + "..." : title;
129
+ return `Complete: ${truncatedTitle}`;
130
+ }
131
+
132
+ /**
133
+ * Get intent for a task, extracting from content or generating fallback
134
+ *
135
+ * @param title - Task title
136
+ * @param description - Optional task description
137
+ * @param autoGenerate - Whether to generate fallback if no pattern matches (default: true)
138
+ * @returns Intent string
139
+ */
140
+ export function getIntent(
141
+ title: string,
142
+ description?: string,
143
+ autoGenerate: boolean = true
144
+ ): string | undefined {
145
+ const extracted = extractIntent(title, description);
146
+
147
+ if (extracted) {
148
+ return extracted;
149
+ }
150
+
151
+ if (autoGenerate) {
152
+ return generateFallbackIntent(title);
153
+ }
154
+
155
+ return undefined;
156
+ }
@@ -255,6 +255,33 @@ describe("parseTaskInput", () => {
255
255
  const result = parseTaskInput("간단한 작업 !medium #task");
256
256
  expect(result.workContext).toBeUndefined();
257
257
  });
258
+
259
+ test("parses done: before :: to prevent intent capturing done:", () => {
260
+ // Regression test: done: should be extracted before intent (::)
261
+ const result = parseTaskInput(
262
+ "API 리팩토링 :: 성능 개선 done:테스트통과,코드리뷰 @thorough #backend !high"
263
+ );
264
+ expect(result.title).toBe("API 리팩토링");
265
+ expect(result.workContext?.intent).toBe("성능 개선");
266
+ expect(result.workContext?.acceptanceCriteria).toEqual(["테스트통과", "코드리뷰"]);
267
+ expect(result.workContext?.qualityLevel).toBe("thorough");
268
+ expect(result.priority).toBe("high");
269
+ expect(result.tags).toEqual(["backend"]);
270
+ });
271
+
272
+ test("parses done: without intent", () => {
273
+ const result = parseTaskInput("테스트 작성 done:유닛테스트,통합테스트 !high");
274
+ expect(result.title).toBe("테스트 작성");
275
+ expect(result.workContext?.acceptanceCriteria).toEqual(["유닛테스트", "통합테스트"]);
276
+ expect(result.workContext?.intent).toBeUndefined();
277
+ });
278
+
279
+ test("parses intent without done:", () => {
280
+ const result = parseTaskInput("리팩토링 :: 가독성 향상 !medium");
281
+ expect(result.title).toBe("리팩토링");
282
+ expect(result.workContext?.intent).toBe("가독성 향상");
283
+ expect(result.workContext?.acceptanceCriteria).toBeUndefined();
284
+ });
258
285
  });
259
286
  });
260
287
 
@@ -316,7 +316,16 @@ export function parseTaskInput(input: string): TaskCreateInput {
316
316
  const result: TaskCreateInput = { title: "" };
317
317
  const workContext: Partial<WorkContext> = {};
318
318
 
319
- // Extract intent first (:: separator)
319
+ // Extract acceptanceCriteria FIRST (done:xxx,yyy or done:xxx)
320
+ // Must be before intent extraction to prevent done: from being captured as intent
321
+ // Format: "done:criterion1,criterion2" or "done:single criterion"
322
+ const doneMatch = remaining.match(/done:([^\s@#!~^:]+(?:,[^\s@#!~^:]+)*)/i);
323
+ if (doneMatch) {
324
+ workContext.acceptanceCriteria = doneMatch[1]!.split(",").map((c) => c.trim());
325
+ remaining = remaining.replace(doneMatch[0], " ").trim();
326
+ }
327
+
328
+ // Extract intent (:: separator)
320
329
  // Format: "Task title :: intent description"
321
330
  const intentMatch = remaining.match(/\s*::\s*(.+?)(?=\s+[@#!~^]|$)/);
322
331
  if (intentMatch) {
@@ -324,14 +333,6 @@ export function parseTaskInput(input: string): TaskCreateInput {
324
333
  remaining = remaining.replace(intentMatch[0], " ").trim();
325
334
  }
326
335
 
327
- // Extract acceptanceCriteria (done:xxx,yyy or done:xxx)
328
- // Format: "done:criterion1,criterion2" or "done:single criterion"
329
- const doneMatch = remaining.match(/done:([^\s@#!~^]+(?:,[^\s@#!~^]+)*)/i);
330
- if (doneMatch) {
331
- workContext.acceptanceCriteria = doneMatch[1]!.split(",").map((c) => c.trim());
332
- remaining = remaining.replace(doneMatch[0], " ").trim();
333
- }
334
-
335
336
  // Extract qualityLevel (@quick, @thorough, etc.) - before contexts
336
337
  // These are reserved keywords that become qualityLevel, not contexts
337
338
  const qualityLevelKeywords = Object.keys(QUALITY_LEVEL_KEYWORDS);