Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## [Unreleased]

### Added
- `edit --children` flag to open a ticket and all its descendants in `$EDITOR`

## [0.3.1] - 2026-01-28

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Commands:
blocked [-a X] [-T X] List open/in-progress tickets with unresolved deps
closed [--limit=N] [-a X] [-T X] List recently closed tickets (default 20, by mtime)
show <id> Display ticket
edit <id> Open ticket in $EDITOR
edit <id> [--children] Open ticket in $EDITOR (--children includes descendants)
add-note <id> [text] Append timestamped note (or pipe via stdin)
query [jq-filter] Output tickets as JSON, optionally filtered
migrate-beads Import tickets from .beads/issues.jsonl
Expand Down
49 changes: 49 additions & 0 deletions features/ticket_edit.feature
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,52 @@ Feature: Ticket Edit
When I run "ticket edit 0001" in non-TTY mode
Then the command should succeed
And the output should contain "edit-0001.md"

Scenario: Edit with --children opens parent and child
Given a ticket exists with ID "edit-child1" and title "Child ticket" with parent "edit-0001"
When I run "ticket edit edit-0001 --children" in non-TTY mode
Then the command should succeed
And the output should contain "edit-0001.md"
And the output should contain "edit-child1.md"

Scenario: Edit with --children opens nested descendants
Given a ticket exists with ID "edit-mid" and title "Middle ticket" with parent "edit-0001"
And a ticket exists with ID "edit-leaf" and title "Leaf ticket" with parent "edit-mid"
When I run "ticket edit edit-0001 --children" in non-TTY mode
Then the command should succeed
And the output should contain "edit-0001.md"
And the output should contain "edit-mid.md"
And the output should contain "edit-leaf.md"

Scenario: Edit with --children on leaf ticket shows only that ticket
When I run "ticket edit edit-0001 --children" in non-TTY mode
Then the command should succeed
And the output should contain "edit-0001.md"
And the output line count should be 1

Scenario: Edit with --children outputs parent before children
Given a ticket exists with ID "edit-kid" and title "Kid ticket" with parent "edit-0001"
When I run "ticket edit edit-0001 --children" in non-TTY mode
Then the command should succeed
And the output line 1 should contain "edit-0001.md"
And the output line 2 should contain "edit-kid.md"

Scenario: Edit with --children and partial ID
Given a ticket exists with ID "edit-pc01" and title "Parent for partial"
And a ticket exists with ID "edit-pc02" and title "Child for partial" with parent "edit-pc01"
When I run "ticket edit pc01 --children" in non-TTY mode
Then the command should succeed
And the output should contain "edit-pc01.md"
And the output should contain "edit-pc02.md"

Scenario: Edit with --children flag before ID
Given a ticket exists with ID "edit-flagfirst" and title "Flag first child" with parent "edit-0001"
When I run "ticket edit --children edit-0001" in non-TTY mode
Then the command should succeed
And the output should contain "edit-0001.md"
And the output should contain "edit-flagfirst.md"

Scenario: Edit with --children on non-existent ticket
When I run "ticket edit nonexistent --children"
Then the command should fail
And the output should contain "Error: ticket 'nonexistent' not found"
84 changes: 78 additions & 6 deletions ticket
Original file line number Diff line number Diff line change
Expand Up @@ -1301,18 +1301,90 @@ cmd_add_note() {
}

cmd_edit() {
if [[ $# -lt 1 ]]; then
echo "Usage: ticket edit <id>" >&2
local children=false
local id=""

while [[ $# -gt 0 ]]; do
case "$1" in
--children) children=true; shift ;;
-*) echo "Error: unknown flag '$1'" >&2; return 1 ;;
*) id="$1"; shift ;;
esac
done

if [[ -z "$id" ]]; then
echo "Usage: ticket edit <id> [--children]" >&2
return 1
fi

local file
file=$(ticket_path "$1") || return 1
file=$(ticket_path "$id") || return 1

local files=("$file")

if [[ "$children" == true ]]; then
local target_id
target_id=$(basename "$file" .md)

local descendants
descendants=$(awk -v root="$target_id" '
BEGIN { FS=": "; in_front=0 }
FNR==1 {
if (prev_file) store()
id=""; parent=""; in_front=0
prev_file=FILENAME
}
/^---$/ { in_front = !in_front; next }
in_front && /^id:/ { id = $2 }
in_front && /^parent:/ { parent = $2 }

function store() {
if (id != "") {
ids[id] = prev_file
if (parent != "") parents[id] = parent
}
}

END {
if (prev_file) store()

# Build parent -> children map
for (id in parents) {
p = parents[id]
if (!(p in child_count)) child_count[p] = 0
child_count[p]++
children[p, child_count[p]] = id
}

# Iterative DFS
stack[1] = root
sp = 1
while (sp > 0) {
node = stack[sp]; sp--
if (node in visited) continue
visited[node] = 1
if (node != root) print ids[node]
# Push children in reverse order for correct DFS ordering
n = (node in child_count) ? child_count[node] : 0
for (i = n; i >= 1; i--) {
ch = children[node, i]
if (!(ch in visited)) { sp++; stack[sp] = ch }
}
}
}
' "$TICKETS_DIR"/*.md 2>/dev/null)

while IFS= read -r desc_file; do
[[ -n "$desc_file" ]] && files+=("$desc_file")
done <<< "$descendants"
fi

if [ -t 0 ] && [ -t 1 ]; then
"${EDITOR:-vi}" "$file"
"${EDITOR:-vi}" "${files[@]}"
else
echo "Edit ticket file: $file"
for f in "${files[@]}"; do
echo "Edit ticket file: $f"
done
fi
}

Expand Down Expand Up @@ -1463,7 +1535,7 @@ Commands:
blocked [-a X] [-T X] List open/in-progress tickets with unresolved deps
closed [--limit=N] [-a X] [-T X] List recently closed tickets (default 20, by mtime)
show <id> Display ticket
edit <id> Open ticket in \$EDITOR
edit <id> [--children] Open ticket in \$EDITOR (--children includes descendants)
add-note <id> [text] Append timestamped note (or pipe via stdin)
query [jq-filter] Output tickets as JSON, optionally filtered
migrate-beads Import tickets from .beads/issues.jsonl
Expand Down