task-agent 1.0.2__tar.gz → 1.0.3__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 (70) hide show
  1. {task_agent-1.0.2 → task_agent-1.0.3}/PKG-INFO +1 -1
  2. {task_agent-1.0.2 → task_agent-1.0.3}/pyproject.toml +2 -2
  3. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/cli.py +59 -0
  4. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/mcp.py +69 -2
  5. {task_agent-1.0.2 → task_agent-1.0.3}/tests/test_cli.py +29 -0
  6. {task_agent-1.0.2 → task_agent-1.0.3}/tests/test_mcp.py +17 -0
  7. {task_agent-1.0.2 → task_agent-1.0.3}/uv.lock +1 -1
  8. {task_agent-1.0.2 → task_agent-1.0.3}/.env +0 -0
  9. {task_agent-1.0.2 → task_agent-1.0.3}/.gemini/settings.json +0 -0
  10. {task_agent-1.0.2 → task_agent-1.0.3}/.github/workflows/ci.yml +0 -0
  11. {task_agent-1.0.2 → task_agent-1.0.3}/.github/workflows/publish.yml +0 -0
  12. {task_agent-1.0.2 → task_agent-1.0.3}/.gitignore +0 -0
  13. {task_agent-1.0.2 → task_agent-1.0.3}/.mise.toml +0 -0
  14. {task_agent-1.0.2 → task_agent-1.0.3}/.pre-commit-config.yaml +0 -0
  15. {task_agent-1.0.2 → task_agent-1.0.3}/.python-version +0 -0
  16. {task_agent-1.0.2 → task_agent-1.0.3}/.ta/agents/agy-cli/meta.toml +0 -0
  17. {task_agent-1.0.2 → task_agent-1.0.3}/.ta/agents/gh/meta.toml +0 -0
  18. {task_agent-1.0.2 → task_agent-1.0.3}/.ta/agents/minimal/meta.toml +0 -0
  19. {task_agent-1.0.2 → task_agent-1.0.3}/.ta/agents/uat-aws/meta.toml +0 -0
  20. {task_agent-1.0.2 → task_agent-1.0.3}/.ta/worker +0 -0
  21. {task_agent-1.0.2 → task_agent-1.0.3}/CLAUDE.md +0 -0
  22. {task_agent-1.0.2 → task_agent-1.0.3}/GEMINI.md +0 -0
  23. {task_agent-1.0.2 → task_agent-1.0.3}/Makefile +0 -0
  24. {task_agent-1.0.2 → task_agent-1.0.3}/README.md +0 -0
  25. {task_agent-1.0.2 → task_agent-1.0.3}/agent.env +0 -0
  26. {task_agent-1.0.2 → task_agent-1.0.3}/docs/README.md +0 -0
  27. {task_agent-1.0.2 → task_agent-1.0.3}/docs/architecture/README.md +0 -0
  28. {task_agent-1.0.2 → task_agent-1.0.3}/docs/chats/gemini-conversation-1773283034591.json +0 -0
  29. {task_agent-1.0.2 → task_agent-1.0.3}/docs/chats/project-init-to-v0.1.46.md +0 -0
  30. {task_agent-1.0.2 → task_agent-1.0.3}/docs/cli/README.md +0 -0
  31. {task_agent-1.0.2 → task_agent-1.0.3}/docs/development/README.md +0 -0
  32. {task_agent-1.0.2 → task_agent-1.0.3}/docs/task-agents.md +0 -0
  33. {task_agent-1.0.2 → task_agent-1.0.3}/docs/workflow/README.md +0 -0
  34. {task_agent-1.0.2 → task_agent-1.0.3}/scripts/clean_duplicates.py +0 -0
  35. {task_agent-1.0.2 → task_agent-1.0.3}/scripts/migrate_tasks.py +0 -0
  36. {task_agent-1.0.2 → task_agent-1.0.3}/scripts/recover_dates.py +0 -0
  37. {task_agent-1.0.2 → task_agent-1.0.3}/sidecars/adk-worker/.env.example +0 -0
  38. {task_agent-1.0.2 → task_agent-1.0.3}/sidecars/adk-worker/README.md +0 -0
  39. {task_agent-1.0.2 → task_agent-1.0.3}/sidecars/adk-worker/instructions/manager.md +0 -0
  40. {task_agent-1.0.2 → task_agent-1.0.3}/sidecars/adk-worker/instructions/validator.md +0 -0
  41. {task_agent-1.0.2 → task_agent-1.0.3}/sidecars/adk-worker/instructions/worker.md +0 -0
  42. {task_agent-1.0.2 → task_agent-1.0.3}/sidecars/adk-worker/profiles/default.json +0 -0
  43. {task_agent-1.0.2 → task_agent-1.0.3}/sidecars/adk-worker/pyproject.toml +0 -0
  44. {task_agent-1.0.2 → task_agent-1.0.3}/sidecars/adk-worker/uv.lock +0 -0
  45. {task_agent-1.0.2 → task_agent-1.0.3}/sidecars/adk-worker/worker.py +0 -0
  46. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/__init__.py +0 -0
  47. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/__main__.py +0 -0
  48. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/agent.py +0 -0
  49. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/audit.py +0 -0
  50. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/config.py +0 -0
  51. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/discovery.py +0 -0
  52. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/manager.py +0 -0
  53. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/models/__init__.py +0 -0
  54. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/models/issue.py +0 -0
  55. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/models/metric.py +0 -0
  56. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/plugins/__init__.py +0 -0
  57. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/plugins/github.py +0 -0
  58. {task_agent-1.0.2 → task_agent-1.0.3}/src/taskagent/templates.py +0 -0
  59. {task_agent-1.0.2 → task_agent-1.0.3}/test-dir/opencode.json +0 -0
  60. {task_agent-1.0.2 → task_agent-1.0.3}/tests/conftest.py +0 -0
  61. {task_agent-1.0.2 → task_agent-1.0.3}/tests/e2e/test_agent_sandboxing.sh +0 -0
  62. {task_agent-1.0.2 → task_agent-1.0.3}/tests/test_agent.py +0 -0
  63. {task_agent-1.0.2 → task_agent-1.0.3}/tests/test_agy_cli_worker.py +0 -0
  64. {task_agent-1.0.2 → task_agent-1.0.3}/tests/test_cli_windows.py +0 -0
  65. {task_agent-1.0.2 → task_agent-1.0.3}/tests/test_discovery.py +0 -0
  66. {task_agent-1.0.2 → task_agent-1.0.3}/tests/test_manager.py +0 -0
  67. {task_agent-1.0.2 → task_agent-1.0.3}/tests/test_manager_git.py +0 -0
  68. {task_agent-1.0.2 → task_agent-1.0.3}/tests/test_metrics.py +0 -0
  69. {task_agent-1.0.2 → task_agent-1.0.3}/tests/test_migration.py +0 -0
  70. {task_agent-1.0.2 → task_agent-1.0.3}/tests/test_templates.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: task-agent
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Summary: A prioritized, file-based task queue for autonomous agentic workers
5
5
  Author-email: Mark Stouffer <1802850+InTEGr8or@users.noreply.github.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "task-agent"
