tuplet 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (319) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +136 -0
  3. package/dist/agent.d.ts +46 -0
  4. package/dist/agent.d.ts.map +1 -0
  5. package/dist/agent.js +393 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/built-in-agents/explore.d.ts +9 -0
  8. package/dist/built-in-agents/explore.d.ts.map +1 -0
  9. package/dist/built-in-agents/explore.js +40 -0
  10. package/dist/built-in-agents/explore.js.map +1 -0
  11. package/dist/built-in-agents/index.d.ts +15 -0
  12. package/dist/built-in-agents/index.d.ts.map +1 -0
  13. package/dist/built-in-agents/index.js +19 -0
  14. package/dist/built-in-agents/index.js.map +1 -0
  15. package/dist/built-in-agents/plan.d.ts +10 -0
  16. package/dist/built-in-agents/plan.d.ts.map +1 -0
  17. package/dist/built-in-agents/plan.js +62 -0
  18. package/dist/built-in-agents/plan.js.map +1 -0
  19. package/dist/built-in-agents/worker.d.ts +9 -0
  20. package/dist/built-in-agents/worker.d.ts.map +1 -0
  21. package/dist/built-in-agents/worker.js +53 -0
  22. package/dist/built-in-agents/worker.js.map +1 -0
  23. package/dist/constants.d.ts +7 -0
  24. package/dist/constants.d.ts.map +1 -0
  25. package/dist/constants.js +18 -0
  26. package/dist/constants.js.map +1 -0
  27. package/dist/context-manager.d.ts +65 -0
  28. package/dist/context-manager.d.ts.map +1 -0
  29. package/dist/context-manager.js +272 -0
  30. package/dist/context-manager.js.map +1 -0
  31. package/dist/context-manager.test.d.ts +2 -0
  32. package/dist/context-manager.test.d.ts.map +1 -0
  33. package/dist/context-manager.test.js +394 -0
  34. package/dist/context-manager.test.js.map +1 -0
  35. package/dist/executor.d.ts +29 -0
  36. package/dist/executor.d.ts.map +1 -0
  37. package/dist/executor.js +399 -0
  38. package/dist/executor.js.map +1 -0
  39. package/dist/index.d.ts +24 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +40 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/prompt/index.d.ts +9 -0
  44. package/dist/prompt/index.d.ts.map +1 -0
  45. package/dist/prompt/index.js +10 -0
  46. package/dist/prompt/index.js.map +1 -0
  47. package/dist/prompt/main-agent-builder.d.ts +81 -0
  48. package/dist/prompt/main-agent-builder.d.ts.map +1 -0
  49. package/dist/prompt/main-agent-builder.js +287 -0
  50. package/dist/prompt/main-agent-builder.js.map +1 -0
  51. package/dist/prompt/sub-agent-builder.d.ts +133 -0
  52. package/dist/prompt/sub-agent-builder.d.ts.map +1 -0
  53. package/dist/prompt/sub-agent-builder.js +337 -0
  54. package/dist/prompt/sub-agent-builder.js.map +1 -0
  55. package/dist/prompt/templates.d.ts +87 -0
  56. package/dist/prompt/templates.d.ts.map +1 -0
  57. package/dist/prompt/templates.js +343 -0
  58. package/dist/prompt/templates.js.map +1 -0
  59. package/dist/prompt/types.d.ts +159 -0
  60. package/dist/prompt/types.d.ts.map +1 -0
  61. package/dist/prompt/types.js +5 -0
  62. package/dist/prompt/types.js.map +1 -0
  63. package/dist/prompt.d.ts +32 -0
  64. package/dist/prompt.d.ts.map +1 -0
  65. package/dist/prompt.js +86 -0
  66. package/dist/prompt.js.map +1 -0
  67. package/dist/providers/dataset/base.d.ts +74 -0
  68. package/dist/providers/dataset/base.d.ts.map +1 -0
  69. package/dist/providers/dataset/base.js +7 -0
  70. package/dist/providers/dataset/base.js.map +1 -0
  71. package/dist/providers/dataset/index.d.ts +8 -0
  72. package/dist/providers/dataset/index.d.ts.map +1 -0
  73. package/dist/providers/dataset/index.js +8 -0
  74. package/dist/providers/dataset/index.js.map +1 -0
  75. package/dist/providers/dataset/recorder.d.ts +46 -0
  76. package/dist/providers/dataset/recorder.d.ts.map +1 -0
  77. package/dist/providers/dataset/recorder.js +105 -0
  78. package/dist/providers/dataset/recorder.js.map +1 -0
  79. package/dist/providers/dataset/replayer.d.ts +46 -0
  80. package/dist/providers/dataset/replayer.d.ts.map +1 -0
  81. package/dist/providers/dataset/replayer.js +163 -0
  82. package/dist/providers/dataset/replayer.js.map +1 -0
  83. package/dist/providers/dataset/tester.d.ts +89 -0
  84. package/dist/providers/dataset/tester.d.ts.map +1 -0
  85. package/dist/providers/dataset/tester.js +143 -0
  86. package/dist/providers/dataset/tester.js.map +1 -0
  87. package/dist/providers/env/memory.d.ts +14 -0
  88. package/dist/providers/env/memory.d.ts.map +1 -0
  89. package/dist/providers/env/memory.js +19 -0
  90. package/dist/providers/env/memory.js.map +1 -0
  91. package/dist/providers/index.d.ts +12 -0
  92. package/dist/providers/index.d.ts.map +1 -0
  93. package/dist/providers/index.js +10 -0
  94. package/dist/providers/index.js.map +1 -0
  95. package/dist/providers/llm/base.d.ts +7 -0
  96. package/dist/providers/llm/base.d.ts.map +1 -0
  97. package/dist/providers/llm/base.js +7 -0
  98. package/dist/providers/llm/base.js.map +1 -0
  99. package/dist/providers/llm/claude.d.ts +32 -0
  100. package/dist/providers/llm/claude.d.ts.map +1 -0
  101. package/dist/providers/llm/claude.js +171 -0
  102. package/dist/providers/llm/claude.js.map +1 -0
  103. package/dist/providers/llm/openai.d.ts +26 -0
  104. package/dist/providers/llm/openai.d.ts.map +1 -0
  105. package/dist/providers/llm/openai.js +174 -0
  106. package/dist/providers/llm/openai.js.map +1 -0
  107. package/dist/providers/llm/openrouter.d.ts +43 -0
  108. package/dist/providers/llm/openrouter.d.ts.map +1 -0
  109. package/dist/providers/llm/openrouter.js +288 -0
  110. package/dist/providers/llm/openrouter.js.map +1 -0
  111. package/dist/providers/logger/base.d.ts +7 -0
  112. package/dist/providers/logger/base.d.ts.map +1 -0
  113. package/dist/providers/logger/base.js +7 -0
  114. package/dist/providers/logger/base.js.map +1 -0
  115. package/dist/providers/logger/console.d.ts +29 -0
  116. package/dist/providers/logger/console.d.ts.map +1 -0
  117. package/dist/providers/logger/console.js +70 -0
  118. package/dist/providers/logger/console.js.map +1 -0
  119. package/dist/providers/repository/base.d.ts +7 -0
  120. package/dist/providers/repository/base.d.ts.map +1 -0
  121. package/dist/providers/repository/base.js +7 -0
  122. package/dist/providers/repository/base.js.map +1 -0
  123. package/dist/providers/repository/memory.d.ts +21 -0
  124. package/dist/providers/repository/memory.d.ts.map +1 -0
  125. package/dist/providers/repository/memory.js +50 -0
  126. package/dist/providers/repository/memory.js.map +1 -0
  127. package/dist/providers/workspace/file.d.ts +26 -0
  128. package/dist/providers/workspace/file.d.ts.map +1 -0
  129. package/dist/providers/workspace/file.js +151 -0
  130. package/dist/providers/workspace/file.js.map +1 -0
  131. package/dist/providers/workspace/index.d.ts +7 -0
  132. package/dist/providers/workspace/index.d.ts.map +1 -0
  133. package/dist/providers/workspace/index.js +6 -0
  134. package/dist/providers/workspace/index.js.map +1 -0
  135. package/dist/providers/workspace/memory.d.ts +26 -0
  136. package/dist/providers/workspace/memory.d.ts.map +1 -0
  137. package/dist/providers/workspace/memory.js +136 -0
  138. package/dist/providers/workspace/memory.js.map +1 -0
  139. package/dist/providers/workspace/types.d.ts +27 -0
  140. package/dist/providers/workspace/types.d.ts.map +1 -0
  141. package/dist/providers/workspace/types.js +8 -0
  142. package/dist/providers/workspace/types.js.map +1 -0
  143. package/dist/providers/workspace/workspace-provider.test.d.ts +2 -0
  144. package/dist/providers/workspace/workspace-provider.test.d.ts.map +1 -0
  145. package/dist/providers/workspace/workspace-provider.test.js +250 -0
  146. package/dist/providers/workspace/workspace-provider.test.js.map +1 -0
  147. package/dist/shell/commands/browse.d.ts +6 -0
  148. package/dist/shell/commands/browse.d.ts.map +1 -0
  149. package/dist/shell/commands/browse.js +158 -0
  150. package/dist/shell/commands/browse.js.map +1 -0
  151. package/dist/shell/commands/cat.d.ts +6 -0
  152. package/dist/shell/commands/cat.d.ts.map +1 -0
  153. package/dist/shell/commands/cat.js +104 -0
  154. package/dist/shell/commands/cat.js.map +1 -0
  155. package/dist/shell/commands/curl.d.ts +6 -0
  156. package/dist/shell/commands/curl.d.ts.map +1 -0
  157. package/dist/shell/commands/curl.js +190 -0
  158. package/dist/shell/commands/curl.js.map +1 -0
  159. package/dist/shell/commands/date.d.ts +6 -0
  160. package/dist/shell/commands/date.d.ts.map +1 -0
  161. package/dist/shell/commands/date.js +151 -0
  162. package/dist/shell/commands/date.js.map +1 -0
  163. package/dist/shell/commands/echo.d.ts +6 -0
  164. package/dist/shell/commands/echo.d.ts.map +1 -0
  165. package/dist/shell/commands/echo.js +48 -0
  166. package/dist/shell/commands/echo.js.map +1 -0
  167. package/dist/shell/commands/env.d.ts +8 -0
  168. package/dist/shell/commands/env.d.ts.map +1 -0
  169. package/dist/shell/commands/env.js +41 -0
  170. package/dist/shell/commands/env.js.map +1 -0
  171. package/dist/shell/commands/file.d.ts +6 -0
  172. package/dist/shell/commands/file.d.ts.map +1 -0
  173. package/dist/shell/commands/file.js +213 -0
  174. package/dist/shell/commands/file.js.map +1 -0
  175. package/dist/shell/commands/find.d.ts +6 -0
  176. package/dist/shell/commands/find.d.ts.map +1 -0
  177. package/dist/shell/commands/find.js +100 -0
  178. package/dist/shell/commands/find.js.map +1 -0
  179. package/dist/shell/commands/grep.d.ts +6 -0
  180. package/dist/shell/commands/grep.d.ts.map +1 -0
  181. package/dist/shell/commands/grep.js +229 -0
  182. package/dist/shell/commands/grep.js.map +1 -0
  183. package/dist/shell/commands/head.d.ts +6 -0
  184. package/dist/shell/commands/head.d.ts.map +1 -0
  185. package/dist/shell/commands/head.js +88 -0
  186. package/dist/shell/commands/head.js.map +1 -0
  187. package/dist/shell/commands/index.d.ts +25 -0
  188. package/dist/shell/commands/index.d.ts.map +1 -0
  189. package/dist/shell/commands/index.js +43 -0
  190. package/dist/shell/commands/index.js.map +1 -0
  191. package/dist/shell/commands/jq.d.ts +8 -0
  192. package/dist/shell/commands/jq.d.ts.map +1 -0
  193. package/dist/shell/commands/jq.js +233 -0
  194. package/dist/shell/commands/jq.js.map +1 -0
  195. package/dist/shell/commands/ls.d.ts +6 -0
  196. package/dist/shell/commands/ls.d.ts.map +1 -0
  197. package/dist/shell/commands/ls.js +88 -0
  198. package/dist/shell/commands/ls.js.map +1 -0
  199. package/dist/shell/commands/mkdir.d.ts +6 -0
  200. package/dist/shell/commands/mkdir.d.ts.map +1 -0
  201. package/dist/shell/commands/mkdir.js +43 -0
  202. package/dist/shell/commands/mkdir.js.map +1 -0
  203. package/dist/shell/commands/rm.d.ts +6 -0
  204. package/dist/shell/commands/rm.d.ts.map +1 -0
  205. package/dist/shell/commands/rm.js +64 -0
  206. package/dist/shell/commands/rm.js.map +1 -0
  207. package/dist/shell/commands/sed.d.ts +6 -0
  208. package/dist/shell/commands/sed.d.ts.map +1 -0
  209. package/dist/shell/commands/sed.js +414 -0
  210. package/dist/shell/commands/sed.js.map +1 -0
  211. package/dist/shell/commands/sort.d.ts +6 -0
  212. package/dist/shell/commands/sort.d.ts.map +1 -0
  213. package/dist/shell/commands/sort.js +109 -0
  214. package/dist/shell/commands/sort.js.map +1 -0
  215. package/dist/shell/commands/tail.d.ts +6 -0
  216. package/dist/shell/commands/tail.d.ts.map +1 -0
  217. package/dist/shell/commands/tail.js +68 -0
  218. package/dist/shell/commands/tail.js.map +1 -0
  219. package/dist/shell/commands/wc.d.ts +6 -0
  220. package/dist/shell/commands/wc.d.ts.map +1 -0
  221. package/dist/shell/commands/wc.js +86 -0
  222. package/dist/shell/commands/wc.js.map +1 -0
  223. package/dist/shell/index.d.ts +10 -0
  224. package/dist/shell/index.d.ts.map +1 -0
  225. package/dist/shell/index.js +10 -0
  226. package/dist/shell/index.js.map +1 -0
  227. package/dist/shell/limits.d.ts +5 -0
  228. package/dist/shell/limits.d.ts.map +1 -0
  229. package/dist/shell/limits.js +5 -0
  230. package/dist/shell/limits.js.map +1 -0
  231. package/dist/shell/parser.d.ts +8 -0
  232. package/dist/shell/parser.d.ts.map +1 -0
  233. package/dist/shell/parser.js +307 -0
  234. package/dist/shell/parser.js.map +1 -0
  235. package/dist/shell/path-validation.d.ts +35 -0
  236. package/dist/shell/path-validation.d.ts.map +1 -0
  237. package/dist/shell/path-validation.js +81 -0
  238. package/dist/shell/path-validation.js.map +1 -0
  239. package/dist/shell/shell.d.ts +66 -0
  240. package/dist/shell/shell.d.ts.map +1 -0
  241. package/dist/shell/shell.js +301 -0
  242. package/dist/shell/shell.js.map +1 -0
  243. package/dist/shell/shell.test.d.ts +2 -0
  244. package/dist/shell/shell.test.d.ts.map +1 -0
  245. package/dist/shell/shell.test.js +1088 -0
  246. package/dist/shell/shell.test.js.map +1 -0
  247. package/dist/shell/types.d.ts +82 -0
  248. package/dist/shell/types.d.ts.map +1 -0
  249. package/dist/shell/types.js +5 -0
  250. package/dist/shell/types.js.map +1 -0
  251. package/dist/summarizer.d.ts +28 -0
  252. package/dist/summarizer.d.ts.map +1 -0
  253. package/dist/summarizer.js +136 -0
  254. package/dist/summarizer.js.map +1 -0
  255. package/dist/summarizer.test.d.ts +2 -0
  256. package/dist/summarizer.test.d.ts.map +1 -0
  257. package/dist/summarizer.test.js +192 -0
  258. package/dist/summarizer.test.js.map +1 -0
  259. package/dist/tools/ask-user.d.ts +11 -0
  260. package/dist/tools/ask-user.d.ts.map +1 -0
  261. package/dist/tools/ask-user.js +35 -0
  262. package/dist/tools/ask-user.js.map +1 -0
  263. package/dist/tools/index.d.ts +11 -0
  264. package/dist/tools/index.d.ts.map +1 -0
  265. package/dist/tools/index.js +18 -0
  266. package/dist/tools/index.js.map +1 -0
  267. package/dist/tools/output.d.ts +15 -0
  268. package/dist/tools/output.d.ts.map +1 -0
  269. package/dist/tools/output.js +40 -0
  270. package/dist/tools/output.js.map +1 -0
  271. package/dist/tools/shell.d.ts +13 -0
  272. package/dist/tools/shell.d.ts.map +1 -0
  273. package/dist/tools/shell.js +166 -0
  274. package/dist/tools/shell.js.map +1 -0
  275. package/dist/tools/sub-agent.d.ts +25 -0
  276. package/dist/tools/sub-agent.d.ts.map +1 -0
  277. package/dist/tools/sub-agent.js +312 -0
  278. package/dist/tools/sub-agent.js.map +1 -0
  279. package/dist/tools/tasks.d.ts +170 -0
  280. package/dist/tools/tasks.d.ts.map +1 -0
  281. package/dist/tools/tasks.js +947 -0
  282. package/dist/tools/tasks.js.map +1 -0
  283. package/dist/trace/builder.d.ts +54 -0
  284. package/dist/trace/builder.d.ts.map +1 -0
  285. package/dist/trace/builder.js +229 -0
  286. package/dist/trace/builder.js.map +1 -0
  287. package/dist/trace/console.d.ts +45 -0
  288. package/dist/trace/console.d.ts.map +1 -0
  289. package/dist/trace/console.js +143 -0
  290. package/dist/trace/console.js.map +1 -0
  291. package/dist/trace/index.d.ts +11 -0
  292. package/dist/trace/index.d.ts.map +1 -0
  293. package/dist/trace/index.js +11 -0
  294. package/dist/trace/index.js.map +1 -0
  295. package/dist/trace/openrouter-pricing.d.ts +9 -0
  296. package/dist/trace/openrouter-pricing.d.ts.map +1 -0
  297. package/dist/trace/openrouter-pricing.js +321 -0
  298. package/dist/trace/openrouter-pricing.js.map +1 -0
  299. package/dist/trace/pricing.d.ts +13 -0
  300. package/dist/trace/pricing.d.ts.map +1 -0
  301. package/dist/trace/pricing.js +41 -0
  302. package/dist/trace/pricing.js.map +1 -0
  303. package/dist/trace/types.d.ts +142 -0
  304. package/dist/trace/types.d.ts.map +1 -0
  305. package/dist/trace/types.js +16 -0
  306. package/dist/trace/types.js.map +1 -0
  307. package/dist/trace.d.ts +5 -0
  308. package/dist/trace.d.ts.map +1 -0
  309. package/dist/trace.js +5 -0
  310. package/dist/trace.js.map +1 -0
  311. package/dist/types.d.ts +382 -0
  312. package/dist/types.d.ts.map +1 -0
  313. package/dist/types.js +5 -0
  314. package/dist/types.js.map +1 -0
  315. package/dist/workspace.d.ts +287 -0
  316. package/dist/workspace.d.ts.map +1 -0
  317. package/dist/workspace.js +560 -0
  318. package/dist/workspace.js.map +1 -0
  319. package/package.json +47 -0
