aidevops 3.13.95 → 3.14.1

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.
@@ -1,370 +0,0 @@
1
- #!/usr/bin/env bash
2
- # SPDX-License-Identifier: MIT
3
- # SPDX-FileCopyrightText: 2025-2026 Marcus Quinn
4
- # =============================================================================
5
- # aidevops Upgrade Planning Library — planning template upgrade command
6
- # =============================================================================
7
- # Helper functions for `aidevops upgrade-planning`, extracted from aidevops.sh
8
- # to keep the CLI orchestrator below the large-file gate while preserving behaviour.
9
- #
10
- # Usage: source "${INSTALL_DIR}/aidevops-upgrade-planning-lib.sh"
11
- # Part of aidevops framework: https://aidevops.sh
12
-
13
- # Apply strict mode only when executed directly (not when sourced)
14
- [[ "${BASH_SOURCE[0]}" == "${0}" ]] && set -euo pipefail
15
-
16
- # Include guard
17
- [[ -n "${_AIDEVOPS_UPGRADE_PLANNING_LIB_LOADED:-}" ]] && return 0
18
- _AIDEVOPS_UPGRADE_PLANNING_LIB_LOADED=1
19
-
20
- if [[ -z "${SCRIPT_DIR:-}" ]]; then
21
- _lib_path="${BASH_SOURCE[0]%/*}"
22
- [[ "$_lib_path" == "${BASH_SOURCE[0]}" ]] && _lib_path="."
23
- SCRIPT_DIR="$(cd "$_lib_path" && pwd)"
24
- unset _lib_path
25
- fi
26
-
27
- _AIDEVOPS_UPGRADE_TRUE=true
28
- _AIDEVOPS_UPGRADE_TOON_META="TOON:meta"
29
-
30
- # Upgrade planning helpers (extracted for complexity reduction)
31
-
32
- _upgrade_validate() {
33
- local project_root="$1"
34
- [[ ! -f "$project_root/.aidevops.json" ]] && {
35
- print_error "aidevops not initialized in this project"
36
- print_info "Run 'aidevops init' first"
37
- return 1
38
- }
39
- if command -v jq &>/dev/null; then
40
- jq -e '.features.planning == true' "$project_root/.aidevops.json" &>/dev/null || {
41
- print_error "Planning feature not enabled"
42
- print_info "Run 'aidevops init planning' to enable"
43
- return 1
44
- }
45
- else
46
- local pe
47
- pe=$(grep -o '"planning": *true' "$project_root/.aidevops.json" 2>/dev/null || echo "")
48
- [[ -z "$pe" ]] && {
49
- print_error "Planning feature not enabled"
50
- print_info "Run 'aidevops init planning' to enable"
51
- return 1
52
- }
53
- fi
54
- [[ ! -f "$AGENTS_DIR/templates/todo-template.md" ]] && {
55
- print_error "TODO template not found: $AGENTS_DIR/templates/todo-template.md"
56
- return 1
57
- }
58
- [[ ! -f "$AGENTS_DIR/templates/plans-template.md" ]] && {
59
- print_error "PLANS template not found: $AGENTS_DIR/templates/plans-template.md"
60
- return 1
61
- }
62
- return 0
63
- }
64
-
65
- _upgrade_check_version() {
66
- local file="$1" template="$2" label="$3"
67
- if check_planning_file_version "$file" "$template"; then
68
- if [[ -f "$file" ]]; then
69
- if ! grep -q "$_AIDEVOPS_UPGRADE_TOON_META" "$file" 2>/dev/null; then
70
- print_warning "$label uses minimal template (missing TOON markers)"
71
- else
72
- local cv tv
73
- cv=$(grep -A1 "$_AIDEVOPS_UPGRADE_TOON_META" "$file" 2>/dev/null | tail -1 | cut -d',' -f1)
74
- tv=$(grep -A1 "$_AIDEVOPS_UPGRADE_TOON_META" "$template" 2>/dev/null | tail -1 | cut -d',' -f1)
75
- print_warning "$label format version $cv -> $tv"
76
- fi
77
- else print_info "$label not found - will create from template"; fi
78
- return 0
79
- else
80
- local cv
81
- cv=$(grep -A1 "$_AIDEVOPS_UPGRADE_TOON_META" "$file" 2>/dev/null | tail -1 | cut -d',' -f1)
82
- print_success "$label already up to date (v${cv})"
83
- return 1
84
- fi
85
- }
86
-
87
- # t2434: Extract lines under "## <section>" until the next "## " header.
88
- # Skips ## Format block entirely (its content is documentation, not tasks).
89
- # Skips fenced code blocks.
90
- # Exact-match on the section header — no regex escaping concerns.
91
- _extract_todo_section() {
92
- local file="$1" section="$2"
93
- awk -v target="## $section" '
94
- /^## Format/ { in_format=1; next }
95
- in_format && /^## / { in_format=0 }
96
- in_format { next }
97
- /^```/ { in_codeblock = !in_codeblock; next }
98
- in_codeblock { next }
99
- $0 == target { found=1; next }
100
- found && /^## / { exit }
101
- found
102
- ' "$file" 2>/dev/null || echo ""
103
- }
104
-
105
- # t2434: Filter stdin, removing only the literal Format-block placeholder IDs
106
- # (tXXX, tYYY, tZZZ). Real-world repos have historic IDs that don't follow the
107
- # strict t<digits> shape (e.g. "t059b", "t043-merge" from webapp) — we must
108
- # preserve those. A blocklist is safer than an allowlist here: extraction
109
- # already skips the Format section, so the filter is a secondary guard rather
110
- # than primary validation.
111
- _filter_todo_placeholders() {
112
- awk '
113
- !/^- \[[ x-]\] t/ { print; next }
114
- {
115
- id = $0
116
- sub(/^- \[[ x-]\] /, "", id)
117
- sub(/ .*/, "", id)
118
- if (id == "tXXX" || id == "tYYY" || id == "tZZZ") next
119
- print
120
- }
121
- '
122
- }
123
-
124
- # t2434: Insert content_file into target_file immediately after the closing
125
- # "-->" of the named TOON marker block (<!--TOON:<tag>...-->).
126
- # Idempotent only in the sense that each call inserts once per marker; repeated
127
- # calls would stack insertions. Intended to be called once per tag per upgrade.
128
- _insert_after_toon_marker() {
129
- local target_file="$1" toon_tag="$2" content_file="$3"
130
- local temp_file="${target_file}.insert"
131
- local marker_open="<!--TOON:${toon_tag}"
132
- local in_marker=false
133
- while IFS= read -r line || [[ -n "$line" ]]; do
134
- [[ "$line" == *"$marker_open"* ]] && in_marker=true
135
- if [[ "$in_marker" == true && "$line" == "-->" ]]; then
136
- echo "$line"
137
- echo ""
138
- cat "$content_file"
139
- in_marker=false
140
- continue
141
- fi
142
- echo "$line"
143
- done <"$target_file" >"$temp_file"
144
- mv "$temp_file" "$target_file"
145
- }
146
-
147
- # t2434: Preserve each of the 6 task sections into $workdir/<tag>.txt for
148
- # later re-insertion after the template is applied. Placeholder filter runs
149
- # per section so Format-block tXXX-style examples never reach the new file.
150
- _upgrade_todo_preserve_sections() {
151
- local todo_file="$1" workdir="$2"
152
- local sections=("Ready" "Backlog" "In Progress" "In Review" "Done" "Declined")
153
- local tags=("ready" "backlog" "in_progress" "in_review" "done" "declined")
154
- local i=0
155
- while [[ $i -lt ${#sections[@]} ]]; do
156
- local section="${sections[$i]}" tag="${tags[$i]}"
157
- local content
158
- content=$(_extract_todo_section "$todo_file" "$section")
159
- if [[ -n "$content" ]]; then
160
- content=$(printf '%s\n' "$content" | _filter_todo_placeholders)
161
- [[ -n "$content" ]] && printf '%s\n' "$content" >"$workdir/${tag}.txt"
162
- fi
163
- i=$((i + 1))
164
- done
165
- return 0
166
- }
167
-
168
- # t2434: Re-insert preserved section content after its matching TOON marker
169
- # in the freshly-applied new template. Caller is responsible for counting
170
- # merged tasks from the final file — keeping count out of the hot loop avoids
171
- # subshell/arithmetic edge cases under `set -u` when content contains `GH#`-
172
- # style IDs that don't match a naive `t[0-9]` count pattern.
173
- _upgrade_todo_reinsert_sections() {
174
- local todo_file="$1" workdir="$2"
175
- local tags=("ready" "backlog" "in_progress" "in_review" "done" "declined")
176
- local tag content_file
177
- for tag in "${tags[@]}"; do
178
- content_file="$workdir/${tag}.txt"
179
- [[ -f "$content_file" && -s "$content_file" ]] || continue
180
- grep -q "<!--TOON:${tag}" "$todo_file" || continue
181
- _insert_after_toon_marker "$todo_file" "$tag" "$content_file"
182
- done
183
- return 0
184
- }
185
-
186
- # t2434: Upgrade TODO.md to the latest TOON-enhanced template, preserving
187
- # tasks from all 6 sections (Ready, Backlog, In Progress, In Review, Done,
188
- # Declined). Prior behaviour (GH#20077) only preserved Backlog and silently
189
- # dropped the other 5 sections into TODO.md.bak, losing audit-trail data.
190
- _upgrade_todo() {
191
- local todo_file="$1" todo_template="$2" backup="$3"
192
- print_info "Upgrading TODO.md..."
193
- local workdir=""
194
- workdir=$(mktemp -d)
195
- # shellcheck disable=SC2064 # intentional $workdir expansion at trap-set time
196
- trap "rm -rf \"${workdir}\"" RETURN
197
- if [[ -f "$todo_file" ]]; then
198
- _upgrade_todo_preserve_sections "$todo_file" "$workdir"
199
- [[ "$backup" == "$_AIDEVOPS_UPGRADE_TRUE" ]] && {
200
- cp "$todo_file" "${todo_file}.bak"
201
- print_success "Backup created: TODO.md.bak"
202
- }
203
- fi
204
- local temp_todo="${todo_file}.new"
205
- if awk '/^---$/ && !p {c++; if(c==2) p=1; next} p' "$todo_template" >"$temp_todo" 2>/dev/null && [[ -s "$temp_todo" ]]; then
206
- mv "$temp_todo" "$todo_file"
207
- else
208
- rm -f "$temp_todo"
209
- cp "$todo_template" "$todo_file"
210
- fi
211
- sed_inplace "s/{{DATE}}/$(date +%Y-%m-%d)/" "$todo_file" 2>/dev/null || true
212
- _upgrade_todo_reinsert_sections "$todo_file" "$workdir"
213
- local merged=0
214
- merged=$(grep -cE '^- \[[ x-]\] (t[0-9]|GH#[0-9])' "$todo_file" 2>/dev/null || true)
215
- merged="${merged:-0}"
216
- [[ "$merged" -gt 0 ]] && print_success "Merged $merged existing task(s) across sections"
217
- print_success "TODO.md upgraded to TOON-enhanced template"
218
- return 0
219
- }
220
-
221
- _upgrade_plans() {
222
- local plans_file="$1" plans_template="$2" backup="$3" project_root="$4"
223
- print_info "Upgrading todo/PLANS.md..."
224
- mkdir -p "$project_root/todo/tasks"
225
- local existing_plans=""
226
- if [[ -f "$plans_file" ]]; then
227
- existing_plans=$(awk '/^### /{found=1} found{print}' "$plans_file" 2>/dev/null || echo "")
228
- [[ "$backup" == "$_AIDEVOPS_UPGRADE_TRUE" ]] && {
229
- cp "$plans_file" "${plans_file}.bak"
230
- print_success "Backup created: todo/PLANS.md.bak"
231
- }
232
- fi
233
- local temp_plans="${plans_file}.new"
234
- if awk '/^---$/ && !p {c++; if(c==2) p=1; next} p' "$plans_template" >"$temp_plans" 2>/dev/null && [[ -s "$temp_plans" ]]; then
235
- mv "$temp_plans" "$plans_file"
236
- else
237
- rm -f "$temp_plans"
238
- cp "$plans_template" "$plans_file"
239
- fi
240
- sed_inplace "s/{{DATE}}/$(date +%Y-%m-%d)/" "$plans_file" 2>/dev/null || true
241
- if [[ -n "$existing_plans" ]] && grep -q "<!--TOON:active_plans" "$plans_file"; then
242
- local temp_file="${plans_file}.merge" pcf
243
- pcf=$(mktemp)
244
- trap 'rm -f "${pcf:-}"' RETURN
245
- printf '%s\n' "$existing_plans" >"$pcf"
246
- local in_active=false
247
- while IFS= read -r line || [[ -n "$line" ]]; do
248
- [[ "$line" == *"<!--TOON:active_plans"* ]] && in_active=true
249
- if [[ "$in_active" == true && "$line" == "-->" ]]; then
250
- echo "$line"
251
- echo ""
252
- cat "$pcf"
253
- in_active=false
254
- continue
255
- fi
256
- echo "$line"
257
- done <"$plans_file" >"$temp_file"
258
- rm -f "$pcf"
259
- mv "$temp_file" "$plans_file"
260
- print_success "Merged existing plans into Active Plans"
261
- fi
262
- print_success "todo/PLANS.md upgraded to TOON-enhanced template"
263
- return 0
264
- }
265
-
266
- _upgrade_config_version() {
267
- local config_file="$1"
268
- local av
269
- av=$(get_version)
270
- if command -v jq &>/dev/null; then
271
- local tj="${config_file}.tmp"
272
- jq --arg version "$av" '.templates_version = $version' "$config_file" >"$tj" && mv "$tj" "$config_file"
273
- else
274
- if ! grep -q '"templates_version"' "$config_file" 2>/dev/null; then
275
- local tj="${config_file}.tmp"
276
- awk -v ver="$av" '/"version":/ { sub(/"version": "[^"]*"/, "\"version\": \"" ver "\",\n \"templates_version\": \"" ver "\"") } { print }' "$config_file" >"$tj" && mv "$tj" "$config_file"
277
- else sed_inplace "s/\"templates_version\": \"[^\"]*\"/\"templates_version\": \"$av\"/" "$config_file" 2>/dev/null || true; fi
278
- fi
279
- return 0
280
- }
281
-
282
- # Upgrade planning files to latest templates
283
- cmd_upgrade_planning() {
284
- local force=false backup=true dry_run=false
285
- while [[ $# -gt 0 ]]; do
286
- local arg="$1"
287
- case "$arg" in --force | -f)
288
- force=true
289
- shift
290
- ;;
291
- --no-backup)
292
- backup=false
293
- shift
294
- ;;
295
- --dry-run | -n)
296
- dry_run=true
297
- shift
298
- ;;
299
- *) shift ;; esac
300
- done
301
- print_header "Upgrade Planning Files"
302
- echo ""
303
- git rev-parse --is-inside-work-tree &>/dev/null || {
304
- print_error "Not in a git repository"
305
- return 1
306
- }
307
- [[ "$dry_run" != "$_AIDEVOPS_UPGRADE_TRUE" ]] && { check_protected_branch "chore" "upgrade-planning" || return 1; }
308
- local project_root
309
- project_root=$(git rev-parse --show-toplevel)
310
- _upgrade_validate "$project_root" || return 1
311
- local todo_file="$project_root/TODO.md" plans_file="$project_root/todo/PLANS.md"
312
- local todo_template="$AGENTS_DIR/templates/todo-template.md" plans_template="$AGENTS_DIR/templates/plans-template.md"
313
- local needs_upgrade=false todo_needs=false plans_needs=false
314
- _upgrade_check_version "$todo_file" "$todo_template" "TODO.md" && {
315
- todo_needs=true
316
- needs_upgrade=true
317
- }
318
- _upgrade_check_version "$plans_file" "$plans_template" "todo/PLANS.md" && {
319
- plans_needs=true
320
- needs_upgrade=true
321
- }
322
- [[ "$needs_upgrade" == "false" ]] && {
323
- echo ""
324
- print_success "Planning files are up to date!"
325
- return 0
326
- }
327
- echo ""
328
- if [[ "$dry_run" == "$_AIDEVOPS_UPGRADE_TRUE" ]]; then
329
- print_info "Dry run - no changes will be made"
330
- echo ""
331
- [[ "$todo_needs" == "$_AIDEVOPS_UPGRADE_TRUE" ]] && echo " Would upgrade: TODO.md"
332
- [[ "$plans_needs" == "$_AIDEVOPS_UPGRADE_TRUE" ]] && echo " Would upgrade: todo/PLANS.md"
333
- return 0
334
- fi
335
- if [[ "$force" == "false" ]]; then
336
- echo "Files to upgrade:"
337
- [[ "$todo_needs" == "$_AIDEVOPS_UPGRADE_TRUE" ]] && echo " - TODO.md"
338
- [[ "$plans_needs" == "$_AIDEVOPS_UPGRADE_TRUE" ]] && echo " - todo/PLANS.md"
339
- echo ""
340
- echo "This will:"
341
- echo " 1. Extract existing tasks from current files"
342
- echo " 2. Create backups (.bak files)"
343
- echo " 3. Apply new TOON-enhanced templates"
344
- echo " 4. Merge existing tasks into new structure"
345
- echo ""
346
- read -r -p "Continue? [y/N] " response
347
- [[ ! "$response" =~ ^[Yy]$ ]] && {
348
- print_info "Upgrade cancelled"
349
- return 0
350
- }
351
- fi
352
- echo ""
353
- [[ "$todo_needs" == "$_AIDEVOPS_UPGRADE_TRUE" ]] && _upgrade_todo "$todo_file" "$todo_template" "$backup"
354
- [[ "$plans_needs" == "$_AIDEVOPS_UPGRADE_TRUE" ]] && _upgrade_plans "$plans_file" "$plans_template" "$backup" "$project_root"
355
- _upgrade_config_version "$project_root/.aidevops.json"
356
- echo ""
357
- print_success "Planning files upgraded!"
358
- echo ""
359
- echo "Next steps:"
360
- echo " 1. Review the upgraded files"
361
- echo " 2. Verify your tasks were preserved"
362
- if [[ "$backup" == "$_AIDEVOPS_UPGRADE_TRUE" ]]; then
363
- echo " 3. Remove .bak files when satisfied"
364
- echo ""
365
- echo "If issues occurred, restore from backups:"
366
- [[ "$todo_needs" == "$_AIDEVOPS_UPGRADE_TRUE" ]] && echo " mv TODO.md.bak TODO.md"
367
- [[ "$plans_needs" == "$_AIDEVOPS_UPGRADE_TRUE" ]] && echo " mv todo/PLANS.md.bak todo/PLANS.md"
368
- fi
369
- return 0
370
- }