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.
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/PKG-INFO +1 -1
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/pyproject.toml +1 -1
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/src/task_store.py +29 -136
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/PKG-INFO +1 -1
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/.mcp.json +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/.python-version +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/CLAUDE.md +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/LICENSE +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/MANIFEST.in +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/README.md +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/claude-desktop-config.example.json +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/main.py +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/requirements.txt +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/run-arm64.sh +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/setup.cfg +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/src/__init__.py +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/src/embeddings.py +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/src/models.py +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/src/security.py +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/tests/test_task_store.py +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/SOURCES.txt +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/dependency_links.txt +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/entry_points.txt +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/requires.txt +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/top_level.txt +0 -0
|
@@ -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
|
|
204
|
+
def _propagate_completed_to_parents(self, conn: sqlite3.Connection, task_id: int) -> None:
|
|
205
205
|
"""
|
|
206
|
-
Recursively propagate status to parent tasks
|
|
206
|
+
Recursively propagate 'completed' status to parent tasks when ALL children are finished.
|
|
207
207
|
|
|
208
208
|
Rules:
|
|
209
|
-
-
|
|
210
|
-
-
|
|
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
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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
|
|
415
|
-
|
|
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
|
|
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 = ?
|
|
431
|
-
ORDER BY
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vector_task_mcp-1.2.2 → vector_task_mcp-1.2.5}/vector_task_mcp.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|