@@ -0,0 +1,947 @@
1
+ /**
2
+ * Task Management System (Claude Code 4-Tool Approach)
3
+ *
4
+ * Provides task management functionality for agents to track and execute tasks.
5
+ * Uses 4 separate tools: TaskCreate, TaskUpdate, TaskGet, TaskList
6
+ */
7
+ /** Path where tasks are persisted in Workspace */
8
+ const TASKS_CONTEXT_PATH = '.tuplet/tasks.json';
9
+ /**
10
+ * Format task list for display
11
+ * @param tasks - Array of tasks to format
12
+ * @param allTasks - Optional full task list (for checking blocker status). If not provided, blockedBy status won't be resolved.
13
+ */
14
+ export function formatTaskList(tasks, allTasks) {
15
+ if (tasks.length === 0) {
16
+ return "No tasks in the task list.";
17
+ }
18
+ const statusEmoji = {
19
+ pending: "⬜",
20
+ in_progress: "🔄",
21
+ completed: "✅",
22
+ };
23
+ // Create a map for quick task lookup
24
+ const taskMap = new Map();
25
+ for (const t of (allTasks || tasks)) {
26
+ taskMap.set(t.id, t);
27
+ }
28
+ // Helper to check if a task has open blockers
29
+ const hasOpenBlockers = (task) => {
30
+ if (!task.blockedBy || task.blockedBy.length === 0)
31
+ return false;
32
+ return task.blockedBy.some(blockerId => {
33
+ const blocker = taskMap.get(blockerId);
34
+ return blocker && blocker.status !== 'completed';
35
+ });
36
+ };
37
+ // Helper to get open blocker IDs
38
+ const getOpenBlockerIds = (task) => {
39
+ if (!task.blockedBy)
40
+ return [];
41
+ return task.blockedBy.filter(blockerId => {
42
+ const blocker = taskMap.get(blockerId);
43
+ return blocker && blocker.status !== 'completed';
44
+ });
45
+ };
46
+ return tasks
47
+ .map((task) => {
48
+ const emoji = statusEmoji[task.status];
49
+ const ownerTag = task.owner ? ` [@${task.owner}]` : '';
50
+ // Show blocked indicator if task has open blockers
51
+ let blockedTag = '';
52
+ if (task.status !== 'completed' && hasOpenBlockers(task)) {
53
+ const openBlockers = getOpenBlockerIds(task);
54
+ blockedTag = ` (blocked by: ${openBlockers.join(', ')})`;
55
+ }
56
+ // Show activeForm when in_progress, otherwise show subject
57
+ const label = task.status === "in_progress" && task.activeForm
58
+ ? task.activeForm
59
+ : task.subject;
60
+ return `${task.id}. ${emoji} ${label}${ownerTag}${blockedTag}`;
61
+ })
62
+ .join("\n");
63
+ }
64
+ /** @deprecated Use formatTaskList instead */
65
+ export function formatTodoList(todos) {
66
+ // Convert TodoItem to TaskItem format for display
67
+ const tasks = todos.map(todo => ({
68
+ id: todo.id,
69
+ subject: todo.content,
70
+ activeForm: todo.activeForm,
71
+ status: todo.status,
72
+ createdAt: todo.createdAt,
73
+ completedAt: todo.completedAt,
74
+ }));
75
+ return formatTaskList(tasks);
76
+ }
77
+ /**
78
+ * TaskManager class for tracking tasks during execution
79
+ */
80
+ export class TaskManager {
81
+ items = new Map();
82
+ currentTaskId;
83
+ nextId = 1;
84
+ /**
85
+ * Get all task items
86
+ */
87
+ getAll() {
88
+ // Return in ID order (sequential)
89
+ return Array.from(this.items.values()).sort((a, b) => parseInt(a.id) - parseInt(b.id));
90
+ }
91
+ /**
92
+ * Get a task by ID
93
+ */
94
+ get(id) {
95
+ return this.items.get(id);
96
+ }
97
+ /**
98
+ * Get current task being worked on
99
+ */
100
+ getCurrentTask() {
101
+ if (!this.currentTaskId)
102
+ return undefined;
103
+ return this.items.get(this.currentTaskId);
104
+ }
105
+ /**
106
+ * Check if a task is blocked (has unresolved blockers)
107
+ */
108
+ isBlocked(task) {
109
+ if (!task.blockedBy || task.blockedBy.length === 0)
110
+ return false;
111
+ // Check if any blocker is not completed
112
+ return task.blockedBy.some(blockerId => {
113
+ const blocker = this.items.get(blockerId);
114
+ return blocker && blocker.status !== 'completed';
115
+ });
116
+ }
117
+ /**
118
+ * Get open (unresolved) blockers for a task
119
+ */
120
+ getOpenBlockers(task) {
121
+ if (!task.blockedBy)
122
+ return [];
123
+ return task.blockedBy.filter(blockerId => {
124
+ const blocker = this.items.get(blockerId);
125
+ return blocker && blocker.status !== 'completed';
126
+ });
127
+ }
128
+ /**
129
+ * Create a new task
130
+ * If this is the first task and no tasks are in_progress, auto-starts it (if not blocked)
131
+ */
132
+ create(subject, description, activeForm, metadata) {
133
+ const id = String(this.nextId++);
134
+ const item = {
135
+ id,
136
+ subject,
137
+ description,
138
+ activeForm,
139
+ status: "pending",
140
+ metadata,
141
+ createdAt: Date.now(),
142
+ };
143
+ this.items.set(id, item);
144
+ // Auto-start if no task is currently in progress and task is not blocked
145
+ const hasInProgress = Array.from(this.items.values()).some(t => t.status === "in_progress");
146
+ if (!hasInProgress && !this.isBlocked(item)) {
147
+ item.status = "in_progress";
148
+ this.currentTaskId = id;
149
+ }
150
+ return item;
151
+ }
152
+ /**
153
+ * Update a task
154
+ * Returns the updated task or undefined if not found
155
+ */
156
+ update(id, updates) {
157
+ const item = this.items.get(id);
158
+ if (!item)
159
+ return {};
160
+ // Handle deletion
161
+ if (updates.status === 'deleted') {
162
+ // Remove this task from other tasks' blockedBy lists
163
+ this.removeFromBlockedBy(id);
164
+ // Remove this task from other tasks' blocks lists
165
+ this.removeFromBlocks(id);
166
+ this.items.delete(id);
167
+ if (this.currentTaskId === id) {
168
+ this.currentTaskId = undefined;
169
+ }
170
+ // Auto-start next pending task
171
+ const next = this.startNextPending();
172
+ return { deleted: true, next };
173
+ }
174
+ // Apply updates
175
+ if (updates.subject !== undefined)
176
+ item.subject = updates.subject;
177
+ if (updates.description !== undefined)
178
+ item.description = updates.description;
179
+ if (updates.activeForm !== undefined)
180
+ item.activeForm = updates.activeForm;
181
+ if (updates.owner !== undefined)
182
+ item.owner = updates.owner;
183
+ if (updates.metadata !== undefined) {
184
+ // Merge metadata, allowing null values to delete keys
185
+ item.metadata = item.metadata || {};
186
+ for (const [key, value] of Object.entries(updates.metadata)) {
187
+ if (value === null) {
188
+ delete item.metadata[key];
189
+ }
190
+ else {
191
+ item.metadata[key] = value;
192
+ }
193
+ }
194
+ }
195
+ // Handle dependency updates
196
+ if (updates.addBlocks) {
197
+ item.blocks = item.blocks || [];
198
+ for (const blockedId of updates.addBlocks) {
199
+ if (!item.blocks.includes(blockedId)) {
200
+ item.blocks.push(blockedId);
201
+ }
202
+ // Also update the blocked task's blockedBy (bidirectional)
203
+ const blockedTask = this.items.get(blockedId);
204
+ if (blockedTask) {
205
+ blockedTask.blockedBy = blockedTask.blockedBy || [];
206
+ if (!blockedTask.blockedBy.includes(id)) {
207
+ blockedTask.blockedBy.push(id);
208
+ }
209
+ }
210
+ }
211
+ }
212
+ if (updates.addBlockedBy) {
213
+ item.blockedBy = item.blockedBy || [];
214
+ for (const blockerId of updates.addBlockedBy) {
215
+ if (!item.blockedBy.includes(blockerId)) {
216
+ item.blockedBy.push(blockerId);
217
+ }
218
+ // Also update the blocker task's blocks (bidirectional)
219
+ const blockerTask = this.items.get(blockerId);
220
+ if (blockerTask) {
221
+ blockerTask.blocks = blockerTask.blocks || [];
222
+ if (!blockerTask.blocks.includes(id)) {
223
+ blockerTask.blocks.push(id);
224
+ }
225
+ }
226
+ }
227
+ }
228
+ // Handle comment
229
+ if (updates.comment) {
230
+ item.comments = item.comments || [];
231
+ const comment = {
232
+ author: updates.comment.author,
233
+ content: updates.comment.content,
234
+ createdAt: Date.now(),
235
+ };
236
+ item.comments.push(comment);
237
+ }
238
+ // Handle status changes
239
+ if (updates.status) {
240
+ const newStatus = updates.status;
241
+ const oldStatus = item.status;
242
+ item.status = newStatus;
243
+ if (updates.status === 'in_progress') {
244
+ // Pause any other in_progress task
245
+ if (this.currentTaskId && this.currentTaskId !== id) {
246
+ const current = this.items.get(this.currentTaskId);
247
+ if (current && current.status === 'in_progress') {
248
+ current.status = 'pending';
249
+ }
250
+ }
251
+ this.currentTaskId = id;
252
+ }
253
+ else if (updates.status === 'completed') {
254
+ item.completedAt = Date.now();
255
+ if (this.currentTaskId === id) {
256
+ this.currentTaskId = undefined;
257
+ }
258
+ // Auto-start next pending task (checking for unblocked)
259
+ const next = this.startNextPending();
260
+ return { task: item, next };
261
+ }
262
+ else if (oldStatus === 'in_progress' && updates.status === 'pending') {
263
+ if (this.currentTaskId === id) {
264
+ this.currentTaskId = undefined;
265
+ }
266
+ }
267
+ }
268
+ return { task: item };
269
+ }
270
+ /**
271
+ * Remove a task ID from all other tasks' blockedBy lists
272
+ */
273
+ removeFromBlockedBy(taskId) {
274
+ for (const task of this.items.values()) {
275
+ if (task.blockedBy) {
276
+ task.blockedBy = task.blockedBy.filter(id => id !== taskId);
277
+ if (task.blockedBy.length === 0) {
278
+ delete task.blockedBy;
279
+ }
280
+ }
281
+ }
282
+ }
283
+ /**
284
+ * Remove a task ID from all other tasks' blocks lists
285
+ */
286
+ removeFromBlocks(taskId) {
287
+ for (const task of this.items.values()) {
288
+ if (task.blocks) {
289
+ task.blocks = task.blocks.filter(id => id !== taskId);
290
+ if (task.blocks.length === 0) {
291
+ delete task.blocks;
292
+ }
293
+ }
294
+ }
295
+ }
296
+ /**
297
+ * Find and start the next pending task that is not blocked
298
+ */
299
+ startNextPending() {
300
+ const pending = this.getAll().find(t => t.status === 'pending' && !this.isBlocked(t));
301
+ if (pending) {
302
+ pending.status = 'in_progress';
303
+ this.currentTaskId = pending.id;
304
+ return pending;
305
+ }
306
+ return undefined;
307
+ }
308
+ /**
309
+ * Check if all tasks are completed
310
+ */
311
+ isAllCompleted() {
312
+ const items = Array.from(this.items.values());
313
+ return items.length > 0 && items.every(i => i.status === "completed");
314
+ }
315
+ /**
316
+ * Get progress stats
317
+ */
318
+ getProgress() {
319
+ const items = Array.from(this.items.values());
320
+ return {
321
+ total: items.length,
322
+ completed: items.filter(i => i.status === "completed").length,
323
+ pending: items.filter(i => i.status === "pending").length,
324
+ inProgress: items.filter(i => i.status === "in_progress").length,
325
+ };
326
+ }
327
+ /**
328
+ * Normalize a subject string for duplicate comparison.
329
+ * Lowercases, strips common filler words, and collapses whitespace.
330
+ */
331
+ normalizeSubject(subject) {
332
+ const fillerWords = /\b(the|a|an|and|or|to|from|for|in|on|of|with|is|are|was|were|be|been|being|that|this|it)\b/g;
333
+ return subject
334
+ .toLowerCase()
335
+ .replace(fillerWords, '')
336
+ .replace(/[^a-z0-9\s]/g, '')
337
+ .replace(/\s+/g, ' ')
338
+ .trim();
339
+ }
340
+ /**
341
+ * Find a duplicate among non-completed tasks.
342
+ * Checks for exact normalized match or substring containment.
343
+ */
344
+ findDuplicate(subject) {
345
+ const normalized = this.normalizeSubject(subject);
346
+ if (!normalized)
347
+ return undefined;
348
+ for (const task of this.items.values()) {
349
+ if (task.status === 'completed')
350
+ continue;
351
+ const existingNormalized = this.normalizeSubject(task.subject);
352
+ if (!existingNormalized)
353
+ continue;
354
+ // Exact match after normalization
355
+ if (normalized === existingNormalized)
356
+ return task;
357
+ // Substring containment (either direction)
358
+ if (normalized.includes(existingNormalized) || existingNormalized.includes(normalized)) {
359
+ return task;
360
+ }
361
+ }
362
+ return undefined;
363
+ }
364
+ /**
365
+ * Clear all tasks (used internally)
366
+ */
367
+ clear() {
368
+ this.items.clear();
369
+ this.currentTaskId = undefined;
370
+ this.nextId = 1;
371
+ }
372
+ /**
373
+ * Serialize the TaskManager state for persistence
374
+ */
375
+ serialize() {
376
+ return {
377
+ items: this.getAll(),
378
+ currentTaskId: this.currentTaskId,
379
+ nextId: this.nextId,
380
+ };
381
+ }
382
+ /**
383
+ * Restore TaskManager state from serialized data
384
+ */
385
+ restore(state) {
386
+ this.items.clear();
387
+ for (const item of state.items) {
388
+ this.items.set(item.id, item);
389
+ }
390
+ this.currentTaskId = state.currentTaskId;
391
+ this.nextId = state.nextId;
392
+ }
393
+ /**
394
+ * Save tasks to Workspace (if provided)
395
+ */
396
+ saveToWorkspace(workspace, agentName) {
397
+ if (!workspace)
398
+ return;
399
+ try {
400
+ const state = this.serialize();
401
+ workspace.write(TASKS_CONTEXT_PATH, state, agentName || 'task-manager');
402
+ }
403
+ catch {
404
+ // Silently ignore write errors (workspace may not support this path)
405
+ }
406
+ }
407
+ /**
408
+ * Restore tasks from Workspace (if available)
409
+ * Returns true if tasks were restored
410
+ */
411
+ async restoreFromWorkspace(workspace) {
412
+ if (!workspace)
413
+ return false;
414
+ try {
415
+ const state = await workspace.read(TASKS_CONTEXT_PATH);
416
+ if (state && state.items && Array.isArray(state.items)) {
417
+ this.restore(state);
418
+ return true;
419
+ }
420
+ }
421
+ catch {
422
+ // Silently ignore read errors
423
+ }
424
+ return false;
425
+ }
426
+ }
427
+ /** @deprecated Use TaskManager instead */
428
+ export const TodoManager = TaskManager;
429
+ /**
430
+ * Check if the current agent can update a task
431
+ * Returns true if:
432
+ * - Task has no owner
433
+ * - Current agent is the owner
434
+ * - Current agent is a team-lead
435
+ */
436
+ function canUpdateTask(task, options) {
437
+ // No owner - anyone can update
438
+ if (!task.owner)
439
+ return true;
440
+ const agentId = options.agentId || process.env.CLAUDE_CODE_AGENT_ID;
441
+ const agentType = options.agentType || process.env.CLAUDE_CODE_AGENT_TYPE;
442
+ // Team leads can update any task
443
+ if (agentType === 'team-lead')
444
+ return true;
445
+ // Owner can update their own tasks
446
+ if (agentId && task.owner === agentId)
447
+ return true;
448
+ return false;
449
+ }
450
+ /**
451
+ * Notify logger about task list changes and persist to workspace
452
+ */
453
+ function notifyTaskUpdate(manager, action, options) {
454
+ const { logger, agentName, workspace } = options;
455
+ // Persist to workspace on mutations (not on list)
456
+ if (action !== 'list' && workspace) {
457
+ manager.saveToWorkspace(workspace, agentName);
458
+ }
459
+ if (logger?.onTaskUpdate) {
460
+ const tasks = manager.getAll();
461
+ const current = manager.getCurrentTask();
462
+ const progress = manager.getProgress();
463
+ logger.onTaskUpdate({
464
+ agentName,
465
+ action,
466
+ tasks,
467
+ current: current || undefined,
468
+ progress,
469
+ });
470
+ }
471
+ // Also call deprecated onTodoUpdate for backward compatibility
472
+ if (logger?.onTodoUpdate) {
473
+ const tasks = manager.getAll();
474
+ const current = manager.getCurrentTask();
475
+ const progress = manager.getProgress();
476
+ // Convert to old format
477
+ const todos = tasks.map(t => ({
478
+ id: t.id,
479
+ content: t.subject,
480
+ activeForm: t.activeForm,
481
+ status: t.status,
482
+ createdAt: t.createdAt,
483
+ completedAt: t.completedAt,
484
+ }));
485
+ const todoUpdate = {
486
+ agentName,
487
+ action: action === 'create' ? 'set' : action === 'delete' ? 'update' : action === 'list' ? 'update' : 'update',
488
+ todos,
489
+ current: current ? {
490
+ id: current.id,
491
+ content: current.subject,
492
+ activeForm: current.activeForm,
493
+ status: current.status,
494
+ createdAt: current.createdAt,
495
+ completedAt: current.completedAt,
496
+ } : undefined,
497
+ progress,
498
+ };
499
+ logger.onTodoUpdate(todoUpdate);
500
+ }
501
+ }
502
+ /**
503
+ * Create the TaskCreate tool
504
+ */
505
+ export function createTaskCreateTool(manager, options = {}) {
506
+ return {
507
+ name: "TaskCreate",
508
+ description: `Create a task in the task list. Duplicates are automatically rejected.
509
+
510
+ ## Fields
511
+
512
+ - **subject**: Brief, actionable title in imperative form (e.g., "Fix authentication bug")
513
+ - **description**: What needs to be done, including context and acceptance criteria
514
+ - **activeForm**: Present continuous form shown in spinner when in_progress (e.g., "Fixing authentication bug")
515
+
516
+ Always provide activeForm. Tasks are created with status \`pending\` (first task auto-starts).
517
+ `,
518
+ parameters: {
519
+ type: "object",
520
+ properties: {
521
+ subject: {
522
+ type: "string",
523
+ description: "A brief title for the task",
524
+ },
525
+ description: {
526
+ type: "string",
527
+ description: "A detailed description of what needs to be done",
528
+ },
529
+ activeForm: {
530
+ type: "string",
531
+ description: 'Present continuous form shown in spinner when in_progress (e.g., "Running tests")',
532
+ },
533
+ metadata: {
534
+ type: "string",
535
+ description: "JSON string of arbitrary metadata to attach to the task",
536
+ },
537
+ },
538
+ required: ["subject", "description"],
539
+ },
540
+ execute: async (params) => {
541
+ const { subject, description, activeForm, metadata: rawMetadata } = params;
542
+ // Check for duplicates before creating
543
+ const duplicate = manager.findDuplicate(subject);
544
+ if (duplicate) {
545
+ return {
546
+ success: false,
547
+ error: `Duplicate: task #${duplicate.id} "${duplicate.subject}" already covers this. Use the existing task instead.`,
548
+ };
549
+ }
550
+ // Parse metadata if provided
551
+ let metadata;
552
+ if (rawMetadata) {
553
+ try {
554
+ metadata = JSON.parse(rawMetadata);
555
+ }
556
+ catch {
557
+ return { success: false, error: "Invalid metadata JSON" };
558
+ }
559
+ }
560
+ const task = manager.create(subject, description, activeForm, metadata);
561
+ const progress = manager.getProgress();
562
+ notifyTaskUpdate(manager, 'create', options);
563
+ return {
564
+ success: true,
565
+ data: {
566
+ message: `Created task #${task.id}: "${task.subject}"${task.status === 'in_progress' ? ' (auto-started)' : ''}`,
567
+ task,
568
+ progress,
569
+ },
570
+ };
571
+ },
572
+ };
573
+ }
574
+ /**
575
+ * Create the TaskUpdate tool
576
+ */
577
+ export function createTaskUpdateTool(manager, options = {}) {
578
+ return {
579
+ name: "TaskUpdate",
580
+ description: `Update a task's status, details, or dependencies.
581
+
582
+ Status: \`pending\` → \`in_progress\` → \`completed\`. Use \`deleted\` to remove.
583
+
584
+ Only mark completed when fully done. If blocked, keep as in_progress.
585
+
586
+ ## Examples
587
+
588
+ \`\`\`json
589
+ {"taskId": "1", "status": "completed"}
590
+ {"taskId": "1", "status": "in_progress"}
591
+ {"taskId": "2", "addBlockedBy": ["1"]}
592
+ {"taskId": "1", "owner": "my-name"}
593
+ {"taskId": "1", "comment": "Started implementing"}
594
+ \`\`\`
595
+ `,
596
+ parameters: {
597
+ type: "object",
598
+ properties: {
599
+ taskId: {
600
+ type: "string",
601
+ description: "The ID of the task to update",
602
+ },
603
+ status: {
604
+ type: "string",
605
+ enum: ["pending", "in_progress", "completed", "deleted"],
606
+ description: "New status for the task",
607
+ },
608
+ subject: {
609
+ type: "string",
610
+ description: "New subject for the task",
611
+ },
612
+ description: {
613
+ type: "string",
614
+ description: "New description for the task",
615
+ },
616
+ activeForm: {
617
+ type: "string",
618
+ description: 'Present continuous form shown in spinner when in_progress (e.g., "Running tests")',
619
+ },
620
+ owner: {
621
+ type: "string",
622
+ description: "New owner for the task",
623
+ },
624
+ metadata: {
625
+ type: "string",
626
+ description: "JSON string of metadata keys to merge into the task. Set a key to null to delete it.",
627
+ },
628
+ addBlocks: {
629
+ type: "string",
630
+ description: "JSON array of task IDs that this task blocks (cannot start until this completes)",
631
+ },
632
+ addBlockedBy: {
633
+ type: "string",
634
+ description: "JSON array of task IDs that block this task (must complete before this can start)",
635
+ },
636
+ comment: {
637
+ type: "string",
638
+ description: "Add a progress note or comment to the task",
639
+ },
640
+ },
641
+ required: ["taskId"],
642
+ },
643
+ execute: async (params) => {
644
+ const { taskId, status, subject, description, activeForm, owner, metadata: rawMetadata, addBlocks: rawAddBlocks, addBlockedBy: rawAddBlockedBy, comment, } = params;
645
+ // Check if task exists first
646
+ const existingTask = manager.get(taskId);
647
+ if (!existingTask) {
648
+ return { success: false, error: `Task #${taskId} not found` };
649
+ }
650
+ // Check ownership permissions
651
+ if (!canUpdateTask(existingTask, options)) {
652
+ const agentId = options.agentId || process.env.CLAUDE_CODE_AGENT_ID || 'unknown';
653
+ return {
654
+ success: false,
655
+ error: `Task #${taskId} is owned by "${existingTask.owner}" and cannot be updated by agent "${agentId}"`,
656
+ };
657
+ }
658
+ // Parse metadata if provided
659
+ let metadata;
660
+ if (rawMetadata) {
661
+ try {
662
+ metadata = JSON.parse(rawMetadata);
663
+ }
664
+ catch {
665
+ return { success: false, error: "Invalid metadata JSON" };
666
+ }
667
+ }
668
+ // Parse addBlocks if provided
669
+ let addBlocks;
670
+ if (rawAddBlocks) {
671
+ try {
672
+ addBlocks = JSON.parse(rawAddBlocks);
673
+ if (!Array.isArray(addBlocks)) {
674
+ return { success: false, error: "addBlocks must be a JSON array of task IDs" };
675
+ }
676
+ }
677
+ catch {
678
+ return { success: false, error: "Invalid addBlocks JSON" };
679
+ }
680
+ }
681
+ // Parse addBlockedBy if provided
682
+ let addBlockedBy;
683
+ if (rawAddBlockedBy) {
684
+ try {
685
+ addBlockedBy = JSON.parse(rawAddBlockedBy);
686
+ if (!Array.isArray(addBlockedBy)) {
687
+ return { success: false, error: "addBlockedBy must be a JSON array of task IDs" };
688
+ }
689
+ }
690
+ catch {
691
+ return { success: false, error: "Invalid addBlockedBy JSON" };
692
+ }
693
+ }
694
+ // Build comment object if provided
695
+ const commentObj = comment ? {
696
+ author: options.agentId || process.env.CLAUDE_CODE_AGENT_ID || options.agentName || 'agent',
697
+ content: comment,
698
+ } : undefined;
699
+ const result = manager.update(taskId, {
700
+ status,
701
+ subject,
702
+ description,
703
+ activeForm,
704
+ owner,
705
+ metadata,
706
+ addBlocks,
707
+ addBlockedBy,
708
+ comment: commentObj,
709
+ });
710
+ if (result.deleted) {
711
+ notifyTaskUpdate(manager, 'delete', options);
712
+ const progress = manager.getProgress();
713
+ let message = `Deleted task #${taskId}`;
714
+ if (result.next) {
715
+ message += `. Next task: #${result.next.id} "${result.next.subject}"`;
716
+ }
717
+ return {
718
+ success: true,
719
+ data: {
720
+ message,
721
+ deleted: true,
722
+ next: result.next,
723
+ progress,
724
+ },
725
+ };
726
+ }
727
+ if (!result.task) {
728
+ return { success: false, error: `Task #${taskId} not found` };
729
+ }
730
+ notifyTaskUpdate(manager, 'update', options);
731
+ const progress = manager.getProgress();
732
+ let message = `Updated task #${taskId}`;
733
+ if (status === 'completed') {
734
+ message = `Completed task #${taskId}: "${result.task.subject}"`;
735
+ if (result.next) {
736
+ message += `. Next: #${result.next.id} "${result.next.subject}"`;
737
+ }
738
+ else if (manager.isAllCompleted()) {
739
+ message += `. All tasks completed!`;
740
+ }
741
+ }
742
+ else if (status === 'in_progress') {
743
+ message = `Started task #${taskId}: "${result.task.subject}"`;
744
+ }
745
+ return {
746
+ success: true,
747
+ data: {
748
+ message,
749
+ task: result.task,
750
+ next: result.next,
751
+ progress,
752
+ },
753
+ };
754
+ },
755
+ };
756
+ }
757
+ /**
758
+ * Create the TaskGet tool
759
+ */
760
+ export function createTaskGetTool(manager, _options = {}) {
761
+ return {
762
+ name: "TaskGet",
763
+ description: `Retrieve a task by ID. Returns full details: subject, description, status, dependencies, owner, and metadata.`,
764
+ parameters: {
765
+ type: "object",
766
+ properties: {
767
+ taskId: {
768
+ type: "string",
769
+ description: "The ID of the task to retrieve",
770
+ },
771
+ },
772
+ required: ["taskId"],
773
+ },
774
+ execute: async (params) => {
775
+ const { taskId } = params;
776
+ const task = manager.get(taskId);
777
+ if (!task) {
778
+ return { success: false, error: `Task #${taskId} not found` };
779
+ }
780
+ return {
781
+ success: true,
782
+ data: {
783
+ task,
784
+ },
785
+ };
786
+ },
787
+ };
788
+ }
789
+ /**
790
+ * Create the TaskList tool
791
+ */
792
+ export function createTaskListTool(manager, options = {}) {
793
+ return {
794
+ name: "TaskList",
795
+ description: `List all tasks with their ID, subject, status, and owner. Use after completing a task to find the next one.`,
796
+ parameters: {
797
+ type: "object",
798
+ properties: {},
799
+ required: [],
800
+ },
801
+ execute: async () => {
802
+ const tasks = manager.getAll();
803
+ const current = manager.getCurrentTask();
804
+ const progress = manager.getProgress();
805
+ notifyTaskUpdate(manager, 'list', options);
806
+ return {
807
+ success: true,
808
+ data: {
809
+ message: formatTaskList(tasks, tasks),
810
+ tasks,
811
+ current: current?.id,
812
+ progress,
813
+ },
814
+ };
815
+ },
816
+ };
817
+ }
818
+ /**
819
+ * Create all 4 task management tools
820
+ */
821
+ export function createTaskTools(manager, options = {}) {
822
+ return [
823
+ createTaskCreateTool(manager, options),
824
+ createTaskUpdateTool(manager, options),
825
+ createTaskGetTool(manager, options),
826
+ createTaskListTool(manager, options),
827
+ ];
828
+ }
829
+ /**
830
+ * @deprecated Use createTaskTools instead
831
+ */
832
+ export function createTodoTool(manager, options = {}) {
833
+ // Return a legacy-compatible single tool that wraps the new system
834
+ return {
835
+ name: "__todo__",
836
+ description: "Deprecated: Use TaskCreate, TaskUpdate, TaskGet, TaskList tools instead.",
837
+ parameters: {
838
+ type: "object",
839
+ properties: {
840
+ action: {
841
+ type: "string",
842
+ enum: ["set", "complete", "list"],
843
+ description: "The action to perform",
844
+ },
845
+ items: {
846
+ type: "string",
847
+ description: 'Array of task descriptions (for "set" action)',
848
+ },
849
+ },
850
+ required: ["action"],
851
+ },
852
+ execute: async (params) => {
853
+ const { action, items: rawItems } = params;
854
+ let items;
855
+ if (rawItems) {
856
+ let parsed = rawItems;
857
+ if (typeof rawItems === "string") {
858
+ try {
859
+ parsed = JSON.parse(rawItems);
860
+ }
861
+ catch {
862
+ parsed = [rawItems];
863
+ }
864
+ }
865
+ if (Array.isArray(parsed)) {
866
+ items = parsed.map((item) => {
867
+ if (typeof item === "string") {
868
+ return { content: item };
869
+ }
870
+ if (typeof item === "object" && item !== null && "content" in item) {
871
+ return {
872
+ content: item.content,
873
+ activeForm: item.activeForm,
874
+ };
875
+ }
876
+ return { content: String(item) };
877
+ });
878
+ }
879
+ }
880
+ switch (action) {
881
+ case "set": {
882
+ if (!items || items.length === 0) {
883
+ return { success: false, error: 'Items array is required for "set" action' };
884
+ }
885
+ manager.clear();
886
+ for (const item of items) {
887
+ manager.create(item.content, undefined, item.activeForm);
888
+ }
889
+ const todos = manager.getAll();
890
+ const current = manager.getCurrentTask();
891
+ notifyTaskUpdate(manager, 'create', options);
892
+ return {
893
+ success: true,
894
+ data: {
895
+ message: `Created ${todos.length} tasks. Starting: "${current?.activeForm || current?.subject}"`,
896
+ todos: todos.map(t => ({ ...t, content: t.subject })),
897
+ current: current?.activeForm || current?.subject,
898
+ },
899
+ };
900
+ }
901
+ case "complete": {
902
+ const current = manager.getCurrentTask();
903
+ if (!current) {
904
+ return { success: true, data: { message: "No tasks to complete.", todos: manager.getAll() } };
905
+ }
906
+ const result = manager.update(current.id, { status: 'completed' });
907
+ const progress = manager.getProgress();
908
+ let message = `Completed: "${current.subject}". `;
909
+ if (result.next) {
910
+ message += `Next: "${result.next.activeForm || result.next.subject}". `;
911
+ }
912
+ else if (manager.isAllCompleted()) {
913
+ message += "All tasks completed!";
914
+ }
915
+ message += `Progress: ${progress.completed}/${progress.total}`;
916
+ notifyTaskUpdate(manager, 'update', options);
917
+ return {
918
+ success: true,
919
+ data: {
920
+ message,
921
+ todos: manager.getAll().map(t => ({ ...t, content: t.subject })),
922
+ current: result.next?.activeForm || result.next?.subject,
923
+ progress,
924
+ },
925
+ };
926
+ }
927
+ case "list": {
928
+ const tasks = manager.getAll();
929
+ const current = manager.getCurrentTask();
930
+ const progress = manager.getProgress();
931
+ return {
932
+ success: true,
933
+ data: {
934
+ message: formatTaskList(tasks, tasks),
935
+ todos: tasks.map(t => ({ ...t, content: t.subject })),
936
+ current: current?.activeForm || current?.subject,
937
+ progress,
938
+ },
939
+ };
940
+ }
941
+ default:
942
+ return { success: false, error: `Unknown action: ${action}` };
943
+ }
944
+ },
945
+ };
946
+ }
947
+ //# sourceMappingURL=tasks.js.map