Skip to content

feat(cli): 3 UX fixes — compact --help, silent plugin update sub-output, --json minified (v3.2.15)#57

Merged
kengio merged 6 commits into
mainfrom
v3.2.15-help-hybrid
May 28, 2026
Merged

feat(cli): 3 UX fixes — compact --help, silent plugin update sub-output, --json minified (v3.2.15)#57
kengio merged 6 commits into
mainfrom
v3.2.15-help-hybrid

Conversation

@kengio
Copy link
Copy Markdown
Contributor

@kengio kengio commented May 28, 2026

Summary

3 UX fixes from live testing of v3.2.14 / this turn — bundled into one PR per user request.

A. `--help` compact-with-wrap (revert v3.2.12 long-format)

v3.2.12's blanket `next_line_help = true` made every arg render in long format. User testing flagged "ดูยาก" (hard to read). v3.2.15 reverts to compact AND wraps `[default]/[possible values]` to indented next line:

```
Options:
--vault Override vault root (highest priority · beats ONEBRAIN_VAULT and walk-up)...
-o, --output Output format. Default `text` is TTY-friendly; pipe-detected calls drop color/pretty automatically
[default: text, possible values: text, json, yaml, table, tsv]
--json Shorthand for `--output json`
```

Same wrap applied to `harness run --help` and `skill run --help` for `--mode` / `--harness`. `HarnessMode` variant docs stripped (the multi-line Possible values block was forcing long format).

B. `plugin update` no header leak

User testing of v3.2.14 flagged that 5 `▸ ` lines from `vault_sync`'s TtyProgress leaked ABOVE the framed `⚡ Plugin Update` header. v3.2.15 routes through new `vault_sync::run_silent` which forces `progress_writer = Some(io::sink())` — kills the per-step TTY lines AND the intro/outro frame. The framed header now appears as the FIRST thing in the report (right after the brand banner), with the animated spinner as the only progress signal — matches `doctor`/`update`.

`run_embedded` removed (no remaining caller; the in-between mode wasn't useful).

C. `--json` minified by default

Pre-3.2.15 auto-prettified JSON on TTY, which was convenient for humans glancing at a structured response but made the "copy/paste into curl / a script" path noisier than necessary. v3.2.15: `--json` (and `--output json`) emit single-line minified JSON by default; `--json --pretty` opts into indented.

```bash
$ onebrain --json plugin update --dry-run
{"version":"1","command":"plugin.update","ok":true,"data":{...},"warnings":[]}

$ onebrain --json --pretty plugin update --dry-run
{
"version": "1",
...
}
```

Test impact

  • 1284 passed; clippy clean; fmt clean.
  • Renamed: `root_help_renders_long_format` → `root_help_renders_compact_with_wrapped_defaults` (now asserts the new shape positively).
  • Updated: `json_shortcut_picks_json_mode` → split into `_minified_by_default` + `_with_pretty_flag_is_indented`. Same for `output_flag_json`.

Files

```
CHANGELOG.md | 8 +++-
Cargo.lock | 8 ++--
Cargo.toml | 2 +-
crates/onebrain-cli/src/cli.rs | 53 ++++++++++++++++----------
crates/onebrain-cli/src/commands/vault_sync.rs | 32 ++++++++++++----
crates/onebrain-cli/src/output/mode.rs | 41 ++++++++++++++++----
crates/onebrain-cli/src/v31/plugin_update.rs | 13 ++++---
crates/onebrain-cli/tests/v31_integration.rs | 51 +++++++++++++++++--------
8 files changed, 145 insertions(+), 63 deletions(-)
```

⚠️ Do not auto-merge

User testing locally before merge per their request. Ready to review when they're back.

kengio added 6 commits May 28, 2026 18:54
…ut, --json minified (v3.2.15)

All from live testing of v3.2.14 / earlier turns:

A. --help compact-with-wrap (revert v3.2.12 next_line_help)
   - root Cli: drop `next_line_help = true` so commands + options render compactly again
   - For args carrying `[default]` + `[possible values]` (-o/--output, --mode, --harness on harness run / skill run), set `hide_default_value` + `hide_possible_values` and embed a manual `\n[default: …, possible values: …]` tail in the `help` string — desc inline, bracketed block wraps to indented next line
   - HarnessMode variant docs stripped back to a comment block (was forcing long-format Possible values block in clap's renderer)
   - v31_integration test `root_help_renders_long_format` renamed to `root_help_renders_compact_with_wrapped_defaults` with the new positive assertions

B. plugin update: silence orchestrator's per-step `▸` lines
   - New `vault_sync::run_silent` forces `progress_writer = Some(io::sink())`, killing both intro/outro frame AND per-step `▸ <label>` lines on TTY
   - `plugin_update::run` switches to `run_silent` so the framed `⚡  Plugin Update` header appears as the FIRST thing after the brand banner. The animated spinner is now the only progress signal during work — matches doctor/update
   - v3.2.13's `run_embedded` removed (no remaining caller; the in-between 'silenced banner but kept step lines' mode wasn't useful)

C. --json minified by default
   - resolve_output_mode: `--json` and `--output json` no longer auto-prettify on TTY
   - To get indented JSON: pass `--json --pretty` (or `--output json --pretty`)
   - Easier copy/paste of the structured response into scripts/curl; humans can still opt back into pretty
   - Tests in mode.rs split into mono+pretty pairs

Tests: 1284 passed; clippy clean; fmt clean.

NOT MERGED — user reviewing locally first.
…-command emoji (v3.2.15 round 2)

Three more UX fixes from this turn's live testing (rolled into the v3.2.15
PR per user request 'ทำรวมใน PR เดียวกัน'):

