Skip to content

feat(toolpath-cli): interactive fzf session picker + topic metadata#60

Merged
eliothedeman merged 1 commit into
mainfrom
eliot/pensive-bhabha-f61ff4
Apr 27, 2026
Merged

feat(toolpath-cli): interactive fzf session picker + topic metadata#60
eliothedeman merged 1 commit into
mainfrom
eliot/pensive-bhabha-f61ff4

Conversation

@eliothedeman
Copy link
Copy Markdown
Collaborator

Summary

path import <provider> now auto-launches fzf when run with no --session, stdin/stderr are TTYs, and fzf is on PATH. TAB multi-selects (produces a Graph); single-select produces a Path. The picker uses path show <provider> --… (a new command, also useful on its own) as its --preview so you can see the session's summary live as you scroll.

The picker leans on a documented machine-readable surface so anyone can build their own:

  • path list <provider> --format tsv|json|pretty — replaces the bool --json flag (which still works as a deprecated alias). Default is pretty on a TTY and tsv when piped. For project-keyed providers (claude/gemini/pi) without --project, the tsv form emits sessions across all projects so the picker can scan globally.
  • path show <provider> — markdown summary for one session, reusing toolpath_md's summary detail.

To make the picker rows actually pickable, every project-keyed provider's session metadata gained a first_user_message: Option<String> field — the first non-empty user-prompt text. The fzf rows lead with the topic, so you can fuzzy-search by what the conversation was about instead of scrolling UUIDs and timestamps.

What's in the diff

Crates with API changes (versions bumped per the release checklist):

  • toolpath-claude 0.7.0 → 0.8.0 — adds ConversationMetadata.first_user_message; populated in the existing JSONL metadata pass (no extra I/O), chain-aware. Also a drive-by bug fix to sanitize_project_path so it maps .- like Claude Code does — without this, projects under dotted paths (github.com/…, .claude/worktrees/…) couldn't be looked up, which broke the path show round-trip for any picker output.
  • toolpath-gemini 0.1.0 → 0.2.0 — same field, populated for both main-session-file and orphan-UUID-directory cases.
  • toolpath-pi 0.1.0 → 0.2.0 — SessionMeta.first_user_message, extracted by walking the JSONL until the first user-role message with text.

toolpath-cli (already at 0.5.0):

  • New crates/toolpath-cli/src/fzf.rs — TTY+fzf detection, picker invocation, manual-recipe printer.
  • New crates/toolpath-cli/src/cmd_show.rspath show <provider> for the five session-keyed providers.
  • cmd_import.rs — project args switched to Option<String> for project-keyed providers; fzf-pick wired into all five session-keyed providers; multi-select produces a Graph.
  • cmd_list.rs--format tsv|json|pretty flag, TSV layout per provider, cross-project listing for project-keyed providers when --project is absent.

Documentation

  • CLAUDE.md — refreshed test counts, added path list --format tsv / path show to the CLI examples, two new "Things to know" entries covering the picker and the metadata title field.
  • README.md — replaced the stale derive block in the CLI reference with the actual current surface (import / export / cache / show / auth); new "Interactive selection (fzf)" section with copy-pasteable manual recipes for both project-keyed and single-keyed providers.
  • Per-crate READMEs (toolpath-claude, toolpath-gemini, toolpath-pi) — example using the new first_user_message field.
  • CHANGELOG.md — entry for the three crate version bumps.

Test plan

  • cargo build --workspace
  • cargo test --workspace — all green (~1300 tests)
  • cargo clippy --workspace -- -D warnings — clean
  • cargo test --doc for the three changed crates — clean
  • Smoke-tested path list claude --format tsv against my real ~/.claude/projects — first-user-message column populated, TSV columns parse correctly
  • Smoke-tested path show claude --project … --session … — works after the .-encoding fix; renders markdown summary
  • Verified the deprecated path derive shim still routes through cmd_import

Reviewer notes:

  • The picker only fires when both stdin and stderr are TTYs. Pipelines and CI keep the previous default-to-most-recent behavior, so this should be invisible to non-interactive consumers.
  • The path show command parses sessions via the existing read_conversation / read_session paths and renders via toolpath_md::Detail::Summary, so each preview is one full derive — adequate for a few hundred sessions, but if it gets sluggish on huge histories we can add a lightweight metadata-only preview later.
  • One pre-existing limitation surfaced (not fixed here): newer Claude Code sessions stored as a directory (<uuid>/ rather than <uuid>.jsonl) error at the read step. Pre-existing toolpath-claude reader behavior; the picker just shows them as un-previewable.

`path import <provider>` now auto-launches `fzf` when run with no
`--session`, stdin/stderr are TTYs, and `fzf` is on PATH. TAB
multi-selects (produces a Graph); single-select produces a Path.
Falls back silently to most-recent when fzf isn't available, or
prints a manual recipe when `--project` is also missing.

The picker leans on two new machine-readable surfaces:

- `path list <provider> --format tsv|json|pretty` — defaults to
  pretty on a TTY, tsv when piped. The deprecated `--json` flag
  still works. For project-keyed providers (claude/gemini/pi)
  without `--project`, tsv emits sessions across all projects so
  the picker can scan globally.
- `path show <provider> --…` — markdown summary for one session,
  used as fzf's `--preview`.

Made the picker rows actually useful by adding a topic column.
Picking a session is now searching by what the conversation was
*about* rather than scrolling timestamps:

- toolpath-claude: `ConversationMetadata.first_user_message`
  populated in the existing JSONL metadata pass (no extra I/O).
  Chain-aware: aggregated from the oldest segment.
- toolpath-gemini: same field, populated for both main-session-
  file and orphan-UUID-directory cases.
- toolpath-pi: `SessionMeta.first_user_message`, extracted by
  walking the JSONL until the first user-role message with text.

Drive-by fix: `toolpath-claude::sanitize_project_path` now also
maps `.` to `-`, matching Claude Code's actual encoding. Without
this, projects under dotted paths (`github.com/…`,
`.claude/worktrees/…`) couldn't be looked up by their original
path — which broke the `path show` round-trip for any picker
output.

Versions bumped per the release checklist:
  toolpath-claude 0.7.0 → 0.8.0
  toolpath-gemini 0.1.0 → 0.2.0
  toolpath-pi     0.1.0 → 0.2.0

Documentation (CLAUDE.md, README.md, per-crate READMEs, CHANGELOG)
updated to cover the new commands, the TSV column layout, the
fzf flow, and the metadata field.
@eliothedeman eliothedeman force-pushed the eliot/pensive-bhabha-f61ff4 branch from 0564e9d to 8009f9e Compare April 27, 2026 19:40
@eliothedeman eliothedeman merged commit 40af145 into main Apr 27, 2026
2 checks passed
@github-actions
Copy link
Copy Markdown

🔍 Preview deployed: https://4c41d66b.toolpath.pages.dev

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