vector-task-mcp 1.2.2__tar.gz → 1.2.4__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.4}/PKG-INFO +1 -1
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/pyproject.toml +1 -1
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/src/task_store.py +8 -154
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/vector_task_mcp.egg-info/PKG-INFO +1 -1
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/.mcp.json +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/.python-version +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/CLAUDE.md +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/LICENSE +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/MANIFEST.in +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/README.md +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/claude-desktop-config.example.json +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/main.py +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/requirements.txt +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/run-arm64.sh +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/setup.cfg +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/src/__init__.py +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/src/embeddings.py +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/src/models.py +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/src/security.py +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/tests/test_task_store.py +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/vector_task_mcp.egg-info/SOURCES.txt +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/vector_task_mcp.egg-info/dependency_links.txt +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/vector_task_mcp.egg-info/entry_points.txt +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/vector_task_mcp.egg-info/requires.txt +0 -0
- {vector_task_mcp-1.2.2 → vector_task_mcp-1.2.4}/vector_task_mcp.egg-info/top_level.txt +0 -0
|
@@ -201,136 +201,6 @@ 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:
|
|
205
|
-
"""
|
|
206
|
-
Recursively propagate status to parent tasks based on aggregate child state.
|
|
207
|
-
|
|
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
|
|
212
|
-
|
|
213
|
-
Args:
|
|
214
|
-
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
|
|
283
|
-
"""
|
|
284
|
-
# Get parent_id of current task
|
|
285
|
-
cursor = conn.execute('SELECT parent_id FROM tasks WHERE id = ?', (task_id,))
|
|
286
|
-
row = cursor.fetchone()
|
|
287
|
-
if not row or not row[0]: # No parent
|
|
288
|
-
return
|
|
289
|
-
|
|
290
|
-
parent_id = row[0]
|
|
291
|
-
now = datetime.utcnow().isoformat()
|
|
292
|
-
|
|
293
|
-
finish_statuses = TaskStatus.finish_statuses()
|
|
294
|
-
|
|
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]
|
|
325
|
-
|
|
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)
|
|
333
|
-
|
|
334
204
|
def _start_time_session(self, conn: sqlite3.Connection, task_id: int, start_status: str) -> None:
|
|
335
205
|
"""
|
|
336
206
|
Create new time session record in task_time_log table.
|
|
@@ -411,8 +281,9 @@ class TaskStore:
|
|
|
411
281
|
|
|
412
282
|
def _get_status_history(self, conn: sqlite3.Connection, task_id: int, limit: int = 5) -> List[Dict[str, Any]]:
|
|
413
283
|
"""
|
|
414
|
-
Get
|
|
415
|
-
|
|
284
|
+
Get status transition history from task_time_log.
|
|
285
|
+
Includes both completed and incomplete (open) sessions.
|
|
286
|
+
Open sessions appear first with to/at/spent as null.
|
|
416
287
|
|
|
417
288
|
Args:
|
|
418
289
|
conn: Database connection
|
|
@@ -421,14 +292,16 @@ class TaskStore:
|
|
|
421
292
|
|
|
422
293
|
Returns:
|
|
423
294
|
List of dicts with keys: from, to, at, spent
|
|
424
|
-
Ordered by
|
|
295
|
+
Ordered by: open sessions first, then finish_at DESC
|
|
425
296
|
"""
|
|
426
297
|
cursor = conn.execute(
|
|
427
298
|
"""
|
|
428
299
|
SELECT start_status, finish_status, finish_at, time_spent
|
|
429
300
|
FROM task_time_log
|
|
430
|
-
WHERE task_id = ?
|
|
431
|
-
ORDER BY
|
|
301
|
+
WHERE task_id = ?
|
|
302
|
+
ORDER BY
|
|
303
|
+
CASE WHEN finish_at IS NULL THEN 0 ELSE 1 END,
|
|
304
|
+
finish_at DESC
|
|
432
305
|
LIMIT ?
|
|
433
306
|
""",
|
|
434
307
|
(task_id, limit)
|
|
@@ -776,20 +649,6 @@ class TaskStore:
|
|
|
776
649
|
update_fields.append('"time_spent" = ?')
|
|
777
650
|
update_values.append(converted_time_spent)
|
|
778
651
|
|
|
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
652
|
# Auto-set start_at when changing to in_progress (only if not explicitly provided)
|
|
794
653
|
if new_status == 'in_progress' and current_status != 'in_progress':
|
|
795
654
|
if 'start_at' not in validated_kwargs:
|
|
@@ -965,11 +824,6 @@ class TaskStore:
|
|
|
965
824
|
if time_delta > 0:
|
|
966
825
|
self._propagate_time_to_parents(conn, task_id, time_delta)
|
|
967
826
|
|
|
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
827
|
# Time session tracking - start/finish sessions on in_progress transitions
|
|
974
828
|
if status_changed and old_status is not None:
|
|
975
829
|
# Start session when entering in_progress
|
|
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.4}/vector_task_mcp.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|