task-agent 1.0.3__tar.gz → 1.0.9__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.9/.gemini/settings.json +22 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/PKG-INFO +1 -1
- {task_agent-1.0.3 → task_agent-1.0.9}/docs/README.md +2 -0
- task_agent-1.0.9/docs/STATION-MAP.md +154 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/docs/architecture/README.md +1 -1
- task_agent-1.0.9/opencode.json +16 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/pyproject.toml +2 -2
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/cli.py +27 -123
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/manager.py +252 -73
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/mcp.py +19 -14
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_cli.py +11 -4
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_manager.py +110 -5
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_mcp.py +8 -3
- {task_agent-1.0.3 → task_agent-1.0.9}/uv.lock +1 -1
- task_agent-1.0.3/.gemini/settings.json +0 -11
- {task_agent-1.0.3 → task_agent-1.0.9}/.env +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/.github/workflows/ci.yml +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/.github/workflows/publish.yml +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/.gitignore +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/.mise.toml +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/.pre-commit-config.yaml +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/.python-version +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/.ta/agents/agy-cli/meta.toml +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/.ta/agents/gh/meta.toml +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/.ta/agents/minimal/meta.toml +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/.ta/agents/uat-aws/meta.toml +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/.ta/worker +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/CLAUDE.md +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/GEMINI.md +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/Makefile +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/README.md +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/agent.env +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/docs/chats/gemini-conversation-1773283034591.json +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/docs/chats/project-init-to-v0.1.46.md +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/docs/cli/README.md +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/docs/development/README.md +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/docs/task-agents.md +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/docs/workflow/README.md +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/scripts/clean_duplicates.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/scripts/migrate_tasks.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/scripts/recover_dates.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/.env.example +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/README.md +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/instructions/manager.md +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/instructions/validator.md +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/instructions/worker.md +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/profiles/default.json +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/pyproject.toml +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/uv.lock +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/sidecars/adk-worker/worker.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/__init__.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/__main__.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/agent.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/audit.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/config.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/discovery.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/models/__init__.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/models/issue.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/models/metric.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/plugins/__init__.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/plugins/github.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/src/taskagent/templates.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/test-dir/opencode.json +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/conftest.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/e2e/test_agent_sandboxing.sh +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_agent.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_agy_cli_worker.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_cli_windows.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_discovery.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_manager_git.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_metrics.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_migration.py +0 -0
- {task_agent-1.0.3 → task_agent-1.0.9}/tests/test_templates.py +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"task-agent": {
|
|
4
|
+
"command": "ta",
|
|
5
|
+
"args": [
|
|
6
|
+
"mcp"
|
|
7
|
+
],
|
|
8
|
+
"trust": true
|
|
9
|
+
},
|
|
10
|
+
"task_agent": {
|
|
11
|
+
"command": "uv",
|
|
12
|
+
"args": [
|
|
13
|
+
"run",
|
|
14
|
+
"--project",
|
|
15
|
+
"/home/mstouffer/repos/task-agent",
|
|
16
|
+
"ta",
|
|
17
|
+
"mcp"
|
|
18
|
+
],
|
|
19
|
+
"trust": true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -12,6 +12,8 @@ Welcome to the comprehensive documentation for **Task Agent**, a prioritized, fi
|
|
|
12
12
|
- Best practices for managing tasks from draft to completion.
|
|
13
13
|
- [**Development**](./development/README.md)
|
|
14
14
|
- Information on project setup, testing, and release processes.
|
|
15
|
+
- [**Station Map**](./STATION-MAP.md)
|
|
16
|
+
- Formal declaration of task-agent as a typed file-path station system: stations, transitions, edge roles, and conformance gaps.
|
|
15
17
|
|
|
16
18
|
## 🚀 Quick Start
|
|
17
19
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# task-agent Station Map
|
|
2
|
+
|
|
3
|
+
> **Status: RATIFIED — installed at `docs/STATION-MAP.md` on 2026-07-15.**
|
|
4
|
+
>
|
|
5
|
+
> This document declares task-agent as an instance of the typed file-path-queue substrate
|
|
6
|
+
> ("a typed state machine where the states are portable file paths") whose vocabulary is
|
|
7
|
+
> owned by the library design spec in the cocli repo (task:
|
|
8
|
+
> `design-spec-for-reusable-typed-file-path-queue-transformer-library-extracted-from-cocli`;
|
|
9
|
+
> thesis: cocli `docs/DESCRIPTION.md`). Do not fork terminology here — if a term is missing,
|
|
10
|
+
> add it to the spec, then use it.
|
|
11
|
+
>
|
|
12
|
+
> Sections marked **observed** describe the implementation as found on 2026-07-12.
|
|
13
|
+
> Sections marked **declared (v1)** are proposals ratified on 2026-07-15.
|
|
14
|
+
|
|
15
|
+
## 1. Record type (observed)
|
|
16
|
+
|
|
17
|
+
One task = one directory, named by **slug**, containing `README.md`
|
|
18
|
+
(Markdown body + YAML frontmatter, currently `created_at` only). The runtime model is
|
|
19
|
+
`Issue` (`src/taskagent/models/issue.py`): `name`, `slug`, `blocked_by: List[str]`,
|
|
20
|
+
`subtask_of: Optional[str]`, with `priority` and `status` derived at runtime.
|
|
21
|
+
Serialization to the index is USV (`\x1f`-delimited).
|
|
22
|
+
|
|
23
|
+
Edge fields are persisted as **body prose**, regex-extracted (`manager.py` ~582):
|
|
24
|
+
`**Blocked by:**` for ordering dependencies, `**Subtask of:**` for hierarchy.
|
|
25
|
+
|
|
26
|
+
**Declared (v1):** edge fields move to structured frontmatter (`blocked_by:`,
|
|
27
|
+
`subtask_of:` YAML lists); prose-line reader extracts values at runtime.
|
|
28
|
+
Frontmatter is the schema; prose is presentation.
|
|
29
|
+
|
|
30
|
+
## 2. Stations (observed)
|
|
31
|
+
|
|
32
|
+
Root: `.task-agent/tasks/` (symlinked at `docs/tasks/`).
|
|
33
|
+
|
|
34
|
+
| Station | Record state | Sharding | Notes |
|
|
35
|
+
| :--- | :--- | :--- | :--- |
|
|
36
|
+
| `draft/` | intention, not yet actionable | flat | |
|
|
37
|
+
| `pending/` | actionable, unclaimed | flat | |
|
|
38
|
+
| `active/` | claimed by a worker | flat | |
|
|
39
|
+
| `completed/` | terminal fact | by year (`2026/`) | month-sharding is a pending task |
|
|
40
|
+
| `deleted/` | terminal, rejected | flat | tombstone station |
|
|
41
|
+
| `mr/` | merge request datagram | by year | **Declared (v1):** task-machine station (see §2a) |
|
|
42
|
+
| `strategy/`, `plan.md` | mission-level records | — | not task records; out of scope of the task state machine |
|
|
43
|
+
|
|
44
|
+
**Declared (v1):** every station binds `(path template, record type, serialization)`.
|
|
45
|
+
`strategy/` and `plan.md` are explicitly **out of scope of the task state machine** —
|
|
46
|
+
they are mission-level documents with their own lifecycle, not task records.
|
|
47
|
+
|
|
48
|
+
### §2a. `mr/` station (ratified)
|
|
49
|
+
|
|
50
|
+
**Record type:** Solution datagram — a text file (`<slug>.md`) containing the solution
|
|
51
|
+
explanation written by a worker agent.
|
|
52
|
+
|
|
53
|
+
**Inbound transition:** A worker that cannot directly commit writes its solution to
|
|
54
|
+
`mr/<slug>.md` instead of calling `ta done`. This is the handoff from the worker's
|
|
55
|
+
isolated environment.
|
|
56
|
+
|
|
57
|
+
**Outbound transition:** `ta merge <slug>` reads the datagram, calls `complete_issue`,
|
|
58
|
+
moves the task to `completed/`, and unlinks the MR file. This is a full task-machine
|
|
59
|
+
transition (`active/` → `completed/`) mediated by the datagram.
|
|
60
|
+
|
|
61
|
+
**Conclusion:** `mr/` is a **station of the task machine**, not a separate machine.
|
|
62
|
+
Its records are ephemeral (deleted on merge); they do not need their own durability
|
|
63
|
+
contract beyond the filesystem.
|
|
64
|
+
|
|
65
|
+
## 3. Transitions (observed)
|
|
66
|
+
|
|
67
|
+
Typed from-station→to-station moves, all implemented as directory renames:
|
|
68
|
+
|
|
69
|
+
| Transition | From → To | Notes |
|
|
70
|
+
| :--- | :--- | :--- |
|
|
71
|
+
| `create` | ∅ → `pending/` (or `draft/` with flag) | assigns slug from title |
|
|
72
|
+
| `promote` | `draft/` → `pending/` | cascades along edges |
|
|
73
|
+
| `demote` | reverse of promote | |
|
|
74
|
+
| `start` / mark-active | `pending/` → `active/` | **this is the claim** |
|
|
75
|
+
| `complete` | `active/` → `completed/YYYY/` | terminal |
|
|
76
|
+
| `restore` | `completed/` → active tree | |
|
|
77
|
+
| `delete` | → `deleted/` | tombstone, not erasure |
|
|
78
|
+
| `merge` (via `mr/`) | `mr/<slug>.md` → `complete` | worker handoff path |
|
|
79
|
+
|
|
80
|
+
**Declared (v1):** each transition is a pure typed transform on the record
|
|
81
|
+
(from-model-to-model); a transition never partially rewrites a record it isn't moving.
|
|
82
|
+
|
|
83
|
+
## 4. Edge roles: queue / WAL / index (observed + declared)
|
|
84
|
+
|
|
85
|
+
Roles attach to *consumer edges*, not to directories. Current reading:
|
|
86
|
+
|
|
87
|
+
- **Queue (future):** `pending/` → worker edge. `start` is the claim; `active/` holds the
|
|
88
|
+
lease; `completed/`, `deleted/` are terminal states. Single-worker today, so the claim
|
|
89
|
+
protocol is trivially safe; multi-agent use makes atomic-rename claiming load-bearing.
|
|
90
|
+
- **WAL (past):** `completed/YYYY/` is the fact log of the task machine — append-only,
|
|
91
|
+
year-partitioned segments, source of truth for history. The pending
|
|
92
|
+
month-sharding+WAL+compaction task extends exactly this role.
|
|
93
|
+
- **Index (present):** the USV issues index (with `datapackage` field declarations in
|
|
94
|
+
`manager.py`) is a fold over the station tree; blocked-state computation is a derived
|
|
95
|
+
view over `blocked_by` edges. Both must be rebuildable by rescanning stations —
|
|
96
|
+
**stations are the source of truth; the index is disposable.**
|
|
97
|
+
- **Event log:** `AuditLog` (`src/taskagent/audit.py`) writes JSONL lifecycle events to
|
|
98
|
+
`.task-agent/logs/YYYY-MM-DD.jsonl`, pruned after 30 days.
|
|
99
|
+
**Ratified:** `AuditLog` is **telemetry**, not a WAL. The station directories are the
|
|
100
|
+
single source of truth for task state; the JSONL log is observability data only. Pruning
|
|
101
|
+
after 30 days is correct and intentional. No compaction policy is needed.
|
|
102
|
+
|
|
103
|
+
## 5. Identity (ratified)
|
|
104
|
+
|
|
105
|
+
The **directory name (slug)** is the immutable unique identifier of a task, assigned at
|
|
106
|
+
creation from the title and never subsequently derived from mutable fields.
|
|
107
|
+
|
|
108
|
+
- Editing the `# Title` heading in `README.md` changes the task's *display name* only;
|
|
109
|
+
the slug (and therefore its station path) is unchanged.
|
|
110
|
+
- Direct directory renaming is **forbidden** as an end-user operation — it breaks the
|
|
111
|
+
index, `blocked_by`/`subtask_of` references in other tasks, and `completed/` history.
|
|
112
|
+
- A future `ta rename <slug> <new-title>` command is the only supported path for slug
|
|
113
|
+
migration. It must: rename the directory, update `mission.usv`, and rewrite all
|
|
114
|
+
`blocked_by`/`subtask_of` references that point to the old slug.
|
|
115
|
+
|
|
116
|
+
## 6. Conformance gaps (the migration backlog)
|
|
117
|
+
|
|
118
|
+
Each accepted gap becomes one follow-up task. Written as typed defects against the
|
|
119
|
+
substrate's invariants:
|
|
120
|
+
|
|
121
|
+
1. **Identity derived from mutable content.** Slug comes from the *original* title;
|
|
122
|
+
retitling a task changes its display name but not its identity, and lookups by new
|
|
123
|
+
title fail. Invariant: identity is assigned at creation and never derived from
|
|
124
|
+
mutable fields. Fix: add a `ta rename` command (see §5).
|
|
125
|
+
2. **Schema data outside the schema.** `blocked_by`/`subtask_of` live as regex-matched
|
|
126
|
+
prose lines. Move to frontmatter (§1 declared); keep the alias reader through migration.
|
|
127
|
+
3. **Non-atomic record updates.** Content updates (`update_task`) rewrite the body and
|
|
128
|
+
silently drop edges — a read-modify-write that loses fields the writer didn't know
|
|
129
|
+
about. Invariant: a record update is a whole-record typed transform; unknown fields
|
|
130
|
+
round-trip. (This is the bug that repeatedly wiped dependencies on the cocli epic.)
|
|
131
|
+
4. **Station store excluded from its own durability.** `.task-agent` is gitignored in
|
|
132
|
+
consumer repos, so `commit_tasks` fails and task state stays uncommitted. Declare the
|
|
133
|
+
durability contract: either the station tree is committed (un-ignore) or durability is
|
|
134
|
+
explicitly delegated elsewhere.
|
|
135
|
+
5. **Edge-type conflation cleanup.** `blocked_by` (ordering) vs `subtask_of` (hierarchy)
|
|
136
|
+
are now separate in the `Issue` model with a legacy alias — finish the migration:
|
|
137
|
+
stored prose lines, docstrings (`create_task` still describes deps as parent-of
|
|
138
|
+
promotion cascade), and historical records under `completed/`.
|
|
139
|
+
6. **Undeclared station resolved.** `mr/` declared in §2a.
|
|
140
|
+
7. **Log role declared.** `AuditLog` is telemetry (§4).
|
|
141
|
+
8. **Index rebuildability unproven.** No command exists to rebuild the USV index from a
|
|
142
|
+
station rescan and verify it matches. This is also the seed of the conformance tool:
|
|
143
|
+
`taskagent audit --conformance` = validate station tree against this map + rebuild
|
|
144
|
+
index and diff. That tool, once built, becomes the executable `done_check` for every
|
|
145
|
+
subsequent conformance transform (the portable-task acceptance predicate).
|
|
146
|
+
|
|
147
|
+
## 7. Relationship to the library spec
|
|
148
|
+
|
|
149
|
+
task-agent is **dogfood consumer #2** (after cocli). The library spec owns: station/
|
|
150
|
+
transition/edge-role vocabulary, the claim protocol, WAL/compaction semantics,
|
|
151
|
+
fold/watermark semantics, and schema versioning. This map owns only: which stations
|
|
152
|
+
exist here, their record types, and this repo's conformance state. When the library's
|
|
153
|
+
Protocol interfaces land, task-agent's `manager.py` should be refactorable to implement
|
|
154
|
+
them with no on-disk change — that refactor being cheap is the test that this map is true.
|
|
@@ -35,7 +35,7 @@ Task Agent supports two ways to store task content:
|
|
|
35
35
|
|
|
36
36
|
## 🔗 Dependencies
|
|
37
37
|
|
|
38
|
-
Tasks can
|
|
38
|
+
Tasks can specify ordering dependencies using the `**Blocked by:** slug1, slug2` syntax within their Markdown descriptions. Task Agent uses this to provide a hierarchical view in `ta list`.
|
|
39
39
|
|
|
40
40
|
## 🔒 File Protection
|
|
41
41
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "task-agent"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.9"
|
|
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.9"
|
|
43
43
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
44
44
|
serialize = ["{major}.{minor}.{patch}"]
|
|
45
45
|
search = "version = \"{current_version}\""
|
|
@@ -586,31 +586,10 @@ def cmd_search(console: Console, manager: TaskAgent, pattern: str):
|
|
|
586
586
|
matches.append(i)
|
|
587
587
|
|
|
588
588
|
# Always search completed tasks too
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
for f in year_dir.glob("*.md"):
|
|
594
|
-
if fuzzy_match(f.stem, pat_norm):
|
|
595
|
-
name = manager.extract_title(f)
|
|
596
|
-
matches.append(
|
|
597
|
-
Issue(
|
|
598
|
-
name=name, slug=f.stem, status="completed", priority=0
|
|
599
|
-
)
|
|
600
|
-
)
|
|
601
|
-
for d in year_dir.iterdir():
|
|
602
|
-
if d.is_dir():
|
|
603
|
-
readme = d / "README.md"
|
|
604
|
-
if readme.exists() and fuzzy_match(d.name, pat_norm):
|
|
605
|
-
name = manager.extract_title(readme)
|
|
606
|
-
matches.append(
|
|
607
|
-
Issue(
|
|
608
|
-
name=name,
|
|
609
|
-
slug=d.name,
|
|
610
|
-
status="completed",
|
|
611
|
-
priority=0,
|
|
612
|
-
)
|
|
613
|
-
)
|
|
589
|
+
for f, slug in manager.walk_completed():
|
|
590
|
+
if fuzzy_match(slug, pat_norm):
|
|
591
|
+
name = manager.extract_title(f)
|
|
592
|
+
matches.append(Issue(name=name, slug=slug, status="completed", priority=0))
|
|
614
593
|
|
|
615
594
|
if not matches:
|
|
616
595
|
console.print(f"[yellow]No issues match pattern '{pattern}'.[/yellow]")
|
|
@@ -703,46 +682,20 @@ def cmd_search(console: Console, manager: TaskAgent, pattern: str):
|
|
|
703
682
|
issues = manager.load_mission()
|
|
704
683
|
matches = [i for i in issues if fuzzy_match(i.slug, pat_norm)]
|
|
705
684
|
# Also re-search completed
|
|
706
|
-
completed_root = manager.issues_root / "completed"
|
|
707
685
|
new_matches: List[Issue] = list(matches)
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
686
|
+
for f, slug in manager.walk_completed():
|
|
687
|
+
if fuzzy_match(slug, pat_norm) and not any(
|
|
688
|
+
m.slug == slug for m in new_matches
|
|
711
689
|
):
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
slug=f.stem,
|
|
722
|
-
status="completed",
|
|
723
|
-
priority=0,
|
|
724
|
-
)
|
|
725
|
-
)
|
|
726
|
-
for d in year_dir.iterdir():
|
|
727
|
-
if d.is_dir():
|
|
728
|
-
readme = d / "README.md"
|
|
729
|
-
if (
|
|
730
|
-
readme.exists()
|
|
731
|
-
and fuzzy_match(d.name, pat_norm)
|
|
732
|
-
and not any(
|
|
733
|
-
m.slug == d.name
|
|
734
|
-
for m in new_matches
|
|
735
|
-
)
|
|
736
|
-
):
|
|
737
|
-
name = manager.extract_title(readme)
|
|
738
|
-
new_matches.append(
|
|
739
|
-
Issue(
|
|
740
|
-
name=name,
|
|
741
|
-
slug=d.name,
|
|
742
|
-
status="completed",
|
|
743
|
-
priority=0,
|
|
744
|
-
)
|
|
745
|
-
)
|
|
690
|
+
name = manager.extract_title(f)
|
|
691
|
+
new_matches.append(
|
|
692
|
+
Issue(
|
|
693
|
+
name=name,
|
|
694
|
+
slug=slug,
|
|
695
|
+
status="completed",
|
|
696
|
+
priority=0,
|
|
697
|
+
)
|
|
698
|
+
)
|
|
746
699
|
matches = new_matches
|
|
747
700
|
if cursor >= len(matches):
|
|
748
701
|
cursor = max(0, len(matches) - 1)
|
|
@@ -766,23 +719,7 @@ def cmd_search(console: Console, manager: TaskAgent, pattern: str):
|
|
|
766
719
|
|
|
767
720
|
def cmd_history(console: Console, manager: TaskAgent, limit: int = 20):
|
|
768
721
|
"""Show completed tasks in reverse chronological order."""
|
|
769
|
-
|
|
770
|
-
if not completed_root.exists():
|
|
771
|
-
console.print("[yellow]No completed tasks found.[/yellow]")
|
|
772
|
-
return
|
|
773
|
-
|
|
774
|
-
all_completed: List[Tuple[Path, str]] = []
|
|
775
|
-
|
|
776
|
-
for year_dir in completed_root.iterdir():
|
|
777
|
-
if not year_dir.is_dir():
|
|
778
|
-
continue
|
|
779
|
-
for f in year_dir.glob("*.md"):
|
|
780
|
-
all_completed.append((f, f.stem))
|
|
781
|
-
for d in year_dir.iterdir():
|
|
782
|
-
if d.is_dir():
|
|
783
|
-
readme = d / "README.md"
|
|
784
|
-
if readme.exists():
|
|
785
|
-
all_completed.append((readme, d.name))
|
|
722
|
+
all_completed = manager.walk_completed()
|
|
786
723
|
|
|
787
724
|
if not all_completed:
|
|
788
725
|
console.print("[yellow]No completed tasks found.[/yellow]")
|
|
@@ -1739,7 +1676,6 @@ def cmd_new(
|
|
|
1739
1676
|
title: Optional[str],
|
|
1740
1677
|
body: str,
|
|
1741
1678
|
draft: bool,
|
|
1742
|
-
depends_on: Optional[str] = None,
|
|
1743
1679
|
as_dir: bool = True,
|
|
1744
1680
|
completion_criteria: Optional[str] = None,
|
|
1745
1681
|
interactive: bool = False,
|
|
@@ -1775,7 +1711,7 @@ def cmd_new(
|
|
|
1775
1711
|
|
|
1776
1712
|
t_body = t.get("body", "")
|
|
1777
1713
|
t_draft = t.get("draft", draft)
|
|
1778
|
-
t_blocked_by = t.get("blocked_by")
|
|
1714
|
+
t_blocked_by = t.get("blocked_by")
|
|
1779
1715
|
t_subtask_of = t.get("subtask_of")
|
|
1780
1716
|
t_as_dir = t.get("as_dir", as_dir)
|
|
1781
1717
|
|
|
@@ -1783,7 +1719,6 @@ def cmd_new(
|
|
|
1783
1719
|
title=t_title,
|
|
1784
1720
|
body=t_body,
|
|
1785
1721
|
draft=t_draft,
|
|
1786
|
-
depends_on=t_blocked_by,
|
|
1787
1722
|
as_dir=t_as_dir,
|
|
1788
1723
|
completion_criteria=t_criteria,
|
|
1789
1724
|
blocked_by=t_blocked_by,
|
|
@@ -1809,7 +1744,7 @@ def cmd_new(
|
|
|
1809
1744
|
temp_file = temp_dir / "README.md"
|
|
1810
1745
|
|
|
1811
1746
|
# Resolve relations for the template
|
|
1812
|
-
final_blocked_by = blocked_by
|
|
1747
|
+
final_blocked_by = blocked_by or ""
|
|
1813
1748
|
final_subtask_of = subtask_of or ""
|
|
1814
1749
|
|
|
1815
1750
|
created_at = datetime.now().astimezone().isoformat()
|
|
@@ -1856,7 +1791,6 @@ created_at: {created_at}
|
|
|
1856
1791
|
title=title,
|
|
1857
1792
|
body=body,
|
|
1858
1793
|
draft=draft,
|
|
1859
|
-
depends_on=depends_on,
|
|
1860
1794
|
as_dir=as_dir,
|
|
1861
1795
|
completion_criteria=completion_criteria,
|
|
1862
1796
|
blocked_by=blocked_by,
|
|
@@ -2142,7 +2076,6 @@ def cmd_update(
|
|
|
2142
2076
|
console: Console,
|
|
2143
2077
|
manager: TaskAgent,
|
|
2144
2078
|
slug_part: str,
|
|
2145
|
-
depends_on: Optional[str] = None,
|
|
2146
2079
|
blocked_by: Optional[str] = None,
|
|
2147
2080
|
subtask_of: Optional[str] = None,
|
|
2148
2081
|
):
|
|
@@ -2159,10 +2092,9 @@ def cmd_update(
|
|
|
2159
2092
|
|
|
2160
2093
|
updated = False
|
|
2161
2094
|
try:
|
|
2162
|
-
# Handle blocked_by
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
manager.update_dependencies(target.slug, final_blocked_by)
|
|
2095
|
+
# Handle blocked_by
|
|
2096
|
+
if blocked_by is not None:
|
|
2097
|
+
manager.update_dependencies(target.slug, blocked_by)
|
|
2166
2098
|
console.print(
|
|
2167
2099
|
f"[bold green]Successfully updated prerequisites for task '{target.slug}'.[/bold green]"
|
|
2168
2100
|
)
|
|
@@ -3258,28 +3190,11 @@ def cmd_triage(
|
|
|
3258
3190
|
if completed:
|
|
3259
3191
|
# Load completed issues from disk since they aren't in mission.usv
|
|
3260
3192
|
all_issues = []
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
for f in sorted(year_dir.glob("*.md")):
|
|
3267
|
-
slug = f.stem
|
|
3268
|
-
name = manager.extract_title(f)
|
|
3269
|
-
all_issues.append(
|
|
3270
|
-
Issue(
|
|
3271
|
-
name=name, slug=slug, status="completed", priority=0
|
|
3272
|
-
)
|
|
3273
|
-
)
|
|
3274
|
-
# Directory-based
|
|
3275
|
-
for d in sorted(year_dir.glob("*/README.md")):
|
|
3276
|
-
slug = d.parent.name
|
|
3277
|
-
name = manager.extract_title(d)
|
|
3278
|
-
all_issues.append(
|
|
3279
|
-
Issue(
|
|
3280
|
-
name=name, slug=slug, status="completed", priority=0
|
|
3281
|
-
)
|
|
3282
|
-
)
|
|
3193
|
+
for f, slug in manager.walk_completed():
|
|
3194
|
+
name = manager.extract_title(f)
|
|
3195
|
+
all_issues.append(
|
|
3196
|
+
Issue(name=name, slug=slug, status="completed", priority=0)
|
|
3197
|
+
)
|
|
3283
3198
|
issues = all_issues
|
|
3284
3199
|
else:
|
|
3285
3200
|
issues = manager.sync_mission()
|
|
@@ -3864,11 +3779,6 @@ def main():
|
|
|
3864
3779
|
"update", help="Update task properties (e.g. relationships)"
|
|
3865
3780
|
)
|
|
3866
3781
|
update_parser.add_argument("slug", help="Slug of the task to update")
|
|
3867
|
-
update_parser.add_argument(
|
|
3868
|
-
"-d",
|
|
3869
|
-
"--depends-on",
|
|
3870
|
-
help="Comma-separated list of task slugs this task depends on (use empty string to clear). Deprecated: use --blocked-by instead.",
|
|
3871
|
-
)
|
|
3872
3782
|
update_parser.add_argument(
|
|
3873
3783
|
"--blocked-by",
|
|
3874
3784
|
help="Comma-separated list of prerequisite task slugs that block this task (use empty string to clear)",
|
|
@@ -4087,10 +3997,6 @@ Usage:
|
|
|
4087
3997
|
new_parser.add_argument(
|
|
4088
3998
|
"-i", "--interactive", action="store_true", help="Open editor to fill in task"
|
|
4089
3999
|
)
|
|
4090
|
-
new_parser.add_argument(
|
|
4091
|
-
"--depends-on",
|
|
4092
|
-
help="Comma-separated slugs this task depends on. Deprecated: use --blocked-by instead.",
|
|
4093
|
-
)
|
|
4094
4000
|
new_parser.add_argument(
|
|
4095
4001
|
"--blocked-by",
|
|
4096
4002
|
help="Comma-separated slugs of prerequisite tasks that block this task, e.g. 'setup-ci,build-artifacts'.",
|
|
@@ -4183,7 +4089,6 @@ Usage:
|
|
|
4183
4089
|
console,
|
|
4184
4090
|
manager,
|
|
4185
4091
|
args.slug,
|
|
4186
|
-
depends_on=args.depends_on,
|
|
4187
4092
|
blocked_by=args.blocked_by,
|
|
4188
4093
|
subtask_of=args.subtask_of,
|
|
4189
4094
|
)
|
|
@@ -4254,7 +4159,6 @@ Usage:
|
|
|
4254
4159
|
title=args.title,
|
|
4255
4160
|
body=args.body,
|
|
4256
4161
|
draft=args.draft,
|
|
4257
|
-
depends_on=args.depends_on,
|
|
4258
4162
|
as_dir=not args.file,
|
|
4259
4163
|
completion_criteria=args.criteria,
|
|
4260
4164
|
interactive=args.interactive,
|