Skip to content

Show pack add/remove diff in mcs sync interactive picker #328

@bguidolim

Description

@bguidolim

Problem

Users are misunderstanding the interactive mcs sync picker:

  • They don't select packs to install — the picker looks like a read-only list.
  • They don't unselect installed packs to remove them — they don't realize the selection is the desired final state, so they assume they need a separate remove action.

The root cause: mcs sync is declarative/convergent (desired state reconciliation), but the UI presents it as a flat selection. The ○/● markers give no cue that pre-checked items represent current state rather than new picks, and there's no preview of the delta before applying.

Proposed solution

1. Delta-aware markers in the picker

Today Sources/mcs/Core/CLIOutput.swift:520 renders two states ( selected / not). Extend to four states that compare the current toggle against the baseline (previously-installed packs, already seeded in Sources/mcs/Sync/Configurator.swift:64):

Marker Meaning
(default) Installed, staying installed
●+ (green) Will be added
○- (red) Will be removed
(dim) Not installed, not selected

Plus a live footer updating on each toggle: +2 to add, -1 to remove, 3 unchanged.

2. Confirmation screen before apply

After Enter, render a summary and require y to apply:

Review changes (project: /path/to/repo)
  + add:     ios, github-planner
  - remove:  android
  = keep:    swift

Apply? [y/N]

This catches the misunderstanding even if someone misreads the picker. Skippable when there's nothing to remove (additions-only is the friendly case).

3. Reframe the section header

Rename the picker title from "Tech Packs" to something like "Desired packs (selected = installed after sync)" in Configurator.swift:68-73. One line of copy that reframes the whole interaction.

Tradeoff considered and rejected

Tempting alternative: add mcs install <pack> / mcs remove <pack> as verbs matching user intuition. Rejected because it fragments the convergence engine — two mental models for the same operation, and ProjectState / ProjectIndex ownership tracking assumes a single reconciliation path. Keep sync as the single source of truth and make its intent clearer.

Scope

Touches CLIOutput.swift (picker rendering, new SelectableItem.baselineSelected field) and Configurator.swift (baseline propagation + post-picker confirmation step). Also applies to --global sync since it uses the same picker.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions