Skip to content

Complete all open beads tasks (hm-68a, hm-isi, hm-3w5, hm-3rr + close hm-6if/hm-wqb/hm-6qj)#16

Merged
cooper (czxtm) merged 5 commits into
mainfrom
claude/complete-beads-tasks-OyDY7
May 9, 2026
Merged

Complete all open beads tasks (hm-68a, hm-isi, hm-3w5, hm-3rr + close hm-6if/hm-wqb/hm-6qj)#16
cooper (czxtm) merged 5 commits into
mainfrom
claude/complete-beads-tasks-OyDY7

Conversation

@czxtm
Copy link
Copy Markdown
Contributor

Summary

Closes every open beads issue in one branch. Three were already implemented on main and just needed closing; four required new code.

Implemented

  • hm-68a — Levenshtein "did you mean" + TUI autocomplete. New crate::suggest module (inline DP, no extra crate). himitsu search prints did you mean <path>? to stderr on a zero-result query. Reusable SecretRefAutocomplete widget in tui::widgets is wired into the search bar; Ctrl+Space toggles it, Up/Down navigate, Enter accepts.
  • hm-isi — Ambient bottom-left hints. New tui::hint surface, distinct from the toast lane. Persistent muted text in the left third of the bottom row; auto-suppressed when a toast is on screen. The new-secret form publishes step-aware tips for path (slashes), tags (comma-separated), and expires_at (duration grammar) via NewSecretAction::SetHint / ClearHint.
  • hm-3w5 — Form placeholders. Added a placeholder builder on forms::Field plus a shared field_paragraph helper in new_secret.rs so every input renders a muted example when empty + unfocused. Concrete placeholders for path/value/description/tags/url/totp/env_key/expires_at.
  • hm-3rr — Submit button + Esc-confirm. New Step::Submit tab stop renders a [ submit ] button row; Enter on it runs the existing submit(). Esc with any populated field opens a centered unsaved changes modal with [ keep editing ] [ save ] [ discard ] (Left/Right cycle, Enter activates, Esc dismisses).

Already implemented; closed in .beads/issues.jsonl

  • hm-6if — Tags support. End-to-end: SecretValue.tags + grammar, CLI (set/write/search/ls/tag), env DSL (tag: selectors), TUI (Tags step, viewer chips, search row chips). Verified by 90+ tag-related tests.
  • hm-wqb — Editable item names. Editing path: in the secret-viewer edit doc renames the secret while preserving created_at/history (finish_edit_renames_secret_and_preserves_history).
  • hm-6qj — envs revamp. 2-pane DSL editor with live preview pane, fuzzy-find autocomplete corpus, scope-aware browser, wildcard envs.

Test results

  • cargo fmt --all -- --check — clean.
  • cargo clippy --workspace --all-targets -- -D warnings — clean.
  • cargo test --workspace — 602/602 binary tests pass; 94/97 integration tests pass. The 3 failing tests (test_check_*) are pre-existing on main (a git env quirk in the test setup) and unrelated to this work.

Notes for reviewers

  • Toggle key for the autocomplete popup is Ctrl+Space because Tab is already bound to fold-toggle in the search view. Documented at the call site.
  • The hint surface intentionally suppresses while a toast is on screen — they share the bottom row but never collide.
  • cargo fmt/clippy --fix swept up two pre-existing lint nits (unused_imports in cli/join.rs tests and manual_contains in command_palette.rs tests). Folded into the suggest commit because the gate requires them.

Test plan

  • cargo build succeeds locally.
  • cargo fmt --all -- --check is clean.
  • cargo clippy --workspace --all-targets -- -D warnings is clean.
  • cargo test --workspace — all binary tests pass; the only integration failures are the 3 pre-existing test_check_*.
  • Open the TUI → press n → see the placeholder examples + bottom-left hint.
  • Tab into Submit → press Enter → submit fires.
  • Esc on a populated form → unsaved changes modal appears.
  • In search, type a typo close to an existing secret → press Ctrl+Space → suggestion popup opens; or run himitsu search <typo> from the CLI to see did you mean …?.

https://claude.ai/code/session_01XGkUiFcnS4UX8ZHLGE7p2C


Generated by Claude Code

