Skip to content

refactor(cli, conversation): Overhaul conversation targeting system#557

Merged
JeanMertz merged 6 commits into
mainfrom
prr124
Apr 15, 2026
Merged

refactor(cli, conversation): Overhaul conversation targeting system#557
JeanMertz merged 6 commits into
mainfrom
prr124

Conversation

@JeanMertz

@JeanMertz JeanMertz commented Apr 15, 2026

Copy link
Copy Markdown
Collaborator

To start, here is the new targeting help text, giving an overview of the targeting overhaul:

Conversation Targeting

Use a conversation ID (e.g. jp-c17761673600), a keyword, or any text to
fuzzy-search by title.

Interactive Filter/Picker:
  ?                             # select from all
  ?p, ?pinned                   # select from pinned
  ?s, ?session                  # select from session

Conversation Aliases:
  l, latest                     # target latest active in workspace
  n, newest                     # target newest created
  p, pinned                     # target latest pinned
  s, session                    # target previous active in session

Multi-Target Keywords:
  +p, +pinned                   # target all pinned
  +s, +session                  # target all activated in session

This information can be gotten either when using help as the target name (as a positional argument such as jp conversation use help, or using the --id=help flag), or --help on any command that takes a conversation target.

Additionally, pinned conversations now track their pin time, allowing cross-pinned conversations to be sorted by pin time. This sorting is applied both in jp conversation ls and interactive target pickers.


As for the internals, here is what changed:

Replace the pinned: bool field on Conversation with pinned_at: Option<DateTime<Utc>>, recording when a conversation was pinned. This enables ordering pinned conversations by pin time rather than treating them all as equivalent. A new is_pinned() helper is provided for callers that only need the boolean.

The ConversationTarget enum is redesigned around clearer semantics:

  • LastActivatedLatest (l, latest)
  • LastCreatedNewest (n, newest)
  • PreviousSessionPrevious (s, session)
  • Current is removed
  • LatestPinned added (p, pinned) — resolves the most recently pinned conversation
  • Pinned added (+p, +pinned) — resolves all pinned conversations
  • Session now uses +s/+session prefix to distinguish multi-target keywords from single-target aliases
  • Help added to print keyword help and exit cleanly
  • Interactive picker keywords are now prefixed with ? (?, ?p, ?pinned, ?s, ?session)
  • Unknown input falls back to a fuzzy picker pre-seeded with the text, rather than returning a parse error

ConversationTarget::parse no longer returns Result; invalid conversation IDs now trigger the fuzzy picker instead of failing. Session-gating is moved to a new requires_session() predicate.

The ids fields on PositionalIds and FlagIds are made private behind a new ConversationIds trait exposing ids() and is_multi(). The ConversationLoadRequest builders now accept &dyn ConversationIds and propagate the multi flag, which switches the ? picker between single-select (inquire::Select) and multi-select (inquire::MultiSelect).

PickerFilter gains session and query fields. The query field pre-populates the picker's filter input, making any unrecognised text act as an instant fuzzy search.

Help text is rewritten to reflect the new keyword grammar and exposed via a standalone format_target_help() function used in error messages for NoConversationTarget and the new TargetHelp error.

To start, here is the new targeting help text, that explains it all:

```
Conversation Targeting

Use a conversation ID (e.g. jp-c17761673600), a keyword, or any text to
fuzzy-search by title.

Interactive Filter/Picker:
  ?                             # select from all
  ?p, ?pinned                   # select from pinned
  ?s, ?session                  # select from session

Conversation Aliases:
  n, newest                     # target newest created
  l, latest                     # target latest active in workspace
  p, pinned                     # target latest pinned
  s, session                    # target previous active in session

Multi-Target Keywords:
  +s, +session                  # target all activated in session
  +p, +pinned                   # target all pinned
```

This information can be gotten either via `--id=help`, `jp conversation
use help`, or `--help` on any command that takes a conversation target,
either as a positional argument or as a flag.

Additionally, pinned conversations now track their pin time, allowing
cross-pinned conversations to be sorted by pin time. This sorting is
applied both in `jp conversation ls` and interactive target pickers.

---

As for the internals, here is what changed:

Replace the `pinned: bool` field on `Conversation` with `pinned_at:
Option<DateTime<Utc>>`, recording when a conversation was pinned. This
enables ordering pinned conversations by pin time rather than treating
them all as equivalent. A new `is_pinned()` helper is provided for
callers that only need the boolean.

The `ConversationTarget` enum is redesigned around clearer semantics:

- `LastActivated` → `Latest` (`l`, `latest`)
- `LastCreated` → `Newest` (`n`, `newest`)
- `Previous` → `SessionPrevious` (`s`, `session`)
- `Current` is removed
- `LatestPinned` added (`p`, `pinned`) — resolves the most recently
  pinned conversation
- `Pinned` added (`+p`, `+pinned`) — resolves all pinned conversations
- `Session` now uses `+s`/`+session` prefix to distinguish multi-target
  keywords from single-target aliases
- `Help` added to print keyword help and exit cleanly
- Interactive picker keywords are now prefixed with `?` (`?`, `?p`,
  `?pinned`, `?s`, `?session`)
- Unknown input falls back to a fuzzy picker pre-seeded with the text,
  rather than returning a parse error

`ConversationTarget::parse` no longer returns `Result`; invalid
conversation IDs now trigger the fuzzy picker instead of failing.
Session-gating is moved to a new `requires_session()` predicate.

The `ids` fields on `PositionalIds` and `FlagIds` are made private
behind a new `ConversationIds` trait exposing `ids()` and `is_multi()`.
The `ConversationLoadRequest` builders now accept `&dyn ConversationIds`
and propagate the `multi` flag, which switches the `?` picker between
single-select (`inquire::Select`) and multi-select
(`inquire::MultiSelect`).

`PickerFilter` gains `session` and `query` fields. The `query` field
pre-populates the picker's filter input, making any unrecognised text
act as an instant fuzzy search.

Help text is rewritten to reflect the new keyword grammar and exposed
via a standalone `format_target_help()` function used in error messages
for `NoConversationTarget` and the new `TargetHelp` error.

Signed-off-by: Jean Mertz <git@jeanmertz.com>
…ystem

Signed-off-by: Jean Mertz <git@jeanmertz.com>
…ystem

Signed-off-by: Jean Mertz <git@jeanmertz.com>
…ystem

Signed-off-by: Jean Mertz <git@jeanmertz.com>
…ystem

Signed-off-by: Jean Mertz <git@jeanmertz.com>
…ystem

Signed-off-by: Jean Mertz <git@jeanmertz.com>
@JeanMertz
JeanMertz merged commit 9022b18 into main Apr 15, 2026
13 checks passed
@JeanMertz
JeanMertz deleted the prr124 branch April 15, 2026 11:35
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