@dalmasonto/taskflow-mcp 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,21 +3,29 @@ import { startSSEServer } from './sse.js';
3
3
  import { getDb } from './db.js';
4
4
  import { broadcast } from './sse.js';
5
5
  const httpOnly = process.argv.includes('--http-only');
6
- // Close any orphaned sessions left from a previous crash
7
- // If the server died while sessions were active, they'll have no `end` timestamp
6
+ // Close orphaned sessions left from a previous crash.
7
+ // Uses a grace period so that sessions from the current conversation
8
+ // (stdio transport restarts the server on each tool call) are preserved.
9
+ const ORPHAN_GRACE_MS = 5 * 60 * 1000; // 5 minutes
8
10
  function cleanupOrphanedSessions() {
9
11
  const db = getDb();
10
- const now = new Date().toISOString();
11
- const orphaned = db.prepare('SELECT * FROM sessions WHERE end IS NULL').all();
12
+ const nowMs = Date.now();
13
+ const nowIso = new Date(nowMs).toISOString();
14
+ const cutoff = new Date(nowMs - ORPHAN_GRACE_MS).toISOString();
15
+ // Only close sessions that started more than ORPHAN_GRACE_MS ago
16
+ const orphaned = db.prepare('SELECT * FROM sessions WHERE end IS NULL AND start < ?').all(cutoff);
12
17
  if (orphaned.length === 0)
13
18
  return;
14
- db.prepare('UPDATE sessions SET end = ? WHERE end IS NULL').run(now);
19
+ // Close each orphaned session with end = now (preserves the real elapsed time)
20
+ for (const session of orphaned) {
21
+ db.prepare('UPDATE sessions SET end = ? WHERE id = ?').run(nowIso, session.id);
22
+ }
15
23
  // Set orphaned in_progress tasks back to paused
16
24
  const taskIds = [...new Set(orphaned.map(s => s.task_id))];
17
25
  for (const taskId of taskIds) {
18
26
  const task = db.prepare('SELECT status FROM tasks WHERE id = ?').get(taskId);
19
27
  if (task?.status === 'in_progress') {
20
- db.prepare("UPDATE tasks SET status = 'paused', updated_at = ? WHERE id = ?").run(now, taskId);
28
+ db.prepare("UPDATE tasks SET status = 'paused', updated_at = ? WHERE id = ?").run(nowIso, taskId);
21
29
  broadcast('task_updated', { entity: 'task', action: 'task_status_changed', payload: db.prepare('SELECT * FROM tasks WHERE id = ?').get(taskId) });
22
30
  }
23
31
  }
@@ -303,9 +303,8 @@ export async function updateTaskStatus(params) {
303
303
  statusAction = 'task_status_changed';
304
304
  }
305
305
  const updated = db.prepare('SELECT * FROM tasks WHERE id = ?').get(params.id);
306
- const updatedTask = parseTask(updated);
307
- broadcastChange('task', statusAction, updatedTask);
308
- return successResponse(updatedTask);
306
+ broadcastChange('task', statusAction, parseTask(updated));
307
+ return successResponse(parseTaskCompact(updated));
309
308
  }
310
309
  export async function deleteTask(params) {
311
310
  const db = getDb();
@@ -35,12 +35,23 @@ export async function pauseTimer(params) {
35
35
  return errorResponse('Task not found', 'NOT_FOUND');
36
36
  // Find open session
37
37
  const session = db.prepare('SELECT * FROM sessions WHERE task_id = ? AND end IS NULL').get(params.task_id);
38
- if (!session)
39
- return errorResponse('No active timer session for this task', 'NO_ACTIVE_SESSION');
40
38
  const endTime = now();
41
- const duration = new Date(endTime).getTime() - new Date(session.start).getTime();
42
- // Close session
43
- db.prepare('UPDATE sessions SET end = ? WHERE id = ?').run(endTime, session.id);
39
+ let duration;
40
+ let closedSession;
41
+ if (session) {
42
+ // Normal path — close the active session
43
+ duration = new Date(endTime).getTime() - new Date(session.start).getTime();
44
+ db.prepare('UPDATE sessions SET end = ? WHERE id = ?').run(endTime, session.id);
45
+ closedSession = { ...session, end: endTime };
46
+ }
47
+ else {
48
+ // Fallback — session already closed (orphan cleanup, auto-close)
49
+ const lastSession = db.prepare('SELECT * FROM sessions WHERE task_id = ? ORDER BY start DESC LIMIT 1').get(params.task_id);
50
+ if (!lastSession)
51
+ return errorResponse('No timer sessions found for this task', 'NO_ACTIVE_SESSION');
52
+ duration = new Date(lastSession.end).getTime() - new Date(lastSession.start).getTime();
53
+ closedSession = lastSession;
54
+ }
44
55
  // Update task to paused
45
56
  db.prepare('UPDATE tasks SET status = ?, updated_at = ? WHERE id = ?').run('paused', endTime, params.task_id);
46
57
  logActivity('timer_paused', task.title, {
@@ -48,7 +59,7 @@ export async function pauseTimer(params) {
48
59
  entityType: 'task',
49
60
  entityId: params.task_id,
50
61
  });
51
- const pausedSession = { ...session, end: endTime, duration };
62
+ const pausedSession = { ...closedSession, duration };
52
63
  broadcastChange('timer', 'timer_paused', { task_id: params.task_id, session: pausedSession, task_status: 'paused' });
53
64
  return successResponse(pausedSession);
54
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dalmasonto/taskflow-mcp",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "MCP server for TaskFlow — manage projects, tasks, timers, analytics via AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",