Closes hm-68a. Introduces a shared `crate::suggest` module backed by a
small Levenshtein DP and used in two surfaces:

- `himitsu search <query>` now prints `did you mean <path>?` to stderr
  when the result list is empty, the query is non-tag-only, and the
  closest existing secret is within the heuristic distance threshold.
- A reusable `SecretRefAutocomplete` widget in `tui::widgets` opens
  beneath the search bar on Ctrl+Space, surfacing up to five closest
  paths via Up/Down/Enter. The popup is non-modal — the query field
  keeps consuming printable keys.

Also folds in `cargo fmt --all` reformatting and a couple of
`clippy --fix` cleanups (unused imports, manual_contains) from the
same toolchain pass.
Closes hm-3w5 and hm-3rr.

* hm-3w5 — every field in `new_secret.rs` now renders a muted
  placeholder example when empty + unfocused (path: prod/api/STRIPE_KEY,
  url: https://example.com, expires_at: never | 30d | 6mo | …, etc.).
  Generalised the rendering through a single `field_paragraph` helper
  so the multi-line value field and every single-line field follow the
  same focus/empty/placeholder rules. Added the same support to the
  generic `forms::FormView` (used by `remote_add` and any future
  `ProtoForm`) via a `placeholder` builder on `Field`.
* hm-3rr — added a final `[ submit ]` button row at the end of the
  tab cycle. Enter on the submit step runs the existing `submit()`
  path. Pressing Esc with any populated field opens a centered
  unsaved-changes modal with `[ keep editing ] [ save ] [ discard ]`
  (Left/Right cycle, Enter activates, Esc dismisses to keep editing).
  An empty form still bails immediately so the prompt only fires when
  the user actually has something to lose.

Tests cover: cycle includes Submit + wraps; Shift-Tab wraps to Submit;
Enter on Submit submits; Esc-on-empty cancels; Esc with content opens
modal; Right/Left cycle the focus; Enter on Discard emits Cancel; Esc
inside modal restores the form. forms::FormView gains 3 placeholder
rendering tests.
Closes hm-isi. Adds a `Hint` surface for ambient, persistent guidance
text in the bottom-left of the frame — distinct from the toast lane,
which is transient and full-width.

* `tui::hint` — new module with a `Hint` struct, `MAX_LEN = 60`, multi-
  byte-safe truncation, and a `render` that paints only the left third
  of the bottom strip so a coexisting toast can claim the right portion.
* `tui::app` — `set_hint`/`clear_hint` accessors plus action wiring on
  `NewSecretAction::SetHint(_)` / `ClearHint`. Toast suppresses the hint
  visually (3s TTL means the hint reappears as soon as the toast clears);
  cancel/created/failed routes also clear the hint state explicitly.
* `tui::views::new_secret` — `on_key` snapshots the focused step,
  detects step transitions caused by navigation keys, and translates
  them into `SetHint`/`ClearHint` via `hint_for_step`. Tipped steps are
  Path, Tags, ExpiresAt; everything else clears the strip. Submit/save
  outcomes are forwarded untouched so an internal validation refocus
  doesn't churn the action enum.
* `tui::harness` integration test (`ambient_hint_tracks_focus_and_clears
  _on_cancel`) drives the new-secret flow via real key events and
  asserts the hint is seeded on open, swaps with focus, and clears on
  cancel.
@czxtm cooper (czxtm) marked this pull request as ready for review May 9, 2026 14:39
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c7a40ce17e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

// possibly land on: every secret path search_core just returned for
// an unfiltered scan. This is cheap (already in memory) and dodges
// having to re-walk the store when the popup wants to open.
let corpus: Vec<String> = self.results.iter().map(|r| r.path.clone()).collect();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Build autocomplete corpus from unfiltered results

When the user types a typo that produces zero search hits, refresh_results sets the autocomplete corpus from self.results, which has already been filtered by that typo, so the corpus is empty and Ctrl+Space cannot show the closest existing secret. This defeats the new typo-recovery path in the TUI; the corpus needs to come from an unfiltered scan (as the CLI suggestion path does with search_core(ctx, "", &[])) rather than the current filtered results.

Useful? React with 👍 / 👎.

@czxtm cooper (czxtm) merged commit d452c29 into main May 9, 2026
1 of 5 checks passed
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.

2 participants