vector-task-mcp 1.2.2__tar.gz → 1.2.5__tar.gz

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 (25) hide show
  1. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/PKG-INFO +1 -1
  2. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/pyproject.toml +1 -1
  3. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/src/task_store.py +29 -136
  4. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/PKG-INFO +1 -1
  5. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/.mcp.json +0 -0
  6. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/.python-version +0 -0
  7. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/CLAUDE.md +0 -0
  8. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/LICENSE +0 -0
  9. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/MANIFEST.in +0 -0
  10. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/README.md +0 -0
  11. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/claude-desktop-config.example.json +0 -0
  12. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/main.py +0 -0
  13. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/requirements.txt +0 -0
  14. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/run-arm64.sh +0 -0
  15. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/setup.cfg +0 -0
  16. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/src/__init__.py +0 -0
  17. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/src/embeddings.py +0 -0
  18. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/src/models.py +0 -0
  19. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/src/security.py +0 -0
  20. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/tests/test_task_store.py +0 -0
  21. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/SOURCES.txt +0 -0
  22. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/dependency_links.txt +0 -0
  23. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/entry_points.txt +0 -0
  24. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/requires.txt +0 -0
  25. {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vector-task-mcp
3
- Version: 1.2.2
3
+ Version: 1.2.5
4
4
  Summary: A secure, vector-based task management server for Claude Desktop using sqlite-vec and sentence-transformers
5
5
  Author-email: Xsaven <xsaven@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vector-task-mcp"
3
- version = "1.2.2"
3
+ version = "1.2.5"
4
4
  description = "A secure, vector-based task management server for Claude Desktop using sqlite-vec and sentence-transformers"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -201,85 +201,17 @@ class TaskStore:
201
201
  # Recursively propagate to grandparent
202
202
  self._propagate_time_to_parents(conn, parent_id, time_delta)
203
203
 
204
- def _propagate_status_to_parents(self, conn: sqlite3.Connection, task_id: int, new_status: str) -> None:
204
+ def _propagate_completed_to_parents(self, conn: sqlite3.Connection, task_id: int) -> None:
205
205
  """
206
- Recursively propagate status to parent tasks based on aggregate child state.
206
+ Recursively propagate 'completed' status to parent tasks when ALL children are finished.
207
207
 
208
208
  Rules:
209
- - completed: Parent completed ONLY when ALL children completed
210
- - in_progress: Parent in_progress if ANY child in_progress
211
- - pending/stopped: Parent pending if no children in_progress
209
+ - Parent gets 'completed' ONLY when ALL children are in finish statuses (completed/tested/validated)
210
+ - Recursively propagates up the parent chain with same check at each level
212
211
 
213
212
  Args:
214
213
  conn: Active database connection (must be within transaction)
215
- task_id: Current task ID
216
- new_status: Status that triggered this propagation
217
- """
218
- # Get parent_id of current task
219
- cursor = conn.execute('SELECT parent_id FROM tasks WHERE id = ?', (task_id,))
220
- row = cursor.fetchone()
221
- if not row or not row[0]: # No parent
222
- return
223
-
224
- parent_id = row[0]
225
-
226
- finish_statuses = TaskStatus.finish_statuses()
227
-
228
- if new_status in finish_statuses:
229
- # Check if ALL siblings are finished (completed/tested/validated)
230
- placeholders = ','.join('?' * len(finish_statuses))
231
- cursor = conn.execute(f'''
232
- SELECT COUNT(*) FROM tasks
233
- WHERE parent_id = ? AND status NOT IN ({placeholders})
234
- ''', (parent_id, *finish_statuses))
235
- non_finished = cursor.fetchone()[0]
236
-
237
- if non_finished == 0:
238
- # All children finished → parent always gets "completed" (not tested/validated)
239
- parent_status = 'completed'
240
- conn.execute('UPDATE tasks SET status = ? WHERE id = ?', (parent_status, parent_id))
241
- self._propagate_status_to_parents(conn, parent_id, parent_status)
242
- else:
243
- # Not all finished → parent stays in_progress (active work in subtree)
244
- conn.execute('UPDATE tasks SET status = ? WHERE id = ?', ('in_progress', parent_id))
245
- # CRITICAL: Continue recursion to grandparent even when siblings not finished
246
- # This ensures upper hierarchy is notified of activity in deep subtrees
247
- self._propagate_status_to_parents(conn, parent_id, 'in_progress')
248
-
249
- elif new_status == 'in_progress':
250
- # Any child working → parent in_progress
251
- conn.execute('UPDATE tasks SET status = ? WHERE id = ?', ('in_progress', parent_id))
252
- self._propagate_status_to_parents(conn, parent_id, 'in_progress')
253
-
254
- else: # pending or stopped
255
- # Check if any sibling is in_progress
256
- cursor = conn.execute('''
257
- SELECT COUNT(*) FROM tasks
258
- WHERE parent_id = ? AND status = 'in_progress'
259
- ''', (parent_id,))
260
- in_progress_count = cursor.fetchone()[0]
261
-
262
- if in_progress_count > 0:
263
- # Someone still working → parent stays in_progress
264
- conn.execute('UPDATE tasks SET status = ? WHERE id = ?', ('in_progress', parent_id))
265
- else:
266
- # No one working → parent pending
267
- conn.execute('UPDATE tasks SET status = ? WHERE id = ?', ('pending', parent_id))
268
- # Don't propagate these states up
269
-
270
- def _update_parent_timestamps(self, conn: sqlite3.Connection, task_id: int, new_status: str) -> None:
271
- """
272
- Update parent timestamps based on child status changes.
273
-
274
- - When child starts (in_progress): set parent start_at if first child starting and no completed siblings
275
- - When child completes: set parent finish_at if all siblings are now completed
276
-
277
- Recursively propagates up the parent chain.
278
-
279
- Args:
280
- conn: Active database connection (must be within transaction)
281
- task_id: Current task ID
282
- new_status: New status that triggered this update
214
+ task_id: Current task ID whose status just changed to a finish status
283
215
  """
284
216
  # Get parent_id of current task
285
217
  cursor = conn.execute('SELECT parent_id FROM tasks WHERE id = ?', (task_id,))
@@ -288,48 +220,21 @@ class TaskStore:
288
220
  return
289
221
 
290
222
  parent_id = row[0]
291
- now = datetime.utcnow().isoformat()
292
-
293
223
  finish_statuses = TaskStatus.finish_statuses()
294
224
 
295
- if new_status == 'in_progress':
296
- # Set parent start_at ONLY if:
297
- # 1. Parent has no start_at yet
298
- # 2. No siblings are finished (this is the first activity in hierarchy)
299
- placeholders = ','.join('?' * len(finish_statuses))
300
- cursor = conn.execute(f'''
301
- SELECT
302
- (SELECT start_at FROM tasks WHERE id = ?) as parent_start_at,
303
- (SELECT COUNT(*) FROM tasks WHERE parent_id = ? AND status IN ({placeholders})) as finished_siblings
304
- ''', (parent_id, parent_id, *finish_statuses))
305
- result = cursor.fetchone()
306
- parent_start_at = result[0]
307
- finished_siblings = result[1]
308
-
309
- if parent_start_at is None and finished_siblings == 0:
310
- conn.execute(
311
- 'UPDATE tasks SET start_at = ? WHERE id = ?',
312
- (now, parent_id)
313
- )
314
- # Recursively propagate start_at up
315
- self._update_parent_timestamps(conn, parent_id, new_status)
316
-
317
- elif new_status in finish_statuses:
318
- # Set parent finish_at ONLY if ALL siblings are finished (completed/tested/validated)
319
- placeholders = ','.join('?' * len(finish_statuses))
320
- cursor = conn.execute(f'''
321
- SELECT COUNT(*) FROM tasks
322
- WHERE parent_id = ? AND status NOT IN ({placeholders})
323
- ''', (parent_id, *finish_statuses))
324
- non_finished = cursor.fetchone()[0]
225
+ # Check if ALL children of parent are in finish statuses
226
+ placeholders = ','.join('?' * len(finish_statuses))
227
+ cursor = conn.execute(f'''
228
+ SELECT COUNT(*) FROM tasks
229
+ WHERE parent_id = ? AND status NOT IN ({placeholders})
230
+ ''', (parent_id, *finish_statuses))
231
+ non_finished = cursor.fetchone()[0]
325
232
 
326
- if non_finished == 0:
327
- conn.execute(
328
- 'UPDATE tasks SET finish_at = ? WHERE id = ?',
329
- (now, parent_id)
330
- )
331
- # Recursively propagate finish_at up
332
- self._update_parent_timestamps(conn, parent_id, new_status)
233
+ if non_finished == 0:
234
+ # All children finished → set parent to 'completed'
235
+ conn.execute('UPDATE tasks SET status = ? WHERE id = ?', ('completed', parent_id))
236
+ # Recursively check grandparent
237
+ self._propagate_completed_to_parents(conn, parent_id)
333
238
 
334
239
  def _start_time_session(self, conn: sqlite3.Connection, task_id: int, start_status: str) -> None:
335
240
  """
@@ -411,8 +316,9 @@ class TaskStore:
411
316
 
412
317
  def _get_status_history(self, conn: sqlite3.Connection, task_id: int, limit: int = 5) -> List[Dict[str, Any]]:
413
318
  """
414
- Get recent status transition history from task_time_log.
415
- Returns completed sessions only (finish_at IS NOT NULL).
319
+ Get status transition history from task_time_log.
320
+ Includes both completed and incomplete (open) sessions.
321
+ Open sessions appear first with to/at/spent as null.
416
322
 
417
323
  Args:
418
324
  conn: Database connection
@@ -421,14 +327,16 @@ class TaskStore:
421
327
 
422
328
  Returns:
423
329
  List of dicts with keys: from, to, at, spent
424
- Ordered by finish_at DESC (most recent first)
330
+ Ordered by: open sessions first, then finish_at DESC
425
331
  """
426
332
  cursor = conn.execute(
427
333
  """
428
334
  SELECT start_status, finish_status, finish_at, time_spent
429
335
  FROM task_time_log
430
- WHERE task_id = ? AND finish_at IS NOT NULL
431
- ORDER BY finish_at DESC
336
+ WHERE task_id = ?
337
+ ORDER BY
338
+ CASE WHEN finish_at IS NULL THEN 0 ELSE 1 END,
339
+ finish_at DESC
432
340
  LIMIT ?
433
341
  """,
434
342
  (task_id, limit)
@@ -776,20 +684,6 @@ class TaskStore:
776
684
  update_fields.append('"time_spent" = ?')
777
685
  update_values.append(converted_time_spent)
778
686
 
779
- # Block in_progress if task has incomplete children
780
- if new_status == 'in_progress':
781
- placeholders = ','.join('?' * len(finish_statuses))
782
- cursor = conn.execute(f'''
783
- SELECT COUNT(*) FROM tasks
784
- WHERE parent_id = ? AND status NOT IN ({placeholders})
785
- ''', (task_id, *finish_statuses))
786
- incomplete_count = cursor.fetchone()[0]
787
- if incomplete_count > 0:
788
- raise RuntimeError(
789
- f"Cannot set task to in_progress: task has {incomplete_count} "
790
- f"incomplete child task(s). Complete all children first."
791
- )
792
-
793
687
  # Auto-set start_at when changing to in_progress (only if not explicitly provided)
794
688
  if new_status == 'in_progress' and current_status != 'in_progress':
795
689
  if 'start_at' not in validated_kwargs:
@@ -965,11 +859,6 @@ class TaskStore:
965
859
  if time_delta > 0:
966
860
  self._propagate_time_to_parents(conn, task_id, time_delta)
967
861
 
968
- # Propagate status and timestamps to parent tasks
969
- if status_changed and new_status:
970
- self._propagate_status_to_parents(conn, task_id, new_status)
971
- self._update_parent_timestamps(conn, task_id, new_status)
972
-
973
862
  # Time session tracking - start/finish sessions on in_progress transitions
974
863
  if status_changed and old_status is not None:
975
864
  # Start session when entering in_progress
@@ -979,6 +868,10 @@ class TaskStore:
979
868
  elif old_status == 'in_progress' and new_status != 'in_progress':
980
869
  self._finish_time_session(conn, task_id, time_delta, new_status)
981
870
 
871
+ # Propagate 'completed' status to parent when ALL children are finished
872
+ if status_changed and new_status in TaskStatus.finish_statuses():
873
+ self._propagate_completed_to_parents(conn, task_id)
874
+
982
875
  conn.commit()
983
876
 
984
877
  # Fetch updated task
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vector-task-mcp
3
- Version: 1.2.2
3
+ Version: 1.2.5
4
4
  Summary: A secure, vector-based task management server for Claude Desktop using sqlite-vec and sentence-transformers
5
5
  Author-email: Xsaven <xsaven@gmail.com>
6
6
  License: MIT
File without changes
File without changes