ace-git-worktree 0.26.1 → 0.27.0
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.
- checksums.yaml +4 -4
- data/.ace-defaults/nav/protocols/skill-sources/ace-git-worktree.yml +0 -0
- data/.ace-defaults/nav/protocols/wfi-sources/ace-git-worktree.yml +0 -0
- data/CHANGELOG.md +41 -0
- data/LICENSE +0 -0
- data/README.md +0 -0
- data/Rakefile +0 -0
- data/docs/demo/ace-git-worktree-getting-started.gif +0 -0
- data/docs/demo/ace-git-worktree-getting-started.tape.yml +0 -0
- data/docs/demo/fixtures/README.md +0 -0
- data/docs/demo/fixtures/sample.txt +0 -0
- data/docs/getting-started.md +7 -4
- data/docs/handbook.md +0 -0
- data/docs/usage.md +141 -24
- data/handbook/agents/worktree.ag.md +10 -6
- data/handbook/skills/as-git-worktree/SKILL.md +0 -0
- data/handbook/skills/as-git-worktree-cleanup/SKILL.md +0 -0
- data/handbook/skills/as-git-worktree-create/SKILL.md +0 -0
- data/handbook/skills/as-git-worktree-manage/SKILL.md +0 -0
- data/handbook/workflow-instructions/git/worktree-create.wf.md +3 -3
- data/handbook/workflow-instructions/git/worktree-manage.wf.md +6 -6
- data/handbook/workflow-instructions/git/worktree.wf.md +2 -2
- data/lib/ace/git/worktree/atoms/duration_parser.rb +81 -0
- data/lib/ace/git/worktree/atoms/git_command.rb +0 -0
- data/lib/ace/git/worktree/atoms/path_expander.rb +0 -0
- data/lib/ace/git/worktree/atoms/relative_age_formatter.rb +59 -0
- data/lib/ace/git/worktree/atoms/slug_generator.rb +0 -0
- data/lib/ace/git/worktree/atoms/task_id_extractor.rb +20 -1
- data/lib/ace/git/worktree/atoms/text_truncator.rb +67 -0
- data/lib/ace/git/worktree/cli/commands/bootstrap.rb +0 -0
- data/lib/ace/git/worktree/cli/commands/cleanup.rb +0 -0
- data/lib/ace/git/worktree/cli/commands/config.rb +0 -0
- data/lib/ace/git/worktree/cli/commands/create.rb +0 -0
- data/lib/ace/git/worktree/cli/commands/list.rb +29 -8
- data/lib/ace/git/worktree/cli/commands/prune.rb +0 -0
- data/lib/ace/git/worktree/cli/commands/remove.rb +0 -0
- data/lib/ace/git/worktree/cli/commands/shared_helpers.rb +0 -0
- data/lib/ace/git/worktree/cli/commands/switch.rb +0 -0
- data/lib/ace/git/worktree/cli.rb +1 -1
- data/lib/ace/git/worktree/commands/bootstrap_command.rb +0 -0
- data/lib/ace/git/worktree/commands/list_command.rb +90 -94
- data/lib/ace/git/worktree/commands/prune_command.rb +0 -0
- data/lib/ace/git/worktree/commands/remove_command.rb +0 -0
- data/lib/ace/git/worktree/commands/switch_command.rb +0 -0
- data/lib/ace/git/worktree/configuration.rb +0 -0
- data/lib/ace/git/worktree/models/task_index.rb +171 -0
- data/lib/ace/git/worktree/models/worktree_info.rb +52 -3
- data/lib/ace/git/worktree/models/worktree_metadata.rb +0 -0
- data/lib/ace/git/worktree/molecules/bootstrap_executor.rb +0 -0
- data/lib/ace/git/worktree/molecules/cleanup_applier.rb +0 -0
- data/lib/ace/git/worktree/molecules/config_loader.rb +0 -0
- data/lib/ace/git/worktree/molecules/current_task_linker.rb +0 -0
- data/lib/ace/git/worktree/molecules/hook_executor.rb +0 -0
- data/lib/ace/git/worktree/molecules/parent_task_resolver.rb +0 -0
- data/lib/ace/git/worktree/molecules/pr_status_resolver.rb +172 -0
- data/lib/ace/git/worktree/molecules/recent_branch_counter.rb +70 -0
- data/lib/ace/git/worktree/molecules/task_association_resolver.rb +121 -0
- data/lib/ace/git/worktree/molecules/task_fetcher.rb +0 -0
- data/lib/ace/git/worktree/molecules/task_index_builder.rb +121 -0
- data/lib/ace/git/worktree/molecules/task_pusher.rb +0 -0
- data/lib/ace/git/worktree/molecules/task_status_updater.rb +0 -0
- data/lib/ace/git/worktree/molecules/toolchain_truster.rb +0 -0
- data/lib/ace/git/worktree/molecules/worktree_footer_formatter.rb +43 -0
- data/lib/ace/git/worktree/molecules/worktree_lister.rb +22 -93
- data/lib/ace/git/worktree/molecules/worktree_probe.rb +176 -0
- data/lib/ace/git/worktree/molecules/worktree_remover.rb +0 -0
- data/lib/ace/git/worktree/molecules/worktree_table_formatter.rb +236 -0
- data/lib/ace/git/worktree/organisms/worktree_list_report.rb +277 -0
- data/lib/ace/git/worktree/organisms/worktree_manager.rb +22 -20
- data/lib/ace/git/worktree/organisms/worktree_probe_runner.rb +320 -0
- data/lib/ace/git/worktree/version.rb +1 -1
- data/lib/ace/git/worktree.rb +14 -0
- metadata +15 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8cc9f7055655e28b11fd7caa92051817825c8487316237972859985aca1d2478
|
|
4
|
+
data.tar.gz: dba29a6ffef411ec95e890d6521aca30385f823bb3bb52827bad86a4fe8fd6ba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 776ccc9af30e1dc2027d9a27933eefe6589e33c4e4ab3a23a11d3d3e37ef238a1e360480d1ac60739438277e37bf8c27586438588bd6efcc39a7e22010558606
|
|
7
|
+
data.tar.gz: 633565bd32222e69ba61d521a3f7d110f28a197c6f7790061eeb131acf5bd714cd302c5ae5558adfb03d7880daf6db8eb5ef2a02d02da4eb9a099e3120bdfea3
|
|
File without changes
|
|
File without changes
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.27.0] - 2026-10-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `ace-git-worktree list` now defaults to a compact active-worktree table: one
|
|
15
|
+
enriched row per worktree whose last commit or uncommitted changes fall inside
|
|
16
|
+
the activity window (`--active-within`, default `18h`), plus a two-line count
|
|
17
|
+
footer. Columns: Task, Branch, Age, Commit, Changes, PR, Path.
|
|
18
|
+
- `--full` lists every worktree in the same columns with absolute, untruncated
|
|
19
|
+
paths; `--compact` is the explicit default and is mutually exclusive with
|
|
20
|
+
`--full`.
|
|
21
|
+
- Name-independent task association with provenance: recorded task metadata,
|
|
22
|
+
the `_current` symlink, validated path/branch names, then task files modified
|
|
23
|
+
in the worktree (uncommitted first, then commits vs the primary branch
|
|
24
|
+
merge-base). Ambiguous inference renders `<id>+N` and exposes all candidates.
|
|
25
|
+
- PR resolution through one bounded forge query per run: branch match, then HEAD
|
|
26
|
+
commit sha, then a unique task reference in the PR title/body.
|
|
27
|
+
- Per-worktree probing in isolated forked children: at most 16 live children,
|
|
28
|
+
5 second per-probe budget, process-group kill on timeout, deterministic
|
|
29
|
+
`git worktree list` order, and a sequential fallback where `fork` is
|
|
30
|
+
unavailable. Probe failures render `?` without failing the command.
|
|
31
|
+
- Width-aware table layout driven by `COLUMNS`/the terminal size: columns shrink
|
|
32
|
+
in priority order (Path, Commit, Branch) and Path/Commit drop on narrow
|
|
33
|
+
terminals; compact output never exceeds the detected width.
|
|
34
|
+
- `--format json` additions per selected worktree: `task_id_source`, `task_ids`,
|
|
35
|
+
`active_within_window`, `last_commit`, `uncommitted`, and `pr` with `state`,
|
|
36
|
+
`draft`, and `matched_by`. Existing keys are unchanged.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- `ace-git-worktree list` output is now the active-worktree table; the legacy
|
|
41
|
+
plain Task/Branch/Path/Status table and the `Summary:` block are gone.
|
|
42
|
+
- Invalid `--active-within` values, mutually exclusive detail flags, and removed
|
|
43
|
+
flags fail with actionable one-line errors and a non-zero exit.
|
|
44
|
+
|
|
45
|
+
### Removed
|
|
46
|
+
|
|
47
|
+
- `--show-tasks`: the Task column is always present and association is always
|
|
48
|
+
inferred. Passing `--show-tasks` now fails as an unknown option (ADR-024, no
|
|
49
|
+
compatibility layer).
|
|
50
|
+
|
|
10
51
|
## [0.26.1] - 2026-10-10
|
|
11
52
|
|
|
12
53
|
### Fixed
|
data/LICENSE
CHANGED
|
File without changes
|
data/README.md
CHANGED
|
File without changes
|
data/Rakefile
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
data/docs/getting-started.md
CHANGED
|
@@ -53,10 +53,13 @@ This keeps your shell history simple while avoiding hard-coded worktree paths.
|
|
|
53
53
|
List worktrees with task context when you want a quick overview:
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
|
-
ace-git-worktree list
|
|
56
|
+
ace-git-worktree list
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
The default view shows only worktrees active in the last 18 hours, with task,
|
|
60
|
+
age, last commit, uncommitted changes, and PR context. Use `--full` for an
|
|
61
|
+
exhaustive listing, `--active-within 3d` for a wider window, `--search auth` to
|
|
62
|
+
filter, or `--format json` for machine-readable output.
|
|
60
63
|
|
|
61
64
|
## 4. Create a PR Worktree
|
|
62
65
|
|
|
@@ -102,7 +105,7 @@ Add hooks, auto-push, terminal launch, or PR automation only after the basic cre
|
|
|
102
105
|
| Create a task worktree | `ace-git-worktree create --task 081` |
|
|
103
106
|
| Create a PR worktree | `ace-git-worktree create --pr 26` |
|
|
104
107
|
| Switch into a worktree | `cd "$(ace-git-worktree switch 081)"` |
|
|
105
|
-
| List worktrees with task info | `ace-git-worktree list
|
|
108
|
+
| List active worktrees with task info | `ace-git-worktree list` |
|
|
106
109
|
| Remove a task worktree | `ace-git-worktree remove --task 081` |
|
|
107
110
|
| Preview stale cleanup | `ace-git-worktree prune --dry-run` |
|
|
108
111
|
|
|
@@ -128,7 +131,7 @@ ace-git-worktree list
|
|
|
128
131
|
### Task filtering behavior
|
|
129
132
|
|
|
130
133
|
```bash
|
|
131
|
-
ace-git-worktree list
|
|
134
|
+
ace-git-worktree list
|
|
132
135
|
ace-git-worktree list --task-associated
|
|
133
136
|
ace-git-worktree list --no-task-associated
|
|
134
137
|
```
|
data/docs/handbook.md
CHANGED
|
File without changes
|
data/docs/usage.md
CHANGED
|
@@ -3,8 +3,8 @@ doc-type: user
|
|
|
3
3
|
title: ace-git-worktree CLI Usage Reference
|
|
4
4
|
purpose: Command reference for ace-git-worktree
|
|
5
5
|
ace-docs:
|
|
6
|
-
last-updated: 2026-
|
|
7
|
-
last-checked: 2026-
|
|
6
|
+
last-updated: 2026-10-17
|
|
7
|
+
last-checked: 2026-10-17
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# ace-git-worktree CLI Usage Reference
|
|
@@ -38,7 +38,7 @@ Create a task worktree, list active worktrees, and resolve the path for shell na
|
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
ace-git-worktree create --task 081
|
|
41
|
-
ace-git-worktree list
|
|
41
|
+
ace-git-worktree list
|
|
42
42
|
ace-git-worktree switch 081
|
|
43
43
|
```
|
|
44
44
|
|
|
@@ -62,14 +62,14 @@ Success looks like a new worktree directory exists and `switch` returns a path y
|
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
64
|
ace-git-worktree create --task 081
|
|
65
|
-
ace-git-worktree list
|
|
65
|
+
ace-git-worktree list
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
**Expected output:**
|
|
69
69
|
|
|
70
70
|
```text
|
|
71
71
|
Created task worktree and branch for 081
|
|
72
|
-
... task
|
|
72
|
+
... task 081 appears in the active worktree table ...
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
**Next steps:** Run `cd "$(ace-git-worktree switch 081)"` to move into the new worktree.
|
|
@@ -143,7 +143,7 @@ ace-git-worktree cleanup --target main --apply --approved-digest <sha256-digest>
|
|
|
143
143
|
**Commands:**
|
|
144
144
|
|
|
145
145
|
```bash
|
|
146
|
-
ace-git-worktree list
|
|
146
|
+
ace-git-worktree list
|
|
147
147
|
ace-git-worktree list --task-associated
|
|
148
148
|
ace-git-worktree list --no-task-associated
|
|
149
149
|
```
|
|
@@ -151,9 +151,50 @@ ace-git-worktree list --no-task-associated
|
|
|
151
151
|
**Expected output:**
|
|
152
152
|
|
|
153
153
|
```text
|
|
154
|
-
...
|
|
155
|
-
... --task-associated
|
|
156
|
-
... --no-task-associated shows
|
|
154
|
+
... the default view lists only worktrees active in the last 18h ...
|
|
155
|
+
... --task-associated keeps only worktrees with a resolved task id ...
|
|
156
|
+
... --no-task-associated shows only worktrees without a task id ...
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Scenario 4: Find Work Without Meaningful Worktree Names
|
|
160
|
+
|
|
161
|
+
**Goal:** A worktree created by another tool has a path/branch that names no
|
|
162
|
+
task. Still identify its task and PR.
|
|
163
|
+
|
|
164
|
+
**Commands:**
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
ace-git-worktree list
|
|
168
|
+
ace-git-worktree list --format json
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Expected output:**
|
|
172
|
+
|
|
173
|
+
```text
|
|
174
|
+
... the Task cell shows the task whose .ace-tasks files were modified there ...
|
|
175
|
+
... `ace-git-worktree list --format json` reports task_id_source=task_files ...
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The PR cell resolves by branch name, then by HEAD commit, then by a unique task
|
|
179
|
+
reference in the PR title/body; `null`/`-` means no match. The JSON entry exposes
|
|
180
|
+
`pr.matched_by` (`branch`, `head_sha`, or `task_reference`).
|
|
181
|
+
|
|
182
|
+
### Scenario 5: Exhaustive Listing and Custom Window
|
|
183
|
+
|
|
184
|
+
**Goal:** Inspect every worktree, or widen the activity window after a weekend.
|
|
185
|
+
|
|
186
|
+
**Commands:**
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
ace-git-worktree list --full
|
|
190
|
+
ace-git-worktree list --active-within 3d
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Expected output:**
|
|
194
|
+
|
|
195
|
+
```text
|
|
196
|
+
... --full lists every worktree with absolute paths ...
|
|
197
|
+
... --active-within 3d reads "Active worktrees (last 3d) -- N of M:" ...
|
|
157
198
|
```
|
|
158
199
|
|
|
159
200
|
### Scenario 5: Remove by Task and Delete Branch
|
|
@@ -216,33 +257,109 @@ ace-git-worktree create --task 081 --dry-run
|
|
|
216
257
|
|
|
217
258
|
### `ace-git-worktree list`
|
|
218
259
|
|
|
219
|
-
List active
|
|
260
|
+
List worktrees as an active-worktree table with task, activity, dirty-state, and
|
|
261
|
+
PR context. By default only worktrees active inside the activity window are
|
|
262
|
+
rows; everything else is collapsed into counts.
|
|
220
263
|
|
|
221
264
|
```bash
|
|
222
265
|
ace-git-worktree list
|
|
223
|
-
ace-git-worktree list --
|
|
266
|
+
ace-git-worktree list --full
|
|
267
|
+
ace-git-worktree list --active-within 3d
|
|
224
268
|
ace-git-worktree list --format json
|
|
269
|
+
ace-git-worktree list --task-associated
|
|
225
270
|
ace-git-worktree list --search auth
|
|
226
271
|
```
|
|
227
272
|
|
|
228
273
|
**Options:**
|
|
229
274
|
|
|
230
|
-
* `--
|
|
231
|
-
* `--
|
|
232
|
-
* `--
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
* `--
|
|
236
|
-
*
|
|
275
|
+
* `--compact` - Active worktrees only (the default; mutually exclusive with `--full`)
|
|
276
|
+
* `--full` - List every worktree in the same columns with absolute paths
|
|
277
|
+
* `--active-within <duration>` - Activity window: `30m`, `18h`, `2d`, or a bare
|
|
278
|
+
integer (hours). Default `18h`. Invalid, empty, zero, or negative values fail
|
|
279
|
+
with a non-zero exit.
|
|
280
|
+
* `--format` - Output format: `table` (default), `json`, or `simple`
|
|
281
|
+
* `--task-associated` / `--no-task-associated` - Filter by resolved task id
|
|
282
|
+
* `--usable` / `--no-usable` - Filter by usable (not bare/detached) state
|
|
283
|
+
* `--search <pattern>` - Filter by branch-name pattern
|
|
284
|
+
* `-q`, `--quiet` - Suppress the count footer
|
|
237
285
|
* `-v`, `--verbose` - Show verbose output
|
|
238
286
|
* `-d`, `--debug` - Show debug output
|
|
239
287
|
|
|
240
|
-
|
|
288
|
+
There is no `--show-tasks` flag: the Task column is always present and task
|
|
289
|
+
association is always inferred.
|
|
290
|
+
|
|
291
|
+
**Default output shape:**
|
|
292
|
+
|
|
293
|
+
```text
|
|
294
|
+
Active worktrees (last 18h) -- 3 of 88:
|
|
295
|
+
Task Branch Age Commit Changes PR Path
|
|
296
|
+
----------------------------------------------------------------------------------------------
|
|
297
|
+
k9y codex/r3-bounded-convergence 17h feat(review): define bo… 2 #142 draft …/codex-r3-bounded-convergence
|
|
298
|
+
- codex/ei9-campaign-join 8m feat(ace-assign): compl… - - …/ei9-campaign-composition-join
|
|
299
|
+
|
|
300
|
+
Inactive: 85 (use --full to list all)
|
|
301
|
+
Task-associated: 14 Usable: 71 Recent branches without worktrees: 4
|
|
302
|
+
```
|
|
241
303
|
|
|
242
|
-
|
|
243
|
-
-
|
|
244
|
-
|
|
245
|
-
|
|
304
|
+
**Activity:** a worktree is active when the last commit reachable from its
|
|
305
|
+
checked-out HEAD is inside the window **or** it has uncommitted changes (staged,
|
|
306
|
+
unstaged, or untracked). Bare worktrees are never active. Filters apply before
|
|
307
|
+
the active/inactive split, so the header and footer counts always describe the
|
|
308
|
+
filtered set. `Recent branches without worktrees` is branch-based and is not
|
|
309
|
+
affected by worktree filters.
|
|
310
|
+
|
|
311
|
+
**Columns:**
|
|
312
|
+
|
|
313
|
+
* `Task` - resolved short task id; `<id>+N` when N additional candidate tasks
|
|
314
|
+
were inferred from modified task files; `-` when unresolved.
|
|
315
|
+
* `Branch` - checked-out branch (middle-elided when truncated); `detached`.
|
|
316
|
+
* `Age` - last commit age in one unit (`8m`, `3h`, `9d`, `3w`, `2mo`, `1y`);
|
|
317
|
+
`never` when the branch has no commits; future timestamps render `0m`.
|
|
318
|
+
* `Commit` - last commit subject, right-truncated with `…`.
|
|
319
|
+
* `Changes` - uncommitted file count (staged + unstaged + untracked); `-` when
|
|
320
|
+
clean.
|
|
321
|
+
* `PR` - `#N` (open), `#N draft`, `#N merged`, `#N closed`, or `-`.
|
|
322
|
+
* `Path` - `$HOME` shortened to `~` and left-elided in compact mode; absolute
|
|
323
|
+
and untruncated with `--full`.
|
|
324
|
+
|
|
325
|
+
**Task association (name-independent), in precedence order:**
|
|
326
|
+
|
|
327
|
+
1. recorded task metadata (`worktree.path` / `worktree.branch`);
|
|
328
|
+
2. the `_current` symlink resolving under `.ace-tasks/`;
|
|
329
|
+
3. a validated path/branch name extraction (rejected when it names no task);
|
|
330
|
+
4. task files modified in the worktree (uncommitted first, then commits relative
|
|
331
|
+
to the merge-base with the primary worktree's branch). The dominant task wins
|
|
332
|
+
(most changed task files; lexicographic tie-break), rendered `<id>+N`.
|
|
333
|
+
|
|
334
|
+
**PR resolution:** one bounded forge query per run. A PR is matched by branch
|
|
335
|
+
name, then by HEAD commit sha, then by a unique task reference in the PR
|
|
336
|
+
title/body. Unconfigured/offline forges render `-` and never fail the command.
|
|
337
|
+
|
|
338
|
+
**Width behavior:** the table targets `120` columns, shrinks `Path`, then
|
|
339
|
+
`Commit`, then `Branch` down to their minimums, and drops `Path` (then `Commit`)
|
|
340
|
+
on very narrow terminals. Width comes from `COLUMNS`, else the terminal size,
|
|
341
|
+
else `120`. `--full` is exempt from truncation.
|
|
342
|
+
|
|
343
|
+
**Probe isolation:** each worktree is probed in its own forked child process
|
|
344
|
+
(one child per worktree, at most 16 alive, hard 5 second budget per probe).
|
|
345
|
+
Probe results are ordered by `git worktree list` order, so output is
|
|
346
|
+
deterministic; a failed or timed-out probe renders `?` in `Age`/`Commit`/
|
|
347
|
+
`Changes` while the rest of the table renders and the command exits 0. Where
|
|
348
|
+
`fork` is unavailable, probes run sequentially with the same timeouts.
|
|
349
|
+
|
|
350
|
+
**JSON contract (`--format json`):**
|
|
351
|
+
|
|
352
|
+
- Output is a top-level array of every selected (post-filter) worktree.
|
|
353
|
+
- Existing keys stay unchanged (`path`, `branch`, `commit`, `task_id`, `bare`,
|
|
354
|
+
`detached`, `created_at`, `usable`, `task_associated`, `exists`, `empty`).
|
|
355
|
+
- Added keys: `task_id_source` (`metadata|symlink|path|branch|task_files|null`),
|
|
356
|
+
`task_ids` (all candidates), `active_within_window` (boolean),
|
|
357
|
+
`last_commit` (`sha`, `age_hours`, `committed_at`, `subject`), `uncommitted`
|
|
358
|
+
(`staged`, `unstaged`, `untracked`, `total`), and `pr` (`number`, `state` in
|
|
359
|
+
`open|merged|closed`, `draft`, `matched_by` in
|
|
360
|
+
`branch|head_sha|task_reference`).
|
|
361
|
+
- Absent sub-objects are `null` rather than omitted. Detail flags do not change
|
|
362
|
+
JSON output.
|
|
246
363
|
|
|
247
364
|
### `ace-git-worktree switch [IDENTIFIER]`
|
|
248
365
|
|
|
@@ -477,7 +594,7 @@ fall back to another provider.
|
|
|
477
594
|
**Solution:**
|
|
478
595
|
|
|
479
596
|
```bash
|
|
480
|
-
ace-git-worktree list --
|
|
597
|
+
ace-git-worktree list --full
|
|
481
598
|
```
|
|
482
599
|
|
|
483
600
|
Confirm the identifier matches a task ID, branch name, directory name, or path shown in the current worktree list.
|
|
@@ -50,8 +50,8 @@ A specialized agent for managing git worktrees with task-aware automation. Handl
|
|
|
50
50
|
# Create worktree for feature branch
|
|
51
51
|
@worktree create feature-branch
|
|
52
52
|
|
|
53
|
-
# List
|
|
54
|
-
@worktree list
|
|
53
|
+
# List active worktrees (last 18h) with task context
|
|
54
|
+
@worktree list
|
|
55
55
|
|
|
56
56
|
# Switch to worktree
|
|
57
57
|
@worktree switch feature-branch
|
|
@@ -106,10 +106,14 @@ The worktree agent accepts standard CLI parameters:
|
|
|
106
106
|
- `--no-commit`: Skip committing task changes
|
|
107
107
|
|
|
108
108
|
### list
|
|
109
|
+
- `--compact`: Active worktrees only (default)
|
|
110
|
+
- `--full`: List every worktree (including inactive) with absolute paths
|
|
111
|
+
- `--active-within <duration>`: Activity window (`30m`, `18h`, `2d`, or bare hours; default 18h)
|
|
109
112
|
- `--format <format>`: Output format (table, json, simple)
|
|
110
|
-
- `--
|
|
111
|
-
- `--
|
|
112
|
-
- `--search <pattern>`: Filter by
|
|
113
|
+
- `--task-associated` / `--no-task-associated`: Filter by resolved task id
|
|
114
|
+
- `--usable` / `--no-usable`: Filter by usability
|
|
115
|
+
- `--search <pattern>`: Filter by branch-name pattern
|
|
116
|
+
- `-q`, `--quiet`: Suppress the count footer
|
|
113
117
|
|
|
114
118
|
### switch
|
|
115
119
|
- `identifier`: Worktree identifier (task ID, branch name, directory, path)
|
|
@@ -179,7 +183,7 @@ The worktree agent provides deterministic output suitable for AI automation:
|
|
|
179
183
|
WORKTREE_PATH=$(ace-git-worktree switch 081)
|
|
180
184
|
|
|
181
185
|
# Get worktree status as JSON
|
|
182
|
-
STATUS=$(ace-git-worktree list --format json
|
|
186
|
+
STATUS=$(ace-git-worktree list --format json)
|
|
183
187
|
|
|
184
188
|
# Create worktree and verify creation
|
|
185
189
|
RESULT=$(ace-git-worktree create --task 081 --dry-run --format json)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -86,7 +86,7 @@ ace-git-worktree create feature-branch --path ~/dev-work
|
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
88
|
# List all worktrees
|
|
89
|
-
ace-git-worktree list
|
|
89
|
+
ace-git-worktree list
|
|
90
90
|
|
|
91
91
|
# Switch to the worktree
|
|
92
92
|
cd $(ace-git-worktree switch 081)
|
|
@@ -218,7 +218,7 @@ ace-git-worktree list --task-associated
|
|
|
218
218
|
ace-git-worktree prune --cleanup-directories
|
|
219
219
|
|
|
220
220
|
# Get status in JSON for scripts
|
|
221
|
-
ace-git-worktree list --format json
|
|
221
|
+
ace-git-worktree list --format json
|
|
222
222
|
```
|
|
223
223
|
|
|
224
224
|
## Error Handling
|
|
@@ -244,7 +244,7 @@ RESULT=$(ace-git-worktree create --task 081 --dry-run --format json)
|
|
|
244
244
|
echo "$RESULT" | jq '.worktree_path'
|
|
245
245
|
|
|
246
246
|
# List worktrees with task associations
|
|
247
|
-
STATUS=$(ace-git-worktree list --format json
|
|
247
|
+
STATUS=$(ace-git-worktree list --format json)
|
|
248
248
|
echo "$STATUS" | jq '.worktrees[] | select(.task_associated == true)'
|
|
249
249
|
```
|
|
250
250
|
|
|
@@ -35,7 +35,7 @@ $action: Management action (list, switch, remove, prune)
|
|
|
35
35
|
ace-git-worktree list
|
|
36
36
|
|
|
37
37
|
# List with task associations
|
|
38
|
-
ace-git-worktree list
|
|
38
|
+
ace-git-worktree list
|
|
39
39
|
|
|
40
40
|
# List in different formats
|
|
41
41
|
ace-git-worktree list --format json
|
|
@@ -158,10 +158,10 @@ ace-git-worktree config --show
|
|
|
158
158
|
|
|
159
159
|
```bash
|
|
160
160
|
# Get worktree status for all tasks
|
|
161
|
-
ace-git-worktree list --format json
|
|
161
|
+
ace-git-worktree list --format json | jq '.[]'
|
|
162
162
|
|
|
163
163
|
# Count worktrees by type
|
|
164
|
-
ace-git-worktree list --
|
|
164
|
+
ace-git-worktree list --full | grep -c "Task"
|
|
165
165
|
|
|
166
166
|
# Find worktrees with specific patterns
|
|
167
167
|
ace-git-worktree list --search "bug" --format simple
|
|
@@ -184,7 +184,7 @@ ace-git-worktree list | grep -v "bare\|detached"
|
|
|
184
184
|
|
|
185
185
|
```bash
|
|
186
186
|
# Get comprehensive status
|
|
187
|
-
ace-git-worktree list --
|
|
187
|
+
ace-git-worktree list --format json
|
|
188
188
|
|
|
189
189
|
# Check specific worktree
|
|
190
190
|
ace-git-worktree switch 081 --verbose
|
|
@@ -199,7 +199,7 @@ ace-git-worktree config --validate
|
|
|
199
199
|
|
|
200
200
|
```bash
|
|
201
201
|
# Get all worktrees as JSON
|
|
202
|
-
WORKTREES=$(ace-git-worktree list --format json
|
|
202
|
+
WORKTREES=$(ace-git-worktree list --format json)
|
|
203
203
|
|
|
204
204
|
# Extract specific information
|
|
205
205
|
echo "$WORKTREES" | jq '.worktrees[] | select(.task_id == "081")'
|
|
@@ -311,7 +311,7 @@ fi
|
|
|
311
311
|
|
|
312
312
|
# 1. Check current worktree status
|
|
313
313
|
echo "=== Current Worktree Status ==="
|
|
314
|
-
ace-git-worktree list --
|
|
314
|
+
ace-git-worktree list --format simple
|
|
315
315
|
|
|
316
316
|
# 2. Clean up if needed
|
|
317
317
|
echo "=== Cleanup Check ==="
|
|
@@ -72,7 +72,7 @@ ace-git-worktree create feature-branch --no-mise-trust
|
|
|
72
72
|
ace-git-worktree list
|
|
73
73
|
|
|
74
74
|
# Show task associations
|
|
75
|
-
ace-git-worktree list
|
|
75
|
+
ace-git-worktree list
|
|
76
76
|
|
|
77
77
|
# Filter by task association
|
|
78
78
|
ace-git-worktree list --task-associated
|
|
@@ -170,7 +170,7 @@ git:
|
|
|
170
170
|
|
|
171
171
|
### list
|
|
172
172
|
- `--format <format>`: Output format (table, json, simple)
|
|
173
|
-
- `--
|
|
173
|
+
- `--full`: List every worktree (including inactive) with absolute paths
|
|
174
174
|
- `--task-associated`: Filter by task association
|
|
175
175
|
- `--search <pattern>`: Filter by search pattern
|
|
176
176
|
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Git
|
|
5
|
+
module Worktree
|
|
6
|
+
module Atoms
|
|
7
|
+
# Parses the `--active-within` activity window into hours.
|
|
8
|
+
#
|
|
9
|
+
# Accepted forms: `Nm` (minutes), `Nh` (hours), `Nd` (days), and bare
|
|
10
|
+
# integers (interpreted as hours). The canonical label echoes the
|
|
11
|
+
# operator's unit so the table header reads the way it was requested
|
|
12
|
+
# (`3d` -> "3d", `5` -> "5h").
|
|
13
|
+
#
|
|
14
|
+
# @example Parse a window
|
|
15
|
+
# DurationParser.parse("18h") # => {hours: 18.0, label: "18h"}
|
|
16
|
+
# DurationParser.parse("90m") # => {hours: 1.5, label: "90m"}
|
|
17
|
+
# DurationParser.parse("5") # => {hours: 5.0, label: "5h"}
|
|
18
|
+
#
|
|
19
|
+
# @example Invalid window
|
|
20
|
+
# DurationParser.parse("abc") # raises ArgumentError
|
|
21
|
+
module DurationParser
|
|
22
|
+
# Default activity window label
|
|
23
|
+
DEFAULT_LABEL = "18h"
|
|
24
|
+
|
|
25
|
+
# Accepted duration shape: positive integer + optional unit
|
|
26
|
+
PATTERN = /\A(\d+)([mhd])?\z/
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
# Default activity window in hours (no unit = hours follows).
|
|
30
|
+
# @return [Hash] parsed default window
|
|
31
|
+
def default
|
|
32
|
+
parse(DEFAULT_LABEL)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Parse a duration string into hours.
|
|
36
|
+
#
|
|
37
|
+
# @param value [String, nil] raw CLI value
|
|
38
|
+
# @return [Hash] `{hours: Float, label: String}`
|
|
39
|
+
# @raise [ArgumentError] when the value is empty, zero, negative, or
|
|
40
|
+
# not one of the accepted forms
|
|
41
|
+
def parse(value)
|
|
42
|
+
raw = value.to_s.strip
|
|
43
|
+
raise invalid_duration(value) if raw.empty?
|
|
44
|
+
|
|
45
|
+
match = PATTERN.match(raw)
|
|
46
|
+
raise invalid_duration(value) unless match
|
|
47
|
+
|
|
48
|
+
amount = Integer(match[1], 10)
|
|
49
|
+
unit = match[2]
|
|
50
|
+
raise invalid_duration(value) if amount <= 0
|
|
51
|
+
|
|
52
|
+
{hours: hours_for(amount, unit), label: "#{amount}#{display_unit(unit)}"}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
# @return [Float] duration expressed in hours
|
|
58
|
+
def hours_for(amount, unit)
|
|
59
|
+
case unit
|
|
60
|
+
when "m" then amount / 60.0
|
|
61
|
+
when "d" then amount * 24.0
|
|
62
|
+
else amount.to_f # "h" or bare integer
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @return [String] unit to echo back (bare integers render as hours)
|
|
67
|
+
def display_unit(unit)
|
|
68
|
+
unit || "h"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def invalid_duration(value)
|
|
72
|
+
ArgumentError.new(
|
|
73
|
+
"invalid duration '#{value}'. Use forms like 30m, 18h, 2d, or bare hours."
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Git
|
|
5
|
+
module Worktree
|
|
6
|
+
module Atoms
|
|
7
|
+
# Formats a commit age as one compact unit for the `Age` table column.
|
|
8
|
+
#
|
|
9
|
+
# Ladder (floor of the current unit, no `ago` suffix):
|
|
10
|
+
# 0-59m -> "Nm" 1-23h -> "Nh"
|
|
11
|
+
# 1-13d -> "Nd" 14-59d -> "Nw"
|
|
12
|
+
# 60-364d -> "Nmo" >=365d -> "Ny"
|
|
13
|
+
#
|
|
14
|
+
# `nil` means the branch has no commits ("never"); clock skew/future
|
|
15
|
+
# timestamps render "0m" so the row still reads as active.
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
# RelativeAgeFormatter.format(9 * 24 * 3600) # => "9d"
|
|
19
|
+
# RelativeAgeFormatter.format(nil) # => "never"
|
|
20
|
+
module RelativeAgeFormatter
|
|
21
|
+
MINUTE = 60
|
|
22
|
+
HOUR = 60 * MINUTE
|
|
23
|
+
DAY = 24 * HOUR
|
|
24
|
+
WEEK = 7 * DAY
|
|
25
|
+
|
|
26
|
+
# Unit thresholds in seconds, largest unit last.
|
|
27
|
+
# The day->week switch is at 14 days and week->month at 60 days so
|
|
28
|
+
# short-lived worktrees keep a day count (e.g. "9d") while older
|
|
29
|
+
# ones step up to weeks ("3w") and then months ("2mo").
|
|
30
|
+
WEEK_THRESHOLD = 14 * DAY
|
|
31
|
+
MONTH_THRESHOLD = 60 * DAY
|
|
32
|
+
MONTH = 30 * DAY
|
|
33
|
+
YEAR_THRESHOLD = 365 * DAY
|
|
34
|
+
|
|
35
|
+
class << self
|
|
36
|
+
# @param age_seconds [Numeric, nil] age in seconds (nil = no commits)
|
|
37
|
+
# @return [String] compact age label
|
|
38
|
+
def format(age_seconds)
|
|
39
|
+
return "never" if age_seconds.nil?
|
|
40
|
+
|
|
41
|
+
seconds = age_seconds.to_f
|
|
42
|
+
return "0m" if seconds.negative?
|
|
43
|
+
|
|
44
|
+
case seconds
|
|
45
|
+
when 0...MINUTE then "0m"
|
|
46
|
+
when MINUTE...HOUR then "#{(seconds / MINUTE).floor}m"
|
|
47
|
+
when HOUR...DAY then "#{(seconds / HOUR).floor}h"
|
|
48
|
+
when DAY...WEEK_THRESHOLD then "#{(seconds / DAY).floor}d"
|
|
49
|
+
when WEEK_THRESHOLD...MONTH_THRESHOLD then "#{(seconds / WEEK).floor}w"
|
|
50
|
+
when MONTH_THRESHOLD...YEAR_THRESHOLD then "#{(seconds / MONTH).floor}mo"
|
|
51
|
+
else "#{(seconds / YEAR_THRESHOLD).floor}y"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
File without changes
|