D. Per-command emoji — pre-3.2.15 both doctor and update used 🧠 (same as
   the OneBrain wordmark banner). v3.2.15: doctor → 🔬, update → 🚀,
   plugin update → 🔄.

E. skill/harness --help flag-per-line — the run subcommand's description
   in those help screens was a dense dot-separated single-line summary.
   Now uses #[command(about = ...\n...)] so each --flag lands on its
   own indented line.

F. plugin update version tracking — captures
   .claude-plugin/plugin.json::version BEFORE + AFTER vault-sync.
   Surfaces the delta in:
     - vault-sync step row: vX → vY / vX · up-to-date / installed vY /
       current vY · skipped (dry-run)
     - verdict footer: updated vX → vY / update complete · vY /
       already up-to-date · vY / dry-run · current vY
     - JSON envelope: version_before / version_after (additive)

Implementation: shared helpers plugin_update_vault_detail +
plugin_update_verdict_text in dispatch.rs so the static and animated
renderers stay in sync.

1284 passed; clippy clean; fmt clean. NOT MERGED — user testing locally.
…lag duplicate (v3.2.15 round 3)

Two more UX fixes from this turn's live testing — bundled per user's
'ทำรวมใน PR เดียวกัน' request:

G. **skill run / harness run --help: no flag duplicate at top.** Pre-3.2.15
   round-3 the verb-level help repeated the parent-level Commands:-listing
   flag breakdown above its own Options: section (where every flag is
   already listed with [default] + [possible values]). Now uses
   #[command(about = ..., long_about = ...)] so the parent listing keeps
   the flag breakdown but the verb-level help shows just the short prose.

H. **--output table / --output tsv removed entirely.** Both variants fell
   through to serialize_json(value, false) for every command — the format
   flag silently lied (\`--output table doctor\` emitted the same minified
   JSON as \`--output json doctor\`). User picked 'lบออก — เหลือ text/json/yaml
   (แนะนำ)'. Removed the OutputMode::Table / Tsv variants, the dispatcher
   arms, the resolver branches, the value_parser entries, the help text
   listings, and all 6 dependent tests. Remaining set: text / json / yaml.

1281 passed (lost 3 tests with the variants); clippy clean; fmt clean.
Still NOT MERGED — user testing locally first.
… (v3.2.15 round 4)

Two more UX fixes from this turn's live testing:

I. **`skill run --help` / `harness run --help` now render Options compact**
   like top-level `onebrain --help`. Reverted round-3's parent-listing
   flag-per-line breakdown on `SkillVerb::Run` / `HarnessVerb::Run` —
   discovered the multi-line `#[command(about = ...)]` was triggering
   clap's auto-switch to long format (option name on own line + indented
   desc) at the verb level. Now both variants use a single-line doc-comment
   about so verb-level Options stays compact AND `[default]` + `[possible
   values]` still wraps to an indented next line for the args that carry
   both. Parent-level `skill --help` / `harness --help` Commands: rows now
   show a one-line summary (trade-off: lost the per-flag breakdown in the
   parent listing, but user explicitly chose compact verb-level).

J. **Positional `<NAME>` description for skill info / show / bootstrap +
   hidden bundle verbs.** Previously rendered as a bare `<NAME>` with no
   help text because the variant's doc-comment described the verb itself
   but didn't propagate to the inline-struct positional arg. Now every
   such variant uses the block form `Variant { /// <doc> name: String, }`.

1281 passed; clippy clean; fmt clean. Still NOT MERGED — user testing.
…v doc-drift cleaned

Round-1 review caught:
- CHANGELOG had 9 bullets under v3.2.15 (over the 8-bullet cap) — dropped
  the stale entry about parent-listing flag-per-line (round 4 reverted
  that, so the bullet documented behaviour that never shipped).
- Stale doc-comment refs to dropped `table` / `tsv` variants in
  legacy_output.rs, main.rs, banner.rs (top-level module doc + the
  suppress-precedence comment), output/mode.rs, output/progress.rs.

