task-agent 1.0.0__tar.gz → 1.0.2__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.
- {task_agent-1.0.0 → task_agent-1.0.2}/PKG-INFO +1 -1
- {task_agent-1.0.0 → task_agent-1.0.2}/pyproject.toml +2 -2
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/cli.py +84 -36
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/test_cli.py +49 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/uv.lock +1 -1
- {task_agent-1.0.0 → task_agent-1.0.2}/.env +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/.gemini/settings.json +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/.github/workflows/ci.yml +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/.github/workflows/publish.yml +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/.gitignore +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/.mise.toml +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/.pre-commit-config.yaml +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/.python-version +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/.ta/agents/agy-cli/meta.toml +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/.ta/agents/gh/meta.toml +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/.ta/agents/minimal/meta.toml +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/.ta/agents/uat-aws/meta.toml +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/.ta/worker +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/CLAUDE.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/GEMINI.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/Makefile +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/README.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/agent.env +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/docs/README.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/docs/architecture/README.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/docs/chats/gemini-conversation-1773283034591.json +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/docs/chats/project-init-to-v0.1.46.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/docs/cli/README.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/docs/development/README.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/docs/task-agents.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/docs/workflow/README.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/scripts/clean_duplicates.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/scripts/migrate_tasks.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/scripts/recover_dates.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/sidecars/adk-worker/.env.example +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/sidecars/adk-worker/README.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/sidecars/adk-worker/instructions/manager.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/sidecars/adk-worker/instructions/validator.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/sidecars/adk-worker/instructions/worker.md +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/sidecars/adk-worker/profiles/default.json +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/sidecars/adk-worker/pyproject.toml +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/sidecars/adk-worker/uv.lock +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/sidecars/adk-worker/worker.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/__init__.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/__main__.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/agent.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/audit.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/config.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/discovery.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/manager.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/mcp.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/models/__init__.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/models/issue.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/models/metric.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/plugins/__init__.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/plugins/github.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/src/taskagent/templates.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/test-dir/opencode.json +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/conftest.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/e2e/test_agent_sandboxing.sh +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/test_agent.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/test_agy_cli_worker.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/test_cli_windows.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/test_discovery.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/test_manager.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/test_manager_git.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/test_mcp.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/test_metrics.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/test_migration.py +0 -0
- {task_agent-1.0.0 → task_agent-1.0.2}/tests/test_templates.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "task-agent"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.2"
|
|
4
4
|
description = "A prioritized, file-based task queue for autonomous agentic workers"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -39,7 +39,7 @@ dev = [
|
|
|
39
39
|
]
|
|
40
40
|
|
|
41
41
|
[tool.bumpversion]
|
|
42
|
-
current_version = "1.0.
|
|
42
|
+
current_version = "1.0.2"
|
|
43
43
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
44
44
|
serialize = ["{major}.{minor}.{patch}"]
|
|
45
45
|
search = "version = \"{current_version}\""
|
|
@@ -3505,49 +3505,94 @@ def cmd_triage(
|
|
|
3505
3505
|
target_issue = indexed_issues[sibling_idx][0]
|
|
3506
3506
|
|
|
3507
3507
|
if target_issue:
|
|
3508
|
+
live.stop()
|
|
3508
3509
|
try:
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3510
|
+
choice = questionary.select(
|
|
3511
|
+
f"Link '{current_issue.slug}' to '{target_issue.slug}' as:",
|
|
3512
|
+
choices=[
|
|
3513
|
+
f"Subtask of (Hierarchy: parent '{target_issue.slug}' is blocked until child '{current_issue.slug}' is done)",
|
|
3514
|
+
f"Blocked by (Ordering: child '{current_issue.slug}' cannot start/finish until parent '{target_issue.slug}' is done)",
|
|
3515
|
+
"Cancel",
|
|
3516
|
+
],
|
|
3517
|
+
).ask()
|
|
3518
|
+
except (EOFError, Exception):
|
|
3519
|
+
choice = "Blocked by"
|
|
3520
|
+
|
|
3521
|
+
if choice and not choice.startswith("Cancel"):
|
|
3522
|
+
try:
|
|
3523
|
+
if choice.startswith("Subtask of"):
|
|
3524
|
+
# If it was blocked_by, remove to avoid redundancy
|
|
3525
|
+
if target_issue.slug in current_issue.blocked_by:
|
|
3526
|
+
manager.remove_dependency(
|
|
3527
|
+
current_issue.slug,
|
|
3528
|
+
target_issue.slug,
|
|
3529
|
+
)
|
|
3530
|
+
manager.update_subtask_of(
|
|
3531
|
+
current_issue.slug, target_issue.slug
|
|
3532
|
+
)
|
|
3533
|
+
else:
|
|
3534
|
+
# Blocked by
|
|
3535
|
+
# If it was subtask_of, remove to avoid conflicts
|
|
3536
|
+
if current_issue.subtask_of == target_issue.slug:
|
|
3537
|
+
manager.update_subtask_of(
|
|
3538
|
+
current_issue.slug, None
|
|
3539
|
+
)
|
|
3540
|
+
|
|
3541
|
+
# Check transitive dependencies recursively to avoid redundancy
|
|
3542
|
+
all_issues = manager.load_mission()
|
|
3543
|
+
|
|
3544
|
+
def is_ancestor(ancestor_slug, desc_slug):
|
|
3545
|
+
slug_to_issue = {i.slug: i for i in all_issues}
|
|
3546
|
+
if desc_slug not in slug_to_issue:
|
|
3547
|
+
return False
|
|
3548
|
+
todo = list(
|
|
3549
|
+
slug_to_issue[desc_slug].dependencies
|
|
3550
|
+
)
|
|
3551
|
+
visited = set(todo)
|
|
3552
|
+
while todo:
|
|
3553
|
+
curr = todo.pop()
|
|
3554
|
+
if curr == ancestor_slug:
|
|
3555
|
+
return True
|
|
3556
|
+
if curr in slug_to_issue:
|
|
3557
|
+
for dep in slug_to_issue[
|
|
3558
|
+
curr
|
|
3559
|
+
].dependencies:
|
|
3560
|
+
if dep not in visited:
|
|
3561
|
+
visited.add(dep)
|
|
3562
|
+
todo.append(dep)
|
|
3563
|
+
return False
|
|
3564
|
+
|
|
3565
|
+
manager.add_dependency(
|
|
3566
|
+
current_issue.slug, target_issue.slug
|
|
3567
|
+
)
|
|
3568
|
+
|
|
3569
|
+
# Remove redundant direct dependencies that are transitively covered
|
|
3570
|
+
for dep in list(current_issue.dependencies):
|
|
3571
|
+
if dep != target_issue.slug and is_ancestor(
|
|
3572
|
+
dep, target_issue.slug
|
|
3573
|
+
):
|
|
3574
|
+
manager.remove_dependency(
|
|
3575
|
+
current_issue.slug, dep
|
|
3576
|
+
)
|
|
3540
3577
|
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3578
|
+
issues = get_display_issues(
|
|
3579
|
+
search_query, show_completed
|
|
3580
|
+
)
|
|
3581
|
+
indexed_issues = build_hierarchy(issues)
|
|
3582
|
+
except Exception as e:
|
|
3583
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
3584
|
+
questionary.press_any_key_to_continue().ask()
|
|
3585
|
+
live.start()
|
|
3545
3586
|
elif key == "h" and not show_completed: # remove dependency on above
|
|
3546
3587
|
if cursor > 0:
|
|
3547
3588
|
current_issue = indexed_issues[cursor][0]
|
|
3548
3589
|
above_issue = indexed_issues[cursor - 1][0]
|
|
3549
3590
|
try:
|
|
3550
|
-
|
|
3591
|
+
updated = False
|
|
3592
|
+
if current_issue.subtask_of == above_issue.slug:
|
|
3593
|
+
manager.update_subtask_of(current_issue.slug, None)
|
|
3594
|
+
updated = True
|
|
3595
|
+
elif above_issue.slug in current_issue.blocked_by:
|
|
3551
3596
|
parents = list(above_issue.dependencies)
|
|
3552
3597
|
manager.remove_dependency(
|
|
3553
3598
|
current_issue.slug, above_issue.slug
|
|
@@ -3555,11 +3600,14 @@ def cmd_triage(
|
|
|
3555
3600
|
# Move up: inherit the parent's dependencies
|
|
3556
3601
|
for parent in parents:
|
|
3557
3602
|
manager.add_dependency(current_issue.slug, parent)
|
|
3603
|
+
updated = True
|
|
3558
3604
|
|
|
3605
|
+
if updated:
|
|
3559
3606
|
issues = get_display_issues(search_query, show_completed)
|
|
3560
3607
|
indexed_issues = build_hierarchy(issues)
|
|
3561
3608
|
except Exception as e:
|
|
3562
3609
|
console.print(f"[red]Error: {e}[/red]")
|
|
3610
|
+
questionary.press_any_key_to_continue().ask()
|
|
3563
3611
|
|
|
3564
3612
|
|
|
3565
3613
|
def display_overview(console: Console, manager: TaskAgent):
|
|
@@ -575,3 +575,52 @@ def test_cmd_triage_depends_on_above(manager, monkeypatch):
|
|
|
575
575
|
t3_updated = manager.find_issue_file("task-3")
|
|
576
576
|
assert "task-1" in manager.extract_deps(t3_updated)
|
|
577
577
|
assert "task-2" not in manager.extract_deps(t3_updated)
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
def test_cmd_triage_subtask_of(manager, monkeypatch):
|
|
581
|
+
from taskagent.cli import cmd_triage
|
|
582
|
+
from unittest.mock import MagicMock, patch
|
|
583
|
+
|
|
584
|
+
manager.create_issue("Task 1")
|
|
585
|
+
manager.create_issue("Task 2")
|
|
586
|
+
|
|
587
|
+
mock_console = MagicMock()
|
|
588
|
+
|
|
589
|
+
# We mock questionary.select().ask() to return "Subtask of..."
|
|
590
|
+
mock_select = MagicMock()
|
|
591
|
+
mock_select.ask.return_value = "Subtask of (Hierarchy...)"
|
|
592
|
+
|
|
593
|
+
keys = ["j", "l", "q"]
|
|
594
|
+
|
|
595
|
+
def mock_get_key():
|
|
596
|
+
if keys:
|
|
597
|
+
return keys.pop(0)
|
|
598
|
+
return "q"
|
|
599
|
+
|
|
600
|
+
with (
|
|
601
|
+
patch("taskagent.cli.get_key", mock_get_key),
|
|
602
|
+
patch("taskagent.cli.Live"),
|
|
603
|
+
patch("questionary.select", return_value=mock_select),
|
|
604
|
+
):
|
|
605
|
+
cmd_triage(mock_console, manager)
|
|
606
|
+
|
|
607
|
+
issue_t2 = manager.load_mission()[1]
|
|
608
|
+
assert issue_t2.subtask_of == "task-1"
|
|
609
|
+
assert "task-1" not in issue_t2.blocked_by
|
|
610
|
+
|
|
611
|
+
# Test 2: Unlink subtask relation using 'h' key
|
|
612
|
+
keys = ["j", "h", "q"]
|
|
613
|
+
|
|
614
|
+
def mock_get_key_unlink():
|
|
615
|
+
if keys:
|
|
616
|
+
return keys.pop(0)
|
|
617
|
+
return "q"
|
|
618
|
+
|
|
619
|
+
with (
|
|
620
|
+
patch("taskagent.cli.get_key", mock_get_key_unlink),
|
|
621
|
+
patch("taskagent.cli.Live"),
|
|
622
|
+
):
|
|
623
|
+
cmd_triage(mock_console, manager)
|
|
624
|
+
|
|
625
|
+
issue_t2_unlinked = manager.load_mission()[1]
|
|
626
|
+
assert issue_t2_unlinked.subtask_of is None
|
|
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
|
|
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
|
|
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
|
|
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
|