ktg-plugin-marketplace/plugins/ultraplan-local/examples/01-add-verbose-flag/plan.md
Kjell Tore Guttormsen 9ecd225018 feat(ultraplan-local): Spor 3 — semantic plan-critic, examples, CC features, security docs
- agents/plan-critic.md: rule #7 split into literal blockers (TBD/TODO/FIXME)
  + semantic rubric with 8 deferred-decision tests; calibrated against the
  5-phrase corpus from the v3.1.0 quality brief
- hooks/hooks.json: rebuilt from corrupted state; valid JSON, registers
  PreToolUse(Bash,Write), UserPromptSubmit, PostToolUse(Bash), PreCompact
- hooks/scripts/session-title.mjs: NEW — sets ultra:<cmd>:<slug> session
  title for ultra commands (CC v2.1.94+)
- hooks/scripts/post-bash-stats.mjs: NEW — appends duration_ms per Bash
  call to ultraexecute-stats.jsonl (CC v2.1.97+)
- SECURITY.md: NEW — Forgejo private-issue reporting, supported = current
  minor only, scope = 4 hooks + denylist, hardening recommendations
- docs/architect-bridge-test.md: NEW — manual smoke checklist for the
  ultraplan ↔ ultra-cc-architect bridge
- examples/01-add-verbose-flag/: NEW — calibrated end-to-end (brief +
  research + plan + progress.json) for fork-er onramp; all four artifacts
  pass their validators
- README.md: + Extending the plugin, + Headless multi-session tuning
  (MCP_CONNECTION_NONBLOCKING), + Session titles, + Per-step timing,
  + disableSkillShellExecution recommendation
- CLAUDE.md: documents session-title.mjs and post-bash-stats.mjs
- root README.md: v3.1.0 entry expanded with Spor 2+3 deliverables

CC features adopted: F8, F9, F12 implemented; F3 implemented as Bash
PostToolUse logger; F2 (hook 'if'-field scoping) deferred — universal
protection beats reduced-scope protection for blocked commands.

Tests: 109/109 green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 06:28:44 +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 ultraplan-local 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.