3
- version = "1.0.2"
3
+ version = "1.0.3"
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.2"
42
+ current_version = "1.0.3"
43
43
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
44
44
  serialize = ["{major}.{minor}.{patch}"]
45
45
  search = "version = \"{current_version}\""
@@ -1745,8 +1745,62 @@ def cmd_new(
1745
1745
  interactive: bool = False,
1746
1746
  blocked_by: Optional[str] = None,
1747
1747
  subtask_of: Optional[str] = None,
1748
+ bulk: Optional[str] = None,
1748
1749
  ):
1749
1750
  """Create a new issue."""
1751
+ if bulk:
1752
+ try:
1753
+ if bulk == "-":
1754
+ raw_data = sys.stdin.read()
1755
+ else:
1756
+ raw_data = Path(bulk).read_text(encoding="utf-8")
1757
+
1758
+ tasks = json.loads(raw_data)
1759
+ if not isinstance(tasks, list):
1760
+ raise ValueError("Bulk JSON must be a list/array of task objects.")
1761
+
1762
+ for idx, t in enumerate(tasks):
1763
+ t_title = t.get("title")
1764
+ t_criteria = t.get("completion_criteria")
1765
+ if not t_title:
1766
+ console.print(
1767
+ f"[red]Error at task {idx}: 'title' is required.[/red]"
1768
+ )
1769
+ continue
1770
+ if not t_criteria:
1771
+ console.print(
1772
+ f"[red]Error at task '{t_title}' (index {idx}): 'completion_criteria' is required.[/red]"
1773
+ )
1774
+ continue
1775
+
1776
+ t_body = t.get("body", "")
1777
+ t_draft = t.get("draft", draft)
1778
+ t_blocked_by = t.get("blocked_by") or t.get("depends_on")
1779
+ t_subtask_of = t.get("subtask_of")
1780
+ t_as_dir = t.get("as_dir", as_dir)
1781
+
1782
+ issue = manager.create_issue(
1783
+ title=t_title,
1784
+ body=t_body,
1785
+ draft=t_draft,
1786
+ depends_on=t_blocked_by,
1787
+ as_dir=t_as_dir,
1788
+ completion_criteria=t_criteria,
1789
+ blocked_by=t_blocked_by,
1790
+ subtask_of=t_subtask_of,
1791
+ )
1792
+ console.print(
1793
+ f"[bold green]Created new issue: {issue.slug}[/bold green]"
1794
+ )
1795
+ issue_file = manager.find_issue_file(issue.slug)
1796
+ console.print(f"File: {issue_file}")
1797
+ if issue.subtask_of:
1798
+ console.print(f"Subtask of: {issue.subtask_of}")
1799
+ return
1800
+ except Exception as e:
1801
+ console.print(f"[red]Error processing bulk tasks: {e}[/red]")
1802
+ sys.exit(1)
1803
+
1750
1804
  if interactive:
1751
1805
  editor = get_editor()
1752
1806
  slug = manager.slugify(title or "new-task")
@@ -4045,6 +4099,10 @@ Usage:
4045
4099
  "--subtask-of",
4046
4100
  help="Slug of the parent task this task is a subtask of, e.g. 'cli-consolidation'.",
4047
4101
  )
4102
+ new_parser.add_argument(
4103
+ "--bulk",
4104
+ help="Path to a JSON file containing an array of task definitions, or '-' to read JSON from stdin.",
4105
+ )
4048
4106
  version_parser = subparsers.add_parser("version")
4049
4107
  v_sub = version_parser.add_subparsers(dest="version_command")
4050
4108
  p_v = v_sub.add_parser("promote")
@@ -4202,6 +4260,7 @@ Usage:
4202
4260
  interactive=args.interactive,
4203
4261
  blocked_by=args.blocked_by,
4204
4262
  subtask_of=args.subtask_of,
4263
+ bulk=args.bulk,
4205
4264
  )
4206
4265
  elif args.command == "worktree":
4207
4266
  cmd_worktree(console, manager, args)
@@ -455,8 +455,75 @@ def commit_tasks(message: str = "", push: bool = False) -> str:
455
455
  return f"Committed: {message}"
456
456
  except subprocess.CalledProcessError as e:
457
457
  return f"Error: {e.stderr}"
458
- except Exception as e:
459
- return f"Error: {e}"
458
+
459
+
460
+ @mcp.tool()
461
+ def create_tasks(
462
+ tasks: list[dict],
463
+ ) -> str:
464
+ """Create multiple new tasks in the mission queue at once.
465
+
466
+ Args:
467
+ tasks: A list of task definitions. Each definition is a dictionary that
468
+ can contain the following keys:
469
+ - 'title' (str, required): The title of the task.
470
+ - 'completion_criteria' (str, required): Clear, measurable criteria for task completion.
471
+ - 'body' (str, optional): Detailed description of the task.
472
+ - 'draft' (bool, optional): If True, creates the task in 'draft' status. Default is False (pending).
473
+ - 'blocked_by' (str, optional): Comma-separated list of existing task slugs this task depends on.
474
+ - 'depends_on' (str, optional): Deprecated alias for blocked_by.
475
+ - 'subtask_of' (str, optional): Slug of the parent task this task is a subtask of.
476
+ - 'as_dir' (bool, optional): Create as folder directory. Default is True.
477
+
478
+ Returns:
479
+ A summary report of created task slugs and any errors.
480
+ """
481
+ manager = get_manager()
482
+ created = []
483
+ errors = []
484
+
485
+ for idx, t in enumerate(tasks):
486
+ title = t.get("title")
487
+ criteria = t.get("completion_criteria")
488
+ if not title:
489
+ errors.append(f"Task at index {idx} is missing required 'title'.")
490
+ continue
491
+ if not criteria:
492
+ errors.append(
493
+ f"Task '{title}' (index {idx}) is missing required 'completion_criteria'."
494
+ )
495
+ continue
496
+
497
+ body = t.get("body", "")
498
+ draft = t.get("draft", False)
499
+ blocked_by = t.get("blocked_by") or t.get("depends_on")
500
+ subtask_of = t.get("subtask_of")
501
+ as_dir = t.get("as_dir", True)
502
+
503
+ try:
504
+ issue = manager.create_issue(
505
+ title=title,
506
+ body=body,
507
+ draft=draft,
508
+ depends_on=blocked_by,
509
+ as_dir=as_dir,
510
+ completion_criteria=criteria,
511
+ blocked_by=blocked_by,
512
+ subtask_of=subtask_of,
513
+ )
514
+ created.append(f"{issue.slug} (Status: {issue.status})")
515
+ except Exception as e:
516
+ errors.append(f"Error creating task '{title}': {e}")
517
+
518
+ report = []
519
+ if created:
520
+ report.append(
521
+ "Successfully created tasks:\n" + "\n".join(f"- {c}" for c in created)
522
+ )
523
+ if errors:
524
+ report.append("Errors encountered:\n" + "\n".join(f"- {err}" for err in errors))
525
+
526
+ return "\n\n".join(report)
460
527
 