1281 passed; clippy clean; fmt clean. Still NOT MERGED.
Round-2 cleanup pass on PR #57 before the v3.2.x final cut. Applied
high-ROI items from the three review streams (clean code / consistency /
binary footprint):

**Binary footprint (-39% release size)**

- Cargo.toml release profile: `opt-level = "z"` (was implicit 3) +
  `lto = "fat"` (was "thin"). Trimmed 5.4 MB → 3.3 MB. The CLI's hot
  paths are I/O-bound, so size-optimised codegen carries no measurable
  runtime hit for sub-second runs.

**Deduplication**

- `read_plugin_version`: hoisted from private `fn` in
  `onebrain-fs::vault_sync::orchestrate` to `pub fn` re-exported at the
  crate root. `onebrain-cli::v31::plugin_update` drops its duplicate
  helper and calls through. (Code-Simplifier finding 1.1.)
- `vault_sync::run_with(embedded, silent)` → `run_with(silent)`. The
  orchestrator only consults `embedded` on the `progress_writer = None`
  branch, and the silent path always sets the writer to a sink — the flag
  was structurally dead. (Code-Simplifier finding 1.4.)

**Dead code drop**

- `plugin_update_vault_detail`: dropped the unused `partial_failed: bool`
  parameter. Both callsites + the rustdoc decision-table updated.
  (Code-Simplifier finding 2.2.)
- `ProgressRenderer::set_step_delay`: demoted from `pub` to `pub(crate)`.
  Production paths only ever pass `None`; the seam exists for the
  cross-module animated-path tests. (Code-Simplifier finding 2.1.)

**Consistency**

- `OutputMode` enum: added rustdoc summary line naming the three
  remaining variants (Text/Json/Yaml). (Consistency finding M3.)
- `commands/vault_sync.rs`: added `use std::io;` so body uses
  `io::sink()` matching the rustdoc references. (Consistency finding M4.)

**Deferred to v3.3** (per binary-size reviewer):

- reqwest → ureq swap (kills the entire tokio/hyper/h2 async stack; ~700–900 KB
  saved but needs its own PR for review)
- serde_yaml (deprecated) → serde_yaml_ng (active fork; ~30–60 KB, drop-in)
- clap feature audit (`env` usage)

**Skipped** (per Consistency reviewer):

- M1 rename `run_silent`/`run_quiet` → `run_embedded` (cosmetic only;
  the cleanup above already shipped the semantic simplification)
- M2 naming-convention comment block (informational; defer)
- Renderer unification `render_plugin_update_inner` (~80 LOC reduction
  but cross-cuts the static/animated split — too risky for the final
  v3.2.x cut)

1281 passed; clippy clean; fmt clean. Release binary: 3.3 MB (was 5.4 MB).
@kengio kengio merged commit 73ffaa6 into main May 28, 2026
6 checks passed
@kengio kengio deleted the v3.2.15-help-hybrid branch May 28, 2026 15:23
kengio added a commit that referenced this pull request May 29, 2026
…nify (v3.2.18) (#60)

Five dependency/cleanup changes for the v3.3 cycle, shipped as one patch
release (no new CLI command — completions deferred to v3.3):

- reqwest → ureq (blocking sync HTTP) across the 4 GitHub/tarball fetch sites
  (update + vault-sync). ureq carries no async runtime, so this removes the
  entire async stack (tokio/hyper/h2/tower/tower-http/hyper-rustls/
  tokio-rustls/hyper-util) from the release binary -- all pulled in only by
  reqwest. TLS stays rustls; JSON parsed via existing serde_json. Net:
  -342 KB (3.34->3.01 MB), -54 crates (178->124), ~12% faster clean build
  (41.0->36.2s). Runtime of everyday commands unchanged (they make no HTTP
  calls; the fetch paths are network-bound).
- Removed the dead tokio_helper runtime shim (#[allow(dead_code)], zero
  callers, the only direct tokio user). The daemon (v3.3) re-introduces tokio
  deliberately for its async RPC server.
- serde_yaml (archived/unmaintained) -> serde_yaml_ng via package-rename
  alias; zero code changes.
- Dropped the unused clap `env` feature (no #[arg(env)] in the tree).
- Unified the two plugin-update text renderers into render_plugin_update_inner,
  removing the PluginUpdateTextData trait that existed only to make the static
  renderer generic over a test double (-~80 LOC; Code-Simplifier finding, PR #57).
- Renamed vault_sync::run_silent + register_schedule::run_quiet -> both
  run_embedded (Consistency reviewer finding, PR #57).

3-round review (correctness/ureq parity · deps+tokio-removal · holistic):
cargo fmt + clippy -D warnings clean, 1296 tests pass, live `update --check`
verified against real GitHub over rustls. docs/reference updated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant