1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
|
# `-l` directional color + DelReq column Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** In the `-l` table, color the newer version cell green (hint vs SBo `.info`), keep equal-version rows yellow, and add a `DelReq` boolean column before `Created`.
**Architecture:** All changes live in `list_hint_files` in `mkhint`. A green color pair is added next to the existing yellow. Per row, byte-equality keeps the yellow-row + `MATCHED_PKGS` path; on a difference `sort -V` picks the newer side and only that cell is wrapped green. Version cells are pre-padded to width 22 before wrapping so color bytes don't break alignment. A `DelReq` column (`✓` / blank) is derived from a `DELREQUIRES="..."` grep.
**Tech Stack:** bash, coreutils (`sort -V`), `tput`, existing `tests/mkhint_test.sh` harness.
---
## File structure
- Modify: `mkhint` — `list_hint_files` (lines ~97-158): color setup, header, per-row logic, legend.
- Modify: `tests/mkhint_test.sh` — append T60-T64 after T59 (~line 1145).
- Modify: `CLAUDE.md` — update the `--list` / `-l` behavior bullet and the test table.
- Modify: `README.md` — update `-l` description if it enumerates columns/colors.
No new files. No new dependencies.
---
## Task 1: Green color pair + directional cell coloring
**Files:**
- Modify: `mkhint` `list_hint_files` (color block ~104-111, header ~115, row loop ~118-147, legend ~155-157)
- Test: `tests/mkhint_test.sh` (append T60, T61, T62)
- [ ] **Step 1: Write failing tests T60, T61, T62**
Append after T59 in `tests/mkhint_test.sh`:
```bash
# ── T60: -l hint newer than SBo → HintVer cell green ─────────────────────────
echo ""
echo "T60: -l hint version newer than .info → HintVer green"
rm -f "$MOCK_HINT"/*.hint "$MOCK_HINT"/*.bak 2>/dev/null
# curl .info is 8.5.0; hint 8.6.0 is newer
cat > "$MOCK_HINT/curl.hint" << 'EOF'
VERSION="8.6.0"
ARCH="x86_64"
DOWNLOAD="https://curl.se/download/curl-8.6.0.tar.gz"
MD5SUM="abc123def456abc123def456abc123de"
EOF
out=$(MKHINT_FORCE_COLOR=1 run_mkhint -l 2>&1)
# green start code (ESC[32m) appears before the hint version 8.6.0 on the curl row
echo "$out" | grep curl.hint | grep -q $'\033\[32m.*8\.6\.0' \
&& { echo " PASS: HintVer green when hint newer"; (( PASS++ )); } \
|| { echo " FAIL: HintVer not green"; echo "$out" | grep curl.hint | cat -v | sed 's/^/ /'; (( FAIL++ )); ERRORS+=("T60 green"); }
# whole row must NOT be yellow (differing versions)
echo "$out" | grep curl.hint | grep -q $'\033\[33m' \
&& { echo " FAIL: differing row wrongly yellow"; (( FAIL++ )); ERRORS+=("T60 yellow"); } \
|| { echo " PASS: differing row not yellow"; (( PASS++ )); }
# ── T61: -l SBo newer than hint → SBOVer cell green ──────────────────────────
echo ""
echo "T61: -l .info version newer than hint → SBOVer green"
rm -f "$MOCK_HINT"/*.hint "$MOCK_HINT"/*.bak 2>/dev/null
# curl .info is 8.5.0; hint 8.4.0 is older
cat > "$MOCK_HINT/curl.hint" << 'EOF'
VERSION="8.4.0"
ARCH="x86_64"
DOWNLOAD="https://curl.se/download/curl-8.4.0.tar.gz"
MD5SUM="abc123def456abc123def456abc123de"
EOF
out=$(MKHINT_FORCE_COLOR=1 run_mkhint -l 2>&1)
# green start code appears before the SBo version 8.5.0 on the curl row
echo "$out" | grep curl.hint | grep -q $'\033\[32m.*8\.5\.0' \
&& { echo " PASS: SBOVer green when .info newer"; (( PASS++ )); } \
|| { echo " FAIL: SBOVer not green"; echo "$out" | grep curl.hint | cat -v | sed 's/^/ /'; (( FAIL++ )); ERRORS+=("T61 green"); }
# ── T62: -l equal versions → whole row yellow, no green ──────────────────────
echo ""
echo "T62: -l equal versions → yellow row, no green cell"
rm -f "$MOCK_HINT"/*.hint "$MOCK_HINT"/*.bak 2>/dev/null
cat > "$MOCK_HINT/curl.hint" << 'EOF'
VERSION="8.5.0"
ARCH="x86_64"
DOWNLOAD="https://curl.se/download/curl-8.5.0.tar.gz"
MD5SUM="abc123def456abc123def456abc123de"
EOF
out=$(MKHINT_FORCE_COLOR=1 run_mkhint -l 2>&1)
echo "$out" | grep curl.hint | grep -q $'\033\[33m' \
&& { echo " PASS: equal row yellow"; (( PASS++ )); } \
|| { echo " FAIL: equal row not yellow"; echo "$out" | grep curl.hint | cat -v | sed 's/^/ /'; (( FAIL++ )); ERRORS+=("T62 yellow"); }
echo "$out" | grep curl.hint | grep -q $'\033\[32m' \
&& { echo " FAIL: equal row wrongly green"; (( FAIL++ )); ERRORS+=("T62 green"); } \
|| { echo " PASS: equal row has no green"; (( PASS++ )); }
```
- [ ] **Step 2: Run tests, verify they fail**
Run: `bash tests/mkhint_test.sh 2>&1 | grep -A1 'T6[012]'`
Expected: FAIL lines for T60 green, T61 green (no green codes emitted yet). T62 yellow passes (existing behavior), T62 green passes.
- [ ] **Step 3: Add green color pair to the color setup block**
In `mkhint`, replace the color block (currently ~104-111):
```bash
# Color only on a TTY, or when forced for tests. tput optional.
local c_on="" c_off="" g_on=""
if [[ -n "$MKHINT_FORCE_COLOR" || -t 1 ]]; then
if command -v tput &>/dev/null && tput setaf 3 &>/dev/null; then
c_on=$(tput setaf 3); g_on=$(tput setaf 2); c_off=$(tput sgr0)
else
c_on=$'\033[33m'; g_on=$'\033[32m'; c_off=$'\033[0m'
fi
fi
```
`c_on` = yellow (row), `g_on` = green (cell), `c_off` = reset (shared).
- [ ] **Step 4: Rewrite the per-row loop for directional coloring**
Replace the row body (currently ~136-145, from `local row` through `count=$((count + 1))`) with:
```bash
local hv sv
hv=$(printf "%22s" "$VER")
sv=$(printf "%22s" "$SBO_VER")
if [[ -n "$SBO_VER" && "$VER" == "$SBO_VER" ]]; then
# equal → whole row yellow
local row
row=$(printf "%-40s %s %s %-20s" "$name" "$hv" "$sv" "$category")
printf "%s%s%s\n" "$c_on" "$row" "$c_off"
MATCHED_PKGS+=("$pkg")
matched=$((matched + 1))
else
# differ (or no SBO_VER) → green the newer cell, row plain
if [[ -n "$SBO_VER" ]]; then
local newer
newer=$(printf '%s\n%s\n' "$VER" "$SBO_VER" | sort -V | tail -1)
if [[ "$newer" == "$VER" ]]; then
hv="${g_on}${hv}${c_off}"
else
sv="${g_on}${sv}${c_off}"
fi
fi
printf "%-40s %s %s %-20s\n" "$name" "$hv" "$sv" "$category"
fi
count=$((count + 1))
```
Note: the trailing `Category`/`Created` columns are handled in Task 2 (DelReq column insertion). For now this task drops `date` from the printf to keep the diff focused; Task 2 adds `DelReq` + `Created` back. If executing Task 1 standalone, append ` %s` and `"$date"` to both printf lines and their format strings.
- [ ] **Step 5: Update the legend**
Replace the matched-count legend (currently ~155-157):
```bash
if [[ $matched -gt 0 && -n "$c_on" ]]; then
echo "(yellow row = versions match; green = newer side)"
fi
```
Keep grep-compatible text; T36 greps `"highlighted = hint version matches"` — update T36 in the same commit (see Step 6).
- [ ] **Step 6: Fix T36 legend grep**
T36 (line ~823) greps for the old legend string. Change:
```bash
echo "$out" | grep -q "yellow row = versions match" \
```
- [ ] **Step 7: Run tests, verify T60/T61/T62 and T36 pass**
Run: `bash tests/mkhint_test.sh 2>&1 | grep -E 'T(36|6[012])|FAIL'`
Expected: all PASS, no FAIL.
- [ ] **Step 8: bash -n check**
Run: `bash -n mkhint && echo OK`
Expected: `OK`
- [ ] **Step 9: Commit**
```bash
git add mkhint tests/mkhint_test.sh
git commit -S -m "feat: -l colors newer version cell green, keeps equal rows yellow"
```
---
## Task 2: DelReq column
**Files:**
- Modify: `mkhint` `list_hint_files` (header ~115, row loop printf lines from Task 1)
- Test: `tests/mkhint_test.sh` (append T63, T64)
- [ ] **Step 1: Write failing tests T63, T64**
Append after T62 in `tests/mkhint_test.sh`:
```bash
# ── T63: -l shows ✓ in DelReq for hint with DELREQUIRES ──────────────────────
echo ""
echo "T63: -l DelReq column shows ✓ when DELREQUIRES populated"
rm -f "$MOCK_HINT"/*.hint "$MOCK_HINT"/*.bak 2>/dev/null
cat > "$MOCK_HINT/curl.hint" << 'EOF'
VERSION="8.5.0"
ARCH="x86_64"
DELREQUIRES="rust-opt"
DOWNLOAD="https://curl.se/download/curl-8.5.0.tar.gz"
MD5SUM="abc123def456abc123def456abc123de"
EOF
out=$(run_mkhint -l 2>&1)
echo "$out" | grep -q "DelReq" \
&& { echo " PASS: DelReq header present"; (( PASS++ )); } \
|| { echo " FAIL: DelReq header missing"; echo "$out" | sed 's/^/ /'; (( FAIL++ )); ERRORS+=("T63 header"); }
echo "$out" | grep curl.hint | grep -q "✓" \
&& { echo " PASS: ✓ shown for DELREQUIRES hint"; (( PASS++ )); } \
|| { echo " FAIL: ✓ missing"; echo "$out" | grep curl.hint | sed 's/^/ /'; (( FAIL++ )); ERRORS+=("T63 check"); }
# ── T64: -l DelReq blank when no DELREQUIRES ─────────────────────────────────
echo ""
echo "T64: -l DelReq column blank when no DELREQUIRES"
rm -f "$MOCK_HINT"/*.hint "$MOCK_HINT"/*.bak 2>/dev/null
cat > "$MOCK_HINT/curl.hint" << 'EOF'
VERSION="8.5.0"
ARCH="x86_64"
DOWNLOAD="https://curl.se/download/curl-8.5.0.tar.gz"
MD5SUM="abc123def456abc123def456abc123de"
EOF
out=$(run_mkhint -l 2>&1)
echo "$out" | grep curl.hint | grep -q "✓" \
&& { echo " FAIL: ✓ shown without DELREQUIRES"; (( FAIL++ )); ERRORS+=("T64 check"); } \
|| { echo " PASS: no ✓ without DELREQUIRES"; (( PASS++ )); }
```
- [ ] **Step 2: Run tests, verify they fail**
Run: `bash tests/mkhint_test.sh 2>&1 | grep -A2 'T6[34]'`
Expected: FAIL for T63 header (no DelReq column yet), T63 check. T64 passes vacuously.
- [ ] **Step 3: Add the DelReq header**
Replace the header printf (currently ~115):
```bash
printf "%-40s %22s %22s %-20s %-6s %s\n" "File" "HintVer" "SBOVer" "Category" "DelReq" "Created"
```
Column order: `File | HintVer | SBOVer | Category | DelReq | Created`.
- [ ] **Step 4: Compute the DelReq value in the row loop**
After the `VER` skip check in the loop (after line ~124), add:
```bash
local delreq=""
grep -q '^DELREQUIRES="..*"' "$file" && delreq="✓"
```
- [ ] **Step 5: Add a display-width pad helper and use it for DelReq + Created**
Add this helper just above `list_hint_files` (near line 96):
```bash
# Pad a possibly-multibyte glyph (0 or 1 display column) to a fixed width.
_pad_glyph() {
local g="$1" w="$2"
if [[ -n "$g" ]]; then printf "%s%*s" "$g" $((w - 1)) ""; else printf "%*s" "$w" ""; fi
}
```
In both row printf calls from Task 1, append the DelReq + Created columns.
Yellow (equal) branch:
```bash
row=$(printf "%-40s %s %s %-20s %s %s" "$name" "$hv" "$sv" "$category" "$(_pad_glyph "$delreq" 6)" "$date")
```
Plain (differ) branch:
```bash
printf "%-40s %s %s %-20s %s %s\n" "$name" "$hv" "$sv" "$category" "$(_pad_glyph "$delreq" 6)" "$date"
```
(`$date` is already computed at ~135; keep it.)
- [ ] **Step 6: Run tests, verify T63/T64 pass**
Run: `bash tests/mkhint_test.sh 2>&1 | grep -E 'T6[34]|FAIL'`
Expected: all PASS, no FAIL.
- [ ] **Step 7: Full suite + bash -n**
Run: `bash -n mkhint && bash tests/mkhint_test.sh 2>&1 | tail -5`
Expected: `OK`, and the summary reports 0 failures.
- [ ] **Step 8: Commit**
```bash
git add mkhint tests/mkhint_test.sh
git commit -S -m "feat: -l adds DelReq column showing populated DELREQUIRES"
```
---
## Task 3: Docs
**Files:**
- Modify: `CLAUDE.md` (the `--list` / `-l` behavior bullet, the test-coverage table)
- Modify: `README.md` (if the `-l` description enumerates columns)
- [ ] **Step 1: Update CLAUDE.md `--list` bullet**
Find the `- \`--list\` / \`-l\`:` bullet under "Key Behaviors". Update the color description: rows with equal versions are yellow; when versions differ the newer side's cell is green (via `sort -V`); a `DelReq` column (before `Created`) shows `✓` when the hint has a populated `DELREQUIRES`. Legend now reads `(yellow row = versions match; green = newer side)`.
- [ ] **Step 2: Add T60-T64 to the CLAUDE.md test table**
Append rows:
```
| T60 | `-l` hint newer than `.info` — HintVer cell green, row not yellow |
| T61 | `-l` `.info` newer than hint — SBOVer cell green |
| T62 | `-l` equal versions — whole row yellow, no green |
| T63 | `-l` hint with populated `DELREQUIRES` — `✓` in DelReq column |
| T64 | `-l` hint without `DELREQUIRES` — DelReq blank |
```
- [ ] **Step 3: Update README.md if needed**
Check the `-l` / `--list` section. If it describes the highlight behavior or columns, update it to mention directional green + the DelReq column. If README does not enumerate columns, no change.
Run: `grep -n 'list\|highlight\|HintVer' README.md`
- [ ] **Step 4: Commit**
```bash
git add CLAUDE.md README.md
git commit -S -m "docs: document -l directional color and DelReq column"
```
---
## Self-review notes
- Spec coverage: directional color (Task 1), equal→yellow kept (Task 1), DelReq column (Task 2), legend (Task 1), tests T60-T64 (Tasks 1-2), docs (Task 3). All covered.
- `MATCHED_PKGS` / `matched` only populated on the equal branch — preserves `-R` behavior.
- `sort -V` verified present and correct on target box (orders `1.10.0 > 1.2.3`, `2026_06_10 > 2026_06_02`).
- Color-byte alignment: version cells pre-padded to 22 before green wrap; DelReq via `_pad_glyph` for multibyte `✓`.
- Task 1 Step 4 note flags the date-column dependency between Task 1 and Task 2 so tasks stay runnable in order.
|