461
528
 
462
529
  def run_mcp_server():
@@ -75,6 +75,35 @@ def test_cmd_new_dir(manager, temp_issues_dir):
75
75
  assert issues[0].status == "draft"
76
76
 
77
77
 
78
+ def test_cmd_new_bulk(manager, temp_issues_dir, tmp_path):
79
+ console = Console()
80
+ import json
81
+
82
+ tasks_data = [
83
+ {"title": "Bulk 1", "completion_criteria": "CC 1", "body": "Body 1"},
84
+ {"title": "Bulk 2", "completion_criteria": "CC 2", "draft": True},
85
+ ]
86
+
87
+ json_file = tmp_path / "bulk_tasks.json"
88
+ json_file.write_text(json.dumps(tasks_data), encoding="utf-8")
89
+
90
+ cmd_new(
91
+ console=console,
92
+ manager=manager,
93
+ title=None,
94
+ body="",
95
+ draft=False,
96
+ bulk=str(json_file),
97
+ )
98
+
99
+ issues = manager.load_mission()
100
+ assert len(issues) == 2
101
+ assert issues[0].slug == "bulk-1"
102
+ assert issues[0].status == "pending"
103
+ assert issues[1].slug == "bulk-2"
104
+ assert issues[1].status == "draft"
105
+
106
+
78
107
  def test_cmd_done(manager, temp_issues_dir):
79
108
  console = Console()
80
109
  cmd_new(console, manager, "Done Task", "Body", draft=False)
@@ -44,6 +44,22 @@ def test_mcp_create_task(mock_manager):
44
44
  )
45
45
 
46
46
 
47
+ def test_mcp_create_tasks(mock_manager):
48
+ mock_manager.create_issue.return_value = Issue(
49
+ name="Task 1", slug="task-1", status="pending"
50
+ )
51
+
52
+ tasks = [
53
+ {"title": "Task 1", "completion_criteria": "Must pass tests", "body": "Desc 1"},
54
+ {"title": "Task 2", "completion_criteria": "Done 2", "draft": True},
55
+ ]
56
+
57
+ result = mcp.create_tasks(tasks)
58
+ assert "Successfully created tasks:" in result
59
+ assert "- task-1 (Status: pending)" in result
60
+ assert mock_manager.create_issue.call_count == 2
61
+
62
+
47
63
  def test_mcp_mark_task_active(mock_manager):
48
64
  mock_manager.slugify.return_value = "task-1"
49
65
  result = mcp.mark_task_active("Task 1")
@@ -244,6 +260,7 @@ EXPECTED_TOOLS = {
244
260
  "list_tasks",
245
261
  "list_active_tasks",
246
262
  "create_task",
263
+ "create_tasks",
247
264
  "promote_task",
248
265
  "demote_task",
249
266
  "mark_task_active",
@@ -1135,7 +1135,7 @@ wheels = [
1135
1135
 
1136
1136
  [[package]]
1137
1137
  name = "task-agent"
1138
- version = "1.0.2"
1138
+ version = "1.0.3"
1139
1139
  source = { editable = "." }
1140
1140
  dependencies = [
1141
1141
  { name = "githubkit" },
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