From 7869c2a9790f43aad933dfeb63edcb04bca088e9 Mon Sep 17 00:00:00 2001 From: mika kuns Date: Fri, 29 May 2026 15:26:32 +0200 Subject: [PATCH] docs: add repo import list helper design spec Co-Authored-By: Claude Opus 4.7 --- ...26-05-29-repo-import-list-helper-design.md | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-29-repo-import-list-helper-design.md diff --git a/docs/superpowers/specs/2026-05-29-repo-import-list-helper-design.md b/docs/superpowers/specs/2026-05-29-repo-import-list-helper-design.md new file mode 100644 index 0000000..ae1007f --- /dev/null +++ b/docs/superpowers/specs/2026-05-29-repo-import-list-helper-design.md @@ -0,0 +1,138 @@ +# Repo Import List Helper — Design + +**Date:** 2026-05-29 +**Status:** Approved (pending spec review) + +## Problem + +Creating lists is one-at-a-time: click `+ New list`, then open List Settings to set the +working directory. Users with many repos under a few parent folders want to wire them all up +in one pass. + +## Goal + +A "list helper" that scans one or more parent folders for git repos, presents them as a +checklist, and bulk-creates a list (with `WorkingDir` pre-filled) for each ticked repo. + +## Entry Points + +1. **Help menu** — the title-bar dropdown in `MainWindow.axaml` that contains `About…`, + `Worktrees…`, etc. Add a new `MenuItem` `Add repos as lists…` wired to a command on + `MainWindowViewModel`. +2. **Lists island** — a small folder icon button beside the existing `+ New list` button in + `ListsIslandView.axaml`, wired to a command on `ListsIslandViewModel`. + +Both open the same modal. + +## Components + +### `RepoScanner` (new, `ClaudeDo.Ui/Services` or `ClaudeDo.Data`) + +Pure filesystem helper, no git library. Given a parent folder path, enumerates immediate +subdirectories and returns those that contain a `.git` entry (directory or file). Kept +separate from the VM so it is unit-testable. + +``` +IReadOnlyList Scan(string parentFolder) +record RepoCandidate(string Name, string FullPath) +``` + +- Skips the parent itself; only immediate children are considered (non-recursive). +- `.git` may be a directory (normal repo) or a file (worktree/submodule) — both count. +- Returns empty on missing/unreadable folder rather than throwing. + +### `RepoImportModalViewModel` (new, `ClaudeDo.Ui/ViewModels/Modals`) + +Follows the existing modal-VM pattern (`CloseAction`, resolved from DI). + +Dependencies: +- `IDbContextFactory` — load existing lists' `WorkingDir` values (for the + "already added" check) and create new `ListEntity` rows. Same dependency + `ListsIslandViewModel` already uses. + +State: +- `ObservableCollection Repos` — the combined checklist. +- A set of parent folder paths already scanned (to de-dupe re-adds). +- `CreateCount` — computed count of ticked-and-new rows (drives the confirm button label). + +Commands: +- `AddFolderAsync` — invokes the folder picker (via view code-behind callback, see below), + scans each chosen folder with `RepoScanner`, appends new candidates. De-dupes by full path + (case-insensitive) against rows already present. +- `CreateAsync` — for each ticked, non-existing row, create a `ListEntity` via + `ListRepository.AddAsync` (Name = folder name, WorkingDir = full path, + DefaultCommitType = `CommitTypeRegistry.DefaultType`, fresh `Guid` id, `CreatedAt` = now). + Then `CloseAction()`. +- `Cancel` — `CloseAction()`. + +On load, fetch all existing lists once and capture their `WorkingDir`s into a case-insensitive +set; each appended candidate whose path is in that set is marked `AlreadyAdded`. + +### `RepoImportItemViewModel` (new) + +- `Name`, `FullPath` (display). +- `AlreadyAdded` (bool) — true if a list already points at this path. +- `IsChecked` ([ObservableProperty]) — defaults `true` for new repos. For already-added rows it + is forced `true` and the checkbox is disabled. +- `CanToggle` => `!AlreadyAdded` (binds to checkbox `IsEnabled`). + +### `RepoImportModalView` (new, `ClaudeDo.Ui/Views/Modals`) + +A `Window` styled like the other modals (header bar, body, footer), shown via +`ShowDialog(owner)`. + +- **Header:** title `ADD REPOS AS LISTS` + close button. +- **Top of body:** `Add folder…` button. +- **Body:** scrollable `ItemsControl` over `Repos`. Each row = `CheckBox` (IsChecked two-way, + IsEnabled = `CanToggle`) + repo name + dim full path + `(already added)` label when + `AlreadyAdded`. +- **Footer:** `Create {CreateCount} lists` button (disabled when `CreateCount == 0`) + `Cancel`. +- Folder picker lives in the code-behind (mirrors `ListSettingsModalView.BrowseClicked`): + `OpenFolderPickerAsync` with `AllowMultiple = true`, results handed to the VM's + `AddFolderAsync`. + +## Data Flow + +1. User opens the modal from either entry point → modal loads existing lists' `WorkingDir`s. +2. User clicks `Add folder…` → picks one or more parent folders → `RepoScanner` finds repos → + rows appended (de-duped), already-added rows shown ticked+disabled. +3. User adjusts ticks → clicks `Create N lists`. +4. VM creates one `ListEntity` per ticked-new row via `ListRepository`. +5. Modal closes → the **caller reloads the Lists island** so new lists appear: + - Lists-island entry point: `ListsIslandViewModel.LoadAsync()`. + - Help-menu entry point: `MainWindowViewModel` reloads its `Lists` (the + `ListsIslandViewModel` instance) after the modal closes. + +## DI / Wiring + +- Register `RepoImportModalViewModel` (transient) alongside other modal VMs. +- Register `RepoScanner` if implemented as an injected service; a static helper needs no + registration. +- `ListsIslandViewModel` gains `Func? ShowRepoImportModal` and + an `OpenRepoImportCommand`, wired in `ListsIslandView.axaml.cs` (mirrors + `ShowListSettingsModal`). +- `MainWindowViewModel` gains the same `Func` + an `OpenRepoImportCommand`, wired in + `MainWindow.axaml.cs`. + +## Error Handling + +- Unreadable / missing folders: `RepoScanner` returns empty, no crash. +- Re-adding a folder already scanned: de-duped by path, no duplicate rows. +- Two ticked repos sharing a folder name: both created (list names are not unique) — acceptable. +- List creation failure (rare): best-effort per the existing pattern; do not block remaining + creations. + +## Testing + +- `RepoScanner` unit tests (the testable seam): a temp directory tree with a mix of git repos + (`.git` dir), a `.git`-file repo, plain folders, and an empty/missing parent. Assert only the + repo subfolders are returned and missing folders yield empty. +- VM-level "already added" logic and `CreateCount` can be exercised if a test seam is convenient, + but the filesystem scanner is the primary unit under test. UI wiring verified manually. + +## Out of Scope (YAGNI) + +- Recursive / deep scanning. +- Inline editing of the list name before creation. +- Setting model / system prompt / agent during import (tuned later per-list in List Settings). +- Picking repo folders directly (only parent-folder scan, per decision).