ktg-plugin-marketplace/plugins/voyage/examples/01-add-verbose-flag/plan.md
Kjell Tore Guttormsen 7a90d348ad feat(voyage)!: marketplace handoff — rename plugins/ultraplan-local to plugins/voyage [skip-docs]
Session 5 of voyage-rebrand (V6). Operator-authorized cross-plugin scope.

- git mv plugins/ultraplan-local plugins/voyage (rename detected, history preserved)
- .claude-plugin/marketplace.json: voyage entry replaces ultraplan-local
- CLAUDE.md: voyage row in plugin list, voyage in design-system consumer list
- README.md: bulk rename ultra*-local commands -> trek* commands; ultraplan-local refs -> voyage; type discriminators (type: trekbrief/trekreview); session-title pattern (voyage:<command>:<slug>); v4.0.0 release-note paragraph
- plugins/voyage/.claude-plugin/plugin.json: homepage/repository URLs point to monorepo voyage path
- plugins/voyage/verify.sh: drop URL whitelist exception (no longer needed)

Closes voyage-rebrand. bash plugins/voyage/verify.sh PASS 7/7. npm test 361/361.
2026-05-05 15:37:52 +02:00

251 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Add `--verbose` flag to small-auth CLI
plan_version: 1.7
> **Plan quality: A** (92/100) — APPROVE
>
> Generated by trekplan v3.1.0 on 2026-05-01.
## Context
The `small-auth` CLI has six commands and emits only final results; no
progress, no internal step trace. Operators debugging slow `token-refresh`
or mis-routed `users-list` calls have no signal between "started" and
"finished". This plan adds a `--verbose` / `-v` flag that, when set,
emits structured progress lines to stderr without changing stdout. The
default path stays byte-identical.
This is a textbook minimal-scope addition: the parser is small,
centralized, and already supports global flags.
## Architecture Diagram
```mermaid
graph TD
subgraph "Changes in this plan"
cli["src/cli.mjs<br/>parse globalFlags"]
ctx["ctx object<br/>+ verbose: boolean"]
login["src/commands/login.mjs<br/>+ 3 verbose calls"]
token["src/commands/token-refresh.mjs<br/>+ 4 verbose calls"]
userlist["src/commands/users-list.mjs<br/>+ 2 verbose calls"]
usercreate["src/commands/users-create.mjs<br/>+ 3 verbose calls"]
logout["src/commands/logout.mjs<br/>+ 2 verbose calls"]
whoami["src/commands/whoami.mjs<br/>(accepts flag, no traces)"]
help["src/cli.mjs<br/>--help text"]
tests["tests/cli-verbose-flag.test.mjs<br/>tests/cli-no-verbose-stability.test.mjs"]
cli --> ctx
ctx --> login
ctx --> token
ctx --> userlist
ctx --> usercreate
ctx --> logout
ctx --> whoami
cli --> help
login --> tests
end
```
## Codebase Analysis
- **Tech stack:** Node.js ≥ 18, no external runtime dependencies, `node:test` for tests
- **Key patterns:** hand-rolled argv parser, two-pass extract (globals → command), handler contract `run(positional, flags, ctx)`
- **Relevant files:** `src/cli.mjs`, `src/commands/{login,logout,whoami,token-refresh,users-list,users-create}.mjs`, `tests/`
- **Reusable code:** existing `[error]` stderr pattern at `src/cli.mjs:67` — mirror it for `[verbose]`
- **External tech:** none
- **Recent git activity:** parser last changed in commit `ab1c2d3` (added `--version`); pattern still current
## Research Sources
*Internal research only — see `research/01-cli-parser-conventions.md`.*
## Implementation Plan
Each step targets 12 files and one focused change. TDD structure: test
or stability harness comes before behavior change.
### Step 1: Capture golden stdout for stability test
- **Files:** `tests/golden/login.stdout` (new file), `tests/golden/whoami.stdout` (new file), `tests/golden/users-list.stdout` (new file)
- **Changes:** Run current CLI for three representative commands, save stdout byte-for-byte. Use `node src/cli.mjs login alice > tests/golden/login.stdout` and similar.
- **Verify:** `wc -c tests/golden/*.stdout` → expected: each file > 0 bytes
- **Checkpoint:** `git commit -m "test(small-auth): capture pre-change golden stdout for verbose-flag stability"`
- **On failure:** revert files; do not proceed. Likely cause: CLI itself broken — investigate before continuing.
- **Manifest:**
```yaml
manifest:
expected_paths:
- tests/golden/login.stdout
- tests/golden/whoami.stdout
- tests/golden/users-list.stdout
min_file_count: 3
commit_message_pattern: "^test\\(small-auth\\): capture"
bash_syntax_check: []
forbidden_paths: []
must_contain: []
```
### Step 2: Add stability test (must FAIL initially — verbose not yet wired)
- **Files:** `tests/cli-no-verbose-stability.test.mjs` (new file)
- **Changes:** Three subtests, one per golden file. Each runs `node src/cli.mjs <cmd> ...` and asserts stdout `===` `readFileSync('tests/golden/<cmd>.stdout')`. The test should PASS today (no behavior change yet) — it's the canary for step 5 onwards.
- **Verify:** `node --test tests/cli-no-verbose-stability.test.mjs` → expected: 3 pass
- **Checkpoint:** `git commit -m "test(small-auth): stdout stability harness for verbose-flag work"`
- **On failure:** if subtests fail, the goldens are wrong — re-run step 1.
- **Manifest:**
```yaml
manifest:
expected_paths:
- tests/cli-no-verbose-stability.test.mjs
min_file_count: 1
commit_message_pattern: "^test\\(small-auth\\): stdout stability"
bash_syntax_check: []
forbidden_paths: []
must_contain:
- path: tests/cli-no-verbose-stability.test.mjs
pattern: "tests/golden/login\\.stdout"
```
### Step 3: Extend parser to recognize `--verbose` and `-v`
- **Files:** `src/cli.mjs`
- **Changes:** At `src/cli.mjs:34` (alias table) add `'-v': '--verbose'`. At `src/cli.mjs:48` (globalFlags loop) add `'--verbose'` case that sets `globalFlags.verbose = true`. Default the field to `false`. The flag is consumed (removed from argv) like `--help` and `--version`.
- **Verify:** `node src/cli.mjs --verbose login alice 2>&1 | head -1` → expected: no parse error
- **Checkpoint:** `git commit -m "feat(cli): recognize --verbose / -v as global flag"`
- **On failure:** revert `src/cli.mjs`; rerun stability test to confirm clean baseline.
- **Manifest:**
```yaml
manifest:
expected_paths:
- src/cli.mjs
min_file_count: 1
commit_message_pattern: "^feat\\(cli\\): recognize --verbose"
bash_syntax_check: []
forbidden_paths: []
must_contain:
- path: src/cli.mjs
pattern: "globalFlags\\.verbose"
```
### Step 4: Pass `verbose` into handler `ctx`
- **Files:** `src/cli.mjs`
- **Changes:** At `src/cli.mjs:62` (ctx construction) add `verbose: globalFlags.verbose` to the ctx literal. No handler changes yet.
- **Verify:** `node --test tests/cli-no-verbose-stability.test.mjs` → expected: 3 pass (handlers ignore the new field for now)
- **Checkpoint:** `git commit -m "feat(cli): thread verbose into command handler ctx"`
- **On failure:** stability tests fail → ctx mutation broke something. Bisect by reverting and adding back one line at a time.
- **Manifest:**
```yaml
manifest:
expected_paths:
- src/cli.mjs
min_file_count: 1
commit_message_pattern: "^feat\\(cli\\): thread verbose"
bash_syntax_check: []
forbidden_paths: []
must_contain:
- path: src/cli.mjs
pattern: "verbose: globalFlags\\.verbose"
```
### Step 5: Wire verbose output in `login`, `token-refresh`, `users-list`, `users-create`, `logout`
- **Files:** `src/commands/login.mjs`, `src/commands/token-refresh.mjs`, `src/commands/users-list.mjs`, `src/commands/users-create.mjs`, `src/commands/logout.mjs`
- **Changes:** At each internal step (3 for login, 4 for token-refresh, 2 for users-list, 3 for users-create, 2 for logout — 14 call sites total), add `if (ctx.verbose) ctx.stderr.write(\`[verbose] <step description>\\n\`);`. Step descriptions per file:
- login: "parsing argv", "credential lookup", "issuing session token"
- token-refresh: "parsing argv", "validating refresh token", "rotating session token", "persisting new token"
- users-list: "parsing argv", "querying user store"
- users-create: "parsing argv", "validating input", "writing user record"
- logout: "parsing argv", "invalidating session token"
- **Verify:** `node --test tests/cli-no-verbose-stability.test.mjs` → expected: 3 pass (stdout unchanged when flag absent)
- **Checkpoint:** `git commit -m "feat(commands): emit verbose stderr trace for 5 commands"`
- **On failure:** stability tests fail → likely a stray `console.log` or `ctx.stdout.write` instead of `ctx.stderr.write`. Re-grep all five files for `stdout` mentions added in this step.
- **Manifest:**
```yaml
manifest:
expected_paths:
- src/commands/login.mjs
- src/commands/token-refresh.mjs
- src/commands/users-list.mjs
- src/commands/users-create.mjs
- src/commands/logout.mjs
min_file_count: 5
commit_message_pattern: "^feat\\(commands\\): emit verbose"
bash_syntax_check: []
forbidden_paths: []
must_contain:
- path: src/commands/login.mjs
pattern: "ctx\\.verbose"
- path: src/commands/token-refresh.mjs
pattern: "ctx\\.verbose"
```
### Step 6: Add verbose-content test for `login`
- **Files:** `tests/cli-verbose-flag.test.mjs` (new file)
- **Changes:** Single test: spawn `node src/cli.mjs login --verbose alice`, capture stderr, assert exit 0, assert stderr contains all three expected verbose lines: "[verbose] parsing argv", "[verbose] credential lookup", "[verbose] issuing session token", in that order.
- **Verify:** `node --test tests/cli-verbose-flag.test.mjs` → expected: 1 pass
- **Checkpoint:** `git commit -m "test(small-auth): assert --verbose emits expected stderr trace"`
- **On failure:** if assertion misses a line, check step 5 for typos in the `[verbose]` strings; if exit code != 0, check that login still works without verbose (regression).
- **Manifest:**
```yaml
manifest:
expected_paths:
- tests/cli-verbose-flag.test.mjs
min_file_count: 1
commit_message_pattern: "^test\\(small-auth\\): assert --verbose"
bash_syntax_check: []
forbidden_paths: []
must_contain:
- path: tests/cli-verbose-flag.test.mjs
pattern: "\\[verbose\\] credential lookup"
```
### Step 7: Update `--help` text
- **Files:** `src/cli.mjs`
- **Changes:** At the help-text constant (`src/cli.mjs:78`), add a line under "Global flags": ` -v, --verbose emit per-step trace to stderr (single level only)`.
- **Verify:** `node src/cli.mjs --help | grep -E "verbose"` → expected: 1 line containing "emit per-step trace"
- **Checkpoint:** `git commit -m "docs(cli): document --verbose / -v in --help text"`
- **On failure:** revert just the constant; help text isn't load-bearing.
- **Manifest:**
```yaml
manifest:
expected_paths:
- src/cli.mjs
min_file_count: 1
commit_message_pattern: "^docs\\(cli\\): document --verbose"
bash_syntax_check: []
forbidden_paths: []
must_contain:
- path: src/cli.mjs
pattern: "emit per-step trace"
```
## Verification
Final acceptance run after step 7:
```bash
node --test tests/ # all 26 tests pass (24 + 2 new)
node src/cli.mjs login alice > /tmp/out 2>/dev/null
diff /tmp/out tests/golden/login.stdout # exit 0
node src/cli.mjs login --verbose alice 2>/tmp/err 1>/dev/null
grep -c "\[verbose\]" /tmp/err # ≥ 3
node src/cli.mjs --help | grep -c "\-v, --verbose" # 1
```
## Plan-critic notes
- No deferred decisions: every step names its files, lines, and exact
string changes.
- TDD: stability harness (step 2) precedes behavior changes (steps 3-5).
- Verify commands are runnable, not "test it works".
- Steps 5 wires 5 files in one commit; this is over the 12 file
guideline but is justified by symmetry — the change is mechanical
and atomic across the five files; splitting would create five tiny
commits with no test value between them.
## Execution Strategy
Single session, 7 steps, ~15-20 minutes. No parallel decomposition needed.