task-agent 0.1.232__tar.gz → 0.1.246__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-0.1.232 → task_agent-0.1.246}/Makefile +1 -1
- {task_agent-0.1.232 → task_agent-0.1.246}/PKG-INFO +2 -1
- task_agent-0.1.246/docs/tasks/completed/2026/add-commit-and-push/README.md +6 -0
- task_agent-0.1.246/docs/tasks/completed/2026/add-configuration-file-support-for-github-plugin/README.md +6 -0
- task_agent-0.1.246/docs/tasks/completed/2026/integreate-with-github-issues/README.md +16 -0
- {task_agent-0.1.232/docs/tasks/pending → task_agent-0.1.246/docs/tasks/completed/2026}/make-copy-slug-available-from-the-history-list/README.md +3 -0
- {task_agent-0.1.232/docs/tasks/pending → task_agent-0.1.246/docs/tasks/completed/2026}/search-is-not-working/README.md +3 -0
- task_agent-0.1.246/docs/tasks/completed/2026/search-should-include-completed-items-in-the-resultes/README.md +12 -0
- task_agent-0.1.246/docs/tasks/completed/2026/ta-list-should-use-the-same-style-template-as-ta-prior/README.md +10 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/pyproject.toml +3 -2
- {task_agent-0.1.232 → task_agent-0.1.246}/src/taskagent/cli.py +303 -78
- {task_agent-0.1.232 → task_agent-0.1.246}/src/taskagent/manager.py +10 -9
- task_agent-0.1.246/src/taskagent/models/__init__.py +0 -0
- task_agent-0.1.246/src/taskagent/plugins/__init__.py +22 -0
- task_agent-0.1.246/src/taskagent/plugins/github.py +114 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/uv.lock +91 -1
- task_agent-0.1.232/docs/tasks/mission.usv +0 -2
- {task_agent-0.1.232 → task_agent-0.1.246}/.gemini/settings.json +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/.github/workflows/ci.yml +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/.github/workflows/publish.yml +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/.gitignore +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/.mise.toml +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/.pre-commit-config.yaml +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/.python-version +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/.ta/worker +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/.task-agent/worktree-config.json +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/GEMINI.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/architecture/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/chats/gemini-conversation-1773283034591.json +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/chats/project-init-to-v0.1.46.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/cli/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/development/README.md +0 -0
- {task_agent-0.1.232/docs/tasks → task_agent-0.1.246/docs/tasks/.task-agent}/datapackage.json +0 -0
- {task_agent-0.1.232/docs/issues → task_agent-0.1.246/docs/tasks/.task-agent}/mission.usv +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/add-a-history-function/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/add-an-interactive-new/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/add-json-switch-to-output-to-json-for-integrations-such-as-nvim.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/add-search-feature.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/add-ta-path-command.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/add-triage-cli-sorting.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/add-view-and-edit-features-to-triage.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/agent-reports-mcp-access-issue.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/allow-optional-role-property-of-the-issuetask.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/cli-observability-layer/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/create-a-copy-slug-feature-in-ta-prior/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/create-done-process.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/create-make-next-script.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/create-model-configuration-schema/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/create-plugin-sidecar-strategy.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/define-task-metadata-standard/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/dependent-task.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/directory-task/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/done-should-require-explainaition-of-solution.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/dummy-task.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/enhanced-task-lifecycle-and-git-orchestration/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/fix-uv-lock.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/history-feature-should-be-sorted-newest-at-the-top/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/history-should-allow-l-to-view/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/history-should-show-datetime-in-iso-with-local-timezone/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/i-can-only-see-20-items-in-the-hsitory-view/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/implement-adk-sidecar-worker-with-multi-agent-architecture.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/implement-plugin-based-secret-manager/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/implement-strict-worktree-lifecycle/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/implement-ta-done-git-primitive-integration/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/init-mcp-doesnt-work-for-opencode/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/initial-setup.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/make-nvim-the-default-editor.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/make-sure-x1e-is-not-used-as-a-record-separator-anywhere.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/make-ta-version-show-the-latest-pypiorg-version-of-task-agent/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/mcp-init-breaks-in-powershell.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/migrate-docsissues-to-docstasks.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/migrate-from-markdown-files-to-folders-with-readmemd/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/migrate-to-tasks-directory.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/nest-dependent-tasks-under-their-parent-in-list-view.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/new-issue-feature-in-mcp-should-require-completion-criteria.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/refactor-models.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/remove-from-usv-writer-end-of-line.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/remove-test-issues-from-the-data.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/rename-triage.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/restyle-to-not-use-borders/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/secure-secret-management-and-config-abstraction/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/shortcuts-in-prior-are-not-highlighted.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/sub-agent-observability-and-transparency/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/task-agent-should-not-create-read-only-access-on-each-task-markdown.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/test-plan.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/test-task.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/the-border-around-triage-is-dark-blue-on-black.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/triage-should-provide-an-add-feature.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/update-development-standards-to-use-ta-done.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/tasks/completed/2026/upgrade-to-tasks-should-handle-existing-issues-and-tasks.md +0 -0
- /task_agent-0.1.232/src/taskagent/models/__init__.py → /task_agent-0.1.246/docs/tasks/mission.usv +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/docs/workflow/README.md +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/opencode +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/sidecars/adk-worker/.env.example +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/sidecars/adk-worker/pyproject.toml +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/sidecars/adk-worker/worker.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/src/taskagent/__init__.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/src/taskagent/__main__.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/src/taskagent/config.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/src/taskagent/discovery.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/src/taskagent/mcp.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/src/taskagent/models/issue.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/test-dir/opencode.json +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/tests/test_cli.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/tests/test_cli_windows.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/tests/test_discovery.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/tests/test_manager.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/tests/test_manager_git.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/tests/test_mcp.py +0 -0
- {task_agent-0.1.232 → task_agent-0.1.246}/tests/test_migration.py +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: task-agent
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.246
|
|
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
|
|
7
7
|
Requires-Python: >=3.12
|
|
8
|
+
Requires-Dist: githubkit>=0.15.5
|
|
8
9
|
Requires-Dist: mcp>=1.26.0
|
|
9
10
|
Requires-Dist: pydantic>=2.10.6
|
|
10
11
|
Requires-Dist: pyperclip>=1.9.0
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Integreate with GitHub Issues
|
|
2
|
+
|
|
3
|
+
Allow the user to turn on GitHub Issues integration.
|
|
4
|
+
|
|
5
|
+
This should be a pluggin.
|
|
6
|
+
|
|
7
|
+
We might later want to integrate with Jira or Rally or other systems.
|
|
8
|
+
|
|
9
|
+
We should define a data schema for our current task item structure, probably in Pydantic.
|
|
10
|
+
|
|
11
|
+
We should define another one for the GitHub Issue Schema and their board structure.
|
|
12
|
+
|
|
13
|
+
Then, we should have a clear, declared formal structure to map one to the other. I don't know what the best mapping library or tooling is in Python, but we should think that through and use the most production-ready and robust tooling. Maybe Protocols would be involved.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
**Completed in commit:** `<pending-commit-id>`
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# search should include completed items in the resultes
|
|
2
|
+
|
|
3
|
+
Currently, search does not appear to include completed items.
|
|
4
|
+
|
|
5
|
+
Search should be a fuzzy search that matches the first part of the slug, even if the user didn't include dashes, and without case sensitifity, and without trying to match punctuation. Punctuation should be ignored in the search to include more results.
|
|
6
|
+
|
|
7
|
+
## Solution
|
|
8
|
+
|
|
9
|
+
Updated search to always include completed items and use fuzzy matching that strips dashes/punctuation and is case-insensitive.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
**Completed in commit:** `a5e9f5b`
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# ta list should use the same style template as ta prior
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
## Solution
|
|
6
|
+
|
|
7
|
+
Updated ta list table to match triage style: box=None, padding=(0,2), UPPERCASE status, and simplified columns to Priority, Status, Slug.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
**Completed in commit:** `4a91bd4`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "task-agent"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.246"
|
|
4
4
|
description = "A prioritized, file-based task queue for autonomous agentic workers"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -15,6 +15,7 @@ dependencies = [
|
|
|
15
15
|
"pyperclip>=1.9.0",
|
|
16
16
|
"questionary>=2.1.1",
|
|
17
17
|
"rich>=13.9.4",
|
|
18
|
+
"githubkit>=0.15.5",
|
|
18
19
|
]
|
|
19
20
|
|
|
20
21
|
[project.scripts]
|
|
@@ -38,7 +39,7 @@ dev = [
|
|
|
38
39
|
]
|
|
39
40
|
|
|
40
41
|
[tool.bumpversion]
|
|
41
|
-
current_version = "0.1.
|
|
42
|
+
current_version = "0.1.246"
|
|
42
43
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
43
44
|
serialize = ["{major}.{minor}.{patch}"]
|
|
44
45
|
search = "version = \"{current_version}\""
|
|
@@ -27,12 +27,13 @@ try:
|
|
|
27
27
|
HAS_TERMIOS = True
|
|
28
28
|
except ImportError:
|
|
29
29
|
HAS_TERMIOS = False
|
|
30
|
-
try:
|
|
31
|
-
import msvcrt
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
try:
|
|
32
|
+
import msvcrt
|
|
33
|
+
|
|
34
|
+
HAS_MSVCRT = True
|
|
35
|
+
except ImportError:
|
|
36
|
+
HAS_MSVCRT = False
|
|
36
37
|
|
|
37
38
|
from rich.live import Live
|
|
38
39
|
|
|
@@ -308,13 +309,57 @@ def cmd_next(console: Console, manager: TaskAgent):
|
|
|
308
309
|
|
|
309
310
|
|
|
310
311
|
def cmd_search(console: Console, manager: TaskAgent, pattern: str):
|
|
311
|
-
"""Search for issues by slug
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
312
|
+
"""Search for issues by slug pattern (case-insensitive fuzzy match)."""
|
|
313
|
+
import re
|
|
314
|
+
|
|
315
|
+
# Normalize pattern: remove dashes, punctuation, lowercase
|
|
316
|
+
def normalize(s: str) -> str:
|
|
317
|
+
return re.sub(r"[^a-zA-Z0-9]", "", s).lower()
|
|
318
|
+
|
|
319
|
+
def fuzzy_match(slug: str, pattern: str) -> bool:
|
|
320
|
+
slug_clean = normalize(slug)
|
|
321
|
+
pat_clean = normalize(pattern)
|
|
322
|
+
return pat_clean in slug_clean or slug_clean.startswith(pat_clean)
|
|
323
|
+
|
|
324
|
+
pat_norm = normalize(pattern)
|
|
325
|
+
if not pat_norm:
|
|
326
|
+
console.print("[yellow]No pattern provided.[/yellow]")
|
|
315
327
|
return
|
|
316
328
|
|
|
317
|
-
matches = [
|
|
329
|
+
matches: List[Issue] = []
|
|
330
|
+
|
|
331
|
+
# Search mission issues
|
|
332
|
+
issues = manager.load_mission()
|
|
333
|
+
for i in issues:
|
|
334
|
+
if fuzzy_match(i.slug, pat_norm):
|
|
335
|
+
matches.append(i)
|
|
336
|
+
|
|
337
|
+
# Always search completed tasks too
|
|
338
|
+
completed_root = manager.issues_root / "completed"
|
|
339
|
+
if completed_root.exists():
|
|
340
|
+
for year_dir in sorted(completed_root.iterdir(), reverse=True):
|
|
341
|
+
if year_dir.is_dir():
|
|
342
|
+
for f in year_dir.glob("*.md"):
|
|
343
|
+
if fuzzy_match(f.stem, pat_norm):
|
|
344
|
+
name = manager.extract_title(f)
|
|
345
|
+
matches.append(
|
|
346
|
+
Issue(
|
|
347
|
+
name=name, slug=f.stem, status="completed", priority=0
|
|
348
|
+
)
|
|
349
|
+
)
|
|
350
|
+
for d in year_dir.iterdir():
|
|
351
|
+
if d.is_dir():
|
|
352
|
+
readme = d / "README.md"
|
|
353
|
+
if readme.exists() and fuzzy_match(d.name, pat_norm):
|
|
354
|
+
name = manager.extract_title(readme)
|
|
355
|
+
matches.append(
|
|
356
|
+
Issue(
|
|
357
|
+
name=name,
|
|
358
|
+
slug=d.name,
|
|
359
|
+
status="completed",
|
|
360
|
+
priority=0,
|
|
361
|
+
)
|
|
362
|
+
)
|
|
318
363
|
|
|
319
364
|
if not matches:
|
|
320
365
|
console.print(f"[yellow]No issues match pattern '{pattern}'.[/yellow]")
|
|
@@ -401,7 +446,49 @@ def cmd_search(console: Console, manager: TaskAgent, pattern: str):
|
|
|
401
446
|
subprocess.run([editor, str(issue_file)])
|
|
402
447
|
manager.init_project()
|
|
403
448
|
issues = manager.load_mission()
|
|
404
|
-
matches = [i for i in issues if i.slug
|
|
449
|
+
matches = [i for i in issues if fuzzy_match(i.slug, pat_norm)]
|
|
450
|
+
# Also re-search completed
|
|
451
|
+
completed_root = manager.issues_root / "completed"
|
|
452
|
+
new_matches: List[Issue] = list(matches)
|
|
453
|
+
if completed_root.exists():
|
|
454
|
+
for year_dir in sorted(
|
|
455
|
+
completed_root.iterdir(), reverse=True
|
|
456
|
+
):
|
|
457
|
+
if year_dir.is_dir():
|
|
458
|
+
for f in year_dir.glob("*.md"):
|
|
459
|
+
if fuzzy_match(f.stem, pat_norm) and not any(
|
|
460
|
+
m.slug == f.stem for m in new_matches
|
|
461
|
+
):
|
|
462
|
+
name = manager.extract_title(f)
|
|
463
|
+
new_matches.append(
|
|
464
|
+
Issue(
|
|
465
|
+
name=name,
|
|
466
|
+
slug=f.stem,
|
|
467
|
+
status="completed",
|
|
468
|
+
priority=0,
|
|
469
|
+
)
|
|
470
|
+
)
|
|
471
|
+
for d in year_dir.iterdir():
|
|
472
|
+
if d.is_dir():
|
|
473
|
+
readme = d / "README.md"
|
|
474
|
+
if (
|
|
475
|
+
readme.exists()
|
|
476
|
+
and fuzzy_match(d.name, pat_norm)
|
|
477
|
+
and not any(
|
|
478
|
+
m.slug == d.name
|
|
479
|
+
for m in new_matches
|
|
480
|
+
)
|
|
481
|
+
):
|
|
482
|
+
name = manager.extract_title(readme)
|
|
483
|
+
new_matches.append(
|
|
484
|
+
Issue(
|
|
485
|
+
name=name,
|
|
486
|
+
slug=d.name,
|
|
487
|
+
status="completed",
|
|
488
|
+
priority=0,
|
|
489
|
+
)
|
|
490
|
+
)
|
|
491
|
+
matches = new_matches
|
|
405
492
|
if cursor >= len(matches):
|
|
406
493
|
cursor = max(0, len(matches) - 1)
|
|
407
494
|
else:
|
|
@@ -489,7 +576,7 @@ def cmd_history(console: Console, manager: TaskAgent, limit: int = 20):
|
|
|
489
576
|
str(absolute_idx + 1), date_str, f"{prefix}{slug}", style=style
|
|
490
577
|
)
|
|
491
578
|
|
|
492
|
-
help_text = "[dim]v/l: view | q: exit[/dim]"
|
|
579
|
+
help_text = "[dim]v/l: view | c: copy slug | q: exit[/dim]"
|
|
493
580
|
|
|
494
581
|
live.update(Panel(table, subtitle=help_text, box=box.MINIMAL), refresh=True)
|
|
495
582
|
|
|
@@ -503,7 +590,7 @@ def cmd_history(console: Console, manager: TaskAgent, limit: int = 20):
|
|
|
503
590
|
elif key in ["k", "\x1b[A"]:
|
|
504
591
|
cursor = max(0, cursor - 1)
|
|
505
592
|
elif key in ["j", "\x1b[B"]:
|
|
506
|
-
cursor = min(len(all_completed)
|
|
593
|
+
cursor = min(len(all_completed), cursor + 1)
|
|
507
594
|
elif key in ["v", "l"]:
|
|
508
595
|
live.stop()
|
|
509
596
|
file, slug = all_completed[cursor]
|
|
@@ -514,6 +601,14 @@ def cmd_history(console: Console, manager: TaskAgent, limit: int = 20):
|
|
|
514
601
|
except Exception:
|
|
515
602
|
pass
|
|
516
603
|
live.start()
|
|
604
|
+
elif key in ["c"]:
|
|
605
|
+
# Copy slug to clipboard
|
|
606
|
+
_, slug = all_completed[cursor]
|
|
607
|
+
try:
|
|
608
|
+
pyperclip.copy(slug)
|
|
609
|
+
console.print(f"[green]Copied slug to clipboard: {slug}[/green]")
|
|
610
|
+
except Exception as e:
|
|
611
|
+
console.print(f"[yellow]Failed to copy to clipboard: {e}[/yellow]")
|
|
517
612
|
|
|
518
613
|
|
|
519
614
|
def cmd_report(console: Console, manager: TaskAgent, slug: str):
|
|
@@ -613,6 +708,76 @@ def cmd_push(console: Console, manager: TaskAgent):
|
|
|
613
708
|
console.print(f"[red]Failed to push mission repository: {e}[/red]")
|
|
614
709
|
|
|
615
710
|
|
|
711
|
+
def cmd_commit(
|
|
712
|
+
console: Console,
|
|
713
|
+
manager: TaskAgent,
|
|
714
|
+
message: Optional[str] = None,
|
|
715
|
+
should_push: bool = True,
|
|
716
|
+
):
|
|
717
|
+
"""Commit and optionally push changes in the tasks/ directory."""
|
|
718
|
+
import subprocess
|
|
719
|
+
|
|
720
|
+
# Determine the tasks directory
|
|
721
|
+
tasks_dir = manager.issues_root
|
|
722
|
+
if not tasks_dir or not tasks_dir.exists():
|
|
723
|
+
console.print("[red]Tasks directory not found.[/red]")
|
|
724
|
+
return
|
|
725
|
+
|
|
726
|
+
# Generate default commit message if not provided
|
|
727
|
+
if not message:
|
|
728
|
+
message = f"Update tasks - {datetime.now().strftime('%Y-%m-%d %H:%M')}"
|
|
729
|
+
|
|
730
|
+
console.print(f"[blue]Committing changes in {tasks_dir}...[/blue]")
|
|
731
|
+
|
|
732
|
+
try:
|
|
733
|
+
# Add all changes in tasks directory
|
|
734
|
+
subprocess.run(
|
|
735
|
+
["git", "add", str(tasks_dir / ".")],
|
|
736
|
+
check=True,
|
|
737
|
+
capture_output=True,
|
|
738
|
+
text=True,
|
|
739
|
+
shell=(os.name == "nt"),
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
# Check if there are changes to commit
|
|
743
|
+
result = subprocess.run(
|
|
744
|
+
["git", "diff", "--cached", "--quiet"],
|
|
745
|
+
capture_output=True,
|
|
746
|
+
text=True,
|
|
747
|
+
shell=(os.name == "nt"),
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
if result.returncode == 0:
|
|
751
|
+
console.print("[yellow]No changes to commit in tasks/ directory.[/yellow]")
|
|
752
|
+
return
|
|
753
|
+
|
|
754
|
+
# Commit
|
|
755
|
+
subprocess.run(
|
|
756
|
+
["git", "commit", "-m", message],
|
|
757
|
+
check=True,
|
|
758
|
+
capture_output=True,
|
|
759
|
+
text=True,
|
|
760
|
+
shell=(os.name == "nt"),
|
|
761
|
+
)
|
|
762
|
+
console.print(f"[bold green]Committed: {message}[/bold green]")
|
|
763
|
+
|
|
764
|
+
# Push if requested
|
|
765
|
+
if should_push:
|
|
766
|
+
if manager.mission_root:
|
|
767
|
+
console.print("[blue]Pushing to remote...[/blue]")
|
|
768
|
+
manager.push_mission_repo()
|
|
769
|
+
console.print("[bold green]Successfully pushed.[/bold green]")
|
|
770
|
+
else:
|
|
771
|
+
console.print(
|
|
772
|
+
"[yellow]No mission repository configured, skipping push.[/yellow]"
|
|
773
|
+
)
|
|
774
|
+
|
|
775
|
+
except subprocess.CalledProcessError as e:
|
|
776
|
+
console.print(f"[red]Error: {e.stderr}[/red]")
|
|
777
|
+
except Exception as e:
|
|
778
|
+
console.print(f"[red]Unexpected error: {e}[/red]")
|
|
779
|
+
|
|
780
|
+
|
|
616
781
|
def cmd_eject_mission(console: Console, manager: TaskAgent, public: bool = False):
|
|
617
782
|
"""Automate the move of docs/tasks to a separate repository."""
|
|
618
783
|
source_dir = manager.issues_root
|
|
@@ -896,25 +1061,21 @@ def cmd_list(
|
|
|
896
1061
|
)
|
|
897
1062
|
return
|
|
898
1063
|
|
|
899
|
-
table = Table(title="Task Queue")
|
|
1064
|
+
table = Table(title="Task Queue", box=None, padding=(0, 2))
|
|
900
1065
|
table.add_column("Priority", justify="right", style="cyan")
|
|
901
|
-
table.add_column("Status",
|
|
902
|
-
table.add_column("
|
|
903
|
-
table.add_column("Slug", style="green")
|
|
904
|
-
table.add_column("Depends On", style="yellow")
|
|
905
|
-
table.add_column("Location", style="dim")
|
|
1066
|
+
table.add_column("Status", width=10)
|
|
1067
|
+
table.add_column("Slug")
|
|
906
1068
|
|
|
907
1069
|
for issue, depth in rows_to_display:
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
status_str = f"[bold green]{status_str}[/bold green]"
|
|
1070
|
+
status_style = "white"
|
|
1071
|
+
if issue.status == "active":
|
|
1072
|
+
status_style = "bold green"
|
|
1073
|
+
elif issue.status == "pending":
|
|
1074
|
+
status_style = "bold yellow"
|
|
1075
|
+
elif issue.status == "draft":
|
|
1076
|
+
status_style = "dim"
|
|
1077
|
+
elif issue.status == "completed":
|
|
1078
|
+
status_style = "bold blue"
|
|
918
1079
|
|
|
919
1080
|
indent = " " * depth
|
|
920
1081
|
prefix = "└─ " if depth > 0 else ""
|
|
@@ -922,11 +1083,8 @@ def cmd_list(
|
|
|
922
1083
|
|
|
923
1084
|
table.add_row(
|
|
924
1085
|
str(issue.priority),
|
|
925
|
-
|
|
926
|
-
issue.name,
|
|
1086
|
+
f"[{status_style}]{issue.status.upper()}[/{status_style}]",
|
|
927
1087
|
display_slug,
|
|
928
|
-
", ".join(issue.dependencies),
|
|
929
|
-
location,
|
|
930
1088
|
)
|
|
931
1089
|
console.print(table)
|
|
932
1090
|
|
|
@@ -1544,6 +1702,86 @@ def cmd_worktree(console: Console, manager: TaskAgent, args):
|
|
|
1544
1702
|
console.print(f"[red]Error pruning worktrees: {e.stderr}[/red]")
|
|
1545
1703
|
|
|
1546
1704
|
|
|
1705
|
+
def cmd_github(console: Console, manager: TaskAgent, args):
|
|
1706
|
+
"""Sync with GitHub Issues."""
|
|
1707
|
+
try:
|
|
1708
|
+
from taskagent.plugins.github import GitHubPlugin
|
|
1709
|
+
except ImportError:
|
|
1710
|
+
console.print("[red]GitHub plugin not installed. Run: uv add githubkit[/red]")
|
|
1711
|
+
return
|
|
1712
|
+
|
|
1713
|
+
# Load config from .task-agent/worktree-config.json
|
|
1714
|
+
config = {}
|
|
1715
|
+
config_file = Path(".task-agent/worktree-config.json")
|
|
1716
|
+
if config_file.exists():
|
|
1717
|
+
try:
|
|
1718
|
+
with config_file.open("r", encoding="utf-8") as f:
|
|
1719
|
+
config = json.load(f)
|
|
1720
|
+
except Exception:
|
|
1721
|
+
pass
|
|
1722
|
+
|
|
1723
|
+
# Override repo if specified in args
|
|
1724
|
+
if hasattr(args, "repo") and args.repo:
|
|
1725
|
+
if "github" not in config:
|
|
1726
|
+
config["github"] = {}
|
|
1727
|
+
config["github"]["repo"] = args.repo
|
|
1728
|
+
|
|
1729
|
+
try:
|
|
1730
|
+
plugin = GitHubPlugin(config)
|
|
1731
|
+
except ValueError as e:
|
|
1732
|
+
console.print(f"[red]{e}[/red]")
|
|
1733
|
+
return
|
|
1734
|
+
|
|
1735
|
+
if args.github_command == "sync":
|
|
1736
|
+
try:
|
|
1737
|
+
issues = plugin.sync_from_github()
|
|
1738
|
+
console.print(f"[green]Imported {len(issues)} issues from GitHub[/green]")
|
|
1739
|
+
|
|
1740
|
+
# Add issues to task-agent
|
|
1741
|
+
for issue in issues:
|
|
1742
|
+
try:
|
|
1743
|
+
manager.create_issue(issue.name, body="Imported from GitHub")
|
|
1744
|
+
console.print(f" Added: {issue.name}")
|
|
1745
|
+
except Exception as e:
|
|
1746
|
+
console.print(f" [yellow]Skipped {issue.slug}: {e}[/yellow]")
|
|
1747
|
+
|
|
1748
|
+
# Save mission
|
|
1749
|
+
manager.save_mission(manager.load_mission())
|
|
1750
|
+
console.print("[green]Mission file updated[/green]")
|
|
1751
|
+
except Exception as e:
|
|
1752
|
+
console.print(f"[red]Error syncing: {e}[/red]")
|
|
1753
|
+
|
|
1754
|
+
elif args.github_command == "create":
|
|
1755
|
+
try:
|
|
1756
|
+
issue = None
|
|
1757
|
+
# Find the issue by slug
|
|
1758
|
+
issue_file = manager.find_issue_file(args.slug)
|
|
1759
|
+
if not issue_file:
|
|
1760
|
+
console.print(f"[red]Issue '{args.slug}' not found[/red]")
|
|
1761
|
+
return
|
|
1762
|
+
|
|
1763
|
+
# Load issue details
|
|
1764
|
+
content = (
|
|
1765
|
+
issue_file.read_text()
|
|
1766
|
+
if issue_file.name == "README.md"
|
|
1767
|
+
else issue_file.read_text()
|
|
1768
|
+
)
|
|
1769
|
+
name = content.split("\n")[0].lstrip("#").strip()
|
|
1770
|
+
|
|
1771
|
+
# Create GitHub issue
|
|
1772
|
+
from taskagent.models.issue import Issue
|
|
1773
|
+
|
|
1774
|
+
temp_issue = Issue(name=name, slug=args.slug, dependencies=[])
|
|
1775
|
+
result = plugin.create_github_issue(temp_issue)
|
|
1776
|
+
|
|
1777
|
+
console.print(f"[green]Created GitHub Issue #{result['number']}[/green]")
|
|
1778
|
+
console.print(f"URL: {result['url']}")
|
|
1779
|
+
except Exception as e:
|
|
1780
|
+
console.print(f"[red]Error creating issue: {e}[/red]")
|
|
1781
|
+
else:
|
|
1782
|
+
console.print("[yellow]Use 'sync' or 'create' subcommand[/yellow]")
|
|
1783
|
+
|
|
1784
|
+
|
|
1547
1785
|
def _copy_files_to_worktree(console: Console, worktree_path: Path, patterns: list):
|
|
1548
1786
|
"""Copy files matching patterns to the worktree directory."""
|
|
1549
1787
|
import shutil
|
|
@@ -1669,51 +1907,6 @@ def _configure_git_user_for_worktree(
|
|
|
1669
1907
|
console.print(
|
|
1670
1908
|
f"[yellow]Warning: Failed to configure git user for worktree: {e}[/yellow]"
|
|
1671
1909
|
)
|
|
1672
|
-
user_email_result = subprocess.run(
|
|
1673
|
-
["git", "config", "--get", "user.email"],
|
|
1674
|
-
capture_output=True,
|
|
1675
|
-
text=True,
|
|
1676
|
-
check=False,
|
|
1677
|
-
)
|
|
1678
|
-
|
|
1679
|
-
user_name = (
|
|
1680
|
-
user_name_result.stdout.strip() if user_name_result.returncode == 0 else ""
|
|
1681
|
-
)
|
|
1682
|
-
user_email = (
|
|
1683
|
-
user_email_result.stdout.strip()
|
|
1684
|
-
if user_email_result.returncode == 0
|
|
1685
|
-
else ""
|
|
1686
|
-
)
|
|
1687
|
-
|
|
1688
|
-
# TODO: Implement branch-specific git config logic here
|
|
1689
|
-
# For now, we'll just use the current user's config
|
|
1690
|
-
# In the future, this could read from a config file to determine
|
|
1691
|
-
# which GitHub account to use for which branch
|
|
1692
|
-
|
|
1693
|
-
if user_name and user_email:
|
|
1694
|
-
# Set git config locally for this worktree
|
|
1695
|
-
subprocess.run(
|
|
1696
|
-
["git", "-C", str(worktree_path), "config", "user.name", user_name],
|
|
1697
|
-
check=False,
|
|
1698
|
-
capture_output=True,
|
|
1699
|
-
)
|
|
1700
|
-
subprocess.run(
|
|
1701
|
-
["git", "-C", str(worktree_path), "config", "user.email", user_email],
|
|
1702
|
-
check=False,
|
|
1703
|
-
capture_output=True,
|
|
1704
|
-
)
|
|
1705
|
-
console.print(
|
|
1706
|
-
f"[dim]Configured git user for worktree: {user_name} <{user_email}>[/dim]"
|
|
1707
|
-
)
|
|
1708
|
-
else:
|
|
1709
|
-
console.print(
|
|
1710
|
-
"[yellow]Warning: Could not determine git user from current config[/yellow]"
|
|
1711
|
-
)
|
|
1712
|
-
|
|
1713
|
-
except Exception as e:
|
|
1714
|
-
console.print(
|
|
1715
|
-
f"[yellow]Warning: Failed to configure git user for worktree: {e}[/yellow]"
|
|
1716
|
-
)
|
|
1717
1910
|
|
|
1718
1911
|
|
|
1719
1912
|
def cmd_restore(
|
|
@@ -2178,6 +2371,21 @@ def main():
|
|
|
2178
2371
|
worktree_parser.add_argument(
|
|
2179
2372
|
"--no-env", action="store_true", help="Do not copy .env files to worktree"
|
|
2180
2373
|
)
|
|
2374
|
+
|
|
2375
|
+
# GitHub integration
|
|
2376
|
+
github_parser = subparsers.add_parser("github", help="Sync with GitHub Issues")
|
|
2377
|
+
github_sub = github_parser.add_subparsers(dest="github_command")
|
|
2378
|
+
|
|
2379
|
+
sync_parser = github_sub.add_parser("sync", help="Import issues from GitHub")
|
|
2380
|
+
sync_parser.add_argument("--repo", help="Repository (owner/repo) override")
|
|
2381
|
+
|
|
2382
|
+
create_parser = github_sub.add_parser(
|
|
2383
|
+
"create", help="Create GitHub issue from task"
|
|
2384
|
+
)
|
|
2385
|
+
create_parser.add_argument("slug", help="Task slug to create GitHub issue for")
|
|
2386
|
+
|
|
2387
|
+
# Add other commands as needed
|
|
2388
|
+
|
|
2181
2389
|
up_parser = subparsers.add_parser("up")
|
|
2182
2390
|
up_parser.add_argument("slug")
|
|
2183
2391
|
down_parser = subparsers.add_parser("down")
|
|
@@ -2221,6 +2429,21 @@ def main():
|
|
|
2221
2429
|
# push
|
|
2222
2430
|
subparsers.add_parser("push", help="Push the mission repository to origin")
|
|
2223
2431
|
|
|
2432
|
+
# commit - commit and push changes in tasks/ directory
|
|
2433
|
+
commit_parser = subparsers.add_parser(
|
|
2434
|
+
"commit", help="Commit and push changes in tasks/ directory"
|
|
2435
|
+
)
|
|
2436
|
+
commit_parser.add_argument(
|
|
2437
|
+
"-m", "--message", help="Commit message (default: auto-generated)"
|
|
2438
|
+
)
|
|
2439
|
+
commit_parser.add_argument(
|
|
2440
|
+
"--no-push",
|
|
2441
|
+
dest="push",
|
|
2442
|
+
action="store_false",
|
|
2443
|
+
help="Do not push to remote",
|
|
2444
|
+
)
|
|
2445
|
+
commit_parser.set_defaults(push=True)
|
|
2446
|
+
|
|
2224
2447
|
# eject-mission
|
|
2225
2448
|
eject_parser = subparsers.add_parser(
|
|
2226
2449
|
"eject-mission", help="Move mission queue to a separate repository"
|
|
@@ -2362,6 +2585,8 @@ def main():
|
|
|
2362
2585
|
)
|
|
2363
2586
|
elif args.command == "worktree":
|
|
2364
2587
|
cmd_worktree(console, manager, args)
|
|
2588
|
+
elif args.command == "github":
|
|
2589
|
+
cmd_github(console, manager, args)
|
|
2365
2590
|
elif args.command == "version":
|
|
2366
2591
|
if args.version_command == "promote":
|
|
2367
2592
|
cmd_version(console, promote=args.part, push=False)
|
|
@@ -63,20 +63,21 @@ class TaskAgent:
|
|
|
63
63
|
return
|
|
64
64
|
|
|
65
65
|
# First handle chattr (immutable attribute)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
if writable:
|
|
67
|
+
# Remove immutable attribute before making writable
|
|
68
|
+
try:
|
|
69
69
|
subprocess.run(
|
|
70
70
|
["chattr", "-i", str(path)],
|
|
71
71
|
capture_output=True,
|
|
72
|
-
check=
|
|
72
|
+
check=True,
|
|
73
73
|
shell=(os.name == "nt"),
|
|
74
74
|
)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
except subprocess.CalledProcessError:
|
|
76
|
+
raise RuntimeError(
|
|
77
|
+
f"Cannot modify {path.name} (immutable attribute set).\n"
|
|
78
|
+
f"Run: sudo chattr -i {path}\n"
|
|
79
|
+
f"Or: sudo setcap cap_linux_immutable+ep $(which ta)"
|
|
80
|
+
)
|
|
80
81
|
|
|
81
82
|
# Handle chmod
|
|
82
83
|
current_mode = path.stat().st_mode
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Plugin system for task-agent external integrations."""
|
|
2
|
+
|
|
3
|
+
from typing import Protocol, List, Optional
|
|
4
|
+
from taskagent.models.issue import Issue
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class IssueProvider(Protocol):
|
|
8
|
+
"""Protocol for external issue tracking system providers."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, config: dict): ...
|
|
11
|
+
|
|
12
|
+
def sync_from_external(self) -> List[Issue]:
|
|
13
|
+
"""Import issues from external system."""
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
def sync_to_external(self, issues: List[Issue]) -> None:
|
|
17
|
+
"""Export issues to external system."""
|
|
18
|
+
...
|
|
19
|
+
|
|
20
|
+
def get_external_issue(self, issue_id: str) -> Optional[Issue]:
|
|
21
|
+
"""Get a specific issue by ID."""
|
|
22
|
+
...
|